src/arm/arm.c (view raw)
1/* Copyright (c) 2013-2014 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include <mgba/internal/arm/arm.h>
7
8#include <mgba/internal/arm/isa-arm.h>
9#include <mgba/internal/arm/isa-inlines.h>
10#include <mgba/internal/arm/isa-thumb.h>
11
12static inline enum RegisterBank _ARMSelectBank(enum PrivilegeMode);
13
14void ARMSetPrivilegeMode(struct ARMCore* cpu, enum PrivilegeMode mode) {
15 if (mode == cpu->privilegeMode) {
16 // Not switching modes after all
17 return;
18 }
19
20 enum RegisterBank newBank = _ARMSelectBank(mode);
21 enum RegisterBank oldBank = _ARMSelectBank(cpu->privilegeMode);
22 if (newBank != oldBank) {
23 // Switch banked registers
24 if (mode == MODE_FIQ || cpu->privilegeMode == MODE_FIQ) {
25 int oldFIQBank = oldBank == BANK_FIQ;
26 int newFIQBank = newBank == BANK_FIQ;
27 cpu->bankedRegisters[oldFIQBank][2] = cpu->gprs[8];
28 cpu->bankedRegisters[oldFIQBank][3] = cpu->gprs[9];
29 cpu->bankedRegisters[oldFIQBank][4] = cpu->gprs[10];
30 cpu->bankedRegisters[oldFIQBank][5] = cpu->gprs[11];
31 cpu->bankedRegisters[oldFIQBank][6] = cpu->gprs[12];
32 cpu->gprs[8] = cpu->bankedRegisters[newFIQBank][2];
33 cpu->gprs[9] = cpu->bankedRegisters[newFIQBank][3];
34 cpu->gprs[10] = cpu->bankedRegisters[newFIQBank][4];
35 cpu->gprs[11] = cpu->bankedRegisters[newFIQBank][5];
36 cpu->gprs[12] = cpu->bankedRegisters[newFIQBank][6];
37 }
38 cpu->bankedRegisters[oldBank][0] = cpu->gprs[ARM_SP];
39 cpu->bankedRegisters[oldBank][1] = cpu->gprs[ARM_LR];
40 cpu->gprs[ARM_SP] = cpu->bankedRegisters[newBank][0];
41 cpu->gprs[ARM_LR] = cpu->bankedRegisters[newBank][1];
42
43 cpu->bankedSPSRs[oldBank] = cpu->spsr;
44 cpu->spsr = cpu->bankedSPSRs[newBank];
45 }
46 cpu->privilegeMode = mode;
47}
48
49static inline enum RegisterBank _ARMSelectBank(enum PrivilegeMode mode) {
50 switch (mode) {
51 case MODE_USER:
52 case MODE_SYSTEM:
53 // No banked registers
54 return BANK_NONE;
55 case MODE_FIQ:
56 return BANK_FIQ;
57 case MODE_IRQ:
58 return BANK_IRQ;
59 case MODE_SUPERVISOR:
60 return BANK_SUPERVISOR;
61 case MODE_ABORT:
62 return BANK_ABORT;
63 case MODE_UNDEFINED:
64 return BANK_UNDEFINED;
65 default:
66 // This should be unreached
67 return BANK_NONE;
68 }
69}
70
71void ARMInit(struct ARMCore* cpu) {
72 cpu->master->init(cpu, cpu->master);
73 size_t i;
74 for (i = 0; i < cpu->numComponents; ++i) {
75 if (cpu->components[i] && cpu->components[i]->init) {
76 cpu->components[i]->init(cpu, cpu->components[i]);
77 }
78 }
79}
80
81void ARMDeinit(struct ARMCore* cpu) {
82 if (cpu->master->deinit) {
83 cpu->master->deinit(cpu->master);
84 }
85 size_t i;
86 for (i = 0; i < cpu->numComponents; ++i) {
87 if (cpu->components[i] && cpu->components[i]->deinit) {
88 cpu->components[i]->deinit(cpu->components[i]);
89 }
90 }
91}
92
93void ARMSetComponents(struct ARMCore* cpu, struct mCPUComponent* master, int extra, struct mCPUComponent** extras) {
94 cpu->master = master;
95 cpu->numComponents = extra;
96 cpu->components = extras;
97}
98
99void ARMHotplugAttach(struct ARMCore* cpu, size_t slot) {
100 if (slot >= cpu->numComponents) {
101 return;
102 }
103 cpu->components[slot]->init(cpu, cpu->components[slot]);
104}
105
106void ARMHotplugDetach(struct ARMCore* cpu, size_t slot) {
107 if (slot >= cpu->numComponents) {
108 return;
109 }
110 cpu->components[slot]->deinit(cpu->components[slot]);
111}
112
113void ARMReset(struct ARMCore* cpu) {
114 int i;
115 for (i = 0; i < 16; ++i) {
116 cpu->gprs[i] = 0;
117 }
118 for (i = 0; i < 6; ++i) {
119 cpu->bankedRegisters[i][0] = 0;
120 cpu->bankedRegisters[i][1] = 0;
121 cpu->bankedRegisters[i][2] = 0;
122 cpu->bankedRegisters[i][3] = 0;
123 cpu->bankedRegisters[i][4] = 0;
124 cpu->bankedRegisters[i][5] = 0;
125 cpu->bankedRegisters[i][6] = 0;
126 cpu->bankedSPSRs[i] = 0;
127 }
128
129 cpu->privilegeMode = MODE_SYSTEM;
130 cpu->cpsr = MODE_SYSTEM;
131 cpu->spsr = 0;
132
133 cpu->shifterOperand = 0;
134 cpu->shifterCarryOut = 0;
135
136 cpu->executionMode = MODE_THUMB;
137 _ARMSetMode(cpu, MODE_ARM);
138
139 int currentCycles = 0;
140 ARM_WRITE_PC;
141
142 cpu->cycles = 0;
143 cpu->nextEvent = 0;
144 cpu->halted = 0;
145
146 cpu->irqh.reset(cpu);
147}
148
149void ARMRaiseIRQ(struct ARMCore* cpu) {
150 if (ARMPSRIsI(cpu->cpsr)) {
151 return;
152 }
153 ARMPSR cpsr = cpu->cpsr;
154 int instructionWidth;
155 if (cpu->executionMode == MODE_THUMB) {
156 instructionWidth = WORD_SIZE_THUMB;
157 } else {
158 instructionWidth = WORD_SIZE_ARM;
159 }
160 ARMSetPrivilegeMode(cpu, MODE_IRQ);
161 cpu->cpsr = ARMPSRSetPriv(cpu->cpsr, MODE_IRQ);
162 cpu->gprs[ARM_LR] = cpu->gprs[ARM_PC] - instructionWidth + WORD_SIZE_ARM;
163 cpu->gprs[ARM_PC] = BASE_IRQ;
164 int currentCycles = 0;
165 ARM_WRITE_PC;
166 _ARMSetMode(cpu, MODE_ARM);
167 cpu->spsr = cpsr;
168 cpu->cpsr = ARMPSRFillI(cpu->cpsr);
169 cpu->cycles += currentCycles;
170}
171
172void ARMRaiseSWI(struct ARMCore* cpu) {
173 ARMPSR cpsr = cpu->cpsr;
174 int instructionWidth;
175 if (cpu->executionMode == MODE_THUMB) {
176 instructionWidth = WORD_SIZE_THUMB;
177 } else {
178 instructionWidth = WORD_SIZE_ARM;
179 }
180 ARMSetPrivilegeMode(cpu, MODE_SUPERVISOR);
181 cpu->cpsr = ARMPSRSetPriv(cpu->cpsr, MODE_SUPERVISOR);
182 cpu->gprs[ARM_LR] = cpu->gprs[ARM_PC] - instructionWidth;
183 cpu->gprs[ARM_PC] = BASE_SWI;
184 int currentCycles = 0;
185 ARM_WRITE_PC;
186 _ARMSetMode(cpu, MODE_ARM);
187 cpu->spsr = cpsr;
188 cpu->cpsr = ARMPSRFillI(cpu->cpsr);
189 cpu->cycles += currentCycles;
190}
191
192void ARMRaiseUndefined(struct ARMCore* cpu) {
193 ARMPSR cpsr = cpu->cpsr;
194 int instructionWidth;
195 if (cpu->executionMode == MODE_THUMB) {
196 instructionWidth = WORD_SIZE_THUMB;
197 } else {
198 instructionWidth = WORD_SIZE_ARM;
199 }
200 ARMSetPrivilegeMode(cpu, MODE_UNDEFINED);
201 cpu->cpsr = ARMPSRSetPriv(cpu->cpsr, MODE_UNDEFINED);
202 cpu->gprs[ARM_LR] = cpu->gprs[ARM_PC] - instructionWidth;
203 cpu->gprs[ARM_PC] = BASE_UNDEF;
204 int currentCycles = 0;
205 ARM_WRITE_PC;
206 _ARMSetMode(cpu, MODE_ARM);
207 cpu->spsr = cpsr;
208 cpu->cpsr = ARMPSRFillI(cpu->cpsr);
209 cpu->cycles += currentCycles;
210}
211
212static inline void ARMStep(struct ARMCore* cpu) {
213 uint32_t opcode = cpu->prefetch[0];
214 cpu->prefetch[0] = cpu->prefetch[1];
215 cpu->gprs[ARM_PC] += WORD_SIZE_ARM;
216 LOAD_32(cpu->prefetch[1], cpu->gprs[ARM_PC] & cpu->memory.activeMask, cpu->memory.activeRegion);
217
218 unsigned condition = opcode >> 28;
219 if (condition != 0xE) {
220 bool conditionMet = false;
221 switch (condition) {
222 case 0x0:
223 conditionMet = ARM_COND_EQ;
224 break;
225 case 0x1:
226 conditionMet = ARM_COND_NE;
227 break;
228 case 0x2:
229 conditionMet = ARM_COND_CS;
230 break;
231 case 0x3:
232 conditionMet = ARM_COND_CC;
233 break;
234 case 0x4:
235 conditionMet = ARM_COND_MI;
236 break;
237 case 0x5:
238 conditionMet = ARM_COND_PL;
239 break;
240 case 0x6:
241 conditionMet = ARM_COND_VS;
242 break;
243 case 0x7:
244 conditionMet = ARM_COND_VC;
245 break;
246 case 0x8:
247 conditionMet = ARM_COND_HI;
248 break;
249 case 0x9:
250 conditionMet = ARM_COND_LS;
251 break;
252 case 0xA:
253 conditionMet = ARM_COND_GE;
254 break;
255 case 0xB:
256 conditionMet = ARM_COND_LT;
257 break;
258 case 0xC:
259 conditionMet = ARM_COND_GT;
260 break;
261 case 0xD:
262 conditionMet = ARM_COND_LE;
263 break;
264 default:
265 break;
266 }
267 if (!conditionMet) {
268 cpu->cycles += ARM_PREFETCH_CYCLES;
269 return;
270 }
271 }
272 ARMInstruction instruction = _armTable[((opcode >> 16) & 0xFF0) | ((opcode >> 4) & 0x00F)];
273 instruction(cpu, opcode);
274}
275
276static inline void ThumbStep(struct ARMCore* cpu) {
277 uint32_t opcode = cpu->prefetch[0];
278 cpu->prefetch[0] = cpu->prefetch[1];
279 cpu->gprs[ARM_PC] += WORD_SIZE_THUMB;
280 LOAD_16(cpu->prefetch[1], cpu->gprs[ARM_PC] & cpu->memory.activeMask, cpu->memory.activeRegion);
281 ThumbInstruction instruction = _thumbTable[opcode >> 6];
282 instruction(cpu, opcode);
283}
284
285void ARMRun(struct ARMCore* cpu) {
286 if (cpu->executionMode == MODE_THUMB) {
287 ThumbStep(cpu);
288 } else {
289 ARMStep(cpu);
290 }
291 if (cpu->cycles >= cpu->nextEvent) {
292 cpu->irqh.processEvents(cpu);
293 }
294}
295
296void ARMRunLoop(struct ARMCore* cpu) {
297 if (cpu->executionMode == MODE_THUMB) {
298 while (cpu->cycles < cpu->nextEvent) {
299 ThumbStep(cpu);
300 }
301 } else {
302 while (cpu->cycles < cpu->nextEvent) {
303 ARMStep(cpu);
304 }
305 }
306 cpu->irqh.processEvents(cpu);
307}
308
309void ARMRunFake(struct ARMCore* cpu, uint32_t opcode) {
310 if (cpu->executionMode == MODE_ARM) {
311 cpu->gprs[ARM_PC] -= WORD_SIZE_ARM;
312 } else {
313 cpu->gprs[ARM_PC] -= WORD_SIZE_THUMB;
314 }
315 cpu->prefetch[1] = cpu->prefetch[0];
316 cpu->prefetch[0] = opcode;
317}