all repos — mgba @ 8bc5ec422285bb84b3d133830f4d5e0ecc5742bd

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