all repos — mgba @ 9435226c58e998555e3908251b2fae32db783bd5

mGBA Game Boy Advance Emulator

src/arm/arm.c (view raw)

  1#include "arm.h"
  2
  3#include "isa-arm.h"
  4#include "isa-inlines.h"
  5#include "isa-thumb.h"
  6
  7#include <limits.h>
  8
  9static inline enum RegisterBank _ARMSelectBank(enum PrivilegeMode);
 10
 11void ARMSetPrivilegeMode(struct ARMCore* cpu, enum PrivilegeMode mode) {
 12	if (mode == cpu->privilegeMode) {
 13		// Not switching modes after all
 14		return;
 15	}
 16
 17	enum RegisterBank newBank = _ARMSelectBank(mode);
 18	enum RegisterBank oldBank = _ARMSelectBank(cpu->privilegeMode);
 19	if (newBank != oldBank) {
 20		// Switch banked registers
 21		if (mode == MODE_FIQ || cpu->privilegeMode == MODE_FIQ) {
 22			int oldFIQBank = oldBank == BANK_FIQ;
 23			int newFIQBank = newBank == BANK_FIQ;
 24			cpu->bankedRegisters[oldFIQBank][2] = cpu->gprs[8];
 25			cpu->bankedRegisters[oldFIQBank][3] = cpu->gprs[9];
 26			cpu->bankedRegisters[oldFIQBank][4] = cpu->gprs[10];
 27			cpu->bankedRegisters[oldFIQBank][5] = cpu->gprs[11];
 28			cpu->bankedRegisters[oldFIQBank][6] = cpu->gprs[12];
 29			cpu->gprs[8] = cpu->bankedRegisters[newFIQBank][2];
 30			cpu->gprs[9] = cpu->bankedRegisters[newFIQBank][3];
 31			cpu->gprs[10] = cpu->bankedRegisters[newFIQBank][4];
 32			cpu->gprs[11] = cpu->bankedRegisters[newFIQBank][5];
 33			cpu->gprs[12] = cpu->bankedRegisters[newFIQBank][6];
 34		}
 35		cpu->bankedRegisters[oldBank][0] = cpu->gprs[ARM_SP];
 36		cpu->bankedRegisters[oldBank][1] = cpu->gprs[ARM_LR];
 37		cpu->gprs[ARM_SP] = cpu->bankedRegisters[newBank][0];
 38		cpu->gprs[ARM_LR] = cpu->bankedRegisters[newBank][1];
 39
 40		cpu->bankedSPSRs[oldBank] = cpu->spsr.packed;
 41		cpu->spsr.packed = cpu->bankedSPSRs[newBank];
 42
 43	}
 44	cpu->privilegeMode = mode;
 45}
 46
 47static inline enum RegisterBank _ARMSelectBank(enum PrivilegeMode mode) {
 48	switch (mode) {
 49		case MODE_USER:
 50		case MODE_SYSTEM:
 51			// No banked registers
 52			return BANK_NONE;
 53		case MODE_FIQ:
 54			return BANK_FIQ;
 55		case MODE_IRQ:
 56			return BANK_IRQ;
 57		case MODE_SUPERVISOR:
 58			return BANK_SUPERVISOR;
 59		case MODE_ABORT:
 60			return BANK_ABORT;
 61		case MODE_UNDEFINED:
 62			return BANK_UNDEFINED;
 63		default:
 64			// This should be unreached
 65			return BANK_NONE;
 66	}
 67}
 68
 69void ARMInit(struct ARMCore* cpu) {
 70	cpu->memory = 0;
 71	cpu->board = 0;
 72}
 73
 74void ARMAssociateMemory(struct ARMCore* cpu, struct ARMMemory* memory) {
 75	cpu->memory = memory;
 76}
 77
 78void ARMAssociateBoard(struct ARMCore* cpu, struct ARMBoard* board) {
 79	cpu->board = board;
 80	board->cpu = cpu;
 81}
 82
 83void ARMReset(struct ARMCore* cpu) {
 84	int i;
 85	for (i = 0; i < 16; ++i) {
 86		cpu->gprs[i] = 0;
 87	}
 88	for (i = 0; i < 6; ++i) {
 89		cpu->bankedRegisters[i][0] = 0;
 90		cpu->bankedRegisters[i][1] = 0;
 91		cpu->bankedRegisters[i][2] = 0;
 92		cpu->bankedRegisters[i][3] = 0;
 93		cpu->bankedRegisters[i][4] = 0;
 94		cpu->bankedRegisters[i][5] = 0;
 95		cpu->bankedRegisters[i][6] = 0;
 96		cpu->bankedSPSRs[i] = 0;
 97	}
 98
 99	cpu->privilegeMode = MODE_SYSTEM;
100	cpu->cpsr.packed = MODE_SYSTEM;
101	cpu->spsr.packed = 0;
102
103	cpu->shifterOperand = 0;
104	cpu->shifterCarryOut = 0;
105
106	cpu->executionMode = MODE_THUMB;
107	_ARMSetMode(cpu, MODE_ARM);
108
109	cpu->currentPC = 0;
110	int currentCycles = 0;
111	ARM_WRITE_PC;
112
113	cpu->cycles = 0;
114	cpu->nextEvent = 0;
115
116	cpu->board->reset(cpu->board);
117}
118
119void ARMRaiseIRQ(struct ARMCore* cpu) {
120	if (cpu->cpsr.i) {
121		return;
122	}
123	union PSR cpsr = cpu->cpsr;
124	int instructionWidth;
125	if (cpu->executionMode == MODE_THUMB) {
126		instructionWidth = WORD_SIZE_THUMB;
127	} else {
128		instructionWidth = WORD_SIZE_ARM;
129	}
130	ARMSetPrivilegeMode(cpu, MODE_IRQ);
131	cpu->cpsr.priv = MODE_IRQ;
132	cpu->gprs[ARM_LR] = cpu->gprs[ARM_PC] - instructionWidth + WORD_SIZE_ARM;
133	cpu->gprs[ARM_PC] = BASE_IRQ + WORD_SIZE_ARM;
134	cpu->memory->setActiveRegion(cpu->memory, cpu->gprs[ARM_PC]);
135	_ARMSetMode(cpu, MODE_ARM);
136	cpu->spsr = cpsr;
137	cpu->cpsr.i = 1;
138}
139
140void ARMRaiseSWI(struct ARMCore* cpu) {
141	union PSR cpsr = cpu->cpsr;
142	int instructionWidth;
143	if (cpu->executionMode == MODE_THUMB) {
144		instructionWidth = WORD_SIZE_THUMB;
145	} else {
146		instructionWidth = WORD_SIZE_ARM;
147	}
148	ARMSetPrivilegeMode(cpu, MODE_SUPERVISOR);
149	cpu->cpsr.priv = MODE_SUPERVISOR;
150	cpu->gprs[ARM_LR] = cpu->gprs[ARM_PC] - instructionWidth;
151	cpu->gprs[ARM_PC] = BASE_SWI + WORD_SIZE_ARM;
152	cpu->memory->setActiveRegion(cpu->memory, cpu->gprs[ARM_PC]);
153	_ARMSetMode(cpu, MODE_ARM);
154	cpu->spsr = cpsr;
155	cpu->cpsr.i = 1;
156}
157
158static inline void ARMStep(struct ARMCore* cpu) {
159	uint32_t opcode;
160	cpu->currentPC = cpu->gprs[ARM_PC] - WORD_SIZE_ARM;
161	LOAD_32(opcode, cpu->currentPC & cpu->memory->activeMask, cpu->memory->activeRegion);
162	cpu->gprs[ARM_PC] += WORD_SIZE_ARM;
163
164	int condition = opcode >> 28;
165	if (condition == 0xE) {
166		ARMInstruction instruction = _armTable[((opcode >> 16) & 0xFF0) | ((opcode >> 4) & 0x00F)];
167		instruction(cpu, opcode);
168		return;
169	} else {
170		switch (condition) {
171		case 0x0:
172			if (!ARM_COND_EQ) {
173				cpu->cycles += ARM_PREFETCH_CYCLES;
174				return;
175			}
176			break;
177		case 0x1:
178			if (!ARM_COND_NE) {
179				cpu->cycles += ARM_PREFETCH_CYCLES;
180				return;
181			}
182			break;
183		case 0x2:
184			if (!ARM_COND_CS) {
185				cpu->cycles += ARM_PREFETCH_CYCLES;
186				return;
187			}
188			break;
189		case 0x3:
190			if (!ARM_COND_CC) {
191				cpu->cycles += ARM_PREFETCH_CYCLES;
192				return;
193			}
194			break;
195		case 0x4:
196			if (!ARM_COND_MI) {
197				cpu->cycles += ARM_PREFETCH_CYCLES;
198				return;
199			}
200			break;
201		case 0x5:
202			if (!ARM_COND_PL) {
203				cpu->cycles += ARM_PREFETCH_CYCLES;
204				return;
205			}
206			break;
207		case 0x6:
208			if (!ARM_COND_VS) {
209				cpu->cycles += ARM_PREFETCH_CYCLES;
210				return;
211			}
212			break;
213		case 0x7:
214			if (!ARM_COND_VC) {
215				cpu->cycles += ARM_PREFETCH_CYCLES;
216				return;
217			}
218			break;
219		case 0x8:
220			if (!ARM_COND_HI) {
221				cpu->cycles += ARM_PREFETCH_CYCLES;
222				return;
223			}
224			break;
225		case 0x9:
226			if (!ARM_COND_LS) {
227				cpu->cycles += ARM_PREFETCH_CYCLES;
228				return;
229			}
230			break;
231		case 0xA:
232			if (!ARM_COND_GE) {
233				cpu->cycles += ARM_PREFETCH_CYCLES;
234				return;
235			}
236			break;
237		case 0xB:
238			if (!ARM_COND_LT) {
239				cpu->cycles += ARM_PREFETCH_CYCLES;
240				return;
241			}
242			break;
243		case 0xC:
244			if (!ARM_COND_GT) {
245				cpu->cycles += ARM_PREFETCH_CYCLES;
246				return;
247			}
248			break;
249		case 0xD:
250			if (!ARM_COND_LE) {
251				cpu->cycles += ARM_PREFETCH_CYCLES;
252				return;
253			}
254			break;
255		default:
256			break;
257		}
258	}
259	ARMInstruction instruction = _armTable[((opcode >> 16) & 0xFF0) | ((opcode >> 4) & 0x00F)];
260	instruction(cpu, opcode);
261}
262
263static inline void ThumbStep(struct ARMCore* cpu) {
264	cpu->currentPC = cpu->gprs[ARM_PC] - WORD_SIZE_THUMB;
265	cpu->gprs[ARM_PC] += WORD_SIZE_THUMB;
266	uint16_t opcode;
267	LOAD_16(opcode, cpu->currentPC & cpu->memory->activeMask, cpu->memory->activeRegion);
268	ThumbInstruction instruction = _thumbTable[opcode >> 6];
269	instruction(cpu, opcode);
270}
271
272void ARMRun(struct ARMCore* cpu) {
273	if (cpu->executionMode == MODE_THUMB) {
274		ThumbStep(cpu);
275	} else {
276		ARMStep(cpu);
277	}
278	if (cpu->cycles >= cpu->nextEvent) {
279		cpu->board->processEvents(cpu->board);
280	}
281}