all repos — mgba @ a7357df857cf775d339fbc24fd56de3f1be4fba2

mGBA Game Boy Advance Emulator

src/gba/gba.c (view raw)

  1/* Copyright (c) 2013-2014 Jeffrey Pfau
  2 *
  3 * This Source Code Form is subject to the terms of the Mozilla Public
  4 * License, v. 2.0. If a copy of the MPL was not distributed with this
  5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  6#include "gba.h"
  7
  8#include "gba-bios.h"
  9#include "gba-io.h"
 10#include "gba-rr.h"
 11#include "gba-sio.h"
 12#include "gba-thread.h"
 13
 14#include "isa-inlines.h"
 15
 16#include "util/crc32.h"
 17#include "util/memory.h"
 18#include "util/patch.h"
 19#include "util/vfs.h"
 20
 21const uint32_t GBA_ARM7TDMI_FREQUENCY = 0x1000000;
 22const uint32_t GBA_COMPONENT_MAGIC = 0x1000000;
 23
 24static const size_t GBA_ROM_MAGIC_OFFSET = 3;
 25static const uint8_t GBA_ROM_MAGIC[] = { 0xEA };
 26
 27static void GBAInit(struct ARMCore* cpu, struct ARMComponent* component);
 28static void GBAInterruptHandlerInit(struct ARMInterruptHandler* irqh);
 29static void GBAProcessEvents(struct ARMCore* cpu);
 30static int32_t GBATimersProcessEvents(struct GBA* gba, int32_t cycles);
 31static void GBAHitStub(struct ARMCore* cpu, uint32_t opcode);
 32static void GBAIllegal(struct ARMCore* cpu, uint32_t opcode);
 33
 34void GBACreate(struct GBA* gba) {
 35	gba->d.id = GBA_COMPONENT_MAGIC;
 36	gba->d.init = GBAInit;
 37	gba->d.deinit = 0;
 38}
 39
 40static void GBAInit(struct ARMCore* cpu, struct ARMComponent* component) {
 41	struct GBA* gba = (struct GBA*) component;
 42	gba->cpu = cpu;
 43	gba->debugger = 0;
 44
 45	GBAInterruptHandlerInit(&cpu->irqh);
 46	GBAMemoryInit(gba);
 47	GBASavedataInit(&gba->memory.savedata, 0);
 48
 49	gba->video.p = gba;
 50	GBAVideoInit(&gba->video);
 51
 52	gba->audio.p = gba;
 53	GBAAudioInit(&gba->audio, GBA_AUDIO_SAMPLES);
 54
 55	GBAIOInit(gba);
 56
 57	gba->sio.p = gba;
 58	GBASIOInit(&gba->sio);
 59
 60	gba->timersEnabled = 0;
 61	memset(gba->timers, 0, sizeof(gba->timers));
 62
 63	gba->springIRQ = 0;
 64	gba->keySource = 0;
 65	gba->rotationSource = 0;
 66	gba->luminanceSource = 0;
 67	gba->rtcSource = 0;
 68	gba->rumble = 0;
 69	gba->rr = 0;
 70
 71	gba->romVf = 0;
 72	gba->biosVf = 0;
 73
 74	gba->logLevel = GBA_LOG_INFO | GBA_LOG_WARN | GBA_LOG_ERROR | GBA_LOG_FATAL;
 75
 76	gba->biosChecksum = GBAChecksum(gba->memory.bios, SIZE_BIOS);
 77
 78	gba->busyLoop = -1;
 79	gba->performingDMA = false;
 80}
 81
 82void GBADestroy(struct GBA* gba) {
 83	if (gba->pristineRom == gba->memory.rom) {
 84		gba->memory.rom = 0;
 85	}
 86
 87	if (gba->romVf) {
 88		gba->romVf->unmap(gba->romVf, gba->pristineRom, gba->pristineRomSize);
 89	}
 90
 91	if (gba->biosVf) {
 92		gba->biosVf->unmap(gba->biosVf, gba->memory.bios, SIZE_BIOS);
 93	}
 94
 95	GBAMemoryDeinit(gba);
 96	GBAVideoDeinit(&gba->video);
 97	GBAAudioDeinit(&gba->audio);
 98	GBARRContextDestroy(gba);
 99}
100
101void GBAInterruptHandlerInit(struct ARMInterruptHandler* irqh) {
102	irqh->reset = GBAReset;
103	irqh->processEvents = GBAProcessEvents;
104	irqh->swi16 = GBASwi16;
105	irqh->swi32 = GBASwi32;
106	irqh->hitIllegal = GBAIllegal;
107	irqh->readCPSR = GBATestIRQ;
108	irqh->hitStub = GBAHitStub;
109}
110
111void GBAReset(struct ARMCore* cpu) {
112	ARMSetPrivilegeMode(cpu, MODE_IRQ);
113	cpu->gprs[ARM_SP] = SP_BASE_IRQ;
114	ARMSetPrivilegeMode(cpu, MODE_SUPERVISOR);
115	cpu->gprs[ARM_SP] = SP_BASE_SUPERVISOR;
116	ARMSetPrivilegeMode(cpu, MODE_SYSTEM);
117	cpu->gprs[ARM_SP] = SP_BASE_SYSTEM;
118
119	struct GBA* gba = (struct GBA*) cpu->master;
120	if (!GBARRIsPlaying(gba->rr) && !GBARRIsRecording(gba->rr)) {
121		GBASavedataUnmask(&gba->memory.savedata);
122	}
123	GBAMemoryReset(gba);
124	GBAVideoReset(&gba->video);
125	GBAAudioReset(&gba->audio);
126	GBAIOInit(gba);
127
128	GBASIODeinit(&gba->sio);
129	GBASIOInit(&gba->sio);
130
131	gba->timersEnabled = 0;
132	memset(gba->timers, 0, sizeof(gba->timers));
133}
134
135void GBASkipBIOS(struct ARMCore* cpu) {
136	if (cpu->gprs[ARM_PC] == BASE_RESET + WORD_SIZE_ARM) {
137		cpu->gprs[ARM_PC] = BASE_CART0;
138		int currentCycles = 0;
139		ARM_WRITE_PC;
140	}
141}
142
143static void GBAProcessEvents(struct ARMCore* cpu) {
144	do {
145		struct GBA* gba = (struct GBA*) cpu->master;
146		int32_t cycles = cpu->nextEvent;
147		int32_t nextEvent = INT_MAX;
148		int32_t testEvent;
149
150		gba->bus = cpu->prefetch[1];
151		if (cpu->executionMode == MODE_THUMB) {
152			gba->bus |= cpu->prefetch[1] << 16;
153		}
154
155		if (gba->springIRQ) {
156			ARMRaiseIRQ(cpu);
157			gba->springIRQ = 0;
158		}
159
160		testEvent = GBAVideoProcessEvents(&gba->video, cycles);
161		if (testEvent < nextEvent) {
162			nextEvent = testEvent;
163		}
164
165		testEvent = GBAAudioProcessEvents(&gba->audio, cycles);
166		if (testEvent < nextEvent) {
167			nextEvent = testEvent;
168		}
169
170		testEvent = GBATimersProcessEvents(gba, cycles);
171		if (testEvent < nextEvent) {
172			nextEvent = testEvent;
173		}
174
175		testEvent = GBAMemoryRunDMAs(gba, cycles);
176		if (testEvent < nextEvent) {
177			nextEvent = testEvent;
178		}
179
180		testEvent = GBASIOProcessEvents(&gba->sio, cycles);
181		if (testEvent < nextEvent) {
182			nextEvent = testEvent;
183		}
184
185		cpu->cycles -= cycles;
186		cpu->nextEvent = nextEvent;
187
188		if (cpu->halted) {
189			cpu->cycles = cpu->nextEvent;
190		}
191	} while (cpu->cycles >= cpu->nextEvent);
192}
193
194static int32_t GBATimersProcessEvents(struct GBA* gba, int32_t cycles) {
195	int32_t nextEvent = INT_MAX;
196	if (gba->timersEnabled) {
197		struct GBATimer* timer;
198		struct GBATimer* nextTimer;
199
200		timer = &gba->timers[0];
201		if (timer->enable) {
202			timer->nextEvent -= cycles;
203			timer->lastEvent -= cycles;
204			if (timer->nextEvent <= 0) {
205				timer->lastEvent = timer->nextEvent;
206				timer->nextEvent += timer->overflowInterval;
207				gba->memory.io[REG_TM0CNT_LO >> 1] = timer->reload;
208				timer->oldReload = timer->reload;
209
210				if (timer->doIrq) {
211					GBARaiseIRQ(gba, IRQ_TIMER0);
212				}
213
214				if (gba->audio.enable) {
215					if ((gba->audio.chALeft || gba->audio.chARight) && gba->audio.chATimer == 0) {
216						GBAAudioSampleFIFO(&gba->audio, 0, timer->lastEvent);
217					}
218
219					if ((gba->audio.chBLeft || gba->audio.chBRight) && gba->audio.chBTimer == 0) {
220						GBAAudioSampleFIFO(&gba->audio, 1, timer->lastEvent);
221					}
222				}
223
224				nextTimer = &gba->timers[1];
225				if (nextTimer->countUp) {
226					++gba->memory.io[REG_TM1CNT_LO >> 1];
227					if (!gba->memory.io[REG_TM1CNT_LO >> 1]) {
228						nextTimer->nextEvent = 0;
229					}
230				}
231			}
232			nextEvent = timer->nextEvent;
233		}
234
235		timer = &gba->timers[1];
236		if (timer->enable) {
237			timer->nextEvent -= cycles;
238			timer->lastEvent -= cycles;
239			if (timer->nextEvent <= 0) {
240				timer->lastEvent = timer->nextEvent;
241				timer->nextEvent += timer->overflowInterval;
242				gba->memory.io[REG_TM1CNT_LO >> 1] = timer->reload;
243				timer->oldReload = timer->reload;
244
245				if (timer->doIrq) {
246					GBARaiseIRQ(gba, IRQ_TIMER1);
247				}
248
249				if (gba->audio.enable) {
250					if ((gba->audio.chALeft || gba->audio.chARight) && gba->audio.chATimer == 1) {
251						GBAAudioSampleFIFO(&gba->audio, 0, timer->lastEvent);
252					}
253
254					if ((gba->audio.chBLeft || gba->audio.chBRight) && gba->audio.chBTimer == 1) {
255						GBAAudioSampleFIFO(&gba->audio, 1, timer->lastEvent);
256					}
257				}
258
259				if (timer->countUp) {
260					timer->nextEvent = INT_MAX;
261				}
262
263				nextTimer = &gba->timers[2];
264				if (nextTimer->countUp) {
265					++gba->memory.io[REG_TM2CNT_LO >> 1];
266					if (!gba->memory.io[REG_TM2CNT_LO >> 1]) {
267						nextTimer->nextEvent = 0;
268					}
269				}
270			}
271			if (timer->nextEvent < nextEvent) {
272				nextEvent = timer->nextEvent;
273			}
274		}
275
276		timer = &gba->timers[2];
277		if (timer->enable) {
278			timer->nextEvent -= cycles;
279			timer->lastEvent -= cycles;
280			if (timer->nextEvent <= 0) {
281				timer->lastEvent = timer->nextEvent;
282				timer->nextEvent += timer->overflowInterval;
283				gba->memory.io[REG_TM2CNT_LO >> 1] = timer->reload;
284				timer->oldReload = timer->reload;
285
286				if (timer->doIrq) {
287					GBARaiseIRQ(gba, IRQ_TIMER2);
288				}
289
290				if (timer->countUp) {
291					timer->nextEvent = INT_MAX;
292				}
293
294				nextTimer = &gba->timers[3];
295				if (nextTimer->countUp) {
296					++gba->memory.io[REG_TM3CNT_LO >> 1];
297					if (!gba->memory.io[REG_TM3CNT_LO >> 1]) {
298						nextTimer->nextEvent = 0;
299					}
300				}
301			}
302			if (timer->nextEvent < nextEvent) {
303				nextEvent = timer->nextEvent;
304			}
305		}
306
307		timer = &gba->timers[3];
308		if (timer->enable) {
309			timer->nextEvent -= cycles;
310			timer->lastEvent -= cycles;
311			if (timer->nextEvent <= 0) {
312				timer->lastEvent = timer->nextEvent;
313				timer->nextEvent += timer->overflowInterval;
314				gba->memory.io[REG_TM3CNT_LO >> 1] = timer->reload;
315				timer->oldReload = timer->reload;
316
317				if (timer->doIrq) {
318					GBARaiseIRQ(gba, IRQ_TIMER3);
319				}
320
321				if (timer->countUp) {
322					timer->nextEvent = INT_MAX;
323				}
324			}
325			if (timer->nextEvent < nextEvent) {
326				nextEvent = timer->nextEvent;
327			}
328		}
329	}
330	return nextEvent;
331}
332
333void GBAAttachDebugger(struct GBA* gba, struct ARMDebugger* debugger) {
334	gba->debugger = debugger;
335	gba->cpu->components[GBA_COMPONENT_DEBUGGER] = &debugger->d;
336	ARMHotplugAttach(gba->cpu, GBA_COMPONENT_DEBUGGER);
337}
338
339void GBADetachDebugger(struct GBA* gba) {
340	gba->debugger = 0;
341	ARMHotplugDetach(gba->cpu, GBA_COMPONENT_DEBUGGER);
342	gba->cpu->components[GBA_COMPONENT_DEBUGGER] = 0;
343}
344
345void GBALoadROM(struct GBA* gba, struct VFile* vf, struct VFile* sav, const char* fname) {
346	gba->romVf = vf;
347	gba->pristineRomSize = vf->size(vf);
348	vf->seek(vf, 0, SEEK_SET);
349	if (gba->pristineRomSize > SIZE_CART0) {
350		gba->pristineRomSize = SIZE_CART0;
351	}
352	gba->pristineRom = vf->map(vf, gba->pristineRomSize, MAP_READ);
353	if (!gba->pristineRom) {
354		GBALog(gba, GBA_LOG_WARN, "Couldn't map ROM");
355		return;
356	}
357	gba->memory.rom = gba->pristineRom;
358	gba->activeFile = fname;
359	gba->memory.romSize = gba->pristineRomSize;
360	gba->romCrc32 = doCrc32(gba->memory.rom, gba->memory.romSize);
361	GBASavedataInit(&gba->memory.savedata, sav);
362	GBAGPIOInit(&gba->memory.gpio, &((uint16_t*) gba->memory.rom)[GPIO_REG_DATA >> 1]);
363	// TODO: error check
364}
365
366void GBALoadBIOS(struct GBA* gba, struct VFile* vf) {
367	gba->biosVf = vf;
368	uint32_t* bios = vf->map(vf, SIZE_BIOS, MAP_READ);
369	if (!bios) {
370		GBALog(gba, GBA_LOG_WARN, "Couldn't map BIOS");
371		return;
372	}
373	gba->memory.bios = bios;
374	gba->memory.fullBios = 1;
375	uint32_t checksum = GBAChecksum(gba->memory.bios, SIZE_BIOS);
376	GBALog(gba, GBA_LOG_DEBUG, "BIOS Checksum: 0x%X", checksum);
377	if (checksum == GBA_BIOS_CHECKSUM) {
378		GBALog(gba, GBA_LOG_INFO, "Official GBA BIOS detected");
379	} else if (checksum == GBA_DS_BIOS_CHECKSUM) {
380		GBALog(gba, GBA_LOG_INFO, "Official GBA (DS) BIOS detected");
381	} else {
382		GBALog(gba, GBA_LOG_WARN, "BIOS checksum incorrect");
383	}
384	gba->biosChecksum = checksum;
385	if (gba->memory.activeRegion == REGION_BIOS) {
386		gba->cpu->memory.activeRegion = gba->memory.bios;
387	}
388	// TODO: error check
389}
390
391void GBAApplyPatch(struct GBA* gba, struct Patch* patch) {
392	size_t patchedSize = patch->outputSize(patch, gba->memory.romSize);
393	if (!patchedSize) {
394		return;
395	}
396	gba->memory.rom = anonymousMemoryMap(patchedSize);
397	if (!patch->applyPatch(patch, gba->pristineRom, gba->pristineRomSize, gba->memory.rom, patchedSize)) {
398		mappedMemoryFree(gba->memory.rom, patchedSize);
399		gba->memory.rom = gba->pristineRom;
400		return;
401	}
402	gba->memory.romSize = patchedSize;
403	gba->romCrc32 = doCrc32(gba->memory.rom, gba->memory.romSize);
404}
405
406void GBATimerUpdateRegister(struct GBA* gba, int timer) {
407	struct GBATimer* currentTimer = &gba->timers[timer];
408	if (currentTimer->enable && !currentTimer->countUp) {
409		gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->oldReload + ((gba->cpu->cycles - currentTimer->lastEvent) >> currentTimer->prescaleBits);
410	}
411}
412
413void GBATimerWriteTMCNT_LO(struct GBA* gba, int timer, uint16_t reload) {
414	gba->timers[timer].reload = reload;
415}
416
417void GBATimerWriteTMCNT_HI(struct GBA* gba, int timer, uint16_t control) {
418	struct GBATimer* currentTimer = &gba->timers[timer];
419	GBATimerUpdateRegister(gba, timer);
420
421	int oldPrescale = currentTimer->prescaleBits;
422	switch (control & 0x0003) {
423	case 0x0000:
424		currentTimer->prescaleBits = 0;
425		break;
426	case 0x0001:
427		currentTimer->prescaleBits = 6;
428		break;
429	case 0x0002:
430		currentTimer->prescaleBits = 8;
431		break;
432	case 0x0003:
433		currentTimer->prescaleBits = 10;
434		break;
435	}
436	currentTimer->countUp = !!(control & 0x0004);
437	currentTimer->doIrq = !!(control & 0x0040);
438	currentTimer->overflowInterval = (0x10000 - currentTimer->reload) << currentTimer->prescaleBits;
439	int wasEnabled = currentTimer->enable;
440	currentTimer->enable = !!(control & 0x0080);
441	if (!wasEnabled && currentTimer->enable) {
442		if (!currentTimer->countUp) {
443			currentTimer->nextEvent = gba->cpu->cycles + currentTimer->overflowInterval;
444		} else {
445			currentTimer->nextEvent = INT_MAX;
446		}
447		gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->reload;
448		currentTimer->oldReload = currentTimer->reload;
449		currentTimer->lastEvent = 0;
450		gba->timersEnabled |= 1 << timer;
451	} else if (wasEnabled && !currentTimer->enable) {
452		if (!currentTimer->countUp) {
453			gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->oldReload + ((gba->cpu->cycles - currentTimer->lastEvent) >> oldPrescale);
454		}
455		gba->timersEnabled &= ~(1 << timer);
456	} else if (currentTimer->prescaleBits != oldPrescale && !currentTimer->countUp) {
457		// FIXME: this might be before present
458		currentTimer->nextEvent = currentTimer->lastEvent + currentTimer->overflowInterval;
459	}
460
461	if (currentTimer->nextEvent < gba->cpu->nextEvent) {
462		gba->cpu->nextEvent = currentTimer->nextEvent;
463	}
464};
465
466void GBAWriteIE(struct GBA* gba, uint16_t value) {
467	if (value & (1 << IRQ_KEYPAD)) {
468		GBALog(gba, GBA_LOG_STUB, "Keypad interrupts not implemented");
469	}
470
471	if (value & (1 << IRQ_GAMEPAK)) {
472		GBALog(gba, GBA_LOG_STUB, "Gamepak interrupts not implemented");
473	}
474
475	if (gba->memory.io[REG_IME >> 1] && value & gba->memory.io[REG_IF >> 1]) {
476		ARMRaiseIRQ(gba->cpu);
477	}
478}
479
480void GBAWriteIME(struct GBA* gba, uint16_t value) {
481	if (value && gba->memory.io[REG_IE >> 1] & gba->memory.io[REG_IF >> 1]) {
482		ARMRaiseIRQ(gba->cpu);
483	}
484}
485
486void GBARaiseIRQ(struct GBA* gba, enum GBAIRQ irq) {
487	gba->memory.io[REG_IF >> 1] |= 1 << irq;
488	gba->cpu->halted = 0;
489
490	if (gba->memory.io[REG_IME >> 1] && (gba->memory.io[REG_IE >> 1] & 1 << irq)) {
491		ARMRaiseIRQ(gba->cpu);
492	}
493}
494
495void GBATestIRQ(struct ARMCore* cpu) {
496	struct GBA* gba = (struct GBA*) cpu->master;
497	if (gba->memory.io[REG_IME >> 1] && gba->memory.io[REG_IE >> 1] & gba->memory.io[REG_IF >> 1]) {
498		gba->springIRQ = 1;
499		gba->cpu->nextEvent = 0;
500	}
501}
502
503void GBAHalt(struct GBA* gba) {
504	gba->cpu->nextEvent = 0;
505	gba->cpu->halted = 1;
506}
507
508static void _GBAVLog(struct GBA* gba, enum GBALogLevel level, const char* format, va_list args) {
509	struct GBAThread* threadContext = GBAThreadGetContext();
510	enum GBALogLevel logLevel = -1;
511
512	if (gba) {
513		logLevel = gba->logLevel;
514	}
515
516	if (threadContext) {
517		logLevel = threadContext->logLevel;
518		gba = threadContext->gba;
519	}
520
521	if (!(level & logLevel) && level != GBA_LOG_FATAL) {
522		return;
523	}
524
525	if (level == GBA_LOG_FATAL && gba) {
526		gba->cpu->nextEvent = 0;
527	}
528
529	if (threadContext) {
530		if (level == GBA_LOG_FATAL) {
531			MutexLock(&threadContext->stateMutex);
532			threadContext->state = THREAD_CRASHED;
533			MutexUnlock(&threadContext->stateMutex);
534		}
535		if (threadContext->logHandler) {
536			threadContext->logHandler(threadContext, level, format, args);
537			return;
538		}
539	}
540
541	vprintf(format, args);
542	printf("\n");
543
544	if (level == GBA_LOG_FATAL && !threadContext) {
545		abort();
546	}
547}
548
549void GBALog(struct GBA* gba, enum GBALogLevel level, const char* format, ...) {
550	va_list args;
551	va_start(args, format);
552	_GBAVLog(gba, level, format, args);
553	va_end(args);
554}
555
556void GBADebuggerLogShim(struct ARMDebugger* debugger, enum DebuggerLogLevel level, const char* format, ...) {
557	struct GBA* gba = 0;
558	if (debugger->cpu) {
559		gba = (struct GBA*) debugger->cpu->master;
560	}
561
562	enum GBALogLevel gbaLevel;
563	switch (level) {
564	default: // Avoids compiler warning
565	case DEBUGGER_LOG_DEBUG:
566		gbaLevel = GBA_LOG_DEBUG;
567		break;
568	case DEBUGGER_LOG_INFO:
569		gbaLevel = GBA_LOG_INFO;
570		break;
571	case DEBUGGER_LOG_WARN:
572		gbaLevel = GBA_LOG_WARN;
573		break;
574	case DEBUGGER_LOG_ERROR:
575		gbaLevel = GBA_LOG_ERROR;
576		break;
577	}
578	va_list args;
579	va_start(args, format);
580	_GBAVLog(gba, gbaLevel, format, args);
581	va_end(args);
582}
583
584bool GBAIsROM(struct VFile* vf) {
585	if (vf->seek(vf, GBA_ROM_MAGIC_OFFSET, SEEK_SET) < 0) {
586		return false;
587	}
588	uint8_t signature[sizeof(GBA_ROM_MAGIC)];
589	if (vf->read(vf, &signature, sizeof(signature)) != sizeof(signature)) {
590		return false;
591	}
592	return memcmp(signature, GBA_ROM_MAGIC, sizeof(signature)) == 0;
593}
594
595bool GBAIsBIOS(struct VFile* vf) {
596	if (vf->seek(vf, 0, SEEK_SET) < 0) {
597		return false;
598	}
599	uint32_t interruptTable[7];
600	if (vf->read(vf, &interruptTable, sizeof(interruptTable)) != sizeof(interruptTable)) {
601		return false;
602	}
603	int i;
604	for (i = 0; i < 7; ++i) {
605		if ((interruptTable[i] & 0xFFFF0000) != 0xEA000000) {
606			return false;
607		}
608	}
609	return true;
610}
611
612void GBAGetGameCode(struct GBA* gba, char* out) {
613	memcpy(out, &((struct GBACartridge*) gba->memory.rom)->id, 4);
614}
615
616void GBAGetGameTitle(struct GBA* gba, char* out) {
617	memcpy(out, &((struct GBACartridge*) gba->memory.rom)->title, 12);
618}
619
620void GBAHitStub(struct ARMCore* cpu, uint32_t opcode) {
621	struct GBA* gba = (struct GBA*) cpu->master;
622	enum GBALogLevel level = GBA_LOG_FATAL;
623	if (gba->debugger) {
624		level = GBA_LOG_STUB;
625		ARMDebuggerEnter(gba->debugger, DEBUGGER_ENTER_ILLEGAL_OP);
626	}
627	GBALog(gba, level, "Stub opcode: %08x", opcode);
628}
629
630void GBAIllegal(struct ARMCore* cpu, uint32_t opcode) {
631	struct GBA* gba = (struct GBA*) cpu->master;
632	GBALog(gba, GBA_LOG_WARN, "Illegal opcode: %08x", opcode);
633	if (gba->debugger) {
634		ARMDebuggerEnter(gba->debugger, DEBUGGER_ENTER_ILLEGAL_OP);
635	}
636}