| ... | @@ -192,7 +192,7 @@ pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 }; | ... | @@ -192,7 +192,7 @@ pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 }; |
| 192 | | 192 | |
| 193 | /// Represents an instruction in the ARM instruction set architecture | 193 | /// Represents an instruction in the ARM instruction set architecture |
| 194 | pub const Instruction = union(enum) { | 194 | pub const Instruction = union(enum) { |
| 195 | DataProcessing: packed struct { | 195 | data_processing: packed struct { |
| 196 | // Note to self: The order of the fields top-to-bottom is | 196 | // Note to self: The order of the fields top-to-bottom is |
| 197 | // right-to-left in the actual 32-bit int representation | 197 | // right-to-left in the actual 32-bit int representation |
| 198 | op2: u12, | 198 | op2: u12, |
| ... | @@ -204,7 +204,7 @@ pub const Instruction = union(enum) { | ... | @@ -204,7 +204,7 @@ pub const Instruction = union(enum) { |
| 204 | fixed: u2 = 0b00, | 204 | fixed: u2 = 0b00, |
| 205 | cond: u4, | 205 | cond: u4, |
| 206 | }, | 206 | }, |
| 207 | Multiply: packed struct { | 207 | multiply: packed struct { |
| 208 | rn: u4, | 208 | rn: u4, |
| 209 | fixed_1: u4 = 0b1001, | 209 | fixed_1: u4 = 0b1001, |
| 210 | rm: u4, | 210 | rm: u4, |
| ... | @@ -215,7 +215,7 @@ pub const Instruction = union(enum) { | ... | @@ -215,7 +215,7 @@ pub const Instruction = union(enum) { |
| 215 | fixed_2: u6 = 0b000000, | 215 | fixed_2: u6 = 0b000000, |
| 216 | cond: u4, | 216 | cond: u4, |
| 217 | }, | 217 | }, |
| 218 | MultiplyLong: packed struct { | 218 | multiply_long: packed struct { |
| 219 | rn: u4, | 219 | rn: u4, |
| 220 | fixed_1: u4 = 0b1001, | 220 | fixed_1: u4 = 0b1001, |
| 221 | rm: u4, | 221 | rm: u4, |
| ... | @@ -227,7 +227,17 @@ pub const Instruction = union(enum) { | ... | @@ -227,7 +227,17 @@ pub const Instruction = union(enum) { |
| 227 | fixed_2: u5 = 0b00001, | 227 | fixed_2: u5 = 0b00001, |
| 228 | cond: u4, | 228 | cond: u4, |
| 229 | }, | 229 | }, |
| 230 | SingleDataTransfer: packed struct { | 230 | integer_saturating_arithmetic: packed struct { |
| | 231 | rm: u4, |
| | 232 | fixed_1: u8 = 0b0000_0101, |
| | 233 | rd: u4, |
| | 234 | rn: u4, |
| | 235 | fixed_2: u1 = 0b0, |
| | 236 | opc: u2, |
| | 237 | fixed_3: u5 = 0b00010, |
| | 238 | cond: u4, |
| | 239 | }, |
| | 240 | single_data_transfer: packed struct { |
| 231 | offset: u12, | 241 | offset: u12, |
| 232 | rd: u4, | 242 | rd: u4, |
| 233 | rn: u4, | 243 | rn: u4, |
| ... | @@ -240,7 +250,7 @@ pub const Instruction = union(enum) { | ... | @@ -240,7 +250,7 @@ pub const Instruction = union(enum) { |
| 240 | fixed: u2 = 0b01, | 250 | fixed: u2 = 0b01, |
| 241 | cond: u4, | 251 | cond: u4, |
| 242 | }, | 252 | }, |
| 243 | ExtraLoadStore: packed struct { | 253 | extra_load_store: packed struct { |
| 244 | imm4l: u4, | 254 | imm4l: u4, |
| 245 | fixed_1: u1 = 0b1, | 255 | fixed_1: u1 = 0b1, |
| 246 | op2: u2, | 256 | op2: u2, |
| ... | @@ -256,7 +266,7 @@ pub const Instruction = union(enum) { | ... | @@ -256,7 +266,7 @@ pub const Instruction = union(enum) { |
| 256 | fixed_3: u3 = 0b000, | 266 | fixed_3: u3 = 0b000, |
| 257 | cond: u4, | 267 | cond: u4, |
| 258 | }, | 268 | }, |
| 259 | BlockDataTransfer: packed struct { | 269 | block_data_transfer: packed struct { |
| 260 | register_list: u16, | 270 | register_list: u16, |
| 261 | rn: u4, | 271 | rn: u4, |
| 262 | load_store: u1, | 272 | load_store: u1, |
| ... | @@ -267,25 +277,25 @@ pub const Instruction = union(enum) { | ... | @@ -267,25 +277,25 @@ pub const Instruction = union(enum) { |
| 267 | fixed: u3 = 0b100, | 277 | fixed: u3 = 0b100, |
| 268 | cond: u4, | 278 | cond: u4, |
| 269 | }, | 279 | }, |
| 270 | Branch: packed struct { | 280 | branch: packed struct { |
| 271 | offset: u24, | 281 | offset: u24, |
| 272 | link: u1, | 282 | link: u1, |
| 273 | fixed: u3 = 0b101, | 283 | fixed: u3 = 0b101, |
| 274 | cond: u4, | 284 | cond: u4, |
| 275 | }, | 285 | }, |
| 276 | BranchExchange: packed struct { | 286 | branch_exchange: packed struct { |
| 277 | rn: u4, | 287 | rn: u4, |
| 278 | fixed_1: u1 = 0b1, | 288 | fixed_1: u1 = 0b1, |
| 279 | link: u1, | 289 | link: u1, |
| 280 | fixed_2: u22 = 0b0001_0010_1111_1111_1111_00, | 290 | fixed_2: u22 = 0b0001_0010_1111_1111_1111_00, |
| 281 | cond: u4, | 291 | cond: u4, |
| 282 | }, | 292 | }, |
| 283 | SupervisorCall: packed struct { | 293 | supervisor_call: packed struct { |
| 284 | comment: u24, | 294 | comment: u24, |
| 285 | fixed: u4 = 0b1111, | 295 | fixed: u4 = 0b1111, |
| 286 | cond: u4, | 296 | cond: u4, |
| 287 | }, | 297 | }, |
| 288 | Breakpoint: packed struct { | 298 | breakpoint: packed struct { |
| 289 | imm4: u4, | 299 | imm4: u4, |
| 290 | fixed_1: u4 = 0b0111, | 300 | fixed_1: u4 = 0b0111, |
| 291 | imm12: u12, | 301 | imm12: u12, |
| ... | @@ -293,7 +303,7 @@ pub const Instruction = union(enum) { | ... | @@ -293,7 +303,7 @@ pub const Instruction = union(enum) { |
| 293 | }, | 303 | }, |
| 294 | | 304 | |
| 295 | /// Represents the possible operations which can be performed by a | 305 | /// Represents the possible operations which can be performed by a |
| 296 | /// DataProcessing instruction | 306 | /// Data Processing instruction |
| 297 | const Opcode = enum(u4) { | 307 | const Opcode = enum(u4) { |
| 298 | // Rd := Op1 AND Op2 | 308 | // Rd := Op1 AND Op2 |
| 299 | @"and", | 309 | @"and", |
| ... | @@ -530,16 +540,17 @@ pub const Instruction = union(enum) { | ... | @@ -530,16 +540,17 @@ pub const Instruction = union(enum) { |
| 530 | | 540 | |
| 531 | pub fn toU32(self: Instruction) u32 { | 541 | pub fn toU32(self: Instruction) u32 { |
| 532 | return switch (self) { | 542 | return switch (self) { |
| 533 | .DataProcessing => |v| @bitCast(u32, v), | 543 | .data_processing => |v| @bitCast(u32, v), |
| 534 | .Multiply => |v| @bitCast(u32, v), | 544 | .multiply => |v| @bitCast(u32, v), |
| 535 | .MultiplyLong => |v| @bitCast(u32, v), | 545 | .multiply_long => |v| @bitCast(u32, v), |
| 536 | .SingleDataTransfer => |v| @bitCast(u32, v), | 546 | .integer_saturating_arithmetic => |v| @bitCast(u32, v), |
| 537 | .ExtraLoadStore => |v| @bitCast(u32, v), | 547 | .single_data_transfer => |v| @bitCast(u32, v), |
| 538 | .BlockDataTransfer => |v| @bitCast(u32, v), | 548 | .extra_load_store => |v| @bitCast(u32, v), |
| 539 | .Branch => |v| @bitCast(u32, v), | 549 | .block_data_transfer => |v| @bitCast(u32, v), |
| 540 | .BranchExchange => |v| @bitCast(u32, v), | 550 | .branch => |v| @bitCast(u32, v), |
| 541 | .SupervisorCall => |v| @bitCast(u32, v), | 551 | .branch_exchange => |v| @bitCast(u32, v), |
| 542 | .Breakpoint => |v| @intCast(u32, v.imm4) | (@intCast(u32, v.fixed_1) << 4) | (@intCast(u32, v.imm12) << 8) | (@intCast(u32, v.fixed_2_and_cond) << 20), | 552 | .supervisor_call => |v| @bitCast(u32, v), |
| | 553 | .breakpoint => |v| @intCast(u32, v.imm4) | (@intCast(u32, v.fixed_1) << 4) | (@intCast(u32, v.imm12) << 8) | (@intCast(u32, v.fixed_2_and_cond) << 20), |
| 543 | }; | 554 | }; |
| 544 | } | 555 | } |
| 545 | | 556 | |
| ... | @@ -554,7 +565,7 @@ pub const Instruction = union(enum) { | ... | @@ -554,7 +565,7 @@ pub const Instruction = union(enum) { |
| 554 | op2: Operand, | 565 | op2: Operand, |
| 555 | ) Instruction { | 566 | ) Instruction { |
| 556 | return Instruction{ | 567 | return Instruction{ |
| 557 | .DataProcessing = .{ | 568 | .data_processing = .{ |
| 558 | .cond = @enumToInt(cond), | 569 | .cond = @enumToInt(cond), |
| 559 | .i = @boolToInt(op2 == .Immediate), | 570 | .i = @boolToInt(op2 == .Immediate), |
| 560 | .opcode = @enumToInt(opcode), | 571 | .opcode = @enumToInt(opcode), |
| ... | @@ -573,7 +584,7 @@ pub const Instruction = union(enum) { | ... | @@ -573,7 +584,7 @@ pub const Instruction = union(enum) { |
| 573 | top: bool, | 584 | top: bool, |
| 574 | ) Instruction { | 585 | ) Instruction { |
| 575 | return Instruction{ | 586 | return Instruction{ |
| 576 | .DataProcessing = .{ | 587 | .data_processing = .{ |
| 577 | .cond = @enumToInt(cond), | 588 | .cond = @enumToInt(cond), |
| 578 | .i = 1, | 589 | .i = 1, |
| 579 | .opcode = if (top) 0b1010 else 0b1000, | 590 | .opcode = if (top) 0b1010 else 0b1000, |
| ... | @@ -594,7 +605,7 @@ pub const Instruction = union(enum) { | ... | @@ -594,7 +605,7 @@ pub const Instruction = union(enum) { |
| 594 | ra: ?Register, | 605 | ra: ?Register, |
| 595 | ) Instruction { | 606 | ) Instruction { |
| 596 | return Instruction{ | 607 | return Instruction{ |
| 597 | .Multiply = .{ | 608 | .multiply = .{ |
| 598 | .cond = @enumToInt(cond), | 609 | .cond = @enumToInt(cond), |
| 599 | .accumulate = @boolToInt(ra != null), | 610 | .accumulate = @boolToInt(ra != null), |
| 600 | .set_cond = set_cond, | 611 | .set_cond = set_cond, |
| ... | @@ -617,7 +628,7 @@ pub const Instruction = union(enum) { | ... | @@ -617,7 +628,7 @@ pub const Instruction = union(enum) { |
| 617 | rn: Register, | 628 | rn: Register, |
| 618 | ) Instruction { | 629 | ) Instruction { |
| 619 | return Instruction{ | 630 | return Instruction{ |
| 620 | .MultiplyLong = .{ | 631 | .multiply_long = .{ |
| 621 | .cond = @enumToInt(cond), | 632 | .cond = @enumToInt(cond), |
| 622 | .unsigned = signed, | 633 | .unsigned = signed, |
| 623 | .accumulate = accumulate, | 634 | .accumulate = accumulate, |
| ... | @@ -630,6 +641,24 @@ pub const Instruction = union(enum) { | ... | @@ -630,6 +641,24 @@ pub const Instruction = union(enum) { |
| 630 | }; | 641 | }; |
| 631 | } | 642 | } |
| 632 | | 643 | |
| | 644 | fn integerSaturationArithmetic( |
| | 645 | cond: Condition, |
| | 646 | rd: Register, |
| | 647 | rm: Register, |
| | 648 | rn: Register, |
| | 649 | opc: u2, |
| | 650 | ) Instruction { |
| | 651 | return Instruction{ |
| | 652 | .integer_saturating_arithmetic = .{ |
| | 653 | .rm = rm.id(), |
| | 654 | .rd = rd.id(), |
| | 655 | .rn = rn.id(), |
| | 656 | .opc = opc, |
| | 657 | .cond = @enumToInt(cond), |
| | 658 | }, |
| | 659 | }; |
| | 660 | } |
| | 661 | |
| 633 | fn singleDataTransfer( | 662 | fn singleDataTransfer( |
| 634 | cond: Condition, | 663 | cond: Condition, |
| 635 | rd: Register, | 664 | rd: Register, |
| ... | @@ -642,7 +671,7 @@ pub const Instruction = union(enum) { | ... | @@ -642,7 +671,7 @@ pub const Instruction = union(enum) { |
| 642 | load_store: u1, | 671 | load_store: u1, |
| 643 | ) Instruction { | 672 | ) Instruction { |
| 644 | return Instruction{ | 673 | return Instruction{ |
| 645 | .SingleDataTransfer = .{ | 674 | .single_data_transfer = .{ |
| 646 | .cond = @enumToInt(cond), | 675 | .cond = @enumToInt(cond), |
| 647 | .rn = rn.id(), | 676 | .rn = rn.id(), |
| 648 | .rd = rd.id(), | 677 | .rd = rd.id(), |
| ... | @@ -678,7 +707,7 @@ pub const Instruction = union(enum) { | ... | @@ -678,7 +707,7 @@ pub const Instruction = union(enum) { |
| 678 | }; | 707 | }; |
| 679 | | 708 | |
| 680 | return Instruction{ | 709 | return Instruction{ |
| 681 | .ExtraLoadStore = .{ | 710 | .extra_load_store = .{ |
| 682 | .imm4l = imm4l, | 711 | .imm4l = imm4l, |
| 683 | .op2 = op2, | 712 | .op2 = op2, |
| 684 | .imm4h = imm4h, | 713 | .imm4h = imm4h, |
| ... | @@ -705,7 +734,7 @@ pub const Instruction = union(enum) { | ... | @@ -705,7 +734,7 @@ pub const Instruction = union(enum) { |
| 705 | load_store: u1, | 734 | load_store: u1, |
| 706 | ) Instruction { | 735 | ) Instruction { |
| 707 | return Instruction{ | 736 | return Instruction{ |
| 708 | .BlockDataTransfer = .{ | 737 | .block_data_transfer = .{ |
| 709 | .register_list = @bitCast(u16, reg_list), | 738 | .register_list = @bitCast(u16, reg_list), |
| 710 | .rn = rn.id(), | 739 | .rn = rn.id(), |
| 711 | .load_store = load_store, | 740 | .load_store = load_store, |
| ... | @@ -720,7 +749,7 @@ pub const Instruction = union(enum) { | ... | @@ -720,7 +749,7 @@ pub const Instruction = union(enum) { |
| 720 | | 749 | |
| 721 | fn branch(cond: Condition, offset: i26, link: u1) Instruction { | 750 | fn branch(cond: Condition, offset: i26, link: u1) Instruction { |
| 722 | return Instruction{ | 751 | return Instruction{ |
| 723 | .Branch = .{ | 752 | .branch = .{ |
| 724 | .cond = @enumToInt(cond), | 753 | .cond = @enumToInt(cond), |
| 725 | .link = link, | 754 | .link = link, |
| 726 | .offset = @bitCast(u24, @intCast(i24, offset >> 2)), | 755 | .offset = @bitCast(u24, @intCast(i24, offset >> 2)), |
| ... | @@ -730,7 +759,7 @@ pub const Instruction = union(enum) { | ... | @@ -730,7 +759,7 @@ pub const Instruction = union(enum) { |
| 730 | | 759 | |
| 731 | fn branchExchange(cond: Condition, rn: Register, link: u1) Instruction { | 760 | fn branchExchange(cond: Condition, rn: Register, link: u1) Instruction { |
| 732 | return Instruction{ | 761 | return Instruction{ |
| 733 | .BranchExchange = .{ | 762 | .branch_exchange = .{ |
| 734 | .cond = @enumToInt(cond), | 763 | .cond = @enumToInt(cond), |
| 735 | .link = link, | 764 | .link = link, |
| 736 | .rn = rn.id(), | 765 | .rn = rn.id(), |
| ... | @@ -740,7 +769,7 @@ pub const Instruction = union(enum) { | ... | @@ -740,7 +769,7 @@ pub const Instruction = union(enum) { |
| 740 | | 769 | |
| 741 | fn supervisorCall(cond: Condition, comment: u24) Instruction { | 770 | fn supervisorCall(cond: Condition, comment: u24) Instruction { |
| 742 | return Instruction{ | 771 | return Instruction{ |
| 743 | .SupervisorCall = .{ | 772 | .supervisor_call = .{ |
| 744 | .cond = @enumToInt(cond), | 773 | .cond = @enumToInt(cond), |
| 745 | .comment = comment, | 774 | .comment = comment, |
| 746 | }, | 775 | }, |
| ... | @@ -749,7 +778,7 @@ pub const Instruction = union(enum) { | ... | @@ -749,7 +778,7 @@ pub const Instruction = union(enum) { |
| 749 | | 778 | |
| 750 | fn breakpoint(imm: u16) Instruction { | 779 | fn breakpoint(imm: u16) Instruction { |
| 751 | return Instruction{ | 780 | return Instruction{ |
| 752 | .Breakpoint = .{ | 781 | .breakpoint = .{ |
| 753 | .imm12 = @truncate(u12, imm >> 4), | 782 | .imm12 = @truncate(u12, imm >> 4), |
| 754 | .imm4 = @truncate(u4, imm), | 783 | .imm4 = @truncate(u4, imm), |
| 755 | }, | 784 | }, |
| ... | @@ -873,6 +902,24 @@ pub const Instruction = union(enum) { | ... | @@ -873,6 +902,24 @@ pub const Instruction = union(enum) { |
| 873 | return dataProcessing(cond, .mvn, 1, rd, .r0, op2); | 902 | return dataProcessing(cond, .mvn, 1, rd, .r0, op2); |
| 874 | } | 903 | } |
| 875 | | 904 | |
| | 905 | // Integer Saturating Arithmetic |
| | 906 | |
| | 907 | pub fn qadd(cond: Condition, rd: Register, rm: Register, rn: Register) Instruction { |
| | 908 | return integerSaturationArithmetic(cond, rd, rm, rn, 0b00); |
| | 909 | } |
| | 910 | |
| | 911 | pub fn qsub(cond: Condition, rd: Register, rm: Register, rn: Register) Instruction { |
| | 912 | return integerSaturationArithmetic(cond, rd, rm, rn, 0b01); |
| | 913 | } |
| | 914 | |
| | 915 | pub fn qdadd(cond: Condition, rd: Register, rm: Register, rn: Register) Instruction { |
| | 916 | return integerSaturationArithmetic(cond, rd, rm, rn, 0b10); |
| | 917 | } |
| | 918 | |
| | 919 | pub fn qdsub(cond: Condition, rd: Register, rm: Register, rn: Register) Instruction { |
| | 920 | return integerSaturationArithmetic(cond, rd, rm, rn, 0b11); |
| | 921 | } |
| | 922 | |
| 876 | // movw and movt | 923 | // movw and movt |
| 877 | | 924 | |
| 878 | pub fn movw(cond: Condition, rd: Register, imm: u16) Instruction { | 925 | pub fn movw(cond: Condition, rd: Register, imm: u16) Instruction { |
| ... | @@ -887,7 +934,7 @@ pub const Instruction = union(enum) { | ... | @@ -887,7 +934,7 @@ pub const Instruction = union(enum) { |
| 887 | | 934 | |
| 888 | pub fn mrs(cond: Condition, rd: Register, psr: Psr) Instruction { | 935 | pub fn mrs(cond: Condition, rd: Register, psr: Psr) Instruction { |
| 889 | return Instruction{ | 936 | return Instruction{ |
| 890 | .DataProcessing = .{ | 937 | .data_processing = .{ |
| 891 | .cond = @enumToInt(cond), | 938 | .cond = @enumToInt(cond), |
| 892 | .i = 0, | 939 | .i = 0, |
| 893 | .opcode = if (psr == .spsr) 0b1010 else 0b1000, | 940 | .opcode = if (psr == .spsr) 0b1010 else 0b1000, |
| ... | @@ -901,7 +948,7 @@ pub const Instruction = union(enum) { | ... | @@ -901,7 +948,7 @@ pub const Instruction = union(enum) { |
| 901 | | 948 | |
| 902 | pub fn msr(cond: Condition, psr: Psr, op: Operand) Instruction { | 949 | pub fn msr(cond: Condition, psr: Psr, op: Operand) Instruction { |
| 903 | return Instruction{ | 950 | return Instruction{ |
| 904 | .DataProcessing = .{ | 951 | .data_processing = .{ |
| 905 | .cond = @enumToInt(cond), | 952 | .cond = @enumToInt(cond), |
| 906 | .i = 0, | 953 | .i = 0, |
| 907 | .opcode = if (psr == .spsr) 0b1011 else 0b1001, | 954 | .opcode = if (psr == .spsr) 0b1011 else 0b1001, |
| ... | @@ -1294,6 +1341,10 @@ test "serialize instructions" { | ... | @@ -1294,6 +1341,10 @@ test "serialize instructions" { |
| 1294 | .inst = Instruction.ldmea(.al, .r4, true, .{ .r2 = true, .r5 = true }), | 1341 | .inst = Instruction.ldmea(.al, .r4, true, .{ .r2 = true, .r5 = true }), |
| 1295 | .expected = 0b1110_100_1_0_0_1_1_0100_0000000000100100, | 1342 | .expected = 0b1110_100_1_0_0_1_1_0100_0000000000100100, |
| 1296 | }, | 1343 | }, |
| | 1344 | .{ // qadd r0, r7, r8 |
| | 1345 | .inst = Instruction.qadd(.al, .r0, .r7, .r8), |
| | 1346 | .expected = 0b1110_00010_00_0_1000_0000_0000_0101_0111, |
| | 1347 | }, |
| 1297 | }; | 1348 | }; |
| 1298 | | 1349 | |
| 1299 | for (testcases) |case| { | 1350 | for (testcases) |case| { |