all repos — mgba @ cb0f95b07053e63e817bd05df0a36bf917667d09

mGBA Game Boy Advance Emulator

src/arm/arm.c (view raw)

  1/* Copyright (c) 2013-2016 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.packed;
 44		cpu->spsr.packed = 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	memset(&cpu->cp15, 0, sizeof(cpu->cp15));
 73	cpu->master->init(cpu, cpu->master);
 74	size_t i;
 75	for (i = 0; i < cpu->numComponents; ++i) {
 76		if (cpu->components[i] && cpu->components[i]->init) {
 77			cpu->components[i]->init(cpu, cpu->components[i]);
 78		}
 79	}
 80}
 81
 82void ARMDeinit(struct ARMCore* cpu) {
 83	if (cpu->master->deinit) {
 84		cpu->master->deinit(cpu->master);
 85	}
 86	size_t i;
 87	for (i = 0; i < cpu->numComponents; ++i) {
 88		if (cpu->components[i] && cpu->components[i]->deinit) {
 89			cpu->components[i]->deinit(cpu->components[i]);
 90		}
 91	}
 92}
 93
 94void ARMSetComponents(struct ARMCore* cpu, struct mCPUComponent* master, int extra, struct mCPUComponent** extras) {
 95	cpu->master = master;
 96	cpu->numComponents = extra;
 97	cpu->components = extras;
 98}
 99
100void ARMHotplugAttach(struct ARMCore* cpu, size_t slot) {
101	if (slot >= cpu->numComponents) {
102		return;
103	}
104	cpu->components[slot]->init(cpu, cpu->components[slot]);
105}
106
107void ARMHotplugDetach(struct ARMCore* cpu, size_t slot) {
108	if (slot >= cpu->numComponents) {
109		return;
110	}
111	cpu->components[slot]->deinit(cpu->components[slot]);
112}
113
114void ARMReset(struct ARMCore* cpu) {
115	int i;
116	for (i = 0; i < 16; ++i) {
117		cpu->gprs[i] = 0;
118	}
119	if (ARMControlRegIsVE(cpu->cp15.r1.c0)) {
120		cpu->gprs[ARM_PC] = 0xFFFF0000;
121	}
122
123	for (i = 0; i < 6; ++i) {
124		cpu->bankedRegisters[i][0] = 0;
125		cpu->bankedRegisters[i][1] = 0;
126		cpu->bankedRegisters[i][2] = 0;
127		cpu->bankedRegisters[i][3] = 0;
128		cpu->bankedRegisters[i][4] = 0;
129		cpu->bankedRegisters[i][5] = 0;
130		cpu->bankedRegisters[i][6] = 0;
131		cpu->bankedSPSRs[i] = 0;
132	}
133
134	cpu->privilegeMode = MODE_SYSTEM;
135	cpu->cpsr.packed = MODE_SYSTEM;
136	cpu->spsr.packed = 0;
137
138	cpu->shifterOperand = 0;
139	cpu->shifterCarryOut = 0;
140
141	cpu->executionMode = MODE_THUMB;
142	_ARMSetMode(cpu, MODE_ARM);
143
144	int currentCycles = 0;
145	ARM_WRITE_PC;
146
147	cpu->cycles = 0;
148	cpu->nextEvent = 0;
149	cpu->halted = 0;
150
151	cpu->irqh.reset(cpu);
152}
153
154void ARMRaiseIRQ(struct ARMCore* cpu) {
155	if (cpu->cpsr.i) {
156		return;
157	}
158	union PSR cpsr = cpu->cpsr;
159	int instructionWidth;
160	if (cpu->executionMode == MODE_THUMB) {
161		instructionWidth = WORD_SIZE_THUMB;
162	} else {
163		instructionWidth = WORD_SIZE_ARM;
164	}
165	ARMSetPrivilegeMode(cpu, MODE_IRQ);
166	cpu->cpsr.priv = MODE_IRQ;
167	cpu->gprs[ARM_LR] = cpu->gprs[ARM_PC] - instructionWidth + WORD_SIZE_ARM;
168	cpu->gprs[ARM_PC] = BASE_IRQ;
169	if (ARMControlRegIsVE(cpu->cp15.r1.c0)) {
170		cpu->gprs[ARM_PC] |= 0xFFFF0000;
171	}
172	int currentCycles = 0;
173	ARM_WRITE_PC;
174	_ARMSetMode(cpu, MODE_ARM);
175	cpu->spsr = cpsr;
176	cpu->cpsr.i = 1;
177	cpu->cycles += currentCycles;
178}
179
180void ARMRaiseSWI(struct ARMCore* cpu) {
181	union PSR cpsr = cpu->cpsr;
182	int instructionWidth;
183	if (cpu->executionMode == MODE_THUMB) {
184		instructionWidth = WORD_SIZE_THUMB;
185	} else {
186		instructionWidth = WORD_SIZE_ARM;
187	}
188	ARMSetPrivilegeMode(cpu, MODE_SUPERVISOR);
189	cpu->cpsr.priv = MODE_SUPERVISOR;
190	cpu->gprs[ARM_LR] = cpu->gprs[ARM_PC] - instructionWidth;
191	cpu->gprs[ARM_PC] = BASE_SWI;
192	if (ARMControlRegIsVE(cpu->cp15.r1.c0)) {
193		cpu->gprs[ARM_PC] |= 0xFFFF0000;
194	}
195	int currentCycles = 0;
196	ARM_WRITE_PC;
197	_ARMSetMode(cpu, MODE_ARM);
198	cpu->spsr = cpsr;
199	cpu->cpsr.i = 1;
200	cpu->cycles += currentCycles;
201}
202
203void ARMRaiseUndefined(struct ARMCore* cpu) {
204	union PSR cpsr = cpu->cpsr;
205	int instructionWidth;
206	if (cpu->executionMode == MODE_THUMB) {
207		instructionWidth = WORD_SIZE_THUMB;
208	} else {
209		instructionWidth = WORD_SIZE_ARM;
210	}
211	ARMSetPrivilegeMode(cpu, MODE_UNDEFINED);
212	cpu->cpsr.priv = MODE_UNDEFINED;
213	cpu->gprs[ARM_LR] = cpu->gprs[ARM_PC] - instructionWidth;
214	cpu->gprs[ARM_PC] = BASE_UNDEF;
215	if (ARMControlRegIsVE(cpu->cp15.r1.c0)) {
216		cpu->gprs[ARM_PC] |= 0xFFFF0000;
217	}
218	int currentCycles = 0;
219	ARM_WRITE_PC;
220	_ARMSetMode(cpu, MODE_ARM);
221	cpu->spsr = cpsr;
222	cpu->cpsr.i = 1;
223	cpu->cycles += currentCycles;
224}
225
226void ARMHalt(struct ARMCore* cpu) {
227	cpu->nextEvent = cpu->cycles;
228	cpu->halted = 1;
229}
230
231#define ARM_IMPLEMENT(VERSION) \
232	static inline void ARM ## VERSION ## Step(struct ARMCore* cpu) { \
233		uint32_t opcode = cpu->prefetch[0]; \
234		cpu->prefetch[0] = cpu->prefetch[1]; \
235		cpu->gprs[ARM_PC] += WORD_SIZE_ARM; \
236		LOAD_32(cpu->prefetch[1], cpu->gprs[ARM_PC] & cpu->memory.activeMask, cpu->memory.activeRegion); \
237		\
238		ARMInstruction instruction; \
239		unsigned condition = opcode >> 28; \
240		if (condition != 0xE) { \
241			bool conditionMet = false; \
242			switch (condition) { \
243			case 0x0: \
244				conditionMet = ARM_COND_EQ; \
245				break; \
246			case 0x1: \
247				conditionMet = ARM_COND_NE; \
248				break; \
249			case 0x2: \
250				conditionMet = ARM_COND_CS; \
251				break; \
252			case 0x3: \
253				conditionMet = ARM_COND_CC; \
254				break; \
255			case 0x4: \
256				conditionMet = ARM_COND_MI; \
257				break; \
258			case 0x5: \
259				conditionMet = ARM_COND_PL; \
260				break; \
261			case 0x6: \
262				conditionMet = ARM_COND_VS; \
263				break; \
264			case 0x7: \
265				conditionMet = ARM_COND_VC; \
266				break; \
267			case 0x8: \
268				conditionMet = ARM_COND_HI; \
269				break; \
270			case 0x9: \
271				conditionMet = ARM_COND_LS; \
272				break; \
273			case 0xA: \
274				conditionMet = ARM_COND_GE; \
275				break; \
276			case 0xB: \
277				conditionMet = ARM_COND_LT; \
278				break; \
279			case 0xC: \
280				conditionMet = ARM_COND_GT; \
281				break; \
282			case 0xD: \
283				conditionMet = ARM_COND_LE; \
284				break; \
285			default: \
286				instruction = _arm ## VERSION ## FTable[((opcode >> 16) & 0xFF0) | ((opcode >> 4) & 0x00F)]; \
287				instruction(cpu, opcode); \
288				return; \
289			} \
290			if (!conditionMet) { \
291				cpu->cycles += ARM_PREFETCH_CYCLES; \
292				return; \
293			} \
294		} \
295		instruction = _arm ## VERSION ## Table[((opcode >> 16) & 0xFF0) | ((opcode >> 4) & 0x00F)]; \
296		instruction(cpu, opcode); \
297	} \
298	\
299	static inline void Thumb ## VERSION ## Step(struct ARMCore* cpu) { \
300		uint32_t opcode = cpu->prefetch[0]; \
301		cpu->prefetch[0] = cpu->prefetch[1]; \
302		cpu->gprs[ARM_PC] += WORD_SIZE_THUMB; \
303		LOAD_16(cpu->prefetch[1], cpu->gprs[ARM_PC] & cpu->memory.activeMask, cpu->memory.activeRegion); \
304		ThumbInstruction instruction = _thumb ## VERSION ## Table[opcode >> 6]; \
305		instruction(cpu, opcode); \
306	} \
307	\
308	void ARM ## VERSION ## Run(struct ARMCore* cpu) { \
309		if (cpu->cycles < cpu->nextEvent) { \
310			if (cpu->executionMode == MODE_THUMB) { \
311				Thumb ## VERSION ## Step(cpu); \
312			} else { \
313				ARM ## VERSION ## Step(cpu); \
314			} \
315		} \
316		if (cpu->cycles >= cpu->nextEvent) { \
317			cpu->irqh.processEvents(cpu); \
318		} \
319	} \
320	\
321	void ARM ## VERSION ## RunLoop(struct ARMCore* cpu) { \
322		if (cpu->executionMode == MODE_THUMB) { \
323			while (cpu->cycles < cpu->nextEvent) { \
324				Thumb ## VERSION ## Step(cpu); \
325			} \
326		} else { \
327			while (cpu->cycles < cpu->nextEvent) { \
328				ARM ## VERSION ## Step(cpu); \
329			} \
330		} \
331		cpu->irqh.processEvents(cpu); \
332	}
333
334ARM_IMPLEMENT(v4)
335ARM_IMPLEMENT(v5)
336
337void ARMRunFake(struct ARMCore* cpu, uint32_t opcode) {
338	if (cpu->executionMode == MODE_ARM) {
339		cpu->gprs[ARM_PC] -= WORD_SIZE_ARM;
340	} else {
341		cpu->gprs[ARM_PC] -= WORD_SIZE_THUMB;
342	}
343	cpu->prefetch[1] = cpu->prefetch[0];
344	cpu->prefetch[0] = opcode;
345}