1 module gbaid.gba.cpu;
2 
3 import gbaid.util;
4 
5 import gbaid.gba.register;
6 import gbaid.gba.memory;
7 import gbaid.gba.arm;
8 import gbaid.gba.thumb;
9 
10 private enum uint AVERAGE_CPI = 2;
11 
12 public class ARM7TDMI {
13     private MemoryBus* memory;
14     private Registers registers;
15     private bool haltSignal = false;
16     private bool irqSignal = false;
17     private int instruction;
18     private int decoded;
19 
20     public this(MemoryBus* memory, uint entryPointAddress = 0x0) {
21         this.memory = memory;
22         // Initialize to ARM in system mode
23         registers.setFlag(CPSRFlag.T, Set.ARM);
24         registers.setMode(Mode.SYSTEM);
25         // Initialize the stack pointers
26         registers.set(Mode.SUPERVISOR, Register.SP, 0x3007FE0);
27         registers.set(Mode.IRQ, Register.SP, 0x3007FA0);
28         registers.set(Mode.USER, Register.SP, 0x3007F00);
29         // Set first instruction
30         registers.setPC(entryPointAddress);
31         // Branch to instruction
32         branch();
33     }
34 
35     public void halt(bool state) {
36         haltSignal = state;
37     }
38 
39     public void irq(bool state) {
40         irqSignal = state;
41     }
42 
43     public bool halted() {
44         return irqSignal;
45     }
46 
47     public bool inIRQ() {
48         return irqSignal;
49     }
50 
51     public int getProgramCounter() {
52         return registers.getExecutedPC();
53     }
54 
55     public int getPreFetch() {
56         return instruction;
57     }
58 
59     public int getNextInstruction() {
60         return decoded;
61     }
62 
63     public size_t emulate(size_t cycles) {
64         debug (outputInstructions) {
65             scope (failure) {
66                 registers.dumpInstructions();
67             }
68         }
69         // Discard all the cycles if halted
70         if (haltSignal) {
71             return 0;
72         }
73         // Otherwise use up 2 cycles per instruction
74         while (cycles >= AVERAGE_CPI) {
75             // Take the cycles for the instruction
76             cycles -= AVERAGE_CPI;
77             // Check for an IRQ
78             if (irqSignal && !registers.getFlag(CPSRFlag.I)) {
79                 // Branch to the handler
80                 branchIRQ();
81                 continue;
82             }
83             // Fetch the next instruction in the pipeline
84             int nextInstruction = fetchInstruction();
85             // "Decode" the second instruction in the pipeline (we don't need to actually do anything)
86             int nextDecoded = instruction;
87             instruction = nextInstruction;
88             // Execute the last instruction in the pipeline
89             final switch (registers.instructionSet) {
90                 case Set.ARM:
91                     executeARMInstruction(&registers, memory, decoded);
92                     break;
93                 case Set.THUMB:
94                     executeTHUMBInstruction(&registers, memory, decoded);
95                     break;
96             }
97             decoded = nextDecoded;
98             // Then go to the next instruction
99             if (registers.wasPCModified()) {
100                 branch();
101             } else {
102                 registers.incrementPC();
103             }
104             // Discard all the cycles if the instruction caused a halt
105             if (haltSignal) {
106                 return 0;
107             }
108         }
109         return cycles;
110     }
111 
112     private void branch() {
113         // fetch first instruction
114         int firstInstruction = fetchInstruction();
115         registers.incrementPC();
116         // fetch second
117         instruction = fetchInstruction();
118         registers.incrementPC();
119         // "decode" first
120         decoded = firstInstruction;
121     }
122 
123     private int fetchInstruction() {
124         final switch (registers.instructionSet) {
125             case Set.ARM:
126                 return memory.get!int(registers.getPC());
127             case Set.THUMB:
128                 return memory.get!short(registers.getPC()).mirror();
129         }
130     }
131 
132     private void branchIRQ() {
133         registers.setSPSR(Mode.IRQ, registers.getCPSR());
134         registers.set(Mode.IRQ, Register.LR, registers.getExecutedPC() + 4);
135         registers.setPC(0x18);
136         registers.setFlag(CPSRFlag.I, 1);
137         registers.setFlag(CPSRFlag.T, Set.ARM);
138         registers.setMode(Mode.IRQ);
139         branch();
140     }
141 }