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 {...@@ -1461,7 +1461,35 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1461 }1461 }
1462 },1462 },
1463 .arm => {1463 .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
1466 if (inst.func.cast(ir.Inst.Constant)) |func_inst| {1494 if (inst.func.cast(ir.Inst.Constant)) |func_inst| {
1467 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {1495 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
...@@ -1482,7 +1510,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1482,7 +1510,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1482 // of course. Add pushing lr to stack1510 // of course. Add pushing lr to stack
1483 // and popping after call1511 // and popping after call
1484 try self.genSetReg(inst.base.src, .lr, .{ .memory = got_addr });1512 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 }
1486 } else {1520 } else {
1487 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});1521 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});
1488 }1522 }
...@@ -2213,14 +2247,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2213,14 +2247,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2213 // least amount of necessary instructions (use2247 // least amount of necessary instructions (use
2214 // more intelligent rotating)2248 // more intelligent rotating)
2215 if (x <= math.maxInt(u8)) {2249 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());
2217 return;2251 return;
2218 } else if (x <= math.maxInt(u16)) {2252 } else if (x <= math.maxInt(u16)) {
2219 // TODO Use movw Note: Not supported on2253 // TODO Use movw Note: Not supported on
2220 // all ARM targets!2254 // 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());2256 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, 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());2257 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 8), 12)).toU32());
2224 } else if (x <= math.maxInt(u32)) {2258 } else if (x <= math.maxInt(u32)) {
2225 // TODO Use movw and movt Note: Not2259 // TODO Use movw and movt Note: Not
2226 // supported on all ARM targets! Also TODO2260 // supported on all ARM targets! Also TODO
...@@ -2232,15 +2266,23 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2232,15 +2266,23 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2232 // orr reg, reg, #0xbb, 242266 // orr reg, reg, #0xbb, 24
2233 // orr reg, reg, #0xcc, 162267 // orr reg, reg, #0xcc, 16
2234 // orr reg, reg, #0xdd, 82268 // 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());2269 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, 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());2270 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 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());2271 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 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());2272 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 24), 4)).toU32());
2239 return;2273 return;
2240 } else {2274 } else {
2241 return self.fail(src, "ARM registers are 32-bit wide", .{});2275 return self.fail(src, "ARM registers are 32-bit wide", .{});
2242 }2276 }
2243 },2277 },
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 },
2244 .memory => |addr| {2286 .memory => |addr| {
2245 // The value is in memory at a hard-coded address.2287 // The value is in memory at a hard-coded address.
2246 // If the type is a pointer, it means the pointer address is at this memory location.2288 // 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 {...@@ -2701,6 +2743,53 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2701 else => return self.fail(src, "TODO implement function parameters for {} on x86_64", .{cc}),2743 else => return self.fail(src, "TODO implement function parameters for {} on x86_64", .{cc}),
2702 }2744 }
2703 },2745 },
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 },
2704 else => if (param_types.len != 0)2793 else => if (param_types.len != 0)
2705 return self.fail(src, "TODO implement codegen parameters for {}", .{self.target.cpu.arch}),2794 return self.fail(src, "TODO implement codegen parameters for {}", .{self.target.cpu.arch}),
2706 }2795 }
...@@ -2719,6 +2808,18 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2719,6 +2808,18 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2719 },2808 },
2720 else => return self.fail(src, "TODO implement function return values for {}", .{cc}),2809 else => return self.fail(src, "TODO implement function return values for {}", .{cc}),
2721 },2810 },
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 },
2722 else => return self.fail(src, "TODO implement codegen return values for {}", .{self.target.cpu.arch}),2823 else => return self.fail(src, "TODO implement codegen return values for {}", .{self.target.cpu.arch}),
2723 }2824 }
2724 return result;2825 return result;
src/codegen/arm.zig+190-46
...@@ -113,6 +113,13 @@ test "Register.id" {...@@ -113,6 +113,13 @@ test "Register.id" {
113 testing.expectEqual(@as(u4, 15), Register.pc.id());113 testing.expectEqual(@as(u4, 15), Register.pc.id());
114}114}
115115
116/// Program status registers containing flags, mode bits and other
117/// vital information
118pub const Psr = enum {
119 cpsr,
120 spsr,
121};
122
116pub const callee_preserved_regs = [_]Register{ .r0, .r1, .r2, .r3, .r4, .r5, .r6, .r7, .r8, .r10 };123pub const callee_preserved_regs = [_]Register{ .r0, .r1, .r2, .r3, .r4, .r5, .r6, .r7, .r8, .r10 };
117pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 };124pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 };
118pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 };125pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 };
...@@ -135,15 +142,26 @@ pub const Instruction = union(enum) {...@@ -135,15 +142,26 @@ pub const Instruction = union(enum) {
135 offset: u12,142 offset: u12,
136 rd: u4,143 rd: u4,
137 rn: u4,144 rn: u4,
138 l: u1,145 load_store: u1,
139 w: u1,146 write_back: u1,
140 b: u1,147 byte_word: u1,
141 u: u1,148 up_down: u1,
142 p: u1,149 pre_post: u1,
143 i: u1,150 imm: u1,
144 fixed: u2 = 0b01,151 fixed: u2 = 0b01,
145 cond: u4,152 cond: u4,
146 },153 },
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 },
147 Branch: packed struct {165 Branch: packed struct {
148 offset: u24,166 offset: u24,
149 link: u1,167 link: u1,
...@@ -235,14 +253,14 @@ pub const Instruction = union(enum) {...@@ -235,14 +253,14 @@ pub const Instruction = union(enum) {
235 rs: u4,253 rs: u4,
236 },254 },
237255
238 const Type = enum(u2) {256 pub const Type = enum(u2) {
239 LogicalLeft,257 logical_left,
240 LogicalRight,258 logical_right,
241 ArithmeticRight,259 arithmetic_right,
242 RotateRight,260 rotate_right,
243 };261 };
244262
245 const none = Shift{263 pub const none = Shift{
246 .Immediate = .{264 .Immediate = .{
247 .amount = 0,265 .amount = 0,
248 .typ = 0,266 .typ = 0,
...@@ -338,10 +356,32 @@ pub const Instruction = union(enum) {...@@ -338,10 +356,32 @@ pub const Instruction = union(enum) {
338 }356 }
339 };357 };
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
341 pub fn toU32(self: Instruction) u32 {380 pub fn toU32(self: Instruction) u32 {
342 return switch (self) {381 return switch (self) {
343 .DataProcessing => |v| @bitCast(u32, v),382 .DataProcessing => |v| @bitCast(u32, v),
344 .SingleDataTransfer => |v| @bitCast(u32, v),383 .SingleDataTransfer => |v| @bitCast(u32, v),
384 .BlockDataTransfer => |v| @bitCast(u32, v),
345 .Branch => |v| @bitCast(u32, v),385 .Branch => |v| @bitCast(u32, v),
346 .BranchExchange => |v| @bitCast(u32, v),386 .BranchExchange => |v| @bitCast(u32, v),
347 .SupervisorCall => |v| @bitCast(u32, v),387 .SupervisorCall => |v| @bitCast(u32, v),
...@@ -380,7 +420,7 @@ pub const Instruction = union(enum) {...@@ -380,7 +420,7 @@ pub const Instruction = union(enum) {
380 pre_post: u1,420 pre_post: u1,
381 up_down: u1,421 up_down: u1,
382 byte_word: u1,422 byte_word: u1,
383 writeback: u1,423 write_back: u1,
384 load_store: u1,424 load_store: u1,
385 ) Instruction {425 ) Instruction {
386 return Instruction{426 return Instruction{
...@@ -389,12 +429,36 @@ pub const Instruction = union(enum) {...@@ -389,12 +429,36 @@ pub const Instruction = union(enum) {
389 .rn = rn.id(),429 .rn = rn.id(),
390 .rd = rd.id(),430 .rd = rd.id(),
391 .offset = offset.toU12(),431 .offset = offset.toU12(),
392 .l = load_store,432 .load_store = load_store,
393 .w = writeback,433 .write_back = write_back,
394 .b = byte_word,434 .byte_word = byte_word,
395 .u = up_down,435 .up_down = up_down,
396 .p = pre_post,436 .pre_post = pre_post,
397 .i = if (offset == .Immediate) 0 else 1,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),
398 },462 },
399 };463 };
400 }464 }
...@@ -442,36 +506,68 @@ pub const Instruction = union(enum) {...@@ -442,36 +506,68 @@ pub const Instruction = union(enum) {
442506
443 // Data processing507 // Data processing
444508
445 pub fn @"and"(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {509 pub fn @"and"(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
446 return dataProcessing(cond, .@"and", s, rd, rn, op2);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);
447 }539 }
448540
449 pub fn eor(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {541 pub fn add(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
450 return dataProcessing(cond, .eor, s, rd, rn, op2);542 return dataProcessing(cond, .add, 0, rd, rn, op2);
451 }543 }
452544
453 pub fn sub(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {545 pub fn adds(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
454 return dataProcessing(cond, .sub, s, rd, rn, op2);546 return dataProcessing(cond, .add, 1, rd, rn, op2);
455 }547 }
456548
457 pub fn rsb(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {549 pub fn adc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
458 return dataProcessing(cond, .rsb, s, rd, rn, op2);550 return dataProcessing(cond, .adc, 0, rd, rn, op2);
459 }551 }
460552
461 pub fn add(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {553 pub fn adcs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
462 return dataProcessing(cond, .add, s, rd, rn, op2);554 return dataProcessing(cond, .adc, 1, rd, rn, op2);
463 }555 }
464556
465 pub fn adc(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {557 pub fn sbc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
466 return dataProcessing(cond, .adc, s, rd, rn, op2);558 return dataProcessing(cond, .sbc, 0, rd, rn, op2);
467 }559 }
468560
469 pub fn sbc(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {561 pub fn sbcs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
470 return dataProcessing(cond, .sbc, s, rd, rn, op2);562 return dataProcessing(cond, .sbc, 1, rd, rn, op2);
471 }563 }
472564
473 pub fn rsc(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {565 pub fn rsc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
474 return dataProcessing(cond, .rsc, s, rd, rn, op2);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);
475 }571 }
476572
477 pub fn tst(cond: Condition, rn: Register, op2: Operand) Instruction {573 pub fn tst(cond: Condition, rn: Register, op2: Operand) Instruction {
...@@ -490,20 +586,42 @@ pub const Instruction = union(enum) {...@@ -490,20 +586,42 @@ pub const Instruction = union(enum) {
490 return dataProcessing(cond, .cmn, 1, .r0, rn, op2);586 return dataProcessing(cond, .cmn, 1, .r0, rn, op2);
491 }587 }
492588
493 pub fn orr(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {589 pub fn orr(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
494 return dataProcessing(cond, .orr, s, rd, rn, op2);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);
495 }603 }
496604
497 pub fn mov(cond: Condition, s: u1, rd: Register, op2: Operand) Instruction {605 pub fn bic(cond: Condition, rd: Register, op2: Operand) Instruction {
498 return dataProcessing(cond, .mov, s, rd, .r0, op2);606 return dataProcessing(cond, .bic, 0, rd, rn, op2);
499 }607 }
500608
501 pub fn bic(cond: Condition, s: u1, rd: Register, op2: Operand) Instruction {609 pub fn bics(cond: Condition, rd: Register, op2: Operand) Instruction {
502 return dataProcessing(cond, .bic, s, rd, rn, op2);610 return dataProcessing(cond, .bic, 1, rd, rn, op2);
503 }611 }
504612
505 pub fn mvn(cond: Condition, s: u1, rd: Register, op2: Operand) Instruction {613 pub fn mvn(cond: Condition, rd: Register, op2: Operand) Instruction {
506 return dataProcessing(cond, .mvn, s, rd, .r0, op2);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));
507 }625 }
508626
509 // Single data transfer627 // Single data transfer
...@@ -512,10 +630,28 @@ pub const Instruction = union(enum) {...@@ -512,10 +630,28 @@ pub const Instruction = union(enum) {
512 return singleDataTransfer(cond, rd, rn, offset, 1, 1, 0, 0, 1);630 return singleDataTransfer(cond, rd, rn, offset, 1, 1, 0, 0, 1);
513 }631 }
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
515 pub fn str(cond: Condition, rd: Register, rn: Register, offset: Offset) Instruction {637 pub fn str(cond: Condition, rd: Register, rn: Register, offset: Offset) Instruction {
516 return singleDataTransfer(cond, rd, rn, offset, 1, 1, 0, 0, 0);638 return singleDataTransfer(cond, rd, rn, offset, 1, 1, 0, 0, 0);
517 }639 }
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
519 // Branch655 // Branch
520656
521 pub fn b(cond: Condition, offset: i24) Instruction {657 pub fn b(cond: Condition, offset: i24) Instruction {
...@@ -559,17 +695,21 @@ test "serialize instructions" {...@@ -559,17 +695,21 @@ test "serialize instructions" {
559695
560 const testcases = [_]Testcase{696 const testcases = [_]Testcase{
561 .{ // add r0, r0, r0697 .{ // 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)),
563 .expected = 0b1110_00_0_0100_0_0000_0000_00000000_0000,699 .expected = 0b1110_00_0_0100_0_0000_0000_00000000_0000,
564 },700 },
565 .{ // mov r4, r2701 .{ // 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)),
567 .expected = 0b1110_00_0_1101_0_0000_0100_00000000_0010,703 .expected = 0b1110_00_0_1101_0_0000_0100_00000000_0010,
568 },704 },
569 .{ // mov r0, #42705 .{ // 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)),
571 .expected = 0b1110_00_1_1101_0_0000_0000_0000_00101010,707 .expected = 0b1110_00_1_1101_0_0000_0000_0000_00101010,
572 },708 },
709 .{ // mrs r5, cpsr
710 .inst = Instruction.mrs(.al, .r5, .cpsr),
711 .expected = 0b1110_00010_0_001111_0101_000000000000,
712 },
573 .{ // ldr r0, [r2, #42]713 .{ // ldr r0, [r2, #42]
574 .inst = Instruction.ldr(.al, .r0, .r2, Instruction.Offset.imm(42)),714 .inst = Instruction.ldr(.al, .r0, .r2, Instruction.Offset.imm(42)),
575 .expected = 0b1110_01_0_1_1_0_0_1_0010_0000_000000101010,715 .expected = 0b1110_01_0_1_1_0_0_1_0010_0000_000000101010,
...@@ -598,6 +738,10 @@ test "serialize instructions" {...@@ -598,6 +738,10 @@ test "serialize instructions" {
598 .inst = Instruction.bkpt(42),738 .inst = Instruction.bkpt(42),
599 .expected = 0b1110_0001_0010_000000000010_0111_1010,739 .expected = 0b1110_0001_0010_000000000010_0111_1010,
600 },740 },
741 .{ // stmfd r9, {r0}
742 .inst = Instruction.stm(.al, .r9, .{ .r0 = true }),
743 .expected = 0b1110_100_1_0_0_0_0_1001_0000000000000001,
744 },
601 };745 };
602746
603 for (testcases) |case| {747 for (testcases) |case| {