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 GBATestKeypadIRQ(core->board);
402}
403
404static void _GBACoreAddKeys(struct mCore* core, uint32_t keys) {
405 struct GBACore* gbacore = (struct GBACore*) core;
406 gbacore->keys |= keys;
407 GBATestKeypadIRQ(core->board);
408}
409
410static void _GBACoreClearKeys(struct mCore* core, uint32_t keys) {
411 struct GBACore* gbacore = (struct GBACore*) core;
412 gbacore->keys &= ~keys;
413 GBATestKeypadIRQ(core->board);
414}
415
416static int32_t _GBACoreFrameCounter(const struct mCore* core) {
417 const struct GBA* gba = core->board;
418 return gba->video.frameCounter;
419}
420
421static int32_t _GBACoreFrameCycles(const struct mCore* core) {
422 UNUSED(core);
423 return VIDEO_TOTAL_LENGTH;
424}
425
426static int32_t _GBACoreFrequency(const struct mCore* core) {
427 UNUSED(core);
428 return GBA_ARM7TDMI_FREQUENCY;
429}
430
431static void _GBACoreGetGameTitle(const struct mCore* core, char* title) {
432 GBAGetGameTitle(core->board, title);
433}
434
435static void _GBACoreGetGameCode(const struct mCore* core, char* title) {
436 GBAGetGameCode(core->board, title);
437}
438
439static void _GBACoreSetPeripheral(struct mCore* core, int type, void* periph) {
440 struct GBA* gba = core->board;
441 switch (type) {
442 case mPERIPH_ROTATION:
443 gba->rotationSource = periph;
444 break;
445 case mPERIPH_RUMBLE:
446 gba->rumble = periph;
447 break;
448 case mPERIPH_GBA_LUMINANCE:
449 gba->luminanceSource = periph;
450 break;
451 default:
452 return;
453 }
454}
455
456static uint32_t _GBACoreBusRead8(struct mCore* core, uint32_t address) {
457 struct ARMCore* cpu = core->cpu;
458 return cpu->memory.load8(cpu, address, 0);
459}
460
461static uint32_t _GBACoreBusRead16(struct mCore* core, uint32_t address) {
462 struct ARMCore* cpu = core->cpu;
463 return cpu->memory.load16(cpu, address, 0);
464
465}
466
467static uint32_t _GBACoreBusRead32(struct mCore* core, uint32_t address) {
468 struct ARMCore* cpu = core->cpu;
469 return cpu->memory.load32(cpu, address, 0);
470}
471
472static void _GBACoreBusWrite8(struct mCore* core, uint32_t address, uint8_t value) {
473 struct ARMCore* cpu = core->cpu;
474 cpu->memory.store8(cpu, address, value, 0);
475}
476
477static void _GBACoreBusWrite16(struct mCore* core, uint32_t address, uint16_t value) {
478 struct ARMCore* cpu = core->cpu;
479 cpu->memory.store16(cpu, address, value, 0);
480}
481
482static void _GBACoreBusWrite32(struct mCore* core, uint32_t address, uint32_t value) {
483 struct ARMCore* cpu = core->cpu;
484 cpu->memory.store32(cpu, address, value, 0);
485}
486
487static uint32_t _GBACoreRawRead8(struct mCore* core, uint32_t address, int segment) {
488 UNUSED(segment);
489 struct ARMCore* cpu = core->cpu;
490 return GBAView8(cpu, address);
491}
492
493static uint32_t _GBACoreRawRead16(struct mCore* core, uint32_t address, int segment) {
494 UNUSED(segment);
495 struct ARMCore* cpu = core->cpu;
496 return GBAView16(cpu, address);
497}
498
499static uint32_t _GBACoreRawRead32(struct mCore* core, uint32_t address, int segment) {
500 UNUSED(segment);
501 struct ARMCore* cpu = core->cpu;
502 return GBAView32(cpu, address);
503}
504
505static void _GBACoreRawWrite8(struct mCore* core, uint32_t address, int segment, uint8_t value) {
506 UNUSED(segment);
507 struct ARMCore* cpu = core->cpu;
508 GBAPatch8(cpu, address, value, NULL);
509}
510
511static void _GBACoreRawWrite16(struct mCore* core, uint32_t address, int segment, uint16_t value) {
512 UNUSED(segment);
513 struct ARMCore* cpu = core->cpu;
514 GBAPatch16(cpu, address, value, NULL);
515}
516
517static void _GBACoreRawWrite32(struct mCore* core, uint32_t address, int segment, uint32_t value) {
518 UNUSED(segment);
519 struct ARMCore* cpu = core->cpu;
520 GBAPatch32(cpu, address, value, NULL);
521}
522
523#ifdef USE_DEBUGGERS
524static bool _GBACoreSupportsDebuggerType(struct mCore* core, enum mDebuggerType type) {
525 UNUSED(core);
526 switch (type) {
527 case DEBUGGER_CLI:
528 return true;
529#ifdef USE_GDB_STUB
530 case DEBUGGER_GDB:
531 return true;
532#endif
533 default:
534 return false;
535 }
536}
537
538static struct mDebuggerPlatform* _GBACoreDebuggerPlatform(struct mCore* core) {
539 struct GBACore* gbacore = (struct GBACore*) core;
540 if (!gbacore->debuggerPlatform) {
541 gbacore->debuggerPlatform = ARMDebuggerPlatformCreate();
542 }
543 return gbacore->debuggerPlatform;
544}
545
546static struct CLIDebuggerSystem* _GBACoreCliDebuggerSystem(struct mCore* core) {
547 return &GBACLIDebuggerCreate(core)->d;
548}
549
550static void _GBACoreAttachDebugger(struct mCore* core, struct mDebugger* debugger) {
551 if (core->debugger) {
552 GBADetachDebugger(core->board);
553 }
554 GBAAttachDebugger(core->board, debugger);
555 core->debugger = debugger;
556}
557
558static void _GBACoreDetachDebugger(struct mCore* core) {
559 GBADetachDebugger(core->board);
560 core->debugger = NULL;
561}
562
563static void _GBACoreLoadSymbols(struct mCore* core, struct VFile* vf) {
564 // TODO
565}
566#endif
567
568static struct mCheatDevice* _GBACoreCheatDevice(struct mCore* core) {
569 struct GBACore* gbacore = (struct GBACore*) core;
570 if (!gbacore->cheatDevice) {
571 gbacore->cheatDevice = GBACheatDeviceCreate();
572 ((struct ARMCore*) core->cpu)->components[CPU_COMPONENT_CHEAT_DEVICE] = &gbacore->cheatDevice->d;
573 ARMHotplugAttach(core->cpu, CPU_COMPONENT_CHEAT_DEVICE);
574 gbacore->cheatDevice->p = core;
575 }
576 return gbacore->cheatDevice;
577}
578
579static size_t _GBACoreSavedataClone(struct mCore* core, void** sram) {
580 struct GBA* gba = core->board;
581 size_t size = GBASavedataSize(&gba->memory.savedata);
582 if (!size) {
583 *sram = NULL;
584 return 0;
585 }
586 *sram = malloc(size);
587 struct VFile* vf = VFileFromMemory(*sram, size);
588 if (!vf) {
589 free(*sram);
590 *sram = NULL;
591 return 0;
592 }
593 bool success = GBASavedataClone(&gba->memory.savedata, vf);
594 vf->close(vf);
595 if (!success) {
596 free(*sram);
597 *sram = NULL;
598 return 0;
599 }
600 return size;
601}
602
603static bool _GBACoreSavedataRestore(struct mCore* core, const void* sram, size_t size, bool writeback) {
604 struct VFile* vf = VFileMemChunk(sram, size);
605 if (!vf) {
606 return false;
607 }
608 struct GBA* gba = core->board;
609 bool success = true;
610 if (writeback) {
611 success = GBASavedataLoad(&gba->memory.savedata, vf);
612 vf->close(vf);
613 } else {
614 GBASavedataMask(&gba->memory.savedata, vf, true);
615 }
616 return success;
617}
618
619static size_t _GBACoreListVideoLayers(const struct mCore* core, const struct mCoreChannelInfo** info) {
620 UNUSED(core);
621 *info = _GBAVideoLayers;
622 return sizeof(_GBAVideoLayers) / sizeof(*_GBAVideoLayers);
623}
624
625static size_t _GBACoreListAudioChannels(const struct mCore* core, const struct mCoreChannelInfo** info) {
626 UNUSED(core);
627 *info = _GBAAudioChannels;
628 return sizeof(_GBAAudioChannels) / sizeof(*_GBAAudioChannels);
629}
630
631static void _GBACoreEnableVideoLayer(struct mCore* core, size_t id, bool enable) {
632 struct GBA* gba = core->board;
633 switch (id) {
634 case 0:
635 case 1:
636 case 2:
637 case 3:
638 gba->video.renderer->disableBG[id] = !enable;
639 break;
640 case 4:
641 gba->video.renderer->disableOBJ = !enable;
642 break;
643 default:
644 break;
645 }
646}
647
648static void _GBACoreEnableAudioChannel(struct mCore* core, size_t id, bool enable) {
649 struct GBA* gba = core->board;
650 switch (id) {
651 case 0:
652 case 1:
653 case 2:
654 case 3:
655 gba->audio.psg.forceDisableCh[id] = !enable;
656 break;
657 case 4:
658 gba->audio.forceDisableChA = !enable;
659 case 5:
660 gba->audio.forceDisableChB = !enable;
661 break;
662 default:
663 break;
664 }
665}
666
667static void _GBACoreStartVideoLog(struct mCore* core, struct mVideoLogContext* context) {
668 struct GBACore* gbacore = (struct GBACore*) core;
669 struct GBA* gba = core->board;
670 gbacore->logContext = context;
671
672 struct GBASerializedState* state = mVideoLogContextInitialState(context, NULL);
673 state->id = 0;
674 state->cpu.gprs[ARM_PC] = BASE_WORKING_RAM;
675
676 int channelId = mVideoLoggerAddChannel(context);
677 gbacore->proxyRenderer.logger = malloc(sizeof(struct mVideoLogger));
678 mVideoLoggerRendererCreate(gbacore->proxyRenderer.logger, false);
679 mVideoLoggerAttachChannel(gbacore->proxyRenderer.logger, context, channelId);
680 gbacore->proxyRenderer.logger->block = false;
681
682 GBAVideoProxyRendererCreate(&gbacore->proxyRenderer, &gbacore->renderer.d);
683 GBAVideoProxyRendererShim(&gba->video, &gbacore->proxyRenderer);
684}
685
686static void _GBACoreEndVideoLog(struct mCore* core) {
687 struct GBACore* gbacore = (struct GBACore*) core;
688 struct GBA* gba = core->board;
689 GBAVideoProxyRendererUnshim(&gba->video, &gbacore->proxyRenderer);
690 free(gbacore->proxyRenderer.logger);
691 gbacore->proxyRenderer.logger = NULL;
692}
693
694struct mCore* GBACoreCreate(void) {
695 struct GBACore* gbacore = malloc(sizeof(*gbacore));
696 struct mCore* core = &gbacore->d;
697 memset(&core->opts, 0, sizeof(core->opts));
698 core->cpu = NULL;
699 core->board = NULL;
700 core->debugger = NULL;
701 core->init = _GBACoreInit;
702 core->deinit = _GBACoreDeinit;
703 core->platform = _GBACorePlatform;
704 core->setSync = _GBACoreSetSync;
705 core->loadConfig = _GBACoreLoadConfig;
706 core->desiredVideoDimensions = _GBACoreDesiredVideoDimensions;
707 core->setVideoBuffer = _GBACoreSetVideoBuffer;
708 core->getPixels = _GBACoreGetPixels;
709 core->putPixels = _GBACorePutPixels;
710 core->getAudioChannel = _GBACoreGetAudioChannel;
711 core->setAudioBufferSize = _GBACoreSetAudioBufferSize;
712 core->getAudioBufferSize = _GBACoreGetAudioBufferSize;
713 core->addCoreCallbacks = _GBACoreAddCoreCallbacks;
714 core->clearCoreCallbacks = _GBACoreClearCoreCallbacks;
715 core->setAVStream = _GBACoreSetAVStream;
716 core->isROM = GBAIsROM;
717 core->loadROM = _GBACoreLoadROM;
718 core->loadBIOS = _GBACoreLoadBIOS;
719 core->loadSave = _GBACoreLoadSave;
720 core->loadTemporarySave = _GBACoreLoadTemporarySave;
721 core->loadPatch = _GBACoreLoadPatch;
722 core->unloadROM = _GBACoreUnloadROM;
723 core->checksum = _GBACoreChecksum;
724 core->reset = _GBACoreReset;
725 core->runFrame = _GBACoreRunFrame;
726 core->runLoop = _GBACoreRunLoop;
727 core->step = _GBACoreStep;
728 core->stateSize = _GBACoreStateSize;
729 core->loadState = _GBACoreLoadState;
730 core->saveState = _GBACoreSaveState;
731 core->setKeys = _GBACoreSetKeys;
732 core->addKeys = _GBACoreAddKeys;
733 core->clearKeys = _GBACoreClearKeys;
734 core->frameCounter = _GBACoreFrameCounter;
735 core->frameCycles = _GBACoreFrameCycles;
736 core->frequency = _GBACoreFrequency;
737 core->getGameTitle = _GBACoreGetGameTitle;
738 core->getGameCode = _GBACoreGetGameCode;
739 core->setPeripheral = _GBACoreSetPeripheral;
740 core->busRead8 = _GBACoreBusRead8;
741 core->busRead16 = _GBACoreBusRead16;
742 core->busRead32 = _GBACoreBusRead32;
743 core->busWrite8 = _GBACoreBusWrite8;
744 core->busWrite16 = _GBACoreBusWrite16;
745 core->busWrite32 = _GBACoreBusWrite32;
746 core->rawRead8 = _GBACoreRawRead8;
747 core->rawRead16 = _GBACoreRawRead16;
748 core->rawRead32 = _GBACoreRawRead32;
749 core->rawWrite8 = _GBACoreRawWrite8;
750 core->rawWrite16 = _GBACoreRawWrite16;
751 core->rawWrite32 = _GBACoreRawWrite32;
752#ifdef USE_DEBUGGERS
753 core->supportsDebuggerType = _GBACoreSupportsDebuggerType;
754 core->debuggerPlatform = _GBACoreDebuggerPlatform;
755 core->cliDebuggerSystem = _GBACoreCliDebuggerSystem;
756 core->attachDebugger = _GBACoreAttachDebugger;
757 core->detachDebugger = _GBACoreDetachDebugger;
758 core->loadSymbols = _GBACoreLoadSymbols;
759#endif
760 core->cheatDevice = _GBACoreCheatDevice;
761 core->savedataClone = _GBACoreSavedataClone;
762 core->savedataRestore = _GBACoreSavedataRestore;
763 core->listVideoLayers = _GBACoreListVideoLayers;
764 core->listAudioChannels = _GBACoreListAudioChannels;
765 core->enableVideoLayer = _GBACoreEnableVideoLayer;
766 core->enableAudioChannel = _GBACoreEnableAudioChannel;
767#ifndef MINIMAL_CORE
768 core->startVideoLog = _GBACoreStartVideoLog;
769 core->endVideoLog = _GBACoreEndVideoLog;
770#endif
771 return core;
772}
773
774#ifndef MINIMAL_CORE
775static void _GBAVLPStartFrameCallback(void *context) {
776 struct mCore* core = context;
777 struct GBACore* gbacore = (struct GBACore*) core;
778 struct GBA* gba = core->board;
779
780 if (!mVideoLoggerRendererRun(gbacore->proxyRenderer.logger, true)) {
781 GBAVideoProxyRendererUnshim(&gba->video, &gbacore->proxyRenderer);
782 mVideoLogContextRewind(gbacore->logContext, core);
783 GBAVideoProxyRendererShim(&gba->video, &gbacore->proxyRenderer);
784 }
785}
786
787static bool _GBAVLPInit(struct mCore* core) {
788 struct GBACore* gbacore = (struct GBACore*) core;
789 if (!_GBACoreInit(core)) {
790 return false;
791 }
792 gbacore->proxyRenderer.logger = malloc(sizeof(struct mVideoLogger));
793 mVideoLoggerRendererCreate(gbacore->proxyRenderer.logger, true);
794 GBAVideoProxyRendererCreate(&gbacore->proxyRenderer, NULL);
795 memset(&gbacore->logCallbacks, 0, sizeof(gbacore->logCallbacks));
796 gbacore->logCallbacks.videoFrameStarted = _GBAVLPStartFrameCallback;
797 gbacore->logCallbacks.context = core;
798 core->addCoreCallbacks(core, &gbacore->logCallbacks);
799 return true;
800}
801
802static void _GBAVLPDeinit(struct mCore* core) {
803 struct GBACore* gbacore = (struct GBACore*) core;
804 if (gbacore->logContext) {
805 mVideoLogContextDestroy(core, gbacore->logContext);
806 }
807 _GBACoreDeinit(core);
808}
809
810static void _GBAVLPReset(struct mCore* core) {
811 struct GBACore* gbacore = (struct GBACore*) core;
812 struct GBA* gba = (struct GBA*) core->board;
813 if (gba->video.renderer == &gbacore->proxyRenderer.d) {
814 GBAVideoProxyRendererUnshim(&gba->video, &gbacore->proxyRenderer);
815 } else if (gbacore->renderer.outputBuffer) {
816 struct GBAVideoRenderer* renderer = &gbacore->renderer.d;
817 GBAVideoAssociateRenderer(&gba->video, renderer);
818 }
819
820 ARMReset(core->cpu);
821 mVideoLogContextRewind(gbacore->logContext, core);
822 GBAVideoProxyRendererShim(&gba->video, &gbacore->proxyRenderer);
823
824 // Make sure CPU loop never spins
825 GBAHalt(gba);
826 gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IME, 0, NULL);
827 gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IE, 0, NULL);
828}
829
830static bool _GBAVLPLoadROM(struct mCore* core, struct VFile* vf) {
831 struct GBACore* gbacore = (struct GBACore*) core;
832 gbacore->logContext = mVideoLogContextCreate(NULL);
833 if (!mVideoLogContextLoad(gbacore->logContext, vf)) {
834 mVideoLogContextDestroy(core, gbacore->logContext);
835 gbacore->logContext = NULL;
836 return false;
837 }
838 mVideoLoggerAttachChannel(gbacore->proxyRenderer.logger, gbacore->logContext, 0);
839 return true;
840}
841
842static bool _GBAVLPLoadState(struct mCore* core, const void* state) {
843 struct GBA* gba = (struct GBA*) core->board;
844
845 gba->timing.root = NULL;
846 gba->cpu->gprs[ARM_PC] = BASE_WORKING_RAM;
847 gba->cpu->memory.setActiveRegion(gba->cpu, gba->cpu->gprs[ARM_PC]);
848
849 // Make sure CPU loop never spins
850 GBAHalt(gba);
851 gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IME, 0, NULL);
852 gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IE, 0, NULL);
853 GBAVideoDeserialize(&gba->video, state);
854 GBAIODeserialize(gba, state);
855 GBAAudioReset(&gba->audio);
856
857 return true;
858}
859
860static bool _returnTrue(struct VFile* vf) {
861 UNUSED(vf);
862 return true;
863}
864
865struct mCore* GBAVideoLogPlayerCreate(void) {
866 struct mCore* core = GBACoreCreate();
867 core->init = _GBAVLPInit;
868 core->deinit = _GBAVLPDeinit;
869 core->reset = _GBAVLPReset;
870 core->loadROM = _GBAVLPLoadROM;
871 core->loadState = _GBAVLPLoadState;
872 core->isROM = _returnTrue;
873 return core;
874}
875#else
876struct mCore* GBAVideoLogPlayerCreate(void) {
877 return false;
878}
879#endif