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