all repos — mgba @ 4680a767c638530856255b133e7a88bf1ebb333e

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