all repos — mgba @ c5582501e14539e77bb2387e73e6f3fb52df4653

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