all repos — mgba @ a05d97a85cc95b935f1b0cf22cf1287239b9b9f6

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/extra/cli.h>
 14#include <mgba/internal/gba/overrides.h>
 15#ifndef DISABLE_THREADING
 16#include <mgba/internal/gba/renderers/thread-proxy.h>
 17#endif
 18#include <mgba/internal/gba/renderers/video-software.h>
 19#include <mgba/internal/gba/savedata.h>
 20#include <mgba/internal/gba/serialize.h>
 21#include <mgba-util/memory.h>
 22#include <mgba-util/patch.h>
 23#include <mgba-util/vfs.h>
 24
 25#ifndef MINIMAL_CORE
 26#include <mgba/internal/gba/input.h>
 27#endif
 28
 29const static struct mCoreChannelInfo _GBAVideoLayers[] = {
 30	{ 0, "bg0", "Background 0", NULL },
 31	{ 1, "bg1", "Background 1", NULL },
 32	{ 2, "bg2", "Background 2", NULL },
 33	{ 3, "bg3", "Background 3", NULL },
 34	{ 4, "obj", "Objects", NULL },
 35};
 36
 37const static struct mCoreChannelInfo _GBAAudioChannels[] = {
 38	{ 0, "ch0", "PSG Channel 0", "Square/Sweep" },
 39	{ 1, "ch1", "PSG Channel 1", "Square" },
 40	{ 2, "ch2", "PSG Channel 2", "PCM" },
 41	{ 3, "ch3", "PSG Channel 3", "Noise" },
 42	{ 4, "chA", "FIFO Channel A", NULL },
 43	{ 5, "chB", "FIFO Channel B", NULL },
 44};
 45
 46struct GBACore {
 47	struct mCore d;
 48	struct GBAVideoSoftwareRenderer renderer;
 49#ifndef DISABLE_THREADING
 50	struct GBAVideoThreadProxyRenderer threadProxy;
 51	int threadedVideo;
 52#endif
 53	int keys;
 54	struct mCPUComponent* components[CPU_COMPONENT_MAX];
 55	const struct Configuration* overrides;
 56	struct mDebuggerPlatform* debuggerPlatform;
 57	struct mCheatDevice* cheatDevice;
 58};
 59
 60static bool _GBACoreInit(struct mCore* core) {
 61	struct GBACore* gbacore = (struct GBACore*) core;
 62
 63	struct ARMCore* cpu = anonymousMemoryMap(sizeof(struct ARMCore));
 64	struct GBA* gba = anonymousMemoryMap(sizeof(struct GBA));
 65	if (!cpu || !gba) {
 66		free(cpu);
 67		free(gba);
 68		return false;
 69	}
 70	core->cpu = cpu;
 71	core->board = gba;
 72	core->debugger = NULL;
 73	gbacore->overrides = NULL;
 74	gbacore->debuggerPlatform = NULL;
 75	gbacore->cheatDevice = NULL;
 76
 77	GBACreate(gba);
 78	// TODO: Restore cheats
 79	memset(gbacore->components, 0, sizeof(gbacore->components));
 80	ARMSetComponents(cpu, &gba->d, CPU_COMPONENT_MAX, gbacore->components);
 81	ARMInit(cpu);
 82	mRTCGenericSourceInit(&core->rtc, core);
 83	gba->rtcSource = &core->rtc.d;
 84
 85	GBAVideoSoftwareRendererCreate(&gbacore->renderer);
 86	gbacore->renderer.outputBuffer = NULL;
 87
 88#ifndef DISABLE_THREADING
 89	gbacore->threadedVideo = false;
 90	GBAVideoThreadProxyRendererCreate(&gbacore->threadProxy, &gbacore->renderer.d);
 91#endif
 92
 93	gbacore->keys = 0;
 94	gba->keySource = &gbacore->keys;
 95
 96#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
 97	mDirectorySetInit(&core->dirs);
 98#endif
 99
100#ifndef MINIMAL_CORE
101	core->inputInfo = &GBAInputInfo;
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			renderer = &gbacore->threadProxy.d;
302		}
303#endif
304		GBAVideoAssociateRenderer(&gba->video, renderer);
305	}
306
307	struct GBACartridgeOverride override;
308	const struct GBACartridge* cart = (const struct GBACartridge*) gba->memory.rom;
309	if (cart) {
310		memcpy(override.id, &cart->id, sizeof(override.id));
311		if (GBAOverrideFind(gbacore->overrides, &override)) {
312			GBAOverrideApply(gba, &override);
313		}
314	}
315
316#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
317	if (!gba->biosVf && core->opts.useBios) {
318		struct VFile* bios = NULL;
319		bool found = false;
320		if (core->opts.bios) {
321			bios = VFileOpen(core->opts.bios, O_RDONLY);
322			if (bios && GBAIsBIOS(bios)) {
323				found = true;
324			} else if (bios) {
325				bios->close(bios);
326				bios = NULL;
327			}
328		}
329		if (!found) {
330			const char* configPath = mCoreConfigGetValue(&core->config, "gba.bios");
331			if (configPath) {
332				bios = VFileOpen(configPath, O_RDONLY);
333			}
334			if (bios && GBAIsBIOS(bios)) {
335				found = true;
336			} else if (bios) {
337				bios->close(bios);
338				bios = NULL;
339			}
340		}
341		if (!found) {
342			char path[PATH_MAX];
343			mCoreConfigDirectory(path, PATH_MAX);
344			strncat(path, PATH_SEP "gba_bios.bin", PATH_MAX - strlen(path));
345			bios = VFileOpen(path, O_RDONLY);
346			if (bios && GBIsBIOS(bios)) {
347				found = true;
348			} else if (bios) {
349				bios->close(bios);
350				bios = NULL;
351			}
352		}
353		if (bios) {
354			GBALoadBIOS(gba, bios);
355		}
356	}
357#endif
358
359	ARMReset(core->cpu);
360	if (core->opts.skipBios && gba->isPristine) {
361		GBASkipBIOS(core->board);
362	}
363}
364
365static void _GBACoreRunFrame(struct mCore* core) {
366	struct GBA* gba = core->board;
367	int32_t frameCounter = gba->video.frameCounter;
368	while (gba->video.frameCounter == frameCounter) {
369		ARMv4RunLoop(core->cpu);
370	}
371}
372
373static void _GBACoreRunLoop(struct mCore* core) {
374	ARMv4RunLoop(core->cpu);
375}
376
377static void _GBACoreStep(struct mCore* core) {
378	ARMv4Run(core->cpu);
379}
380
381static size_t _GBACoreStateSize(struct mCore* core) {
382	UNUSED(core);
383	return sizeof(struct GBASerializedState);
384}
385
386static bool _GBACoreLoadState(struct mCore* core, const void* state) {
387	return GBADeserialize(core->board, state);
388}
389
390static bool _GBACoreSaveState(struct mCore* core, void* state) {
391	GBASerialize(core->board, state);
392	return true;
393}
394
395static void _GBACoreSetKeys(struct mCore* core, uint32_t keys) {
396	struct GBACore* gbacore = (struct GBACore*) core;
397	gbacore->keys = keys;
398}
399
400static void _GBACoreAddKeys(struct mCore* core, uint32_t keys) {
401	struct GBACore* gbacore = (struct GBACore*) core;
402	gbacore->keys |= keys;
403}
404
405static void _GBACoreClearKeys(struct mCore* core, uint32_t keys) {
406	struct GBACore* gbacore = (struct GBACore*) core;
407	gbacore->keys &= ~keys;
408}
409
410static void _GBACoreSetCursorLocation(struct mCore* core, int x, int y) {
411	UNUSED(core);
412	UNUSED(x);
413	UNUSED(y);
414}
415
416static void _GBACoreSetCursorDown(struct mCore* core, bool down) {
417	UNUSED(core);
418	UNUSED(down);
419}
420
421static int32_t _GBACoreFrameCounter(const struct mCore* core) {
422	const struct GBA* gba = core->board;
423	return gba->video.frameCounter;
424}
425
426static int32_t _GBACoreFrameCycles(const struct mCore* core) {
427	UNUSED(core);
428	return VIDEO_TOTAL_LENGTH;
429}
430
431static int32_t _GBACoreFrequency(const struct mCore* core) {
432	UNUSED(core);
433	return GBA_ARM7TDMI_FREQUENCY;
434}
435
436static void _GBACoreGetGameTitle(const struct mCore* core, char* title) {
437	GBAGetGameTitle(core->board, title);
438}
439
440static void _GBACoreGetGameCode(const struct mCore* core, char* title) {
441	GBAGetGameCode(core->board, title);
442}
443
444static void _GBACoreSetPeripheral(struct mCore* core, int type, void* periph) {
445	struct GBA* gba = core->board;
446	switch (type) {
447	case mPERIPH_ROTATION:
448		gba->rotationSource = periph;
449		break;
450	case mPERIPH_RUMBLE:
451		gba->rumble = periph;
452		break;
453	case mPERIPH_GBA_LUMINANCE:
454		gba->luminanceSource = periph;
455		break;
456	default:
457		return;
458	}
459}
460
461static uint32_t _GBACoreBusRead8(struct mCore* core, uint32_t address) {
462	struct ARMCore* cpu = core->cpu;
463	return cpu->memory.load8(cpu, address, 0);
464}
465
466static uint32_t _GBACoreBusRead16(struct mCore* core, uint32_t address) {
467	struct ARMCore* cpu = core->cpu;
468	return cpu->memory.load16(cpu, address, 0);
469
470}
471
472static uint32_t _GBACoreBusRead32(struct mCore* core, uint32_t address) {
473	struct ARMCore* cpu = core->cpu;
474	return cpu->memory.load32(cpu, address, 0);
475}
476
477static void _GBACoreBusWrite8(struct mCore* core, uint32_t address, uint8_t value) {
478	struct ARMCore* cpu = core->cpu;
479	cpu->memory.store8(cpu, address, value, 0);
480}
481
482static void _GBACoreBusWrite16(struct mCore* core, uint32_t address, uint16_t value) {
483	struct ARMCore* cpu = core->cpu;
484	cpu->memory.store16(cpu, address, value, 0);
485}
486
487static void _GBACoreBusWrite32(struct mCore* core, uint32_t address, uint32_t value) {
488	struct ARMCore* cpu = core->cpu;
489	cpu->memory.store32(cpu, address, value, 0);
490}
491
492static uint32_t _GBACoreRawRead8(struct mCore* core, uint32_t address, int segment) {
493	UNUSED(segment);
494	struct ARMCore* cpu = core->cpu;
495	return GBAView8(cpu, address);
496}
497
498static uint32_t _GBACoreRawRead16(struct mCore* core, uint32_t address, int segment) {
499	UNUSED(segment);
500	struct ARMCore* cpu = core->cpu;
501	return GBAView16(cpu, address);
502}
503
504static uint32_t _GBACoreRawRead32(struct mCore* core, uint32_t address, int segment) {
505	UNUSED(segment);
506	struct ARMCore* cpu = core->cpu;
507	return GBAView32(cpu, address);
508}
509
510static void _GBACoreRawWrite8(struct mCore* core, uint32_t address, int segment, uint8_t value) {
511	UNUSED(segment);
512	struct ARMCore* cpu = core->cpu;
513	GBAPatch8(cpu, address, value, NULL);
514}
515
516static void _GBACoreRawWrite16(struct mCore* core, uint32_t address, int segment, uint16_t value) {
517	UNUSED(segment);
518	struct ARMCore* cpu = core->cpu;
519	GBAPatch16(cpu, address, value, NULL);
520}
521
522static void _GBACoreRawWrite32(struct mCore* core, uint32_t address, int segment, uint32_t value) {
523	UNUSED(segment);
524	struct ARMCore* cpu = core->cpu;
525	GBAPatch32(cpu, address, value, NULL);
526}
527
528#ifdef USE_DEBUGGERS
529static bool _GBACoreSupportsDebuggerType(struct mCore* core, enum mDebuggerType type) {
530	UNUSED(core);
531	switch (type) {
532	case DEBUGGER_CLI:
533		return true;
534#ifdef USE_GDB_STUB
535	case DEBUGGER_GDB:
536		return true;
537#endif
538	default:
539		return false;
540	}
541}
542
543static struct mDebuggerPlatform* _GBACoreDebuggerPlatform(struct mCore* core) {
544	struct GBACore* gbacore = (struct GBACore*) core;
545	if (!gbacore->debuggerPlatform) {
546		gbacore->debuggerPlatform = ARMDebuggerPlatformCreate();
547	}
548	return gbacore->debuggerPlatform;
549}
550
551static struct CLIDebuggerSystem* _GBACoreCliDebuggerSystem(struct mCore* core) {
552	return &GBACLIDebuggerCreate(core)->d;
553}
554
555static void _GBACoreAttachDebugger(struct mCore* core, struct mDebugger* debugger) {
556	if (core->debugger) {
557		GBADetachDebugger(core->board);
558	}
559	GBAAttachDebugger(core->board, debugger);
560	core->debugger = debugger;
561}
562
563static void _GBACoreDetachDebugger(struct mCore* core) {
564	GBADetachDebugger(core->board);
565	core->debugger = NULL;
566}
567#endif
568
569static struct mCheatDevice* _GBACoreCheatDevice(struct mCore* core) {
570	struct GBACore* gbacore = (struct GBACore*) core;
571	if (!gbacore->cheatDevice) {
572		gbacore->cheatDevice = GBACheatDeviceCreate();
573		((struct ARMCore*) core->cpu)->components[CPU_COMPONENT_CHEAT_DEVICE] = &gbacore->cheatDevice->d;
574		ARMHotplugAttach(core->cpu, CPU_COMPONENT_CHEAT_DEVICE);
575		gbacore->cheatDevice->p = core;
576	}
577	return gbacore->cheatDevice;
578}
579
580static size_t _GBACoreSavedataClone(struct mCore* core, void** sram) {
581	struct GBA* gba = core->board;
582	size_t size = GBASavedataSize(&gba->memory.savedata);
583	if (!size) {
584		*sram = NULL;
585		return 0;
586	}
587	*sram = malloc(size);
588	struct VFile* vf = VFileFromMemory(*sram, size);
589	if (!vf) {
590		free(*sram);
591		*sram = NULL;
592		return 0;
593	}
594	bool success = GBASavedataClone(&gba->memory.savedata, vf);
595	vf->close(vf);
596	if (!success) {
597		free(*sram);
598		*sram = NULL;
599		return 0;
600	}
601	return size;
602}
603
604static bool _GBACoreSavedataRestore(struct mCore* core, const void* sram, size_t size, bool writeback) {
605	struct VFile* vf = VFileMemChunk(sram, size);
606	if (!vf) {
607		return false;
608	}
609	struct GBA* gba = core->board;
610	bool success = true;
611	if (writeback) {
612		success = GBASavedataLoad(&gba->memory.savedata, vf);
613		vf->close(vf);
614	} else {
615		GBASavedataMask(&gba->memory.savedata, vf, true);
616	}
617	return success;
618}
619
620static size_t _GBACoreListVideoLayers(const struct mCore* core, const struct mCoreChannelInfo** info) {
621	UNUSED(core);
622	*info = _GBAVideoLayers;
623	return sizeof(_GBAVideoLayers) / sizeof(*_GBAVideoLayers);
624}
625
626static size_t _GBACoreListAudioChannels(const struct mCore* core, const struct mCoreChannelInfo** info) {
627	UNUSED(core);
628	*info = _GBAAudioChannels;
629	return sizeof(_GBAAudioChannels) / sizeof(*_GBAAudioChannels);
630}
631
632static void _GBACoreEnableVideoLayer(struct mCore* core, size_t id, bool enable) {
633	struct GBA* gba = core->board;
634	switch (id) {
635	case 0:
636	case 1:
637	case 2:
638	case 3:
639		gba->video.renderer->disableBG[id] = !enable;
640		break;
641	case 4:
642		gba->video.renderer->disableOBJ = !enable;
643		break;
644	default:
645		break;
646	}
647}
648
649static void _GBACoreEnableAudioChannel(struct mCore* core, size_t id, bool enable) {
650	struct GBA* gba = core->board;
651	switch (id) {
652	case 0:
653	case 1:
654	case 2:
655	case 3:
656		gba->audio.psg.forceDisableCh[id] = !enable;
657		break;
658	case 4:
659		gba->audio.forceDisableChA = !enable;
660	case 5:
661		gba->audio.forceDisableChB = !enable;
662		break;
663	default:
664		break;
665	}
666}
667
668struct mCore* GBACoreCreate(void) {
669	struct GBACore* gbacore = malloc(sizeof(*gbacore));
670	struct mCore* core = &gbacore->d;
671	memset(&core->opts, 0, sizeof(core->opts));
672	core->cpu = NULL;
673	core->board = NULL;
674	core->debugger = NULL;
675	core->init = _GBACoreInit;
676	core->deinit = _GBACoreDeinit;
677	core->platform = _GBACorePlatform;
678	core->setSync = _GBACoreSetSync;
679	core->loadConfig = _GBACoreLoadConfig;
680	core->desiredVideoDimensions = _GBACoreDesiredVideoDimensions;
681	core->setVideoBuffer = _GBACoreSetVideoBuffer;
682	core->getPixels = _GBACoreGetPixels;
683	core->putPixels = _GBACorePutPixels;
684	core->getAudioChannel = _GBACoreGetAudioChannel;
685	core->setAudioBufferSize = _GBACoreSetAudioBufferSize;
686	core->getAudioBufferSize = _GBACoreGetAudioBufferSize;
687	core->addCoreCallbacks = _GBACoreAddCoreCallbacks;
688	core->clearCoreCallbacks = _GBACoreClearCoreCallbacks;
689	core->setAVStream = _GBACoreSetAVStream;
690	core->isROM = GBAIsROM;
691	core->loadROM = _GBACoreLoadROM;
692	core->loadBIOS = _GBACoreLoadBIOS;
693	core->loadSave = _GBACoreLoadSave;
694	core->loadTemporarySave = _GBACoreLoadTemporarySave;
695	core->loadPatch = _GBACoreLoadPatch;
696	core->unloadROM = _GBACoreUnloadROM;
697	core->checksum = _GBACoreChecksum;
698	core->reset = _GBACoreReset;
699	core->runFrame = _GBACoreRunFrame;
700	core->runLoop = _GBACoreRunLoop;
701	core->step = _GBACoreStep;
702	core->stateSize = _GBACoreStateSize;
703	core->loadState = _GBACoreLoadState;
704	core->saveState = _GBACoreSaveState;
705	core->setKeys = _GBACoreSetKeys;
706	core->addKeys = _GBACoreAddKeys;
707	core->clearKeys = _GBACoreClearKeys;
708	core->setCursorLocation = _GBACoreSetCursorLocation;
709	core->setCursorDown = _GBACoreSetCursorDown;
710	core->frameCounter = _GBACoreFrameCounter;
711	core->frameCycles = _GBACoreFrameCycles;
712	core->frequency = _GBACoreFrequency;
713	core->getGameTitle = _GBACoreGetGameTitle;
714	core->getGameCode = _GBACoreGetGameCode;
715	core->setPeripheral = _GBACoreSetPeripheral;
716	core->busRead8 = _GBACoreBusRead8;
717	core->busRead16 = _GBACoreBusRead16;
718	core->busRead32 = _GBACoreBusRead32;
719	core->busWrite8 = _GBACoreBusWrite8;
720	core->busWrite16 = _GBACoreBusWrite16;
721	core->busWrite32 = _GBACoreBusWrite32;
722	core->rawRead8 = _GBACoreRawRead8;
723	core->rawRead16 = _GBACoreRawRead16;
724	core->rawRead32 = _GBACoreRawRead32;
725	core->rawWrite8 = _GBACoreRawWrite8;
726	core->rawWrite16 = _GBACoreRawWrite16;
727	core->rawWrite32 = _GBACoreRawWrite32;
728#ifdef USE_DEBUGGERS
729	core->supportsDebuggerType = _GBACoreSupportsDebuggerType;
730	core->debuggerPlatform = _GBACoreDebuggerPlatform;
731	core->cliDebuggerSystem = _GBACoreCliDebuggerSystem;
732	core->attachDebugger = _GBACoreAttachDebugger;
733	core->detachDebugger = _GBACoreDetachDebugger;
734#endif
735	core->cheatDevice = _GBACoreCheatDevice;
736	core->savedataClone = _GBACoreSavedataClone;
737	core->savedataRestore = _GBACoreSavedataRestore;
738	core->listVideoLayers = _GBACoreListVideoLayers;
739	core->listAudioChannels = _GBACoreListAudioChannels;
740	core->enableVideoLayer = _GBACoreEnableVideoLayer;
741	core->enableAudioChannel = _GBACoreEnableAudioChannel;
742	return core;
743}