1const std = @import("std");
2const assert = std.debug.assert;
3const log = std.log.scoped(.x86_64_encoder);
4const math = std.math;
5const testing = std.testing;
6const Writer = std.Io.Writer;
7
8const bits = @import("bits.zig");
9const Encoding = @import("Encoding.zig");
10const FrameIndex = bits.FrameIndex;
11const Register = bits.Register;
12const Symbol = bits.Symbol;
13
14pub const Instruction = struct {
15 prefix: Prefix = .none,
16 encoding: Encoding,
17 ops: [4]Operand = @splat(.none),
18
19 pub const Mnemonic = Encoding.Mnemonic;
20
21 pub const Prefix = enum(u3) {
22 none,
23 lock,
24 rep,
25 repe,
26 repz,
27 repne,
28 repnz,
29 directive,
30 };
31
32 pub const Immediate = union(enum) {
33 signed: i32,
34 unsigned: u64,
35
36 pub fn u(x: u64) Immediate {
37 return .{ .unsigned = x };
38 }
39
40 pub fn s(x: i32) Immediate {
41 return .{ .signed = x };
42 }
43
44 pub fn asSigned(imm: Immediate, bit_size: u64) i64 {
45 return switch (imm) {
46 inline .signed, .unsigned => |x| switch (bit_size) {
47 1, 8 => @as(i8, if (x < 0) @intCast(x) else @bitCast(@as(u8, @intCast(x)))),
48 16 => @as(i16, if (x < 0) @intCast(x) else @bitCast(@as(u16, @intCast(x)))),
49 32 => @as(i32, if (x < 0) @intCast(x) else @bitCast(@as(u32, @intCast(x)))),
50 64 => @as(i64, if (x < 0) @intCast(x) else @bitCast(@as(u64, @intCast(x)))),
51 else => unreachable,
52 },
53 };
54 }
55
56 pub fn asUnsigned(imm: Immediate, bit_size: u64) u64 {
57 return switch (imm) {
58 inline .signed, .unsigned => |x| switch (bit_size) {
59 1, 8 => @as(u8, if (x < 0) @bitCast(@as(i8, @intCast(x))) else @intCast(x)),
60 16 => @as(u16, if (x < 0) @bitCast(@as(i16, @intCast(x))) else @intCast(x)),
61 32 => @as(u32, if (x < 0) @bitCast(@as(i32, @intCast(x))) else @intCast(x)),
62 64 => @as(u64, if (x < 0) @bitCast(@as(i64, @intCast(x))) else @intCast(x)),
63 else => unreachable,
64 },
65 };
66 }
67 };
68
69 pub const Memory = union(enum) {
70 sib: Sib,
71 rip: Rip,
72 moffs: Moffs,
73
74 pub const Base = bits.Memory.Base;
75
76 pub const ScaleIndex = struct {
77 scale: u4,
78 index: Register,
79
80 const none = ScaleIndex{ .scale = 0, .index = undefined };
81 };
82
83 pub const PtrSize = bits.Memory.Size;
84
85 pub const Sib = struct {
86 ptr_size: PtrSize,
87 base: Base,
88 scale_index: ScaleIndex,
89 disp: i32,
90 };
91
92 pub const Rip = struct {
93 ptr_size: PtrSize,
94 disp: i32,
95 };
96
97 pub const Moffs = struct {
98 seg: Register,
99 offset: u64,
100 };
101
102 pub fn initMoffs(reg: Register, offset: u64) Memory {
103 assert(reg.isClass(.segment));
104 return .{ .moffs = .{ .seg = reg, .offset = offset } };
105 }
106
107 pub fn initSib(ptr_size: PtrSize, args: struct {
108 disp: i32 = 0,
109 base: Base = .none,
110 scale_index: ?ScaleIndex = null,
111 }) Memory {
112 if (args.scale_index) |si| assert(std.math.isPowerOfTwo(si.scale));
113 return .{ .sib = .{
114 .base = args.base,
115 .disp = args.disp,
116 .ptr_size = ptr_size,
117 .scale_index = if (args.scale_index) |si| si else ScaleIndex.none,
118 } };
119 }
120
121 pub fn initRip(ptr_size: PtrSize, displacement: i32) Memory {
122 return .{ .rip = .{ .ptr_size = ptr_size, .disp = displacement } };
123 }
124
125 pub fn isSegmentRegister(mem: Memory) bool {
126 return switch (mem) {
127 .moffs => true,
128 .rip => false,
129 .sib => |s| switch (s.base) {
130 .none, .frame, .table, .rip_inst, .nav, .uav, .lazy_sym, .extern_func => false,
131 .reg => |reg| reg.isClass(.segment),
132 },
133 };
134 }
135
136 pub fn base(mem: Memory) Base {
137 return switch (mem) {
138 .moffs => |m| .{ .reg = m.seg },
139 .sib => |s| s.base,
140 .rip => .none,
141 };
142 }
143
144 pub fn scaleIndex(mem: Memory) ?ScaleIndex {
145 return switch (mem) {
146 .moffs, .rip => null,
147 .sib => |s| if (s.scale_index.scale > 0) s.scale_index else null,
148 };
149 }
150
151 pub fn disp(mem: Memory) Immediate {
152 return switch (mem) {
153 .sib => |s| .s(s.disp),
154 .rip => |r| .s(r.disp),
155 .moffs => |m| .u(m.offset),
156 };
157 }
158
159 pub fn bitSize(mem: Memory, target: *const std.Target) u64 {
160 return switch (mem) {
161 .rip => |r| r.ptr_size.bitSize(target),
162 .sib => |s| s.ptr_size.bitSize(target),
163 .moffs => target.ptrBitWidth(),
164 };
165 }
166 };
167
168 pub const Operand = union(enum) {
169 none,
170 reg: Register,
171 mem: Memory,
172 imm: Immediate,
173 bytes: []const u8,
174
175 /// Returns the bitsize of the operand.
176 pub fn bitSize(op: Operand) u64 {
177 return switch (op) {
178 .none => unreachable,
179 .reg => |reg| reg.bitSize(),
180 .mem => |mem| mem.bitSize(),
181 .imm => unreachable,
182 .bytes => unreachable,
183 };
184 }
185
186 /// Returns true if the operand is a segment register.
187 /// Asserts the operand is either register or memory.
188 pub fn isSegmentRegister(op: Operand) bool {
189 return switch (op) {
190 .none => unreachable,
191 .reg => |reg| reg.isClass(.segment),
192 .mem => |mem| mem.isSegmentRegister(),
193 .imm => unreachable,
194 .bytes => unreachable,
195 };
196 }
197
198 pub fn baseExtEnc(op: Operand) u2 {
199 return switch (op) {
200 .none, .imm => 0b00,
201 .reg => |reg| @truncate(reg.enc() >> 3),
202 .mem => |mem| switch (mem.base()) {
203 .none, .frame, .table, .rip_inst, .nav, .uav, .lazy_sym, .extern_func => 0b00, // rsp, rbp, and rip are not extended
204 .reg => |reg| @truncate(reg.enc() >> 3),
205 },
206 .bytes => unreachable,
207 };
208 }
209
210 pub fn indexExtEnc(op: Operand) u2 {
211 return switch (op) {
212 .none, .reg, .imm => 0b00,
213 .mem => |mem| if (mem.scaleIndex()) |si| @truncate(si.index.enc() >> 3) else 0b00,
214 .bytes => unreachable,
215 };
216 }
217
218 const Format = struct {
219 op: Operand,
220 enc_op: Encoding.Op,
221
222 fn default(f: Format, w: *Writer) Writer.Error!void {
223 const op = f.op;
224 const enc_op = f.enc_op;
225 switch (op) {
226 .none => {},
227 .reg => |reg| try w.writeAll(@tagName(reg)),
228 .mem => |mem| switch (mem) {
229 .rip => |rip| {
230 try w.print("{f} [rip", .{rip.ptr_size});
231 if (rip.disp != 0) try w.print(" {c} 0x{x}", .{
232 @as(u8, if (rip.disp < 0) '-' else '+'),
233 @abs(rip.disp),
234 });
235 try w.writeByte(']');
236 },
237 .sib => |sib| {
238 try w.print("{f} ", .{sib.ptr_size});
239
240 if (mem.isSegmentRegister()) {
241 return w.print("{s}:0x{x}", .{ @tagName(sib.base.reg), sib.disp });
242 }
243
244 try w.writeByte('[');
245
246 var any = true;
247 switch (sib.base) {
248 .none => any = false,
249 .reg => |reg| try w.print("{s}", .{@tagName(reg)}),
250 .frame => |frame_index| try w.print("{f}", .{frame_index}),
251 .table => try w.print("Table", .{}),
252 .rip_inst => |inst_index| try w.print("RipInst({d})", .{inst_index}),
253 .nav => |nav| try w.print("Nav({d})", .{@backingInt(nav)}),
254 .uav => |uav| try w.print("Uav({d})", .{@backingInt(uav.val)}),
255 .lazy_sym => |lazy_sym| try w.print("LazySym({s}, {d})", .{
256 @tagName(lazy_sym.kind),
257 @backingInt(lazy_sym.ty),
258 }),
259 .extern_func => |extern_func| try w.print("ExternFunc({d})", .{@backingInt(extern_func)}),
260 }
261 if (mem.scaleIndex()) |si| {
262 if (any) try w.writeAll(" + ");
263 try w.print("{s} * {d}", .{ @tagName(si.index), si.scale });
264 any = true;
265 }
266 if (sib.disp != 0 or !any) {
267 if (any)
268 try w.print(" {c} ", .{@as(u8, if (sib.disp < 0) '-' else '+')})
269 else if (sib.disp < 0)
270 try w.writeByte('-');
271 try w.print("0x{x}", .{@abs(sib.disp)});
272 any = true;
273 }
274
275 try w.writeByte(']');
276 },
277 .moffs => |moffs| try w.print("{s}:0x{x}", .{
278 @tagName(moffs.seg),
279 moffs.offset,
280 }),
281 },
282 .imm => |imm| if (enc_op.isSigned()) {
283 const imms = imm.asSigned(enc_op.immBitSize());
284 if (imms < 0) try w.writeByte('-');
285 try w.print("0x{x}", .{@abs(imms)});
286 } else try w.print("0x{x}", .{imm.asUnsigned(enc_op.immBitSize())}),
287 .bytes => unreachable,
288 }
289 }
290 };
291
292 pub fn fmt(op: Operand, enc_op: Encoding.Op) std.fmt.Alt(Format, Format.default) {
293 return .{ .data = .{ .op = op, .enc_op = enc_op } };
294 }
295 };
296
297 pub fn new(
298 prefix: Prefix,
299 mnemonic: Mnemonic,
300 ops: []const Operand,
301 target: *const std.Target,
302 ) !Instruction {
303 const encoding: Encoding = switch (prefix) {
304 else => (try Encoding.findByMnemonic(prefix, mnemonic, ops, target)) orelse {
305 log.err("no encoding found for: {s} {s} {s} {s} {s} {s}", .{
306 @tagName(prefix),
307 @tagName(mnemonic),
308 @tagName(if (ops.len > 0) Encoding.Op.fromOperand(ops[0], target) else .none),
309 @tagName(if (ops.len > 1) Encoding.Op.fromOperand(ops[1], target) else .none),
310 @tagName(if (ops.len > 2) Encoding.Op.fromOperand(ops[2], target) else .none),
311 @tagName(if (ops.len > 3) Encoding.Op.fromOperand(ops[3], target) else .none),
312 });
313 return error.InvalidInstruction;
314 },
315 .directive => .{
316 .mnemonic = mnemonic,
317 .data = .{
318 .op_en = .z,
319 .ops = .{
320 if (ops.len > 0) Encoding.Op.fromOperand(ops[0], target) else .none,
321 if (ops.len > 1) Encoding.Op.fromOperand(ops[1], target) else .none,
322 if (ops.len > 2) Encoding.Op.fromOperand(ops[2], target) else .none,
323 if (ops.len > 3) Encoding.Op.fromOperand(ops[3], target) else .none,
324 },
325 .opc_len = 0,
326 .opc = undefined,
327 .modrm_ext = 0,
328 .mode = .none,
329 .feature = .none,
330 },
331 },
332 };
333 log.debug("selected encoding: {f}", .{encoding});
334
335 var inst: Instruction = .{
336 .prefix = prefix,
337 .encoding = encoding,
338 .ops = @splat(.none),
339 };
340 @memcpy(inst.ops[0..ops.len], ops);
341 return inst;
342 }
343
344 pub fn format(inst: Instruction, w: *Writer) Writer.Error!void {
345 switch (inst.prefix) {
346 .none, .directive => {},
347 else => try w.print("{s} ", .{@tagName(inst.prefix)}),
348 }
349 try w.print("{s}", .{@tagName(inst.encoding.mnemonic)});
350 for (inst.ops, inst.encoding.data.ops, 0..) |op, enc, i| {
351 if (op == .none) break;
352 if (i > 0) try w.writeByte(',');
353 try w.print(" {f}", .{op.fmt(enc)});
354 }
355 }
356
357 pub fn encode(inst: Instruction, w: *Writer, comptime opts: Options) !void {
358 assert(inst.prefix != .directive);
359 const encoder: Encoder(opts) = .{ .w = w };
360 const enc = inst.encoding;
361 const data = enc.data;
362
363 try inst.encodeWait(encoder);
364 if (data.mode.isVex()) {
365 try inst.encodeVexPrefix(encoder);
366 const opc = inst.encoding.opcode();
367 try encoder.opcode_1byte(opc[opc.len - 1]);
368 } else {
369 try inst.encodeLegacyPrefixes(encoder);
370 try inst.encodeMandatoryPrefix(encoder);
371 try inst.encodeRexPrefix(encoder);
372 try inst.encodeOpcode(encoder);
373 }
374
375 switch (data.op_en) {
376 .z, .o, .zo, .oz => {},
377 .i, .d => try encodeImm(inst.ops[0].imm, data.ops[0], encoder),
378 .zi, .oi => try encodeImm(inst.ops[1].imm, data.ops[1], encoder),
379 .ii => {
380 try encodeImm(inst.ops[0].imm, data.ops[0], encoder);
381 try encodeImm(inst.ops[1].imm, data.ops[1], encoder);
382 },
383 .fd => try encoder.imm64(inst.ops[1].mem.moffs.offset),
384 .td => try encoder.imm64(inst.ops[0].mem.moffs.offset),
385 else => {
386 const mem_op: Operand = switch (data.op_en) {
387 .ia => .{ .reg = .eax },
388 .m, .mi, .m1, .mc, .mr, .mri, .mrc, .mvr => inst.ops[0],
389 .rm, .rmi, .rm0, .vm, .vmi, .rmv => inst.ops[1],
390 .rvm, .rvmr, .rvmi => inst.ops[2],
391 else => unreachable,
392 };
393 switch (mem_op) {
394 .reg => |reg| {
395 const rm: u3 = switch (data.op_en) {
396 .ia, .m, .mi, .m1, .mc, .vm, .vmi => enc.modRmExt(),
397 .mr, .mri, .mrc => @truncate(inst.ops[1].reg.enc()),
398 .rm, .rmi, .rm0, .rvm, .rvmr, .rvmi, .rmv => @truncate(inst.ops[0].reg.enc()),
399 .mvr => @truncate(inst.ops[2].reg.enc()),
400 else => unreachable,
401 };
402 try encoder.modRm_direct(rm, @truncate(reg.enc()));
403 },
404 .mem => |mem| {
405 const op = switch (data.op_en) {
406 .m, .mi, .m1, .mc, .vm, .vmi => .none,
407 .mr, .mri, .mrc => inst.ops[1],
408 .rm, .rmi, .rm0, .rvm, .rvmr, .rvmi, .rmv => inst.ops[0],
409 .mvr => inst.ops[2],
410 else => unreachable,
411 };
412 try encodeMemory(enc, mem, op, encoder);
413 },
414 else => unreachable,
415 }
416
417 switch (data.op_en) {
418 .ia => try encodeImm(inst.ops[0].imm, data.ops[0], encoder),
419 .mi => try encodeImm(inst.ops[1].imm, data.ops[1], encoder),
420 .rmi, .mri, .vmi => try encodeImm(inst.ops[2].imm, data.ops[2], encoder),
421 .rvmr => try encoder.imm8(@as(u8, @as(u4, @intCast(inst.ops[3].reg.enc()))) << 4),
422 .rvmi => try encodeImm(inst.ops[3].imm, data.ops[3], encoder),
423 else => {},
424 }
425 },
426 }
427 }
428
429 fn encodeOpcode(inst: Instruction, encoder: anytype) !void {
430 const opcode = inst.encoding.opcode();
431 const first = @intFromBool(inst.encoding.mandatoryPrefix() != null);
432 const final = opcode.len - 1;
433 for (opcode[first..final]) |byte| try encoder.opcode_1byte(byte);
434 switch (inst.encoding.data.op_en) {
435 .o, .oz, .oi => try encoder.opcode_withReg(opcode[final], @truncate(inst.ops[0].reg.enc())),
436 .zo => try encoder.opcode_withReg(opcode[final], @truncate(inst.ops[1].reg.enc())),
437 else => try encoder.opcode_1byte(opcode[final]),
438 }
439 }
440
441 fn encodeWait(inst: Instruction, encoder: anytype) !void {
442 switch (inst.encoding.data.mode) {
443 .wait => try encoder.opcode_1byte(0x9b),
444 else => {},
445 }
446 }
447
448 fn encodeLegacyPrefixes(inst: Instruction, encoder: anytype) !void {
449 const enc = inst.encoding;
450 const data = enc.data;
451 const op_en = data.op_en;
452
453 var legacy = LegacyPrefixes{};
454
455 switch (inst.prefix) {
456 .none => {},
457 .lock => legacy.prefix_f0 = true,
458 .repne, .repnz => legacy.prefix_f2 = true,
459 .rep, .repe, .repz => legacy.prefix_f3 = true,
460 .directive => unreachable,
461 }
462
463 switch (data.mode) {
464 .short, .rex_short => legacy.set16BitOverride(),
465 else => {},
466 }
467
468 const segment_override: ?Register = switch (op_en) {
469 .z, .i, .zi, .ii, .ia, .o, .zo, .oz, .oi, .d => null,
470 .fd => inst.ops[1].mem.base().reg,
471 .td => inst.ops[0].mem.base().reg,
472 .rm, .rmi, .rm0 => if (inst.ops[1].isSegmentRegister())
473 switch (inst.ops[1]) {
474 .reg => |reg| reg,
475 .mem => |mem| mem.base().reg,
476 else => unreachable,
477 }
478 else
479 null,
480 .m, .mi, .m1, .mc, .mr, .mri, .mrc => if (inst.ops[0].isSegmentRegister())
481 switch (inst.ops[0]) {
482 .reg => |reg| reg,
483 .mem => |mem| mem.base().reg,
484 else => unreachable,
485 }
486 else
487 null,
488 .vm, .vmi, .rvm, .rvmr, .rvmi, .mvr, .rmv => unreachable,
489 };
490 if (segment_override) |seg| {
491 legacy.setSegmentOverride(seg);
492 }
493
494 try encoder.legacyPrefixes(legacy);
495 }
496
497 fn encodeRexPrefix(inst: Instruction, encoder: anytype) !void {
498 const op_en = inst.encoding.data.op_en;
499
500 var rex = Rex{};
501 rex.present = inst.encoding.data.mode == .rex;
502 rex.w = inst.encoding.data.mode == .long;
503
504 switch (op_en) {
505 .z, .i, .zi, .ii, .ia, .fd, .td, .d => {},
506 .o, .oz, .oi => rex.b = inst.ops[0].reg.enc() & 0b01000 != 0,
507 .zo => rex.b = inst.ops[1].reg.enc() & 0b01000 != 0,
508 .m, .mi, .m1, .mc, .mr, .rm, .rmi, .mri, .mrc, .rm0, .rmv => {
509 const r_op = switch (op_en) {
510 .rm, .rmi, .rm0, .rmv => inst.ops[0],
511 .mr, .mri, .mrc => inst.ops[1],
512 else => .none,
513 };
514 const r_op_base_ext_enc = r_op.baseExtEnc();
515 rex.r = r_op_base_ext_enc & 0b01 != 0;
516 assert(r_op_base_ext_enc & 0b10 == 0);
517
518 const b_x_op = switch (op_en) {
519 .rm, .rmi, .rm0 => inst.ops[1],
520 .m, .mi, .m1, .mc, .mr, .mri, .mrc => inst.ops[0],
521 else => unreachable,
522 };
523 const b_x_op_base_ext_enc = b_x_op.baseExtEnc();
524 rex.b = b_x_op_base_ext_enc & 0b01 != 0;
525 assert(b_x_op_base_ext_enc & 0b10 == 0);
526 const b_x_op_index_ext_enc = b_x_op.indexExtEnc();
527 rex.x = b_x_op_index_ext_enc & 0b01 != 0;
528 assert(b_x_op_index_ext_enc & 0b10 == 0);
529 },
530 .vm, .vmi, .rvm, .rvmr, .rvmi, .mvr => unreachable,
531 }
532
533 try encoder.rex(rex);
534 }
535
536 fn encodeVexPrefix(inst: Instruction, encoder: anytype) !void {
537 const op_en = inst.encoding.data.op_en;
538 const opc = inst.encoding.opcode();
539 const mand_pre = inst.encoding.mandatoryPrefix();
540
541 var vex = Vex{};
542
543 vex.w = inst.encoding.data.mode.isLong();
544
545 switch (op_en) {
546 .z, .i, .zi, .ii, .ia, .fd, .td, .d, .o, .oz, .oi, .zo => unreachable,
547 .m, .mi, .m1, .mc, .mr, .rm, .rmi, .mri, .mrc, .rm0, .vm, .vmi, .rvm, .rvmr, .rvmi, .mvr, .rmv => {
548 const r_op = switch (op_en) {
549 .rm, .rmi, .rm0, .rvm, .rvmr, .rvmi, .rmv => inst.ops[0],
550 .mr, .mri, .mrc => inst.ops[1],
551 .mvr => inst.ops[2],
552 .m, .mi, .m1, .mc, .vm, .vmi => .none,
553 else => unreachable,
554 };
555 const r_op_base_ext_enc = r_op.baseExtEnc();
556 vex.r = r_op_base_ext_enc & 0b01 != 0;
557 assert(r_op_base_ext_enc & 0b10 == 0);
558
559 const b_x_op = switch (op_en) {
560 .rm, .rmi, .rm0, .vm, .vmi, .rmv => inst.ops[1],
561 .m, .mi, .m1, .mc, .mr, .mri, .mrc, .mvr => inst.ops[0],
562 .rvm, .rvmr, .rvmi => inst.ops[2],
563 else => unreachable,
564 };
565 const b_x_op_base_ext_enc = b_x_op.baseExtEnc();
566 vex.b = b_x_op_base_ext_enc & 0b01 != 0;
567 assert(b_x_op_base_ext_enc & 0b10 == 0);
568 const b_x_op_index_ext_enc = b_x_op.indexExtEnc();
569 vex.x = b_x_op_index_ext_enc & 0b01 != 0;
570 assert(b_x_op_index_ext_enc & 0b10 == 0);
571 },
572 }
573
574 vex.l = inst.encoding.data.mode.isVecLong();
575
576 vex.p = if (mand_pre) |mand| switch (mand) {
577 0x66 => .@"66",
578 0xf2 => .f2,
579 0xf3 => .f3,
580 else => unreachable,
581 } else .none;
582
583 const leading: usize = if (mand_pre) |_| 1 else 0;
584 assert(opc[leading] == 0x0f);
585 vex.m = switch (opc[leading + 1]) {
586 else => .@"0f",
587 0x38 => .@"0f38",
588 0x3a => .@"0f3a",
589 };
590
591 switch (op_en) {
592 else => {},
593 .vm, .vmi => vex.v = inst.ops[0].reg,
594 .rvm, .rvmr, .rvmi => vex.v = inst.ops[1].reg,
595 .rmv => vex.v = inst.ops[2].reg,
596 }
597
598 try encoder.vex(vex);
599 }
600
601 fn encodeMandatoryPrefix(inst: Instruction, encoder: anytype) !void {
602 const prefix = inst.encoding.mandatoryPrefix() orelse return;
603 try encoder.opcode_1byte(prefix);
604 }
605
606 fn encodeMemory(encoding: Encoding, mem: Memory, operand: Operand, encoder: anytype) !void {
607 const operand_enc: u3 = switch (operand) {
608 .reg => |reg| @truncate(reg.enc()),
609 .none => encoding.modRmExt(),
610 else => unreachable,
611 };
612
613 switch (mem) {
614 .moffs => unreachable,
615 .sib => |sib| switch (sib.base) {
616 .none, .table => {
617 try encoder.modRm_SIBDisp0(operand_enc);
618 if (mem.scaleIndex()) |si| {
619 const scale = math.log2_int(u4, si.scale);
620 try encoder.sib_scaleIndexDisp32(scale, @truncate(si.index.enc()));
621 } else {
622 try encoder.sib_disp32();
623 }
624 try encoder.disp32(sib.disp);
625 },
626 .reg => |base| switch (base.class()) {
627 .segment => {
628 // TODO audit this wrt SIB
629 try encoder.modRm_SIBDisp0(operand_enc);
630 if (mem.scaleIndex()) |si| {
631 const scale = math.log2_int(u4, si.scale);
632 try encoder.sib_scaleIndexDisp32(scale, @truncate(si.index.enc()));
633 } else {
634 try encoder.sib_disp32();
635 }
636 try encoder.disp32(sib.disp);
637 },
638 .general_purpose => {
639 const dst: u3 = @truncate(base.enc());
640 const src = operand_enc;
641 if (dst == 4 or mem.scaleIndex() != null) {
642 if (sib.disp == 0 and dst != 5) {
643 try encoder.modRm_SIBDisp0(src);
644 if (mem.scaleIndex()) |si| {
645 const scale = math.log2_int(u4, si.scale);
646 try encoder.sib_scaleIndexBase(scale, @truncate(si.index.enc()), dst);
647 } else {
648 try encoder.sib_base(dst);
649 }
650 } else if (math.cast(i8, sib.disp)) |_| {
651 try encoder.modRm_SIBDisp8(src);
652 if (mem.scaleIndex()) |si| {
653 const scale = math.log2_int(u4, si.scale);
654 try encoder.sib_scaleIndexBaseDisp8(scale, @truncate(si.index.enc()), dst);
655 } else {
656 try encoder.sib_baseDisp8(dst);
657 }
658 try encoder.disp8(@as(i8, @truncate(sib.disp)));
659 } else {
660 try encoder.modRm_SIBDisp32(src);
661 if (mem.scaleIndex()) |si| {
662 const scale = math.log2_int(u4, si.scale);
663 try encoder.sib_scaleIndexBaseDisp32(scale, @truncate(si.index.enc()), dst);
664 } else {
665 try encoder.sib_baseDisp32(dst);
666 }
667 try encoder.disp32(sib.disp);
668 }
669 } else {
670 if (sib.disp == 0 and dst != 5) {
671 try encoder.modRm_indirectDisp0(src, dst);
672 } else if (math.cast(i8, sib.disp)) |_| {
673 try encoder.modRm_indirectDisp8(src, dst);
674 try encoder.disp8(@as(i8, @truncate(sib.disp)));
675 } else {
676 try encoder.modRm_indirectDisp32(src, dst);
677 try encoder.disp32(sib.disp);
678 }
679 }
680 },
681 else => unreachable,
682 },
683 .frame => if (@TypeOf(encoder).options.allow_frame_locs) {
684 try encoder.modRm_indirectDisp32(operand_enc, 0);
685 try encoder.disp32(undefined);
686 } else return error.CannotEncode,
687 .nav, .uav, .lazy_sym, .extern_func => if (@TypeOf(encoder).options.allow_symbols) {
688 try encoder.modRm_indirectDisp32(operand_enc, 0);
689 try encoder.disp32(undefined);
690 } else return error.CannotEncode,
691 .rip_inst => {
692 try encoder.modRm_RIPDisp32(operand_enc);
693 try encoder.disp32(sib.disp);
694 },
695 },
696 .rip => |rip| {
697 try encoder.modRm_RIPDisp32(operand_enc);
698 try encoder.disp32(rip.disp);
699 },
700 }
701 }
702
703 fn encodeImm(imm: Immediate, enc_op: Encoding.Op, encoder: anytype) !void {
704 const bit_size = enc_op.immBitSize();
705 const raw = imm.asUnsigned(bit_size);
706 switch (bit_size) {
707 8 => try encoder.imm8(@as(u8, @intCast(raw))),
708 16 => try encoder.imm16(@as(u16, @intCast(raw))),
709 32 => try encoder.imm32(@as(u32, @intCast(raw))),
710 64 => try encoder.imm64(raw),
711 else => unreachable,
712 }
713 }
714};
715
716pub const LegacyPrefixes = packed struct {
717 /// LOCK
718 prefix_f0: bool = false,
719 /// REPNZ, REPNE, REP, Scalar Double-precision
720 prefix_f2: bool = false,
721 /// REPZ, REPE, REP, Scalar Single-precision
722 prefix_f3: bool = false,
723
724 /// CS segment override or Branch not taken
725 prefix_2e: bool = false,
726 /// SS segment override
727 prefix_36: bool = false,
728 /// ES segment override
729 prefix_26: bool = false,
730 /// FS segment override
731 prefix_64: bool = false,
732 /// GS segment override
733 prefix_65: bool = false,
734
735 /// Branch taken
736 prefix_3e: bool = false,
737
738 /// Address size override (enables 16 bit address size)
739 prefix_67: bool = false,
740
741 /// Operand size override (enables 16 bit operation)
742 prefix_66: bool = false,
743
744 padding: u5 = 0,
745
746 pub fn setSegmentOverride(self: *LegacyPrefixes, reg: Register) void {
747 assert(reg.isClass(.segment));
748 switch (reg) {
749 .cs => self.prefix_2e = true,
750 .ss => self.prefix_36 = true,
751 .es => self.prefix_26 = true,
752 .fs => self.prefix_64 = true,
753 .gs => self.prefix_65 = true,
754 .ds => {},
755 else => unreachable,
756 }
757 }
758
759 pub fn set16BitOverride(self: *LegacyPrefixes) void {
760 self.prefix_66 = true;
761 }
762};
763
764pub const Options = struct { allow_frame_locs: bool = false, allow_symbols: bool = false };
765
766fn Encoder(comptime opts: Options) type {
767 return struct {
768 w: *Writer,
769
770 const Self = @This();
771 pub const options = opts;
772
773 // --------
774 // Prefixes
775 // --------
776
777 /// Encodes legacy prefixes
778 pub fn legacyPrefixes(self: Self, prefixes: LegacyPrefixes) !void {
779 if (@as(u16, @bitCast(prefixes)) != 0) {
780 // Hopefully this path isn't taken very often, so we'll do it the slow way for now
781
782 // LOCK
783 if (prefixes.prefix_f0) try self.w.writeByte(0xf0);
784 // REPNZ, REPNE, REP, Scalar Double-precision
785 if (prefixes.prefix_f2) try self.w.writeByte(0xf2);
786 // REPZ, REPE, REP, Scalar Single-precision
787 if (prefixes.prefix_f3) try self.w.writeByte(0xf3);
788
789 // CS segment override or Branch not taken
790 if (prefixes.prefix_2e) try self.w.writeByte(0x2e);
791 // DS segment override
792 if (prefixes.prefix_36) try self.w.writeByte(0x36);
793 // ES segment override
794 if (prefixes.prefix_26) try self.w.writeByte(0x26);
795 // FS segment override
796 if (prefixes.prefix_64) try self.w.writeByte(0x64);
797 // GS segment override
798 if (prefixes.prefix_65) try self.w.writeByte(0x65);
799
800 // Branch taken
801 if (prefixes.prefix_3e) try self.w.writeByte(0x3e);
802
803 // Operand size override
804 if (prefixes.prefix_66) try self.w.writeByte(0x66);
805
806 // Address size override
807 if (prefixes.prefix_67) try self.w.writeByte(0x67);
808 }
809 }
810
811 /// Use 16 bit operand size
812 ///
813 /// Note that this flag is overridden by REX.W, if both are present.
814 pub fn prefix16BitMode(self: Self) !void {
815 try self.w.writeByte(0x66);
816 }
817
818 /// Encodes a REX prefix byte given all the fields
819 ///
820 /// Use this byte whenever you need 64 bit operation,
821 /// or one of reg, index, r/m, base, or opcode-reg might be extended.
822 ///
823 /// See struct `Rex` for a description of each field.
824 pub fn rex(self: Self, fields: Rex) !void {
825 if (!fields.present and !fields.isSet()) return;
826
827 var byte: u8 = 0b0100_0000;
828
829 if (fields.w) byte |= 0b1000;
830 if (fields.r) byte |= 0b0100;
831 if (fields.x) byte |= 0b0010;
832 if (fields.b) byte |= 0b0001;
833
834 try self.w.writeByte(byte);
835 }
836
837 /// Encodes a VEX prefix given all the fields
838 ///
839 /// See struct `Vex` for a description of each field.
840 pub fn vex(self: Self, fields: Vex) !void {
841 if (fields.is3Byte()) {
842 try self.w.writeByte(0b1100_0100);
843
844 try self.w.writeByte(
845 @as(u8, ~@intFromBool(fields.r)) << 7 |
846 @as(u8, ~@intFromBool(fields.x)) << 6 |
847 @as(u8, ~@intFromBool(fields.b)) << 5 |
848 @as(u8, @backingInt(fields.m)) << 0,
849 );
850
851 try self.w.writeByte(
852 @as(u8, @intFromBool(fields.w)) << 7 |
853 @as(u8, ~@as(u4, @intCast(fields.v.enc()))) << 3 |
854 @as(u8, @intFromBool(fields.l)) << 2 |
855 @as(u8, @backingInt(fields.p)) << 0,
856 );
857 } else {
858 try self.w.writeByte(0b1100_0101);
859 try self.w.writeByte(
860 @as(u8, ~@intFromBool(fields.r)) << 7 |
861 @as(u8, ~@as(u4, @intCast(fields.v.enc()))) << 3 |
862 @as(u8, @intFromBool(fields.l)) << 2 |
863 @as(u8, @backingInt(fields.p)) << 0,
864 );
865 }
866 }
867
868 // ------
869 // Opcode
870 // ------
871
872 /// Encodes a 1 byte opcode
873 pub fn opcode_1byte(self: Self, opcode: u8) !void {
874 try self.w.writeByte(opcode);
875 }
876
877 /// Encodes a 2 byte opcode
878 ///
879 /// e.g. IMUL has the opcode 0x0f 0xaf, so you use
880 ///
881 /// encoder.opcode_2byte(0x0f, 0xaf);
882 pub fn opcode_2byte(self: Self, prefix: u8, opcode: u8) !void {
883 try self.w.writeAll(&.{ prefix, opcode });
884 }
885
886 /// Encodes a 3 byte opcode
887 ///
888 /// e.g. MOVSD has the opcode 0xf2 0x0f 0x10
889 ///
890 /// encoder.opcode_3byte(0xf2, 0x0f, 0x10);
891 pub fn opcode_3byte(self: Self, prefix_1: u8, prefix_2: u8, opcode: u8) !void {
892 try self.w.writeAll(&.{ prefix_1, prefix_2, opcode });
893 }
894
895 /// Encodes a 1 byte opcode with a reg field
896 ///
897 /// Remember to add a REX prefix byte if reg is extended!
898 pub fn opcode_withReg(self: Self, opcode: u8, reg: u3) !void {
899 assert(opcode & 0b111 == 0);
900 try self.w.writeByte(opcode | reg);
901 }
902
903 // ------
904 // ModR/M
905 // ------
906
907 /// Construct a ModR/M byte given all the fields
908 ///
909 /// Remember to add a REX prefix byte if reg or rm are extended!
910 pub fn modRm(self: Self, mod: u2, reg_or_opx: u3, rm: u3) !void {
911 try self.w.writeByte(@as(u8, mod) << 6 | @as(u8, reg_or_opx) << 3 | rm);
912 }
913
914 /// Construct a ModR/M byte using direct r/m addressing
915 /// r/m effective address: r/m
916 ///
917 /// Note reg's effective address is always just reg for the ModR/M byte.
918 /// Remember to add a REX prefix byte if reg or rm are extended!
919 pub fn modRm_direct(self: Self, reg_or_opx: u3, rm: u3) !void {
920 try self.modRm(0b11, reg_or_opx, rm);
921 }
922
923 /// Construct a ModR/M byte using indirect r/m addressing
924 /// r/m effective address: [r/m]
925 ///
926 /// Note reg's effective address is always just reg for the ModR/M byte.
927 /// Remember to add a REX prefix byte if reg or rm are extended!
928 pub fn modRm_indirectDisp0(self: Self, reg_or_opx: u3, rm: u3) !void {
929 assert(rm != 4 and rm != 5);
930 try self.modRm(0b00, reg_or_opx, rm);
931 }
932
933 /// Construct a ModR/M byte using indirect SIB addressing
934 /// r/m effective address: [SIB]
935 ///
936 /// Note reg's effective address is always just reg for the ModR/M byte.
937 /// Remember to add a REX prefix byte if reg or rm are extended!
938 pub fn modRm_SIBDisp0(self: Self, reg_or_opx: u3) !void {
939 try self.modRm(0b00, reg_or_opx, 0b100);
940 }
941
942 /// Construct a ModR/M byte using RIP-relative addressing
943 /// r/m effective address: [RIP + disp32]
944 ///
945 /// Note reg's effective address is always just reg for the ModR/M byte.
946 /// Remember to add a REX prefix byte if reg or rm are extended!
947 pub fn modRm_RIPDisp32(self: Self, reg_or_opx: u3) !void {
948 try self.modRm(0b00, reg_or_opx, 0b101);
949 }
950
951 /// Construct a ModR/M byte using indirect r/m with a 8bit displacement
952 /// r/m effective address: [r/m + disp8]
953 ///
954 /// Note reg's effective address is always just reg for the ModR/M byte.
955 /// Remember to add a REX prefix byte if reg or rm are extended!
956 pub fn modRm_indirectDisp8(self: Self, reg_or_opx: u3, rm: u3) !void {
957 assert(rm != 4);
958 try self.modRm(0b01, reg_or_opx, rm);
959 }
960
961 /// Construct a ModR/M byte using indirect SIB with a 8bit displacement
962 /// r/m effective address: [SIB + disp8]
963 ///
964 /// Note reg's effective address is always just reg for the ModR/M byte.
965 /// Remember to add a REX prefix byte if reg or rm are extended!
966 pub fn modRm_SIBDisp8(self: Self, reg_or_opx: u3) !void {
967 try self.modRm(0b01, reg_or_opx, 0b100);
968 }
969
970 /// Construct a ModR/M byte using indirect r/m with a 32bit displacement
971 /// r/m effective address: [r/m + disp32]
972 ///
973 /// Note reg's effective address is always just reg for the ModR/M byte.
974 /// Remember to add a REX prefix byte if reg or rm are extended!
975 pub fn modRm_indirectDisp32(self: Self, reg_or_opx: u3, rm: u3) !void {
976 assert(rm != 4);
977 try self.modRm(0b10, reg_or_opx, rm);
978 }
979
980 /// Construct a ModR/M byte using indirect SIB with a 32bit displacement
981 /// r/m effective address: [SIB + disp32]
982 ///
983 /// Note reg's effective address is always just reg for the ModR/M byte.
984 /// Remember to add a REX prefix byte if reg or rm are extended!
985 pub fn modRm_SIBDisp32(self: Self, reg_or_opx: u3) !void {
986 try self.modRm(0b10, reg_or_opx, 0b100);
987 }
988
989 // ---
990 // SIB
991 // ---
992
993 /// Construct a SIB byte given all the fields
994 ///
995 /// Remember to add a REX prefix byte if index or base are extended!
996 pub fn sib(self: Self, scale: u2, index: u3, base: u3) !void {
997 try self.w.writeByte(@as(u8, scale) << 6 | @as(u8, index) << 3 | base);
998 }
999
1000 /// Construct a SIB byte with scale * index + base, no frills.
1001 /// r/m effective address: [base + scale * index]
1002 ///
1003 /// Remember to add a REX prefix byte if index or base are extended!
1004 pub fn sib_scaleIndexBase(self: Self, scale: u2, index: u3, base: u3) !void {
1005 assert(base != 5);
1006
1007 try self.sib(scale, index, base);
1008 }
1009
1010 /// Construct a SIB byte with scale * index + disp32
1011 /// r/m effective address: [scale * index + disp32]
1012 ///
1013 /// Remember to add a REX prefix byte if index or base are extended!
1014 pub fn sib_scaleIndexDisp32(self: Self, scale: u2, index: u3) !void {
1015 // scale is actually ignored
1016 // index = 4 means no index if and only if we haven't extended the register
1017 // TODO enforce this
1018 // base = 5 means no base, if mod == 0.
1019 try self.sib(scale, index, 5);
1020 }
1021
1022 /// Construct a SIB byte with just base
1023 /// r/m effective address: [base]
1024 ///
1025 /// Remember to add a REX prefix byte if index or base are extended!
1026 pub fn sib_base(self: Self, base: u3) !void {
1027 assert(base != 5);
1028
1029 // scale is actually ignored
1030 // index = 4 means no index
1031 try self.sib(0, 4, base);
1032 }
1033
1034 /// Construct a SIB byte with just disp32
1035 /// r/m effective address: [disp32]
1036 ///
1037 /// Remember to add a REX prefix byte if index or base are extended!
1038 pub fn sib_disp32(self: Self) !void {
1039 // scale is actually ignored
1040 // index = 4 means no index
1041 // base = 5 means no base, if mod == 0.
1042 try self.sib(0, 4, 5);
1043 }
1044
1045 /// Construct a SIB byte with scale * index + base + disp8
1046 /// r/m effective address: [base + scale * index + disp8]
1047 ///
1048 /// Remember to add a REX prefix byte if index or base are extended!
1049 pub fn sib_scaleIndexBaseDisp8(self: Self, scale: u2, index: u3, base: u3) !void {
1050 try self.sib(scale, index, base);
1051 }
1052
1053 /// Construct a SIB byte with base + disp8, no index
1054 /// r/m effective address: [base + disp8]
1055 ///
1056 /// Remember to add a REX prefix byte if index or base are extended!
1057 pub fn sib_baseDisp8(self: Self, base: u3) !void {
1058 // scale is ignored
1059 // index = 4 means no index
1060 try self.sib(0, 4, base);
1061 }
1062
1063 /// Construct a SIB byte with scale * index + base + disp32
1064 /// r/m effective address: [base + scale * index + disp32]
1065 ///
1066 /// Remember to add a REX prefix byte if index or base are extended!
1067 pub fn sib_scaleIndexBaseDisp32(self: Self, scale: u2, index: u3, base: u3) !void {
1068 try self.sib(scale, index, base);
1069 }
1070
1071 /// Construct a SIB byte with base + disp32, no index
1072 /// r/m effective address: [base + disp32]
1073 ///
1074 /// Remember to add a REX prefix byte if index or base are extended!
1075 pub fn sib_baseDisp32(self: Self, base: u3) !void {
1076 // scale is ignored
1077 // index = 4 means no index
1078 try self.sib(0, 4, base);
1079 }
1080
1081 // -------------------------
1082 // Trivial (no bit fiddling)
1083 // -------------------------
1084
1085 /// Encode an 8 bit displacement
1086 ///
1087 /// It is sign-extended to 64 bits by the cpu.
1088 pub fn disp8(self: Self, disp: i8) !void {
1089 try self.w.writeByte(@as(u8, @bitCast(disp)));
1090 }
1091
1092 /// Encode an 32 bit displacement
1093 ///
1094 /// It is sign-extended to 64 bits by the cpu.
1095 pub fn disp32(self: Self, disp: i32) !void {
1096 try self.w.writeInt(i32, disp, .little);
1097 }
1098
1099 /// Encode an 8 bit immediate
1100 ///
1101 /// It is sign-extended to 64 bits by the cpu.
1102 pub fn imm8(self: Self, imm: u8) !void {
1103 try self.w.writeByte(imm);
1104 }
1105
1106 /// Encode an 16 bit immediate
1107 ///
1108 /// It is sign-extended to 64 bits by the cpu.
1109 pub fn imm16(self: Self, imm: u16) !void {
1110 try self.w.writeInt(u16, imm, .little);
1111 }
1112
1113 /// Encode an 32 bit immediate
1114 ///
1115 /// It is sign-extended to 64 bits by the cpu.
1116 pub fn imm32(self: Self, imm: u32) !void {
1117 try self.w.writeInt(u32, imm, .little);
1118 }
1119
1120 /// Encode an 64 bit immediate
1121 ///
1122 /// It is sign-extended to 64 bits by the cpu.
1123 pub fn imm64(self: Self, imm: u64) !void {
1124 try self.w.writeInt(u64, imm, .little);
1125 }
1126 };
1127}
1128
1129pub const Rex = struct {
1130 w: bool = false,
1131 r: bool = false,
1132 x: bool = false,
1133 b: bool = false,
1134 present: bool = false,
1135
1136 pub fn isSet(rex: Rex) bool {
1137 return rex.w or rex.r or rex.x or rex.b;
1138 }
1139};
1140
1141pub const Vex = struct {
1142 w: bool = false,
1143 r: bool = false,
1144 x: bool = false,
1145 b: bool = false,
1146 l: bool = false,
1147 p: enum(u2) {
1148 none = 0b00,
1149 @"66" = 0b01,
1150 f3 = 0b10,
1151 f2 = 0b11,
1152 } = .none,
1153 m: enum(u5) {
1154 @"0f" = 0b0_0001,
1155 @"0f38" = 0b0_0010,
1156 @"0f3a" = 0b0_0011,
1157 _,
1158 } = .@"0f",
1159 v: Register = .ymm0,
1160
1161 pub fn is3Byte(vex: Vex) bool {
1162 return vex.w or vex.x or vex.b or vex.m != .@"0f";
1163 }
1164};
1165
1166// Tests
1167fn expectEqualHexStrings(expected: []const u8, given: []const u8, assembly: []const u8) !void {
1168 assert(expected.len > 0);
1169 if (std.mem.eql(u8, expected, given)) return;
1170 const expected_fmt = try std.fmt.allocPrint(testing.allocator, "{x}", .{expected});
1171 defer testing.allocator.free(expected_fmt);
1172 const given_fmt = try std.fmt.allocPrint(testing.allocator, "{x}", .{given});
1173 defer testing.allocator.free(given_fmt);
1174 const idx = std.mem.findDiff(u8, expected_fmt, given_fmt).?;
1175 const padding = try testing.allocator.alloc(u8, idx + 5);
1176 defer testing.allocator.free(padding);
1177 @memset(padding, ' ');
1178 std.debug.print("\nASM: {s}\nEXP: {s}\nGIV: {s}\n{s}^ -- first differing byte\n", .{
1179 assembly,
1180 expected_fmt,
1181 given_fmt,
1182 padding,
1183 });
1184 return error.TestFailed;
1185}
1186
1187const TestEncode = struct {
1188 buffer: [32]u8 = undefined,
1189 index: usize = 0,
1190
1191 fn encode(
1192 enc: *TestEncode,
1193 mnemonic: Instruction.Mnemonic,
1194 ops: []const Instruction.Operand,
1195 ) !void {
1196 var writer: std.Io.Writer = .fixed(&enc.buffer);
1197 const inst: Instruction = try .new(.none, mnemonic, ops);
1198 try inst.encode(&writer, .{});
1199 enc.index = writer.bufferedLen();
1200 }
1201
1202 fn code(enc: TestEncode) []const u8 {
1203 return enc.buffer[0..enc.index];
1204 }
1205};
1206
1207test "encode" {
1208 var buf = std.array_list.Managed(u8).init(testing.allocator);
1209 defer buf.deinit();
1210
1211 const inst: Instruction = try .new(.none, .mov, &.{
1212 .{ .reg = .rbx },
1213 .{ .imm = .u(4) },
1214 });
1215 try inst.encode(buf.writer(), .{});
1216 try testing.expectEqualSlices(u8, &.{ 0x48, 0xc7, 0xc3, 0x4, 0x0, 0x0, 0x0 }, buf.items);
1217}
1218
1219test "lower I encoding" {
1220 var enc = TestEncode{};
1221
1222 try enc.encode(.push, &.{
1223 .{ .imm = .u(0x10) },
1224 });
1225 try expectEqualHexStrings("\x6A\x10", enc.code(), "push 0x10");
1226
1227 try enc.encode(.push, &.{
1228 .{ .imm = .u(0x1000) },
1229 });
1230 try expectEqualHexStrings("\x66\x68\x00\x10", enc.code(), "push 0x1000");
1231
1232 try enc.encode(.push, &.{
1233 .{ .imm = .u(0x10000000) },
1234 });
1235 try expectEqualHexStrings("\x68\x00\x00\x00\x10", enc.code(), "push 0x10000000");
1236
1237 try enc.encode(.adc, &.{
1238 .{ .reg = .rax },
1239 .{ .imm = .u(0x10000000) },
1240 });
1241 try expectEqualHexStrings("\x48\x15\x00\x00\x00\x10", enc.code(), "adc rax, 0x10000000");
1242
1243 try enc.encode(.add, &.{
1244 .{ .reg = .al },
1245 .{ .imm = .u(0x10) },
1246 });
1247 try expectEqualHexStrings("\x04\x10", enc.code(), "add al, 0x10");
1248
1249 try enc.encode(.add, &.{
1250 .{ .reg = .rax },
1251 .{ .imm = .u(0x10) },
1252 });
1253 try expectEqualHexStrings("\x48\x83\xC0\x10", enc.code(), "add rax, 0x10");
1254
1255 try enc.encode(.sbb, &.{
1256 .{ .reg = .ax },
1257 .{ .imm = .u(0x10) },
1258 });
1259 try expectEqualHexStrings("\x66\x1D\x10\x00", enc.code(), "sbb ax, 0x10");
1260
1261 try enc.encode(.xor, &.{
1262 .{ .reg = .al },
1263 .{ .imm = .u(0x10) },
1264 });
1265 try expectEqualHexStrings("\x34\x10", enc.code(), "xor al, 0x10");
1266}
1267
1268test "lower MI encoding" {
1269 var enc = TestEncode{};
1270
1271 try enc.encode(.mov, &.{
1272 .{ .reg = .r12 },
1273 .{ .imm = .u(0x1000) },
1274 });
1275 try expectEqualHexStrings("\x49\xC7\xC4\x00\x10\x00\x00", enc.code(), "mov r12, 0x1000");
1276
1277 try enc.encode(.mov, &.{
1278 .{ .mem = Instruction.Memory.initSib(.byte, .{ .base = .{ .reg = .r12 } }) },
1279 .{ .imm = .u(0x10) },
1280 });
1281 try expectEqualHexStrings("\x41\xC6\x04\x24\x10", enc.code(), "mov BYTE PTR [r12], 0x10");
1282
1283 try enc.encode(.mov, &.{
1284 .{ .reg = .r12 },
1285 .{ .imm = .u(0x1000) },
1286 });
1287 try expectEqualHexStrings("\x49\xC7\xC4\x00\x10\x00\x00", enc.code(), "mov r12, 0x1000");
1288
1289 try enc.encode(.mov, &.{
1290 .{ .reg = .r12 },
1291 .{ .imm = .u(0x1000) },
1292 });
1293 try expectEqualHexStrings("\x49\xC7\xC4\x00\x10\x00\x00", enc.code(), "mov r12, 0x1000");
1294
1295 try enc.encode(.mov, &.{
1296 .{ .reg = .rax },
1297 .{ .imm = .u(0x10) },
1298 });
1299 try expectEqualHexStrings("\x48\xc7\xc0\x10\x00\x00\x00", enc.code(), "mov rax, 0x10");
1300
1301 try enc.encode(.mov, &.{
1302 .{ .mem = Instruction.Memory.initSib(.dword, .{ .base = .{ .reg = .r11 } }) },
1303 .{ .imm = .u(0x10) },
1304 });
1305 try expectEqualHexStrings("\x41\xc7\x03\x10\x00\x00\x00", enc.code(), "mov DWORD PTR [r11], 0x10");
1306
1307 try enc.encode(.mov, &.{
1308 .{ .mem = Instruction.Memory.initRip(.qword, 0x10) },
1309 .{ .imm = .u(0x10) },
1310 });
1311 try expectEqualHexStrings(
1312 "\x48\xC7\x05\x10\x00\x00\x00\x10\x00\x00\x00",
1313 enc.code(),
1314 "mov QWORD PTR [rip + 0x10], 0x10",
1315 );
1316
1317 try enc.encode(.mov, &.{
1318 .{ .mem = Instruction.Memory.initSib(.qword, .{ .base = .{ .reg = .rbp }, .disp = -8 }) },
1319 .{ .imm = .u(0x10) },
1320 });
1321 try expectEqualHexStrings("\x48\xc7\x45\xf8\x10\x00\x00\x00", enc.code(), "mov QWORD PTR [rbp - 8], 0x10");
1322
1323 try enc.encode(.mov, &.{
1324 .{ .mem = Instruction.Memory.initSib(.word, .{ .base = .{ .reg = .rbp }, .disp = -2 }) },
1325 .{ .imm = .s(-16) },
1326 });
1327 try expectEqualHexStrings("\x66\xC7\x45\xFE\xF0\xFF", enc.code(), "mov WORD PTR [rbp - 2], -16");
1328
1329 try enc.encode(.mov, &.{
1330 .{ .mem = Instruction.Memory.initSib(.byte, .{ .base = .{ .reg = .rbp }, .disp = -1 }) },
1331 .{ .imm = .u(0x10) },
1332 });
1333 try expectEqualHexStrings("\xC6\x45\xFF\x10", enc.code(), "mov BYTE PTR [rbp - 1], 0x10");
1334
1335 try enc.encode(.mov, &.{
1336 .{ .mem = Instruction.Memory.initSib(.qword, .{
1337 .base = .{ .reg = .ds },
1338 .disp = 0x10000000,
1339 .scale_index = .{ .scale = 2, .index = .rcx },
1340 }) },
1341 .{ .imm = .u(0x10) },
1342 });
1343 try expectEqualHexStrings(
1344 "\x48\xC7\x04\x4D\x00\x00\x00\x10\x10\x00\x00\x00",
1345 enc.code(),
1346 "mov QWORD PTR [rcx*2 + 0x10000000], 0x10",
1347 );
1348
1349 try enc.encode(.adc, &.{
1350 .{ .mem = Instruction.Memory.initSib(.byte, .{ .base = .{ .reg = .rbp }, .disp = -0x10 }) },
1351 .{ .imm = .u(0x10) },
1352 });
1353 try expectEqualHexStrings("\x80\x55\xF0\x10", enc.code(), "adc BYTE PTR [rbp - 0x10], 0x10");
1354
1355 try enc.encode(.adc, &.{
1356 .{ .mem = Instruction.Memory.initRip(.qword, 0) },
1357 .{ .imm = .u(0x10) },
1358 });
1359 try expectEqualHexStrings("\x48\x83\x15\x00\x00\x00\x00\x10", enc.code(), "adc QWORD PTR [rip], 0x10");
1360
1361 try enc.encode(.adc, &.{
1362 .{ .reg = .rax },
1363 .{ .imm = .u(0x10) },
1364 });
1365 try expectEqualHexStrings("\x48\x83\xD0\x10", enc.code(), "adc rax, 0x10");
1366
1367 try enc.encode(.add, &.{
1368 .{ .mem = Instruction.Memory.initSib(.dword, .{ .base = .{ .reg = .rdx }, .disp = -8 }) },
1369 .{ .imm = .u(0x10) },
1370 });
1371 try expectEqualHexStrings("\x83\x42\xF8\x10", enc.code(), "add DWORD PTR [rdx - 8], 0x10");
1372
1373 try enc.encode(.add, &.{
1374 .{ .reg = .rax },
1375 .{ .imm = .u(0x10) },
1376 });
1377 try expectEqualHexStrings("\x48\x83\xC0\x10", enc.code(), "add rax, 0x10");
1378
1379 try enc.encode(.add, &.{
1380 .{ .mem = Instruction.Memory.initSib(.qword, .{ .base = .{ .reg = .rbp }, .disp = -0x10 }) },
1381 .{ .imm = .s(-0x10) },
1382 });
1383 try expectEqualHexStrings("\x48\x83\x45\xF0\xF0", enc.code(), "add QWORD PTR [rbp - 0x10], -0x10");
1384
1385 try enc.encode(.@"and", &.{
1386 .{ .mem = Instruction.Memory.initSib(.dword, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) },
1387 .{ .imm = .u(0x10) },
1388 });
1389 try expectEqualHexStrings(
1390 "\x83\x24\x25\x00\x00\x00\x10\x10",
1391 enc.code(),
1392 "and DWORD PTR ds:0x10000000, 0x10",
1393 );
1394
1395 try enc.encode(.@"and", &.{
1396 .{ .mem = Instruction.Memory.initSib(.dword, .{ .base = .{ .reg = .es }, .disp = 0x10000000 }) },
1397 .{ .imm = .u(0x10) },
1398 });
1399 try expectEqualHexStrings(
1400 "\x26\x83\x24\x25\x00\x00\x00\x10\x10",
1401 enc.code(),
1402 "and DWORD PTR es:0x10000000, 0x10",
1403 );
1404
1405 try enc.encode(.@"and", &.{
1406 .{ .mem = Instruction.Memory.initSib(.dword, .{ .base = .{ .reg = .r12 }, .disp = 0x10000000 }) },
1407 .{ .imm = .u(0x10) },
1408 });
1409 try expectEqualHexStrings(
1410 "\x41\x83\xA4\x24\x00\x00\x00\x10\x10",
1411 enc.code(),
1412 "and DWORD PTR [r12 + 0x10000000], 0x10",
1413 );
1414
1415 try enc.encode(.sub, &.{
1416 .{ .mem = Instruction.Memory.initSib(.dword, .{ .base = .{ .reg = .r11 }, .disp = 0x10000000 }) },
1417 .{ .imm = .u(0x10) },
1418 });
1419 try expectEqualHexStrings(
1420 "\x41\x83\xAB\x00\x00\x00\x10\x10",
1421 enc.code(),
1422 "sub DWORD PTR [r11 + 0x10000000], 0x10",
1423 );
1424}
1425
1426test "lower RM encoding" {
1427 var enc = TestEncode{};
1428
1429 try enc.encode(.mov, &.{
1430 .{ .reg = .rax },
1431 .{ .mem = Instruction.Memory.initSib(.qword, .{ .base = .{ .reg = .r11 } }) },
1432 });
1433 try expectEqualHexStrings("\x49\x8b\x03", enc.code(), "mov rax, QWORD PTR [r11]");
1434
1435 try enc.encode(.mov, &.{
1436 .{ .reg = .rbx },
1437 .{ .mem = Instruction.Memory.initSib(.qword, .{ .base = .{ .reg = .ds }, .disp = 0x10 }) },
1438 });
1439 try expectEqualHexStrings("\x48\x8B\x1C\x25\x10\x00\x00\x00", enc.code(), "mov rbx, QWORD PTR ds:0x10");
1440
1441 try enc.encode(.mov, &.{
1442 .{ .reg = .rax },
1443 .{ .mem = Instruction.Memory.initSib(.qword, .{ .base = .{ .reg = .rbp }, .disp = -4 }) },
1444 });
1445 try expectEqualHexStrings("\x48\x8B\x45\xFC", enc.code(), "mov rax, QWORD PTR [rbp - 4]");
1446
1447 try enc.encode(.mov, &.{
1448 .{ .reg = .rax },
1449 .{ .mem = Instruction.Memory.initSib(.qword, .{
1450 .base = .{ .reg = .rbp },
1451 .scale_index = .{ .scale = 1, .index = .rcx },
1452 .disp = -8,
1453 }) },
1454 });
1455 try expectEqualHexStrings("\x48\x8B\x44\x0D\xF8", enc.code(), "mov rax, QWORD PTR [rbp + rcx*1 - 8]");
1456
1457 try enc.encode(.mov, &.{
1458 .{ .reg = .eax },
1459 .{ .mem = Instruction.Memory.initSib(.dword, .{
1460 .base = .{ .reg = .rbp },
1461 .scale_index = .{ .scale = 4, .index = .rdx },
1462 .disp = -4,
1463 }) },
1464 });
1465 try expectEqualHexStrings("\x8B\x44\x95\xFC", enc.code(), "mov eax, dword ptr [rbp + rdx*4 - 4]");
1466
1467 try enc.encode(.mov, &.{
1468 .{ .reg = .rax },
1469 .{ .mem = Instruction.Memory.initSib(.qword, .{
1470 .base = .{ .reg = .rbp },
1471 .scale_index = .{ .scale = 8, .index = .rcx },
1472 .disp = -8,
1473 }) },
1474 });
1475 try expectEqualHexStrings("\x48\x8B\x44\xCD\xF8", enc.code(), "mov rax, QWORD PTR [rbp + rcx*8 - 8]");
1476
1477 try enc.encode(.mov, &.{
1478 .{ .reg = .r8b },
1479 .{ .mem = Instruction.Memory.initSib(.byte, .{
1480 .base = .{ .reg = .rsi },
1481 .scale_index = .{ .scale = 1, .index = .rcx },
1482 .disp = -24,
1483 }) },
1484 });
1485 try expectEqualHexStrings("\x44\x8A\x44\x0E\xE8", enc.code(), "mov r8b, BYTE PTR [rsi + rcx*1 - 24]");
1486
1487 // TODO this mnemonic needs cleanup as some prefixes are obsolete.
1488 try enc.encode(.mov, &.{
1489 .{ .reg = .rax },
1490 .{ .reg = .cs },
1491 });
1492 try expectEqualHexStrings("\x48\x8C\xC8", enc.code(), "mov rax, cs");
1493
1494 try enc.encode(.mov, &.{
1495 .{ .mem = Instruction.Memory.initSib(.word, .{ .base = .{ .reg = .rbp }, .disp = -16 }) },
1496 .{ .reg = .fs },
1497 });
1498 try expectEqualHexStrings("\x8C\x65\xF0", enc.code(), "mov WORD PTR [rbp - 16], fs");
1499
1500 try enc.encode(.mov, &.{
1501 .{ .reg = .r12w },
1502 .{ .reg = .cs },
1503 });
1504 try expectEqualHexStrings("\x66\x41\x8C\xCC", enc.code(), "mov r12w, cs");
1505
1506 try enc.encode(.movsx, &.{
1507 .{ .reg = .eax },
1508 .{ .reg = .bx },
1509 });
1510 try expectEqualHexStrings("\x0F\xBF\xC3", enc.code(), "movsx eax, bx");
1511
1512 try enc.encode(.movsx, &.{
1513 .{ .reg = .eax },
1514 .{ .reg = .bl },
1515 });
1516 try expectEqualHexStrings("\x0F\xBE\xC3", enc.code(), "movsx eax, bl");
1517
1518 try enc.encode(.movsx, &.{
1519 .{ .reg = .ax },
1520 .{ .reg = .bl },
1521 });
1522 try expectEqualHexStrings("\x66\x0F\xBE\xC3", enc.code(), "movsx ax, bl");
1523
1524 try enc.encode(.movsx, &.{
1525 .{ .reg = .eax },
1526 .{ .mem = Instruction.Memory.initSib(.word, .{ .base = .{ .reg = .rbp } }) },
1527 });
1528 try expectEqualHexStrings("\x0F\xBF\x45\x00", enc.code(), "movsx eax, BYTE PTR [rbp]");
1529
1530 try enc.encode(.movsx, &.{
1531 .{ .reg = .eax },
1532 .{ .mem = Instruction.Memory.initSib(.byte, .{ .scale_index = .{ .index = .rax, .scale = 2 } }) },
1533 });
1534 try expectEqualHexStrings("\x0F\xBE\x04\x45\x00\x00\x00\x00", enc.code(), "movsx eax, BYTE PTR [rax * 2]");
1535
1536 try enc.encode(.movsx, &.{
1537 .{ .reg = .ax },
1538 .{ .mem = Instruction.Memory.initRip(.byte, 0x10) },
1539 });
1540 try expectEqualHexStrings("\x66\x0F\xBE\x05\x10\x00\x00\x00", enc.code(), "movsx ax, BYTE PTR [rip + 0x10]");
1541
1542 try enc.encode(.movsx, &.{
1543 .{ .reg = .rax },
1544 .{ .reg = .bx },
1545 });
1546 try expectEqualHexStrings("\x48\x0F\xBF\xC3", enc.code(), "movsx rax, bx");
1547
1548 try enc.encode(.movsxd, &.{
1549 .{ .reg = .rax },
1550 .{ .reg = .ebx },
1551 });
1552 try expectEqualHexStrings("\x48\x63\xC3", enc.code(), "movsxd rax, ebx");
1553
1554 try enc.encode(.lea, &.{
1555 .{ .reg = .rax },
1556 .{ .mem = Instruction.Memory.initRip(.qword, 0x10) },
1557 });
1558 try expectEqualHexStrings("\x48\x8D\x05\x10\x00\x00\x00", enc.code(), "lea rax, QWORD PTR [rip + 0x10]");
1559
1560 try enc.encode(.lea, &.{
1561 .{ .reg = .rax },
1562 .{ .mem = Instruction.Memory.initRip(.dword, 0x10) },
1563 });
1564 try expectEqualHexStrings("\x48\x8D\x05\x10\x00\x00\x00", enc.code(), "lea rax, DWORD PTR [rip + 0x10]");
1565
1566 try enc.encode(.lea, &.{
1567 .{ .reg = .eax },
1568 .{ .mem = Instruction.Memory.initRip(.dword, 0x10) },
1569 });
1570 try expectEqualHexStrings("\x8D\x05\x10\x00\x00\x00", enc.code(), "lea eax, DWORD PTR [rip + 0x10]");
1571
1572 try enc.encode(.lea, &.{
1573 .{ .reg = .eax },
1574 .{ .mem = Instruction.Memory.initRip(.word, 0x10) },
1575 });
1576 try expectEqualHexStrings("\x8D\x05\x10\x00\x00\x00", enc.code(), "lea eax, WORD PTR [rip + 0x10]");
1577
1578 try enc.encode(.lea, &.{
1579 .{ .reg = .ax },
1580 .{ .mem = Instruction.Memory.initRip(.byte, 0x10) },
1581 });
1582 try expectEqualHexStrings("\x66\x8D\x05\x10\x00\x00\x00", enc.code(), "lea ax, BYTE PTR [rip + 0x10]");
1583
1584 try enc.encode(.lea, &.{
1585 .{ .reg = .rsi },
1586 .{ .mem = Instruction.Memory.initSib(.qword, .{
1587 .base = .{ .reg = .rbp },
1588 .scale_index = .{ .scale = 1, .index = .rcx },
1589 }) },
1590 });
1591 try expectEqualHexStrings("\x48\x8D\x74\x0D\x00", enc.code(), "lea rsi, QWORD PTR [rbp + rcx*1 + 0]");
1592
1593 try enc.encode(.add, &.{
1594 .{ .reg = .r11 },
1595 .{ .mem = Instruction.Memory.initSib(.qword, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) },
1596 });
1597 try expectEqualHexStrings("\x4C\x03\x1C\x25\x00\x00\x00\x10", enc.code(), "add r11, QWORD PTR ds:0x10000000");
1598
1599 try enc.encode(.add, &.{
1600 .{ .reg = .r12b },
1601 .{ .mem = Instruction.Memory.initSib(.byte, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) },
1602 });
1603 try expectEqualHexStrings("\x44\x02\x24\x25\x00\x00\x00\x10", enc.code(), "add r11b, BYTE PTR ds:0x10000000");
1604
1605 try enc.encode(.add, &.{
1606 .{ .reg = .r12b },
1607 .{ .mem = Instruction.Memory.initSib(.byte, .{ .base = .{ .reg = .fs }, .disp = 0x10000000 }) },
1608 });
1609 try expectEqualHexStrings("\x64\x44\x02\x24\x25\x00\x00\x00\x10", enc.code(), "add r11b, BYTE PTR fs:0x10000000");
1610
1611 try enc.encode(.sub, &.{
1612 .{ .reg = .r11 },
1613 .{ .mem = Instruction.Memory.initSib(.qword, .{ .base = .{ .reg = .r13 }, .disp = 0x10000000 }) },
1614 });
1615 try expectEqualHexStrings("\x4D\x2B\x9D\x00\x00\x00\x10", enc.code(), "sub r11, QWORD PTR [r13 + 0x10000000]");
1616
1617 try enc.encode(.sub, &.{
1618 .{ .reg = .r11 },
1619 .{ .mem = Instruction.Memory.initSib(.qword, .{ .base = .{ .reg = .r12 }, .disp = 0x10000000 }) },
1620 });
1621 try expectEqualHexStrings("\x4D\x2B\x9C\x24\x00\x00\x00\x10", enc.code(), "sub r11, QWORD PTR [r12 + 0x10000000]");
1622
1623 try enc.encode(.imul, &.{
1624 .{ .reg = .r11 },
1625 .{ .reg = .r12 },
1626 });
1627 try expectEqualHexStrings("\x4D\x0F\xAF\xDC", enc.code(), "mov r11, r12");
1628}
1629
1630test "lower RMI encoding" {
1631 var enc = TestEncode{};
1632
1633 try enc.encode(.imul, &.{
1634 .{ .reg = .r11 },
1635 .{ .reg = .r12 },
1636 .{ .imm = .s(-2) },
1637 });
1638 try expectEqualHexStrings("\x4D\x6B\xDC\xFE", enc.code(), "imul r11, r12, -2");
1639
1640 try enc.encode(.imul, &.{
1641 .{ .reg = .r11 },
1642 .{ .mem = Instruction.Memory.initRip(.qword, -16) },
1643 .{ .imm = .s(-1024) },
1644 });
1645 try expectEqualHexStrings(
1646 "\x4C\x69\x1D\xF0\xFF\xFF\xFF\x00\xFC\xFF\xFF",
1647 enc.code(),
1648 "imul r11, QWORD PTR [rip - 16], -1024",
1649 );
1650
1651 try enc.encode(.imul, &.{
1652 .{ .reg = .bx },
1653 .{ .mem = Instruction.Memory.initSib(.word, .{ .base = .{ .reg = .rbp }, .disp = -16 }) },
1654 .{ .imm = .s(-1024) },
1655 });
1656 try expectEqualHexStrings(
1657 "\x66\x69\x5D\xF0\x00\xFC",
1658 enc.code(),
1659 "imul bx, WORD PTR [rbp - 16], -1024",
1660 );
1661
1662 try enc.encode(.imul, &.{
1663 .{ .reg = .bx },
1664 .{ .mem = Instruction.Memory.initSib(.word, .{ .base = .{ .reg = .rbp }, .disp = -16 }) },
1665 .{ .imm = .u(1024) },
1666 });
1667 try expectEqualHexStrings(
1668 "\x66\x69\x5D\xF0\x00\x04",
1669 enc.code(),
1670 "imul bx, WORD PTR [rbp - 16], 1024",
1671 );
1672}
1673
1674test "lower MR encoding" {
1675 var enc = TestEncode{};
1676
1677 try enc.encode(.mov, &.{
1678 .{ .reg = .rax },
1679 .{ .reg = .rbx },
1680 });
1681 try expectEqualHexStrings("\x48\x89\xD8", enc.code(), "mov rax, rbx");
1682
1683 try enc.encode(.mov, &.{
1684 .{ .mem = Instruction.Memory.initSib(.qword, .{ .base = .{ .reg = .rbp }, .disp = -4 }) },
1685 .{ .reg = .r11 },
1686 });
1687 try expectEqualHexStrings("\x4c\x89\x5d\xfc", enc.code(), "mov QWORD PTR [rbp - 4], r11");
1688
1689 try enc.encode(.mov, &.{
1690 .{ .mem = Instruction.Memory.initRip(.qword, 0x10) },
1691 .{ .reg = .r12 },
1692 });
1693 try expectEqualHexStrings("\x4C\x89\x25\x10\x00\x00\x00", enc.code(), "mov QWORD PTR [rip + 0x10], r12");
1694
1695 try enc.encode(.mov, &.{
1696 .{ .mem = Instruction.Memory.initSib(.qword, .{
1697 .base = .{ .reg = .r11 },
1698 .scale_index = .{ .scale = 2, .index = .r12 },
1699 .disp = 0x10,
1700 }) },
1701 .{ .reg = .r13 },
1702 });
1703 try expectEqualHexStrings("\x4F\x89\x6C\x63\x10", enc.code(), "mov QWORD PTR [r11 + 2 * r12 + 0x10], r13");
1704
1705 try enc.encode(.mov, &.{
1706 .{ .mem = Instruction.Memory.initRip(.word, -0x10) },
1707 .{ .reg = .r12w },
1708 });
1709 try expectEqualHexStrings("\x66\x44\x89\x25\xF0\xFF\xFF\xFF", enc.code(), "mov WORD PTR [rip - 0x10], r12w");
1710
1711 try enc.encode(.mov, &.{
1712 .{ .mem = Instruction.Memory.initSib(.byte, .{
1713 .base = .{ .reg = .r11 },
1714 .scale_index = .{ .scale = 2, .index = .r12 },
1715 .disp = 0x10,
1716 }) },
1717 .{ .reg = .r13b },
1718 });
1719 try expectEqualHexStrings("\x47\x88\x6C\x63\x10", enc.code(), "mov BYTE PTR [r11 + 2 * r12 + 0x10], r13b");
1720
1721 try enc.encode(.add, &.{
1722 .{ .mem = Instruction.Memory.initSib(.byte, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) },
1723 .{ .reg = .r12b },
1724 });
1725 try expectEqualHexStrings("\x44\x00\x24\x25\x00\x00\x00\x10", enc.code(), "add BYTE PTR ds:0x10000000, r12b");
1726
1727 try enc.encode(.add, &.{
1728 .{ .mem = Instruction.Memory.initSib(.dword, .{ .base = .{ .reg = .ds }, .disp = 0x10000000 }) },
1729 .{ .reg = .r12d },
1730 });
1731 try expectEqualHexStrings("\x44\x01\x24\x25\x00\x00\x00\x10", enc.code(), "add DWORD PTR [ds:0x10000000], r12d");
1732
1733 try enc.encode(.add, &.{
1734 .{ .mem = Instruction.Memory.initSib(.dword, .{ .base = .{ .reg = .gs }, .disp = 0x10000000 }) },
1735 .{ .reg = .r12d },
1736 });
1737 try expectEqualHexStrings("\x65\x44\x01\x24\x25\x00\x00\x00\x10", enc.code(), "add DWORD PTR [gs:0x10000000], r12d");
1738
1739 try enc.encode(.sub, &.{
1740 .{ .mem = Instruction.Memory.initSib(.qword, .{ .base = .{ .reg = .r11 }, .disp = 0x10000000 }) },
1741 .{ .reg = .r12 },
1742 });
1743 try expectEqualHexStrings("\x4D\x29\xA3\x00\x00\x00\x10", enc.code(), "sub QWORD PTR [r11 + 0x10000000], r12");
1744}
1745
1746test "lower M encoding" {
1747 var enc = TestEncode{};
1748
1749 try enc.encode(.call, &.{
1750 .{ .reg = .r12 },
1751 });
1752 try expectEqualHexStrings("\x41\xFF\xD4", enc.code(), "call r12");
1753
1754 try enc.encode(.call, &.{
1755 .{ .mem = Instruction.Memory.initSib(.qword, .{ .base = .{ .reg = .r12 } }) },
1756 });
1757 try expectEqualHexStrings("\x41\xFF\x14\x24", enc.code(), "call QWORD PTR [r12]");
1758
1759 try enc.encode(.call, &.{
1760 .{ .mem = Instruction.Memory.initSib(.qword, .{
1761 .base = .none,
1762 .scale_index = .{ .index = .r11, .scale = 2 },
1763 }) },
1764 });
1765 try expectEqualHexStrings("\x42\xFF\x14\x5D\x00\x00\x00\x00", enc.code(), "call QWORD PTR [r11 * 2]");
1766
1767 try enc.encode(.call, &.{
1768 .{ .mem = Instruction.Memory.initSib(.qword, .{
1769 .base = .none,
1770 .scale_index = .{ .index = .r12, .scale = 2 },
1771 }) },
1772 });
1773 try expectEqualHexStrings("\x42\xFF\x14\x65\x00\x00\x00\x00", enc.code(), "call QWORD PTR [r12 * 2]");
1774
1775 try enc.encode(.call, &.{
1776 .{ .mem = Instruction.Memory.initSib(.qword, .{ .base = .{ .reg = .gs } }) },
1777 });
1778 try expectEqualHexStrings("\x65\xFF\x14\x25\x00\x00\x00\x00", enc.code(), "call gs:0x0");
1779
1780 try enc.encode(.call, &.{
1781 .{ .imm = .s(0) },
1782 });
1783 try expectEqualHexStrings("\xE8\x00\x00\x00\x00", enc.code(), "call 0x0");
1784
1785 try enc.encode(.push, &.{
1786 .{ .mem = Instruction.Memory.initSib(.qword, .{ .base = .{ .reg = .rbp } }) },
1787 });
1788 try expectEqualHexStrings("\xFF\x75\x00", enc.code(), "push QWORD PTR [rbp]");
1789
1790 try enc.encode(.push, &.{
1791 .{ .mem = Instruction.Memory.initSib(.word, .{ .base = .{ .reg = .rbp } }) },
1792 });
1793 try expectEqualHexStrings("\x66\xFF\x75\x00", enc.code(), "push QWORD PTR [rbp]");
1794
1795 try enc.encode(.pop, &.{
1796 .{ .mem = Instruction.Memory.initRip(.qword, 0) },
1797 });
1798 try expectEqualHexStrings("\x8F\x05\x00\x00\x00\x00", enc.code(), "pop QWORD PTR [rip]");
1799
1800 try enc.encode(.pop, &.{
1801 .{ .mem = Instruction.Memory.initRip(.word, 0) },
1802 });
1803 try expectEqualHexStrings("\x66\x8F\x05\x00\x00\x00\x00", enc.code(), "pop WORD PTR [rbp]");
1804
1805 try enc.encode(.imul, &.{
1806 .{ .reg = .rax },
1807 });
1808 try expectEqualHexStrings("\x48\xF7\xE8", enc.code(), "imul rax");
1809
1810 try enc.encode(.imul, &.{
1811 .{ .reg = .r12 },
1812 });
1813 try expectEqualHexStrings("\x49\xF7\xEC", enc.code(), "imul r12");
1814}
1815
1816test "lower O encoding" {
1817 var enc = TestEncode{};
1818
1819 try enc.encode(.push, &.{
1820 .{ .reg = .rax },
1821 });
1822 try expectEqualHexStrings("\x50", enc.code(), "push rax");
1823
1824 try enc.encode(.push, &.{
1825 .{ .reg = .r12w },
1826 });
1827 try expectEqualHexStrings("\x66\x41\x54", enc.code(), "push r12w");
1828
1829 try enc.encode(.pop, &.{
1830 .{ .reg = .r12 },
1831 });
1832 try expectEqualHexStrings("\x41\x5c", enc.code(), "pop r12");
1833}
1834
1835test "lower OI encoding" {
1836 var enc = TestEncode{};
1837
1838 try enc.encode(.mov, &.{
1839 .{ .reg = .rax },
1840 .{ .imm = .u(0x1000000000000000) },
1841 });
1842 try expectEqualHexStrings(
1843 "\x48\xB8\x00\x00\x00\x00\x00\x00\x00\x10",
1844 enc.code(),
1845 "movabs rax, 0x1000000000000000",
1846 );
1847
1848 try enc.encode(.mov, &.{
1849 .{ .reg = .r11 },
1850 .{ .imm = .u(0x1000000000000000) },
1851 });
1852 try expectEqualHexStrings(
1853 "\x49\xBB\x00\x00\x00\x00\x00\x00\x00\x10",
1854 enc.code(),
1855 "movabs r11, 0x1000000000000000",
1856 );
1857
1858 try enc.encode(.mov, &.{
1859 .{ .reg = .r11d },
1860 .{ .imm = .u(0x10000000) },
1861 });
1862 try expectEqualHexStrings("\x41\xBB\x00\x00\x00\x10", enc.code(), "mov r11d, 0x10000000");
1863
1864 try enc.encode(.mov, &.{
1865 .{ .reg = .r11w },
1866 .{ .imm = .u(0x1000) },
1867 });
1868 try expectEqualHexStrings("\x66\x41\xBB\x00\x10", enc.code(), "mov r11w, 0x1000");
1869
1870 try enc.encode(.mov, &.{
1871 .{ .reg = .r11b },
1872 .{ .imm = .u(0x10) },
1873 });
1874 try expectEqualHexStrings("\x41\xB3\x10", enc.code(), "mov r11b, 0x10");
1875}
1876
1877test "lower FD/TD encoding" {
1878 var enc = TestEncode{};
1879
1880 try enc.encode(.mov, &.{
1881 .{ .reg = .rax },
1882 .{ .mem = Instruction.Memory.initMoffs(.cs, 0x10) },
1883 });
1884 try expectEqualHexStrings("\x2E\x48\xA1\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs rax, cs:0x10");
1885
1886 try enc.encode(.mov, &.{
1887 .{ .reg = .eax },
1888 .{ .mem = Instruction.Memory.initMoffs(.fs, 0x10) },
1889 });
1890 try expectEqualHexStrings("\x64\xA1\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs eax, fs:0x10");
1891
1892 try enc.encode(.mov, &.{
1893 .{ .reg = .ax },
1894 .{ .mem = Instruction.Memory.initMoffs(.gs, 0x10) },
1895 });
1896 try expectEqualHexStrings("\x65\x66\xA1\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs ax, gs:0x10");
1897
1898 try enc.encode(.mov, &.{
1899 .{ .reg = .al },
1900 .{ .mem = Instruction.Memory.initMoffs(.ds, 0x10) },
1901 });
1902 try expectEqualHexStrings("\xA0\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs al, ds:0x10");
1903
1904 try enc.encode(.mov, &.{
1905 .{ .mem = Instruction.Memory.initMoffs(.cs, 0x10) },
1906 .{ .reg = .rax },
1907 });
1908 try expectEqualHexStrings("\x2E\x48\xA3\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs cs:0x10, rax");
1909
1910 try enc.encode(.mov, &.{
1911 .{ .mem = Instruction.Memory.initMoffs(.fs, 0x10) },
1912 .{ .reg = .eax },
1913 });
1914 try expectEqualHexStrings("\x64\xA3\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs fs:0x10, eax");
1915
1916 try enc.encode(.mov, &.{
1917 .{ .mem = Instruction.Memory.initMoffs(.gs, 0x10) },
1918 .{ .reg = .ax },
1919 });
1920 try expectEqualHexStrings("\x65\x66\xA3\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs gs:0x10, ax");
1921
1922 try enc.encode(.mov, &.{
1923 .{ .mem = Instruction.Memory.initMoffs(.ds, 0x10) },
1924 .{ .reg = .al },
1925 });
1926 try expectEqualHexStrings("\xA2\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs ds:0x10, al");
1927}
1928
1929test "lower NP encoding" {
1930 var enc = TestEncode{};
1931
1932 try enc.encode(.int3, &.{});
1933 try expectEqualHexStrings("\xCC", enc.code(), "int3");
1934
1935 try enc.encode(.nop, &.{});
1936 try expectEqualHexStrings("\x90", enc.code(), "nop");
1937
1938 try enc.encode(.ret, &.{});
1939 try expectEqualHexStrings("\xC3", enc.code(), "ret");
1940
1941 try enc.encode(.syscall, &.{});
1942 try expectEqualHexStrings("\x0f\x05", enc.code(), "syscall");
1943}
1944
1945fn invalidInstruction(mnemonic: Instruction.Mnemonic, ops: []const Instruction.Operand) !void {
1946 const err: Instruction = .new(.none, mnemonic, ops);
1947 try testing.expectError(error.InvalidInstruction, err);
1948}
1949
1950test "invalid instruction" {
1951 try invalidInstruction(.call, &.{
1952 .{ .reg = .eax },
1953 });
1954 try invalidInstruction(.call, &.{
1955 .{ .reg = .ax },
1956 });
1957 try invalidInstruction(.call, &.{
1958 .{ .reg = .al },
1959 });
1960 try invalidInstruction(.call, &.{
1961 .{ .mem = Instruction.Memory.initRip(.dword, 0) },
1962 });
1963 try invalidInstruction(.call, &.{
1964 .{ .mem = Instruction.Memory.initRip(.word, 0) },
1965 });
1966 try invalidInstruction(.call, &.{
1967 .{ .mem = Instruction.Memory.initRip(.byte, 0) },
1968 });
1969 try invalidInstruction(.mov, &.{
1970 .{ .mem = Instruction.Memory.initRip(.word, 0x10) },
1971 .{ .reg = .r12 },
1972 });
1973 try invalidInstruction(.lea, &.{
1974 .{ .reg = .rax },
1975 .{ .reg = .rbx },
1976 });
1977 try invalidInstruction(.lea, &.{
1978 .{ .reg = .al },
1979 .{ .mem = Instruction.Memory.initRip(.byte, 0) },
1980 });
1981 try invalidInstruction(.pop, &.{
1982 .{ .reg = .r12b },
1983 });
1984 try invalidInstruction(.pop, &.{
1985 .{ .reg = .r12d },
1986 });
1987 try invalidInstruction(.push, &.{
1988 .{ .reg = .r12b },
1989 });
1990 try invalidInstruction(.push, &.{
1991 .{ .reg = .r12d },
1992 });
1993 try invalidInstruction(.push, &.{
1994 .{ .imm = .u(0x1000000000000000) },
1995 });
1996}
1997
1998fn cannotEncode(mnemonic: Instruction.Mnemonic, ops: []const Instruction.Operand) !void {
1999 try testing.expectError(error.CannotEncode, .new(.none, mnemonic, ops));
2000}
2001
2002test "cannot encode" {
2003 try cannotEncode(.@"test", &.{
2004 .{ .mem = Instruction.Memory.initSib(.byte, .{ .base = .{ .reg = .r12 } }) },
2005 .{ .reg = .ah },
2006 });
2007 try cannotEncode(.@"test", &.{
2008 .{ .reg = .r11b },
2009 .{ .reg = .bh },
2010 });
2011 try cannotEncode(.mov, &.{
2012 .{ .reg = .sil },
2013 .{ .reg = .ah },
2014 });
2015}
2016
2017const Assembler = struct {
2018 it: Tokenizer,
2019
2020 const Tokenizer = struct {
2021 input: []const u8,
2022 pos: usize = 0,
2023
2024 const Error = error{InvalidToken};
2025
2026 const Token = struct {
2027 id: Id,
2028 start: usize,
2029 end: usize,
2030
2031 const Id = enum {
2032 eof,
2033
2034 space,
2035 new_line,
2036
2037 colon,
2038 comma,
2039 open_br,
2040 close_br,
2041 plus,
2042 minus,
2043 star,
2044
2045 string,
2046 numeral,
2047 };
2048 };
2049
2050 const Iterator = struct {};
2051
2052 fn next(it: *Tokenizer) !Token {
2053 var result = Token{
2054 .id = .eof,
2055 .start = it.pos,
2056 .end = it.pos,
2057 };
2058
2059 var state: enum {
2060 start,
2061 space,
2062 new_line,
2063 string,
2064 numeral,
2065 numeral_hex,
2066 } = .start;
2067
2068 while (it.pos < it.input.len) : (it.pos += 1) {
2069 const ch = it.input[it.pos];
2070 switch (state) {
2071 .start => switch (ch) {
2072 ',' => {
2073 result.id = .comma;
2074 it.pos += 1;
2075 break;
2076 },
2077 ':' => {
2078 result.id = .colon;
2079 it.pos += 1;
2080 break;
2081 },
2082 '[' => {
2083 result.id = .open_br;
2084 it.pos += 1;
2085 break;
2086 },
2087 ']' => {
2088 result.id = .close_br;
2089 it.pos += 1;
2090 break;
2091 },
2092 '+' => {
2093 result.id = .plus;
2094 it.pos += 1;
2095 break;
2096 },
2097 '-' => {
2098 result.id = .minus;
2099 it.pos += 1;
2100 break;
2101 },
2102 '*' => {
2103 result.id = .star;
2104 it.pos += 1;
2105 break;
2106 },
2107 ' ', '\t' => state = .space,
2108 '\n', '\r' => state = .new_line,
2109 'a'...'z', 'A'...'Z' => state = .string,
2110 '0'...'9' => state = .numeral,
2111 else => return error.InvalidToken,
2112 },
2113
2114 .space => switch (ch) {
2115 ' ', '\t' => {},
2116 else => {
2117 result.id = .space;
2118 break;
2119 },
2120 },
2121
2122 .new_line => switch (ch) {
2123 '\n', '\r', ' ', '\t' => {},
2124 else => {
2125 result.id = .new_line;
2126 break;
2127 },
2128 },
2129
2130 .string => switch (ch) {
2131 'a'...'z', 'A'...'Z', '0'...'9' => {},
2132 else => {
2133 result.id = .string;
2134 break;
2135 },
2136 },
2137
2138 .numeral => switch (ch) {
2139 'x' => state = .numeral_hex,
2140 '0'...'9' => {},
2141 else => {
2142 result.id = .numeral;
2143 break;
2144 },
2145 },
2146
2147 .numeral_hex => switch (ch) {
2148 'a'...'f' => {},
2149 '0'...'9' => {},
2150 else => {
2151 result.id = .numeral;
2152 break;
2153 },
2154 },
2155 }
2156 }
2157
2158 if (it.pos >= it.input.len) {
2159 switch (state) {
2160 .string => result.id = .string,
2161 .numeral, .numeral_hex => result.id = .numeral,
2162 else => {},
2163 }
2164 }
2165
2166 result.end = it.pos;
2167 return result;
2168 }
2169
2170 fn seekTo(it: *Tokenizer, pos: usize) void {
2171 it.pos = pos;
2172 }
2173 };
2174
2175 pub fn init(input: []const u8) Assembler {
2176 return .{
2177 .it = Tokenizer{ .input = input },
2178 };
2179 }
2180
2181 pub fn assemble(as: *Assembler, w: *Writer) !void {
2182 while (try as.next()) |parsed_inst| {
2183 const inst: Instruction = try .new(.none, parsed_inst.mnemonic, &parsed_inst.ops);
2184 try inst.encode(w, .{});
2185 }
2186 }
2187
2188 const ParseResult = struct {
2189 mnemonic: Instruction.Mnemonic,
2190 ops: [4]Instruction.Operand,
2191 };
2192
2193 const ParseError = error{
2194 UnexpectedToken,
2195 InvalidMnemonic,
2196 InvalidOperand,
2197 InvalidRegister,
2198 InvalidPtrSize,
2199 InvalidMemoryOperand,
2200 InvalidScaleIndex,
2201 } || Tokenizer.Error || std.fmt.ParseIntError;
2202
2203 fn next(as: *Assembler) ParseError!?ParseResult {
2204 try as.skip(2, .{ .space, .new_line });
2205 const mnemonic_tok = as.expect(.string) catch |err| switch (err) {
2206 error.UnexpectedToken => return if (try as.peek() == .eof) null else err,
2207 else => return err,
2208 };
2209 const mnemonic = mnemonicFromString(as.source(mnemonic_tok)) orelse
2210 return error.InvalidMnemonic;
2211 try as.skip(1, .{.space});
2212
2213 const rules = .{
2214 .{},
2215 .{.register},
2216 .{.memory},
2217 .{.immediate},
2218 .{ .register, .register },
2219 .{ .register, .memory },
2220 .{ .memory, .register },
2221 .{ .register, .immediate },
2222 .{ .memory, .immediate },
2223 .{ .register, .register, .immediate },
2224 .{ .register, .memory, .immediate },
2225 };
2226
2227 const pos = as.it.pos;
2228 inline for (rules) |rule| {
2229 var ops = [4]Instruction.Operand{ .none, .none, .none, .none };
2230 if (as.parseOperandRule(rule, &ops)) {
2231 return .{
2232 .mnemonic = mnemonic,
2233 .ops = ops,
2234 };
2235 } else |_| {
2236 as.it.seekTo(pos);
2237 }
2238 }
2239
2240 return error.InvalidOperand;
2241 }
2242
2243 fn source(as: *Assembler, token: Tokenizer.Token) []const u8 {
2244 return as.it.input[token.start..token.end];
2245 }
2246
2247 fn peek(as: *Assembler) Tokenizer.Error!Tokenizer.Token.Id {
2248 const pos = as.it.pos;
2249 const next_tok = try as.it.next();
2250 const id = next_tok.id;
2251 as.it.seekTo(pos);
2252 return id;
2253 }
2254
2255 fn expect(as: *Assembler, id: Tokenizer.Token.Id) ParseError!Tokenizer.Token {
2256 const next_tok_id = try as.peek();
2257 if (next_tok_id == id) return as.it.next();
2258 return error.UnexpectedToken;
2259 }
2260
2261 fn skip(as: *Assembler, comptime num: comptime_int, tok_ids: [num]Tokenizer.Token.Id) Tokenizer.Error!void {
2262 outer: while (true) {
2263 const pos = as.it.pos;
2264 const next_tok = try as.it.next();
2265 inline for (tok_ids) |tok_id| {
2266 if (next_tok.id == tok_id) continue :outer;
2267 }
2268 as.it.seekTo(pos);
2269 break;
2270 }
2271 }
2272
2273 fn mnemonicFromString(bytes: []const u8) ?Instruction.Mnemonic {
2274 const ti = @typeInfo(Instruction.Mnemonic).@"enum";
2275 inline for (ti.field_names) |field_name| {
2276 if (std.mem.eql(u8, bytes, field_name)) {
2277 return @field(Instruction.Mnemonic, field_name);
2278 }
2279 }
2280 return null;
2281 }
2282
2283 fn parseOperandRule(as: *Assembler, rule: anytype, ops: *[4]Instruction.Operand) ParseError!void {
2284 inline for (rule, 0..) |cond, i| {
2285 comptime assert(i < 4);
2286 if (i > 0) {
2287 _ = try as.expect(.comma);
2288 try as.skip(1, .{.space});
2289 }
2290 if (@typeInfo(@TypeOf(cond)) != .enum_literal) {
2291 @compileError("invalid condition in the rule: " ++ @typeName(@TypeOf(cond)));
2292 }
2293 switch (cond) {
2294 .register => {
2295 const reg_tok = try as.expect(.string);
2296 const reg = registerFromString(as.source(reg_tok)) orelse
2297 return error.InvalidOperand;
2298 ops[i] = .{ .reg = reg };
2299 },
2300 .memory => {
2301 const mem = try as.parseMemory();
2302 ops[i] = .{ .mem = mem };
2303 },
2304 .immediate => {
2305 const is_neg = if (as.expect(.minus)) |_| true else |_| false;
2306 const imm_tok = try as.expect(.numeral);
2307 const imm: Instruction.Immediate = if (is_neg) blk: {
2308 const imm = try std.fmt.parseInt(i32, as.source(imm_tok), 0);
2309 break :blk .{ .signed = imm * -1 };
2310 } else .{ .unsigned = try std.fmt.parseInt(u64, as.source(imm_tok), 0) };
2311 ops[i] = .{ .imm = imm };
2312 },
2313 else => @compileError("unhandled enum literal " ++ @tagName(cond)),
2314 }
2315 try as.skip(1, .{.space});
2316 }
2317
2318 try as.skip(1, .{.space});
2319 const tok = try as.it.next();
2320 switch (tok.id) {
2321 .new_line, .eof => {},
2322 else => return error.InvalidOperand,
2323 }
2324 }
2325
2326 fn registerFromString(bytes: []const u8) ?Register {
2327 const ti = @typeInfo(Register).@"enum";
2328 inline for (ti.field_names) |field_name| {
2329 if (std.mem.eql(u8, bytes, field_name)) {
2330 return @field(Register, field_name);
2331 }
2332 }
2333 return null;
2334 }
2335
2336 fn parseMemory(as: *Assembler) ParseError!Instruction.Memory {
2337 const ptr_size: ?Instruction.Memory.PtrSize = blk: {
2338 const pos = as.it.pos;
2339 const ptr_size = as.parsePtrSize() catch |err| switch (err) {
2340 error.UnexpectedToken => {
2341 as.it.seekTo(pos);
2342 break :blk null;
2343 },
2344 else => return err,
2345 };
2346 break :blk ptr_size;
2347 };
2348
2349 try as.skip(1, .{.space});
2350
2351 // Supported rules and orderings.
2352 const rules = .{
2353 .{ .open_br, .general_purpose, .close_br }, // [ general_purpose ]
2354 .{ .open_br, .general_purpose, .plus, .disp, .close_br }, // [ general_purpose + disp ]
2355 .{ .open_br, .general_purpose, .minus, .disp, .close_br }, // [ general_purpose - disp ]
2356 .{ .open_br, .disp, .plus, .general_purpose, .close_br }, // [ disp + general_purpose ]
2357 .{ .open_br, .general_purpose, .plus, .index, .close_br }, // [ general_purpose + index ]
2358 .{ .open_br, .general_purpose, .plus, .index, .star, .scale, .close_br }, // [ general_purpose + index * scale ]
2359 .{ .open_br, .index, .star, .scale, .plus, .general_purpose, .close_br }, // [ index * scale + general_purpose ]
2360 .{ .open_br, .general_purpose, .plus, .index, .star, .scale, .plus, .disp, .close_br }, // [ general_purpose + index * scale + disp ]
2361 .{ .open_br, .general_purpose, .plus, .index, .star, .scale, .minus, .disp, .close_br }, // [ general_purpose + index * scale - disp ]
2362 .{ .open_br, .index, .star, .scale, .plus, .general_purpose, .plus, .disp, .close_br }, // [ index * scale + general_purpose + disp ]
2363 .{ .open_br, .index, .star, .scale, .plus, .general_purpose, .minus, .disp, .close_br }, // [ index * scale + general_purpose - disp ]
2364 .{ .open_br, .disp, .plus, .index, .star, .scale, .plus, .general_purpose, .close_br }, // [ disp + index * scale + general_purpose ]
2365 .{ .open_br, .disp, .plus, .general_purpose, .plus, .index, .star, .scale, .close_br }, // [ disp + general_purpose + index * scale ]
2366 .{ .open_br, .general_purpose, .plus, .disp, .plus, .index, .star, .scale, .close_br }, // [ general_purpose + disp + index * scale ]
2367 .{ .open_br, .general_purpose, .minus, .disp, .plus, .index, .star, .scale, .close_br }, // [ general_purpose - disp + index * scale ]
2368 .{ .open_br, .general_purpose, .plus, .disp, .plus, .scale, .star, .index, .close_br }, // [ general_purpose + disp + scale * index ]
2369 .{ .open_br, .general_purpose, .minus, .disp, .plus, .scale, .star, .index, .close_br }, // [ general_purpose - disp + scale * index ]
2370 .{ .open_br, .rip, .plus, .disp, .close_br }, // [ rip + disp ]
2371 .{ .open_br, .rip, .minus, .disp, .close_br }, // [ rig - disp ]
2372 .{ .segment, .colon, .disp }, // seg:disp
2373 };
2374
2375 const pos = as.it.pos;
2376 inline for (rules) |rule| {
2377 if (as.parseMemoryRule(rule)) |res| {
2378 if (res.rip) {
2379 if (res.base != null or res.scale_index != null or res.offset != null)
2380 return error.InvalidMemoryOperand;
2381 return Instruction.Memory.initRip(ptr_size orelse .qword, res.disp orelse 0);
2382 }
2383 if (res.base) |base| {
2384 if (res.rip)
2385 return error.InvalidMemoryOperand;
2386 if (res.offset) |offset| {
2387 if (res.scale_index != null or res.disp != null)
2388 return error.InvalidMemoryOperand;
2389 return Instruction.Memory.initMoffs(base, offset);
2390 }
2391 return Instruction.Memory.initSib(ptr_size orelse .qword, .{
2392 .base = .{ .reg = base },
2393 .scale_index = res.scale_index,
2394 .disp = res.disp orelse 0,
2395 });
2396 }
2397 return error.InvalidMemoryOperand;
2398 } else |_| {
2399 as.it.seekTo(pos);
2400 }
2401 }
2402
2403 return error.InvalidOperand;
2404 }
2405
2406 const MemoryParseResult = struct {
2407 rip: bool = false,
2408 base: ?Register = null,
2409 scale_index: ?Instruction.Memory.ScaleIndex = null,
2410 disp: ?i32 = null,
2411 offset: ?u64 = null,
2412 };
2413
2414 fn parseMemoryRule(as: *Assembler, rule: anytype) ParseError!MemoryParseResult {
2415 var res: MemoryParseResult = .{};
2416 inline for (rule, 0..) |cond, i| {
2417 if (@typeInfo(@TypeOf(cond)) != .enum_literal) {
2418 @compileError("unsupported condition type in the rule: " ++ @typeName(@TypeOf(cond)));
2419 }
2420 switch (cond) {
2421 .open_br, .close_br, .plus, .minus, .star, .colon => {
2422 _ = try as.expect(cond);
2423 },
2424 .general_purpose, .segment => {
2425 const tok = try as.expect(.string);
2426 const base = registerFromString(as.source(tok)) orelse return error.InvalidMemoryOperand;
2427 if (!base.isClass(cond)) return error.InvalidMemoryOperand;
2428 res.base = base;
2429 },
2430 .rip => {
2431 const tok = try as.expect(.string);
2432 if (!std.mem.eql(u8, as.source(tok), "rip")) return error.InvalidMemoryOperand;
2433 res.rip = true;
2434 },
2435 .index => {
2436 const tok = try as.expect(.string);
2437 const index = registerFromString(as.source(tok)) orelse
2438 return error.InvalidMemoryOperand;
2439 if (res.scale_index) |*si| {
2440 si.index = index;
2441 } else {
2442 res.scale_index = .{ .scale = 1, .index = index };
2443 }
2444 },
2445 .scale => {
2446 const tok = try as.expect(.numeral);
2447 const scale = try std.fmt.parseInt(u2, as.source(tok), 0);
2448 if (res.scale_index) |*si| {
2449 si.scale = scale;
2450 } else {
2451 res.scale_index = .{ .scale = scale, .index = undefined };
2452 }
2453 },
2454 .disp => {
2455 const tok = try as.expect(.numeral);
2456 const is_neg = blk: {
2457 if (i > 0) {
2458 if (rule[i - 1] == .minus) break :blk true;
2459 }
2460 break :blk false;
2461 };
2462 if (std.fmt.parseInt(i32, as.source(tok), 0)) |disp| {
2463 res.disp = if (is_neg) -1 * disp else disp;
2464 } else |err| switch (err) {
2465 error.Overflow => {
2466 if (is_neg) return err;
2467 if (res.base) |base| {
2468 if (!base.isClass(.segment)) return err;
2469 }
2470 const offset = try std.fmt.parseInt(u64, as.source(tok), 0);
2471 res.offset = offset;
2472 },
2473 else => return err,
2474 }
2475 },
2476 else => @compileError("unhandled operand output type: " ++ @tagName(cond)),
2477 }
2478 try as.skip(1, .{.space});
2479 }
2480 return res;
2481 }
2482
2483 fn parsePtrSize(as: *Assembler) ParseError!Instruction.Memory.PtrSize {
2484 const size = try as.expect(.string);
2485 try as.skip(1, .{.space});
2486 const ptr = try as.expect(.string);
2487
2488 const size_raw = as.source(size);
2489 const ptr_raw = as.source(ptr);
2490 const len = size_raw.len + ptr_raw.len + 1;
2491 var buf: ["qword ptr".len]u8 = undefined;
2492 if (len > buf.len) return error.InvalidPtrSize;
2493
2494 for (size_raw, 0..) |c, i| {
2495 buf[i] = std.ascii.toLower(c);
2496 }
2497 buf[size_raw.len] = ' ';
2498 for (ptr_raw, 0..) |c, i| {
2499 buf[size_raw.len + i + 1] = std.ascii.toLower(c);
2500 }
2501
2502 const slice = buf[0..len];
2503 if (std.mem.eql(u8, slice, "qword ptr")) return .qword;
2504 if (std.mem.eql(u8, slice, "dword ptr")) return .dword;
2505 if (std.mem.eql(u8, slice, "word ptr")) return .word;
2506 if (std.mem.eql(u8, slice, "byte ptr")) return .byte;
2507 if (std.mem.eql(u8, slice, "tbyte ptr")) return .tbyte;
2508 return error.InvalidPtrSize;
2509 }
2510};
2511
2512test "assemble" {
2513 const input =
2514 \\int3
2515 \\mov rax, rbx
2516 \\mov qword ptr [rbp], rax
2517 \\mov qword ptr [rbp - 16], rax
2518 \\mov qword ptr [16 + rbp], rax
2519 \\mov rax, 0x10
2520 \\mov byte ptr [rbp - 0x10], 0x10
2521 \\mov word ptr [rbp + r12], r11w
2522 \\mov word ptr [rbp + r12 * 2], r11w
2523 \\mov word ptr [rbp + r12 * 2 - 16], r11w
2524 \\mov dword ptr [rip - 16], r12d
2525 \\mov rax, fs:0x0
2526 \\mov rax, gs:0x1000000000000000
2527 \\movzx r12, al
2528 \\imul r12, qword ptr [rbp - 16], 6
2529 \\jmp 0x0
2530 \\jc 0x0
2531 \\jb 0x0
2532 \\sal rax, 1
2533 \\sal rax, 63
2534 \\shl rax, 63
2535 \\sar rax, 63
2536 \\shr rax, 63
2537 \\test byte ptr [rbp - 16], r12b
2538 \\sal r12, cl
2539 \\mul qword ptr [rip - 16]
2540 \\div r12
2541 \\idiv byte ptr [rbp - 16]
2542 \\cwde
2543 \\cbw
2544 \\cdqe
2545 \\test byte ptr [rbp], ah
2546 \\test byte ptr [r12], spl
2547 \\cdq
2548 \\cwd
2549 \\cqo
2550 \\test bl, 0x1
2551 \\mov rbx,0x8000000000000000
2552 \\movss xmm0, dword ptr [rbp]
2553 \\movss xmm0, xmm1
2554 \\movss dword ptr [rbp - 16 + rax * 2], xmm7
2555 \\movss dword ptr [rbp - 16 + rax * 2], xmm8
2556 \\movss xmm15, xmm9
2557 \\movsd xmm8, qword ptr [rbp - 16]
2558 \\movsd qword ptr [rbp - 8], xmm0
2559 \\movq xmm8, qword ptr [rbp - 16]
2560 \\movq qword ptr [rbp - 16], xmm8
2561 \\ucomisd xmm0, qword ptr [rbp - 16]
2562 \\fisttp qword ptr [rbp - 16]
2563 \\fisttp word ptr [rip + 32]
2564 \\fisttp dword ptr [rax]
2565 \\fld tbyte ptr [rbp]
2566 \\fld dword ptr [rbp]
2567 \\xor bl, 0xff
2568 \\ud2
2569 \\add rsp, -1
2570 \\add rsp, 0xff
2571 \\mov sil, byte ptr [rax + rcx * 1]
2572 \\
2573 ;
2574
2575 // zig fmt: off
2576 const expected = &[_]u8{
2577 0xCC,
2578 0x48, 0x89, 0xD8,
2579 0x48, 0x89, 0x45, 0x00,
2580 0x48, 0x89, 0x45, 0xF0,
2581 0x48, 0x89, 0x45, 0x10,
2582 0x48, 0xC7, 0xC0, 0x10, 0x00, 0x00, 0x00,
2583 0xC6, 0x45, 0xF0, 0x10,
2584 0x66, 0x46, 0x89, 0x5C, 0x25, 0x00,
2585 0x66, 0x46, 0x89, 0x5C, 0x65, 0x00,
2586 0x66, 0x46, 0x89, 0x5C, 0x65, 0xF0,
2587 0x44, 0x89, 0x25, 0xF0, 0xFF, 0xFF, 0xFF,
2588 0x64, 0x48, 0x8B, 0x04, 0x25, 0x00, 0x00, 0x00, 0x00,
2589 0x65, 0x48, 0xA1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
2590 0x4C, 0x0F, 0xB6, 0xE0,
2591 0x4C, 0x6B, 0x65, 0xF0, 0x06,
2592 0xE9, 0x00, 0x00, 0x00, 0x00,
2593 0x0F, 0x82, 0x00, 0x00, 0x00, 0x00,
2594 0x0F, 0x82, 0x00, 0x00, 0x00, 0x00,
2595 0x48, 0xD1, 0xE0,
2596 0x48, 0xC1, 0xE0, 0x3F,
2597 0x48, 0xC1, 0xE0, 0x3F,
2598 0x48, 0xC1, 0xF8, 0x3F,
2599 0x48, 0xC1, 0xE8, 0x3F,
2600 0x44, 0x84, 0x65, 0xF0,
2601 0x49, 0xD3, 0xE4,
2602 0x48, 0xF7, 0x25, 0xF0, 0xFF, 0xFF, 0xFF,
2603 0x49, 0xF7, 0xF4,
2604 0xF6, 0x7D, 0xF0,
2605 0x98,
2606 0x66, 0x98,
2607 0x48, 0x98,
2608 0x84, 0x65, 0x00,
2609 0x41, 0x84, 0x24, 0x24,
2610 0x99,
2611 0x66, 0x99,
2612 0x48, 0x99,
2613 0xF6, 0xC3, 0x01,
2614 0x48, 0xBB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
2615 0xF3, 0x0F, 0x10, 0x45, 0x00,
2616 0xF3, 0x0F, 0x10, 0xC1,
2617 0xF3, 0x0F, 0x11, 0x7C, 0x45, 0xF0,
2618 0xF3, 0x44, 0x0F, 0x11, 0x44, 0x45, 0xF0,
2619 0xF3, 0x45, 0x0F, 0x10, 0xF9,
2620 0xF2, 0x44, 0x0F, 0x10, 0x45, 0xF0,
2621 0xF2, 0x0F, 0x11, 0x45, 0xF8,
2622 0x66, 0x4C, 0x0F, 0x6E, 0x45, 0xF0,
2623 0x66, 0x4C, 0x0F, 0x7E, 0x45, 0xF0,
2624 0x66, 0x0F, 0x2E, 0x45, 0xF0,
2625 0xDD, 0x4D, 0xF0,
2626 0xDF, 0x0D, 0x20, 0x00, 0x00, 0x00,
2627 0xDB, 0x08,
2628 0xDB, 0x6D, 0x00,
2629 0xD9, 0x45, 0x00,
2630 0x80, 0xF3, 0xFF,
2631 0x0F, 0x0B,
2632 0x48, 0x83, 0xC4, 0xFF,
2633 0x48, 0x81, 0xC4, 0xFF, 0x00, 0x00, 0x00,
2634 0x40, 0x8A, 0x34, 0x08,
2635 };
2636 // zig fmt: on
2637
2638 var as = Assembler.init(input);
2639 var output = std.array_list.Managed(u8).init(testing.allocator);
2640 defer output.deinit();
2641 try as.assemble(output.writer());
2642 try expectEqualHexStrings(expected, output.items, input);
2643}
2644
2645test "assemble - Jcc" {
2646 const mnemonics = [_]struct { Instruction.Mnemonic, u8 }{
2647 .{ .ja, 0x87 },
2648 .{ .jae, 0x83 },
2649 .{ .jb, 0x82 },
2650 .{ .jbe, 0x86 },
2651 .{ .jc, 0x82 },
2652 .{ .je, 0x84 },
2653 .{ .jg, 0x8f },
2654 .{ .jge, 0x8d },
2655 .{ .jl, 0x8c },
2656 .{ .jle, 0x8e },
2657 .{ .jna, 0x86 },
2658 .{ .jnae, 0x82 },
2659 .{ .jnb, 0x83 },
2660 .{ .jnbe, 0x87 },
2661 .{ .jnc, 0x83 },
2662 .{ .jne, 0x85 },
2663 .{ .jng, 0x8e },
2664 .{ .jnge, 0x8c },
2665 .{ .jnl, 0x8d },
2666 .{ .jnle, 0x8f },
2667 .{ .jno, 0x81 },
2668 .{ .jnp, 0x8b },
2669 .{ .jns, 0x89 },
2670 .{ .jnz, 0x85 },
2671 .{ .jo, 0x80 },
2672 .{ .jp, 0x8a },
2673 .{ .jpe, 0x8a },
2674 .{ .jpo, 0x8b },
2675 .{ .js, 0x88 },
2676 .{ .jz, 0x84 },
2677 };
2678
2679 inline for (&mnemonics) |mnemonic| {
2680 const input = @tagName(mnemonic[0]) ++ " 0x0";
2681 const expected = [_]u8{ 0x0f, mnemonic[1], 0x0, 0x0, 0x0, 0x0 };
2682 var as = Assembler.init(input);
2683 var output = std.array_list.Managed(u8).init(testing.allocator);
2684 defer output.deinit();
2685 try as.assemble(output.writer());
2686 try expectEqualHexStrings(&expected, output.items, input);
2687 }
2688}
2689
2690test "assemble - SETcc" {
2691 const mnemonics = [_]struct { Instruction.Mnemonic, u8 }{
2692 .{ .seta, 0x97 },
2693 .{ .setae, 0x93 },
2694 .{ .setb, 0x92 },
2695 .{ .setbe, 0x96 },
2696 .{ .setc, 0x92 },
2697 .{ .sete, 0x94 },
2698 .{ .setg, 0x9f },
2699 .{ .setge, 0x9d },
2700 .{ .setl, 0x9c },
2701 .{ .setle, 0x9e },
2702 .{ .setna, 0x96 },
2703 .{ .setnae, 0x92 },
2704 .{ .setnb, 0x93 },
2705 .{ .setnbe, 0x97 },
2706 .{ .setnc, 0x93 },
2707 .{ .setne, 0x95 },
2708 .{ .setng, 0x9e },
2709 .{ .setnge, 0x9c },
2710 .{ .setnl, 0x9d },
2711 .{ .setnle, 0x9f },
2712 .{ .setno, 0x91 },
2713 .{ .setnp, 0x9b },
2714 .{ .setns, 0x99 },
2715 .{ .setnz, 0x95 },
2716 .{ .seto, 0x90 },
2717 .{ .setp, 0x9a },
2718 .{ .setpe, 0x9a },
2719 .{ .setpo, 0x9b },
2720 .{ .sets, 0x98 },
2721 .{ .setz, 0x94 },
2722 };
2723
2724 inline for (&mnemonics) |mnemonic| {
2725 const input = @tagName(mnemonic[0]) ++ " al";
2726 const expected = [_]u8{ 0x0f, mnemonic[1], 0xC0 };
2727 var as = Assembler.init(input);
2728 var output = std.array_list.Managed(u8).init(testing.allocator);
2729 defer output.deinit();
2730 try as.assemble(output.writer());
2731 try expectEqualHexStrings(&expected, output.items, input);
2732 }
2733}
2734
2735test "assemble - CMOVcc" {
2736 const mnemonics = [_]struct { Instruction.Mnemonic, u8 }{
2737 .{ .cmova, 0x47 },
2738 .{ .cmovae, 0x43 },
2739 .{ .cmovb, 0x42 },
2740 .{ .cmovbe, 0x46 },
2741 .{ .cmovc, 0x42 },
2742 .{ .cmove, 0x44 },
2743 .{ .cmovg, 0x4f },
2744 .{ .cmovge, 0x4d },
2745 .{ .cmovl, 0x4c },
2746 .{ .cmovle, 0x4e },
2747 .{ .cmovna, 0x46 },
2748 .{ .cmovnae, 0x42 },
2749 .{ .cmovnb, 0x43 },
2750 .{ .cmovnbe, 0x47 },
2751 .{ .cmovnc, 0x43 },
2752 .{ .cmovne, 0x45 },
2753 .{ .cmovng, 0x4e },
2754 .{ .cmovnge, 0x4c },
2755 .{ .cmovnl, 0x4d },
2756 .{ .cmovnle, 0x4f },
2757 .{ .cmovno, 0x41 },
2758 .{ .cmovnp, 0x4b },
2759 .{ .cmovns, 0x49 },
2760 .{ .cmovnz, 0x45 },
2761 .{ .cmovo, 0x40 },
2762 .{ .cmovp, 0x4a },
2763 .{ .cmovpe, 0x4a },
2764 .{ .cmovpo, 0x4b },
2765 .{ .cmovs, 0x48 },
2766 .{ .cmovz, 0x44 },
2767 };
2768
2769 inline for (&mnemonics) |mnemonic| {
2770 const input = @tagName(mnemonic[0]) ++ " rax, rbx";
2771 const expected = [_]u8{ 0x48, 0x0f, mnemonic[1], 0xC3 };
2772 var as = Assembler.init(input);
2773 var output = std.array_list.Managed(u8).init(testing.allocator);
2774 defer output.deinit();
2775 try as.assemble(output.writer());
2776 try expectEqualHexStrings(&expected, output.items, input);
2777 }
2778}