all repos — mgba @ 5122a236e0cb6ed73a274584b94d0639bf0157f3

mGBA Game Boy Advance Emulator

src/gba/gba.c (view raw)

  1#include "gba.h"
  2
  3#include "gba-bios.h"
  4#include "gba-io.h"
  5
  6#include "debugger.h"
  7
  8#include <limits.h>
  9#include <stdarg.h>
 10#include <stdio.h>
 11#include <stdlib.h>
 12#include <string.h>
 13#include <sys/mman.h>
 14
 15enum {
 16	SP_BASE_SYSTEM = 0x03FFFF00,
 17	SP_BASE_IRQ = 0x03FFFFA0,
 18	SP_BASE_SUPERVISOR = 0x03FFFFE0
 19};
 20
 21static void GBAProcessEvents(struct ARMBoard* board);
 22static int32_t GBATimersProcessEvents(struct GBA* gba, int32_t cycles);
 23static void GBAHitStub(struct ARMBoard* board, uint32_t opcode);
 24
 25void GBAInit(struct GBA* gba) {
 26	gba->errno = GBA_NO_ERROR;
 27	gba->errstr = 0;
 28
 29	ARMInit(&gba->cpu);
 30
 31	gba->memory.p = gba;
 32	GBAMemoryInit(&gba->memory);
 33	ARMAssociateMemory(&gba->cpu, &gba->memory.d);
 34
 35	gba->board.p = gba;
 36	GBABoardInit(&gba->board);
 37	ARMAssociateBoard(&gba->cpu, &gba->board.d);
 38
 39	gba->video.p = gba;
 40	GBAVideoInit(&gba->video);
 41
 42	GBAIOInit(gba);
 43
 44	memset(gba->timers, 0, sizeof(gba->timers));
 45
 46	gba->springIRQ = 0;
 47	gba->keySource = 0;
 48
 49	ARMReset(&gba->cpu);
 50}
 51
 52void GBADeinit(struct GBA* gba) {
 53	GBAMemoryDeinit(&gba->memory);
 54}
 55
 56void GBABoardInit(struct GBABoard* board) {
 57	board->d.reset = GBABoardReset;
 58	board->d.processEvents = GBAProcessEvents;
 59	board->d.swi16 = GBASwi16;
 60	board->d.swi32 = GBASwi32;
 61	board->d.hitStub = GBAHitStub;
 62}
 63
 64void GBABoardReset(struct ARMBoard* board) {
 65	struct ARMCore* cpu = board->cpu;
 66	ARMSetPrivilegeMode(cpu, MODE_IRQ);
 67	cpu->gprs[ARM_SP] = SP_BASE_IRQ;
 68	ARMSetPrivilegeMode(cpu, MODE_SUPERVISOR);
 69	cpu->gprs[ARM_SP] = SP_BASE_SUPERVISOR;
 70	ARMSetPrivilegeMode(cpu, MODE_SYSTEM);
 71	cpu->gprs[ARM_SP] = SP_BASE_SYSTEM;
 72}
 73
 74static void GBAProcessEvents(struct ARMBoard* board) {
 75	struct GBABoard* gbaBoard = (struct GBABoard*) board;
 76	int32_t cycles = board->cpu->cycles;
 77	int32_t nextEvent = INT_MAX;
 78	int32_t testEvent;
 79
 80	if (gbaBoard->p->springIRQ) {
 81		ARMRaiseIRQ(&gbaBoard->p->cpu);
 82		gbaBoard->p->springIRQ = 0;
 83	}
 84
 85	testEvent = GBAVideoProcessEvents(&gbaBoard->p->video, cycles);
 86	if (testEvent < nextEvent) {
 87		nextEvent = testEvent;
 88	}
 89
 90	testEvent = GBAMemoryProcessEvents(&gbaBoard->p->memory, cycles);
 91	if (testEvent < nextEvent) {
 92		nextEvent = testEvent;
 93	}
 94
 95	testEvent = GBATimersProcessEvents(gbaBoard->p, cycles);
 96	if (testEvent < nextEvent) {
 97		nextEvent = testEvent;
 98	}
 99
100	board->cpu->cycles = 0;
101	board->cpu->nextEvent = nextEvent;
102}
103
104static int32_t GBATimersProcessEvents(struct GBA* gba, int32_t cycles) {
105	int32_t nextEvent = INT_MAX;
106	if (gba->timersEnabled) {
107		struct GBATimer* timer;
108		struct GBATimer* nextTimer;
109
110		timer = &gba->timers[0];
111		if (timer->enable) {
112			timer->nextEvent -= cycles;
113			timer->lastEvent -= cycles;
114			if (timer->nextEvent <= 0) {
115				timer->lastEvent = timer->nextEvent;
116				timer->nextEvent += timer->overflowInterval;
117				gba->memory.io[REG_TM0CNT_LO >> 1] = timer->reload;
118				timer->oldReload = timer->reload;
119
120				if (timer->doIrq) {
121					GBARaiseIRQ(gba, IRQ_TIMER0);
122				}
123
124				nextTimer = &gba->timers[1];
125				if (nextTimer->countUp) {
126					++gba->memory.io[REG_TM1CNT_LO >> 1];
127					if (!gba->memory.io[REG_TM1CNT_LO >> 1]) {
128						nextTimer->nextEvent = 0;
129					}
130				}
131			}
132			nextEvent = timer->nextEvent;
133		}
134
135		timer = &gba->timers[1];
136		if (timer->enable) {
137			timer->nextEvent -= cycles;
138			timer->lastEvent -= cycles;
139			if (timer->nextEvent <= 0) {
140				timer->lastEvent = timer->nextEvent;
141				timer->nextEvent += timer->overflowInterval;
142				gba->memory.io[REG_TM1CNT_LO >> 1] = timer->reload;
143				timer->oldReload = timer->reload;
144
145				if (timer->doIrq) {
146					GBARaiseIRQ(gba, IRQ_TIMER1);
147				}
148
149				if (timer->countUp) {
150					timer->nextEvent = INT_MAX;
151				}
152
153				nextTimer = &gba->timers[2];
154				if (nextTimer->countUp) {
155					++gba->memory.io[REG_TM2CNT_LO >> 1];
156					if (!gba->memory.io[REG_TM2CNT_LO >> 1]) {
157						nextTimer->nextEvent = 0;
158					}
159				}
160			}
161			if (timer->nextEvent < nextEvent) {
162				nextEvent = timer->nextEvent;
163			}
164		}
165
166		timer = &gba->timers[2];
167		if (timer->enable) {
168			timer->nextEvent -= cycles;
169			timer->lastEvent -= cycles;
170			nextEvent = timer->nextEvent;
171			if (timer->nextEvent <= 0) {
172				timer->lastEvent = timer->nextEvent;
173				timer->nextEvent += timer->overflowInterval;
174				gba->memory.io[REG_TM2CNT_LO >> 1] = timer->reload;
175				timer->oldReload = timer->reload;
176
177				if (timer->doIrq) {
178					GBARaiseIRQ(gba, IRQ_TIMER2);
179				}
180
181				if (timer->countUp) {
182					timer->nextEvent = INT_MAX;
183				}
184
185				nextTimer = &gba->timers[3];
186				if (nextTimer->countUp) {
187					++gba->memory.io[REG_TM3CNT_LO >> 1];
188					if (!gba->memory.io[REG_TM3CNT_LO >> 1]) {
189						nextTimer->nextEvent = 0;
190					}
191				}
192			}
193			if (timer->nextEvent < nextEvent) {
194				nextEvent = timer->nextEvent;
195			}
196		}
197
198		timer = &gba->timers[3];
199		if (timer->enable) {
200			timer->nextEvent -= cycles;
201			timer->lastEvent -= cycles;
202			nextEvent = timer->nextEvent;
203			if (timer->nextEvent <= 0) {
204				timer->lastEvent = timer->nextEvent;
205				timer->nextEvent += timer->overflowInterval;
206				gba->memory.io[REG_TM3CNT_LO >> 1] = timer->reload;
207				timer->oldReload = timer->reload;
208
209				if (timer->doIrq) {
210					GBARaiseIRQ(gba, IRQ_TIMER3);
211				}
212
213				if (timer->countUp) {
214					timer->nextEvent = INT_MAX;
215				}
216			}
217			if (timer->nextEvent < nextEvent) {
218				nextEvent = timer->nextEvent;
219			}
220		}
221	}
222	return nextEvent;
223}
224
225void GBAAttachDebugger(struct GBA* gba, struct ARMDebugger* debugger) {
226	ARMDebuggerInit(debugger, &gba->cpu);
227	gba->debugger = debugger;
228}
229
230void GBALoadROM(struct GBA* gba, int fd) {
231	gba->memory.rom = mmap(0, SIZE_CART0, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FILE, fd, 0);
232	// TODO: error check
233}
234
235void GBATimerUpdateRegister(struct GBA* gba, int timer) {
236	struct GBATimer* currentTimer = &gba->timers[timer];
237	if (currentTimer->enable && !currentTimer->countUp) {
238		gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->oldReload + ((gba->cpu.cycles - currentTimer->lastEvent) >> currentTimer->prescaleBits);
239	}
240}
241
242void GBATimerWriteTMCNT_LO(struct GBA* gba, int timer, uint16_t reload) {
243	gba->timers[timer].reload = reload;
244}
245
246void GBATimerWriteTMCNT_HI(struct GBA* gba, int timer, uint16_t control) {
247	struct GBATimer* currentTimer = &gba->timers[timer];
248	GBATimerUpdateRegister(gba, timer);
249
250	int oldPrescale = currentTimer->prescaleBits;
251	switch (control & 0x0003) {
252	case 0x0000:
253		currentTimer->prescaleBits = 0;
254		break;
255	case 0x0001:
256		currentTimer->prescaleBits = 6;
257		break;
258	case 0x0002:
259		currentTimer->prescaleBits = 8;
260		break;
261	case 0x0003:
262		currentTimer->prescaleBits = 10;
263		break;
264	}
265	currentTimer->countUp = !!(control & 0x0004);
266	currentTimer->doIrq = !!(control & 0x0040);
267	currentTimer->overflowInterval = (0x10000 - currentTimer->reload) << currentTimer->prescaleBits;
268	int wasEnabled = currentTimer->enable;
269	currentTimer->enable = !!(control & 0x0080);
270	if (!wasEnabled && currentTimer->enable) {
271		if (!currentTimer->countUp) {
272			currentTimer->nextEvent = gba->cpu.cycles + currentTimer->overflowInterval;
273		} else {
274			currentTimer->nextEvent = INT_MAX;
275		}
276		gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->reload;
277		currentTimer->oldReload = currentTimer->reload;
278		gba->timersEnabled |= 1 << timer;
279	} else if (wasEnabled && !currentTimer->enable) {
280		if (!currentTimer->countUp) {
281			gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->oldReload + ((gba->cpu.cycles - currentTimer->lastEvent) >> oldPrescale);
282		}
283		gba->timersEnabled &= ~(1 << timer);
284	} else if (currentTimer->prescaleBits != oldPrescale && !currentTimer->countUp) {
285		// FIXME: this might be before present
286		currentTimer->nextEvent = currentTimer->lastEvent + currentTimer->overflowInterval;
287	}
288
289	if (currentTimer->nextEvent < gba->cpu.nextEvent) {
290		gba->cpu.nextEvent = currentTimer->nextEvent;
291	}
292};
293
294void GBAWriteIE(struct GBA* gba, uint16_t value) {
295	if (value & (1 << IRQ_SIO)) {
296		GBALog(GBA_LOG_STUB, "SIO interrupts not implemented");
297	}
298
299	if (value & (1 << IRQ_KEYPAD)) {
300		GBALog(GBA_LOG_STUB, "Keypad interrupts not implemented");
301	}
302
303	if (value & (1 << IRQ_GAMEPAK)) {
304		GBALog(GBA_LOG_STUB, "Gamepak interrupts not implemented");
305	}
306
307	if (gba->memory.io[REG_IME >> 1] && value & gba->memory.io[REG_IF >> 1]) {
308		ARMRaiseIRQ(&gba->cpu);
309	}
310}
311
312void GBAWriteIME(struct GBA* gba, uint16_t value) {
313	if (value && gba->memory.io[REG_IE >> 1] & gba->memory.io[REG_IF >> 1]) {
314		ARMRaiseIRQ(&gba->cpu);
315	}
316}
317
318void GBARaiseIRQ(struct GBA* gba, enum GBAIRQ irq) {
319	gba->memory.io[REG_IF >> 1] |= 1 << irq;
320
321	if (gba->memory.io[REG_IME >> 1] && (gba->memory.io[REG_IE >> 1] & 1 << irq)) {
322		ARMRaiseIRQ(&gba->cpu);
323	}
324}
325
326int GBATestIRQ(struct GBA* gba) {
327	if (gba->memory.io[REG_IME >> 1] && gba->memory.io[REG_IE >> 1] & gba->memory.io[REG_IF >> 1]) {
328		gba->springIRQ = 1;
329		gba->cpu.nextEvent = gba->cpu.cycles;
330		return 1;
331	}
332	return 0;
333}
334
335int GBAWaitForIRQ(struct GBA* gba) {
336	while (1) {
337		if (gba->cpu.nextEvent == INT_MAX) {
338			return 0;
339		} else {
340			gba->cpu.cycles = gba->cpu.nextEvent;
341			GBAProcessEvents(&gba->board.d);
342			if (gba->memory.io[REG_IF >> 1]) {
343				return 1;
344			}
345		}
346	}
347}
348
349int GBAHalt(struct GBA* gba) {
350	return GBAWaitForIRQ(gba);
351}
352
353void GBALog(int level, const char* format, ...) {
354	(void)(level);
355	va_list args;
356	va_start(args, format);
357	vprintf(format, args);
358	va_end(args);
359	printf("\n");
360}
361
362void GBAHitStub(struct ARMBoard* board, uint32_t opcode) {
363	GBALog(GBA_LOG_STUB, "Stub opcode: %08x", opcode);
364	struct GBABoard* gbaBoard = (struct GBABoard*) board;
365	if (!gbaBoard->p->debugger) {
366		abort();
367	} else {
368		ARMDebuggerEnter(gbaBoard->p->debugger);
369	}
370}