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