| ... | ... | @@ -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 | 489 | /// Represents the offset operand of a load or store |
| 473 | 490 | /// instruction. Data can be loaded from memory with either an |
| 474 | 491 | /// immediate offset or an offset that is stored in some register. |
| ... | ... | @@ -730,10 +747,9 @@ pub const Instruction = union(enum) { |
| 730 | 747 | rd: Register, |
| 731 | 748 | rn: Register, |
| 732 | 749 | offset: Offset, |
| 733 | | pre_index: bool, |
| 750 | mode: AddressingMode, |
| 734 | 751 | positive: bool, |
| 735 | 752 | byte_word: u1, |
| 736 | | write_back: bool, |
| 737 | 753 | load_store: u1, |
| 738 | 754 | ) Instruction { |
| 739 | 755 | return Instruction{ |
| ... | ... | @@ -743,10 +759,16 @@ pub const Instruction = union(enum) { |
| 743 | 759 | .rd = rd.id(), |
| 744 | 760 | .offset = offset.toU12(), |
| 745 | 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 | 766 | .byte_word = byte_word, |
| 748 | 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 | 772 | .imm = @boolToInt(offset != .immediate), |
| 751 | 773 | }, |
| 752 | 774 | }; |
| ... | ... | @@ -754,9 +776,8 @@ pub const Instruction = union(enum) { |
| 754 | 776 | |
| 755 | 777 | fn extraLoadStore( |
| 756 | 778 | cond: Condition, |
| 757 | | pre_index: bool, |
| 779 | mode: AddressingMode, |
| 758 | 780 | positive: bool, |
| 759 | | write_back: bool, |
| 760 | 781 | o1: u1, |
| 761 | 782 | op2: u2, |
| 762 | 783 | rn: Register, |
| ... | ... | @@ -780,10 +801,16 @@ pub const Instruction = union(enum) { |
| 780 | 801 | .rt = rt.id(), |
| 781 | 802 | .rn = rn.id(), |
| 782 | 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 | 808 | .imm = @boolToInt(offset == .immediate), |
| 785 | 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 | 814 | .cond = @enumToInt(cond), |
| 788 | 815 | }, |
| 789 | 816 | }; |
| ... | ... | @@ -1091,51 +1118,49 @@ pub const Instruction = union(enum) { |
| 1091 | 1118 | // Single data transfer |
| 1092 | 1119 | |
| 1093 | 1120 | pub const OffsetArgs = struct { |
| 1094 | | pre_index: bool = true, |
| 1121 | mode: AddressingMode = .offset, |
| 1095 | 1122 | positive: bool = true, |
| 1096 | 1123 | offset: Offset, |
| 1097 | | write_back: bool = false, |
| 1098 | 1124 | }; |
| 1099 | 1125 | |
| 1100 | 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 | 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 | 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 | 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 | 1142 | // Extra load/store |
| 1117 | 1143 | |
| 1118 | 1144 | pub const ExtraLoadStoreOffsetArgs = struct { |
| 1119 | | pre_index: bool = true, |
| 1145 | mode: AddressingMode = .offset, |
| 1120 | 1146 | positive: bool = true, |
| 1121 | 1147 | offset: ExtraLoadStoreOffset, |
| 1122 | | write_back: bool = false, |
| 1123 | 1148 | }; |
| 1124 | 1149 | |
| 1125 | 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 | 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 | 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 | 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 | 1166 | // Block data transfer |
| ... | ... | @@ -1234,10 +1259,9 @@ pub const Instruction = union(enum) { |
| 1234 | 1259 | } else if (args.len == 1) { |
| 1235 | 1260 | const reg = args[0]; |
| 1236 | 1261 | return ldr(cond, reg, .sp, .{ |
| 1237 | | .pre_index = false, |
| 1262 | .mode = .post_index, |
| 1238 | 1263 | .positive = true, |
| 1239 | 1264 | .offset = Offset.imm(4), |
| 1240 | | .write_back = false, |
| 1241 | 1265 | }); |
| 1242 | 1266 | } else { |
| 1243 | 1267 | var register_list: u16 = 0; |
| ... | ... | @@ -1259,10 +1283,9 @@ pub const Instruction = union(enum) { |
| 1259 | 1283 | } else if (args.len == 1) { |
| 1260 | 1284 | const reg = args[0]; |
| 1261 | 1285 | return str(cond, reg, .sp, .{ |
| 1262 | | .pre_index = true, |
| 1286 | .mode = .pre_index, |
| 1263 | 1287 | .positive = false, |
| 1264 | 1288 | .offset = Offset.imm(4), |
| 1265 | | .write_back = true, |
| 1266 | 1289 | }); |
| 1267 | 1290 | } else { |
| 1268 | 1291 | var register_list: u16 = 0; |
| ... | ... | @@ -1447,10 +1470,9 @@ test "aliases" { |
| 1447 | 1470 | .{ // pop { r6 } |
| 1448 | 1471 | .actual = Instruction.pop(.al, .{.r6}), |
| 1449 | 1472 | .expected = Instruction.ldr(.al, .r6, .sp, .{ |
| 1450 | | .pre_index = false, |
| 1473 | .mode = .post_index, |
| 1451 | 1474 | .positive = true, |
| 1452 | 1475 | .offset = Instruction.Offset.imm(4), |
| 1453 | | .write_back = false, |
| 1454 | 1476 | }), |
| 1455 | 1477 | }, |
| 1456 | 1478 | .{ // pop { r1, r5 } |
| ... | ... | @@ -1460,10 +1482,9 @@ test "aliases" { |
| 1460 | 1482 | .{ // push { r3 } |
| 1461 | 1483 | .actual = Instruction.push(.al, .{.r3}), |
| 1462 | 1484 | .expected = Instruction.str(.al, .r3, .sp, .{ |
| 1463 | | .pre_index = true, |
| 1485 | .mode = .pre_index, |
| 1464 | 1486 | .positive = false, |
| 1465 | 1487 | .offset = Instruction.Offset.imm(4), |
| 1466 | | .write_back = true, |
| 1467 | 1488 | }), |
| 1468 | 1489 | }, |
| 1469 | 1490 | .{ // push { r0, r2 } |