| ... | @@ -417,10 +417,10 @@ pub const Instruction = union(enum) { | ... | @@ -417,10 +417,10 @@ pub const Instruction = union(enum) { |
| 417 | rd: Register, | 417 | rd: Register, |
| 418 | rn: Register, | 418 | rn: Register, |
| 419 | offset: Offset, | 419 | offset: Offset, |
| 420 | pre_post: u1, | 420 | pre_index: bool, |
| 421 | up_down: u1, | 421 | positive: bool, |
| 422 | byte_word: u1, | 422 | byte_word: u1, |
| 423 | write_back: u1, | 423 | write_back: bool, |
| 424 | load_store: u1, | 424 | load_store: u1, |
| 425 | ) Instruction { | 425 | ) Instruction { |
| 426 | return Instruction{ | 426 | return Instruction{ |
| ... | @@ -430,10 +430,10 @@ pub const Instruction = union(enum) { | ... | @@ -430,10 +430,10 @@ pub const Instruction = union(enum) { |
| 430 | .rd = rd.id(), | 430 | .rd = rd.id(), |
| 431 | .offset = offset.toU12(), | 431 | .offset = offset.toU12(), |
| 432 | .load_store = load_store, | 432 | .load_store = load_store, |
| 433 | .write_back = write_back, | 433 | .write_back = if (write_back) 1 else 0, |
| 434 | .byte_word = byte_word, | 434 | .byte_word = byte_word, |
| 435 | .up_down = up_down, | 435 | .up_down = if (positive) 1 else 0, |
| 436 | .pre_post = pre_post, | 436 | .pre_post = if (pre_index) 1 else 0, |
| 437 | .imm = if (offset == .Immediate) 0 else 1, | 437 | .imm = if (offset == .Immediate) 0 else 1, |
| 438 | }, | 438 | }, |
| 439 | }; | 439 | }; |
| ... | @@ -626,20 +626,27 @@ pub const Instruction = union(enum) { | ... | @@ -626,20 +626,27 @@ pub const Instruction = union(enum) { |
| 626 | | 626 | |
| 627 | // Single data transfer | 627 | // Single data transfer |
| 628 | | 628 | |
| 629 | pub fn ldr(cond: Condition, rd: Register, rn: Register, offset: Offset) Instruction { | 629 | pub const OffsetArgs = struct { |
| 630 | return singleDataTransfer(cond, rd, rn, offset, 1, 1, 0, 0, 1); | 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 { | 640 | pub fn ldrb(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction { |
| 634 | return singleDataTransfer(cond, rd, rn, offset, 1, 1, 1, 0, 1); | 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 { | 644 | pub fn str(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction { |
| 638 | return singleDataTransfer(cond, rd, rn, offset, 1, 1, 0, 0, 0); | 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 { | 648 | pub fn strb(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction { |
| 642 | return singleDataTransfer(cond, rd, rn, offset, 1, 1, 1, 0, 0); | 649 | return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 1, args.write_back, 0); |
| 643 | } | 650 | } |
| 644 | | 651 | |
| 645 | // Block data transfer | 652 | // Block data transfer |
| ... | @@ -721,6 +728,58 @@ pub const Instruction = union(enum) { | ... | @@ -721,6 +728,58 @@ pub const Instruction = union(enum) { |
| 721 | pub fn bkpt(imm: u16) Instruction { | 728 | pub fn bkpt(imm: u16) Instruction { |
| 722 | return breakpoint(imm); | 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 | test "serialize instructions" { | 785 | test "serialize instructions" { |
| ... | @@ -747,11 +806,15 @@ test "serialize instructions" { | ... | @@ -747,11 +806,15 @@ test "serialize instructions" { |
| 747 | .expected = 0b1110_00010_0_001111_0101_000000000000, | 806 | .expected = 0b1110_00010_0_001111_0101_000000000000, |
| 748 | }, | 807 | }, |
| 749 | .{ // ldr r0, [r2, #42] | 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 | .expected = 0b1110_01_0_1_1_0_0_1_0010_0000_000000101010, | 812 | .expected = 0b1110_01_0_1_1_0_0_1_0010_0000_000000101010, |
| 752 | }, | 813 | }, |
| 753 | .{ // str r0, [r3] | 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 | .expected = 0b1110_01_0_1_1_0_0_0_0011_0000_000000000000, | 818 | .expected = 0b1110_01_0_1_1_0_0_0_0011_0000_000000000000, |
| 756 | }, | 819 | }, |
| 757 | .{ // b #12 | 820 | .{ // b #12 |
| ... | @@ -789,3 +852,43 @@ test "serialize instructions" { | ... | @@ -789,3 +852,43 @@ test "serialize instructions" { |
| 789 | testing.expectEqual(case.expected, actual); | 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 | } |