lib/isa/src/x86/decode.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const x86 = @import("root.zig");
3
4 pub const Error = error{
5 AmbiguousPrefix,
6 InstructionTooLong,
7 InvalidEncoding,
8 TruncatedInstruction,
9 UnsupportedAddressSize,
10 UnsupportedOpcode,
11 UnsupportedPrefix,
12 };
13
14 const Selector = enum {
15 none,
16 extension,
17 };
18
19 const Encoding = struct {
20 modrm: bool = false,
21 selector: Selector = .none,
22 };
23
24 const Bank = enum {
25 none,
26 gpr,
27 vector,
28 selector,
29 };
30
31 const Embedded = enum {
32 none,
33 accumulator,
34 opcode,
35 };
36
37 const Shape = struct {
38 register_bank: Bank = .none,
39 register_access: x86.Access = .none,
40 register_high8: bool = false,
41 operand_bank: Bank = .none,
42 operand_access: x86.Access = .none,
43 operand_high8: bool = false,
44 operand_width: ?x86.Width = null,
45 embedded: Embedded = .none,
46 embedded_access: x86.Access = .none,
47 };
48
49 const PrefixState = struct {
50 form: x86.Prefix,
51 rex: u8,
52 segment: x86.Segment,
53
54 fn wide(self: PrefixState) bool {
55 return self.rex & 0x08 != 0;
56 }
57
58 fn rexR(self: PrefixState) u4 {
59 return if (self.rex & 0x04 != 0) 8 else 0;
60 }
61
62 fn rexX(self: PrefixState) u4 {
63 return if (self.rex & 0x02 != 0) 8 else 0;
64 }
65
66 fn rexB(self: PrefixState) u4 {
67 return if (self.rex & 0x01 != 0) 8 else 0;
68 }
69 };
70
71 const ModRm = struct {
72 mode: x86.Mode,
73 mode_raw: u2,
74 extension: u3,
75 register: u4,
76 operand: u4,
77 base: ?u4,
78 index: ?u4,
79 scale: x86.Scale,
80 displacement: i32,
81 displacement_bytes: u3,
82 address: x86.AddressKind,
83 segment: x86.Segment,
84 };
85
86 const Payload = struct {
87 immediate: ?x86.Immediate = null,
88 relative: ?i32 = null,
89 };
90
91 pub fn decode(code: []const u8) Error!x86.Instruction {
92 var diagnostic: x86.Diagnostic = .{};
93 return decodeWithDiagnostic(code, &diagnostic);
94 }
95
96 pub fn decodeWithDiagnostic(
97 code: []const u8,
98 diagnostic: *x86.Diagnostic,
99 ) Error!x86.Instruction {
100 diagnostic.* = .{};
101 var cursor: usize = 0;
102 const prefix = try parsePrefix(code, &cursor, diagnostic);
103 const first = try takeByte(code, &cursor);
104 const map: x86.Map = if (first == 0x0f) .two else .one;
105 const opcode = if (map == .two) try takeByte(code, &cursor) else first;
106 const instruction_encoding = encoding(map, opcode) orelse {
107 const candidate = x86.Form{ .prefix = prefix.form, .map = map, .opcode = opcode };
108 diagnostic.captureForm(candidate);
109 return error.UnsupportedOpcode;
110 };
111 var modrm = if (instruction_encoding.modrm)
112 try parseModRm(code, &cursor, prefix)
113 else
114 null;
115 var form = createForm(prefix, map, opcode, instruction_encoding, modrm);
116 const operation = operationFor(prefix, map, opcode, modrm) orelse {
117 diagnostic.captureForm(form);
118 return error.UnsupportedOpcode;
119 };
120 if (modrm) |*value| {
121 parseDisplacement(code, &cursor, value) catch |failure| {
122 diagnostic.captureForm(form);
123 return failure;
124 };
125 }
126 const payload = parsePayload(code, &cursor, prefix, map, opcode, modrm) catch |failure| {
127 if (!(map == .one and opcode == 0xcd)) diagnostic.captureForm(form);
128 return failure;
129 };
130 if (map == .one and opcode == 0xcd) {
131 form.immediate = @truncate(payload.immediate.?.value);
132 }
133 if (cursor > 15) return error.InstructionTooLong;
134 var result = createInstruction(form, operation, cursor, prefix, modrm, payload);
135 addImplicitOperands(&result, prefix);
136 return result;
137 }
138
139 fn parsePrefix(
140 code: []const u8,
141 cursor: *usize,
142 diagnostic: *x86.Diagnostic,
143 ) Error!PrefixState {
144 while (cursor.* < code.len and cursor.* < 15) {
145 const byte = code[cursor.*];
146 if (byte == 0x67) {
147 diagnostic.capturePrefix(code[0 .. cursor.* + 1]);
148 return error.UnsupportedAddressSize;
149 }
150 if (!prefixByte(byte)) break;
151 cursor.* += 1;
152 }
153 if (cursor.* == 15) return error.TruncatedInstruction;
154 const bytes = code[0..cursor.*];
155 const form = x86.Prefix.fromBytes(bytes) orelse {
156 diagnostic.capturePrefix(bytes);
157 return prefixError(bytes);
158 };
159 return .{
160 .form = form,
161 .rex = form.rexByte(),
162 .segment = segmentFor(bytes),
163 };
164 }
165
166 fn prefixByte(byte: u8) bool {
167 return switch (byte) {
168 0x26,
169 0x2e,
170 0x36,
171 0x3e,
172 0x64,
173 0x65,
174 0x66,
175 0xf0,
176 0xf2,
177 0xf3,
178 0x40...0x4f,
179 => true,
180 else => false,
181 };
182 }
183
184 fn prefixError(bytes: []const u8) Error {
185 if (operand16CsSequence(bytes)) return error.UnsupportedPrefix;
186 var operand16 = false;
187 var lock = false;
188 var repeat = false;
189 var segment = false;
190 var rex = false;
191 for (bytes) |byte| {
192 if (byte >= 0x40 and byte <= 0x4f) {
193 if (rex) return error.AmbiguousPrefix;
194 rex = true;
195 continue;
196 }
197 if (rex) return error.AmbiguousPrefix;
198 switch (byte) {
199 0x66 => {
200 if (operand16) return error.AmbiguousPrefix;
201 operand16 = true;
202 },
203 0xf0 => {
204 if (lock) return error.AmbiguousPrefix;
205 lock = true;
206 },
207 0xf2, 0xf3 => {
208 if (repeat) return error.AmbiguousPrefix;
209 repeat = true;
210 },
211 0x26, 0x2e, 0x36, 0x3e, 0x64, 0x65 => {
212 if (segment) return error.AmbiguousPrefix;
213 segment = true;
214 },
215 else => {},
216 }
217 }
218 return error.UnsupportedPrefix;
219 }
220
221 fn operand16CsSequence(bytes: []const u8) bool {
222 if (bytes.len < 2 or bytes[bytes.len - 1] != 0x2e) return false;
223 for (bytes[0 .. bytes.len - 1]) |byte| {
224 if (byte != 0x66) return false;
225 }
226 return true;
227 }
228
229 fn segmentFor(bytes: []const u8) x86.Segment {
230 for (bytes) |byte| {
231 return switch (byte) {
232 0x26 => .es,
233 0x2e => .cs,
234 0x36 => .ss,
235 0x3e => .ds,
236 0x64 => .fs,
237 0x65 => .gs,
238 else => continue,
239 };
240 }
241 return .none;
242 }
243
244 fn encoding(map: x86.Map, opcode: u8) ?Encoding {
245 return switch (map) {
246 .one => encodingOne(opcode),
247 .two => encodingTwo(opcode),
248 };
249 }
250
251 fn encodingOne(opcode: u8) ?Encoding {
252 if (groupOpcode(opcode)) return .{ .modrm = true, .selector = .extension };
253 if (plainModRmOpcode(opcode)) return .{ .modrm = true };
254 if (x86.formModeled(.{ .opcode = opcode })) return .{};
255 return null;
256 }
257
258 fn groupOpcode(opcode: u8) bool {
259 return switch (opcode) {
260 0x80,
261 0x81,
262 0x83,
263 0xc0,
264 0xc1,
265 0xc6,
266 0xc7,
267 0xd0,
268 0xd1,
269 0xd2,
270 0xd3,
271 0xf6,
272 0xf7,
273 0xfe,
274 0xff,
275 => true,
276 else => false,
277 };
278 }
279
280 fn plainModRmOpcode(opcode: u8) bool {
281 if (opcode <= 0x03) return true;
282 if (opcode >= 0x08 and opcode <= 0x0b) return true;
283 if (opcode >= 0x18 and opcode <= 0x1b) return true;
284 if (opcode >= 0x20 and opcode <= 0x23) return true;
285 if (opcode >= 0x28 and opcode <= 0x2b) return true;
286 if (opcode >= 0x30 and opcode <= 0x33) return true;
287 return switch (opcode) {
288 0x38,
289 0x39,
290 0x3b,
291 0x69,
292 0x6b,
293 0x84,
294 0x85,
295 0x88,
296 0x89,
297 0x8a,
298 0x8b,
299 0x8d,
300 => true,
301 else => false,
302 };
303 }
304
305 fn encodingTwo(opcode: u8) ?Encoding {
306 if (opcode == 0x1f or opcode == 0x90 or
307 ((opcode >= 0x92 and opcode <= 0x97) or
308 (opcode >= 0x9c and opcode <= 0x9f)))
309 {
310 return .{ .modrm = true, .selector = .extension };
311 }
312 if (switch (opcode) {
313 0x10,
314 0x11,
315 0x42,
316 0x43,
317 0x44,
318 0x45,
319 0x47,
320 0x6f,
321 0x70,
322 0x7f,
323 0xaf,
324 0xb0,
325 0xb6,
326 0xb7,
327 0xbe,
328 0xbf,
329 => true,
330 else => false,
331 }) return .{ .modrm = true };
332 if (x86.formModeled(.{ .map = .two, .opcode = opcode })) return .{};
333 return null;
334 }
335
336 fn parseModRm(
337 code: []const u8,
338 cursor: *usize,
339 prefix: PrefixState,
340 ) Error!ModRm {
341 const byte = try takeByte(code, cursor);
342 const mode_raw: u2 = @intCast(byte >> 6);
343 const extension: u3 = @intCast((byte >> 3) & 7);
344 const operand_raw: u3 = @intCast(byte & 7);
345 var result = ModRm{
346 .mode = modeFromRaw(mode_raw),
347 .mode_raw = mode_raw,
348 .extension = extension,
349 .register = @as(u4, extension) | prefix.rexR(),
350 .operand = @as(u4, operand_raw) | prefix.rexB(),
351 .base = null,
352 .index = null,
353 .scale = .one,
354 .displacement = 0,
355 .displacement_bytes = 0,
356 .address = .none,
357 .segment = prefix.segment,
358 };
359 if (mode_raw == 3) return result;
360 if (operand_raw == 4) {
361 try parseSib(code, cursor, prefix, &result);
362 } else if (mode_raw == 0 and operand_raw == 5) {
363 result.address = .rip_relative;
364 result.displacement_bytes = 4;
365 } else {
366 result.base = result.operand;
367 result.address = .base;
368 }
369 if (mode_raw == 1) result.displacement_bytes = 1;
370 if (mode_raw == 2) result.displacement_bytes = 4;
371 return result;
372 }
373
374 fn parseSib(
375 code: []const u8,
376 cursor: *usize,
377 prefix: PrefixState,
378 result: *ModRm,
379 ) Error!void {
380 const sib = try takeByte(code, cursor);
381 result.scale = @fromBackingInt(@intCast(@as(u2, @intCast(sib >> 6))));
382 const index_raw: u3 = @intCast((sib >> 3) & 7);
383 const base_raw: u3 = @intCast(sib & 7);
384 if (index_raw != 4 or prefix.rexX() != 0) {
385 result.index = @as(u4, index_raw) | prefix.rexX();
386 }
387 if (result.mode_raw == 0 and base_raw == 5) {
388 result.displacement_bytes = 4;
389 } else {
390 result.base = @as(u4, base_raw) | prefix.rexB();
391 }
392 result.address = if (result.base == null)
393 if (result.index == null) .absolute else .absolute_indexed
394 else if (result.index == null)
395 .base
396 else
397 .base_indexed;
398 }
399
400 fn parseDisplacement(
401 code: []const u8,
402 cursor: *usize,
403 modrm: *ModRm,
404 ) Error!void {
405 modrm.displacement = switch (modrm.displacement_bytes) {
406 0 => 0,
407 1 => try readI8(code, cursor),
408 4 => try readI32(code, cursor),
409 else => return error.InvalidEncoding,
410 };
411 }
412
413 fn createForm(
414 prefix: PrefixState,
415 map: x86.Map,
416 opcode: u8,
417 instruction_encoding: Encoding,
418 modrm: ?ModRm,
419 ) x86.Form {
420 var result = x86.Form{
421 .prefix = prefix.form,
422 .map = map,
423 .opcode = opcode,
424 .width = effectiveWidth(prefix.form, map, opcode),
425 };
426 const shape_value = shape(map, opcode, if (modrm) |value| value.extension else null);
427 if (modrm) |value| {
428 result.mode = value.mode;
429 result.address = value.address;
430 result.base_role = addressRole(value.base);
431 result.index_role = addressRole(value.index);
432 if (instruction_encoding.selector == .extension) {
433 result.extension = .fromRaw(value.extension);
434 }
435 applyOperands(&result, prefix, shape_value, value);
436 }
437 applyEmbedded(&result, prefix, shape_value, opcode);
438 return result;
439 }
440
441 fn effectiveWidth(prefix: x86.Prefix, map: x86.Map, opcode: u8) x86.Width {
442 return switch (map) {
443 .one => oneByteWidth(prefix, opcode),
444 .two => twoByteWidth(prefix, opcode),
445 };
446 }
447
448 fn oneByteWidth(prefix: x86.Prefix, opcode: u8) x86.Width {
449 if (switch (opcode) {
450 0x90, 0x9c, 0xcd, 0xf4, 0xf5, 0xfa, 0xfb => true,
451 else => false,
452 }) return .none;
453 if ((opcode >= 0x50 and opcode <= 0x5f) or
454 ((opcode >= 0x72 and opcode <= 0x77) or
455 (opcode >= 0x7c and opcode <= 0x7f)) or
456 switch (opcode) {
457 0x6a, 0xc3, 0xe8, 0xe9, 0xeb => true,
458 else => false,
459 }) return controlWidth(prefix);
460 if (byteOpcode(opcode)) return .byte;
461 return operandWidth(prefix);
462 }
463
464 fn byteOpcode(opcode: u8) bool {
465 if (opcode >= 0xb0 and opcode <= 0xb7) return true;
466 return switch (opcode) {
467 0x00,
468 0x02,
469 0x04,
470 0x08,
471 0x0a,
472 0x18,
473 0x1a,
474 0x1c,
475 0x20,
476 0x22,
477 0x24,
478 0x28,
479 0x2a,
480 0x2c,
481 0x30,
482 0x32,
483 0x34,
484 0x38,
485 0x3c,
486 0x80,
487 0x84,
488 0x88,
489 0x8a,
490 0xa4,
491 0xa8,
492 0xaa,
493 0xc0,
494 0xc6,
495 0xd0,
496 0xd2,
497 0xee,
498 0xf6,
499 0xfe,
500 => true,
501 else => false,
502 };
503 }
504
505 fn twoByteWidth(prefix: x86.Prefix, opcode: u8) x86.Width {
506 if (switch (opcode) {
507 0x0b, 0x1f, 0x30, 0x32, 0xa2 => true,
508 else => false,
509 }) return .none;
510 if (switch (opcode) {
511 0x10, 0x11, 0x6f, 0x70, 0x7f => true,
512 else => false,
513 }) return .vector128;
514 if ((opcode >= 0x82 and opcode <= 0x87) or
515 (opcode >= 0x8c and opcode <= 0x8f)) return controlWidth(prefix);
516 if (opcode == 0x90 or ((opcode >= 0x92 and opcode <= 0x97) or
517 (opcode >= 0x9c and opcode <= 0x9f)) or opcode == 0xb0)
518 {
519 return .byte;
520 }
521 if (opcode == 0xb6 or opcode == 0xb7 or opcode == 0xbe or opcode == 0xbf) {
522 return if (prefix.wide())
523 .qword
524 else if (prefix.hasOperand16())
525 .word
526 else
527 .dword;
528 }
529 return operandWidth(prefix);
530 }
531
532 fn operandWidth(prefix: x86.Prefix) x86.Width {
533 if (prefix.wide()) return .qword;
534 if (prefix.hasOperand16()) return .word;
535 return .dword;
536 }
537
538 fn controlWidth(prefix: x86.Prefix) x86.Width {
539 return if (prefix.hasOperand16()) .word else .qword;
540 }
541
542 fn shape(map: x86.Map, opcode: u8, extension: ?u3) Shape {
543 return switch (map) {
544 .one => shapeOne(opcode, extension),
545 .two => shapeTwo(opcode),
546 };
547 }
548
549 fn shapeOne(opcode: u8, extension: ?u3) Shape {
550 if (opcode < 0x80) return shapeOneLow(opcode);
551 return shapeOneHigh(opcode, extension);
552 }
553
554 fn shapeOneLow(opcode: u8) Shape {
555 return switch (opcode) {
556 0x00, 0x08, 0x18, 0x20, 0x28, 0x30 => gpr(.read, .read_write, true),
557 0x01, 0x09, 0x19, 0x21, 0x29, 0x31 => gpr(.read, .read_write, false),
558 0x02, 0x0a, 0x1a, 0x22, 0x2a, 0x32 => gpr(.read_write, .read, true),
559 0x03, 0x0b, 0x1b, 0x23, 0x2b, 0x33 => gpr(.read_write, .read, false),
560 0x38 => gpr(.read, .read, true),
561 0x39, 0x3b => gpr(.read, .read, false),
562 0x04,
563 0x05,
564 0x0d,
565 0x1c,
566 0x1d,
567 0x24,
568 0x25,
569 0x2c,
570 0x2d,
571 0x34,
572 0x35,
573 => embedded(.accumulator, .read_write),
574 0x3c, 0x3d => embedded(.accumulator, .read),
575 0x50...0x57 => embedded(.opcode, .read),
576 0x58...0x5f => embedded(.opcode, .write),
577 0x69, 0x6b => gpr(.write, .read, false),
578 else => .{},
579 };
580 }
581
582 fn shapeOneHigh(opcode: u8, extension: ?u3) Shape {
583 const byte_group = opcode == 0xc0 or opcode == 0xfe;
584 return switch (opcode) {
585 0x80 => group(groupAccess(extension), true),
586 0x81, 0x83 => group(groupAccess(extension), false),
587 0x84 => gpr(.read, .read, true),
588 0x85 => gpr(.read, .read, false),
589 0x88 => gpr(.read, .write, true),
590 0x89 => gpr(.read, .write, false),
591 0x8a => gpr(.write, .read, true),
592 0x8b => gpr(.write, .read, false),
593 0x8d => gpr(.write, .none, false),
594 0xa8, 0xa9 => embedded(.accumulator, .read),
595 0xb0...0xbf => embedded(.opcode, .write),
596 0xc0, 0xc1, 0xd0, 0xd1, 0xd2, 0xd3, 0xfe => group(.read_write, byte_group),
597 0xc6, 0xc7 => group(.write, opcode == 0xc6),
598 0xf6, 0xf7 => group(groupThreeAccess(extension), opcode == 0xf6),
599 0xff => groupFiveShape(extension),
600 else => .{},
601 };
602 }
603
604 fn shapeTwo(opcode: u8) Shape {
605 return switch (opcode) {
606 0x10, 0x6f, 0x70 => vector(.read),
607 0x11, 0x7f => vector(.write),
608 0x42...0x45, 0x47 => gpr(.read_write, .read, false),
609 0x90, 0x92...0x97, 0x9c...0x9f => group(.write, true),
610 0xaf => gpr(.read_write, .read, false),
611 0xb0 => gpr(.read, .read_write, true),
612 0xb6, 0xbe => gprMixed(.write, .read, .byte),
613 0xb7, 0xbf => gprMixed(.write, .read, .word),
614 0xc8...0xcf => embedded(.opcode, .write),
615 else => .{},
616 };
617 }
618
619 fn gpr(register_access: x86.Access, operand_access: x86.Access, byte: bool) Shape {
620 return .{
621 .register_bank = .gpr,
622 .register_access = register_access,
623 .register_high8 = byte,
624 .operand_bank = .gpr,
625 .operand_access = operand_access,
626 .operand_high8 = byte,
627 };
628 }
629
630 fn gprMixed(
631 register_access: x86.Access,
632 operand_access: x86.Access,
633 operand_width: x86.Width,
634 ) Shape {
635 return .{
636 .register_bank = .gpr,
637 .register_access = register_access,
638 .operand_bank = .gpr,
639 .operand_access = operand_access,
640 .operand_high8 = operand_width == .byte,
641 .operand_width = operand_width,
642 };
643 }
644
645 fn group(access: x86.Access, byte: bool) Shape {
646 return .{
647 .register_bank = .selector,
648 .operand_bank = .gpr,
649 .operand_access = access,
650 .operand_high8 = byte,
651 };
652 }
653
654 fn groupFiveShape(extension: ?u3) Shape {
655 var result = group(groupFiveAccess(extension), false);
656 if (extension == 2 or extension == 4) result.operand_width = .qword;
657 return result;
658 }
659
660 fn vector(access: x86.Access) Shape {
661 return .{
662 .register_bank = .vector,
663 .register_access = if (access == .read) .write else .read,
664 .operand_bank = .vector,
665 .operand_access = access,
666 };
667 }
668
669 fn embedded(kind: Embedded, access: x86.Access) Shape {
670 return .{ .embedded = kind, .embedded_access = access };
671 }
672
673 fn groupAccess(extension: ?u3) x86.Access {
674 return if (extension == 7) .read else .read_write;
675 }
676
677 fn groupThreeAccess(extension: ?u3) x86.Access {
678 return switch (extension orelse 0) {
679 2, 3 => .read_write,
680 else => .read,
681 };
682 }
683
684 fn groupFiveAccess(extension: ?u3) x86.Access {
685 return switch (extension orelse 0) {
686 0, 1 => .read_write,
687 else => .read,
688 };
689 }
690
691 fn applyOperands(
692 result: *x86.Form,
693 prefix: PrefixState,
694 shape_value: Shape,
695 modrm: ModRm,
696 ) void {
697 if (shape_value.register_bank == .gpr) {
698 const register = gprRegister(
699 modrm.register,
700 result.width,
701 prefix,
702 shape_value.register_high8,
703 );
704 result.register_role = register.role();
705 result.register_access = shape_value.register_access;
706 }
707 if (modrm.mode == .register and shape_value.operand_bank == .gpr) {
708 const width = operandWidthFor(result.*, shape_value);
709 const operand = gprRegister(modrm.operand, width, prefix, shape_value.operand_high8);
710 result.operand_role = operand.role();
711 }
712 result.operand_access = if (shape_value.operand_bank == .vector and
713 modrm.mode == .register)
714 .none
715 else
716 shape_value.operand_access;
717 }
718
719 fn applyEmbedded(
720 result: *x86.Form,
721 prefix: PrefixState,
722 shape_value: Shape,
723 opcode: u8,
724 ) void {
725 result.embedded_access = shape_value.embedded_access;
726 const register = switch (shape_value.embedded) {
727 .none => return,
728 .accumulator => gprRegister(0, result.width, prefix, result.width == .byte),
729 .opcode => gprRegister(
730 @as(u4, @intCast(opcode & 7)) | prefix.rexB(),
731 result.width,
732 prefix,
733 result.width == .byte,
734 ),
735 };
736 result.embedded_role = register.role();
737 }
738
739 fn operandWidthFor(form: x86.Form, shape_value: Shape) x86.Width {
740 return shape_value.operand_width orelse form.width;
741 }
742
743 fn addressRole(value: ?u4) x86.Role {
744 const number = value orelse return .none;
745 return (x86.Register{ .number = number, .lane = .qword }).role();
746 }
747
748 fn operationFor(
749 prefix: PrefixState,
750 map: x86.Map,
751 opcode: u8,
752 modrm: ?ModRm,
753 ) ?x86.Operation {
754 return switch (map) {
755 .one => operationOne(
756 prefix,
757 opcode,
758 if (modrm) |value| value.extension else null,
759 ),
760 .two => operationTwo(
761 prefix,
762 opcode,
763 if (modrm) |value| value.extension else null,
764 ),
765 };
766 }
767
768 fn operationOne(
769 prefix: PrefixState,
770 opcode: u8,
771 extension: ?u3,
772 ) ?x86.Operation {
773 if (arithmeticOperation(opcode)) |operation| return operation;
774 if (opcode >= 0x50 and opcode <= 0x57) return .push;
775 if (opcode >= 0x58 and opcode <= 0x5f) return .pop;
776 if ((opcode >= 0x72 and opcode <= 0x77) or
777 (opcode >= 0x7c and opcode <= 0x7f)) return conditionOperation(opcode & 0x0f);
778 if (opcode >= 0xb0 and opcode <= 0xbf) return .mov;
779 return switch (opcode) {
780 0x69, 0x6b => .imul,
781 0x6a => .push,
782 0x80, 0x81, 0x83 => groupOneOperation(extension),
783 0x84, 0x85, 0xa8, 0xa9 => .test_,
784 0x88...0x8b => .mov,
785 0xc6, 0xc7 => if (extension == 0) .mov else null,
786 0x8d => .lea,
787 0x90 => if (prefix.form.hasOperand16() or prefix.rexB() != 0)
788 .xchg
789 else
790 .nop,
791 0x9c => .pushf,
792 0xa4, 0xa5 => .movs,
793 0xaa, 0xab => .stos,
794 0xc0, 0xc1, 0xd0, 0xd1, 0xd2, 0xd3 => shiftOperation(extension),
795 0xc3 => .ret,
796 0xcd => .int_,
797 0xe8 => .call,
798 0xe9, 0xeb => .jmp,
799 0xee => .out,
800 0xf4 => .hlt,
801 0xf5 => .cmc,
802 0xf6 => groupThreeByteOperation(extension),
803 0xf7 => groupThreeOperation(extension),
804 0xfa => .cli,
805 0xfb => .sti,
806 0xfe => groupFourOperation(extension),
807 0xff => groupFiveOperation(extension),
808 else => null,
809 };
810 }
811
812 fn arithmeticOperation(opcode: u8) ?x86.Operation {
813 return switch (opcode & 0xf8) {
814 0x00 => if (opcode <= 0x05) .add else null,
815 0x08 => if (opcode <= 0x0d) .or_ else null,
816 0x18 => if (opcode <= 0x1d) .sbb else null,
817 0x20 => if (opcode <= 0x25) .and_ else null,
818 0x28 => if (opcode <= 0x2d) .sub else null,
819 0x30 => if (opcode <= 0x35) .xor_ else null,
820 0x38 => if (opcode == 0x38 or opcode == 0x39 or
821 (opcode >= 0x3b and opcode <= 0x3d)) .cmp else null,
822 else => null,
823 };
824 }
825
826 fn groupOneOperation(extension: ?u3) ?x86.Operation {
827 return switch (extension orelse return null) {
828 0 => .add,
829 1 => .or_,
830 2 => .adc,
831 3 => .sbb,
832 4 => .and_,
833 5 => .sub,
834 6 => .xor_,
835 7 => .cmp,
836 };
837 }
838
839 fn shiftOperation(extension: ?u3) ?x86.Operation {
840 return switch (extension orelse return null) {
841 0 => .rol,
842 1 => .ror,
843 4, 6 => .shl,
844 5 => .shr,
845 7 => .sar,
846 else => null,
847 };
848 }
849
850 fn groupThreeOperation(extension: ?u3) ?x86.Operation {
851 return switch (extension orelse return null) {
852 0 => .test_,
853 2 => .not_,
854 3 => .neg,
855 4 => .mul,
856 5 => .imul,
857 6 => .div,
858 7 => .idiv,
859 else => null,
860 };
861 }
862
863 fn groupThreeByteOperation(extension: ?u3) ?x86.Operation {
864 return switch (extension orelse return null) {
865 0 => .test_,
866 2 => .not_,
867 3 => .neg,
868 else => null,
869 };
870 }
871
872 fn groupFiveOperation(extension: ?u3) ?x86.Operation {
873 return switch (extension orelse return null) {
874 0 => .inc,
875 1 => .dec,
876 2 => .call,
877 4 => .jmp,
878 else => null,
879 };
880 }
881
882 fn groupFourOperation(extension: ?u3) ?x86.Operation {
883 return switch (extension orelse return null) {
884 0 => .inc,
885 1 => .dec,
886 else => null,
887 };
888 }
889
890 fn operationTwo(
891 prefix: PrefixState,
892 opcode: u8,
893 extension: ?u3,
894 ) ?x86.Operation {
895 if ((opcode >= 0x82 and opcode <= 0x87) or
896 (opcode >= 0x8c and opcode <= 0x8f)) return conditionOperation(opcode & 0x0f);
897 if (opcode >= 0xc8 and opcode <= 0xcf) {
898 return if (bareOrRex(prefix.form)) .bswap else null;
899 }
900 return switch (opcode) {
901 0x0b => .ud2,
902 0x10, 0x11 => if (bareOrRex(prefix.form)) .movups else null,
903 0x1f => if (extension == 0) .nop else null,
904 0x30 => .wrmsr,
905 0x32 => .rdmsr,
906 0x42 => .cmovb,
907 0x43 => .cmovae,
908 0x44 => .cmove,
909 0x45 => .cmovne,
910 0x47 => .cmova,
911 0x6f, 0x7f => if (prefix.form.repeated()) .movdqu else null,
912 0x70 => if (operand16Encoding(prefix.form)) .pshufd else null,
913 0x90 => if (extension == 0 and bareOrRex(prefix.form)) .seto else null,
914 0x92 => if (extension == 0 and bareOrRex(prefix.form)) .setb else null,
915 0x93 => if (extension == 0 and bareOrRex(prefix.form)) .setae else null,
916 0x94 => if (extension == 0 and bareOrRex(prefix.form)) .sete else null,
917 0x95 => if (extension == 0 and bareOrRex(prefix.form)) .setne else null,
918 0x96 => if (extension == 0 and bareOrRex(prefix.form)) .setbe else null,
919 0x97 => if (extension == 0 and bareOrRex(prefix.form)) .seta else null,
920 0x9c => if (extension == 0 and bareOrRex(prefix.form)) .setl else null,
921 0x9d => if (extension == 0 and bareOrRex(prefix.form)) .setge else null,
922 0x9e => if (extension == 0 and bareOrRex(prefix.form)) .setle else null,
923 0x9f => if (extension == 0 and bareOrRex(prefix.form)) .setg else null,
924 0xa2 => .cpuid,
925 0xaf => .imul,
926 0xb0 => .cmpxchg,
927 0xb6, 0xb7 => .movzx,
928 0xbe, 0xbf => .movsx,
929 else => null,
930 };
931 }
932
933 fn bareOrRex(prefix: x86.Prefix) bool {
934 return prefix == .none or
935 (prefix.rexByte() != 0 and prefix.bytes().len == 1);
936 }
937
938 fn operand16Encoding(prefix: x86.Prefix) bool {
939 return switch (prefix) {
940 .operand16,
941 .operand16_rex_b,
942 .operand16_rex_r,
943 .operand16_rex_rb,
944 => true,
945 else => false,
946 };
947 }
948
949 fn conditionOperation(code: u8) ?x86.Operation {
950 return switch (code) {
951 0x2 => .jb,
952 0x3 => .jae,
953 0x4 => .je,
954 0x5 => .jne,
955 0x6 => .jbe,
956 0x7 => .ja,
957 0xc => .jl,
958 0xd => .jge,
959 0xe => .jle,
960 0xf => .jg,
961 else => null,
962 };
963 }
964
965 fn parsePayload(
966 code: []const u8,
967 cursor: *usize,
968 prefix: PrefixState,
969 map: x86.Map,
970 opcode: u8,
971 modrm: ?ModRm,
972 ) Error!Payload {
973 return switch (map) {
974 .one => parseOnePayload(code, cursor, prefix, opcode, modrm),
975 .two => parseTwoPayload(code, cursor, opcode),
976 };
977 }
978
979 fn parseOnePayload(
980 code: []const u8,
981 cursor: *usize,
982 prefix: PrefixState,
983 opcode: u8,
984 modrm: ?ModRm,
985 ) Error!Payload {
986 if (arithmeticImmediateBytes(prefix, opcode)) |bytes| {
987 return immediatePayload(code, cursor, bytes, prefix.wide());
988 }
989 if ((opcode >= 0x72 and opcode <= 0x77) or
990 (opcode >= 0x7c and opcode <= 0x7f)) return relativePayload(code, cursor, 1);
991 if (opcode >= 0xb0 and opcode <= 0xb7) return immediatePayload(code, cursor, 1, false);
992 if (opcode >= 0xb8 and opcode <= 0xbf) {
993 const bytes = if (prefix.wide()) 8 else operandBytes(prefix);
994 return immediatePayload(code, cursor, bytes, false);
995 }
996 return switch (opcode) {
997 0x69 => immediatePayload(code, cursor, operandBytes(prefix), prefix.wide()),
998 0x6a, 0x6b, 0x80, 0x83 => immediatePayload(code, cursor, 1, opcode != 0x80),
999 0x81 => immediatePayload(code, cursor, operandBytes(prefix), prefix.wide()),
1000 0xa8 => immediatePayload(code, cursor, 1, false),
1001 0xa9 => immediatePayload(code, cursor, operandBytes(prefix), prefix.wide()),
1002 0xc0, 0xc1 => immediatePayload(code, cursor, 1, false),
1003 0xc6 => immediatePayload(code, cursor, 1, false),
1004 0xc7 => immediatePayload(code, cursor, operandBytes(prefix), prefix.wide()),
1005 0xcd => immediatePayload(code, cursor, 1, false),
1006 0xe8, 0xe9 => relativePayload(code, cursor, 4),
1007 0xeb => relativePayload(code, cursor, 1),
1008 0xf6, 0xf7 => if (modrm.?.extension == 0)
1009 immediatePayload(
1010 code,
1011 cursor,
1012 if (opcode == 0xf6) 1 else operandBytes(prefix),
1013 opcode == 0xf7 and prefix.wide(),
1014 )
1015 else
1016 .{},
1017 else => .{},
1018 };
1019 }
1020
1021 fn parseTwoPayload(
1022 code: []const u8,
1023 cursor: *usize,
1024 opcode: u8,
1025 ) Error!Payload {
1026 if ((opcode >= 0x82 and opcode <= 0x87) or
1027 (opcode >= 0x8c and opcode <= 0x8f)) return relativePayload(code, cursor, 4);
1028 if (opcode == 0x70) return immediatePayload(code, cursor, 1, false);
1029 return .{};
1030 }
1031
1032 fn arithmeticImmediateBytes(prefix: PrefixState, opcode: u8) ?usize {
1033 const form = opcode & 7;
1034 if (arithmeticOperation(opcode) == null) return null;
1035 if (form == 4) return 1;
1036 if (form == 5) return operandBytes(prefix);
1037 return null;
1038 }
1039
1040 fn operandBytes(prefix: PrefixState) usize {
1041 return if (!prefix.wide() and prefix.form.hasOperand16()) 2 else 4;
1042 }
1043
1044 fn immediatePayload(
1045 code: []const u8,
1046 cursor: *usize,
1047 bytes: usize,
1048 sign: bool,
1049 ) Error!Payload {
1050 return .{ .immediate = .{
1051 .value = try readUnsigned(code, cursor, bytes),
1052 .encoded_bytes = @intCast(bytes),
1053 .extension = if (sign) .sign else .none,
1054 } };
1055 }
1056
1057 fn relativePayload(code: []const u8, cursor: *usize, bytes: usize) Error!Payload {
1058 return .{ .relative = switch (bytes) {
1059 1 => try readI8(code, cursor),
1060 4 => try readI32(code, cursor),
1061 else => unreachable,
1062 } };
1063 }
1064
1065 fn createInstruction(
1066 form: x86.Form,
1067 operation: x86.Operation,
1068 bytes: usize,
1069 prefix: PrefixState,
1070 modrm: ?ModRm,
1071 payload: Payload,
1072 ) x86.Instruction {
1073 const shape_value = shape(form.map, form.opcode, if (modrm) |value| value.extension else null);
1074 var result = x86.Instruction{
1075 .form = form,
1076 .operation = operation,
1077 .family = operation.family(),
1078 .bytes = @intCast(bytes),
1079 .register_access = shape_value.register_access,
1080 .operand_access = shape_value.operand_access,
1081 .embedded_access = shape_value.embedded_access,
1082 .immediate = payload.immediate,
1083 .relative = payload.relative,
1084 };
1085 if (modrm) |value| applyDecodedOperands(&result, prefix, shape_value, value);
1086 if (modrm) |value| {
1087 result.modrm_mode = value.mode_raw;
1088 result.modrm_operand = value.operand;
1089 }
1090 result.embedded = decodedEmbedded(form, prefix, shape_value);
1091 applyExactEncoding(&result, prefix);
1092 return result;
1093 }
1094
1095 fn applyExactEncoding(result: *x86.Instruction, prefix: PrefixState) void {
1096 switch (result.operation) {
1097 .bswap => result.embedded_access = .read_write,
1098 .xchg => if (result.form.map == .one and result.form.opcode == 0x90) {
1099 const width = if (prefix.form.hasOperand16())
1100 x86.Width.word
1101 else if (prefix.wide())
1102 x86.Width.qword
1103 else
1104 x86.Width.dword;
1105 result.register = gprRegister(0, width, prefix, false);
1106 result.register_access = .read_write;
1107 result.embedded = gprRegister(prefix.rexB(), width, prefix, false);
1108 result.embedded_access = .read_write;
1109 },
1110 else => {},
1111 }
1112 }
1113
1114 fn applyDecodedOperands(
1115 result: *x86.Instruction,
1116 prefix: PrefixState,
1117 shape_value: Shape,
1118 modrm: ModRm,
1119 ) void {
1120 if (shape_value.register_bank == .gpr) {
1121 result.register = gprRegister(
1122 modrm.register,
1123 result.form.width,
1124 prefix,
1125 shape_value.register_high8,
1126 );
1127 } else if (shape_value.register_bank == .vector) {
1128 result.register = vectorRegister(modrm.register);
1129 }
1130 if (modrm.mode == .register) {
1131 result.operand = decodedRegisterOperand(result.form, prefix, shape_value, modrm);
1132 } else {
1133 result.operand = .{ .memory = decodedMemory(
1134 result.form,
1135 shape_value,
1136 modrm,
1137 ) };
1138 }
1139 }
1140
1141 fn decodedRegisterOperand(
1142 form: x86.Form,
1143 prefix: PrefixState,
1144 shape_value: Shape,
1145 modrm: ModRm,
1146 ) x86.Operand {
1147 if (shape_value.operand_bank == .vector) {
1148 return .{ .register = vectorRegister(modrm.operand) };
1149 }
1150 const width = operandWidthFor(form, shape_value);
1151 return .{ .register = gprRegister(
1152 modrm.operand,
1153 width,
1154 prefix,
1155 shape_value.operand_high8,
1156 ) };
1157 }
1158
1159 fn decodedMemory(
1160 form: x86.Form,
1161 shape_value: Shape,
1162 modrm: ModRm,
1163 ) x86.Memory {
1164 return .{
1165 .kind = modrm.address,
1166 .width = if (shape_value.operand_access == .none)
1167 .none
1168 else
1169 operandWidthFor(form, shape_value),
1170 .base = if (modrm.base) |number| addressRegister(number) else null,
1171 .index = if (modrm.index) |number| addressRegister(number) else null,
1172 .scale = modrm.scale,
1173 .displacement = modrm.displacement,
1174 .segment = modrm.segment,
1175 };
1176 }
1177
1178 fn decodedEmbedded(
1179 form: x86.Form,
1180 prefix: PrefixState,
1181 shape_value: Shape,
1182 ) ?x86.Register {
1183 return switch (shape_value.embedded) {
1184 .none => null,
1185 .accumulator => gprRegister(0, form.width, prefix, form.width == .byte),
1186 .opcode => gprRegister(
1187 @as(u4, @intCast(form.opcode & 7)) | prefix.rexB(),
1188 form.width,
1189 prefix,
1190 form.width == .byte,
1191 ),
1192 };
1193 }
1194
1195 fn gprRegister(
1196 number: u4,
1197 width: x86.Width,
1198 prefix: PrefixState,
1199 high8: bool,
1200 ) x86.Register {
1201 if (high8 and prefix.rex == 0 and number >= 4 and number <= 7) {
1202 return .{ .number = number - 4, .lane = .high8 };
1203 }
1204 return .{ .number = number, .lane = laneFor(width) };
1205 }
1206
1207 fn vectorRegister(number: u4) x86.Register {
1208 return .{ .bank = .vector, .number = number, .lane = .vector128 };
1209 }
1210
1211 fn addressRegister(number: u4) x86.Register {
1212 return .{ .number = number, .lane = .qword };
1213 }
1214
1215 fn laneFor(width: x86.Width) x86.RegisterLane {
1216 return switch (width) {
1217 .byte => .low8,
1218 .word => .word,
1219 .dword => .dword,
1220 .qword, .none => .qword,
1221 .vector128 => .vector128,
1222 };
1223 }
1224
1225 fn addImplicitOperands(result: *x86.Instruction, prefix: PrefixState) void {
1226 switch (result.operation) {
1227 .push, .pop, .pushf, .call, .ret => addImplicit(result, gprUse(4, .qword, .read_write)),
1228 .mul, .div, .idiv => addMultiplyImplicit(result),
1229 .imul => if (result.form.opcode == 0xf6 or result.form.opcode == 0xf7)
1230 addMultiplyImplicit(result),
1231 .out => {
1232 addImplicit(result, gprUse(2, .word, .read));
1233 addImplicit(result, gprUse(0, .low8, .read));
1234 },
1235 .movs => addStringImplicit(result, prefix, false),
1236 .stos => addStringImplicit(result, prefix, true),
1237 .cmpxchg => addImplicit(result, gprUse(0, .low8, .read_write)),
1238 .cpuid => addCpuidImplicit(result),
1239 .rdmsr => addRdmsrImplicit(result),
1240 .wrmsr => addWrmsrImplicit(result),
1241 .rol, .ror, .sar, .shl, .shr => if (result.form.opcode == 0xd2 or
1242 result.form.opcode == 0xd3) addImplicit(result, gprUse(1, .low8, .read)),
1243 else => {},
1244 }
1245 }
1246
1247 fn addMultiplyImplicit(result: *x86.Instruction) void {
1248 const lane = laneFor(result.form.width);
1249 addImplicit(result, gprUse(0, lane, .read_write));
1250 addImplicit(result, gprUse(
1251 2,
1252 lane,
1253 if (result.operation == .mul or result.operation == .imul)
1254 .write
1255 else
1256 .read_write,
1257 ));
1258 }
1259
1260 fn addStringImplicit(
1261 result: *x86.Instruction,
1262 prefix: PrefixState,
1263 accumulator: bool,
1264 ) void {
1265 if (!accumulator) addImplicit(result, gprUse(6, .qword, .read_write));
1266 if (accumulator) {
1267 addImplicit(result, gprUse(0, laneFor(result.form.width), .read));
1268 }
1269 addImplicit(result, gprUse(7, .qword, .read_write));
1270 if (prefix.form.repeated()) addImplicit(result, gprUse(1, .qword, .read_write));
1271 }
1272
1273 fn addCpuidImplicit(result: *x86.Instruction) void {
1274 addImplicit(result, gprUse(0, .dword, .read_write));
1275 addImplicit(result, gprUse(1, .dword, .read_write));
1276 addImplicit(result, gprUse(2, .dword, .write));
1277 addImplicit(result, gprUse(3, .dword, .write));
1278 }
1279
1280 fn addRdmsrImplicit(result: *x86.Instruction) void {
1281 addImplicit(result, gprUse(1, .dword, .read));
1282 addImplicit(result, gprUse(0, .dword, .write));
1283 addImplicit(result, gprUse(2, .dword, .write));
1284 }
1285
1286 fn addWrmsrImplicit(result: *x86.Instruction) void {
1287 addImplicit(result, gprUse(1, .dword, .read));
1288 addImplicit(result, gprUse(0, .dword, .read));
1289 addImplicit(result, gprUse(2, .dword, .read));
1290 }
1291
1292 fn gprUse(number: u4, lane: x86.RegisterLane, access: x86.Access) x86.RegisterAccess {
1293 return .{ .register = .{ .number = number, .lane = lane }, .access = access };
1294 }
1295
1296 fn addImplicit(result: *x86.Instruction, value: x86.RegisterAccess) void {
1297 std.debug.assert(result.implicit_count < result.implicit.len);
1298 result.implicit[result.implicit_count] = value;
1299 result.implicit_count += 1;
1300 }
1301
1302 fn modeFromRaw(value: u2) x86.Mode {
1303 return switch (value) {
1304 0 => .memory,
1305 1 => .displacement8,
1306 2 => .displacement32,
1307 3 => .register,
1308 };
1309 }
1310
1311 fn readI8(code: []const u8, cursor: *usize) Error!i32 {
1312 return @as(i8, @bitCast(try takeByte(code, cursor)));
1313 }
1314
1315 fn readI32(code: []const u8, cursor: *usize) Error!i32 {
1316 const value = try take(code, cursor, 4);
1317 return std.mem.readInt(i32, value[0..4], .little);
1318 }
1319
1320 fn readUnsigned(code: []const u8, cursor: *usize, bytes: usize) Error!u64 {
1321 const value = try take(code, cursor, bytes);
1322 return switch (bytes) {
1323 1 => value[0],
1324 2 => std.mem.readInt(u16, value[0..2], .little),
1325 4 => std.mem.readInt(u32, value[0..4], .little),
1326 8 => std.mem.readInt(u64, value[0..8], .little),
1327 else => error.InvalidEncoding,
1328 };
1329 }
1330
1331 fn takeByte(code: []const u8, cursor: *usize) Error!u8 {
1332 return (try take(code, cursor, 1))[0];
1333 }
1334
1335 fn take(code: []const u8, cursor: *usize, count: usize) Error![]const u8 {
1336 if (cursor.* > code.len or count > code.len - cursor.*) {
1337 return error.TruncatedInstruction;
1338 }
1339 if (cursor.* + count > 15) return error.InstructionTooLong;
1340 const result = code[cursor.*..][0..count];
1341 cursor.* += count;
1342 return result;
1343 }