all repos — mgba @ 63ed7421ce05174608dd1799f8e7b0a5f01d5c3d

mGBA Game Boy Advance Emulator

src/gb/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/gb/core.h>
  7
  8#include <mgba/core/core.h>
  9#include <mgba/internal/gb/cheats.h>
 10#include <mgba/internal/gb/extra/cli.h>
 11#include <mgba/internal/gb/gb.h>
 12#include <mgba/internal/gb/mbc.h>
 13#include <mgba/internal/gb/overrides.h>
 14#include <mgba/internal/gb/renderers/software.h>
 15#include <mgba/internal/gb/renderers/proxy.h>
 16#include <mgba/internal/gb/serialize.h>
 17#include <mgba/internal/lr35902/lr35902.h>
 18#include <mgba/internal/lr35902/debugger/debugger.h>
 19#include <mgba-util/crc32.h>
 20#include <mgba-util/memory.h>
 21#include <mgba-util/patch.h>
 22#include <mgba-util/vfs.h>
 23
 24const static struct mCoreChannelInfo _GBVideoLayers[] = {
 25	{ 0, "bg", "Background", NULL },
 26	{ 1, "obj", "Objects", NULL },
 27	{ 2, "win", "Window", NULL },
 28};
 29
 30const static struct mCoreChannelInfo _GBAudioChannels[] = {
 31	{ 0, "ch0", "Channel 0", "Square/Sweep" },
 32	{ 1, "ch1", "Channel 1", "Square" },
 33	{ 2, "ch2", "Channel 2", "PCM" },
 34	{ 3, "ch3", "Channel 3", "Noise" },
 35};
 36
 37struct mVideoLogContext;
 38struct GBCore {
 39	struct mCore d;
 40	struct GBVideoSoftwareRenderer renderer;
 41	struct GBVideoProxyRenderer proxyRenderer;
 42	struct mVideoLogContext* logContext;
 43	struct mCoreCallbacks logCallbacks;
 44	uint8_t keys;
 45	struct mCPUComponent* components[CPU_COMPONENT_MAX];
 46	const struct Configuration* overrides;
 47	struct mDebuggerPlatform* debuggerPlatform;
 48	struct mCheatDevice* cheatDevice;
 49};
 50
 51static bool _GBCoreInit(struct mCore* core) {
 52	struct GBCore* gbcore = (struct GBCore*) core;
 53
 54	struct LR35902Core* cpu = anonymousMemoryMap(sizeof(struct LR35902Core));
 55	struct GB* gb = anonymousMemoryMap(sizeof(struct GB));
 56	if (!cpu || !gb) {
 57		free(cpu);
 58		free(gb);
 59		return false;
 60	}
 61	core->cpu = cpu;
 62	core->board = gb;
 63	gbcore->overrides = NULL;
 64	gbcore->debuggerPlatform = NULL;
 65	gbcore->cheatDevice = NULL;
 66
 67	GBCreate(gb);
 68	memset(gbcore->components, 0, sizeof(gbcore->components));
 69	LR35902SetComponents(cpu, &gb->d, CPU_COMPONENT_MAX, gbcore->components);
 70	LR35902Init(cpu);
 71	mRTCGenericSourceInit(&core->rtc, core);
 72	gb->memory.rtc = &core->rtc.d;
 73
 74	GBVideoSoftwareRendererCreate(&gbcore->renderer);
 75	gbcore->renderer.outputBuffer = NULL;
 76
 77	gbcore->keys = 0;
 78	gb->keySource = &gbcore->keys;
 79
 80#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
 81	mDirectorySetInit(&core->dirs);
 82#endif
 83	
 84	return true;
 85}
 86
 87static void _GBCoreDeinit(struct mCore* core) {
 88	LR35902Deinit(core->cpu);
 89	GBDestroy(core->board);
 90	mappedMemoryFree(core->cpu, sizeof(struct LR35902Core));
 91	mappedMemoryFree(core->board, sizeof(struct GB));
 92#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
 93	mDirectorySetDeinit(&core->dirs);
 94#endif
 95
 96	struct GBCore* gbcore = (struct GBCore*) core;
 97	free(gbcore->debuggerPlatform);
 98	if (gbcore->cheatDevice) {
 99		mCheatDeviceDestroy(gbcore->cheatDevice);
100	}
101	free(gbcore->cheatDevice);
102	mCoreConfigFreeOpts(&core->opts);
103	free(core);
104}
105
106static enum mPlatform _GBCorePlatform(const struct mCore* core) {
107	UNUSED(core);
108	return PLATFORM_GB;
109}
110
111static void _GBCoreSetSync(struct mCore* core, struct mCoreSync* sync) {
112	struct GB* gb = core->board;
113	gb->sync = sync;
114}
115
116static void _GBCoreLoadConfig(struct mCore* core, const struct mCoreConfig* config) {
117	UNUSED(config);
118
119	struct GB* gb = core->board;
120	if (core->opts.mute) {
121		gb->audio.masterVolume = 0;
122	} else {
123		gb->audio.masterVolume = core->opts.volume;
124	}
125	gb->video.frameskip = core->opts.frameskip;
126
127	int color;
128	if (mCoreConfigGetIntValue(&core->config, "gb.pal[0]", &color)) {
129			GBVideoSetPalette(&gb->video, 0, color);
130	}
131	if (mCoreConfigGetIntValue(&core->config, "gb.pal[1]", &color)) {
132			GBVideoSetPalette(&gb->video, 1, color);
133	}
134	if (mCoreConfigGetIntValue(&core->config, "gb.pal[2]", &color)) {
135			GBVideoSetPalette(&gb->video, 2, color);
136	}
137	if (mCoreConfigGetIntValue(&core->config, "gb.pal[3]", &color)) {
138			GBVideoSetPalette(&gb->video, 3, color);
139	}
140
141	mCoreConfigCopyValue(&core->config, config, "gb.bios");
142	mCoreConfigCopyValue(&core->config, config, "gbc.bios");
143
144#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
145	struct GBCore* gbcore = (struct GBCore*) core;
146	gbcore->overrides = mCoreConfigGetOverridesConst(config);
147#endif
148}
149
150static void _GBCoreDesiredVideoDimensions(struct mCore* core, unsigned* width, unsigned* height) {
151	UNUSED(core);
152	*width = GB_VIDEO_HORIZONTAL_PIXELS;
153	*height = GB_VIDEO_VERTICAL_PIXELS;
154}
155
156static void _GBCoreSetVideoBuffer(struct mCore* core, color_t* buffer, size_t stride) {
157	struct GBCore* gbcore = (struct GBCore*) core;
158	gbcore->renderer.outputBuffer = buffer;
159	gbcore->renderer.outputBufferStride = stride;
160}
161
162static void _GBCoreGetPixels(struct mCore* core, const void** buffer, size_t* stride) {
163	struct GBCore* gbcore = (struct GBCore*) core;
164	gbcore->renderer.d.getPixels(&gbcore->renderer.d, stride, buffer);
165}
166
167static void _GBCorePutPixels(struct mCore* core, const void* buffer, size_t stride) {
168	struct GBCore* gbcore = (struct GBCore*) core;
169	gbcore->renderer.d.putPixels(&gbcore->renderer.d, stride, buffer);
170}
171
172static struct blip_t* _GBCoreGetAudioChannel(struct mCore* core, int ch) {
173	struct GB* gb = core->board;
174	switch (ch) {
175	case 0:
176		return gb->audio.left;
177	case 1:
178		return gb->audio.right;
179	default:
180		return NULL;
181	}
182}
183
184static void _GBCoreSetAudioBufferSize(struct mCore* core, size_t samples) {
185	struct GB* gb = core->board;
186	GBAudioResizeBuffer(&gb->audio, samples);
187}
188
189static size_t _GBCoreGetAudioBufferSize(struct mCore* core) {
190	struct GB* gb = core->board;
191	return gb->audio.samples;
192}
193
194static void _GBCoreAddCoreCallbacks(struct mCore* core, struct mCoreCallbacks* coreCallbacks) {
195	struct GB* gb = core->board;
196	*mCoreCallbacksListAppend(&gb->coreCallbacks) = *coreCallbacks;
197}
198
199static void _GBCoreClearCoreCallbacks(struct mCore* core) {
200	struct GB* gb = core->board;
201	mCoreCallbacksListClear(&gb->coreCallbacks);
202}
203
204static void _GBCoreSetAVStream(struct mCore* core, struct mAVStream* stream) {
205	struct GB* gb = core->board;
206	gb->stream = stream;
207	if (stream && stream->videoDimensionsChanged) {
208		stream->videoDimensionsChanged(stream, GB_VIDEO_HORIZONTAL_PIXELS, GB_VIDEO_VERTICAL_PIXELS);
209	}
210}
211
212static bool _GBCoreLoadROM(struct mCore* core, struct VFile* vf) {
213	return GBLoadROM(core->board, vf);
214}
215
216static bool _GBCoreLoadBIOS(struct mCore* core, struct VFile* vf, int type) {
217	UNUSED(type);
218	GBLoadBIOS(core->board, vf);
219	return true;
220}
221
222static bool _GBCoreLoadSave(struct mCore* core, struct VFile* vf) {
223	return GBLoadSave(core->board, vf);
224}
225
226static bool _GBCoreLoadTemporarySave(struct mCore* core, struct VFile* vf) {
227	struct GB* gb = core->board;
228	GBSavedataMask(gb, vf, false);
229	return true; // TODO: Return a real value
230}
231
232static bool _GBCoreLoadPatch(struct mCore* core, struct VFile* vf) {
233	if (!vf) {
234		return false;
235	}
236	struct Patch patch;
237	if (!loadPatch(vf, &patch)) {
238		return false;
239	}
240	GBApplyPatch(core->board, &patch);
241	return true;
242}
243
244static void _GBCoreUnloadROM(struct mCore* core) {
245	struct GBCore* gbcore = (struct GBCore*) core;
246	struct LR35902Core* cpu = core->cpu;
247	if (gbcore->cheatDevice) {
248		LR35902HotplugDetach(cpu, CPU_COMPONENT_CHEAT_DEVICE);
249		cpu->components[CPU_COMPONENT_CHEAT_DEVICE] = NULL;
250		mCheatDeviceDestroy(gbcore->cheatDevice);
251		gbcore->cheatDevice = NULL;
252	}
253	return GBUnloadROM(core->board);
254}
255
256static void _GBCoreChecksum(const struct mCore* core, void* data, enum mCoreChecksumType type) {
257	struct GB* gb = (struct GB*) core->board;
258	switch (type) {
259	case CHECKSUM_CRC32:
260		memcpy(data, &gb->romCrc32, sizeof(gb->romCrc32));
261		break;
262	}
263	return;
264}
265
266static void _GBCoreReset(struct mCore* core) {
267	struct GBCore* gbcore = (struct GBCore*) core;
268	struct GB* gb = (struct GB*) core->board;
269	if (gbcore->renderer.outputBuffer) {
270		GBVideoAssociateRenderer(&gb->video, &gbcore->renderer.d);
271	}
272
273	if (gb->memory.rom) {
274		struct GBCartridgeOverride override;
275		const struct GBCartridge* cart = (const struct GBCartridge*) &gb->memory.rom[0x100];
276		override.headerCrc32 = doCrc32(cart, sizeof(*cart));
277		if (GBOverrideFind(gbcore->overrides, &override)) {
278			GBOverrideApply(gb, &override);
279		}
280	}
281
282#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
283	if (!gb->biosVf && core->opts.useBios) {
284		struct VFile* bios = NULL;
285		bool found = false;
286		if (core->opts.bios) {
287			bios = VFileOpen(core->opts.bios, O_RDONLY);
288			if (bios && GBIsBIOS(bios)) {
289				found = true;
290			} else if (bios) {
291				bios->close(bios);
292				bios = NULL;
293			}
294		}
295		if (!found) {
296			GBDetectModel(gb);
297			const char* configPath = NULL;
298
299			switch (gb->model) {
300			case GB_MODEL_DMG:
301			case GB_MODEL_SGB: // TODO
302				configPath = mCoreConfigGetValue(&core->config, "gb.bios");
303				break;
304			case GB_MODEL_CGB:
305			case GB_MODEL_AGB:
306				configPath = mCoreConfigGetValue(&core->config, "gbc.bios");
307				break;
308			default:
309				break;
310			};
311			if (configPath) {
312				bios = VFileOpen(configPath, O_RDONLY);
313			}
314			if (bios && GBIsBIOS(bios)) {
315				found = true;
316			} else if (bios) {
317				bios->close(bios);
318				bios = NULL;
319			}
320		}
321		if (!found) {
322			char path[PATH_MAX];
323			mCoreConfigDirectory(path, PATH_MAX);
324			switch (gb->model) {
325			case GB_MODEL_DMG:
326			case GB_MODEL_SGB: // TODO
327				strncat(path, PATH_SEP "gb_bios.bin", PATH_MAX - strlen(path));
328				break;
329			case GB_MODEL_CGB:
330			case GB_MODEL_AGB:
331				strncat(path, PATH_SEP "gbc_bios.bin", PATH_MAX - strlen(path));
332				break;
333			default:
334				break;
335			};
336			bios = VFileOpen(path, O_RDONLY);
337			if (bios && GBIsBIOS(bios)) {
338				found = true;
339			} else if (bios) {
340				bios->close(bios);
341				bios = NULL;
342			}
343		}
344		if (bios) {
345			GBLoadBIOS(gb, bios);
346		}
347	}
348#endif
349
350	LR35902Reset(core->cpu);
351}
352
353static void _GBCoreRunFrame(struct mCore* core) {
354	struct GB* gb = core->board;
355	int32_t frameCounter = gb->video.frameCounter;
356	while (gb->video.frameCounter == frameCounter) {
357		LR35902Run(core->cpu);
358	}
359}
360
361static void _GBCoreRunLoop(struct mCore* core) {
362	LR35902Run(core->cpu);
363}
364
365static void _GBCoreStep(struct mCore* core) {
366	struct LR35902Core* cpu = core->cpu;
367	do {
368		LR35902Tick(cpu);
369	} while (cpu->executionState != LR35902_CORE_FETCH);
370}
371
372static size_t _GBCoreStateSize(struct mCore* core) {
373	UNUSED(core);
374	return sizeof(struct GBSerializedState);
375}
376
377static bool _GBCoreLoadState(struct mCore* core, const void* state) {
378	return GBDeserialize(core->board, state);
379}
380
381static bool _GBCoreSaveState(struct mCore* core, void* state) {
382	struct LR35902Core* cpu = core->cpu;
383	while (cpu->executionState != LR35902_CORE_FETCH) {
384		LR35902Tick(cpu);
385	}
386	GBSerialize(core->board, state);
387	return true;
388}
389
390static void _GBCoreSetKeys(struct mCore* core, uint32_t keys) {
391	struct GBCore* gbcore = (struct GBCore*) core;
392	gbcore->keys = keys;
393}
394
395static void _GBCoreAddKeys(struct mCore* core, uint32_t keys) {
396	struct GBCore* gbcore = (struct GBCore*) core;
397	gbcore->keys |= keys;
398}
399
400static void _GBCoreClearKeys(struct mCore* core, uint32_t keys) {
401	struct GBCore* gbcore = (struct GBCore*) core;
402	gbcore->keys &= ~keys;
403}
404
405static int32_t _GBCoreFrameCounter(const struct mCore* core) {
406	const struct GB* gb = core->board;
407	return gb->video.frameCounter;
408}
409
410static int32_t _GBCoreFrameCycles(const  struct mCore* core) {
411	UNUSED(core);
412	return GB_VIDEO_TOTAL_LENGTH;
413}
414
415static int32_t _GBCoreFrequency(const struct mCore* core) {
416	UNUSED(core);
417	// TODO: GB differences
418	return DMG_LR35902_FREQUENCY;
419}
420
421static void _GBCoreGetGameTitle(const struct mCore* core, char* title) {
422	GBGetGameTitle(core->board, title);
423}
424
425static void _GBCoreGetGameCode(const struct mCore* core, char* title) {
426	GBGetGameCode(core->board, title);
427}
428
429static void _GBCoreSetPeripheral(struct mCore* core, int type, void* periph) {
430	struct GB* gb = core->board;
431	switch (type) {
432	case mPERIPH_ROTATION:
433		gb->memory.rotation = periph;
434		break;
435	case mPERIPH_RUMBLE:
436		gb->memory.rumble = periph;
437		break;
438	default:
439		return;
440	}
441}
442
443static uint32_t _GBCoreBusRead8(struct mCore* core, uint32_t address) {
444	struct LR35902Core* cpu = core->cpu;
445	return cpu->memory.load8(cpu, address);
446}
447
448static uint32_t _GBCoreBusRead16(struct mCore* core, uint32_t address) {
449	struct LR35902Core* cpu = core->cpu;
450	return cpu->memory.load8(cpu, address) | (cpu->memory.load8(cpu, address + 1) << 8);
451}
452
453static uint32_t _GBCoreBusRead32(struct mCore* core, uint32_t address) {
454	struct LR35902Core* cpu = core->cpu;
455	return cpu->memory.load8(cpu, address) | (cpu->memory.load8(cpu, address + 1) << 8) |
456	       (cpu->memory.load8(cpu, address + 2) << 16) | (cpu->memory.load8(cpu, address + 3) << 24);
457}
458
459static void _GBCoreBusWrite8(struct mCore* core, uint32_t address, uint8_t value) {
460	struct LR35902Core* cpu = core->cpu;
461	cpu->memory.store8(cpu, address, value);
462}
463
464static void _GBCoreBusWrite16(struct mCore* core, uint32_t address, uint16_t value) {
465	struct LR35902Core* cpu = core->cpu;
466	cpu->memory.store8(cpu, address, value);
467	cpu->memory.store8(cpu, address + 1, value >> 8);
468}
469
470static void _GBCoreBusWrite32(struct mCore* core, uint32_t address, uint32_t value) {
471	struct LR35902Core* cpu = core->cpu;
472	cpu->memory.store8(cpu, address, value);
473	cpu->memory.store8(cpu, address + 1, value >> 8);
474	cpu->memory.store8(cpu, address + 2, value >> 16);
475	cpu->memory.store8(cpu, address + 3, value >> 24);
476}
477
478static uint32_t _GBCoreRawRead8(struct mCore* core, uint32_t address, int segment) {
479	struct LR35902Core* cpu = core->cpu;
480	return GBView8(cpu, address, segment);
481}
482
483static uint32_t _GBCoreRawRead16(struct mCore* core, uint32_t address, int segment) {
484	struct LR35902Core* cpu = core->cpu;
485	return GBView8(cpu, address, segment) | (GBView8(cpu, address + 1, segment) << 8);
486}
487
488static uint32_t _GBCoreRawRead32(struct mCore* core, uint32_t address, int segment) {
489	struct LR35902Core* cpu = core->cpu;
490	return GBView8(cpu, address, segment) | (GBView8(cpu, address + 1, segment) << 8) |
491	       (GBView8(cpu, address + 2, segment) << 16) | (GBView8(cpu, address + 3, segment) << 24);
492}
493
494static void _GBCoreRawWrite8(struct mCore* core, uint32_t address, int segment, uint8_t value) {
495	struct LR35902Core* cpu = core->cpu;
496	GBPatch8(cpu, address, value, NULL, segment);
497}
498
499static void _GBCoreRawWrite16(struct mCore* core, uint32_t address, int segment, uint16_t value) {
500	struct LR35902Core* cpu = core->cpu;
501	GBPatch8(cpu, address, value, NULL, segment);
502	GBPatch8(cpu, address + 1, value >> 8, NULL, segment);
503}
504
505static void _GBCoreRawWrite32(struct mCore* core, uint32_t address, int segment, uint32_t value) {
506	struct LR35902Core* cpu = core->cpu;
507	GBPatch8(cpu, address, value, NULL, segment);
508	GBPatch8(cpu, address + 1, value >> 8, NULL, segment);
509	GBPatch8(cpu, address + 2, value >> 16, NULL, segment);
510	GBPatch8(cpu, address + 3, value >> 24, NULL, segment);
511}
512
513#ifdef USE_DEBUGGERS
514static bool _GBCoreSupportsDebuggerType(struct mCore* core, enum mDebuggerType type) {
515	UNUSED(core);
516	switch (type) {
517	case DEBUGGER_CLI:
518		return true;
519	default:
520		return false;
521	}
522}
523
524static struct mDebuggerPlatform* _GBCoreDebuggerPlatform(struct mCore* core) {
525	struct GBCore* gbcore = (struct GBCore*) core;
526	if (!gbcore->debuggerPlatform) {
527		gbcore->debuggerPlatform = LR35902DebuggerPlatformCreate();
528	}
529	return gbcore->debuggerPlatform;
530}
531
532static struct CLIDebuggerSystem* _GBCoreCliDebuggerSystem(struct mCore* core) {
533	return GBCLIDebuggerCreate(core);
534}
535
536static void _GBCoreAttachDebugger(struct mCore* core, struct mDebugger* debugger) {
537	struct LR35902Core* cpu = core->cpu;
538	if (core->debugger) {
539		LR35902HotplugDetach(cpu, CPU_COMPONENT_DEBUGGER);
540	}
541	cpu->components[CPU_COMPONENT_DEBUGGER] = &debugger->d;
542	LR35902HotplugAttach(cpu, CPU_COMPONENT_DEBUGGER);
543	core->debugger = debugger;
544}
545
546static void _GBCoreDetachDebugger(struct mCore* core) {
547	struct LR35902Core* cpu = core->cpu;
548	if (core->debugger) {
549		LR35902HotplugDetach(cpu, CPU_COMPONENT_DEBUGGER);
550	}
551	cpu->components[CPU_COMPONENT_DEBUGGER] = NULL;
552	core->debugger = NULL;
553}
554#endif
555
556static struct mCheatDevice* _GBCoreCheatDevice(struct mCore* core) {
557	struct GBCore* gbcore = (struct GBCore*) core;
558	if (!gbcore->cheatDevice) {
559		gbcore->cheatDevice = GBCheatDeviceCreate();
560		((struct LR35902Core*) core->cpu)->components[CPU_COMPONENT_CHEAT_DEVICE] = &gbcore->cheatDevice->d;
561		LR35902HotplugAttach(core->cpu, CPU_COMPONENT_CHEAT_DEVICE);
562		gbcore->cheatDevice->p = core;
563	}
564	return gbcore->cheatDevice;
565}
566
567static size_t _GBCoreSavedataClone(struct mCore* core, void** sram) {
568	struct GB* gb = core->board;
569	struct VFile* vf = gb->sramVf;
570	if (vf) {
571		*sram = malloc(vf->size(vf));
572		vf->seek(vf, 0, SEEK_SET);
573		return vf->read(vf, *sram, vf->size(vf));
574	}
575	*sram = malloc(gb->sramSize);
576	memcpy(*sram, gb->memory.sram, gb->sramSize);
577	return gb->sramSize;
578}
579
580static bool _GBCoreSavedataRestore(struct mCore* core, const void* sram, size_t size, bool writeback) {
581	struct GB* gb = core->board;
582	if (!writeback) {
583		struct VFile* vf = VFileMemChunk(sram, size);
584		GBSavedataMask(gb, vf, true);
585		return true;
586	}
587	struct VFile* vf = gb->sramVf;
588	if (vf) {
589		vf->seek(vf, 0, SEEK_SET);
590		return vf->write(vf, sram, size) > 0;
591	}
592	if (size > 0x20000) {
593		size = 0x20000;
594	}
595	GBResizeSram(gb, size);
596	memcpy(gb->memory.sram, sram, size);
597	return true;
598}
599
600static size_t _GBCoreListVideoLayers(const struct mCore* core, const struct mCoreChannelInfo** info) {
601	UNUSED(core);
602	*info = _GBVideoLayers;
603	return sizeof(_GBVideoLayers) / sizeof(*_GBVideoLayers);
604}
605
606static size_t _GBCoreListAudioChannels(const struct mCore* core, const struct mCoreChannelInfo** info) {
607	UNUSED(core);
608	*info = _GBAudioChannels;
609	return sizeof(_GBAudioChannels) / sizeof(*_GBAudioChannels);
610}
611
612static void _GBCoreEnableVideoLayer(struct mCore* core, size_t id, bool enable) {
613	struct GB* gb = core->board;
614	switch (id) {
615	case 0:
616		gb->video.renderer->disableBG = !enable;
617		break;
618	case 1:
619		gb->video.renderer->disableOBJ = !enable;
620		break;
621	case 2:
622		gb->video.renderer->disableWIN = !enable;
623		break;
624	default:
625		break;
626	}
627}
628
629static void _GBCoreEnableAudioChannel(struct mCore* core, size_t id, bool enable) {
630	struct GB* gb = core->board;
631	switch (id) {
632	case 0:
633	case 1:
634	case 2:
635	case 3:
636		gb->audio.forceDisableCh[id] = !enable;
637		break;
638	default:
639		break;
640	}
641}
642
643static void _GBCoreStartVideoLog(struct mCore* core, struct mVideoLogContext* context) {
644	struct GBCore* gbcore = (struct GBCore*) core;
645	struct GB* gb = core->board;
646	gbcore->logContext = context;
647
648	int channelId = mVideoLoggerAddChannel(context);
649	gbcore->proxyRenderer.logger = malloc(sizeof(struct mVideoLogger));
650	mVideoLoggerRendererCreate(gbcore->proxyRenderer.logger, false);
651	mVideoLoggerAttachChannel(gbcore->proxyRenderer.logger, context, channelId);
652	gbcore->proxyRenderer.logger->block = false;
653
654	GBVideoProxyRendererCreate(&gbcore->proxyRenderer, &gbcore->renderer.d);
655	GBVideoProxyRendererShim(&gb->video, &gbcore->proxyRenderer);
656}
657
658static void _GBCoreEndVideoLog(struct mCore* core) {
659	struct GBCore* gbcore = (struct GBCore*) core;
660	struct GB* gb = core->board;
661	GBVideoProxyRendererUnshim(&gb->video, &gbcore->proxyRenderer);
662	free(gbcore->proxyRenderer.logger);
663	gbcore->proxyRenderer.logger = NULL;
664}
665
666struct mCore* GBCoreCreate(void) {
667	struct GBCore* gbcore = malloc(sizeof(*gbcore));
668	struct mCore* core = &gbcore->d;
669	memset(&core->opts, 0, sizeof(core->opts));
670	core->cpu = NULL;
671	core->board = NULL;
672	core->debugger = NULL;
673	core->init = _GBCoreInit;
674	core->deinit = _GBCoreDeinit;
675	core->platform = _GBCorePlatform;
676	core->setSync = _GBCoreSetSync;
677	core->loadConfig = _GBCoreLoadConfig;
678	core->desiredVideoDimensions = _GBCoreDesiredVideoDimensions;
679	core->setVideoBuffer = _GBCoreSetVideoBuffer;
680	core->getPixels = _GBCoreGetPixels;
681	core->putPixels = _GBCorePutPixels;
682	core->getAudioChannel = _GBCoreGetAudioChannel;
683	core->setAudioBufferSize = _GBCoreSetAudioBufferSize;
684	core->getAudioBufferSize = _GBCoreGetAudioBufferSize;
685	core->setAVStream = _GBCoreSetAVStream;
686	core->addCoreCallbacks = _GBCoreAddCoreCallbacks;
687	core->clearCoreCallbacks = _GBCoreClearCoreCallbacks;
688	core->isROM = GBIsROM;
689	core->loadROM = _GBCoreLoadROM;
690	core->loadBIOS = _GBCoreLoadBIOS;
691	core->loadSave = _GBCoreLoadSave;
692	core->loadTemporarySave = _GBCoreLoadTemporarySave;
693	core->loadPatch = _GBCoreLoadPatch;
694	core->unloadROM = _GBCoreUnloadROM;
695	core->checksum = _GBCoreChecksum;
696	core->reset = _GBCoreReset;
697	core->runFrame = _GBCoreRunFrame;
698	core->runLoop = _GBCoreRunLoop;
699	core->step = _GBCoreStep;
700	core->stateSize = _GBCoreStateSize;
701	core->loadState = _GBCoreLoadState;
702	core->saveState = _GBCoreSaveState;
703	core->setKeys = _GBCoreSetKeys;
704	core->addKeys = _GBCoreAddKeys;
705	core->clearKeys = _GBCoreClearKeys;
706	core->frameCounter = _GBCoreFrameCounter;
707	core->frameCycles = _GBCoreFrameCycles;
708	core->frequency = _GBCoreFrequency;
709	core->getGameTitle = _GBCoreGetGameTitle;
710	core->getGameCode = _GBCoreGetGameCode;
711	core->setPeripheral = _GBCoreSetPeripheral;
712	core->busRead8 = _GBCoreBusRead8;
713	core->busRead16 = _GBCoreBusRead16;
714	core->busRead32 = _GBCoreBusRead32;
715	core->busWrite8 = _GBCoreBusWrite8;
716	core->busWrite16 = _GBCoreBusWrite16;
717	core->busWrite32 = _GBCoreBusWrite32;
718	core->rawRead8 = _GBCoreRawRead8;
719	core->rawRead16 = _GBCoreRawRead16;
720	core->rawRead32 = _GBCoreRawRead32;
721	core->rawWrite8 = _GBCoreRawWrite8;
722	core->rawWrite16 = _GBCoreRawWrite16;
723	core->rawWrite32 = _GBCoreRawWrite32;
724#ifdef USE_DEBUGGERS
725	core->supportsDebuggerType = _GBCoreSupportsDebuggerType;
726	core->debuggerPlatform = _GBCoreDebuggerPlatform;
727	core->cliDebuggerSystem = _GBCoreCliDebuggerSystem;
728	core->attachDebugger = _GBCoreAttachDebugger;
729	core->detachDebugger = _GBCoreDetachDebugger;
730#endif
731	core->cheatDevice = _GBCoreCheatDevice;
732	core->savedataClone = _GBCoreSavedataClone;
733	core->savedataRestore = _GBCoreSavedataRestore;
734	core->listVideoLayers = _GBCoreListVideoLayers;
735	core->listAudioChannels = _GBCoreListAudioChannels;
736	core->enableVideoLayer = _GBCoreEnableVideoLayer;
737	core->enableAudioChannel = _GBCoreEnableAudioChannel;
738#ifndef MINIMAL_CORE
739	core->startVideoLog = _GBCoreStartVideoLog;
740	core->endVideoLog = _GBCoreEndVideoLog;
741#endif
742	return core;
743}
744
745#ifndef MINIMAL_CORE
746static void _GBVLPStartFrameCallback(void *context) {
747	struct mCore* core = context;
748	struct GBCore* gbcore = (struct GBCore*) core;
749	struct GB* gb = core->board;
750
751	if (!mVideoLoggerRendererRun(gbcore->proxyRenderer.logger, true)) {
752		GBVideoProxyRendererUnshim(&gb->video, &gbcore->proxyRenderer);
753		mVideoLogContextRewind(gbcore->logContext, core);
754		GBVideoProxyRendererShim(&gb->video, &gbcore->proxyRenderer);
755
756		// Make sure CPU loop never spins
757		GBHalt(gb->cpu);
758		gb->memory.ie = 0;
759		gb->memory.ime = false;
760	}
761}
762
763static bool _GBVLPInit(struct mCore* core) {
764	struct GBCore* gbcore = (struct GBCore*) core;
765	if (!_GBCoreInit(core)) {
766		return false;
767	}
768	gbcore->proxyRenderer.logger = malloc(sizeof(struct mVideoLogger));
769	mVideoLoggerRendererCreate(gbcore->proxyRenderer.logger, true);
770	GBVideoProxyRendererCreate(&gbcore->proxyRenderer, NULL);
771	memset(&gbcore->logCallbacks, 0, sizeof(gbcore->logCallbacks));
772	gbcore->logCallbacks.videoFrameStarted = _GBVLPStartFrameCallback;
773	gbcore->logCallbacks.context = core;
774	core->addCoreCallbacks(core, &gbcore->logCallbacks);
775	return true;
776}
777
778static void _GBVLPDeinit(struct mCore* core) {
779	struct GBCore* gbcore = (struct GBCore*) core;
780	if (gbcore->logContext) {
781		mVideoLogContextDestroy(core, gbcore->logContext);
782	}
783	_GBCoreDeinit(core);
784}
785
786static void _GBVLPReset(struct mCore* core) {
787	struct GBCore* gbcore = (struct GBCore*) core;
788	struct GB* gb = (struct GB*) core->board;
789	if (gb->video.renderer == &gbcore->proxyRenderer.d) {
790		GBVideoProxyRendererUnshim(&gb->video, &gbcore->proxyRenderer);
791	} else if (gbcore->renderer.outputBuffer) {
792		struct GBVideoRenderer* renderer = &gbcore->renderer.d;
793		GBVideoAssociateRenderer(&gb->video, renderer);
794	}
795
796	LR35902Reset(core->cpu);
797	mVideoLogContextRewind(gbcore->logContext, core);
798	GBVideoProxyRendererShim(&gb->video, &gbcore->proxyRenderer);
799
800	// Make sure CPU loop never spins
801	GBHalt(gb->cpu);
802	gb->memory.ie = 0;
803	gb->memory.ime = false;
804}
805
806static bool _GBVLPLoadROM(struct mCore* core, struct VFile* vf) {
807	struct GBCore* gbcore = (struct GBCore*) core;
808	gbcore->logContext = mVideoLogContextCreate(NULL);
809	if (!mVideoLogContextLoad(gbcore->logContext, vf)) {
810		mVideoLogContextDestroy(core, gbcore->logContext);
811		gbcore->logContext = NULL;
812		return false;
813	}
814	mVideoLoggerAttachChannel(gbcore->proxyRenderer.logger, gbcore->logContext, 0);
815	return true;
816}
817
818static bool _returnTrue(struct VFile* vf) {
819	UNUSED(vf);
820	return true;
821}
822
823struct mCore* GBVideoLogPlayerCreate(void) {
824	struct mCore* core = GBCoreCreate();
825	core->init = _GBVLPInit;
826	core->deinit = _GBVLPDeinit;
827	core->reset = _GBVLPReset;
828	core->loadROM = _GBVLPLoadROM;
829	core->isROM = _returnTrue;
830	return core;
831}
832#else
833struct mCore* GBVideoLogPlayerCreate(void) {
834	return false;
835}
836#endif