all repos — mgba @ 404332e68962e0658cf63f1faca1e722174be276

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