all repos — mgba @ 3d77a9d922dbdcf6e92b5f63e577f61fc43776aa

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