all repos — mgba @ 3021996a49cf72be55d15a1be4b24a13a8b751b9

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 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