all repos — mgba @ bf2aa98338a163457ddbe1bea0c2e7476fd6535d

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