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