| ... | @@ -469,6 +469,23 @@ pub const Instruction = union(enum) { | ... | @@ -469,6 +469,23 @@ pub const Instruction = union(enum) { |
| 469 | } | 469 | } |
| 470 | }; | 470 | }; |
| 471 | | 471 | |
| | 472 | pub const AddressingMode = enum { |
| | 473 | /// [<Rn>, <offset>] |
| | 474 | /// |
| | 475 | /// Address = Rn + offset |
| | 476 | offset, |
| | 477 | /// [<Rn>, <offset>]! |
| | 478 | /// |
| | 479 | /// Address = Rn + offset |
| | 480 | /// Rn = Rn + offset |
| | 481 | pre_index, |
| | 482 | /// [<Rn>], <offset> |
| | 483 | /// |
| | 484 | /// Address = Rn |
| | 485 | /// Rn = Rn + offset |
| | 486 | post_index, |
| | 487 | }; |
| | 488 | |
| 472 | /// Represents the offset operand of a load or store | 489 | /// Represents the offset operand of a load or store |
| 473 | /// instruction. Data can be loaded from memory with either an | 490 | /// instruction. Data can be loaded from memory with either an |
| 474 | /// immediate offset or an offset that is stored in some register. | 491 | /// immediate offset or an offset that is stored in some register. |
| ... | @@ -730,10 +747,9 @@ pub const Instruction = union(enum) { | ... | @@ -730,10 +747,9 @@ pub const Instruction = union(enum) { |
| 730 | rd: Register, | 747 | rd: Register, |
| 731 | rn: Register, | 748 | rn: Register, |
| 732 | offset: Offset, | 749 | offset: Offset, |
| 733 | pre_index: bool, | 750 | mode: AddressingMode, |
| 734 | positive: bool, | 751 | positive: bool, |
| 735 | byte_word: u1, | 752 | byte_word: u1, |
| 736 | write_back: bool, | | |
| 737 | load_store: u1, | 753 | load_store: u1, |
| 738 | ) Instruction { | 754 | ) Instruction { |
| 739 | return Instruction{ | 755 | return Instruction{ |
| ... | @@ -743,10 +759,16 @@ pub const Instruction = union(enum) { | ... | @@ -743,10 +759,16 @@ pub const Instruction = union(enum) { |
| 743 | .rd = rd.id(), | 759 | .rd = rd.id(), |
| 744 | .offset = offset.toU12(), | 760 | .offset = offset.toU12(), |
| 745 | .load_store = load_store, | 761 | .load_store = load_store, |
| 746 | .write_back = @boolToInt(write_back), | 762 | .write_back = switch (mode) { |
| | 763 | .offset => 0b0, |
| | 764 | .pre_index, .post_index => 0b1, |
| | 765 | }, |
| 747 | .byte_word = byte_word, | 766 | .byte_word = byte_word, |
| 748 | .up_down = @boolToInt(positive), | 767 | .up_down = @boolToInt(positive), |
| 749 | .pre_post = @boolToInt(pre_index), | 768 | .pre_post = switch (mode) { |
| | 769 | .offset, .pre_index => 0b1, |
| | 770 | .post_index => 0b0, |
| | 771 | }, |
| 750 | .imm = @boolToInt(offset != .immediate), | 772 | .imm = @boolToInt(offset != .immediate), |
| 751 | }, | 773 | }, |
| 752 | }; | 774 | }; |
| ... | @@ -754,9 +776,8 @@ pub const Instruction = union(enum) { | ... | @@ -754,9 +776,8 @@ pub const Instruction = union(enum) { |
| 754 | | 776 | |
| 755 | fn extraLoadStore( | 777 | fn extraLoadStore( |
| 756 | cond: Condition, | 778 | cond: Condition, |
| 757 | pre_index: bool, | 779 | mode: AddressingMode, |
| 758 | positive: bool, | 780 | positive: bool, |
| 759 | write_back: bool, | | |
| 760 | o1: u1, | 781 | o1: u1, |
| 761 | op2: u2, | 782 | op2: u2, |
| 762 | rn: Register, | 783 | rn: Register, |
| ... | @@ -780,10 +801,16 @@ pub const Instruction = union(enum) { | ... | @@ -780,10 +801,16 @@ pub const Instruction = union(enum) { |
| 780 | .rt = rt.id(), | 801 | .rt = rt.id(), |
| 781 | .rn = rn.id(), | 802 | .rn = rn.id(), |
| 782 | .o1 = o1, | 803 | .o1 = o1, |
| 783 | .write_back = @boolToInt(write_back), | 804 | .write_back = switch (mode) { |
| | 805 | .offset => 0b0, |
| | 806 | .pre_index, .post_index => 0b1, |
| | 807 | }, |
| 784 | .imm = @boolToInt(offset == .immediate), | 808 | .imm = @boolToInt(offset == .immediate), |
| 785 | .up_down = @boolToInt(positive), | 809 | .up_down = @boolToInt(positive), |
| 786 | .pre_index = @boolToInt(pre_index), | 810 | .pre_index = switch (mode) { |
| | 811 | .offset, .pre_index => 0b1, |
| | 812 | .post_index => 0b0, |
| | 813 | }, |
| 787 | .cond = @enumToInt(cond), | 814 | .cond = @enumToInt(cond), |
| 788 | }, | 815 | }, |
| 789 | }; | 816 | }; |
| ... | @@ -1091,51 +1118,49 @@ pub const Instruction = union(enum) { | ... | @@ -1091,51 +1118,49 @@ pub const Instruction = union(enum) { |
| 1091 | // Single data transfer | 1118 | // Single data transfer |
| 1092 | | 1119 | |
| 1093 | pub const OffsetArgs = struct { | 1120 | pub const OffsetArgs = struct { |
| 1094 | pre_index: bool = true, | 1121 | mode: AddressingMode = .offset, |
| 1095 | positive: bool = true, | 1122 | positive: bool = true, |
| 1096 | offset: Offset, | 1123 | offset: Offset, |
| 1097 | write_back: bool = false, | | |
| 1098 | }; | 1124 | }; |
| 1099 | | 1125 | |
| 1100 | pub fn ldr(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction { | 1126 | pub fn ldr(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction { |
| 1101 | return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 0, args.write_back, 1); | 1127 | return singleDataTransfer(cond, rd, rn, args.offset, args.mode, args.positive, 0, 1); |
| 1102 | } | 1128 | } |
| 1103 | | 1129 | |
| 1104 | pub fn ldrb(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction { | 1130 | pub fn ldrb(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction { |
| 1105 | return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 1, args.write_back, 1); | 1131 | return singleDataTransfer(cond, rd, rn, args.offset, args.mode, args.positive, 1, 1); |
| 1106 | } | 1132 | } |
| 1107 | | 1133 | |
| 1108 | pub fn str(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction { | 1134 | pub fn str(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction { |
| 1109 | return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 0, args.write_back, 0); | 1135 | return singleDataTransfer(cond, rd, rn, args.offset, args.mode, args.positive, 0, 0); |
| 1110 | } | 1136 | } |
| 1111 | | 1137 | |
| 1112 | pub fn strb(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction { | 1138 | pub fn strb(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction { |
| 1113 | return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 1, args.write_back, 0); | 1139 | return singleDataTransfer(cond, rd, rn, args.offset, args.mode, args.positive, 1, 0); |
| 1114 | } | 1140 | } |
| 1115 | | 1141 | |
| 1116 | // Extra load/store | 1142 | // Extra load/store |
| 1117 | | 1143 | |
| 1118 | pub const ExtraLoadStoreOffsetArgs = struct { | 1144 | pub const ExtraLoadStoreOffsetArgs = struct { |
| 1119 | pre_index: bool = true, | 1145 | mode: AddressingMode = .offset, |
| 1120 | positive: bool = true, | 1146 | positive: bool = true, |
| 1121 | offset: ExtraLoadStoreOffset, | 1147 | offset: ExtraLoadStoreOffset, |
| 1122 | write_back: bool = false, | | |
| 1123 | }; | 1148 | }; |
| 1124 | | 1149 | |
| 1125 | pub fn strh(cond: Condition, rt: Register, rn: Register, args: ExtraLoadStoreOffsetArgs) Instruction { | 1150 | pub fn strh(cond: Condition, rt: Register, rn: Register, args: ExtraLoadStoreOffsetArgs) Instruction { |
| 1126 | return extraLoadStore(cond, args.pre_index, args.positive, args.write_back, 0b0, 0b01, rn, rt, args.offset); | 1151 | return extraLoadStore(cond, args.mode, args.positive, 0b0, 0b01, rn, rt, args.offset); |
| 1127 | } | 1152 | } |
| 1128 | | 1153 | |
| 1129 | pub fn ldrh(cond: Condition, rt: Register, rn: Register, args: ExtraLoadStoreOffsetArgs) Instruction { | 1154 | pub fn ldrh(cond: Condition, rt: Register, rn: Register, args: ExtraLoadStoreOffsetArgs) Instruction { |
| 1130 | return extraLoadStore(cond, args.pre_index, args.positive, args.write_back, 0b1, 0b01, rn, rt, args.offset); | 1155 | return extraLoadStore(cond, args.mode, args.positive, 0b1, 0b01, rn, rt, args.offset); |
| 1131 | } | 1156 | } |
| 1132 | | 1157 | |
| 1133 | pub fn ldrsh(cond: Condition, rt: Register, rn: Register, args: ExtraLoadStoreOffsetArgs) Instruction { | 1158 | pub fn ldrsh(cond: Condition, rt: Register, rn: Register, args: ExtraLoadStoreOffsetArgs) Instruction { |
| 1134 | return extraLoadStore(cond, args.pre_index, args.positive, args.write_back, 0b1, 0b11, rn, rt, args.offset); | 1159 | return extraLoadStore(cond, args.mode, args.positive, 0b1, 0b11, rn, rt, args.offset); |
| 1135 | } | 1160 | } |
| 1136 | | 1161 | |
| 1137 | pub fn ldrsb(cond: Condition, rt: Register, rn: Register, args: ExtraLoadStoreOffsetArgs) Instruction { | 1162 | pub fn ldrsb(cond: Condition, rt: Register, rn: Register, args: ExtraLoadStoreOffsetArgs) Instruction { |
| 1138 | return extraLoadStore(cond, args.pre_index, args.positive, args.write_back, 0b1, 0b10, rn, rt, args.offset); | 1163 | return extraLoadStore(cond, args.mode, args.positive, 0b1, 0b10, rn, rt, args.offset); |
| 1139 | } | 1164 | } |
| 1140 | | 1165 | |
| 1141 | // Block data transfer | 1166 | // Block data transfer |
| ... | @@ -1234,10 +1259,9 @@ pub const Instruction = union(enum) { | ... | @@ -1234,10 +1259,9 @@ pub const Instruction = union(enum) { |
| 1234 | } else if (args.len == 1) { | 1259 | } else if (args.len == 1) { |
| 1235 | const reg = args[0]; | 1260 | const reg = args[0]; |
| 1236 | return ldr(cond, reg, .sp, .{ | 1261 | return ldr(cond, reg, .sp, .{ |
| 1237 | .pre_index = false, | 1262 | .mode = .post_index, |
| 1238 | .positive = true, | 1263 | .positive = true, |
| 1239 | .offset = Offset.imm(4), | 1264 | .offset = Offset.imm(4), |
| 1240 | .write_back = false, | | |
| 1241 | }); | 1265 | }); |
| 1242 | } else { | 1266 | } else { |
| 1243 | var register_list: u16 = 0; | 1267 | var register_list: u16 = 0; |
| ... | @@ -1259,10 +1283,9 @@ pub const Instruction = union(enum) { | ... | @@ -1259,10 +1283,9 @@ pub const Instruction = union(enum) { |
| 1259 | } else if (args.len == 1) { | 1283 | } else if (args.len == 1) { |
| 1260 | const reg = args[0]; | 1284 | const reg = args[0]; |
| 1261 | return str(cond, reg, .sp, .{ | 1285 | return str(cond, reg, .sp, .{ |
| 1262 | .pre_index = true, | 1286 | .mode = .pre_index, |
| 1263 | .positive = false, | 1287 | .positive = false, |
| 1264 | .offset = Offset.imm(4), | 1288 | .offset = Offset.imm(4), |
| 1265 | .write_back = true, | | |
| 1266 | }); | 1289 | }); |
| 1267 | } else { | 1290 | } else { |
| 1268 | var register_list: u16 = 0; | 1291 | var register_list: u16 = 0; |
| ... | @@ -1447,10 +1470,9 @@ test "aliases" { | ... | @@ -1447,10 +1470,9 @@ test "aliases" { |
| 1447 | .{ // pop { r6 } | 1470 | .{ // pop { r6 } |
| 1448 | .actual = Instruction.pop(.al, .{.r6}), | 1471 | .actual = Instruction.pop(.al, .{.r6}), |
| 1449 | .expected = Instruction.ldr(.al, .r6, .sp, .{ | 1472 | .expected = Instruction.ldr(.al, .r6, .sp, .{ |
| 1450 | .pre_index = false, | 1473 | .mode = .post_index, |
| 1451 | .positive = true, | 1474 | .positive = true, |
| 1452 | .offset = Instruction.Offset.imm(4), | 1475 | .offset = Instruction.Offset.imm(4), |
| 1453 | .write_back = false, | | |
| 1454 | }), | 1476 | }), |
| 1455 | }, | 1477 | }, |
| 1456 | .{ // pop { r1, r5 } | 1478 | .{ // pop { r1, r5 } |
| ... | @@ -1460,10 +1482,9 @@ test "aliases" { | ... | @@ -1460,10 +1482,9 @@ test "aliases" { |
| 1460 | .{ // push { r3 } | 1482 | .{ // push { r3 } |
| 1461 | .actual = Instruction.push(.al, .{.r3}), | 1483 | .actual = Instruction.push(.al, .{.r3}), |
| 1462 | .expected = Instruction.str(.al, .r3, .sp, .{ | 1484 | .expected = Instruction.str(.al, .r3, .sp, .{ |
| 1463 | .pre_index = true, | 1485 | .mode = .pre_index, |
| 1464 | .positive = false, | 1486 | .positive = false, |
| 1465 | .offset = Instruction.Offset.imm(4), | 1487 | .offset = Instruction.Offset.imm(4), |
| 1466 | .write_back = true, | | |
| 1467 | }), | 1488 | }), |
| 1468 | }, | 1489 | }, |
| 1469 | .{ // push { r0, r2 } | 1490 | .{ // push { r0, r2 } |