src/gb/core.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/gb/core.h>
7
8#include <mgba/core/core.h>
9#include <mgba/internal/debugger/symbols.h>
10#include <mgba/internal/gb/cheats.h>
11#include <mgba/internal/gb/debugger/debugger.h>
12#include <mgba/internal/gb/debugger/symbols.h>
13#include <mgba/internal/gb/extra/cli.h>
14#include <mgba/internal/gb/io.h>
15#include <mgba/internal/gb/gb.h>
16#include <mgba/internal/gb/mbc.h>
17#include <mgba/internal/gb/overrides.h>
18#include <mgba/internal/gb/renderers/software.h>
19#include <mgba/internal/gb/renderers/proxy.h>
20#include <mgba/internal/gb/serialize.h>
21#include <mgba/internal/sm83/sm83.h>
22#include <mgba/internal/sm83/debugger/debugger.h>
23#include <mgba-util/crc32.h>
24#include <mgba-util/memory.h>
25#include <mgba-util/patch.h>
26#include <mgba-util/vfs.h>
27
28static const struct mCoreChannelInfo _GBVideoLayers[] = {
29 { 0, "bg", "Background", NULL },
30 { 1, "bgwin", "Window", NULL },
31 { 2, "obj", "Objects", NULL },
32};
33
34static const struct mCoreChannelInfo _GBAudioChannels[] = {
35 { 0, "ch1", "Channel 1", "Square/Sweep" },
36 { 1, "ch2", "Channel 2", "Square" },
37 { 2, "ch3", "Channel 3", "PCM" },
38 { 3, "ch4", "Channel 4", "Noise" },
39};
40
41static const struct mCoreMemoryBlock _GBMemoryBlocks[] = {
42 { -1, "mem", "All", "All", 0, 0x10000, 0x10000, mCORE_MEMORY_VIRTUAL },
43 { GB_REGION_CART_BANK0, "cart0", "ROM Bank", "Game Pak (32kiB)", GB_BASE_CART_BANK0, GB_BASE_CART_BANK0 + GB_SIZE_CART_BANK0 * 2, 0x800000, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED, 511, GB_BASE_CART_BANK0 + GB_SIZE_CART_BANK0 },
44 { GB_REGION_VRAM, "vram", "VRAM", "Video RAM (8kiB)", GB_BASE_VRAM, GB_BASE_VRAM + GB_SIZE_VRAM, GB_SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
45 { GB_REGION_EXTERNAL_RAM, "sram", "SRAM", "External RAM (8kiB)", GB_BASE_EXTERNAL_RAM, GB_BASE_EXTERNAL_RAM + GB_SIZE_EXTERNAL_RAM, GB_SIZE_EXTERNAL_RAM * 4, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED, 3 },
46 { GB_REGION_WORKING_RAM_BANK0, "wram", "WRAM", "Working RAM (8kiB)", GB_BASE_WORKING_RAM_BANK0, GB_BASE_WORKING_RAM_BANK0 + GB_SIZE_WORKING_RAM_BANK0 * 2 , GB_SIZE_WORKING_RAM_BANK0 * 2, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
47 { GB_BASE_OAM, "oam", "OAM", "OBJ Attribute Memory", GB_BASE_OAM, GB_BASE_OAM + GB_SIZE_OAM, GB_SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
48 { GB_BASE_IO, "io", "MMIO", "Memory-Mapped I/O", GB_BASE_IO, GB_BASE_IO + GB_SIZE_IO, GB_SIZE_IO, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
49 { GB_BASE_HRAM, "hram", "HRAM", "High RAM", GB_BASE_HRAM, GB_BASE_HRAM + GB_SIZE_HRAM, GB_SIZE_HRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
50};
51
52static const struct mCoreMemoryBlock _GBCMemoryBlocks[] = {
53 { -1, "mem", "All", "All", 0, 0x10000, 0x10000, mCORE_MEMORY_VIRTUAL },
54 { GB_REGION_CART_BANK0, "cart0", "ROM Bank", "Game Pak (32kiB)", GB_BASE_CART_BANK0, GB_BASE_CART_BANK0 + GB_SIZE_CART_BANK0 * 2, 0x800000, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED, 511, GB_BASE_CART_BANK0 + GB_SIZE_CART_BANK0 },
55 { GB_REGION_VRAM, "vram", "VRAM", "Video RAM (8kiB)", GB_BASE_VRAM, GB_BASE_VRAM + GB_SIZE_VRAM, GB_SIZE_VRAM * 2, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED, 1 },
56 { GB_REGION_EXTERNAL_RAM, "sram", "SRAM", "External RAM (8kiB)", GB_BASE_EXTERNAL_RAM, GB_BASE_EXTERNAL_RAM + GB_SIZE_EXTERNAL_RAM, GB_SIZE_EXTERNAL_RAM * 4, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED, 3 },
57 { GB_REGION_WORKING_RAM_BANK0, "wram", "WRAM", "Working RAM (8kiB)", GB_BASE_WORKING_RAM_BANK0, GB_BASE_WORKING_RAM_BANK0 + GB_SIZE_WORKING_RAM_BANK0 * 2, GB_SIZE_WORKING_RAM_BANK0 * 8, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED, 7, GB_BASE_WORKING_RAM_BANK0 + GB_SIZE_WORKING_RAM_BANK0 },
58 { GB_BASE_OAM, "oam", "OAM", "OBJ Attribute Memory", GB_BASE_OAM, GB_BASE_OAM + GB_SIZE_OAM, GB_SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
59 { GB_BASE_IO, "io", "MMIO", "Memory-Mapped I/O", GB_BASE_IO, GB_BASE_IO + GB_SIZE_IO, GB_SIZE_IO, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
60 { GB_BASE_HRAM, "hram", "HRAM", "High RAM", GB_BASE_HRAM, GB_BASE_HRAM + GB_SIZE_HRAM, GB_SIZE_HRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
61};
62
63struct mVideoLogContext;
64struct GBCore {
65 struct mCore d;
66 struct GBVideoRenderer dummyRenderer;
67 struct GBVideoSoftwareRenderer renderer;
68#ifndef MINIMAL_CORE
69 struct GBVideoProxyRenderer proxyRenderer;
70 struct mVideoLogContext* logContext;
71#endif
72 struct mCoreCallbacks logCallbacks;
73 uint8_t keys;
74 struct mCPUComponent* components[CPU_COMPONENT_MAX];
75 const struct Configuration* overrides;
76 struct mDebuggerPlatform* debuggerPlatform;
77 struct mCheatDevice* cheatDevice;
78};
79
80static bool _GBCoreInit(struct mCore* core) {
81 struct GBCore* gbcore = (struct GBCore*) core;
82
83 struct SM83Core* cpu = anonymousMemoryMap(sizeof(struct SM83Core));
84 struct GB* gb = anonymousMemoryMap(sizeof(struct GB));
85 if (!cpu || !gb) {
86 free(cpu);
87 free(gb);
88 return false;
89 }
90 core->cpu = cpu;
91 core->board = gb;
92 core->timing = &gb->timing;
93 gbcore->overrides = NULL;
94 gbcore->debuggerPlatform = NULL;
95 gbcore->cheatDevice = NULL;
96#ifndef MINIMAL_CORE
97 gbcore->logContext = NULL;
98#endif
99
100 GBCreate(gb);
101 memset(gbcore->components, 0, sizeof(gbcore->components));
102 SM83SetComponents(cpu, &gb->d, CPU_COMPONENT_MAX, gbcore->components);
103 SM83Init(cpu);
104 mRTCGenericSourceInit(&core->rtc, core);
105 gb->memory.rtc = &core->rtc.d;
106
107 GBVideoDummyRendererCreate(&gbcore->dummyRenderer);
108 GBVideoAssociateRenderer(&gb->video, &gbcore->dummyRenderer);
109
110 GBVideoSoftwareRendererCreate(&gbcore->renderer);
111 gbcore->renderer.outputBuffer = NULL;
112
113#ifndef MINIMAL_CORE
114 gbcore->proxyRenderer.logger = NULL;
115#endif
116
117 gbcore->keys = 0;
118 gb->keySource = &gbcore->keys;
119
120#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
121 mDirectorySetInit(&core->dirs);
122#endif
123
124 return true;
125}
126
127static void _GBCoreDeinit(struct mCore* core) {
128 SM83Deinit(core->cpu);
129 GBDestroy(core->board);
130 mappedMemoryFree(core->cpu, sizeof(struct SM83Core));
131 mappedMemoryFree(core->board, sizeof(struct GB));
132#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
133 mDirectorySetDeinit(&core->dirs);
134#endif
135#ifdef USE_DEBUGGERS
136 if (core->symbolTable) {
137 mDebuggerSymbolTableDestroy(core->symbolTable);
138 }
139#endif
140
141 struct GBCore* gbcore = (struct GBCore*) core;
142 free(gbcore->debuggerPlatform);
143 if (gbcore->cheatDevice) {
144 mCheatDeviceDestroy(gbcore->cheatDevice);
145 }
146 free(gbcore->cheatDevice);
147 mCoreConfigFreeOpts(&core->opts);
148 free(core);
149}
150
151static enum mPlatform _GBCorePlatform(const struct mCore* core) {
152 UNUSED(core);
153 return PLATFORM_GB;
154}
155
156static bool _GBCoreSupportsFeature(const struct mCore* core, enum mCoreFeature feature) {
157 UNUSED(core);
158 switch (feature) {
159 default:
160 return false;
161 }
162}
163
164static void _GBCoreSetSync(struct mCore* core, struct mCoreSync* sync) {
165 struct GB* gb = core->board;
166 gb->sync = sync;
167}
168
169static void _GBCoreLoadConfig(struct mCore* core, const struct mCoreConfig* config) {
170 UNUSED(config);
171
172 struct GB* gb = core->board;
173 if (core->opts.mute) {
174 gb->audio.masterVolume = 0;
175 } else {
176 gb->audio.masterVolume = core->opts.volume;
177 }
178 gb->video.frameskip = core->opts.frameskip;
179
180 int color;
181 if (mCoreConfigGetIntValue(config, "gb.pal[0]", &color)) {
182 GBVideoSetPalette(&gb->video, 0, color);
183 }
184 if (mCoreConfigGetIntValue(config, "gb.pal[1]", &color)) {
185 GBVideoSetPalette(&gb->video, 1, color);
186 }
187 if (mCoreConfigGetIntValue(config, "gb.pal[2]", &color)) {
188 GBVideoSetPalette(&gb->video, 2, color);
189 }
190 if (mCoreConfigGetIntValue(config, "gb.pal[3]", &color)) {
191 GBVideoSetPalette(&gb->video, 3, color);
192 }
193 if (mCoreConfigGetIntValue(config, "gb.pal[4]", &color)) {
194 GBVideoSetPalette(&gb->video, 4, color);
195 }
196 if (mCoreConfigGetIntValue(config, "gb.pal[5]", &color)) {
197 GBVideoSetPalette(&gb->video, 5, color);
198 }
199 if (mCoreConfigGetIntValue(config, "gb.pal[6]", &color)) {
200 GBVideoSetPalette(&gb->video, 6, color);
201 }
202 if (mCoreConfigGetIntValue(config, "gb.pal[7]", &color)) {
203 GBVideoSetPalette(&gb->video, 7, color);
204 }
205 if (mCoreConfigGetIntValue(config, "gb.pal[8]", &color)) {
206 GBVideoSetPalette(&gb->video, 8, color);
207 }
208 if (mCoreConfigGetIntValue(config, "gb.pal[9]", &color)) {
209 GBVideoSetPalette(&gb->video, 9, color);
210 }
211 if (mCoreConfigGetIntValue(config, "gb.pal[10]", &color)) {
212 GBVideoSetPalette(&gb->video, 10, color);
213 }
214 if (mCoreConfigGetIntValue(config, "gb.pal[11]", &color)) {
215 GBVideoSetPalette(&gb->video, 11, color);
216 }
217
218 mCoreConfigCopyValue(&core->config, config, "gb.bios");
219 mCoreConfigCopyValue(&core->config, config, "sgb.bios");
220 mCoreConfigCopyValue(&core->config, config, "gbc.bios");
221 mCoreConfigCopyValue(&core->config, config, "gb.model");
222 mCoreConfigCopyValue(&core->config, config, "sgb.model");
223 mCoreConfigCopyValue(&core->config, config, "cgb.model");
224 mCoreConfigCopyValue(&core->config, config, "cgb.hybridModel");
225 mCoreConfigCopyValue(&core->config, config, "cgb.sgbModel");
226 mCoreConfigCopyValue(&core->config, config, "useCgbColors");
227 mCoreConfigCopyValue(&core->config, config, "allowOpposingDirections");
228
229 int fakeBool = 0;
230 mCoreConfigGetIntValue(config, "allowOpposingDirections", &fakeBool);
231 gb->allowOpposingDirections = fakeBool;
232
233 if (mCoreConfigGetIntValue(config, "sgb.borders", &fakeBool)) {
234 gb->video.sgbBorders = fakeBool;
235 gb->video.renderer->enableSGBBorder(gb->video.renderer, fakeBool);
236 }
237
238#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
239 struct GBCore* gbcore = (struct GBCore*) core;
240 gbcore->overrides = mCoreConfigGetOverridesConst(config);
241#endif
242}
243
244static void _GBCoreReloadConfigOption(struct mCore* core, const char* option, const struct mCoreConfig* config) {
245 struct GB* gb = core->board;
246 if (!config) {
247 config = &core->config;
248 }
249
250 if (!option) {
251 // Reload options from opts
252 if (core->opts.mute) {
253 gb->audio.masterVolume = 0;
254 } else {
255 gb->audio.masterVolume = core->opts.volume;
256 }
257 gb->video.frameskip = core->opts.frameskip;
258 return;
259 }
260
261 int fakeBool;
262 if (strcmp("mute", option) == 0) {
263 if (mCoreConfigGetIntValue(config, "mute", &fakeBool)) {
264 core->opts.mute = fakeBool;
265
266 if (core->opts.mute) {
267 gb->audio.masterVolume = 0;
268 } else {
269 gb->audio.masterVolume = core->opts.volume;
270 }
271 }
272 return;
273 }
274 if (strcmp("volume", option) == 0) {
275 if (mCoreConfigGetIntValue(config, "volume", &core->opts.volume) && !core->opts.mute) {
276 gb->audio.masterVolume = core->opts.volume;
277 }
278 return;
279 }
280 if (strcmp("frameskip", option) == 0) {
281 if (mCoreConfigGetIntValue(config, "frameskip", &core->opts.frameskip)) {
282 gb->video.frameskip = core->opts.frameskip;
283 }
284 return;
285 }
286 if (strcmp("allowOpposingDirections", option) == 0) {
287 if (config != &core->config) {
288 mCoreConfigCopyValue(&core->config, config, "allowOpposingDirections");
289 }
290 if (mCoreConfigGetIntValue(config, "allowOpposingDirections", &fakeBool)) {
291 gb->allowOpposingDirections = fakeBool;
292 }
293 return;
294 }
295}
296
297static void _GBCoreDesiredVideoDimensions(const struct mCore* core, unsigned* width, unsigned* height) {
298 const struct GB* gb = core->board;
299 if (gb && (!(gb->model & GB_MODEL_SGB) || !gb->video.sgbBorders)) {
300 *width = GB_VIDEO_HORIZONTAL_PIXELS;
301 *height = GB_VIDEO_VERTICAL_PIXELS;
302 } else {
303 *width = 256;
304 *height = 224;
305 }
306}
307
308static void _GBCoreSetVideoBuffer(struct mCore* core, color_t* buffer, size_t stride) {
309 struct GBCore* gbcore = (struct GBCore*) core;
310 gbcore->renderer.outputBuffer = buffer;
311 gbcore->renderer.outputBufferStride = stride;
312}
313
314static void _GBCoreSetVideoGLTex(struct mCore* core, unsigned texid) {
315 UNUSED(core);
316 UNUSED(texid);
317}
318
319static void _GBCoreGetPixels(struct mCore* core, const void** buffer, size_t* stride) {
320 struct GBCore* gbcore = (struct GBCore*) core;
321 gbcore->renderer.d.getPixels(&gbcore->renderer.d, stride, buffer);
322}
323
324static void _GBCorePutPixels(struct mCore* core, const void* buffer, size_t stride) {
325 struct GBCore* gbcore = (struct GBCore*) core;
326 gbcore->renderer.d.putPixels(&gbcore->renderer.d, stride, buffer);
327}
328
329static struct blip_t* _GBCoreGetAudioChannel(struct mCore* core, int ch) {
330 struct GB* gb = core->board;
331 switch (ch) {
332 case 0:
333 return gb->audio.left;
334 case 1:
335 return gb->audio.right;
336 default:
337 return NULL;
338 }
339}
340
341static void _GBCoreSetAudioBufferSize(struct mCore* core, size_t samples) {
342 struct GB* gb = core->board;
343 GBAudioResizeBuffer(&gb->audio, samples);
344}
345
346static size_t _GBCoreGetAudioBufferSize(struct mCore* core) {
347 struct GB* gb = core->board;
348 return gb->audio.samples;
349}
350
351static void _GBCoreAddCoreCallbacks(struct mCore* core, struct mCoreCallbacks* coreCallbacks) {
352 struct GB* gb = core->board;
353 *mCoreCallbacksListAppend(&gb->coreCallbacks) = *coreCallbacks;
354}
355
356static void _GBCoreClearCoreCallbacks(struct mCore* core) {
357 struct GB* gb = core->board;
358 mCoreCallbacksListClear(&gb->coreCallbacks);
359}
360
361static void _GBCoreSetAVStream(struct mCore* core, struct mAVStream* stream) {
362 struct GB* gb = core->board;
363 gb->stream = stream;
364 if (stream && stream->videoDimensionsChanged) {
365 unsigned width, height;
366 core->desiredVideoDimensions(core, &width, &height);
367 stream->videoDimensionsChanged(stream, width, height);
368 }
369}
370
371static bool _GBCoreLoadROM(struct mCore* core, struct VFile* vf) {
372 return GBLoadROM(core->board, vf);
373}
374
375static bool _GBCoreLoadBIOS(struct mCore* core, struct VFile* vf, int type) {
376 UNUSED(type);
377 GBLoadBIOS(core->board, vf);
378 return true;
379}
380
381static bool _GBCoreLoadSave(struct mCore* core, struct VFile* vf) {
382 return GBLoadSave(core->board, vf);
383}
384
385static bool _GBCoreLoadTemporarySave(struct mCore* core, struct VFile* vf) {
386 struct GB* gb = core->board;
387 GBSavedataMask(gb, vf, false);
388 return true; // TODO: Return a real value
389}
390
391static bool _GBCoreLoadPatch(struct mCore* core, struct VFile* vf) {
392 if (!vf) {
393 return false;
394 }
395 struct Patch patch;
396 if (!loadPatch(vf, &patch)) {
397 return false;
398 }
399 GBApplyPatch(core->board, &patch);
400 return true;
401}
402
403static void _GBCoreUnloadROM(struct mCore* core) {
404 struct GBCore* gbcore = (struct GBCore*) core;
405 struct SM83Core* cpu = core->cpu;
406 if (gbcore->cheatDevice) {
407 SM83HotplugDetach(cpu, CPU_COMPONENT_CHEAT_DEVICE);
408 cpu->components[CPU_COMPONENT_CHEAT_DEVICE] = NULL;
409 mCheatDeviceDestroy(gbcore->cheatDevice);
410 gbcore->cheatDevice = NULL;
411 }
412 return GBUnloadROM(core->board);
413}
414
415static void _GBCoreChecksum(const struct mCore* core, void* data, enum mCoreChecksumType type) {
416 struct GB* gb = (struct GB*) core->board;
417 switch (type) {
418 case CHECKSUM_CRC32:
419 memcpy(data, &gb->romCrc32, sizeof(gb->romCrc32));
420 break;
421 }
422 return;
423}
424
425static void _GBCoreReset(struct mCore* core) {
426 struct GBCore* gbcore = (struct GBCore*) core;
427 struct GB* gb = (struct GB*) core->board;
428 if (gbcore->renderer.outputBuffer) {
429 GBVideoAssociateRenderer(&gb->video, &gbcore->renderer.d);
430 }
431
432 if (gb->memory.rom) {
433 int doColorOverride = 0;
434 mCoreConfigGetIntValue(&core->config, "useCgbColors", &doColorOverride);
435
436 struct GBCartridgeOverride override;
437 const struct GBCartridge* cart = (const struct GBCartridge*) &gb->memory.rom[0x100];
438 override.headerCrc32 = doCrc32(cart, sizeof(*cart));
439 if (GBOverrideFind(gbcore->overrides, &override) || (doColorOverride && GBOverrideColorFind(&override))) {
440 GBOverrideApply(gb, &override);
441 }
442
443 const char* modelGB = mCoreConfigGetValue(&core->config, "gb.model");
444 const char* modelSGB = mCoreConfigGetValue(&core->config, "sgb.model");
445 const char* modelCGB = mCoreConfigGetValue(&core->config, "cgb.model");
446 const char* modelCGBHybrid = mCoreConfigGetValue(&core->config, "cgb.hybridModel");
447 const char* modelCGBSGB = mCoreConfigGetValue(&core->config, "cgb.sgbModel");
448 if (modelGB || modelCGB || modelSGB || modelCGBHybrid || modelCGBSGB) {
449 int models = GBValidModels(gb->memory.rom);
450 switch (models) {
451 case GB_MODEL_SGB | GB_MODEL_MGB:
452 if (modelSGB) {
453 gb->model = GBNameToModel(modelSGB);
454 }
455 break;
456 case GB_MODEL_MGB:
457 if (modelGB) {
458 gb->model = GBNameToModel(modelGB);
459 }
460 break;
461 case GB_MODEL_MGB | GB_MODEL_CGB:
462 if (modelCGBHybrid) {
463 gb->model = GBNameToModel(modelCGBHybrid);
464 }
465 break;
466 case GB_MODEL_SGB | GB_MODEL_CGB: // TODO: Do these even exist?
467 case GB_MODEL_MGB | GB_MODEL_SGB | GB_MODEL_CGB:
468 if (modelCGBSGB) {
469 gb->model = GBNameToModel(modelCGBSGB);
470 }
471 break;
472 case GB_MODEL_CGB:
473 if (modelCGB) {
474 gb->model = GBNameToModel(modelCGB);
475 }
476 break;
477 }
478 }
479 }
480
481#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
482 if (!gb->biosVf && core->opts.useBios) {
483 struct VFile* bios = NULL;
484 bool found = false;
485 if (core->opts.bios) {
486 bios = VFileOpen(core->opts.bios, O_RDONLY);
487 if (bios && GBIsBIOS(bios)) {
488 found = true;
489 } else if (bios) {
490 bios->close(bios);
491 bios = NULL;
492 }
493 }
494 if (!found) {
495 GBDetectModel(gb);
496 const char* configPath = NULL;
497
498 switch (gb->model) {
499 case GB_MODEL_DMG:
500 case GB_MODEL_MGB: // TODO
501 configPath = mCoreConfigGetValue(&core->config, "gb.bios");
502 break;
503 case GB_MODEL_SGB:
504 case GB_MODEL_SGB2: // TODO
505 configPath = mCoreConfigGetValue(&core->config, "sgb.bios");
506 break;
507 case GB_MODEL_CGB:
508 case GB_MODEL_AGB:
509 configPath = mCoreConfigGetValue(&core->config, "gbc.bios");
510 break;
511 default:
512 break;
513 };
514 if (configPath) {
515 bios = VFileOpen(configPath, O_RDONLY);
516 }
517 if (bios && GBIsBIOS(bios)) {
518 found = true;
519 } else if (bios) {
520 bios->close(bios);
521 bios = NULL;
522 }
523 }
524 if (!found) {
525 char path[PATH_MAX];
526 mCoreConfigDirectory(path, PATH_MAX);
527 switch (gb->model) {
528 case GB_MODEL_DMG:
529 case GB_MODEL_MGB: // TODO
530 strncat(path, PATH_SEP "gb_bios.bin", PATH_MAX - strlen(path));
531 break;
532 case GB_MODEL_SGB:
533 case GB_MODEL_SGB2: // TODO
534 strncat(path, PATH_SEP "sgb_bios.bin", PATH_MAX - strlen(path));
535 break;
536 case GB_MODEL_CGB:
537 case GB_MODEL_AGB:
538 strncat(path, PATH_SEP "gbc_bios.bin", PATH_MAX - strlen(path));
539 break;
540 default:
541 break;
542 };
543 bios = VFileOpen(path, O_RDONLY);
544 if (bios && GBIsBIOS(bios)) {
545 found = true;
546 } else if (bios) {
547 bios->close(bios);
548 bios = NULL;
549 }
550 }
551 if (found && bios) {
552 GBLoadBIOS(gb, bios);
553 }
554 }
555#endif
556
557 SM83Reset(core->cpu);
558
559 if (core->opts.skipBios) {
560 GBSkipBIOS(core->board);
561 }
562}
563
564static void _GBCoreRunFrame(struct mCore* core) {
565 struct GB* gb = core->board;
566 int32_t frameCounter = gb->video.frameCounter;
567 while (gb->video.frameCounter == frameCounter) {
568 SM83Run(core->cpu);
569 }
570}
571
572static void _GBCoreRunLoop(struct mCore* core) {
573 SM83Run(core->cpu);
574}
575
576static void _GBCoreStep(struct mCore* core) {
577 struct SM83Core* cpu = core->cpu;
578 do {
579 SM83Tick(cpu);
580 } while (cpu->executionState != SM83_CORE_FETCH);
581}
582
583static size_t _GBCoreStateSize(struct mCore* core) {
584 UNUSED(core);
585 return sizeof(struct GBSerializedState);
586}
587
588static bool _GBCoreLoadState(struct mCore* core, const void* state) {
589 return GBDeserialize(core->board, state);
590}
591
592static bool _GBCoreSaveState(struct mCore* core, void* state) {
593 struct SM83Core* cpu = core->cpu;
594 while (cpu->executionState != SM83_CORE_FETCH) {
595 SM83Tick(cpu);
596 }
597 GBSerialize(core->board, state);
598 return true;
599}
600
601static void _GBCoreSetKeys(struct mCore* core, uint32_t keys) {
602 struct GBCore* gbcore = (struct GBCore*) core;
603 gbcore->keys = keys;
604 GBTestKeypadIRQ(core->board);
605}
606
607static void _GBCoreAddKeys(struct mCore* core, uint32_t keys) {
608 struct GBCore* gbcore = (struct GBCore*) core;
609 gbcore->keys |= keys;
610 GBTestKeypadIRQ(core->board);
611}
612
613static void _GBCoreClearKeys(struct mCore* core, uint32_t keys) {
614 struct GBCore* gbcore = (struct GBCore*) core;
615 gbcore->keys &= ~keys;
616}
617
618static int32_t _GBCoreFrameCounter(const struct mCore* core) {
619 const struct GB* gb = core->board;
620 return gb->video.frameCounter;
621}
622
623static int32_t _GBCoreFrameCycles(const struct mCore* core) {
624 UNUSED(core);
625 return GB_VIDEO_TOTAL_LENGTH;
626}
627
628static int32_t _GBCoreFrequency(const struct mCore* core) {
629 UNUSED(core);
630 // TODO: GB differences
631 return DMG_SM83_FREQUENCY;
632}
633
634static void _GBCoreGetGameTitle(const struct mCore* core, char* title) {
635 GBGetGameTitle(core->board, title);
636}
637
638static void _GBCoreGetGameCode(const struct mCore* core, char* title) {
639 GBGetGameCode(core->board, title);
640}
641
642static void _GBCoreSetPeripheral(struct mCore* core, int type, void* periph) {
643 struct GB* gb = core->board;
644 switch (type) {
645 case mPERIPH_ROTATION:
646 gb->memory.rotation = periph;
647 break;
648 case mPERIPH_RUMBLE:
649 gb->memory.rumble = periph;
650 break;
651 case mPERIPH_IMAGE_SOURCE:
652 gb->memory.cam = periph;
653 break;
654 default:
655 return;
656 }
657}
658
659static uint32_t _GBCoreBusRead8(struct mCore* core, uint32_t address) {
660 struct SM83Core* cpu = core->cpu;
661 return cpu->memory.load8(cpu, address);
662}
663
664static uint32_t _GBCoreBusRead16(struct mCore* core, uint32_t address) {
665 struct SM83Core* cpu = core->cpu;
666 return cpu->memory.load8(cpu, address) | (cpu->memory.load8(cpu, address + 1) << 8);
667}
668
669static uint32_t _GBCoreBusRead32(struct mCore* core, uint32_t address) {
670 struct SM83Core* cpu = core->cpu;
671 return cpu->memory.load8(cpu, address) | (cpu->memory.load8(cpu, address + 1) << 8) |
672 (cpu->memory.load8(cpu, address + 2) << 16) | (cpu->memory.load8(cpu, address + 3) << 24);
673}
674
675static void _GBCoreBusWrite8(struct mCore* core, uint32_t address, uint8_t value) {
676 struct SM83Core* cpu = core->cpu;
677 cpu->memory.store8(cpu, address, value);
678}
679
680static void _GBCoreBusWrite16(struct mCore* core, uint32_t address, uint16_t value) {
681 struct SM83Core* cpu = core->cpu;
682 cpu->memory.store8(cpu, address, value);
683 cpu->memory.store8(cpu, address + 1, value >> 8);
684}
685
686static void _GBCoreBusWrite32(struct mCore* core, uint32_t address, uint32_t value) {
687 struct SM83Core* cpu = core->cpu;
688 cpu->memory.store8(cpu, address, value);
689 cpu->memory.store8(cpu, address + 1, value >> 8);
690 cpu->memory.store8(cpu, address + 2, value >> 16);
691 cpu->memory.store8(cpu, address + 3, value >> 24);
692}
693
694static uint32_t _GBCoreRawRead8(struct mCore* core, uint32_t address, int segment) {
695 struct SM83Core* cpu = core->cpu;
696 return GBView8(cpu, address, segment);
697}
698
699static uint32_t _GBCoreRawRead16(struct mCore* core, uint32_t address, int segment) {
700 struct SM83Core* cpu = core->cpu;
701 return GBView8(cpu, address, segment) | (GBView8(cpu, address + 1, segment) << 8);
702}
703
704static uint32_t _GBCoreRawRead32(struct mCore* core, uint32_t address, int segment) {
705 struct SM83Core* cpu = core->cpu;
706 return GBView8(cpu, address, segment) | (GBView8(cpu, address + 1, segment) << 8) |
707 (GBView8(cpu, address + 2, segment) << 16) | (GBView8(cpu, address + 3, segment) << 24);
708}
709
710static void _GBCoreRawWrite8(struct mCore* core, uint32_t address, int segment, uint8_t value) {
711 struct SM83Core* cpu = core->cpu;
712 GBPatch8(cpu, address, value, NULL, segment);
713}
714
715static void _GBCoreRawWrite16(struct mCore* core, uint32_t address, int segment, uint16_t value) {
716 struct SM83Core* cpu = core->cpu;
717 GBPatch8(cpu, address, value, NULL, segment);
718 GBPatch8(cpu, address + 1, value >> 8, NULL, segment);
719}
720
721static void _GBCoreRawWrite32(struct mCore* core, uint32_t address, int segment, uint32_t value) {
722 struct SM83Core* cpu = core->cpu;
723 GBPatch8(cpu, address, value, NULL, segment);
724 GBPatch8(cpu, address + 1, value >> 8, NULL, segment);
725 GBPatch8(cpu, address + 2, value >> 16, NULL, segment);
726 GBPatch8(cpu, address + 3, value >> 24, NULL, segment);
727}
728
729size_t _GBListMemoryBlocks(const struct mCore* core, const struct mCoreMemoryBlock** blocks) {
730 const struct GB* gb = core->board;
731 switch (gb->model) {
732 case GB_MODEL_DMG:
733 case GB_MODEL_MGB:
734 case GB_MODEL_SGB:
735 case GB_MODEL_SGB2:
736 default:
737 *blocks = _GBMemoryBlocks;
738 return sizeof(_GBMemoryBlocks) / sizeof(*_GBMemoryBlocks);
739 case GB_MODEL_CGB:
740 case GB_MODEL_AGB:
741 *blocks = _GBCMemoryBlocks;
742 return sizeof(_GBCMemoryBlocks) / sizeof(*_GBCMemoryBlocks);
743 }
744}
745
746void* _GBGetMemoryBlock(struct mCore* core, size_t id, size_t* sizeOut) {
747 struct GB* gb = core->board;
748 bool isCgb = gb->model >= GB_MODEL_CGB;
749 switch (id) {
750 default:
751 return NULL;
752 case GB_REGION_CART_BANK0:
753 *sizeOut = gb->memory.romSize;
754 return gb->memory.rom;
755 case GB_REGION_VRAM:
756 *sizeOut = GB_SIZE_WORKING_RAM_BANK0 * (isCgb ? 1 : 2);
757 return gb->video.vram;
758 case GB_REGION_EXTERNAL_RAM:
759 *sizeOut = gb->sramSize;
760 return gb->memory.sram;
761 case GB_REGION_WORKING_RAM_BANK0:
762 *sizeOut = GB_SIZE_VRAM * (isCgb ? 8 : 2);
763 return gb->memory.wram;
764 case GB_BASE_OAM:
765 *sizeOut = GB_SIZE_OAM;
766 return gb->video.oam.raw;
767 case GB_BASE_HRAM:
768 *sizeOut = GB_SIZE_HRAM;
769 return gb->memory.hram;
770 }
771}
772
773#ifdef USE_DEBUGGERS
774static bool _GBCoreSupportsDebuggerType(struct mCore* core, enum mDebuggerType type) {
775 UNUSED(core);
776 switch (type) {
777 case DEBUGGER_CUSTOM:
778 case DEBUGGER_CLI:
779 return true;
780 default:
781 return false;
782 }
783}
784
785static struct mDebuggerPlatform* _GBCoreDebuggerPlatform(struct mCore* core) {
786 struct GBCore* gbcore = (struct GBCore*) core;
787 struct GB* gb = core->board;
788 if (!gbcore->debuggerPlatform) {
789 struct SM83Debugger* platform = (struct SM83Debugger*) GBDebuggerCreate(gb);
790 gbcore->debuggerPlatform = &platform->d;
791 }
792 return gbcore->debuggerPlatform;
793}
794
795static struct CLIDebuggerSystem* _GBCoreCliDebuggerSystem(struct mCore* core) {
796 return GBCLIDebuggerCreate(core);
797}
798
799static void _GBCoreAttachDebugger(struct mCore* core, struct mDebugger* debugger) {
800 struct SM83Core* cpu = core->cpu;
801 if (core->debugger) {
802 SM83HotplugDetach(cpu, CPU_COMPONENT_DEBUGGER);
803 }
804 cpu->components[CPU_COMPONENT_DEBUGGER] = &debugger->d;
805 SM83HotplugAttach(cpu, CPU_COMPONENT_DEBUGGER);
806 core->debugger = debugger;
807}
808
809static void _GBCoreDetachDebugger(struct mCore* core) {
810 struct SM83Core* cpu = core->cpu;
811 if (core->debugger) {
812 SM83HotplugDetach(cpu, CPU_COMPONENT_DEBUGGER);
813 }
814 cpu->components[CPU_COMPONENT_DEBUGGER] = NULL;
815 core->debugger = NULL;
816}
817
818static void _GBCoreLoadSymbols(struct mCore* core, struct VFile* vf) {
819 core->symbolTable = mDebuggerSymbolTableCreate();
820#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
821 if (!vf) {
822 vf = mDirectorySetOpenSuffix(&core->dirs, core->dirs.base, ".sym", O_RDONLY);
823 }
824#endif
825 if (!vf) {
826 return;
827 }
828 GBLoadSymbols(core->symbolTable, vf);
829}
830
831static bool _GBCoreLookupIdentifier(struct mCore* core, const char* name, int32_t* value, int* segment) {
832 UNUSED(core);
833 *segment = -1;
834 int i;
835 for (i = 0; i < REG_MAX; ++i) {
836 const char* reg = GBIORegisterNames[i];
837 if (reg && strcasecmp(reg, name) == 0) {
838 *value = GB_BASE_IO | i;
839 return true;
840 }
841 }
842 return false;
843}
844#endif
845
846static struct mCheatDevice* _GBCoreCheatDevice(struct mCore* core) {
847 struct GBCore* gbcore = (struct GBCore*) core;
848 if (!gbcore->cheatDevice) {
849 gbcore->cheatDevice = GBCheatDeviceCreate();
850 ((struct SM83Core*) core->cpu)->components[CPU_COMPONENT_CHEAT_DEVICE] = &gbcore->cheatDevice->d;
851 SM83HotplugAttach(core->cpu, CPU_COMPONENT_CHEAT_DEVICE);
852 gbcore->cheatDevice->p = core;
853 }
854 return gbcore->cheatDevice;
855}
856
857static size_t _GBCoreSavedataClone(struct mCore* core, void** sram) {
858 struct GB* gb = core->board;
859 struct VFile* vf = gb->sramVf;
860 if (vf) {
861 *sram = malloc(vf->size(vf));
862 vf->seek(vf, 0, SEEK_SET);
863 return vf->read(vf, *sram, vf->size(vf));
864 }
865 if (gb->sramSize) {
866 *sram = malloc(gb->sramSize);
867 memcpy(*sram, gb->memory.sram, gb->sramSize);
868 return gb->sramSize;
869 }
870 *sram = NULL;
871 return 0;
872}
873
874static bool _GBCoreSavedataRestore(struct mCore* core, const void* sram, size_t size, bool writeback) {
875 struct GB* gb = core->board;
876 if (!writeback) {
877 struct VFile* vf = VFileMemChunk(sram, size);
878 GBSavedataMask(gb, vf, true);
879 return true;
880 }
881 struct VFile* vf = gb->sramVf;
882 if (vf) {
883 vf->seek(vf, 0, SEEK_SET);
884 return vf->write(vf, sram, size) > 0;
885 }
886 if (size > 0x20000) {
887 size = 0x20000;
888 }
889 GBResizeSram(gb, size);
890 memcpy(gb->memory.sram, sram, size);
891 return true;
892}
893
894static size_t _GBCoreListVideoLayers(const struct mCore* core, const struct mCoreChannelInfo** info) {
895 UNUSED(core);
896 if (info) {
897 *info = _GBVideoLayers;
898 }
899 return sizeof(_GBVideoLayers) / sizeof(*_GBVideoLayers);
900}
901
902static size_t _GBCoreListAudioChannels(const struct mCore* core, const struct mCoreChannelInfo** info) {
903 UNUSED(core);
904 if (info) {
905 *info = _GBAudioChannels;
906 }
907 return sizeof(_GBAudioChannels) / sizeof(*_GBAudioChannels);
908}
909
910static void _GBCoreEnableVideoLayer(struct mCore* core, size_t id, bool enable) {
911 struct GB* gb = core->board;
912 switch (id) {
913 case 0:
914 gb->video.renderer->disableBG = !enable;
915 break;
916 case 1:
917 gb->video.renderer->disableWIN = !enable;
918 break;
919 case 2:
920 gb->video.renderer->disableOBJ = !enable;
921 break;
922 default:
923 break;
924 }
925}
926
927static void _GBCoreEnableAudioChannel(struct mCore* core, size_t id, bool enable) {
928 struct GB* gb = core->board;
929 switch (id) {
930 case 0:
931 case 1:
932 case 2:
933 case 3:
934 gb->audio.forceDisableCh[id] = !enable;
935 break;
936 default:
937 break;
938 }
939}
940
941static void _GBCoreAdjustVideoLayer(struct mCore* core, size_t id, int32_t x, int32_t y) {
942 struct GBCore* gbcore = (struct GBCore*) core;
943 switch (id) {
944 case 0:
945 gbcore->renderer.offsetScx = x;
946 gbcore->renderer.offsetScy = y;
947 break;
948 case 1:
949 gbcore->renderer.offsetWx = x;
950 gbcore->renderer.offsetWy = y;
951 break;
952 case 2:
953 gbcore->renderer.objOffsetX = x;
954 gbcore->renderer.objOffsetY = y;
955 break;
956 default:
957 return;
958 }
959}
960
961#ifndef MINIMAL_CORE
962static void _GBCoreStartVideoLog(struct mCore* core, struct mVideoLogContext* context) {
963 struct GBCore* gbcore = (struct GBCore*) core;
964 struct GB* gb = core->board;
965 gbcore->logContext = context;
966
967 int channelId = mVideoLoggerAddChannel(context);
968 gbcore->proxyRenderer.logger = malloc(sizeof(struct mVideoLogger));
969 mVideoLoggerRendererCreate(gbcore->proxyRenderer.logger, false);
970 mVideoLoggerAttachChannel(gbcore->proxyRenderer.logger, context, channelId);
971 gbcore->proxyRenderer.logger->block = false;
972
973 GBVideoProxyRendererCreate(&gbcore->proxyRenderer, &gbcore->renderer.d);
974 GBVideoProxyRendererShim(&gb->video, &gbcore->proxyRenderer);
975}
976
977static void _GBCoreEndVideoLog(struct mCore* core) {
978 struct GBCore* gbcore = (struct GBCore*) core;
979 struct GB* gb = core->board;
980 if (gbcore->proxyRenderer.logger) {
981 GBVideoProxyRendererUnshim(&gb->video, &gbcore->proxyRenderer);
982 free(gbcore->proxyRenderer.logger);
983 gbcore->proxyRenderer.logger = NULL;
984 }
985}
986#endif
987
988struct mCore* GBCoreCreate(void) {
989 struct GBCore* gbcore = malloc(sizeof(*gbcore));
990 struct mCore* core = &gbcore->d;
991 memset(&core->opts, 0, sizeof(core->opts));
992 core->cpu = NULL;
993 core->board = NULL;
994 core->debugger = NULL;
995 core->symbolTable = NULL;
996 core->init = _GBCoreInit;
997 core->deinit = _GBCoreDeinit;
998 core->platform = _GBCorePlatform;
999 core->supportsFeature = _GBCoreSupportsFeature;
1000 core->setSync = _GBCoreSetSync;
1001 core->loadConfig = _GBCoreLoadConfig;
1002 core->reloadConfigOption = _GBCoreReloadConfigOption;
1003 core->desiredVideoDimensions = _GBCoreDesiredVideoDimensions;
1004 core->setVideoBuffer = _GBCoreSetVideoBuffer;
1005 core->setVideoGLTex = _GBCoreSetVideoGLTex;
1006 core->getPixels = _GBCoreGetPixels;
1007 core->putPixels = _GBCorePutPixels;
1008 core->getAudioChannel = _GBCoreGetAudioChannel;
1009 core->setAudioBufferSize = _GBCoreSetAudioBufferSize;
1010 core->getAudioBufferSize = _GBCoreGetAudioBufferSize;
1011 core->setAVStream = _GBCoreSetAVStream;
1012 core->addCoreCallbacks = _GBCoreAddCoreCallbacks;
1013 core->clearCoreCallbacks = _GBCoreClearCoreCallbacks;
1014 core->isROM = GBIsROM;
1015 core->loadROM = _GBCoreLoadROM;
1016 core->loadBIOS = _GBCoreLoadBIOS;
1017 core->loadSave = _GBCoreLoadSave;
1018 core->loadTemporarySave = _GBCoreLoadTemporarySave;
1019 core->loadPatch = _GBCoreLoadPatch;
1020 core->unloadROM = _GBCoreUnloadROM;
1021 core->checksum = _GBCoreChecksum;
1022 core->reset = _GBCoreReset;
1023 core->runFrame = _GBCoreRunFrame;
1024 core->runLoop = _GBCoreRunLoop;
1025 core->step = _GBCoreStep;
1026 core->stateSize = _GBCoreStateSize;
1027 core->loadState = _GBCoreLoadState;
1028 core->saveState = _GBCoreSaveState;
1029 core->setKeys = _GBCoreSetKeys;
1030 core->addKeys = _GBCoreAddKeys;
1031 core->clearKeys = _GBCoreClearKeys;
1032 core->frameCounter = _GBCoreFrameCounter;
1033 core->frameCycles = _GBCoreFrameCycles;
1034 core->frequency = _GBCoreFrequency;
1035 core->getGameTitle = _GBCoreGetGameTitle;
1036 core->getGameCode = _GBCoreGetGameCode;
1037 core->setPeripheral = _GBCoreSetPeripheral;
1038 core->busRead8 = _GBCoreBusRead8;
1039 core->busRead16 = _GBCoreBusRead16;
1040 core->busRead32 = _GBCoreBusRead32;
1041 core->busWrite8 = _GBCoreBusWrite8;
1042 core->busWrite16 = _GBCoreBusWrite16;
1043 core->busWrite32 = _GBCoreBusWrite32;
1044 core->rawRead8 = _GBCoreRawRead8;
1045 core->rawRead16 = _GBCoreRawRead16;
1046 core->rawRead32 = _GBCoreRawRead32;
1047 core->rawWrite8 = _GBCoreRawWrite8;
1048 core->rawWrite16 = _GBCoreRawWrite16;
1049 core->rawWrite32 = _GBCoreRawWrite32;
1050 core->listMemoryBlocks = _GBListMemoryBlocks;
1051 core->getMemoryBlock = _GBGetMemoryBlock;
1052#ifdef USE_DEBUGGERS
1053 core->supportsDebuggerType = _GBCoreSupportsDebuggerType;
1054 core->debuggerPlatform = _GBCoreDebuggerPlatform;
1055 core->cliDebuggerSystem = _GBCoreCliDebuggerSystem;
1056 core->attachDebugger = _GBCoreAttachDebugger;
1057 core->detachDebugger = _GBCoreDetachDebugger;
1058 core->loadSymbols = _GBCoreLoadSymbols;
1059 core->lookupIdentifier = _GBCoreLookupIdentifier;
1060#endif
1061 core->cheatDevice = _GBCoreCheatDevice;
1062 core->savedataClone = _GBCoreSavedataClone;
1063 core->savedataRestore = _GBCoreSavedataRestore;
1064 core->listVideoLayers = _GBCoreListVideoLayers;
1065 core->listAudioChannels = _GBCoreListAudioChannels;
1066 core->enableVideoLayer = _GBCoreEnableVideoLayer;
1067 core->enableAudioChannel = _GBCoreEnableAudioChannel;
1068 core->adjustVideoLayer = _GBCoreAdjustVideoLayer;
1069#ifndef MINIMAL_CORE
1070 core->startVideoLog = _GBCoreStartVideoLog;
1071 core->endVideoLog = _GBCoreEndVideoLog;
1072#endif
1073 return core;
1074}
1075
1076#ifndef MINIMAL_CORE
1077static void _GBVLPStartFrameCallback(void *context) {
1078 struct mCore* core = context;
1079 struct GBCore* gbcore = (struct GBCore*) core;
1080 struct GB* gb = core->board;
1081
1082 if (!mVideoLoggerRendererRun(gbcore->proxyRenderer.logger, true)) {
1083 GBVideoProxyRendererUnshim(&gb->video, &gbcore->proxyRenderer);
1084 mVideoLogContextRewind(gbcore->logContext, core);
1085 GBVideoProxyRendererShim(&gb->video, &gbcore->proxyRenderer);
1086 gb->earlyExit = true;
1087 }
1088}
1089
1090static bool _GBVLPInit(struct mCore* core) {
1091 struct GBCore* gbcore = (struct GBCore*) core;
1092 if (!_GBCoreInit(core)) {
1093 return false;
1094 }
1095 gbcore->proxyRenderer.logger = malloc(sizeof(struct mVideoLogger));
1096 mVideoLoggerRendererCreate(gbcore->proxyRenderer.logger, true);
1097 GBVideoProxyRendererCreate(&gbcore->proxyRenderer, NULL);
1098 memset(&gbcore->logCallbacks, 0, sizeof(gbcore->logCallbacks));
1099 gbcore->logCallbacks.videoFrameStarted = _GBVLPStartFrameCallback;
1100 gbcore->logCallbacks.context = core;
1101 core->addCoreCallbacks(core, &gbcore->logCallbacks);
1102 core->videoLogger = gbcore->proxyRenderer.logger;
1103 return true;
1104}
1105
1106static void _GBVLPDeinit(struct mCore* core) {
1107 struct GBCore* gbcore = (struct GBCore*) core;
1108 if (gbcore->logContext) {
1109 mVideoLogContextDestroy(core, gbcore->logContext, true);
1110 }
1111 _GBCoreDeinit(core);
1112}
1113
1114static void _GBVLPReset(struct mCore* core) {
1115 struct GBCore* gbcore = (struct GBCore*) core;
1116 struct GB* gb = (struct GB*) core->board;
1117 if (gb->video.renderer == &gbcore->proxyRenderer.d) {
1118 GBVideoProxyRendererUnshim(&gb->video, &gbcore->proxyRenderer);
1119 } else if (gbcore->renderer.outputBuffer) {
1120 struct GBVideoRenderer* renderer = &gbcore->renderer.d;
1121 GBVideoAssociateRenderer(&gb->video, renderer);
1122 }
1123
1124 SM83Reset(core->cpu);
1125 mVideoLogContextRewind(gbcore->logContext, core);
1126 GBVideoProxyRendererShim(&gb->video, &gbcore->proxyRenderer);
1127
1128 // Make sure CPU loop never spins
1129 gb->memory.ie = 0;
1130 gb->memory.ime = false;
1131 GBHalt(gb->cpu);
1132}
1133
1134static bool _GBVLPLoadROM(struct mCore* core, struct VFile* vf) {
1135 struct GBCore* gbcore = (struct GBCore*) core;
1136 gbcore->logContext = mVideoLogContextCreate(NULL);
1137 if (!mVideoLogContextLoad(gbcore->logContext, vf)) {
1138 mVideoLogContextDestroy(core, gbcore->logContext, false);
1139 gbcore->logContext = NULL;
1140 return false;
1141 }
1142 mVideoLoggerAttachChannel(gbcore->proxyRenderer.logger, gbcore->logContext, 0);
1143 return true;
1144}
1145
1146static bool _GBVLPLoadState(struct mCore* core, const void* buffer) {
1147 struct GB* gb = (struct GB*) core->board;
1148 const struct GBSerializedState* state = buffer;
1149
1150 gb->timing.root = NULL;
1151 gb->model = state->model;
1152
1153 gb->cpu->pc = GB_BASE_HRAM;
1154 gb->cpu->memory.setActiveRegion(gb->cpu, gb->cpu->pc);
1155
1156 GBVideoReset(&gb->video);
1157 GBVideoDeserialize(&gb->video, state);
1158 GBIODeserialize(gb, state);
1159 GBAudioReset(&gb->audio);
1160
1161 // Make sure CPU loop never spins
1162 gb->memory.ie = 0;
1163 gb->memory.ime = false;
1164 GBHalt(gb->cpu);
1165
1166 return true;
1167}
1168
1169static bool _returnTrue(struct VFile* vf) {
1170 UNUSED(vf);
1171 return true;
1172}
1173
1174struct mCore* GBVideoLogPlayerCreate(void) {
1175 struct mCore* core = GBCoreCreate();
1176 core->init = _GBVLPInit;
1177 core->deinit = _GBVLPDeinit;
1178 core->reset = _GBVLPReset;
1179 core->loadROM = _GBVLPLoadROM;
1180 core->loadState = _GBVLPLoadState;
1181 core->isROM = _returnTrue;
1182 return core;
1183}
1184#else
1185struct mCore* GBVideoLogPlayerCreate(void) {
1186 return false;
1187}
1188#endif