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}