all repos — mgba @ 2bd8dd2fa299679629972e3a66c28e6004307e0c

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