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