all repos — mgba @ aec8b8275622c3b54ee2bebfd903d5fc051255bc

mGBA Game Boy Advance Emulator

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