all repos — mgba @ d9764e8cea482fead7e1a3376d1007c884ee252b

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