| ... | ... | @@ -417,10 +417,10 @@ pub const Instruction = union(enum) { |
| 417 | 417 | rd: Register, |
| 418 | 418 | rn: Register, |
| 419 | 419 | offset: Offset, |
| 420 | | pre_post: u1, |
| 421 | | up_down: u1, |
| 420 | pre_index: bool, |
| 421 | positive: bool, |
| 422 | 422 | byte_word: u1, |
| 423 | | write_back: u1, |
| 423 | write_back: bool, |
| 424 | 424 | load_store: u1, |
| 425 | 425 | ) Instruction { |
| 426 | 426 | return Instruction{ |
| ... | ... | @@ -430,10 +430,10 @@ pub const Instruction = union(enum) { |
| 430 | 430 | .rd = rd.id(), |
| 431 | 431 | .offset = offset.toU12(), |
| 432 | 432 | .load_store = load_store, |
| 433 | | .write_back = write_back, |
| 433 | .write_back = if (write_back) 1 else 0, |
| 434 | 434 | .byte_word = byte_word, |
| 435 | | .up_down = up_down, |
| 436 | | .pre_post = pre_post, |
| 435 | .up_down = if (positive) 1 else 0, |
| 436 | .pre_post = if (pre_index) 1 else 0, |
| 437 | 437 | .imm = if (offset == .Immediate) 0 else 1, |
| 438 | 438 | }, |
| 439 | 439 | }; |
| ... | ... | @@ -626,20 +626,27 @@ pub const Instruction = union(enum) { |
| 626 | 626 | |
| 627 | 627 | // Single data transfer |
| 628 | 628 | |
| 629 | | pub fn ldr(cond: Condition, rd: Register, rn: Register, offset: Offset) Instruction { |
| 630 | | return singleDataTransfer(cond, rd, rn, offset, 1, 1, 0, 0, 1); |
| 629 | pub const OffsetArgs = struct { |
| 630 | pre_index: bool = true, |
| 631 | positive: bool = true, |
| 632 | offset: Offset, |
| 633 | write_back: bool = false, |
| 634 | }; |
| 635 | |
| 636 | pub fn ldr(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction { |
| 637 | return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 0, args.write_back, 1); |
| 631 | 638 | } |
| 632 | 639 | |
| 633 | | pub fn ldrb(cond: Condition, rd: Register, rn: Register, offset: Offset) Instruction { |
| 634 | | return singleDataTransfer(cond, rd, rn, offset, 1, 1, 1, 0, 1); |
| 640 | pub fn ldrb(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction { |
| 641 | return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 1, args.write_back, 1); |
| 635 | 642 | } |
| 636 | 643 | |
| 637 | | pub fn str(cond: Condition, rd: Register, rn: Register, offset: Offset) Instruction { |
| 638 | | return singleDataTransfer(cond, rd, rn, offset, 1, 1, 0, 0, 0); |
| 644 | pub fn str(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction { |
| 645 | return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 0, args.write_back, 0); |
| 639 | 646 | } |
| 640 | 647 | |
| 641 | | pub fn strb(cond: Condition, rd: Register, rn: Register, offset: Offset) Instruction { |
| 642 | | return singleDataTransfer(cond, rd, rn, offset, 1, 1, 1, 0, 0); |
| 648 | pub fn strb(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction { |
| 649 | return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 1, args.write_back, 0); |
| 643 | 650 | } |
| 644 | 651 | |
| 645 | 652 | // Block data transfer |
| ... | ... | @@ -721,6 +728,58 @@ pub const Instruction = union(enum) { |
| 721 | 728 | pub fn bkpt(imm: u16) Instruction { |
| 722 | 729 | return breakpoint(imm); |
| 723 | 730 | } |
| 731 | |
| 732 | // Aliases |
| 733 | |
| 734 | pub fn pop(cond: Condition, args: anytype) Instruction { |
| 735 | if (@typeInfo(@TypeOf(args)) != .Struct) { |
| 736 | @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args))); |
| 737 | } |
| 738 | |
| 739 | if (args.len < 1) { |
| 740 | @compileError("Expected at least one register"); |
| 741 | } else if (args.len == 1) { |
| 742 | const reg = args[0]; |
| 743 | return ldr(cond, reg, .sp, .{ |
| 744 | .pre_index = false, |
| 745 | .positive = true, |
| 746 | .offset = Offset.imm(4), |
| 747 | .write_back = false, |
| 748 | }); |
| 749 | } else { |
| 750 | var register_list: u16 = 0; |
| 751 | inline for (args) |arg| { |
| 752 | const reg = @as(Register, arg); |
| 753 | register_list |= @as(u16, 1) << reg.id(); |
| 754 | } |
| 755 | return ldm(cond, .sp, true, @bitCast(RegisterList, register_list)); |
| 756 | } |
| 757 | } |
| 758 | |
| 759 | pub fn push(cond: Condition, args: anytype) Instruction { |
| 760 | if (@typeInfo(@TypeOf(args)) != .Struct) { |
| 761 | @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args))); |
| 762 | } |
| 763 | |
| 764 | if (args.len < 1) { |
| 765 | @compileError("Expected at least one register"); |
| 766 | } else if (args.len == 1) { |
| 767 | const reg = args[0]; |
| 768 | return str(cond, reg, .sp, .{ |
| 769 | .pre_index = true, |
| 770 | .positive = false, |
| 771 | .offset = Offset.imm(4), |
| 772 | .write_back = true, |
| 773 | }); |
| 774 | } else { |
| 775 | var register_list: u16 = 0; |
| 776 | inline for (args) |arg| { |
| 777 | const reg = @as(Register, arg); |
| 778 | register_list |= @as(u16, 1) << reg.id(); |
| 779 | } |
| 780 | return stmdb(cond, .sp, true, @bitCast(RegisterList, register_list)); |
| 781 | } |
| 782 | } |
| 724 | 783 | }; |
| 725 | 784 | |
| 726 | 785 | test "serialize instructions" { |
| ... | ... | @@ -747,11 +806,15 @@ test "serialize instructions" { |
| 747 | 806 | .expected = 0b1110_00010_0_001111_0101_000000000000, |
| 748 | 807 | }, |
| 749 | 808 | .{ // ldr r0, [r2, #42] |
| 750 | | .inst = Instruction.ldr(.al, .r0, .r2, Instruction.Offset.imm(42)), |
| 809 | .inst = Instruction.ldr(.al, .r0, .r2, .{ |
| 810 | .offset = Instruction.Offset.imm(42), |
| 811 | }), |
| 751 | 812 | .expected = 0b1110_01_0_1_1_0_0_1_0010_0000_000000101010, |
| 752 | 813 | }, |
| 753 | 814 | .{ // str r0, [r3] |
| 754 | | .inst = Instruction.str(.al, .r0, .r3, Instruction.Offset.none), |
| 815 | .inst = Instruction.str(.al, .r0, .r3, .{ |
| 816 | .offset = Instruction.Offset.none, |
| 817 | }), |
| 755 | 818 | .expected = 0b1110_01_0_1_1_0_0_0_0011_0000_000000000000, |
| 756 | 819 | }, |
| 757 | 820 | .{ // b #12 |
| ... | ... | @@ -789,3 +852,43 @@ test "serialize instructions" { |
| 789 | 852 | testing.expectEqual(case.expected, actual); |
| 790 | 853 | } |
| 791 | 854 | } |
| 855 | |
| 856 | test "aliases" { |
| 857 | const Testcase = struct { |
| 858 | expected: Instruction, |
| 859 | actual: Instruction, |
| 860 | }; |
| 861 | |
| 862 | const testcases = [_]Testcase{ |
| 863 | .{ // pop { r6 } |
| 864 | .actual = Instruction.pop(.al, .{.r6}), |
| 865 | .expected = Instruction.ldr(.al, .r6, .sp, .{ |
| 866 | .pre_index = false, |
| 867 | .positive = true, |
| 868 | .offset = Instruction.Offset.imm(4), |
| 869 | .write_back = false, |
| 870 | }), |
| 871 | }, |
| 872 | .{ // pop { r1, r5 } |
| 873 | .actual = Instruction.pop(.al, .{ .r1, .r5 }), |
| 874 | .expected = Instruction.ldm(.al, .sp, true, .{ .r1 = true, .r5 = true }), |
| 875 | }, |
| 876 | .{ // push { r3 } |
| 877 | .actual = Instruction.push(.al, .{.r3}), |
| 878 | .expected = Instruction.str(.al, .r3, .sp, .{ |
| 879 | .pre_index = true, |
| 880 | .positive = false, |
| 881 | .offset = Instruction.Offset.imm(4), |
| 882 | .write_back = true, |
| 883 | }), |
| 884 | }, |
| 885 | .{ // push { r0, r2 } |
| 886 | .actual = Instruction.push(.al, .{ .r0, .r2 }), |
| 887 | .expected = Instruction.stmdb(.al, .sp, true, .{ .r0 = true, .r2 = true }), |
| 888 | }, |
| 889 | }; |
| 890 | |
| 891 | for (testcases) |case| { |
| 892 | testing.expectEqual(case.expected.toU32(), case.actual.toU32()); |
| 893 | } |
| 894 | } |