src/gba/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/gba/core.h>
7
8#include <mgba/core/core.h>
9#include <mgba/core/log.h>
10#include <mgba/internal/arm/debugger/debugger.h>
11#include <mgba/internal/gba/cheats.h>
12#include <mgba/internal/gba/gba.h>
13#include <mgba/internal/gba/io.h>
14#include <mgba/internal/gba/extra/cli.h>
15#include <mgba/internal/gba/overrides.h>
16#ifndef DISABLE_THREADING
17#include <mgba/feature/thread-proxy.h>
18#endif
19#include <mgba/internal/gba/renderers/proxy.h>
20#include <mgba/internal/gba/renderers/video-software.h>
21#include <mgba/internal/gba/savedata.h>
22#include <mgba/internal/gba/serialize.h>
23#include <mgba-util/memory.h>
24#include <mgba-util/patch.h>
25#include <mgba-util/vfs.h>
26
27const static struct mCoreChannelInfo _GBAVideoLayers[] = {
28 { 0, "bg0", "Background 0", NULL },
29 { 1, "bg1", "Background 1", NULL },
30 { 2, "bg2", "Background 2", NULL },
31 { 3, "bg3", "Background 3", NULL },
32 { 4, "obj", "Objects", NULL },
33};
34
35const static struct mCoreChannelInfo _GBAAudioChannels[] = {
36 { 0, "ch0", "PSG Channel 0", "Square/Sweep" },
37 { 1, "ch1", "PSG Channel 1", "Square" },
38 { 2, "ch2", "PSG Channel 2", "PCM" },
39 { 3, "ch3", "PSG Channel 3", "Noise" },
40 { 4, "chA", "FIFO Channel A", NULL },
41 { 5, "chB", "FIFO Channel B", NULL },
42};
43
44struct mVideoLogContext;
45struct GBACore {
46 struct mCore d;
47 struct GBAVideoSoftwareRenderer renderer;
48 struct GBAVideoProxyRenderer proxyRenderer;
49 struct mVideoLogContext* logContext;
50 struct mCoreCallbacks logCallbacks;
51#ifndef DISABLE_THREADING
52 struct mVideoThreadProxy threadProxy;
53 int threadedVideo;
54#endif
55 int keys;
56 struct mCPUComponent* components[CPU_COMPONENT_MAX];
57 const struct Configuration* overrides;
58 struct mDebuggerPlatform* debuggerPlatform;
59 struct mCheatDevice* cheatDevice;
60};
61
62static bool _GBACoreInit(struct mCore* core) {
63 struct GBACore* gbacore = (struct GBACore*) core;
64
65 struct ARMCore* cpu = anonymousMemoryMap(sizeof(struct ARMCore));
66 struct GBA* gba = anonymousMemoryMap(sizeof(struct GBA));
67 if (!cpu || !gba) {
68 free(cpu);
69 free(gba);
70 return false;
71 }
72 core->cpu = cpu;
73 core->board = gba;
74 core->debugger = NULL;
75 core->symbolTable = NULL;
76 gbacore->overrides = NULL;
77 gbacore->debuggerPlatform = NULL;
78 gbacore->cheatDevice = NULL;
79 gbacore->logContext = NULL;
80
81 GBACreate(gba);
82 // TODO: Restore cheats
83 memset(gbacore->components, 0, sizeof(gbacore->components));
84 ARMSetComponents(cpu, &gba->d, CPU_COMPONENT_MAX, gbacore->components);
85 ARMInit(cpu);
86 mRTCGenericSourceInit(&core->rtc, core);
87 gba->rtcSource = &core->rtc.d;
88
89 GBAVideoSoftwareRendererCreate(&gbacore->renderer);
90 gbacore->renderer.outputBuffer = NULL;
91
92#ifndef DISABLE_THREADING
93 gbacore->threadedVideo = false;
94 mVideoThreadProxyCreate(&gbacore->threadProxy);
95#endif
96 gbacore->proxyRenderer.logger = NULL;
97
98 gbacore->keys = 0;
99 gba->keySource = &gbacore->keys;
100
101#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
102 mDirectorySetInit(&core->dirs);
103#endif
104
105 return true;
106}
107
108static void _GBACoreDeinit(struct mCore* core) {
109 ARMDeinit(core->cpu);
110 GBADestroy(core->board);
111 mappedMemoryFree(core->cpu, sizeof(struct ARMCore));
112 mappedMemoryFree(core->board, sizeof(struct GBA));
113#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
114 mDirectorySetDeinit(&core->dirs);
115#endif
116
117 struct GBACore* gbacore = (struct GBACore*) core;
118 free(gbacore->debuggerPlatform);
119 if (gbacore->cheatDevice) {
120 mCheatDeviceDestroy(gbacore->cheatDevice);
121 }
122 free(gbacore->cheatDevice);
123 mCoreConfigFreeOpts(&core->opts);
124 free(core);
125}
126
127static enum mPlatform _GBACorePlatform(const struct mCore* core) {
128 UNUSED(core);
129 return PLATFORM_GBA;
130}
131
132static void _GBACoreSetSync(struct mCore* core, struct mCoreSync* sync) {
133 struct GBA* gba = core->board;
134 gba->sync = sync;
135}
136
137static void _GBACoreLoadConfig(struct mCore* core, const struct mCoreConfig* config) {
138 struct GBA* gba = core->board;
139 if (core->opts.mute) {
140 gba->audio.masterVolume = 0;
141 } else {
142 gba->audio.masterVolume = core->opts.volume;
143 }
144 gba->video.frameskip = core->opts.frameskip;
145
146#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
147 struct GBACore* gbacore = (struct GBACore*) core;
148 gbacore->overrides = mCoreConfigGetOverridesConst(config);
149#endif
150
151 const char* idleOptimization = mCoreConfigGetValue(config, "idleOptimization");
152 if (idleOptimization) {
153 if (strcasecmp(idleOptimization, "ignore") == 0) {
154 gba->idleOptimization = IDLE_LOOP_IGNORE;
155 } else if (strcasecmp(idleOptimization, "remove") == 0) {
156 gba->idleOptimization = IDLE_LOOP_REMOVE;
157 } else if (strcasecmp(idleOptimization, "detect") == 0) {
158 if (gba->idleLoop == IDLE_LOOP_NONE) {
159 gba->idleOptimization = IDLE_LOOP_DETECT;
160 } else {
161 gba->idleOptimization = IDLE_LOOP_REMOVE;
162 }
163 }
164 }
165
166 mCoreConfigCopyValue(&core->config, config, "gba.bios");
167
168#ifndef DISABLE_THREADING
169 mCoreConfigGetIntValue(config, "threadedVideo", &gbacore->threadedVideo);
170#endif
171}
172
173static void _GBACoreDesiredVideoDimensions(struct mCore* core, unsigned* width, unsigned* height) {
174 UNUSED(core);
175 *width = VIDEO_HORIZONTAL_PIXELS;
176 *height = VIDEO_VERTICAL_PIXELS;
177}
178
179static void _GBACoreSetVideoBuffer(struct mCore* core, color_t* buffer, size_t stride) {
180 struct GBACore* gbacore = (struct GBACore*) core;
181 gbacore->renderer.outputBuffer = buffer;
182 gbacore->renderer.outputBufferStride = stride;
183}
184
185static void _GBACoreGetPixels(struct mCore* core, const void** buffer, size_t* stride) {
186 struct GBACore* gbacore = (struct GBACore*) core;
187 gbacore->renderer.d.getPixels(&gbacore->renderer.d, stride, buffer);
188}
189
190static void _GBACorePutPixels(struct mCore* core, const void* buffer, size_t stride) {
191 struct GBACore* gbacore = (struct GBACore*) core;
192 gbacore->renderer.d.putPixels(&gbacore->renderer.d, stride, buffer);
193}
194
195static struct blip_t* _GBACoreGetAudioChannel(struct mCore* core, int ch) {
196 struct GBA* gba = core->board;
197 switch (ch) {
198 case 0:
199 return gba->audio.psg.left;
200 case 1:
201 return gba->audio.psg.right;
202 default:
203 return NULL;
204 }
205}
206
207static void _GBACoreSetAudioBufferSize(struct mCore* core, size_t samples) {
208 struct GBA* gba = core->board;
209 GBAAudioResizeBuffer(&gba->audio, samples);
210}
211
212static size_t _GBACoreGetAudioBufferSize(struct mCore* core) {
213 struct GBA* gba = core->board;
214 return gba->audio.samples;
215}
216
217static void _GBACoreAddCoreCallbacks(struct mCore* core, struct mCoreCallbacks* coreCallbacks) {
218 struct GBA* gba = core->board;
219 *mCoreCallbacksListAppend(&gba->coreCallbacks) = *coreCallbacks;
220}
221
222static void _GBACoreClearCoreCallbacks(struct mCore* core) {
223 struct GBA* gba = core->board;
224 mCoreCallbacksListClear(&gba->coreCallbacks);
225}
226
227static void _GBACoreSetAVStream(struct mCore* core, struct mAVStream* stream) {
228 struct GBA* gba = core->board;
229 gba->stream = stream;
230 if (stream && stream->videoDimensionsChanged) {
231 stream->videoDimensionsChanged(stream, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
232 }
233}
234
235static bool _GBACoreLoadROM(struct mCore* core, struct VFile* vf) {
236 if (GBAIsMB(vf)) {
237 return GBALoadMB(core->board, vf);
238 }
239 return GBALoadROM(core->board, vf);
240}
241
242static bool _GBACoreLoadBIOS(struct mCore* core, struct VFile* vf, int type) {
243 UNUSED(type);
244 if (!GBAIsBIOS(vf)) {
245 return false;
246 }
247 GBALoadBIOS(core->board, vf);
248 return true;
249}
250
251static bool _GBACoreLoadSave(struct mCore* core, struct VFile* vf) {
252 return GBALoadSave(core->board, vf);
253}
254
255static bool _GBACoreLoadTemporarySave(struct mCore* core, struct VFile* vf) {
256 struct GBA* gba = core->board;
257 GBASavedataMask(&gba->memory.savedata, vf, false);
258 return true; // TODO: Return a real value
259}
260
261static bool _GBACoreLoadPatch(struct mCore* core, struct VFile* vf) {
262 if (!vf) {
263 return false;
264 }
265 struct Patch patch;
266 if (!loadPatch(vf, &patch)) {
267 return false;
268 }
269 GBAApplyPatch(core->board, &patch);
270 return true;
271}
272
273static void _GBACoreUnloadROM(struct mCore* core) {
274 struct GBACore* gbacore = (struct GBACore*) core;
275 struct ARMCore* cpu = core->cpu;
276 if (gbacore->cheatDevice) {
277 ARMHotplugDetach(cpu, CPU_COMPONENT_CHEAT_DEVICE);
278 cpu->components[CPU_COMPONENT_CHEAT_DEVICE] = NULL;
279 mCheatDeviceDestroy(gbacore->cheatDevice);
280 gbacore->cheatDevice = NULL;
281 }
282 return GBAUnloadROM(core->board);
283}
284
285static void _GBACoreChecksum(const struct mCore* core, void* data, enum mCoreChecksumType type) {
286 struct GBA* gba = (struct GBA*) core->board;
287 switch (type) {
288 case CHECKSUM_CRC32:
289 memcpy(data, &gba->romCrc32, sizeof(gba->romCrc32));
290 break;
291 }
292 return;
293}
294
295static void _GBACoreReset(struct mCore* core) {
296 struct GBACore* gbacore = (struct GBACore*) core;
297 struct GBA* gba = (struct GBA*) core->board;
298 if (gbacore->renderer.outputBuffer) {
299 struct GBAVideoRenderer* renderer = &gbacore->renderer.d;
300#ifndef DISABLE_THREADING
301 if (gbacore->threadedVideo) {
302 gbacore->proxyRenderer.logger = &gbacore->threadProxy.d;
303 GBAVideoProxyRendererCreate(&gbacore->proxyRenderer, renderer);
304 renderer = &gbacore->proxyRenderer.d;
305 }
306#endif
307 GBAVideoAssociateRenderer(&gba->video, renderer);
308 }
309
310 struct GBACartridgeOverride override;
311 const struct GBACartridge* cart = (const struct GBACartridge*) gba->memory.rom;
312 if (cart) {
313 memcpy(override.id, &cart->id, sizeof(override.id));
314 if (GBAOverrideFind(gbacore->overrides, &override)) {
315 GBAOverrideApply(gba, &override);
316 }
317 }
318
319#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
320 if (!gba->biosVf && core->opts.useBios) {
321 struct VFile* bios = NULL;
322 bool found = false;
323 if (core->opts.bios) {
324 bios = VFileOpen(core->opts.bios, O_RDONLY);
325 if (bios && GBAIsBIOS(bios)) {
326 found = true;
327 } else if (bios) {
328 bios->close(bios);
329 bios = NULL;
330 }
331 }
332 if (!found) {
333 const char* configPath = mCoreConfigGetValue(&core->config, "gba.bios");
334 if (configPath) {
335 bios = VFileOpen(configPath, O_RDONLY);
336 }
337 if (bios && GBAIsBIOS(bios)) {
338 found = true;
339 } else if (bios) {
340 bios->close(bios);
341 bios = NULL;
342 }
343 }
344 if (!found) {
345 char path[PATH_MAX];
346 mCoreConfigDirectory(path, PATH_MAX);
347 strncat(path, PATH_SEP "gba_bios.bin", PATH_MAX - strlen(path));
348 bios = VFileOpen(path, O_RDONLY);
349 if (bios && GBAIsBIOS(bios)) {
350 found = true;
351 } else if (bios) {
352 bios->close(bios);
353 bios = NULL;
354 }
355 }
356 if (bios) {
357 GBALoadBIOS(gba, bios);
358 }
359 }
360#endif
361
362 ARMReset(core->cpu);
363 if (core->opts.skipBios && gba->isPristine) {
364 GBASkipBIOS(core->board);
365 }
366}
367
368static void _GBACoreRunFrame(struct mCore* core) {
369 struct GBA* gba = core->board;
370 int32_t frameCounter = gba->video.frameCounter;
371 while (gba->video.frameCounter == frameCounter) {
372 ARMRunLoop(core->cpu);
373 }
374}
375
376static void _GBACoreRunLoop(struct mCore* core) {
377 ARMRunLoop(core->cpu);
378}
379
380static void _GBACoreStep(struct mCore* core) {
381 ARMRun(core->cpu);
382}
383
384static size_t _GBACoreStateSize(struct mCore* core) {
385 UNUSED(core);
386 return sizeof(struct GBASerializedState);
387}
388
389static bool _GBACoreLoadState(struct mCore* core, const void* state) {
390 return GBADeserialize(core->board, state);
391}
392
393static bool _GBACoreSaveState(struct mCore* core, void* state) {
394 GBASerialize(core->board, state);
395 return true;
396}
397
398static void _GBACoreSetKeys(struct mCore* core, uint32_t keys) {
399 struct GBACore* gbacore = (struct GBACore*) core;
400 gbacore->keys = keys;
401}
402
403static void _GBACoreAddKeys(struct mCore* core, uint32_t keys) {
404 struct GBACore* gbacore = (struct GBACore*) core;
405 gbacore->keys |= keys;
406}
407
408static void _GBACoreClearKeys(struct mCore* core, uint32_t keys) {
409 struct GBACore* gbacore = (struct GBACore*) core;
410 gbacore->keys &= ~keys;
411}
412
413static int32_t _GBACoreFrameCounter(const struct mCore* core) {
414 const struct GBA* gba = core->board;
415 return gba->video.frameCounter;
416}
417
418static int32_t _GBACoreFrameCycles(const struct mCore* core) {
419 UNUSED(core);
420 return VIDEO_TOTAL_LENGTH;
421}
422
423static int32_t _GBACoreFrequency(const struct mCore* core) {
424 UNUSED(core);
425 return GBA_ARM7TDMI_FREQUENCY;
426}
427
428static void _GBACoreGetGameTitle(const struct mCore* core, char* title) {
429 GBAGetGameTitle(core->board, title);
430}
431
432static void _GBACoreGetGameCode(const struct mCore* core, char* title) {
433 GBAGetGameCode(core->board, title);
434}
435
436static void _GBACoreSetPeripheral(struct mCore* core, int type, void* periph) {
437 struct GBA* gba = core->board;
438 switch (type) {
439 case mPERIPH_ROTATION:
440 gba->rotationSource = periph;
441 break;
442 case mPERIPH_RUMBLE:
443 gba->rumble = periph;
444 break;
445 case mPERIPH_GBA_LUMINANCE:
446 gba->luminanceSource = periph;
447 break;
448 default:
449 return;
450 }
451}
452
453static uint32_t _GBACoreBusRead8(struct mCore* core, uint32_t address) {
454 struct ARMCore* cpu = core->cpu;
455 return cpu->memory.load8(cpu, address, 0);
456}
457
458static uint32_t _GBACoreBusRead16(struct mCore* core, uint32_t address) {
459 struct ARMCore* cpu = core->cpu;
460 return cpu->memory.load16(cpu, address, 0);
461
462}
463
464static uint32_t _GBACoreBusRead32(struct mCore* core, uint32_t address) {
465 struct ARMCore* cpu = core->cpu;
466 return cpu->memory.load32(cpu, address, 0);
467}
468
469static void _GBACoreBusWrite8(struct mCore* core, uint32_t address, uint8_t value) {
470 struct ARMCore* cpu = core->cpu;
471 cpu->memory.store8(cpu, address, value, 0);
472}
473
474static void _GBACoreBusWrite16(struct mCore* core, uint32_t address, uint16_t value) {
475 struct ARMCore* cpu = core->cpu;
476 cpu->memory.store16(cpu, address, value, 0);
477}
478
479static void _GBACoreBusWrite32(struct mCore* core, uint32_t address, uint32_t value) {
480 struct ARMCore* cpu = core->cpu;
481 cpu->memory.store32(cpu, address, value, 0);
482}
483
484static uint32_t _GBACoreRawRead8(struct mCore* core, uint32_t address, int segment) {
485 UNUSED(segment);
486 struct ARMCore* cpu = core->cpu;
487 return GBAView8(cpu, address);
488}
489
490static uint32_t _GBACoreRawRead16(struct mCore* core, uint32_t address, int segment) {
491 UNUSED(segment);
492 struct ARMCore* cpu = core->cpu;
493 return GBAView16(cpu, address);
494}
495
496static uint32_t _GBACoreRawRead32(struct mCore* core, uint32_t address, int segment) {
497 UNUSED(segment);
498 struct ARMCore* cpu = core->cpu;
499 return GBAView32(cpu, address);
500}
501
502static void _GBACoreRawWrite8(struct mCore* core, uint32_t address, int segment, uint8_t value) {
503 UNUSED(segment);
504 struct ARMCore* cpu = core->cpu;
505 GBAPatch8(cpu, address, value, NULL);
506}
507
508static void _GBACoreRawWrite16(struct mCore* core, uint32_t address, int segment, uint16_t value) {
509 UNUSED(segment);
510 struct ARMCore* cpu = core->cpu;
511 GBAPatch16(cpu, address, value, NULL);
512}
513
514static void _GBACoreRawWrite32(struct mCore* core, uint32_t address, int segment, uint32_t value) {
515 UNUSED(segment);
516 struct ARMCore* cpu = core->cpu;
517 GBAPatch32(cpu, address, value, NULL);
518}
519
520#ifdef USE_DEBUGGERS
521static bool _GBACoreSupportsDebuggerType(struct mCore* core, enum mDebuggerType type) {
522 UNUSED(core);
523 switch (type) {
524 case DEBUGGER_CLI:
525 return true;
526#ifdef USE_GDB_STUB
527 case DEBUGGER_GDB:
528 return true;
529#endif
530 default:
531 return false;
532 }
533}
534
535static struct mDebuggerPlatform* _GBACoreDebuggerPlatform(struct mCore* core) {
536 struct GBACore* gbacore = (struct GBACore*) core;
537 if (!gbacore->debuggerPlatform) {
538 gbacore->debuggerPlatform = ARMDebuggerPlatformCreate();
539 }
540 return gbacore->debuggerPlatform;
541}
542
543static struct CLIDebuggerSystem* _GBACoreCliDebuggerSystem(struct mCore* core) {
544 return &GBACLIDebuggerCreate(core)->d;
545}
546
547static void _GBACoreAttachDebugger(struct mCore* core, struct mDebugger* debugger) {
548 if (core->debugger) {
549 GBADetachDebugger(core->board);
550 }
551 GBAAttachDebugger(core->board, debugger);
552 core->debugger = debugger;
553}
554
555static void _GBACoreDetachDebugger(struct mCore* core) {
556 GBADetachDebugger(core->board);
557 core->debugger = NULL;
558}
559
560static void _GBACoreLoadSymbols(struct mCore* core, struct VFile* vf) {
561 // TODO
562}
563#endif
564
565static struct mCheatDevice* _GBACoreCheatDevice(struct mCore* core) {
566 struct GBACore* gbacore = (struct GBACore*) core;
567 if (!gbacore->cheatDevice) {
568 gbacore->cheatDevice = GBACheatDeviceCreate();
569 ((struct ARMCore*) core->cpu)->components[CPU_COMPONENT_CHEAT_DEVICE] = &gbacore->cheatDevice->d;
570 ARMHotplugAttach(core->cpu, CPU_COMPONENT_CHEAT_DEVICE);
571 gbacore->cheatDevice->p = core;
572 }
573 return gbacore->cheatDevice;
574}
575
576static size_t _GBACoreSavedataClone(struct mCore* core, void** sram) {
577 struct GBA* gba = core->board;
578 size_t size = GBASavedataSize(&gba->memory.savedata);
579 if (!size) {
580 *sram = NULL;
581 return 0;
582 }
583 *sram = malloc(size);
584 struct VFile* vf = VFileFromMemory(*sram, size);
585 if (!vf) {
586 free(*sram);
587 *sram = NULL;
588 return 0;
589 }
590 bool success = GBASavedataClone(&gba->memory.savedata, vf);
591 vf->close(vf);
592 if (!success) {
593 free(*sram);
594 *sram = NULL;
595 return 0;
596 }
597 return size;
598}
599
600static bool _GBACoreSavedataRestore(struct mCore* core, const void* sram, size_t size, bool writeback) {
601 struct VFile* vf = VFileMemChunk(sram, size);
602 if (!vf) {
603 return false;
604 }
605 struct GBA* gba = core->board;
606 bool success = true;
607 if (writeback) {
608 success = GBASavedataLoad(&gba->memory.savedata, vf);
609 vf->close(vf);
610 } else {
611 GBASavedataMask(&gba->memory.savedata, vf, true);
612 }
613 return success;
614}
615
616static size_t _GBACoreListVideoLayers(const struct mCore* core, const struct mCoreChannelInfo** info) {
617 UNUSED(core);
618 *info = _GBAVideoLayers;
619 return sizeof(_GBAVideoLayers) / sizeof(*_GBAVideoLayers);
620}
621
622static size_t _GBACoreListAudioChannels(const struct mCore* core, const struct mCoreChannelInfo** info) {
623 UNUSED(core);
624 *info = _GBAAudioChannels;
625 return sizeof(_GBAAudioChannels) / sizeof(*_GBAAudioChannels);
626}
627
628static void _GBACoreEnableVideoLayer(struct mCore* core, size_t id, bool enable) {
629 struct GBA* gba = core->board;
630 switch (id) {
631 case 0:
632 case 1:
633 case 2:
634 case 3:
635 gba->video.renderer->disableBG[id] = !enable;
636 break;
637 case 4:
638 gba->video.renderer->disableOBJ = !enable;
639 break;
640 default:
641 break;
642 }
643}
644
645static void _GBACoreEnableAudioChannel(struct mCore* core, size_t id, bool enable) {
646 struct GBA* gba = core->board;
647 switch (id) {
648 case 0:
649 case 1:
650 case 2:
651 case 3:
652 gba->audio.psg.forceDisableCh[id] = !enable;
653 break;
654 case 4:
655 gba->audio.forceDisableChA = !enable;
656 case 5:
657 gba->audio.forceDisableChB = !enable;
658 break;
659 default:
660 break;
661 }
662}
663
664static void _GBACoreStartVideoLog(struct mCore* core, struct mVideoLogContext* context) {
665 struct GBACore* gbacore = (struct GBACore*) core;
666 struct GBA* gba = core->board;
667 gbacore->logContext = context;
668
669 struct GBASerializedState* state = mVideoLogContextInitialState(context, NULL);
670 state->id = 0;
671 state->cpu.gprs[ARM_PC] = BASE_WORKING_RAM;
672
673 int channelId = mVideoLoggerAddChannel(context);
674 gbacore->proxyRenderer.logger = malloc(sizeof(struct mVideoLogger));
675 mVideoLoggerRendererCreate(gbacore->proxyRenderer.logger, false);
676 mVideoLoggerAttachChannel(gbacore->proxyRenderer.logger, context, channelId);
677 gbacore->proxyRenderer.logger->block = false;
678
679 GBAVideoProxyRendererCreate(&gbacore->proxyRenderer, &gbacore->renderer.d);
680 GBAVideoProxyRendererShim(&gba->video, &gbacore->proxyRenderer);
681}
682
683static void _GBACoreEndVideoLog(struct mCore* core) {
684 struct GBACore* gbacore = (struct GBACore*) core;
685 struct GBA* gba = core->board;
686 GBAVideoProxyRendererUnshim(&gba->video, &gbacore->proxyRenderer);
687 free(gbacore->proxyRenderer.logger);
688 gbacore->proxyRenderer.logger = NULL;
689}
690
691struct mCore* GBACoreCreate(void) {
692 struct GBACore* gbacore = malloc(sizeof(*gbacore));
693 struct mCore* core = &gbacore->d;
694 memset(&core->opts, 0, sizeof(core->opts));
695 core->cpu = NULL;
696 core->board = NULL;
697 core->debugger = NULL;
698 core->init = _GBACoreInit;
699 core->deinit = _GBACoreDeinit;
700 core->platform = _GBACorePlatform;
701 core->setSync = _GBACoreSetSync;
702 core->loadConfig = _GBACoreLoadConfig;
703 core->desiredVideoDimensions = _GBACoreDesiredVideoDimensions;
704 core->setVideoBuffer = _GBACoreSetVideoBuffer;
705 core->getPixels = _GBACoreGetPixels;
706 core->putPixels = _GBACorePutPixels;
707 core->getAudioChannel = _GBACoreGetAudioChannel;
708 core->setAudioBufferSize = _GBACoreSetAudioBufferSize;
709 core->getAudioBufferSize = _GBACoreGetAudioBufferSize;
710 core->addCoreCallbacks = _GBACoreAddCoreCallbacks;
711 core->clearCoreCallbacks = _GBACoreClearCoreCallbacks;
712 core->setAVStream = _GBACoreSetAVStream;
713 core->isROM = GBAIsROM;
714 core->loadROM = _GBACoreLoadROM;
715 core->loadBIOS = _GBACoreLoadBIOS;
716 core->loadSave = _GBACoreLoadSave;
717 core->loadTemporarySave = _GBACoreLoadTemporarySave;
718 core->loadPatch = _GBACoreLoadPatch;
719 core->unloadROM = _GBACoreUnloadROM;
720 core->checksum = _GBACoreChecksum;
721 core->reset = _GBACoreReset;
722 core->runFrame = _GBACoreRunFrame;
723 core->runLoop = _GBACoreRunLoop;
724 core->step = _GBACoreStep;
725 core->stateSize = _GBACoreStateSize;
726 core->loadState = _GBACoreLoadState;
727 core->saveState = _GBACoreSaveState;
728 core->setKeys = _GBACoreSetKeys;
729 core->addKeys = _GBACoreAddKeys;
730 core->clearKeys = _GBACoreClearKeys;
731 core->frameCounter = _GBACoreFrameCounter;
732 core->frameCycles = _GBACoreFrameCycles;
733 core->frequency = _GBACoreFrequency;
734 core->getGameTitle = _GBACoreGetGameTitle;
735 core->getGameCode = _GBACoreGetGameCode;
736 core->setPeripheral = _GBACoreSetPeripheral;
737 core->busRead8 = _GBACoreBusRead8;
738 core->busRead16 = _GBACoreBusRead16;
739 core->busRead32 = _GBACoreBusRead32;
740 core->busWrite8 = _GBACoreBusWrite8;
741 core->busWrite16 = _GBACoreBusWrite16;
742 core->busWrite32 = _GBACoreBusWrite32;
743 core->rawRead8 = _GBACoreRawRead8;
744 core->rawRead16 = _GBACoreRawRead16;
745 core->rawRead32 = _GBACoreRawRead32;
746 core->rawWrite8 = _GBACoreRawWrite8;
747 core->rawWrite16 = _GBACoreRawWrite16;
748 core->rawWrite32 = _GBACoreRawWrite32;
749#ifdef USE_DEBUGGERS
750 core->supportsDebuggerType = _GBACoreSupportsDebuggerType;
751 core->debuggerPlatform = _GBACoreDebuggerPlatform;
752 core->cliDebuggerSystem = _GBACoreCliDebuggerSystem;
753 core->attachDebugger = _GBACoreAttachDebugger;
754 core->detachDebugger = _GBACoreDetachDebugger;
755 core->loadSymbols = _GBACoreLoadSymbols;
756#endif
757 core->cheatDevice = _GBACoreCheatDevice;
758 core->savedataClone = _GBACoreSavedataClone;
759 core->savedataRestore = _GBACoreSavedataRestore;
760 core->listVideoLayers = _GBACoreListVideoLayers;
761 core->listAudioChannels = _GBACoreListAudioChannels;
762 core->enableVideoLayer = _GBACoreEnableVideoLayer;
763 core->enableAudioChannel = _GBACoreEnableAudioChannel;
764#ifndef MINIMAL_CORE
765 core->startVideoLog = _GBACoreStartVideoLog;
766 core->endVideoLog = _GBACoreEndVideoLog;
767#endif
768 return core;
769}
770
771#ifndef MINIMAL_CORE
772static void _GBAVLPStartFrameCallback(void *context) {
773 struct mCore* core = context;
774 struct GBACore* gbacore = (struct GBACore*) core;
775 struct GBA* gba = core->board;
776
777 if (!mVideoLoggerRendererRun(gbacore->proxyRenderer.logger, true)) {
778 GBAVideoProxyRendererUnshim(&gba->video, &gbacore->proxyRenderer);
779 mVideoLogContextRewind(gbacore->logContext, core);
780 GBAVideoProxyRendererShim(&gba->video, &gbacore->proxyRenderer);
781 }
782}
783
784static bool _GBAVLPInit(struct mCore* core) {
785 struct GBACore* gbacore = (struct GBACore*) core;
786 if (!_GBACoreInit(core)) {
787 return false;
788 }
789 gbacore->proxyRenderer.logger = malloc(sizeof(struct mVideoLogger));
790 mVideoLoggerRendererCreate(gbacore->proxyRenderer.logger, true);
791 GBAVideoProxyRendererCreate(&gbacore->proxyRenderer, NULL);
792 memset(&gbacore->logCallbacks, 0, sizeof(gbacore->logCallbacks));
793 gbacore->logCallbacks.videoFrameStarted = _GBAVLPStartFrameCallback;
794 gbacore->logCallbacks.context = core;
795 core->addCoreCallbacks(core, &gbacore->logCallbacks);
796 return true;
797}
798
799static void _GBAVLPDeinit(struct mCore* core) {
800 struct GBACore* gbacore = (struct GBACore*) core;
801 if (gbacore->logContext) {
802 mVideoLogContextDestroy(core, gbacore->logContext);
803 }
804 _GBACoreDeinit(core);
805}
806
807static void _GBAVLPReset(struct mCore* core) {
808 struct GBACore* gbacore = (struct GBACore*) core;
809 struct GBA* gba = (struct GBA*) core->board;
810 if (gba->video.renderer == &gbacore->proxyRenderer.d) {
811 GBAVideoProxyRendererUnshim(&gba->video, &gbacore->proxyRenderer);
812 } else if (gbacore->renderer.outputBuffer) {
813 struct GBAVideoRenderer* renderer = &gbacore->renderer.d;
814 GBAVideoAssociateRenderer(&gba->video, renderer);
815 }
816
817 ARMReset(core->cpu);
818 mVideoLogContextRewind(gbacore->logContext, core);
819 GBAVideoProxyRendererShim(&gba->video, &gbacore->proxyRenderer);
820
821 // Make sure CPU loop never spins
822 GBAHalt(gba);
823 gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IME, 0, NULL);
824 gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IE, 0, NULL);
825}
826
827static bool _GBAVLPLoadROM(struct mCore* core, struct VFile* vf) {
828 struct GBACore* gbacore = (struct GBACore*) core;
829 gbacore->logContext = mVideoLogContextCreate(NULL);
830 if (!mVideoLogContextLoad(gbacore->logContext, vf)) {
831 mVideoLogContextDestroy(core, gbacore->logContext);
832 gbacore->logContext = NULL;
833 return false;
834 }
835 mVideoLoggerAttachChannel(gbacore->proxyRenderer.logger, gbacore->logContext, 0);
836 return true;
837}
838
839static bool _GBAVLPLoadState(struct mCore* core, const void* state) {
840 struct GBA* gba = (struct GBA*) core->board;
841
842 gba->timing.root = NULL;
843 gba->cpu->gprs[ARM_PC] = BASE_WORKING_RAM;
844 gba->cpu->memory.setActiveRegion(gba->cpu, gba->cpu->gprs[ARM_PC]);
845
846 // Make sure CPU loop never spins
847 GBAHalt(gba);
848 gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IME, 0, NULL);
849 gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IE, 0, NULL);
850 GBAVideoDeserialize(&gba->video, state);
851 GBAIODeserialize(gba, state);
852 GBAAudioReset(&gba->audio);
853
854 return true;
855}
856
857static bool _returnTrue(struct VFile* vf) {
858 UNUSED(vf);
859 return true;
860}
861
862struct mCore* GBAVideoLogPlayerCreate(void) {
863 struct mCore* core = GBACoreCreate();
864 core->init = _GBAVLPInit;
865 core->deinit = _GBAVLPDeinit;
866 core->reset = _GBAVLPReset;
867 core->loadROM = _GBAVLPLoadROM;
868 core->loadState = _GBAVLPLoadState;
869 core->isROM = _returnTrue;
870 return core;
871}
872#else
873struct mCore* GBAVideoLogPlayerCreate(void) {
874 return false;
875}
876#endif