all repos — mgba @ 39a73ecb95659ef36ec40519acc16e403adc15a2

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	GBATestKeypadIRQ(core->board);
402}
403
404static void _GBACoreAddKeys(struct mCore* core, uint32_t keys) {
405	struct GBACore* gbacore = (struct GBACore*) core;
406	gbacore->keys |= keys;
407	GBATestKeypadIRQ(core->board);
408}
409
410static void _GBACoreClearKeys(struct mCore* core, uint32_t keys) {
411	struct GBACore* gbacore = (struct GBACore*) core;
412	gbacore->keys &= ~keys;
413	GBATestKeypadIRQ(core->board);
414}
415
416static int32_t _GBACoreFrameCounter(const struct mCore* core) {
417	const struct GBA* gba = core->board;
418	return gba->video.frameCounter;
419}
420
421static int32_t _GBACoreFrameCycles(const struct mCore* core) {
422	UNUSED(core);
423	return VIDEO_TOTAL_LENGTH;
424}
425
426static int32_t _GBACoreFrequency(const struct mCore* core) {
427	UNUSED(core);
428	return GBA_ARM7TDMI_FREQUENCY;
429}
430
431static void _GBACoreGetGameTitle(const struct mCore* core, char* title) {
432	GBAGetGameTitle(core->board, title);
433}
434
435static void _GBACoreGetGameCode(const struct mCore* core, char* title) {
436	GBAGetGameCode(core->board, title);
437}
438
439static void _GBACoreSetPeripheral(struct mCore* core, int type, void* periph) {
440	struct GBA* gba = core->board;
441	switch (type) {
442	case mPERIPH_ROTATION:
443		gba->rotationSource = periph;
444		break;
445	case mPERIPH_RUMBLE:
446		gba->rumble = periph;
447		break;
448	case mPERIPH_GBA_LUMINANCE:
449		gba->luminanceSource = periph;
450		break;
451	default:
452		return;
453	}
454}
455
456static uint32_t _GBACoreBusRead8(struct mCore* core, uint32_t address) {
457	struct ARMCore* cpu = core->cpu;
458	return cpu->memory.load8(cpu, address, 0);
459}
460
461static uint32_t _GBACoreBusRead16(struct mCore* core, uint32_t address) {
462	struct ARMCore* cpu = core->cpu;
463	return cpu->memory.load16(cpu, address, 0);
464
465}
466
467static uint32_t _GBACoreBusRead32(struct mCore* core, uint32_t address) {
468	struct ARMCore* cpu = core->cpu;
469	return cpu->memory.load32(cpu, address, 0);
470}
471
472static void _GBACoreBusWrite8(struct mCore* core, uint32_t address, uint8_t value) {
473	struct ARMCore* cpu = core->cpu;
474	cpu->memory.store8(cpu, address, value, 0);
475}
476
477static void _GBACoreBusWrite16(struct mCore* core, uint32_t address, uint16_t value) {
478	struct ARMCore* cpu = core->cpu;
479	cpu->memory.store16(cpu, address, value, 0);
480}
481
482static void _GBACoreBusWrite32(struct mCore* core, uint32_t address, uint32_t value) {
483	struct ARMCore* cpu = core->cpu;
484	cpu->memory.store32(cpu, address, value, 0);
485}
486
487static uint32_t _GBACoreRawRead8(struct mCore* core, uint32_t address, int segment) {
488	UNUSED(segment);
489	struct ARMCore* cpu = core->cpu;
490	return GBAView8(cpu, address);
491}
492
493static uint32_t _GBACoreRawRead16(struct mCore* core, uint32_t address, int segment) {
494	UNUSED(segment);
495	struct ARMCore* cpu = core->cpu;
496	return GBAView16(cpu, address);
497}
498
499static uint32_t _GBACoreRawRead32(struct mCore* core, uint32_t address, int segment) {
500	UNUSED(segment);
501	struct ARMCore* cpu = core->cpu;
502	return GBAView32(cpu, address);
503}
504
505static void _GBACoreRawWrite8(struct mCore* core, uint32_t address, int segment, uint8_t value) {
506	UNUSED(segment);
507	struct ARMCore* cpu = core->cpu;
508	GBAPatch8(cpu, address, value, NULL);
509}
510
511static void _GBACoreRawWrite16(struct mCore* core, uint32_t address, int segment, uint16_t value) {
512	UNUSED(segment);
513	struct ARMCore* cpu = core->cpu;
514	GBAPatch16(cpu, address, value, NULL);
515}
516
517static void _GBACoreRawWrite32(struct mCore* core, uint32_t address, int segment, uint32_t value) {
518	UNUSED(segment);
519	struct ARMCore* cpu = core->cpu;
520	GBAPatch32(cpu, address, value, NULL);
521}
522
523#ifdef USE_DEBUGGERS
524static bool _GBACoreSupportsDebuggerType(struct mCore* core, enum mDebuggerType type) {
525	UNUSED(core);
526	switch (type) {
527	case DEBUGGER_CLI:
528		return true;
529#ifdef USE_GDB_STUB
530	case DEBUGGER_GDB:
531		return true;
532#endif
533	default:
534		return false;
535	}
536}
537
538static struct mDebuggerPlatform* _GBACoreDebuggerPlatform(struct mCore* core) {
539	struct GBACore* gbacore = (struct GBACore*) core;
540	if (!gbacore->debuggerPlatform) {
541		gbacore->debuggerPlatform = ARMDebuggerPlatformCreate();
542	}
543	return gbacore->debuggerPlatform;
544}
545
546static struct CLIDebuggerSystem* _GBACoreCliDebuggerSystem(struct mCore* core) {
547	return &GBACLIDebuggerCreate(core)->d;
548}
549
550static void _GBACoreAttachDebugger(struct mCore* core, struct mDebugger* debugger) {
551	if (core->debugger) {
552		GBADetachDebugger(core->board);
553	}
554	GBAAttachDebugger(core->board, debugger);
555	core->debugger = debugger;
556}
557
558static void _GBACoreDetachDebugger(struct mCore* core) {
559	GBADetachDebugger(core->board);
560	core->debugger = NULL;
561}
562
563static void _GBACoreLoadSymbols(struct mCore* core, struct VFile* vf) {
564	// TODO
565}
566#endif
567
568static struct mCheatDevice* _GBACoreCheatDevice(struct mCore* core) {
569	struct GBACore* gbacore = (struct GBACore*) core;
570	if (!gbacore->cheatDevice) {
571		gbacore->cheatDevice = GBACheatDeviceCreate();
572		((struct ARMCore*) core->cpu)->components[CPU_COMPONENT_CHEAT_DEVICE] = &gbacore->cheatDevice->d;
573		ARMHotplugAttach(core->cpu, CPU_COMPONENT_CHEAT_DEVICE);
574		gbacore->cheatDevice->p = core;
575	}
576	return gbacore->cheatDevice;
577}
578
579static size_t _GBACoreSavedataClone(struct mCore* core, void** sram) {
580	struct GBA* gba = core->board;
581	size_t size = GBASavedataSize(&gba->memory.savedata);
582	if (!size) {
583		*sram = NULL;
584		return 0;
585	}
586	*sram = malloc(size);
587	struct VFile* vf = VFileFromMemory(*sram, size);
588	if (!vf) {
589		free(*sram);
590		*sram = NULL;
591		return 0;
592	}
593	bool success = GBASavedataClone(&gba->memory.savedata, vf);
594	vf->close(vf);
595	if (!success) {
596		free(*sram);
597		*sram = NULL;
598		return 0;
599	}
600	return size;
601}
602
603static bool _GBACoreSavedataRestore(struct mCore* core, const void* sram, size_t size, bool writeback) {
604	struct VFile* vf = VFileMemChunk(sram, size);
605	if (!vf) {
606		return false;
607	}
608	struct GBA* gba = core->board;
609	bool success = true;
610	if (writeback) {
611		success = GBASavedataLoad(&gba->memory.savedata, vf);
612		vf->close(vf);
613	} else {
614		GBASavedataMask(&gba->memory.savedata, vf, true);
615	}
616	return success;
617}
618
619static size_t _GBACoreListVideoLayers(const struct mCore* core, const struct mCoreChannelInfo** info) {
620	UNUSED(core);
621	*info = _GBAVideoLayers;
622	return sizeof(_GBAVideoLayers) / sizeof(*_GBAVideoLayers);
623}
624
625static size_t _GBACoreListAudioChannels(const struct mCore* core, const struct mCoreChannelInfo** info) {
626	UNUSED(core);
627	*info = _GBAAudioChannels;
628	return sizeof(_GBAAudioChannels) / sizeof(*_GBAAudioChannels);
629}
630
631static void _GBACoreEnableVideoLayer(struct mCore* core, size_t id, bool enable) {
632	struct GBA* gba = core->board;
633	switch (id) {
634	case 0:
635	case 1:
636	case 2:
637	case 3:
638		gba->video.renderer->disableBG[id] = !enable;
639		break;
640	case 4:
641		gba->video.renderer->disableOBJ = !enable;
642		break;
643	default:
644		break;
645	}
646}
647
648static void _GBACoreEnableAudioChannel(struct mCore* core, size_t id, bool enable) {
649	struct GBA* gba = core->board;
650	switch (id) {
651	case 0:
652	case 1:
653	case 2:
654	case 3:
655		gba->audio.psg.forceDisableCh[id] = !enable;
656		break;
657	case 4:
658		gba->audio.forceDisableChA = !enable;
659	case 5:
660		gba->audio.forceDisableChB = !enable;
661		break;
662	default:
663		break;
664	}
665}
666
667static void _GBACoreStartVideoLog(struct mCore* core, struct mVideoLogContext* context) {
668	struct GBACore* gbacore = (struct GBACore*) core;
669	struct GBA* gba = core->board;
670	gbacore->logContext = context;
671
672	struct GBASerializedState* state = mVideoLogContextInitialState(context, NULL);
673	state->id = 0;
674	state->cpu.gprs[ARM_PC] = BASE_WORKING_RAM;
675
676	int channelId = mVideoLoggerAddChannel(context);
677	gbacore->proxyRenderer.logger = malloc(sizeof(struct mVideoLogger));
678	mVideoLoggerRendererCreate(gbacore->proxyRenderer.logger, false);
679	mVideoLoggerAttachChannel(gbacore->proxyRenderer.logger, context, channelId);
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
694struct mCore* GBACoreCreate(void) {
695	struct GBACore* gbacore = malloc(sizeof(*gbacore));
696	struct mCore* core = &gbacore->d;
697	memset(&core->opts, 0, sizeof(core->opts));
698	core->cpu = NULL;
699	core->board = NULL;
700	core->debugger = NULL;
701	core->init = _GBACoreInit;
702	core->deinit = _GBACoreDeinit;
703	core->platform = _GBACorePlatform;
704	core->setSync = _GBACoreSetSync;
705	core->loadConfig = _GBACoreLoadConfig;
706	core->desiredVideoDimensions = _GBACoreDesiredVideoDimensions;
707	core->setVideoBuffer = _GBACoreSetVideoBuffer;
708	core->getPixels = _GBACoreGetPixels;
709	core->putPixels = _GBACorePutPixels;
710	core->getAudioChannel = _GBACoreGetAudioChannel;
711	core->setAudioBufferSize = _GBACoreSetAudioBufferSize;
712	core->getAudioBufferSize = _GBACoreGetAudioBufferSize;
713	core->addCoreCallbacks = _GBACoreAddCoreCallbacks;
714	core->clearCoreCallbacks = _GBACoreClearCoreCallbacks;
715	core->setAVStream = _GBACoreSetAVStream;
716	core->isROM = GBAIsROM;
717	core->loadROM = _GBACoreLoadROM;
718	core->loadBIOS = _GBACoreLoadBIOS;
719	core->loadSave = _GBACoreLoadSave;
720	core->loadTemporarySave = _GBACoreLoadTemporarySave;
721	core->loadPatch = _GBACoreLoadPatch;
722	core->unloadROM = _GBACoreUnloadROM;
723	core->checksum = _GBACoreChecksum;
724	core->reset = _GBACoreReset;
725	core->runFrame = _GBACoreRunFrame;
726	core->runLoop = _GBACoreRunLoop;
727	core->step = _GBACoreStep;
728	core->stateSize = _GBACoreStateSize;
729	core->loadState = _GBACoreLoadState;
730	core->saveState = _GBACoreSaveState;
731	core->setKeys = _GBACoreSetKeys;
732	core->addKeys = _GBACoreAddKeys;
733	core->clearKeys = _GBACoreClearKeys;
734	core->frameCounter = _GBACoreFrameCounter;
735	core->frameCycles = _GBACoreFrameCycles;
736	core->frequency = _GBACoreFrequency;
737	core->getGameTitle = _GBACoreGetGameTitle;
738	core->getGameCode = _GBACoreGetGameCode;
739	core->setPeripheral = _GBACoreSetPeripheral;
740	core->busRead8 = _GBACoreBusRead8;
741	core->busRead16 = _GBACoreBusRead16;
742	core->busRead32 = _GBACoreBusRead32;
743	core->busWrite8 = _GBACoreBusWrite8;
744	core->busWrite16 = _GBACoreBusWrite16;
745	core->busWrite32 = _GBACoreBusWrite32;
746	core->rawRead8 = _GBACoreRawRead8;
747	core->rawRead16 = _GBACoreRawRead16;
748	core->rawRead32 = _GBACoreRawRead32;
749	core->rawWrite8 = _GBACoreRawWrite8;
750	core->rawWrite16 = _GBACoreRawWrite16;
751	core->rawWrite32 = _GBACoreRawWrite32;
752#ifdef USE_DEBUGGERS
753	core->supportsDebuggerType = _GBACoreSupportsDebuggerType;
754	core->debuggerPlatform = _GBACoreDebuggerPlatform;
755	core->cliDebuggerSystem = _GBACoreCliDebuggerSystem;
756	core->attachDebugger = _GBACoreAttachDebugger;
757	core->detachDebugger = _GBACoreDetachDebugger;
758	core->loadSymbols = _GBACoreLoadSymbols;
759#endif
760	core->cheatDevice = _GBACoreCheatDevice;
761	core->savedataClone = _GBACoreSavedataClone;
762	core->savedataRestore = _GBACoreSavedataRestore;
763	core->listVideoLayers = _GBACoreListVideoLayers;
764	core->listAudioChannels = _GBACoreListAudioChannels;
765	core->enableVideoLayer = _GBACoreEnableVideoLayer;
766	core->enableAudioChannel = _GBACoreEnableAudioChannel;
767#ifndef MINIMAL_CORE
768	core->startVideoLog = _GBACoreStartVideoLog;
769	core->endVideoLog = _GBACoreEndVideoLog;
770#endif
771	return core;
772}
773
774#ifndef MINIMAL_CORE
775static void _GBAVLPStartFrameCallback(void *context) {
776	struct mCore* core = context;
777	struct GBACore* gbacore = (struct GBACore*) core;
778	struct GBA* gba = core->board;
779
780	if (!mVideoLoggerRendererRun(gbacore->proxyRenderer.logger, true)) {
781		GBAVideoProxyRendererUnshim(&gba->video, &gbacore->proxyRenderer);
782		mVideoLogContextRewind(gbacore->logContext, core);
783		GBAVideoProxyRendererShim(&gba->video, &gbacore->proxyRenderer);
784	}
785}
786
787static bool _GBAVLPInit(struct mCore* core) {
788	struct GBACore* gbacore = (struct GBACore*) core;
789	if (!_GBACoreInit(core)) {
790		return false;
791	}
792	gbacore->proxyRenderer.logger = malloc(sizeof(struct mVideoLogger));
793	mVideoLoggerRendererCreate(gbacore->proxyRenderer.logger, true);
794	GBAVideoProxyRendererCreate(&gbacore->proxyRenderer, NULL);
795	memset(&gbacore->logCallbacks, 0, sizeof(gbacore->logCallbacks));
796	gbacore->logCallbacks.videoFrameStarted = _GBAVLPStartFrameCallback;
797	gbacore->logCallbacks.context = core;
798	core->addCoreCallbacks(core, &gbacore->logCallbacks);
799	return true;
800}
801
802static void _GBAVLPDeinit(struct mCore* core) {
803	struct GBACore* gbacore = (struct GBACore*) core;
804	if (gbacore->logContext) {
805		mVideoLogContextDestroy(core, gbacore->logContext);
806	}
807	_GBACoreDeinit(core);
808}
809
810static void _GBAVLPReset(struct mCore* core) {
811	struct GBACore* gbacore = (struct GBACore*) core;
812	struct GBA* gba = (struct GBA*) core->board;
813	if (gba->video.renderer == &gbacore->proxyRenderer.d) {
814		GBAVideoProxyRendererUnshim(&gba->video, &gbacore->proxyRenderer);
815	} else if (gbacore->renderer.outputBuffer) {
816		struct GBAVideoRenderer* renderer = &gbacore->renderer.d;
817		GBAVideoAssociateRenderer(&gba->video, renderer);
818	}
819
820	ARMReset(core->cpu);
821	mVideoLogContextRewind(gbacore->logContext, core);
822	GBAVideoProxyRendererShim(&gba->video, &gbacore->proxyRenderer);
823
824	// Make sure CPU loop never spins
825	GBAHalt(gba);
826	gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IME, 0, NULL);
827	gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IE, 0, NULL);
828}
829
830static bool _GBAVLPLoadROM(struct mCore* core, struct VFile* vf) {
831	struct GBACore* gbacore = (struct GBACore*) core;
832	gbacore->logContext = mVideoLogContextCreate(NULL);
833	if (!mVideoLogContextLoad(gbacore->logContext, vf)) {
834		mVideoLogContextDestroy(core, gbacore->logContext);
835		gbacore->logContext = NULL;
836		return false;
837	}
838	mVideoLoggerAttachChannel(gbacore->proxyRenderer.logger, gbacore->logContext, 0);
839	return true;
840}
841
842static bool _GBAVLPLoadState(struct mCore* core, const void* state) {
843	struct GBA* gba = (struct GBA*) core->board;
844
845	gba->timing.root = NULL;
846	gba->cpu->gprs[ARM_PC] = BASE_WORKING_RAM;
847	gba->cpu->memory.setActiveRegion(gba->cpu, gba->cpu->gprs[ARM_PC]);
848
849	// Make sure CPU loop never spins
850	GBAHalt(gba);
851	gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IME, 0, NULL);
852	gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IE, 0, NULL);
853	GBAVideoDeserialize(&gba->video, state);
854	GBAIODeserialize(gba, state);
855	GBAAudioReset(&gba->audio);
856
857	return true;
858}
859
860static bool _returnTrue(struct VFile* vf) {
861	UNUSED(vf);
862	return true;
863}
864
865struct mCore* GBAVideoLogPlayerCreate(void) {
866	struct mCore* core = GBACoreCreate();
867	core->init = _GBAVLPInit;
868	core->deinit = _GBAVLPDeinit;
869	core->reset = _GBAVLPReset;
870	core->loadROM = _GBAVLPLoadROM;
871	core->loadState = _GBAVLPLoadState;
872	core->isROM = _returnTrue;
873	return core;
874}
875#else
876struct mCore* GBAVideoLogPlayerCreate(void) {
877	return false;
878}
879#endif