all repos — mgba @ 775e417cc6781ceb30520c85c968d198efb87429

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