all repos — mgba @ 85285a68e51d3d8b4030c8a45db8f4787beaec93

mGBA Game Boy Advance Emulator

src/gb/gb.c (view raw)

  1/* Copyright (c) 2013-2016 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 "gb.h"
  7
  8#include "gb/io.h"
  9#include "gb/mbc.h"
 10
 11#include "core/core.h"
 12#include "core/cheats.h"
 13#include "util/crc32.h"
 14#include "util/memory.h"
 15#include "util/math.h"
 16#include "util/patch.h"
 17#include "util/vfs.h"
 18
 19#define CLEANUP_THRESHOLD 15
 20
 21const uint32_t CGB_LR35902_FREQUENCY = 0x800000;
 22const uint32_t SGB_LR35902_FREQUENCY = 0x418B1E;
 23
 24const uint32_t GB_COMPONENT_MAGIC = 0x400000;
 25
 26static const uint8_t _knownHeader[4] = { 0xCE, 0xED, 0x66, 0x66};
 27
 28#define DMG_BIOS_CHECKSUM 0xC2F5CC97
 29#define DMG_2_BIOS_CHECKSUM 0x59C8598E
 30#define CGB_BIOS_CHECKSUM 0x41884E46
 31
 32mLOG_DEFINE_CATEGORY(GB, "GB");
 33
 34static void GBInit(void* cpu, struct mCPUComponent* component);
 35static void GBInterruptHandlerInit(struct LR35902InterruptHandler* irqh);
 36static void GBProcessEvents(struct LR35902Core* cpu);
 37static void GBSetInterrupts(struct LR35902Core* cpu, bool enable);
 38static void GBIllegal(struct LR35902Core* cpu);
 39static void GBStop(struct LR35902Core* cpu);
 40
 41#ifdef _3DS
 42extern uint32_t* romBuffer;
 43extern size_t romBufferSize;
 44#endif
 45
 46void GBCreate(struct GB* gb) {
 47	gb->d.id = GB_COMPONENT_MAGIC;
 48	gb->d.init = GBInit;
 49	gb->d.deinit = 0;
 50}
 51
 52static void GBInit(void* cpu, struct mCPUComponent* component) {
 53	struct GB* gb = (struct GB*) component;
 54	gb->cpu = cpu;
 55	gb->sync = NULL;
 56
 57	GBInterruptHandlerInit(&gb->cpu->irqh);
 58	GBMemoryInit(gb);
 59
 60	gb->video.p = gb;
 61	GBVideoInit(&gb->video);
 62
 63	gb->audio.p = gb;
 64	GBAudioInit(&gb->audio, 2048, &gb->memory.io[REG_NR52], GB_AUDIO_DMG); // TODO: Remove magic constant
 65
 66	gb->sio.p = gb;
 67	GBSIOInit(&gb->sio);
 68
 69	gb->timer.p = gb;
 70
 71	gb->model = GB_MODEL_AUTODETECT;
 72
 73	gb->biosVf = NULL;
 74	gb->romVf = NULL;
 75	gb->sramVf = NULL;
 76	gb->sramRealVf = NULL;
 77
 78	gb->pristineRom = 0;
 79	gb->pristineRomSize = 0;
 80	gb->yankedRomSize = 0;
 81
 82	gb->coreCallbacks = NULL;
 83	gb->stream = NULL;
 84}
 85
 86bool GBLoadROM(struct GB* gb, struct VFile* vf) {
 87	if (!vf) {
 88		return false;
 89	}
 90	GBUnloadROM(gb);
 91	gb->romVf = vf;
 92	gb->pristineRomSize = vf->size(vf);
 93	vf->seek(vf, 0, SEEK_SET);
 94#ifdef _3DS
 95	gb->pristineRom = 0;
 96	if (gb->pristineRomSize <= romBufferSize) {
 97		gb->pristineRom = romBuffer;
 98		vf->read(vf, romBuffer, gb->pristineRomSize);
 99	}
100#else
101	gb->pristineRom = vf->map(vf, gb->pristineRomSize, MAP_READ);
102#endif
103	if (!gb->pristineRom) {
104		return false;
105	}
106	gb->yankedRomSize = 0;
107	gb->memory.rom = gb->pristineRom;
108	gb->memory.romBase = gb->memory.rom;
109	gb->memory.romSize = gb->pristineRomSize;
110	gb->romCrc32 = doCrc32(gb->memory.rom, gb->memory.romSize);
111	GBMBCSwitchBank(&gb->memory, gb->memory.currentBank);
112
113	if (gb->cpu) {
114		struct LR35902Core* cpu = gb->cpu;
115		cpu->memory.setActiveRegion(cpu, cpu->pc);
116	}
117
118	// TODO: error check
119	return true;
120}
121
122bool GBLoadSave(struct GB* gb, struct VFile* vf) {
123	gb->sramVf = vf;
124	gb->sramRealVf = vf;
125	return vf;
126}
127
128static void GBSramDeinit(struct GB* gb) {
129	if (gb->sramVf) {
130		gb->sramVf->unmap(gb->sramVf, gb->memory.sram, gb->sramSize);
131		if (gb->memory.mbcType == GB_MBC3_RTC && gb->sramVf == gb->sramRealVf) {
132			GBMBCRTCWrite(gb);
133		}
134		gb->sramVf = NULL;
135	} else if (gb->memory.sram) {
136		mappedMemoryFree(gb->memory.sram, gb->sramSize);
137	}
138	gb->memory.sram = 0;
139}
140
141void GBResizeSram(struct GB* gb, size_t size) {
142	if (gb->memory.sram && size <= gb->sramSize) {
143		return;
144	}
145	mLOG(GB, INFO, "Resizing SRAM to %"PRIz"u bytes", size);
146	struct VFile* vf = gb->sramVf;
147	if (vf) {
148		if (vf == gb->sramRealVf) {
149			ssize_t vfSize = vf->size(vf);
150			if (vfSize >= 0 && (size_t) vfSize < size) {
151				uint8_t extdataBuffer[0x100];
152				if (vfSize & 0xFF) {
153					vf->seek(vf, -(vfSize & 0xFF), SEEK_END);
154					vf->read(vf, extdataBuffer, vfSize & 0xFF);
155				}
156				if (gb->memory.sram) {
157					vf->unmap(vf, gb->memory.sram, gb->sramSize);
158				}
159				vf->truncate(vf, size + (vfSize & 0xFF));
160				if (vfSize & 0xFF) {
161					vf->seek(vf, size, SEEK_SET);
162					vf->write(vf, extdataBuffer, vfSize & 0xFF);
163				}
164				gb->memory.sram = vf->map(vf, size, MAP_WRITE);
165				memset(&gb->memory.sram[gb->sramSize], 0xFF, size - gb->sramSize);
166			} else if (size > gb->sramSize || !gb->memory.sram) {
167				if (gb->memory.sram) {
168					vf->unmap(vf, gb->memory.sram, gb->sramSize);
169				}
170				gb->memory.sram = vf->map(vf, size, MAP_WRITE);
171			}
172		} else {
173			if (gb->memory.sram) {
174				vf->unmap(vf, gb->memory.sram, gb->sramSize);
175			}
176			gb->memory.sram = vf->map(vf, size, MAP_READ);
177		}
178		if (gb->memory.sram == (void*) -1) {
179			gb->memory.sram = NULL;
180		}
181	} else {
182		uint8_t* newSram = anonymousMemoryMap(size);
183		if (gb->memory.sram) {
184			if (size > gb->sramSize) {
185				memcpy(newSram, gb->memory.sram, gb->sramSize);
186				memset(&newSram[gb->sramSize], 0xFF, size - gb->sramSize);
187			} else {
188				memcpy(newSram, gb->memory.sram, size);
189			}
190			mappedMemoryFree(gb->memory.sram, gb->sramSize);
191		} else {
192			memset(newSram, 0xFF, size);
193		}
194		gb->memory.sram = newSram;
195	}
196	if (gb->sramSize < size) {
197		gb->sramSize = size;
198	}
199}
200
201void GBSramClean(struct GB* gb, uint32_t frameCount) {
202	// TODO: Share with GBASavedataClean
203	if (!gb->sramVf || gb->sramVf != gb->sramRealVf) {
204		return;
205	}
206	if (gb->sramDirty & GB_SRAM_DIRT_NEW) {
207		gb->sramDirtAge = frameCount;
208		gb->sramDirty &= ~GB_SRAM_DIRT_NEW;
209		if (!(gb->sramDirty & GB_SRAM_DIRT_SEEN)) {
210			gb->sramDirty |= GB_SRAM_DIRT_SEEN;
211		}
212	} else if ((gb->sramDirty & GB_SRAM_DIRT_SEEN) && frameCount - gb->sramDirtAge > CLEANUP_THRESHOLD) {
213		if (gb->memory.mbcType == GB_MBC3_RTC) {
214			GBMBCRTCWrite(gb);
215		}
216		gb->sramDirty = 0;
217		if (gb->memory.sram && gb->sramVf->sync(gb->sramVf, gb->memory.sram, gb->sramSize)) {
218			mLOG(GB_MEM, INFO, "Savedata synced");
219		} else {
220			mLOG(GB_MEM, INFO, "Savedata failed to sync!");
221		}
222	}
223}
224
225void GBSavedataMask(struct GB* gb, struct VFile* vf, bool writeback) {
226	GBSramDeinit(gb);
227	gb->sramVf = vf;
228	gb->sramMaskWriteback = writeback;
229	gb->memory.sram = vf->map(vf, gb->sramSize, MAP_READ);
230	GBMBCSwitchSramBank(gb, gb->memory.sramCurrentBank);
231}
232
233void GBSavedataUnmask(struct GB* gb) {
234	if (gb->sramVf == gb->sramRealVf) {
235		return;
236	}
237	struct VFile* vf = gb->sramVf;
238	GBSramDeinit(gb);
239	gb->sramVf = gb->sramRealVf;
240	gb->memory.sram = gb->sramVf->map(gb->sramVf, gb->sramSize, MAP_WRITE);
241	if (gb->sramMaskWriteback) {
242		vf->read(vf, gb->memory.sram, gb->sramSize);
243	}
244	vf->close(vf);
245}
246
247void GBUnloadROM(struct GB* gb) {
248	// TODO: Share with GBAUnloadROM
249	if (gb->memory.rom && gb->memory.romBase != gb->memory.rom && gb->memory.romBase != gb->pristineRom) {
250		free(gb->memory.romBase);
251	}
252	if (gb->memory.rom && gb->pristineRom != gb->memory.rom) {
253		if (gb->yankedRomSize) {
254			gb->yankedRomSize = 0;
255		}
256		mappedMemoryFree(gb->memory.rom, GB_SIZE_CART_MAX);
257		gb->memory.rom = gb->pristineRom;
258	}
259	gb->memory.rom = 0;
260
261	if (gb->romVf) {
262#ifndef _3DS
263		gb->romVf->unmap(gb->romVf, gb->pristineRom, gb->pristineRomSize);
264#endif
265		gb->romVf->close(gb->romVf);
266		gb->romVf = 0;
267	}
268	gb->pristineRom = 0;
269
270	GBSavedataUnmask(gb);
271	GBSramDeinit(gb);
272	if (gb->sramRealVf) {
273		gb->sramRealVf->close(gb->sramRealVf);
274	}
275	gb->sramRealVf = NULL;
276	gb->sramVf = NULL;
277}
278
279void GBSynthesizeROM(struct VFile* vf) {
280	if (!vf) {
281		return;
282	}
283	const struct GBCartridge cart = {
284		.logo = { _knownHeader[0], _knownHeader[1], _knownHeader[2], _knownHeader[3]}
285	};
286
287	vf->seek(vf, 0x100, SEEK_SET);
288	vf->write(vf, &cart, sizeof(cart));
289}
290
291void GBLoadBIOS(struct GB* gb, struct VFile* vf) {
292	gb->biosVf = vf;
293}
294
295void GBApplyPatch(struct GB* gb, struct Patch* patch) {
296	size_t patchedSize = patch->outputSize(patch, gb->memory.romSize);
297	if (!patchedSize) {
298		return;
299	}
300	if (patchedSize > GB_SIZE_CART_MAX) {
301		patchedSize = GB_SIZE_CART_MAX;
302	}
303	gb->memory.rom = anonymousMemoryMap(GB_SIZE_CART_MAX);
304	if (!patch->applyPatch(patch, gb->pristineRom, gb->pristineRomSize, gb->memory.rom, patchedSize)) {
305		mappedMemoryFree(gb->memory.rom, patchedSize);
306		gb->memory.rom = gb->pristineRom;
307		return;
308	}
309	gb->memory.romSize = patchedSize;
310	gb->romCrc32 = doCrc32(gb->memory.rom, gb->memory.romSize);
311}
312
313void GBDestroy(struct GB* gb) {
314	GBUnloadROM(gb);
315
316	if (gb->biosVf) {
317		gb->biosVf->close(gb->biosVf);
318		gb->biosVf = 0;
319	}
320
321	GBMemoryDeinit(gb);
322	GBAudioDeinit(&gb->audio);
323	GBVideoDeinit(&gb->video);
324	GBSIODeinit(&gb->sio);
325}
326
327void GBInterruptHandlerInit(struct LR35902InterruptHandler* irqh) {
328	irqh->reset = GBReset;
329	irqh->processEvents = GBProcessEvents;
330	irqh->setInterrupts = GBSetInterrupts;
331	irqh->hitIllegal = GBIllegal;
332	irqh->stop = GBStop;
333	irqh->halt = GBHalt;
334}
335
336static uint32_t _GBBiosCRC32(struct VFile* vf) {
337	ssize_t size = vf->size(vf);
338	if (size <= 0 || size > GB_SIZE_CART_BANK0) {
339		return 0;
340	}
341	void* bios = vf->map(vf, size, MAP_READ);
342	uint32_t biosCrc = doCrc32(bios, size);
343	vf->unmap(vf, bios, size);
344	return biosCrc;
345}
346
347bool GBIsBIOS(struct VFile* vf) {
348	switch (_GBBiosCRC32(vf)) {
349	case DMG_BIOS_CHECKSUM:
350	case DMG_2_BIOS_CHECKSUM:
351	case CGB_BIOS_CHECKSUM:
352		return true;
353	default:
354		return false;
355	}
356}
357
358void GBReset(struct LR35902Core* cpu) {
359	struct GB* gb = (struct GB*) cpu->master;
360	GBDetectModel(gb);
361	if (gb->biosVf) {
362		if (!GBIsBIOS(gb->biosVf)) {
363			gb->biosVf->close(gb->biosVf);
364			gb->biosVf = NULL;
365		} else {
366			gb->biosVf->seek(gb->biosVf, 0, SEEK_SET);
367			gb->memory.romBase = malloc(GB_SIZE_CART_BANK0);
368			ssize_t size = gb->biosVf->read(gb->biosVf, gb->memory.romBase, GB_SIZE_CART_BANK0);
369			memcpy(&gb->memory.romBase[size], &gb->memory.rom[size], GB_SIZE_CART_BANK0 - size);
370			if (size > 0x100) {
371				memcpy(&gb->memory.romBase[0x100], &gb->memory.rom[0x100], sizeof(struct GBCartridge));
372			}
373
374			cpu->a = 0;
375			cpu->f.packed = 0;
376			cpu->c = 0;
377			cpu->e = 0;
378			cpu->h = 0;
379			cpu->l = 0;
380			cpu->sp = 0;
381			cpu->pc = 0;
382		}
383	}
384
385	cpu->b = 0;
386	cpu->d = 0;
387
388	if (!gb->biosVf) {
389		switch (gb->model) {
390		case GB_MODEL_DMG:
391			// TODO: SGB
392		case GB_MODEL_SGB:
393		case GB_MODEL_AUTODETECT: // Silence warnings
394			gb->model = GB_MODEL_DMG;
395			cpu->a = 1;
396			cpu->f.packed = 0xB0;
397			cpu->c = 0x13;
398			cpu->e = 0xD8;
399			cpu->h = 1;
400			cpu->l = 0x4D;
401			break;
402		case GB_MODEL_AGB:
403			cpu->b = 1;
404			// Fall through
405		case GB_MODEL_CGB:
406			cpu->a = 0x11;
407			cpu->f.packed = 0x80;
408			cpu->c = 0;
409			cpu->e = 0x08;
410			cpu->h = 0;
411			cpu->l = 0x7C;
412			break;
413		}
414
415		cpu->sp = 0xFFFE;
416		cpu->pc = 0x100;
417	}
418
419	gb->eiPending = INT_MAX;
420	gb->doubleSpeed = 0;
421
422	cpu->memory.setActiveRegion(cpu, cpu->pc);
423
424	if (gb->yankedRomSize) {
425		gb->memory.romSize = gb->yankedRomSize;
426		gb->yankedRomSize = 0;
427	}
428	GBMemoryReset(gb);
429	GBVideoReset(&gb->video);
430	GBTimerReset(&gb->timer);
431	GBAudioReset(&gb->audio);
432	GBIOReset(gb);
433	GBSIOReset(&gb->sio);
434
435	GBSavedataUnmask(gb);
436}
437
438void GBDetectModel(struct GB* gb) {
439	if (gb->model != GB_MODEL_AUTODETECT) {
440		return;
441	}
442	if (gb->biosVf) {
443		switch (_GBBiosCRC32(gb->biosVf)) {
444		case DMG_BIOS_CHECKSUM:
445		case DMG_2_BIOS_CHECKSUM:
446			gb->model = GB_MODEL_DMG;
447			break;
448		case CGB_BIOS_CHECKSUM:
449			gb->model = GB_MODEL_CGB;
450			break;
451		default:
452			gb->biosVf->close(gb->biosVf);
453			gb->biosVf = NULL;
454		}
455	}
456	if (gb->model == GB_MODEL_AUTODETECT && gb->memory.rom) {
457		const struct GBCartridge* cart = (const struct GBCartridge*) &gb->memory.rom[0x100];
458		if (cart->cgb & 0x80) {
459			gb->model = GB_MODEL_CGB;
460		} else {
461			gb->model = GB_MODEL_DMG;
462		}
463	}
464
465	switch (gb->model) {
466	case GB_MODEL_DMG:
467	case GB_MODEL_SGB:
468	case GB_MODEL_AUTODETECT: //Silence warnings
469		gb->audio.style = GB_AUDIO_DMG;
470		break;
471	case GB_MODEL_AGB:
472	case GB_MODEL_CGB:
473		gb->audio.style = GB_AUDIO_CGB;
474		break;
475	}
476}
477
478void GBUpdateIRQs(struct GB* gb) {
479	int irqs = gb->memory.ie & gb->memory.io[REG_IF];
480	if (!irqs) {
481		return;
482	}
483	gb->cpu->halted = false;
484
485	if (!gb->memory.ime || gb->cpu->irqPending) {
486		return;
487	}
488
489	if (irqs & (1 << GB_IRQ_VBLANK)) {
490		LR35902RaiseIRQ(gb->cpu, GB_VECTOR_VBLANK);
491		gb->memory.io[REG_IF] &= ~(1 << GB_IRQ_VBLANK);
492		return;
493	}
494	if (irqs & (1 << GB_IRQ_LCDSTAT)) {
495		LR35902RaiseIRQ(gb->cpu, GB_VECTOR_LCDSTAT);
496		gb->memory.io[REG_IF] &= ~(1 << GB_IRQ_LCDSTAT);
497		return;
498	}
499	if (irqs & (1 << GB_IRQ_TIMER)) {
500		LR35902RaiseIRQ(gb->cpu, GB_VECTOR_TIMER);
501		gb->memory.io[REG_IF] &= ~(1 << GB_IRQ_TIMER);
502		return;
503	}
504	if (irqs & (1 << GB_IRQ_SIO)) {
505		LR35902RaiseIRQ(gb->cpu, GB_VECTOR_SIO);
506		gb->memory.io[REG_IF] &= ~(1 << GB_IRQ_SIO);
507		return;
508	}
509	if (irqs & (1 << GB_IRQ_KEYPAD)) {
510		LR35902RaiseIRQ(gb->cpu, GB_VECTOR_KEYPAD);
511		gb->memory.io[REG_IF] &= ~(1 << GB_IRQ_KEYPAD);
512	}
513}
514
515void GBProcessEvents(struct LR35902Core* cpu) {
516	struct GB* gb = (struct GB*) cpu->master;
517	do {
518		int32_t cycles = cpu->nextEvent;
519		int32_t nextEvent = INT_MAX;
520		int32_t testEvent;
521
522		if (gb->eiPending != INT_MAX) {
523			gb->eiPending -= cycles;
524			if (gb->eiPending <= 0) {
525				gb->memory.ime = true;
526				GBUpdateIRQs(gb);
527				gb->eiPending = INT_MAX;
528			} else {
529				nextEvent = gb->eiPending;
530			}
531		}
532
533		testEvent = GBVideoProcessEvents(&gb->video, cycles >> gb->doubleSpeed);
534		if (testEvent != INT_MAX) {
535			testEvent <<= gb->doubleSpeed;
536			if (testEvent < nextEvent) {
537				nextEvent = testEvent;
538			}
539		}
540
541		testEvent = GBAudioProcessEvents(&gb->audio, cycles >> gb->doubleSpeed);
542		if (testEvent != INT_MAX) {
543			testEvent <<= gb->doubleSpeed;
544			if (testEvent < nextEvent) {
545				nextEvent = testEvent;
546			}
547		}
548
549		testEvent = GBTimerProcessEvents(&gb->timer, cycles);
550		if (testEvent < nextEvent) {
551			nextEvent = testEvent;
552		}
553
554		testEvent = GBSIOProcessEvents(&gb->sio, cycles);
555		if (testEvent < nextEvent) {
556			nextEvent = testEvent;
557		}
558
559		testEvent = GBMemoryProcessEvents(gb, cycles);
560		if (testEvent < nextEvent) {
561			nextEvent = testEvent;
562		}
563
564		cpu->cycles -= cycles;
565		cpu->nextEvent = nextEvent;
566
567		if (cpu->halted) {
568			cpu->cycles = cpu->nextEvent;
569			if (!gb->memory.ie || !gb->memory.ime) {
570				break;
571			}
572		}
573	} while (cpu->cycles >= cpu->nextEvent);
574}
575
576void GBSetInterrupts(struct LR35902Core* cpu, bool enable) {
577	struct GB* gb = (struct GB*) cpu->master;
578	if (!enable) {
579		gb->memory.ime = enable;
580		gb->eiPending = INT_MAX;
581		GBUpdateIRQs(gb);
582	} else {
583		if (cpu->nextEvent > cpu->cycles + 4) {
584			cpu->nextEvent = cpu->cycles + 4;
585		}
586		gb->eiPending = cpu->cycles + 4;
587	}
588}
589
590void GBHalt(struct LR35902Core* cpu) {
591	if (!cpu->irqPending) {
592		cpu->cycles = cpu->nextEvent;
593		cpu->halted = true;
594	}
595}
596
597void GBStop(struct LR35902Core* cpu) {
598	struct GB* gb = (struct GB*) cpu->master;
599	if (cpu->bus) {
600		mLOG(GB, GAME_ERROR, "Hit illegal stop at address %04X:%02X\n", cpu->pc, cpu->bus);
601	}
602	if (gb->memory.io[REG_KEY1] & 1) {
603		gb->doubleSpeed ^= 1;
604		gb->memory.io[REG_KEY1] = 0;
605		gb->memory.io[REG_KEY1] |= gb->doubleSpeed << 7;
606	} else if (cpu->bus) {
607#ifdef USE_DEBUGGERS
608		if (cpu->components && cpu->components[CPU_COMPONENT_DEBUGGER]) {
609			struct mDebuggerEntryInfo info = {
610				.address = cpu->pc - 1,
611				.opcode = 0x1000 | cpu->bus
612			};
613			mDebuggerEnter((struct mDebugger*) cpu->components[CPU_COMPONENT_DEBUGGER], DEBUGGER_ENTER_ILLEGAL_OP, &info);
614		}
615#endif
616		// Hang forever
617		gb->memory.ime = 0;
618		cpu->pc -= 2;
619	}
620	// TODO: Actually stop
621}
622
623void GBIllegal(struct LR35902Core* cpu) {
624	struct GB* gb = (struct GB*) cpu->master;
625	mLOG(GB, GAME_ERROR, "Hit illegal opcode at address %04X:%02X\n", cpu->pc, cpu->bus);
626#ifdef USE_DEBUGGERS
627	if (cpu->components && cpu->components[CPU_COMPONENT_DEBUGGER]) {
628		struct mDebuggerEntryInfo info = {
629			.address = cpu->pc,
630			.opcode = cpu->bus
631		};
632		mDebuggerEnter((struct mDebugger*) cpu->components[CPU_COMPONENT_DEBUGGER], DEBUGGER_ENTER_ILLEGAL_OP, &info);
633	}
634#endif
635	// Hang forever
636	gb->memory.ime = 0;
637	--cpu->pc;
638}
639
640bool GBIsROM(struct VFile* vf) {
641	vf->seek(vf, 0x104, SEEK_SET);
642	uint8_t header[4];
643
644	if (vf->read(vf, &header, sizeof(header)) < (ssize_t) sizeof(header)) {
645		return false;
646	}
647	if (memcmp(header, _knownHeader, sizeof(header))) {
648		return false;
649	}
650	return true;
651}
652
653void GBGetGameTitle(const struct GB* gb, char* out) {
654	const struct GBCartridge* cart = NULL;
655	if (gb->memory.rom) {
656		cart = (const struct GBCartridge*) &gb->memory.rom[0x100];
657	}
658	if (gb->pristineRom) {
659		cart = (const struct GBCartridge*) &((uint8_t*) gb->pristineRom)[0x100];
660	}
661	if (!cart) {
662		return;
663	}
664	if (cart->oldLicensee != 0x33) {
665		memcpy(out, cart->titleLong, 16);
666	} else {
667		memcpy(out, cart->titleShort, 11);
668	}
669}
670
671void GBGetGameCode(const struct GB* gb, char* out) {
672	memset(out, 0, 8);
673	const struct GBCartridge* cart = NULL;
674	if (gb->memory.rom) {
675		cart = (const struct GBCartridge*) &gb->memory.rom[0x100];
676	}
677	if (gb->pristineRom) {
678		cart = (const struct GBCartridge*) &((uint8_t*) gb->pristineRom)[0x100];
679	}
680	if (!cart) {
681		return;
682	}
683	if (cart->cgb == 0xC0) {
684		memcpy(out, "CGB-????", 8);
685	} else {
686		memcpy(out, "DMG-????", 8);
687	}
688	if (cart->oldLicensee == 0x33) {
689		memcpy(&out[4], cart->maker, 4);
690	}
691}
692
693void GBFrameEnded(struct GB* gb) {
694	GBSramClean(gb, gb->video.frameCounter);
695
696	if (gb->cpu->components && gb->cpu->components[CPU_COMPONENT_CHEAT_DEVICE]) {
697		struct mCheatDevice* device = (struct mCheatDevice*) gb->cpu->components[CPU_COMPONENT_CHEAT_DEVICE];
698		size_t i;
699		for (i = 0; i < mCheatSetsSize(&device->cheats); ++i) {
700			struct mCheatSet* cheats = *mCheatSetsGetPointer(&device->cheats, i);
701			mCheatRefresh(device, cheats);
702		}
703	}
704}