| ... | ... | @@ -200,17 +200,6 @@ test "FloatingPointRegister.toX" { |
| 200 | 200 | |
| 201 | 201 | /// Represents an instruction in the AArch64 instruction set |
| 202 | 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 | 203 | MoveWideImmediate: packed struct { |
| 215 | 204 | rd: u5, |
| 216 | 205 | imm16: u16, |
| ... | ... | @@ -274,10 +263,37 @@ pub const Instruction = union(enum) { |
| 274 | 263 | NoOperation: packed struct { |
| 275 | 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 | 295 | pub fn toU32(self: Instruction) u32 { |
| 279 | 296 | return switch (self) { |
| 280 | | .OrShiftedRegister => |v| @bitCast(u32, v), |
| 281 | 297 | .MoveWideImmediate => |v| @bitCast(u32, v), |
| 282 | 298 | .PCRelativeAddress => |v| @bitCast(u32, v), |
| 283 | 299 | .LoadStoreRegister => |v| @bitCast(u32, v), |
| ... | ... | @@ -287,68 +303,10 @@ pub const Instruction = union(enum) { |
| 287 | 303 | .UnconditionalBranchRegister => |v| @bitCast(u32, v), |
| 288 | 304 | .UnconditionalBranchImmediate => |v| @bitCast(u32, v), |
| 289 | 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 | 310 | fn moveWideImmediate( |
| 353 | 311 | opc: u2, |
| 354 | 312 | rd: Register, |
| ... | ... | @@ -671,15 +629,49 @@ pub const Instruction = union(enum) { |
| 671 | 629 | }; |
| 672 | 630 | } |
| 673 | 631 | |
| 674 | | // Bitwise (inclusive) OR of a register value |
| 675 | | |
| 676 | | pub fn orr(rd: Register, rm: Register, shift: RegisterShift) Instruction { |
| 677 | | return orShiftedRegister(rd, rm, shift, false); |
| 632 | fn logicalShiftedRegister( |
| 633 | opc: u2, |
| 634 | n: u1, |
| 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 { |
| 681 | | return orShiftedRegister(rd, rm, shift, true); |
| 682 | | } |
| 674 | // Helper functions for assembly syntax functions |
| 683 | 675 | |
| 684 | 676 | // Move wide (immediate) |
| 685 | 677 | |
| ... | ... | @@ -823,6 +815,40 @@ pub const Instruction = union(enum) { |
| 823 | 815 | pub fn nop() Instruction { |
| 824 | 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 | 854 | test "" { |
| ... | ... | @@ -836,15 +862,15 @@ test "serialize instructions" { |
| 836 | 862 | }; |
| 837 | 863 | |
| 838 | 864 | const testcases = [_]Testcase{ |
| 839 | | .{ // orr x0 x1 |
| 840 | | .inst = Instruction.orr(.x0, .x1, Instruction.RegisterShift.none()), |
| 865 | .{ // orr x0, xzr, x1 |
| 866 | .inst = Instruction.orr(.x0, .xzr, .x1, Instruction.Shift.none), |
| 841 | 867 | .expected = 0b1_01_01010_00_0_00001_000000_11111_00000, |
| 842 | 868 | }, |
| 843 | | .{ // orn x0 x1 |
| 844 | | .inst = Instruction.orn(.x0, .x1, Instruction.RegisterShift.none()), |
| 869 | .{ // orn x0, xzr, x1 |
| 870 | .inst = Instruction.orn(.x0, .xzr, .x1, Instruction.Shift.none), |
| 845 | 871 | .expected = 0b1_01_01010_00_1_00001_000000_11111_00000, |
| 846 | 872 | }, |
| 847 | | .{ // movz x1 #4 |
| 873 | .{ // movz x1, #4 |
| 848 | 874 | .inst = Instruction.movz(.x1, 4, 0), |
| 849 | 875 | .expected = 0b1_10_100101_00_0000000000000100_00001, |
| 850 | 876 | }, |
| ... | ... | @@ -944,6 +970,14 @@ test "serialize instructions" { |
| 944 | 970 | .inst = Instruction.ldp(.x1, .x2, Register.sp, Instruction.LoadStorePairOffset.post_index(16)), |
| 945 | 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 | 983 | for (testcases) |case| { |