| ... | ... | @@ -19,7 +19,7 @@ pub const Register = enum(u6) { |
| 19 | 19 | w16, w17, w18, w19, w20, w21, w22, w23, |
| 20 | 20 | w24, w25, w26, w27, w28, w29, w30, wzr, |
| 21 | 21 | |
| 22 | | pub const sp = .xzr; |
| 22 | pub const sp = Register.xzr; |
| 23 | 23 | |
| 24 | 24 | pub fn id(self: Register) u5 { |
| 25 | 25 | return @truncate(u5, @enumToInt(self)); |
| ... | ... | @@ -72,6 +72,9 @@ test "Register.id" { |
| 72 | 72 | |
| 73 | 73 | testing.expectEqual(@as(u5, 31), Register.xzr.id()); |
| 74 | 74 | testing.expectEqual(@as(u5, 31), Register.wzr.id()); |
| 75 | |
| 76 | testing.expectEqual(@as(u5, 31), Register.sp.id()); |
| 77 | testing.expectEqual(@as(u5, 31), Register.sp.id()); |
| 75 | 78 | } |
| 76 | 79 | |
| 77 | 80 | test "Register.size" { |
| ... | ... | @@ -232,6 +235,16 @@ pub const Instruction = union(enum) { |
| 232 | 235 | fixed: u4 = 0b111_0, |
| 233 | 236 | size: u2, |
| 234 | 237 | }, |
| 238 | LoadStorePairOfRegisters: packed struct { |
| 239 | rt1: u5, |
| 240 | rn: u5, |
| 241 | rt2: u5, |
| 242 | imm7: u7, |
| 243 | load: u1, |
| 244 | encoding: u2, |
| 245 | fixed: u5 = 0b101_0_0, |
| 246 | opc: u2, |
| 247 | }, |
| 235 | 248 | LoadLiteral: packed struct { |
| 236 | 249 | rt: u5, |
| 237 | 250 | imm19: u19, |
| ... | ... | @@ -268,6 +281,7 @@ pub const Instruction = union(enum) { |
| 268 | 281 | .MoveWideImmediate => |v| @bitCast(u32, v), |
| 269 | 282 | .PCRelativeAddress => |v| @bitCast(u32, v), |
| 270 | 283 | .LoadStoreRegister => |v| @bitCast(u32, v), |
| 284 | .LoadStorePairOfRegisters => |v| @bitCast(u32, v), |
| 271 | 285 | .LoadLiteral => |v| @bitCast(u32, v), |
| 272 | 286 | .ExceptionGeneration => |v| @bitCast(u32, v), |
| 273 | 287 | .UnconditionalBranchRegister => |v| @bitCast(u32, v), |
| ... | ... | @@ -542,6 +556,46 @@ pub const Instruction = union(enum) { |
| 542 | 556 | } |
| 543 | 557 | } |
| 544 | 558 | |
| 559 | fn loadStorePairOfRegisters( |
| 560 | rt1: Register, |
| 561 | rt2: Register, |
| 562 | rn: Register, |
| 563 | imm7: i7, |
| 564 | encoding: u2, |
| 565 | load: bool, |
| 566 | ) Instruction { |
| 567 | const imm7_u: u7 = @bitCast(u7, imm7); |
| 568 | switch (rt1.size()) { |
| 569 | 32 => { |
| 570 | return Instruction{ |
| 571 | .LoadStorePairOfRegisters = .{ |
| 572 | .rt1 = rt1.id(), |
| 573 | .rn = rn.id(), |
| 574 | .rt2 = rt2.id(), |
| 575 | .imm7 = imm7_u, |
| 576 | .load = @boolToInt(load), |
| 577 | .encoding = encoding, |
| 578 | .opc = 0b00, |
| 579 | }, |
| 580 | }; |
| 581 | }, |
| 582 | 64 => { |
| 583 | return Instruction{ |
| 584 | .LoadStorePairOfRegisters = .{ |
| 585 | .rt1 = rt1.id(), |
| 586 | .rn = rn.id(), |
| 587 | .rt2 = rt2.id(), |
| 588 | .imm7 = imm7_u, |
| 589 | .load = @boolToInt(load), |
| 590 | .encoding = encoding, |
| 591 | .opc = 0b10, |
| 592 | }, |
| 593 | }; |
| 594 | }, |
| 595 | else => unreachable, // unexpected register size |
| 596 | } |
| 597 | } |
| 598 | |
| 545 | 599 | fn loadLiteral(rt: Register, imm19: u19) Instruction { |
| 546 | 600 | switch (rt.size()) { |
| 547 | 601 | 32 => { |
| ... | ... | @@ -670,6 +724,29 @@ pub const Instruction = union(enum) { |
| 670 | 724 | return loadStoreRegister(rt, rn, args.offset, false); |
| 671 | 725 | } |
| 672 | 726 | |
| 727 | // Load or store pair of registers |
| 728 | |
| 729 | pub const LoadStorePairEncoding = enum(u2) { |
| 730 | PostIndex = 0b01, |
| 731 | SignedOffset = 0b10, |
| 732 | PreIndex = 0b11, |
| 733 | }; |
| 734 | pub fn ldp(rt1: Register, rt2: Register, rn: Register, imm: i7, encoding: LoadStorePairEncoding) Instruction { |
| 735 | return loadStorePairOfRegisters(rt1, rt2, rn, imm, @enumToInt(encoding), true); |
| 736 | } |
| 737 | |
| 738 | pub fn ldnp(rt1: Register, rt2: Register, rn: Register, imm: i7) Instruction { |
| 739 | return loadStorePairOfRegisters(rt1, rt2, rn, imm, 0, true); |
| 740 | } |
| 741 | |
| 742 | pub fn stp(rt1: Register, rt2: Register, rn: Register, imm: i7, encoding: LoadStorePairEncoding) Instruction { |
| 743 | return loadStorePairOfRegisters(rt1, rt2, rn, imm, @enumToInt(encoding), false); |
| 744 | } |
| 745 | |
| 746 | pub fn stnp(rt1: Register, rt2: Register, rn: Register, imm: i7) Instruction { |
| 747 | return loadStorePairOfRegisters(rt1, rt2, rn, imm, 0, false); |
| 748 | } |
| 749 | |
| 673 | 750 | // Exception generation |
| 674 | 751 | |
| 675 | 752 | pub fn svc(imm16: u16) Instruction { |
| ... | ... | @@ -826,10 +903,29 @@ test "serialize instructions" { |
| 826 | 903 | .inst = Instruction.adrp(.x2, -0x8), |
| 827 | 904 | .expected = 0b1_00_10000_1111111111111111110_00010, |
| 828 | 905 | }, |
| 906 | .{ // stp x1, x2, [sp, #1] |
| 907 | .inst = Instruction.stp(.x1, .x2, Register.sp, 1, .SignedOffset), |
| 908 | .expected = 0b10_101_0_010_0_0000001_00010_11111_00001, |
| 909 | }, |
| 910 | .{ // stp x1, x2, [sp, #-16] |
| 911 | .inst = Instruction.stp(.x1, .x2, Register.sp, -16, .SignedOffset), |
| 912 | .expected = 0b10_101_0_010_0_1110000_00010_11111_00001, |
| 913 | }, |
| 914 | .{ // ldp x1, x2, [sp, #1] |
| 915 | .inst = Instruction.ldp(.x1, .x2, Register.sp, 1, .SignedOffset), |
| 916 | .expected = 0b10_101_0_010_1_0000001_00010_11111_00001, |
| 917 | }, |
| 918 | .{ // ldp x1, x2, [sp, #16] |
| 919 | .inst = Instruction.ldp(.x1, .x2, Register.sp, 16, .SignedOffset), |
| 920 | .expected = 0b10_101_0_010_1_0010000_00010_11111_00001, |
| 921 | }, |
| 829 | 922 | }; |
| 830 | 923 | |
| 831 | 924 | for (testcases) |case| { |
| 832 | 925 | const actual = case.inst.toU32(); |
| 926 | if (case.expected != actual) { |
| 927 | std.debug.print("0b{b} != 0b{b}\n", .{ case.expected, actual }); |
| 928 | } |
| 833 | 929 | testing.expectEqual(case.expected, actual); |
| 834 | 930 | } |
| 835 | 931 | } |