all repos — mgba @ 58611facf1366bbc0af5fe58c183efb816c41ed9

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