authorgravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2020-08-31 12:01:06+02:00
committergravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2020-10-02 19:39:17+02:00
log0a54f04dbc4ce9a0a1d1b5fe9d1a5a502030b47e
treea6bb67b8928dda28d960748ab11d615a99c56673
parente9434ff8f46ec27a863d0b071c0a99b4f01588dc

stage2 ARM: start adding more instructions, return values, parameters


2 files changed, 300 insertions(+), 55 deletions(-)

src/codegen.zig+110-9
......@@ -1461,7 +1461,35 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
14611461 }
14621462 },
14631463 .arm => {
1464 if (info.args.len > 0) return self.fail(inst.base.src, "TODO implement fn args for {}", .{self.target.cpu.arch});
1464 for (info.args) |mc_arg, arg_i| {
1465 const arg = inst.args[arg_i];
1466 const arg_mcv = try self.resolveInst(inst.args[arg_i]);
1467
1468 switch (mc_arg) {
1469 .none => continue,
1470 .undef => unreachable,
1471 .immediate => unreachable,
1472 .unreach => unreachable,
1473 .dead => unreachable,
1474 .embedded_in_code => unreachable,
1475 .memory => unreachable,
1476 .compare_flags_signed => unreachable,
1477 .compare_flags_unsigned => unreachable,
1478 .register => |reg| {
1479 try self.genSetReg(arg.src, reg, arg_mcv);
1480 // TODO interact with the register allocator to mark the instruction as moved.
1481 },
1482 .stack_offset => {
1483 return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});
1484 },
1485 .ptr_stack_offset => {
1486 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_stack_offset arg", .{});
1487 },
1488 .ptr_embedded_in_code => {
1489 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});
1490 },
1491 }
1492 }
14651493
14661494 if (inst.func.cast(ir.Inst.Constant)) |func_inst| {
14671495 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
......@@ -1482,7 +1510,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
14821510 // of course. Add pushing lr to stack
14831511 // and popping after call
14841512 try self.genSetReg(inst.base.src, .lr, .{ .memory = got_addr });
1485 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.blx(.al, .lr).toU32());
1513
1514 if (Target.arm.featureSetHas(self.target.cpu.features, .has_v5t)) {
1515 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.blx(.al, .lr).toU32());
1516 } else {
1517 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, .lr, Instruction.Operand.reg(.pc, Instruction.Operand.Shift.none)).toU32());
1518 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.bx(.al, .lr).toU32());
1519 }
14861520 } else {
14871521 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});
14881522 }
......@@ -2213,14 +2247,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
22132247 // least amount of necessary instructions (use
22142248 // more intelligent rotating)
22152249 if (x <= math.maxInt(u8)) {
2216 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, 0, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32());
2250 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32());
22172251 return;
22182252 } else if (x <= math.maxInt(u16)) {
22192253 // TODO Use movw Note: Not supported on
22202254 // all ARM targets!
22212255
2222 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, 0, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32());
2223 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 8), 12)).toU32());
2256 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32());
2257 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 8), 12)).toU32());
22242258 } else if (x <= math.maxInt(u32)) {
22252259 // TODO Use movw and movt Note: Not
22262260 // supported on all ARM targets! Also TODO
......@@ -2232,15 +2266,23 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
22322266 // orr reg, reg, #0xbb, 24
22332267 // orr reg, reg, #0xcc, 16
22342268 // orr reg, reg, #0xdd, 8
2235 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, 0, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32());
2236 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 8), 12)).toU32());
2237 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 16), 8)).toU32());
2238 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 24), 4)).toU32());
2269 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32());
2270 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 8), 12)).toU32());
2271 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 16), 8)).toU32());
2272 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 24), 4)).toU32());
22392273 return;
22402274 } else {
22412275 return self.fail(src, "ARM registers are 32-bit wide", .{});
22422276 }
22432277 },
2278 .register => |src_reg| {
2279 // If the registers are the same, nothing to do.
2280 if (src_reg.id() == reg.id())
2281 return;
2282
2283 // mov reg, src_reg
2284 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, Instruction.Operand.reg(src_reg, Instruction.Operand.Shift.none)).toU32());
2285 },
22442286 .memory => |addr| {
22452287 // The value is in memory at a hard-coded address.
22462288 // If the type is a pointer, it means the pointer address is at this memory location.
......@@ -2701,6 +2743,53 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
27012743 else => return self.fail(src, "TODO implement function parameters for {} on x86_64", .{cc}),
27022744 }
27032745 },
2746 .arm => {
2747 switch (cc) {
2748 .Naked => {
2749 assert(result.args.len == 0);
2750 result.return_value = .{ .unreach = {} };
2751 result.stack_byte_count = 0;
2752 result.stack_align = 1;
2753 return result;
2754 },
2755 .Unspecified, .C => {
2756 // ARM Procedure Call Standard, Chapter 6.5
2757 var ncrn: usize = 0; // Next Core Register Number
2758 var nsaa: u32 = 0; // Next stacked argument address
2759
2760 for (param_types) |ty, i| {
2761 if (ty.abiAlignment(self.target.*) == 8) {
2762 // Round up NCRN to the next even number
2763 ncrn += ncrn % 2;
2764 }
2765
2766 const param_size = @intCast(u32, ty.abiSize(self.target.*));
2767 if (std.math.divCeil(u32, param_size, 4) catch unreachable <= 4 - ncrn) {
2768 if (param_size <= 4) {
2769 result.args[i] = .{ .register = c_abi_int_param_regs[ncrn] };
2770 ncrn += 1;
2771 } else {
2772 return self.fail(src, "TODO MCValues with multiple registers", .{});
2773 }
2774 } else {
2775 ncrn = 4;
2776 if (ty.abiAlignment(self.target.*) == 8) {
2777 if (nsaa % 8 != 0) {
2778 nsaa += 8 - (nsaa % 8);
2779 }
2780 }
2781
2782 result.args[i] = .{ .stack_offset = nsaa };
2783 nsaa += param_size;
2784 }
2785 }
2786
2787 result.stack_byte_count = nsaa;
2788 result.stack_align = 4;
2789 },
2790 else => return self.fail(src, "TODO implement function parameters for {} on arm", .{cc}),
2791 }
2792 },
27042793 else => if (param_types.len != 0)
27052794 return self.fail(src, "TODO implement codegen parameters for {}", .{self.target.cpu.arch}),
27062795 }
......@@ -2719,6 +2808,18 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
27192808 },
27202809 else => return self.fail(src, "TODO implement function return values for {}", .{cc}),
27212810 },
2811 .arm => switch (cc) {
2812 .Naked => unreachable,
2813 .Unspecified, .C => {
2814 const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*));
2815 if (ret_ty_size <= 4) {
2816 result.return_value = .{ .register = c_abi_int_return_regs[0] };
2817 } else {
2818 return self.fail(src, "TODO support more return types for ARM backend", .{});
2819 }
2820 },
2821 else => return self.fail(src, "TODO implement function return values for {}", .{cc}),
2822 },
27222823 else => return self.fail(src, "TODO implement codegen return values for {}", .{self.target.cpu.arch}),
27232824 }
27242825 return result;
src/codegen/arm.zig+190-46
......@@ -113,6 +113,13 @@ test "Register.id" {
113113 testing.expectEqual(@as(u4, 15), Register.pc.id());
114114}
115115
116/// Program status registers containing flags, mode bits and other
117/// vital information
118pub const Psr = enum {
119 cpsr,
120 spsr,
121};
122
116123pub const callee_preserved_regs = [_]Register{ .r0, .r1, .r2, .r3, .r4, .r5, .r6, .r7, .r8, .r10 };
117124pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 };
118125pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 };
......@@ -135,15 +142,26 @@ pub const Instruction = union(enum) {
135142 offset: u12,
136143 rd: u4,
137144 rn: u4,
138 l: u1,
139 w: u1,
140 b: u1,
141 u: u1,
142 p: u1,
143 i: u1,
145 load_store: u1,
146 write_back: u1,
147 byte_word: u1,
148 up_down: u1,
149 pre_post: u1,
150 imm: u1,
144151 fixed: u2 = 0b01,
145152 cond: u4,
146153 },
154 BlockDataTransfer: packed struct {
155 register_list: u16,
156 rn: u4,
157 load_store: u1,
158 write_back: u1,
159 psr_or_user: u1,
160 up_down: u1,
161 pre_post: u1,
162 fixed: u3 = 0b100,
163 cond: u4,
164 },
147165 Branch: packed struct {
148166 offset: u24,
149167 link: u1,
......@@ -235,14 +253,14 @@ pub const Instruction = union(enum) {
235253 rs: u4,
236254 },
237255
238 const Type = enum(u2) {
239 LogicalLeft,
240 LogicalRight,
241 ArithmeticRight,
242 RotateRight,
256 pub const Type = enum(u2) {
257 logical_left,
258 logical_right,
259 arithmetic_right,
260 rotate_right,
243261 };
244262
245 const none = Shift{
263 pub const none = Shift{
246264 .Immediate = .{
247265 .amount = 0,
248266 .typ = 0,
......@@ -338,10 +356,32 @@ pub const Instruction = union(enum) {
338356 }
339357 };
340358
359 /// Represents the register list operand to a block data transfer
360 /// instruction
361 pub const RegisterList = packed struct {
362 r0: bool = false,
363 r1: bool = false,
364 r2: bool = false,
365 r3: bool = false,
366 r4: bool = false,
367 r5: bool = false,
368 r6: bool = false,
369 r7: bool = false,
370 r8: bool = false,
371 r9: bool = false,
372 r10: bool = false,
373 r11: bool = false,
374 r12: bool = false,
375 r13: bool = false,
376 r14: bool = false,
377 r15: bool = false,
378 };
379
341380 pub fn toU32(self: Instruction) u32 {
342381 return switch (self) {
343382 .DataProcessing => |v| @bitCast(u32, v),
344383 .SingleDataTransfer => |v| @bitCast(u32, v),
384 .BlockDataTransfer => |v| @bitCast(u32, v),
345385 .Branch => |v| @bitCast(u32, v),
346386 .BranchExchange => |v| @bitCast(u32, v),
347387 .SupervisorCall => |v| @bitCast(u32, v),
......@@ -380,7 +420,7 @@ pub const Instruction = union(enum) {
380420 pre_post: u1,
381421 up_down: u1,
382422 byte_word: u1,
383 writeback: u1,
423 write_back: u1,
384424 load_store: u1,
385425 ) Instruction {
386426 return Instruction{
......@@ -389,12 +429,36 @@ pub const Instruction = union(enum) {
389429 .rn = rn.id(),
390430 .rd = rd.id(),
391431 .offset = offset.toU12(),
392 .l = load_store,
393 .w = writeback,
394 .b = byte_word,
395 .u = up_down,
396 .p = pre_post,
397 .i = if (offset == .Immediate) 0 else 1,
432 .load_store = load_store,
433 .write_back = write_back,
434 .byte_word = byte_word,
435 .up_down = up_down,
436 .pre_post = pre_post,
437 .imm = if (offset == .Immediate) 0 else 1,
438 },
439 };
440 }
441
442 fn blockDataTransfer(
443 cond: Condition,
444 rn: Register,
445 reg_list: RegisterList,
446 pre_post: u1,
447 up_down: u1,
448 psr_or_user: u1,
449 write_back: u1,
450 load_store: u1,
451 ) Instruction {
452 return Instruction{
453 .BlockDataTransfer = .{
454 .register_list = @bitCast(u16, reg_list),
455 .rn = rn.id(),
456 .load_store = load_store,
457 .write_back = write_back,
458 .psr_or_user = psr_or_user,
459 .up_down = up_down,
460 .pre_post = pre_post,
461 .cond = @enumToInt(cond),
398462 },
399463 };
400464 }
......@@ -442,36 +506,68 @@ pub const Instruction = union(enum) {
442506
443507 // Data processing
444508
445 pub fn @"and"(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
446 return dataProcessing(cond, .@"and", s, rd, rn, op2);
509 pub fn @"and"(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
510 return dataProcessing(cond, .@"and", 0, rd, rn, op2);
511 }
512
513 pub fn ands(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
514 return dataProcessing(cond, .@"and", 1, rd, rn, op2);
515 }
516
517 pub fn eor(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
518 return dataProcessing(cond, .eor, 0, rd, rn, op2);
519 }
520
521 pub fn eors(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
522 return dataProcessing(cond, .eor, 1, rd, rn, op2);
523 }
524
525 pub fn sub(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
526 return dataProcessing(cond, .sub, 0, rd, rn, op2);
527 }
528
529 pub fn subs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
530 return dataProcessing(cond, .sub, 1, rd, rn, op2);
531 }
532
533 pub fn rsb(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
534 return dataProcessing(cond, .rsb, 0, rd, rn, op2);
535 }
536
537 pub fn rsbs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
538 return dataProcessing(cond, .rsb, 1, rd, rn, op2);
447539 }
448540
449 pub fn eor(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
450 return dataProcessing(cond, .eor, s, rd, rn, op2);
541 pub fn add(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
542 return dataProcessing(cond, .add, 0, rd, rn, op2);
451543 }
452544
453 pub fn sub(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
454 return dataProcessing(cond, .sub, s, rd, rn, op2);
545 pub fn adds(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
546 return dataProcessing(cond, .add, 1, rd, rn, op2);
455547 }
456548
457 pub fn rsb(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
458 return dataProcessing(cond, .rsb, s, rd, rn, op2);
549 pub fn adc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
550 return dataProcessing(cond, .adc, 0, rd, rn, op2);
459551 }
460552
461 pub fn add(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
462 return dataProcessing(cond, .add, s, rd, rn, op2);
553 pub fn adcs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
554 return dataProcessing(cond, .adc, 1, rd, rn, op2);
463555 }
464556
465 pub fn adc(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
466 return dataProcessing(cond, .adc, s, rd, rn, op2);
557 pub fn sbc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
558 return dataProcessing(cond, .sbc, 0, rd, rn, op2);
467559 }
468560
469 pub fn sbc(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
470 return dataProcessing(cond, .sbc, s, rd, rn, op2);
561 pub fn sbcs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
562 return dataProcessing(cond, .sbc, 1, rd, rn, op2);
471563 }
472564
473 pub fn rsc(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
474 return dataProcessing(cond, .rsc, s, rd, rn, op2);
565 pub fn rsc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
566 return dataProcessing(cond, .rsc, 0, rd, rn, op2);
567 }
568
569 pub fn rscs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
570 return dataProcessing(cond, .rsc, 1, rd, rn, op2);
475571 }
476572
477573 pub fn tst(cond: Condition, rn: Register, op2: Operand) Instruction {
......@@ -490,20 +586,42 @@ pub const Instruction = union(enum) {
490586 return dataProcessing(cond, .cmn, 1, .r0, rn, op2);
491587 }
492588
493 pub fn orr(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
494 return dataProcessing(cond, .orr, s, rd, rn, op2);
589 pub fn orr(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
590 return dataProcessing(cond, .orr, 0, rd, rn, op2);
591 }
592
593 pub fn orrs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
594 return dataProcessing(cond, .orr, 1, rd, rn, op2);
595 }
596
597 pub fn mov(cond: Condition, rd: Register, op2: Operand) Instruction {
598 return dataProcessing(cond, .mov, 0, rd, .r0, op2);
599 }
600
601 pub fn movs(cond: Condition, rd: Register, op2: Operand) Instruction {
602 return dataProcessing(cond, .mov, 1, rd, .r0, op2);
495603 }
496604
497 pub fn mov(cond: Condition, s: u1, rd: Register, op2: Operand) Instruction {
498 return dataProcessing(cond, .mov, s, rd, .r0, op2);
605 pub fn bic(cond: Condition, rd: Register, op2: Operand) Instruction {
606 return dataProcessing(cond, .bic, 0, rd, rn, op2);
499607 }
500608
501 pub fn bic(cond: Condition, s: u1, rd: Register, op2: Operand) Instruction {
502 return dataProcessing(cond, .bic, s, rd, rn, op2);
609 pub fn bics(cond: Condition, rd: Register, op2: Operand) Instruction {
610 return dataProcessing(cond, .bic, 1, rd, rn, op2);
503611 }
504612
505 pub fn mvn(cond: Condition, s: u1, rd: Register, op2: Operand) Instruction {
506 return dataProcessing(cond, .mvn, s, rd, .r0, op2);
613 pub fn mvn(cond: Condition, rd: Register, op2: Operand) Instruction {
614 return dataProcessing(cond, .mvn, 0, rd, .r0, op2);
615 }
616
617 pub fn mvns(cond: Condition, rd: Register, op2: Operand) Instruction {
618 return dataProcessing(cond, .mvn, 1, rd, .r0, op2);
619 }
620
621 // PSR transfer
622
623 pub fn mrs(cond: Condition, rd: Register, psr: Psr) Instruction {
624 return dataProcessing(cond, if (psr == .cpsr) .tst else .cmp, 0, rd, .r15, Operand.reg(.r0, Operand.Shift.none));
507625 }
508626
509627 // Single data transfer
......@@ -512,10 +630,28 @@ pub const Instruction = union(enum) {
512630 return singleDataTransfer(cond, rd, rn, offset, 1, 1, 0, 0, 1);
513631 }
514632
633 pub fn ldrb(cond: Condition, rd: Register, rn: Register, offset: Offset) Instruction {
634 return singleDataTransfer(cond, rd, rn, offset, 1, 1, 1, 0, 1);
635 }
636
515637 pub fn str(cond: Condition, rd: Register, rn: Register, offset: Offset) Instruction {
516638 return singleDataTransfer(cond, rd, rn, offset, 1, 1, 0, 0, 0);
517639 }
518640
641 pub fn strb(cond: Condition, rd: Register, rn: Register, offset: Offset) Instruction {
642 return singleDataTransfer(cond, rd, rn, offset, 1, 1, 1, 0, 0);
643 }
644
645 // Block data transfer
646
647 pub fn ldm(cond: Condition, rn: Register, reg_list: RegisterList) Instruction {
648 return blockDataTransfer(cond, rn, reg_list, 1, 0, 0, 0, 1);
649 }
650
651 pub fn stm(cond: Condition, rn: Register, reg_list: RegisterList) Instruction {
652 return blockDataTransfer(cond, rn, reg_list, 1, 0, 0, 0, 0);
653 }
654
519655 // Branch
520656
521657 pub fn b(cond: Condition, offset: i24) Instruction {
......@@ -559,17 +695,21 @@ test "serialize instructions" {
559695
560696 const testcases = [_]Testcase{
561697 .{ // add r0, r0, r0
562 .inst = Instruction.add(.al, 0, .r0, .r0, Instruction.Operand.reg(.r0, Instruction.Operand.Shift.none)),
698 .inst = Instruction.add(.al, .r0, .r0, Instruction.Operand.reg(.r0, Instruction.Operand.Shift.none)),
563699 .expected = 0b1110_00_0_0100_0_0000_0000_00000000_0000,
564700 },
565701 .{ // mov r4, r2
566 .inst = Instruction.mov(.al, 0, .r4, Instruction.Operand.reg(.r2, Instruction.Operand.Shift.none)),
702 .inst = Instruction.mov(.al, .r4, Instruction.Operand.reg(.r2, Instruction.Operand.Shift.none)),
567703 .expected = 0b1110_00_0_1101_0_0000_0100_00000000_0010,
568704 },
569705 .{ // mov r0, #42
570 .inst = Instruction.mov(.al, 0, .r0, Instruction.Operand.imm(42, 0)),
706 .inst = Instruction.mov(.al, .r0, Instruction.Operand.imm(42, 0)),
571707 .expected = 0b1110_00_1_1101_0_0000_0000_0000_00101010,
572708 },
709 .{ // mrs r5, cpsr
710 .inst = Instruction.mrs(.al, .r5, .cpsr),
711 .expected = 0b1110_00010_0_001111_0101_000000000000,
712 },
573713 .{ // ldr r0, [r2, #42]
574714 .inst = Instruction.ldr(.al, .r0, .r2, Instruction.Offset.imm(42)),
575715 .expected = 0b1110_01_0_1_1_0_0_1_0010_0000_000000101010,
......@@ -598,6 +738,10 @@ test "serialize instructions" {
598738 .inst = Instruction.bkpt(42),
599739 .expected = 0b1110_0001_0010_000000000010_0111_1010,
600740 },
741 .{ // stmfd r9, {r0}
742 .inst = Instruction.stm(.al, .r9, .{ .r0 = true }),
743 .expected = 0b1110_100_1_0_0_0_0_1001_0000000000000001,
744 },
601745 };
602746
603747 for (testcases) |case| {