1 module gbaid.gba.arm;
2 
3 import core.bitop : popcnt;
4 
5 import std..string : format;
6 
7 import gbaid.util;
8 
9 import gbaid.gba.instable;
10 
11 private enum ARM_OPCODE_BIT_COUNT = 12;
12 private immutable Executor[1 << ARM_OPCODE_BIT_COUNT] ARM_EXECUTORS = createARMTable();
13 
14 public void executeARMInstruction(Registers* registers, MemoryBus* memory, int instruction) {
15     if (!registers.checkCondition(instruction >>> 28)) {
16         return;
17     }
18     int code = instruction.getBits(20, 27) << 4 | instruction.getBits(4, 7);
19     ARM_EXECUTORS[code](registers, memory, instruction);
20 }
21 
22 private Executor[] createARMTable() {
23     /*
24         The instruction encoding, modified from: http://problemkaputt.de/gbatek.htm#arminstructionsummary
25 
26         |..3 ..................2 ..................1 ..................0|
27         |1_0_9_8_7_6_5_4_3_2_1_0_9_8_7_6_5_4_3_2_1_0_9_8_7_6_5_4_3_2_1_0|
28         |_Cond__|0_0_0|___Op__|S|__Rn___|__Rd___|__Shift__|Typ|0|__Rm___| DataProc
29         |_Cond__|0_0_0|___Op__|S|__Rn___|__Rd___|__Rs___|0|Typ|1|__Rm___| DataProc
30         |_Cond__|0_0_1|___Op__|S|__Rn___|__Rd___|_Shift_|___Immediate___| DataProc
31         |_Cond__|0_0_1_1_0|P|1|0|_Field_|__Rd___|_Shift_|___Immediate___| PSR Imm
32         |_Cond__|0_0_0_1_0|P|L|0|_Field_|__Rd___|0_0_0_0|0_0_0_0|__Rm___| PSR Reg
33         |_Cond__|0_0_0_1_0_0_1_0_1_1_1_1_1_1_1_1_1_1_1_1|0_0|L|1|__Rn___| BX,BLX
34         |_Cond__|0_0_0_0_0_0|A|S|__Rd___|__Rn___|__Rs___|1_0_0_1|__Rm___| Multiply
35         |_Cond__|0_0_0_0_1|U|A|S|_RdHi__|_RdLo__|__Rs___|1_0_0_1|__Rm___| MulLong
36         |_Cond__|0_0_0_1_0|B|0_0|__Rn___|__Rd___|0_0_0_0|1_0_0_1|__Rm___| TransSwp12
37         |_Cond__|0_0_0|P|U|0|W|L|__Rn___|__Rd___|0_0_0_0|1|S|H|1|__Rm___| TransReg10
38         |_Cond__|0_0_0|P|U|1|W|L|__Rn___|__Rd___|OffsetH|1|S|H|1|OffsetL| TransImm10
39         |_Cond__|0_1_0|P|U|B|W|L|__Rn___|__Rd___|_________Offset________| TransImm9
40         |_Cond__|0_1_1|P|U|B|W|L|__Rn___|__Rd___|__Shift__|Typ|0|__Rm___| TransReg9
41         |_Cond__|1_0_0|P|U|S|W|L|__Rn___|__________Register_List________| BlockTrans
42         |_Cond__|1_0_1|L|___________________Offset______________________| B,BL,BLX
43         |_Cond__|1_1_1_1|_____________Ignored_by_Processor______________| SWI
44 
45         The op code is the concatenation of bits 20 to 27 with bits 4 to 7
46         For some instructions some of these bits are not used, hence the need for don't cares
47         Anything not covered by the table must raise an UNDEFINED interrupt
48     */
49 
50     auto table = createTable!(unsupported)(12);
51 
52     // Bits are OpCode(4),S(1)
53     // where S is set flags
54     addSubTable!("000tttttddd0", dataProcessing_RegShiftImm, unsupported)(table);
55     addSubTable!("000ttttt0dd1", dataProcessing_RegShiftReg, unsupported)(table);
56     addSubTable!("001tttttdddd", dataProcessing_Imm, unsupported)(table);
57 
58     // Bits are P(1)
59     // where P is use SPSR
60     addSubTable!("00110t10dddd", psrTransfer_Imm, unsupported)(table);
61 
62     // Bits are P(1),~L(1)
63     // where P is use SPSR and L is load
64     addSubTable!("00010tt00000", psrTransfer_Reg, unsupported)(table);
65 
66     // No bits
67     addSubTable!("000100100001", branchAndExchange, unsupported)(table);
68 
69     // Bits are A(1),S(1)
70     // where A is accumulate and S is set flags
71     addSubTable!("000000tt1001", multiply_Int, unsupported)(table);
72 
73     // Bits are ~U(1),A(1),S(1)
74     // where U is unsigned, A is accumulate and S is set flags
75     addSubTable!("00001ttt1001", multiply_Long, unsupported)(table);
76 
77     // Bits are B(1)
78     // where B is byte quantity
79     addSubTable!("00010t001001", singleDataSwap, unsupported)(table);
80 
81     // Bits are P(1),U(1),W(1),L(1),S(1),H(1)
82     // where P is pre-increment, U is up-increment, W is write back and L is load, S is signed and H is halfword
83     addSubTable!("000tt0tt1tt1", halfwordAndSignedDataTransfer_Reg, unsupported)(table);
84     addSubTable!("000tt1tt1tt1", halfwordAndSignedDataTransfer_Imm, unsupported)(table);
85 
86     // Bits are P(1),U(1),B(1),W(1),L(1)
87     // where P is pre-increment, U is up-increment, B is byte quantity, W is write back and L is load
88     addSubTable!("010tttttdddd", singleDataTransfer_Imm, unsupported)(table);
89     addSubTable!("011tttttddd0", singleDataTransfer_Reg, unsupported)(table);
90 
91     // Bits are P(1),U(1),S(1),W(1),L(1)
92     // where P is pre-increment, U is up-increment, S is load PSR or force user, W is write back and L is load
93     addSubTable!("100tttttdddd", blockDataTransfer, unsupported)(table);
94 
95     // Bits are L(1)
96     // where L is link
97     addSubTable!("101tdddddddd", branchAndBranchWithLink, unsupported)(table);
98 
99     // No bits
100     addSubTable!("1111dddddddd", softwareInterrupt, unsupported)(table);
101 
102     return table;
103 }
104 
105 private mixin template decodeOpDataProcessing_RegShiftImm() {
106     mixin decodeOpDataProcessing_RegShiftReg!true;
107 }
108 
109 private mixin template decodeOpDataProcessing_RegShiftReg() {
110     mixin decodeOpDataProcessing_RegShiftReg!false;
111 }
112 
113 private mixin template decodeOpDataProcessing_Imm() {
114     // Decode
115     int rn = instruction.getBits(16, 19);
116     int rd = instruction.getBits(12, 15);
117     int op1 = registers.get(rn);
118     // Get op2
119     int shift = instruction.getBits(8, 11) * 2;
120     int op2 = rotateRight(instruction & 0xFF, shift);
121     int carry = shift == 0 ? registers.getFlag(CPSRFlag.C) : op2.getBit(31);
122 }
123 
124 private mixin template decodeOpDataProcessing_RegShiftReg(bool immediateShift) {
125     // Decode
126     int rn = instruction.getBits(16, 19);
127     int rd = instruction.getBits(12, 15);
128     int op1 = registers.get(rn);
129     // Get op2
130     static if (immediateShift) {
131         ubyte shift = cast(ubyte) instruction.getBits(7, 11);
132     } else {
133         ubyte shift = cast(ubyte) registers.get(instruction.getBits(8, 11));
134     }
135     int shiftType = instruction.getBits(5, 6);
136     int carry;
137     int op2 = registers.applyShift!(!immediateShift)(shiftType, shift, registers.get(instruction & 0b1111), carry);
138 }
139 
140 private template dataProcessing_RegShiftImm(int code) if (code.getBits(5, 31) == 0) {
141     private alias dataProcessing_RegShiftImm =
142         dataProcessing!(decodeOpDataProcessing_RegShiftImm, code.getBits(1, 4), code.checkBit(0));
143 }
144 
145 private template dataProcessing_RegShiftReg(int code) if (code.getBits(5, 31) == 0) {
146     private alias dataProcessing_RegShiftReg =
147         dataProcessing!(decodeOpDataProcessing_RegShiftReg, code.getBits(1, 4), code.checkBit(0));
148 }
149 
150 private template dataProcessing_Imm(int code) if (code.getBits(5, 31) == 0) {
151     private alias dataProcessing_Imm =
152         dataProcessing!(decodeOpDataProcessing_Imm, code.getBits(1, 4), code.checkBit(0));
153 }
154 
155 private void dataProcessing(alias decodeOperands, int opCode: 0, bool setFlags)
156         (Registers* registers, MemoryBus* memory, int instruction) {
157     debug (outputInstructions) registers.logInstruction(instruction, "AND");
158     mixin decodeOperands;
159     // Operation
160     int res = op1 & op2;
161     registers.set(rd, res);
162     // Flag updates
163     static if (setFlags) {
164         if (rd == Register.PC) {
165             registers.setCPSR(registers.getSPSR());
166         } else {
167             int negative = res < 0;
168             int zero = res == 0;
169             registers.setApsrFlags(negative, zero, carry);
170         }
171     }
172 }
173 
174 private void dataProcessing(alias decodeOperands, int opCode: 1, bool setFlags)
175         (Registers* registers, MemoryBus* memory, int instruction) {
176     debug (outputInstructions) registers.logInstruction(instruction, "EOR");
177     mixin decodeOperands;
178     // Operation
179     int res = op1 ^ op2;
180     registers.set(rd, res);
181     // Flag updates
182     static if (setFlags) {
183         if (rd == Register.PC) {
184             registers.setCPSR(registers.getSPSR());
185         } else {
186             int negative = res < 0;
187             int zero = res == 0;
188             registers.setApsrFlags(negative, zero, carry);
189         }
190     }
191 }
192 
193 
194 private void dataProcessing(alias decodeOperands, int opCode: 2, bool setFlags)
195         (Registers* registers, MemoryBus* memory, int instruction) {
196     debug (outputInstructions) registers.logInstruction(instruction, "SUB");
197     mixin decodeOperands;
198     // Operation
199     int res = op1 - op2;
200     registers.set(rd, res);
201     // Flag updates
202     static if (setFlags) {
203         if (rd == Register.PC) {
204             registers.setCPSR(registers.getSPSR());
205         } else {
206             int negative = res < 0;
207             int zero = res == 0;
208             carry = !borrowedSub(op1, op2, res);
209             int overflow = overflowedSub(op1, op2, res);
210             registers.setApsrFlags(negative, zero, carry, overflow);
211         }
212     }
213 }
214 
215 private void dataProcessing(alias decodeOperands, int opCode: 3, bool setFlags)
216         (Registers* registers, MemoryBus* memory, int instruction) {
217     debug (outputInstructions) registers.logInstruction(instruction, "RSB");
218     mixin decodeOperands;
219     // Operation
220     int res = op2 - op1;
221     registers.set(rd, res);
222     // Flag updates
223     static if (setFlags) {
224         if (rd == Register.PC) {
225             registers.setCPSR(registers.getSPSR());
226         } else {
227             int negative = res < 0;
228             int zero = res == 0;
229             carry = !borrowedSub(op2, op1, res);
230             int overflow = overflowedSub(op2, op1, res);
231             registers.setApsrFlags(negative, zero, carry, overflow);
232         }
233     }
234 }
235 
236 private void dataProcessing(alias decodeOperands, int opCode: 4, bool setFlags)
237         (Registers* registers, MemoryBus* memory, int instruction) {
238     debug (outputInstructions) registers.logInstruction(instruction, "ADD");
239     mixin decodeOperands;
240     // Operation
241     int res = op1 + op2;
242     registers.set(rd, res);
243     // Flag updates
244     static if (setFlags) {
245         if (rd == Register.PC) {
246             registers.setCPSR(registers.getSPSR());
247         } else {
248             int negative = res < 0;
249             int zero = res == 0;
250             carry = carriedAdd(op1, op2, res);
251             int overflow = overflowedAdd(op1, op2, res);
252             registers.setApsrFlags(negative, zero, carry, overflow);
253         }
254     }
255 }
256 
257 private void dataProcessing(alias decodeOperands, int opCode: 5, bool setFlags)
258         (Registers* registers, MemoryBus* memory, int instruction) {
259     debug (outputInstructions) registers.logInstruction(instruction, "ADC");
260     mixin decodeOperands;
261     // Operation
262     int op3 = registers.getFlag(CPSRFlag.C);
263     int tmp = op1 + op2;
264     int res = tmp + op3;
265     registers.set(rd, res);
266     // Flag updates
267     static if (setFlags) {
268         if (rd == Register.PC) {
269             registers.setCPSR(registers.getSPSR());
270         } else {
271             int negative = res < 0;
272             int zero = res == 0;
273             carry = carriedAdd(op1, op2, tmp) || carriedAdd(tmp, op3, res);
274             int overflow = overflowedAdd(op1, op2, tmp) || overflowedAdd(tmp, op3, res);
275             registers.setApsrFlags(negative, zero, carry, overflow);
276         }
277     }
278 }
279 
280 private void dataProcessing(alias decodeOperands, int opCode: 6, bool setFlags)
281         (Registers* registers, MemoryBus* memory, int instruction) {
282     debug (outputInstructions) registers.logInstruction(instruction, "SBC");
283     mixin decodeOperands;
284     // Operation
285     int op3 = !registers.getFlag(CPSRFlag.C);
286     int tmp = op1 - op2;
287     int res = tmp - op3;
288     registers.set(rd, res);
289     // Flag updates
290     static if (setFlags) {
291         if (rd == Register.PC) {
292             registers.setCPSR(registers.getSPSR());
293         } else {
294             int negative = res < 0;
295             int zero = res == 0;
296             carry = !borrowedSub(op1, op2, tmp) && !borrowedSub(tmp, op3, res);
297             int overflow = overflowedSub(op1, op2, tmp) || overflowedSub(tmp, op3, res);
298             registers.setApsrFlags(negative, zero, carry, overflow);
299         }
300     }
301 }
302 
303 private void dataProcessing(alias decodeOperands, int opCode: 7, bool setFlags)
304         (Registers* registers, MemoryBus* memory, int instruction) {
305     debug (outputInstructions) registers.logInstruction(instruction, "RSC");
306     mixin decodeOperands;
307     // Operation
308     int op3 = !registers.getFlag(CPSRFlag.C);
309     int tmp = op2 - op1;
310     int res = tmp - op3;
311     registers.set(rd, res);
312     // Flag updates
313     static if (setFlags) {
314         if (rd == Register.PC) {
315             registers.setCPSR(registers.getSPSR());
316         } else {
317             int negative = res < 0;
318             int zero = res == 0;
319             carry = !borrowedSub(op2, op1, tmp) && !borrowedSub(tmp, op3, res);
320             int overflow = overflowedSub(op2, op1, tmp) || overflowedSub(tmp, op3, res);
321             registers.setApsrFlags(negative, zero, carry, overflow);
322         }
323     }
324 }
325 
326 private void dataProcessing(alias decodeOperands, int opCode: 8, bool setFlags: true)
327         (Registers* registers, MemoryBus* memory, int instruction) {
328     debug (outputInstructions) registers.logInstruction(instruction, "TST");
329     mixin decodeOperands;
330     // Operation
331     int res = op1 & op2;
332     // Flag updates
333     int negative = res < 0;
334     int zero = res == 0;
335     registers.setApsrFlags(negative, zero, carry);
336 }
337 
338 private void dataProcessing(alias decodeOperands, int opCode: 9, bool setFlags: true)
339         (Registers* registers, MemoryBus* memory, int instruction) {
340     debug (outputInstructions) registers.logInstruction(instruction, "TEQ");
341     mixin decodeOperands;
342     // Operation
343     int res = op1 ^ op2;
344     // Flag updates
345     int negative = res < 0;
346     int zero = res == 0;
347     registers.setApsrFlags(negative, zero, carry);
348 }
349 
350 private void dataProcessing(alias decodeOperands, int opCode: 10, bool setFlags: true)
351         (Registers* registers, MemoryBus* memory, int instruction) {
352     debug (outputInstructions) registers.logInstruction(instruction, "CMP");
353     mixin decodeOperands;
354     // Operation
355     int res = op1 - op2;
356     // Flag updates
357     int negative = res < 0;
358     int zero = res == 0;
359     carry = !borrowedSub(op1, op2, res);
360     int overflow = overflowedSub(op1, op2, res);
361     registers.setApsrFlags(negative, zero, carry, overflow);
362 }
363 
364 private void dataProcessing(alias decodeOperands, int opCode: 11, bool setFlags: true)
365         (Registers* registers, MemoryBus* memory, int instruction) {
366     debug (outputInstructions) registers.logInstruction(instruction, "CMN");
367     mixin decodeOperands;
368     // Operation
369     int res = op1 + op2;
370     // Flag updates
371     int negative = res < 0;
372     int zero = res == 0;
373     carry = carriedAdd(op1, op2, res);
374     int overflow = overflowedAdd(op1, op2, res);
375     registers.setApsrFlags(negative, zero, carry, overflow);
376 }
377 
378 private void dataProcessing(alias decodeOperands, int opCode: 12, bool setFlags)
379         (Registers* registers, MemoryBus* memory, int instruction) {
380     debug (outputInstructions) registers.logInstruction(instruction, "ORR");
381     mixin decodeOperands;
382     // Operation
383     int res = op1 | op2;
384     registers.set(rd, res);
385     // Flag updates
386     static if (setFlags) {
387         if (rd == Register.PC) {
388             registers.setCPSR(registers.getSPSR());
389         } else {
390             int negative = res < 0;
391             int zero = res == 0;
392             registers.setApsrFlags(negative, zero, carry);
393         }
394     }
395 }
396 
397 private void dataProcessing(alias decodeOperands, int opCode: 13, bool setFlags)
398         (Registers* registers, MemoryBus* memory, int instruction) {
399     debug (outputInstructions) registers.logInstruction(instruction, "MOV");
400     mixin decodeOperands;
401     // Operation
402     int res = op2;
403     registers.set(rd, res);
404     // Flag updates
405     static if (setFlags) {
406         if (rd == Register.PC) {
407             registers.setCPSR(registers.getSPSR());
408         } else {
409             int negative = res < 0;
410             int zero = res == 0;
411             registers.setApsrFlags(negative, zero, carry);
412         }
413     }
414 }
415 
416 private void dataProcessing(alias decodeOperands, int opCode: 14, bool setFlags)
417         (Registers* registers, MemoryBus* memory, int instruction) {
418     debug (outputInstructions) registers.logInstruction(instruction, "BIC");
419     mixin decodeOperands;
420     // Operation
421     int res = op1 & ~op2;
422     registers.set(rd, res);
423     // Flag updates
424     static if (setFlags) {
425         if (rd == Register.PC) {
426             registers.setCPSR(registers.getSPSR());
427         } else {
428             int negative = res < 0;
429             int zero = res == 0;
430             registers.setApsrFlags(negative, zero, carry);
431         }
432     }
433 }
434 
435 private void dataProcessing(alias decodeOperands, int opCode: 15, bool setFlags)
436         (Registers* registers, MemoryBus* memory, int instruction) {
437     debug (outputInstructions) registers.logInstruction(instruction, "MVN");
438     mixin decodeOperands;
439     // Operation
440     int res = ~op2;
441     registers.set(rd, res);
442     // Flag updates
443     static if (setFlags) {
444         if (rd == Register.PC) {
445             registers.setCPSR(registers.getSPSR());
446         } else {
447             int negative = res < 0;
448             int zero = res == 0;
449             registers.setApsrFlags(negative, zero, carry);
450         }
451     }
452 }
453 
454 @("unsupported")
455 private template dataProcessing(alias decodeOperands, int opCode, bool setFlags)
456         if (opCode >= 8 && opCode <= 11 && !setFlags) {
457     private alias dataProcessing = unsupported;
458 }
459 
460 private mixin template decodeOpPsrTransfer_Imm() {
461     int op = rotateRight(instruction & 0xFF, instruction.getBits(8, 11) * 2);
462 }
463 
464 private mixin template decodeOpPsrTransfer_Reg() {
465     int op = registers.get(instruction & 0xF);
466 }
467 
468 private template psrTransfer_Imm(int code) if (code.getBits(1, 31) == 0) {
469     private alias psrTransfer_Imm = psrTransfer!(decodeOpPsrTransfer_Imm, code.checkBit(0), true);
470 }
471 
472 private template psrTransfer_Reg(int code) if (code.getBits(2, 31) == 0) {
473     private alias psrTransfer_Reg = psrTransfer!(decodeOpPsrTransfer_Reg, code.checkBit(1), code.checkBit(0));
474 }
475 
476 private void psrTransfer(alias decodeOperand, bool useSPSR: false, bool notLoad: false)(Registers* registers, MemoryBus* memory, int instruction)
477         if (__traits(isSame, decodeOperand, decodeOpPsrTransfer_Reg)) {
478     debug (outputInstructions) registers.logInstruction(instruction, "MRS");
479     int rd = instruction.getBits(12, 15);
480     registers.set(rd, registers.getCPSR());
481 }
482 
483 private void psrTransfer(alias decodeOperand, bool useSPSR: false, bool notLoad: true)(Registers* registers, MemoryBus* memory, int instruction) {
484     debug (outputInstructions) registers.logInstruction(instruction, "MSR");
485     mixin decodeOperand;
486     int mask = instruction.getPsrMask() & (0xF0000000 | (registers.mode != Mode.USER ? 0xFF : 0));
487     int cpsr = registers.getCPSR();
488     registers.setCPSR(cpsr & ~mask | op & mask);
489 }
490 
491 private void psrTransfer(alias decodeOperand, bool useSPSR: true, bool notLoad: false)(Registers* registers, MemoryBus* memory, int instruction)
492         if (__traits(isSame, decodeOperand, decodeOpPsrTransfer_Reg)) {
493     debug (outputInstructions) registers.logInstruction(instruction, "MRS");
494     int rd = instruction.getBits(12, 15);
495     registers.set(rd, registers.getSPSR());
496 }
497 
498 private void psrTransfer(alias decodeOperand, bool useSPSR: true, bool notLoad: true)(Registers* registers, MemoryBus* memory, int instruction) {
499     debug (outputInstructions) registers.logInstruction(instruction, "MSR");
500     mixin decodeOperand;
501     int mask = instruction.getPsrMask() & 0xF00000FF;
502     int spsr = registers.getSPSR();
503     registers.setSPSR(spsr & ~mask | op & mask);
504 }
505 
506 @("unsupported")
507 private template psrTransfer(alias decodeOperand, bool useSPSR, bool notLoad)
508         if (!notLoad && !__traits(isSame, decodeOperand, decodeOpPsrTransfer_Reg)) {
509     private alias psrTransfer = unsupported;
510 }
511 
512 private int getPsrMask(int instruction) {
513     int mask = 0;
514     if (instruction.checkBit(19)) {
515         // flags
516         mask |= 0xFF000000;
517     }
518     if (instruction.checkBit(18)) {
519         // status
520         mask |= 0xFF0000;
521     }
522     if (instruction.checkBit(17)) {
523         // extension
524         mask |= 0xFF00;
525     }
526     if (instruction.checkBit(16)) {
527         // control
528         mask |= 0xFF;
529     }
530     return mask;
531 }
532 
533 private void branchAndExchange()(Registers* registers, MemoryBus* memory, int instruction) {
534     debug (outputInstructions) registers.logInstruction(instruction, "BX");
535     int address = registers.get(instruction & 0xF);
536     if (address & 0b1) {
537         registers.setFlag(CPSRFlag.T, Set.THUMB);
538     }
539     registers.setPC(address & ~1);
540 }
541 
542 private mixin template decodeOpMultiply() {
543     int rd = instruction.getBits(16, 19);
544     int op2 = registers.get(instruction.getBits(8, 11));
545     int op1 = registers.get(instruction & 0xF);
546 }
547 
548 private template multiply_Int(int code) if (code.getBits(2, 31) == 0) {
549     private alias multiply_Int = multiply!(false, false, code.checkBit(1), code.checkBit(0));
550 }
551 
552 private template multiply_Long(int code) if (code.getBits(3, 31) == 0) {
553     private alias multiply_Long = multiply!(true, code.checkBit(2), code.checkBit(1), code.checkBit(0));
554 }
555 
556 private void multiply(bool long_: false, bool notUnsigned: false, bool accumulate: false, bool setFlags)
557         (Registers* registers, MemoryBus* memory, int instruction) {
558     debug (outputInstructions) registers.logInstruction(instruction, "MUL");
559     mixin decodeOpMultiply;
560     int res = op1 * op2;
561     registers.setMultiplyIntResult!setFlags(rd, res);
562 }
563 
564 private void multiply(bool long_: false, bool notUnsigned: false, bool accumulate: true, bool setFlags)
565         (Registers* registers, MemoryBus* memory, int instruction) {
566     debug (outputInstructions) registers.logInstruction(instruction, "MLA");
567     mixin decodeOpMultiply;
568     int op3 = registers.get(instruction.getBits(12, 15));
569     int res = op1 * op2 + op3;
570     registers.setMultiplyIntResult!setFlags(rd, res);
571 }
572 
573 private void multiply(bool long_: true, bool notUnsigned: false, bool accumulate: false, bool setFlags)
574         (Registers* registers, MemoryBus* memory, int instruction) {
575     debug (outputInstructions) registers.logInstruction(instruction, "UMULL");
576     mixin decodeOpMultiply;
577     int rn = instruction.getBits(12, 15);
578     ulong res = op1.ucast() * op2.ucast();
579     registers.setMultiplyLongResult!setFlags(rd, rn, res);
580 }
581 
582 private void multiply(bool long_: true, bool notUnsigned: false, bool accumulate: true, bool setFlags)
583         (Registers* registers, MemoryBus* memory, int instruction) {
584     debug (outputInstructions) registers.logInstruction(instruction, "UMLAL");
585     mixin decodeOpMultiply;
586     int rn = instruction.getBits(12, 15);
587     ulong op3 = ucast(registers.get(rd)) << 32 | ucast(registers.get(rn));
588     ulong res = op1.ucast() * op2.ucast() + op3;
589     registers.setMultiplyLongResult!setFlags(rd, rn, res);
590 }
591 
592 private void multiply(bool long_: true, bool notUnsigned: true, bool accumulate: false, bool setFlags)
593         (Registers* registers, MemoryBus* memory, int instruction) {
594     debug (outputInstructions) registers.logInstruction(instruction, "SMULL");
595     mixin decodeOpMultiply;
596     int rn = instruction.getBits(12, 15);
597     long res = cast(long) op1 * cast(long) op2;
598     registers.setMultiplyLongResult!setFlags(rd, rn, res);
599 }
600 
601 private void multiply(bool long_: true, bool notUnsigned: true, bool accumulate: true, bool setFlags)
602         (Registers* registers, MemoryBus* memory, int instruction) {
603     debug (outputInstructions) registers.logInstruction(instruction, "SMLAL");
604     mixin decodeOpMultiply;
605     int rn = instruction.getBits(12, 15);
606     long op3 = ucast(registers.get(rd)) << 32 | ucast(registers.get(rn));
607     long res = cast(long) op1 * cast(long) op2 + op3;
608     registers.setMultiplyLongResult!setFlags(rd, rn, res);
609 }
610 
611 @("unsupported")
612 private template multiply(bool long_, bool notUnsigned, bool accumulate, bool setFlags)
613         if (!long_ && notUnsigned) {
614     private alias multiply = unsupported;
615 }
616 
617 private void setMultiplyIntResult(bool setFlags)(Registers* registers, int rd, int res) {
618     registers.set(rd, res);
619     static if (setFlags) {
620         registers.setApsrFlags(res < 0, res == 0);
621     }
622 }
623 
624 private void setMultiplyLongResult(bool setFlags)(Registers* registers, int rd, int rn, long res) {
625     int resLo = cast(int) res;
626     int resHi = cast(int) (res >> 32);
627     registers.set(rn, resLo);
628     registers.set(rd, resHi);
629     static if (setFlags) {
630         registers.setApsrFlags(res < 0, res == 0);
631     }
632 }
633 
634 private template singleDataSwap(int code) if (code.getBits(1, 31) == 0) {
635     private alias singleDataSwap = singleDataSwap!(code.checkBit(0));
636 }
637 
638 private void singleDataSwap(bool byteQty)(Registers* registers, MemoryBus* memory, int instruction) {
639     debug (outputInstructions) registers.logInstruction(instruction, "SWP");
640     // Decode operands
641     int rn = instruction.getBits(16, 19);
642     int rd = instruction.getBits(12, 15);
643     int rm = instruction & 0xF;
644     int address = registers.get(rn);
645     // Do memory swap
646     static if (byteQty) {
647         int b = memory.get!byte(address) & 0xFF;
648         memory.set!byte(address, cast(byte) registers.get(rm));
649         registers.set(rd, b);
650     } else {
651         int w = address.rotateRead(memory.get!int(address));
652         memory.set!int(address, registers.get(rm));
653         registers.set(rd, w);
654     }
655 }
656 
657 private template halfwordAndSignedDataTransfer_Reg(int code) if (code.getBits(6, 31) == 0) {
658     private alias halfwordAndSignedDataTransfer_Reg = halfwordAndSignedDataTransfer!(
659         code.checkBit(5), code.checkBit(4), false, code.checkBit(3),
660         code.checkBit(2), code.getBit(1), code.getBit(0)
661     );
662 }
663 
664 private template halfwordAndSignedDataTransfer_Imm(int code) if (code.getBits(6, 31) == 0) {
665     private alias halfwordAndSignedDataTransfer_Imm = halfwordAndSignedDataTransfer!(
666         code.checkBit(5), code.checkBit(4), true, code.checkBit(3),
667         code.checkBit(2), code.getBit(1), code.getBit(0)
668     );
669 }
670 
671 private void halfwordAndSignedDataTransfer(bool preIncr, bool upIncr, bool immediate,
672         bool writeBack, bool load, bool signed, bool half)(Registers* registers, MemoryBus* memory, int instruction)
673         if ((!load || half || signed) && (load || half && !signed) && (preIncr || !writeBack)) {
674     // Decode operands
675     int rn = instruction.getBits(16, 19);
676     int rd = instruction.getBits(12, 15);
677     static if (immediate) {
678         int upperOffset = instruction.getBits(8, 11);
679         int lowerOffset = instruction & 0xF;
680         int offset = upperOffset << 4 | lowerOffset;
681     } else {
682         int offset = registers.get(instruction & 0xF);
683     }
684     int address = registers.get(rn);
685     // Do pre-increment if needed
686     static if (preIncr) {
687         static if (upIncr) {
688             address += offset;
689         } else {
690             address -= offset;
691         }
692     }
693     // Read or write memory
694     static if (load) {
695         static if (half) {
696             static if (signed) {
697                 debug (outputInstructions) registers.logInstruction(instruction, "LDRSH");
698                 registers.set(rd, address.rotateReadSigned(memory.get!short(address)));
699             } else {
700                 debug (outputInstructions) registers.logInstruction(instruction, "LDRH");
701                 registers.set(rd, address.rotateRead(memory.get!short(address)));
702             }
703         } else {
704             static if (signed) {
705                 debug (outputInstructions) registers.logInstruction(instruction, "LDRSB");
706                 registers.set(rd, memory.get!byte(address));
707             } else {
708                 static assert (0);
709             }
710         }
711     } else {
712         static if (half && !signed) {
713             debug (outputInstructions) registers.logInstruction(instruction, "STRH");
714             memory.set!short(address, cast(short) registers.get(rd));
715         } else {
716             static assert (0);
717         }
718     }
719     // Do post-increment and write back if needed
720     static if (preIncr) {
721         static if (writeBack) {
722             registers.set(rn, address);
723         }
724     } else {
725         static if (upIncr) {
726             address += offset;
727         } else {
728             address -= offset;
729         }
730         // Always do write back in post increment, the flag should be 0
731         static if (writeBack) {
732             static assert (0);
733         }
734         registers.set(rn, address);
735     }
736 }
737 
738 @("unsupported")
739 private template halfwordAndSignedDataTransfer(bool preIncr, bool upIncr, bool immediate,
740         bool writeBack, bool load, bool signed, bool half)
741         if (load && !half && !signed ||
742             !load && (!half || signed) ||
743             !preIncr && writeBack) {
744     private alias halfwordAndSignedDataTransfer = unsupported;
745 }
746 
747 private template singleDataTransfer_Imm(int code) if (code.getBits(5, 31) == 0) {
748     private alias singleDataTransfer_Imm = singleDataTransfer!(
749         false, code.checkBit(4), code.checkBit(3), code.checkBit(2),
750         code.checkBit(1), code.checkBit(0)
751     );
752 }
753 
754 private template singleDataTransfer_Reg(int code) if (code.getBits(5, 31) == 0) {
755     private alias singleDataTransfer_Reg = singleDataTransfer!(
756         true, code.checkBit(4), code.checkBit(3), code.checkBit(2),
757         code.checkBit(1), code.checkBit(0)
758     );
759 }
760 
761 private void singleDataTransfer(bool notImmediate, bool preIncr, bool upIncr, bool byteQty,
762         bool writeBack, bool load)(Registers* registers, MemoryBus* memory, int instruction) {
763     // Decode operands
764     int rn = instruction.getBits(16, 19);
765     int rd = instruction.getBits(12, 15);
766     static if (notImmediate) {
767         ubyte shift = cast(ubyte) instruction.getBits(7, 11);
768         int shiftType = instruction.getBits(5, 6);
769         int carry;
770         int offset = registers.applyShift!false(shiftType, shift, registers.get(instruction & 0b1111), carry);
771     } else {
772         int offset = instruction & 0xFFF;
773     }
774     int address = registers.get(rn);
775     // Do pre-increment if needed
776     static if (preIncr) {
777         static if (upIncr) {
778             address += offset;
779         } else {
780             address -= offset;
781         }
782     }
783     // Read or write memory
784     static if (load) {
785         static if (byteQty) {
786             debug (outputInstructions) registers.logInstruction(instruction, "LDRB");
787             registers.set(rd, memory.get!byte(address) & 0xFF);
788         } else {
789             debug (outputInstructions) registers.logInstruction(instruction, "LDR");
790             int data = address.rotateRead(memory.get!int(address));
791             if (rd == Register.PC) {
792                 data &= ~0b11;
793             }
794             registers.set(rd, data);
795         }
796     } else {
797         static if (byteQty) {
798             debug (outputInstructions) registers.logInstruction(instruction, "STRB");
799             memory.set!byte(address, cast(byte) registers.get(rd));
800         } else {
801             debug (outputInstructions) registers.logInstruction(instruction, "STR");
802             memory.set!int(address, registers.get(rd));
803         }
804     }
805     // Do post-increment and write back if needed
806     static if (preIncr) {
807         static if (writeBack) {
808             registers.set(rn, address);
809         }
810     } else {
811         static if (upIncr) {
812             address += offset;
813         } else {
814             address -= offset;
815         }
816         // Always do write back in post increment
817         registers.set(rn, address);
818     }
819 }
820 
821 private static string genBlockDataTransferOperation(bool preIncr, bool load) {
822     auto memoryOp = load ? "registers.set(mode, i, memory.get!int(address));\n" :
823         "memory.set!int(address, registers.get(mode, i));\n";
824     auto incr = "address += 4;\n";
825     auto singleOp = preIncr ? incr ~ memoryOp : memoryOp ~ incr;
826     auto ops =
827         `foreach (i; 0 .. 15) {
828             if (registerList.checkBit(i)) {
829                 ` ~ singleOp ~ `
830             }
831          }`;
832     // Handle PC specially because we need to align it on load
833     auto pcOp = load ? "registers.set(mode, i, memory.get!int(address) & ~0b11);\n" : memoryOp;
834     pcOp = preIncr ? incr ~ pcOp : pcOp ~ incr;
835     ops ~= `
836         immutable i = 15;
837         if (registerList.checkBit(i)) {
838             ` ~ pcOp ~ `
839         }`;
840     return ops;
841 }
842 
843 private template blockDataTransfer(int code) if (code.getBits(5, 31) == 0) {
844     private alias blockDataTransfer = blockDataTransfer!(
845         code.checkBit(4), code.checkBit(3), code.checkBit(2),
846         code.checkBit(1), code.checkBit(0)
847     );
848 }
849 
850 private void blockDataTransfer(bool preIncr, bool upIncr, bool loadPSR,
851         bool writeBack, bool load)(Registers* registers, MemoryBus* memory, int instruction) {
852     static if (load) {
853         debug (outputInstructions) registers.logInstruction(instruction, "LDM");
854     } else {
855         debug (outputInstructions) registers.logInstruction(instruction, "STM");
856     }
857     // Decode operands
858     int rn = instruction.getBits(16, 19);
859     int registerList = instruction & 0xFFFF;
860     // Force user mode if flag is set and not loading PC
861     static if (loadPSR) {
862         Mode mode = Mode.USER;
863         static if (load) {
864             if (registerList.checkBit(15)) {
865                 mode = registers.mode;
866             }
867         }
868     } else {
869         Mode mode = registers.mode;
870     }
871     // Memory transfer
872     int baseAddress = registers.get(rn);
873     int address;
874     static if (upIncr) {
875         address = baseAddress;
876         mixin (genBlockDataTransferOperation(preIncr, load));
877     } else {
878         baseAddress -= 4 * registerList.popcnt();
879         address = baseAddress;
880         // Load order is always in increasing memory order, even when
881         // using down-increment. This means we use bit counting to find
882         // the final address and use up-increments instead. This
883         // does reverse the pre-increment behaviour though
884         mixin (genBlockDataTransferOperation(!preIncr, load));
885         // The address to write back is the corrected base
886         address = baseAddress;
887     }
888     // Loading and load PSR flag is set, restore CPSR
889     static if (loadPSR && load) {
890         if (registerList.checkBit(15)) {
891             registers.setCPSR(registers.getSPSR());
892         }
893     }
894     // Writeback the new address into the base if needed
895     static if (writeBack) {
896         registers.set(mode, rn, address);
897     }
898 }
899 
900 private void branchAndBranchWithLink(int code: 0)(Registers* registers, MemoryBus* memory, int instruction) {
901     debug (outputInstructions) registers.logInstruction(instruction, "B");
902     int offset = instruction & 0xFFFFFF;
903     // sign extend the offset
904     offset <<= 8;
905     offset >>= 8;
906     int pc = registers.getPC();
907     registers.setPC(pc + offset * 4);
908 }
909 
910 private void branchAndBranchWithLink(int code: 1)(Registers* registers, MemoryBus* memory, int instruction) {
911     debug (outputInstructions) registers.logInstruction(instruction, "BL");
912     int offset = instruction & 0xFFFFFF;
913     // sign extend the offset
914     offset <<= 8;
915     offset >>= 8;
916     int pc = registers.getPC();
917     registers.set(Register.LR, pc - 4);
918     registers.setPC(pc + offset * 4);
919 }
920 
921 private void softwareInterrupt()(Registers* registers, MemoryBus* memory, int instruction) {
922     debug (outputInstructions) registers.logInstruction(instruction, "SWI");
923     registers.setSPSR(Mode.SUPERVISOR, registers.getCPSR());
924     registers.set(Mode.SUPERVISOR, Register.LR, registers.getPC() - 4);
925     registers.setPC(0x8);
926     registers.setFlag(CPSRFlag.I, 1);
927     registers.setMode(Mode.SUPERVISOR);
928 }
929 
930 private void undefined(Registers* registers, MemoryBus* memory, int instruction) {
931     debug (outputInstructions) registers.logInstruction(instruction, "UND");
932     registers.setSPSR(Mode.UNDEFINED, registers.getCPSR());
933     registers.set(Mode.UNDEFINED, Register.LR, registers.getPC() - 4);
934     registers.setPC(0x4);
935     registers.setFlag(CPSRFlag.I, 1);
936     registers.setMode(Mode.UNDEFINED);
937 }
938 
939 private void unsupported(Registers* registers, MemoryBus* memory, int instruction) {
940     throw new UnsupportedARMInstructionException(registers.getExecutedPC(), instruction);
941 }
942 
943 public class UnsupportedARMInstructionException : Exception {
944     private this(int address, int instruction) {
945         super(format("This ARM instruction is unsupported by the implementation\n%08x: %08x", address, instruction));
946     }
947 }