| ... | @@ -200,17 +200,6 @@ test "FloatingPointRegister.toX" { | ... | @@ -200,17 +200,6 @@ test "FloatingPointRegister.toX" { |
| 200 | | 200 | |
| 201 | /// Represents an instruction in the AArch64 instruction set | 201 | /// Represents an instruction in the AArch64 instruction set |
| 202 | pub const Instruction = union(enum) { | 202 | pub const Instruction = union(enum) { |
| 203 | OrShiftedRegister: packed struct { | | |
| 204 | rd: u5, | | |
| 205 | rn: u5, | | |
| 206 | imm6: u6, | | |
| 207 | rm: u5, | | |
| 208 | n: u1, | | |
| 209 | shift: u2, | | |
| 210 | fixed: u5 = 0b01010, | | |
| 211 | opc: u2 = 0b01, | | |
| 212 | sf: u1, | | |
| 213 | }, | | |
| 214 | MoveWideImmediate: packed struct { | 203 | MoveWideImmediate: packed struct { |
| 215 | rd: u5, | 204 | rd: u5, |
| 216 | imm16: u16, | 205 | imm16: u16, |
| ... | @@ -274,10 +263,37 @@ pub const Instruction = union(enum) { | ... | @@ -274,10 +263,37 @@ pub const Instruction = union(enum) { |
| 274 | NoOperation: packed struct { | 263 | NoOperation: packed struct { |
| 275 | fixed: u32 = 0b1101010100_0_00_011_0010_0000_000_11111, | 264 | fixed: u32 = 0b1101010100_0_00_011_0010_0000_000_11111, |
| 276 | }, | 265 | }, |
| | 266 | LogicalShiftedRegister: packed struct { |
| | 267 | rd: u5, |
| | 268 | rn: u5, |
| | 269 | imm6: u6, |
| | 270 | rm: u5, |
| | 271 | n: u1, |
| | 272 | shift: u2, |
| | 273 | fixed: u5 = 0b01010, |
| | 274 | opc: u2, |
| | 275 | sf: u1, |
| | 276 | }, |
| | 277 | |
| | 278 | pub const Shift = struct { |
| | 279 | shift: Type = .lsl, |
| | 280 | amount: u6 = 0, |
| | 281 | |
| | 282 | pub const Type = enum(u2) { |
| | 283 | lsl, |
| | 284 | lsr, |
| | 285 | asr, |
| | 286 | ror, |
| | 287 | }; |
| | 288 | |
| | 289 | pub const none = Shift{ |
| | 290 | .shift = .lsl, |
| | 291 | .amount = 0, |
| | 292 | }; |
| | 293 | }; |
| 277 | | 294 | |
| 278 | pub fn toU32(self: Instruction) u32 { | 295 | pub fn toU32(self: Instruction) u32 { |
| 279 | return switch (self) { | 296 | return switch (self) { |
| 280 | .OrShiftedRegister => |v| @bitCast(u32, v), | | |
| 281 | .MoveWideImmediate => |v| @bitCast(u32, v), | 297 | .MoveWideImmediate => |v| @bitCast(u32, v), |
| 282 | .PCRelativeAddress => |v| @bitCast(u32, v), | 298 | .PCRelativeAddress => |v| @bitCast(u32, v), |
| 283 | .LoadStoreRegister => |v| @bitCast(u32, v), | 299 | .LoadStoreRegister => |v| @bitCast(u32, v), |
| ... | @@ -287,68 +303,10 @@ pub const Instruction = union(enum) { | ... | @@ -287,68 +303,10 @@ pub const Instruction = union(enum) { |
| 287 | .UnconditionalBranchRegister => |v| @bitCast(u32, v), | 303 | .UnconditionalBranchRegister => |v| @bitCast(u32, v), |
| 288 | .UnconditionalBranchImmediate => |v| @bitCast(u32, v), | 304 | .UnconditionalBranchImmediate => |v| @bitCast(u32, v), |
| 289 | .NoOperation => |v| @bitCast(u32, v), | 305 | .NoOperation => |v| @bitCast(u32, v), |
| | 306 | .LogicalShiftedRegister => |v| @bitCast(u32, v), |
| 290 | }; | 307 | }; |
| 291 | } | 308 | } |
| 292 | | 309 | |
| 293 | pub const RegisterShift = struct { | | |
| 294 | rn: u5, | | |
| 295 | imm6: u6, | | |
| 296 | shift: enum(u2) { | | |
| 297 | Lsl = 0, | | |
| 298 | Lsr = 1, | | |
| 299 | Asr = 2, | | |
| 300 | Ror = 3, | | |
| 301 | }, | | |
| 302 | | | |
| 303 | pub fn none() RegisterShift { | | |
| 304 | return .{ | | |
| 305 | .rn = 0b11111, | | |
| 306 | .imm6 = 0, | | |
| 307 | .shift = .Lsl, | | |
| 308 | }; | | |
| 309 | } | | |
| 310 | }; | | |
| 311 | | | |
| 312 | // Helper functions for assembly syntax functions | | |
| 313 | | | |
| 314 | fn orShiftedRegister( | | |
| 315 | rd: Register, | | |
| 316 | rm: Register, | | |
| 317 | shift: RegisterShift, | | |
| 318 | invert: bool, | | |
| 319 | ) Instruction { | | |
| 320 | const n: u1 = if (invert) 1 else 0; | | |
| 321 | switch (rd.size()) { | | |
| 322 | 32 => { | | |
| 323 | return Instruction{ | | |
| 324 | .OrShiftedRegister = .{ | | |
| 325 | .rd = rd.id(), | | |
| 326 | .rn = shift.rn, | | |
| 327 | .imm6 = shift.imm6, | | |
| 328 | .rm = rm.id(), | | |
| 329 | .n = n, | | |
| 330 | .shift = @enumToInt(shift.shift), | | |
| 331 | .sf = 0, | | |
| 332 | }, | | |
| 333 | }; | | |
| 334 | }, | | |
| 335 | 64 => { | | |
| 336 | return Instruction{ | | |
| 337 | .OrShiftedRegister = .{ | | |
| 338 | .rd = rd.id(), | | |
| 339 | .rn = shift.rn, | | |
| 340 | .imm6 = shift.imm6, | | |
| 341 | .rm = rm.id(), | | |
| 342 | .n = n, | | |
| 343 | .shift = @enumToInt(shift.shift), | | |
| 344 | .sf = 1, | | |
| 345 | }, | | |
| 346 | }; | | |
| 347 | }, | | |
| 348 | else => unreachable, // unexpected register size | | |
| 349 | } | | |
| 350 | } | | |
| 351 | | | |
| 352 | fn moveWideImmediate( | 310 | fn moveWideImmediate( |
| 353 | opc: u2, | 311 | opc: u2, |
| 354 | rd: Register, | 312 | rd: Register, |
| ... | @@ -671,15 +629,49 @@ pub const Instruction = union(enum) { | ... | @@ -671,15 +629,49 @@ pub const Instruction = union(enum) { |
| 671 | }; | 629 | }; |
| 672 | } | 630 | } |
| 673 | | 631 | |
| 674 | // Bitwise (inclusive) OR of a register value | 632 | fn logicalShiftedRegister( |
| 675 | | 633 | opc: u2, |
| 676 | pub fn orr(rd: Register, rm: Register, shift: RegisterShift) Instruction { | 634 | n: u1, |
| 677 | return orShiftedRegister(rd, rm, shift, false); | 635 | shift: Shift, |
| | 636 | rd: Register, |
| | 637 | rn: Register, |
| | 638 | rm: Register, |
| | 639 | ) Instruction { |
| | 640 | switch (rd.size()) { |
| | 641 | 32 => { |
| | 642 | assert(shift.amount < 32); |
| | 643 | return Instruction{ |
| | 644 | .LogicalShiftedRegister = .{ |
| | 645 | .rd = rd.id(), |
| | 646 | .rn = rn.id(), |
| | 647 | .imm6 = shift.amount, |
| | 648 | .rm = rm.id(), |
| | 649 | .n = n, |
| | 650 | .shift = @enumToInt(shift.shift), |
| | 651 | .opc = opc, |
| | 652 | .sf = 0b0, |
| | 653 | }, |
| | 654 | }; |
| | 655 | }, |
| | 656 | 64 => { |
| | 657 | return Instruction{ |
| | 658 | .LogicalShiftedRegister = .{ |
| | 659 | .rd = rd.id(), |
| | 660 | .rn = rn.id(), |
| | 661 | .imm6 = shift.amount, |
| | 662 | .rm = rm.id(), |
| | 663 | .n = n, |
| | 664 | .shift = @enumToInt(shift.shift), |
| | 665 | .opc = opc, |
| | 666 | .sf = 0b1, |
| | 667 | }, |
| | 668 | }; |
| | 669 | }, |
| | 670 | else => unreachable, // unexpected register size |
| | 671 | } |
| 678 | } | 672 | } |
| 679 | | 673 | |
| 680 | pub fn orn(rd: Register, rm: Register, shift: RegisterShift) Instruction { | 674 | // Helper functions for assembly syntax functions |
| 681 | return orShiftedRegister(rd, rm, shift, true); | | |
| 682 | } | | |
| 683 | | 675 | |
| 684 | // Move wide (immediate) | 676 | // Move wide (immediate) |
| 685 | | 677 | |
| ... | @@ -823,6 +815,40 @@ pub const Instruction = union(enum) { | ... | @@ -823,6 +815,40 @@ pub const Instruction = union(enum) { |
| 823 | pub fn nop() Instruction { | 815 | pub fn nop() Instruction { |
| 824 | return Instruction{ .NoOperation = {} }; | 816 | return Instruction{ .NoOperation = {} }; |
| 825 | } | 817 | } |
| | 818 | |
| | 819 | // Logical (shifted register) |
| | 820 | |
| | 821 | pub fn @"and"(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { |
| | 822 | return logicalShiftedRegister(0b00, 0b0, shift, rd, rn, rm); |
| | 823 | } |
| | 824 | |
| | 825 | pub fn bic(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { |
| | 826 | return logicalShiftedRegister(0b00, 0b1, shift, rd, rn, rm); |
| | 827 | } |
| | 828 | |
| | 829 | pub fn orr(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { |
| | 830 | return logicalShiftedRegister(0b01, 0b0, shift, rd, rn, rm); |
| | 831 | } |
| | 832 | |
| | 833 | pub fn orn(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { |
| | 834 | return logicalShiftedRegister(0b01, 0b1, shift, rd, rn, rm); |
| | 835 | } |
| | 836 | |
| | 837 | pub fn eor(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { |
| | 838 | return logicalShiftedRegister(0b10, 0b0, shift, rd, rn, rm); |
| | 839 | } |
| | 840 | |
| | 841 | pub fn eon(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { |
| | 842 | return logicalShiftedRegister(0b10, 0b1, shift, rd, rn, rm); |
| | 843 | } |
| | 844 | |
| | 845 | pub fn ands(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { |
| | 846 | return logicalShiftedRegister(0b11, 0b0, shift, rd, rn, rm); |
| | 847 | } |
| | 848 | |
| | 849 | pub fn bics(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { |
| | 850 | return logicalShiftedRegister(0b11, 0b1, shift, rd, rn, rm); |
| | 851 | } |
| 826 | }; | 852 | }; |
| 827 | | 853 | |
| 828 | test "" { | 854 | test "" { |
| ... | @@ -836,15 +862,15 @@ test "serialize instructions" { | ... | @@ -836,15 +862,15 @@ test "serialize instructions" { |
| 836 | }; | 862 | }; |
| 837 | | 863 | |
| 838 | const testcases = [_]Testcase{ | 864 | const testcases = [_]Testcase{ |
| 839 | .{ // orr x0 x1 | 865 | .{ // orr x0, xzr, x1 |
| 840 | .inst = Instruction.orr(.x0, .x1, Instruction.RegisterShift.none()), | 866 | .inst = Instruction.orr(.x0, .xzr, .x1, Instruction.Shift.none), |
| 841 | .expected = 0b1_01_01010_00_0_00001_000000_11111_00000, | 867 | .expected = 0b1_01_01010_00_0_00001_000000_11111_00000, |
| 842 | }, | 868 | }, |
| 843 | .{ // orn x0 x1 | 869 | .{ // orn x0, xzr, x1 |
| 844 | .inst = Instruction.orn(.x0, .x1, Instruction.RegisterShift.none()), | 870 | .inst = Instruction.orn(.x0, .xzr, .x1, Instruction.Shift.none), |
| 845 | .expected = 0b1_01_01010_00_1_00001_000000_11111_00000, | 871 | .expected = 0b1_01_01010_00_1_00001_000000_11111_00000, |
| 846 | }, | 872 | }, |
| 847 | .{ // movz x1 #4 | 873 | .{ // movz x1, #4 |
| 848 | .inst = Instruction.movz(.x1, 4, 0), | 874 | .inst = Instruction.movz(.x1, 4, 0), |
| 849 | .expected = 0b1_10_100101_00_0000000000000100_00001, | 875 | .expected = 0b1_10_100101_00_0000000000000100_00001, |
| 850 | }, | 876 | }, |
| ... | @@ -944,6 +970,14 @@ test "serialize instructions" { | ... | @@ -944,6 +970,14 @@ test "serialize instructions" { |
| 944 | .inst = Instruction.ldp(.x1, .x2, Register.sp, Instruction.LoadStorePairOffset.post_index(16)), | 970 | .inst = Instruction.ldp(.x1, .x2, Register.sp, Instruction.LoadStorePairOffset.post_index(16)), |
| 945 | .expected = 0b10_101_0_001_1_0000010_00010_11111_00001, | 971 | .expected = 0b10_101_0_001_1_0000010_00010_11111_00001, |
| 946 | }, | 972 | }, |
| | 973 | .{ // and x0, x4, x2 |
| | 974 | .inst = Instruction.@"and"(.x0, .x4, .x2, .{}), |
| | 975 | .expected = 0b1_00_01010_00_0_00010_000000_00100_00000, |
| | 976 | }, |
| | 977 | .{ // and x0, x4, x2, lsl #0x8 |
| | 978 | .inst = Instruction.@"and"(.x0, .x4, .x2, .{ .shift = .lsl, .amount = 0x8 }), |
| | 979 | .expected = 0b1_00_01010_00_0_00010_001000_00100_00000, |
| | 980 | }, |
| 947 | }; | 981 | }; |
| 948 | | 982 | |
| 949 | for (testcases) |case| { | 983 | for (testcases) |case| { |