| ... | @@ -19,7 +19,7 @@ pub const Register = enum(u6) { | ... | @@ -19,7 +19,7 @@ pub const Register = enum(u6) { |
| 19 | w16, w17, w18, w19, w20, w21, w22, w23, | 19 | w16, w17, w18, w19, w20, w21, w22, w23, |
| 20 | w24, w25, w26, w27, w28, w29, w30, wzr, | 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 | pub fn id(self: Register) u5 { | 24 | pub fn id(self: Register) u5 { |
| 25 | return @truncate(u5, @enumToInt(self)); | 25 | return @truncate(u5, @enumToInt(self)); |
| ... | @@ -72,6 +72,9 @@ test "Register.id" { | ... | @@ -72,6 +72,9 @@ test "Register.id" { |
| 72 | | 72 | |
| 73 | testing.expectEqual(@as(u5, 31), Register.xzr.id()); | 73 | testing.expectEqual(@as(u5, 31), Register.xzr.id()); |
| 74 | testing.expectEqual(@as(u5, 31), Register.wzr.id()); | 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 | test "Register.size" { | 80 | test "Register.size" { |
| ... | @@ -232,6 +235,16 @@ pub const Instruction = union(enum) { | ... | @@ -232,6 +235,16 @@ pub const Instruction = union(enum) { |
| 232 | fixed: u4 = 0b111_0, | 235 | fixed: u4 = 0b111_0, |
| 233 | size: u2, | 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 | LoadLiteral: packed struct { | 248 | LoadLiteral: packed struct { |
| 236 | rt: u5, | 249 | rt: u5, |
| 237 | imm19: u19, | 250 | imm19: u19, |
| ... | @@ -268,6 +281,7 @@ pub const Instruction = union(enum) { | ... | @@ -268,6 +281,7 @@ pub const Instruction = union(enum) { |
| 268 | .MoveWideImmediate => |v| @bitCast(u32, v), | 281 | .MoveWideImmediate => |v| @bitCast(u32, v), |
| 269 | .PCRelativeAddress => |v| @bitCast(u32, v), | 282 | .PCRelativeAddress => |v| @bitCast(u32, v), |
| 270 | .LoadStoreRegister => |v| @bitCast(u32, v), | 283 | .LoadStoreRegister => |v| @bitCast(u32, v), |
| | 284 | .LoadStorePairOfRegisters => |v| @bitCast(u32, v), |
| 271 | .LoadLiteral => |v| @bitCast(u32, v), | 285 | .LoadLiteral => |v| @bitCast(u32, v), |
| 272 | .ExceptionGeneration => |v| @bitCast(u32, v), | 286 | .ExceptionGeneration => |v| @bitCast(u32, v), |
| 273 | .UnconditionalBranchRegister => |v| @bitCast(u32, v), | 287 | .UnconditionalBranchRegister => |v| @bitCast(u32, v), |
| ... | @@ -542,6 +556,46 @@ pub const Instruction = union(enum) { | ... | @@ -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 | fn loadLiteral(rt: Register, imm19: u19) Instruction { | 599 | fn loadLiteral(rt: Register, imm19: u19) Instruction { |
| 546 | switch (rt.size()) { | 600 | switch (rt.size()) { |
| 547 | 32 => { | 601 | 32 => { |
| ... | @@ -670,6 +724,29 @@ pub const Instruction = union(enum) { | ... | @@ -670,6 +724,29 @@ pub const Instruction = union(enum) { |
| 670 | return loadStoreRegister(rt, rn, args.offset, false); | 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 | // Exception generation | 750 | // Exception generation |
| 674 | | 751 | |
| 675 | pub fn svc(imm16: u16) Instruction { | 752 | pub fn svc(imm16: u16) Instruction { |
| ... | @@ -826,10 +903,29 @@ test "serialize instructions" { | ... | @@ -826,10 +903,29 @@ test "serialize instructions" { |
| 826 | .inst = Instruction.adrp(.x2, -0x8), | 903 | .inst = Instruction.adrp(.x2, -0x8), |
| 827 | .expected = 0b1_00_10000_1111111111111111110_00010, | 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 | for (testcases) |case| { | 924 | for (testcases) |case| { |
| 832 | const actual = case.inst.toU32(); | 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 | testing.expectEqual(case.expected, actual); | 929 | testing.expectEqual(case.expected, actual); |
| 834 | } | 930 | } |
| 835 | } | 931 | } |