all repos — mgba @ 217d1b238b5d7c194a20d75cf3e48bc98d1411dd

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/debugger.h>
  12#include <mgba/internal/gb/debugger/symbols.h>
  13#include <mgba/internal/gb/extra/cli.h>
  14#include <mgba/internal/gb/io.h>
  15#include <mgba/internal/gb/gb.h>
  16#include <mgba/internal/gb/mbc.h>
  17#include <mgba/internal/gb/overrides.h>
  18#include <mgba/internal/gb/renderers/software.h>
  19#include <mgba/internal/gb/renderers/proxy.h>
  20#include <mgba/internal/gb/serialize.h>
  21#include <mgba/internal/sm83/sm83.h>
  22#include <mgba/internal/sm83/debugger/debugger.h>
  23#include <mgba-util/crc32.h>
  24#include <mgba-util/memory.h>
  25#include <mgba-util/patch.h>
  26#include <mgba-util/vfs.h>
  27
  28static const struct mCoreChannelInfo _GBVideoLayers[] = {
  29	{ GB_LAYER_BACKGROUND, "bg", "Background", NULL },
  30	{ GB_LAYER_WINDOW, "bgwin", "Window", NULL },
  31	{ GB_LAYER_OBJ, "obj", "Objects", NULL },
  32};
  33
  34static const struct mCoreChannelInfo _GBAudioChannels[] = {
  35	{ 0, "ch1", "Channel 1", "Square/Sweep" },
  36	{ 1, "ch2", "Channel 2", "Square" },
  37	{ 2, "ch3", "Channel 3", "PCM" },
  38	{ 3, "ch4", "Channel 4", "Noise" },
  39};
  40
  41static const struct mCoreMemoryBlock _GBMemoryBlocks[] = {
  42	{ -1, "mem", "All", "All", 0, 0x10000, 0x10000, mCORE_MEMORY_VIRTUAL },
  43	{ GB_REGION_CART_BANK0, "cart0", "ROM Bank", "Game Pak (32kiB)", GB_BASE_CART_BANK0, GB_BASE_CART_BANK0 + GB_SIZE_CART_BANK0 * 2, 0x800000, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED, 511, GB_BASE_CART_BANK0 + GB_SIZE_CART_BANK0 },
  44	{ GB_REGION_VRAM, "vram", "VRAM", "Video RAM (8kiB)", GB_BASE_VRAM, GB_BASE_VRAM + GB_SIZE_VRAM, GB_SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
  45	{ GB_REGION_EXTERNAL_RAM, "sram", "SRAM", "External RAM (8kiB)", GB_BASE_EXTERNAL_RAM, GB_BASE_EXTERNAL_RAM + GB_SIZE_EXTERNAL_RAM, GB_SIZE_EXTERNAL_RAM * 4, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED, 3 },
  46	{ GB_REGION_WORKING_RAM_BANK0, "wram", "WRAM", "Working RAM (8kiB)", GB_BASE_WORKING_RAM_BANK0, GB_BASE_WORKING_RAM_BANK0 + GB_SIZE_WORKING_RAM_BANK0 * 2 , GB_SIZE_WORKING_RAM_BANK0 * 2, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
  47	{ GB_BASE_OAM, "oam", "OAM", "OBJ Attribute Memory", GB_BASE_OAM, GB_BASE_OAM + GB_SIZE_OAM, GB_SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
  48	{ GB_BASE_IO, "io", "MMIO", "Memory-Mapped I/O", GB_BASE_IO, GB_BASE_IO + GB_SIZE_IO, GB_SIZE_IO, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
  49	{ GB_BASE_HRAM, "hram", "HRAM", "High RAM", GB_BASE_HRAM, GB_BASE_HRAM + GB_SIZE_HRAM, GB_SIZE_HRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
  50};
  51
  52static const struct mCoreMemoryBlock _GBCMemoryBlocks[] = {
  53	{ -1, "mem", "All", "All", 0, 0x10000, 0x10000, mCORE_MEMORY_VIRTUAL },
  54	{ GB_REGION_CART_BANK0, "cart0", "ROM Bank", "Game Pak (32kiB)", GB_BASE_CART_BANK0, GB_BASE_CART_BANK0 + GB_SIZE_CART_BANK0 * 2, 0x800000, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED, 511, GB_BASE_CART_BANK0 + GB_SIZE_CART_BANK0 },
  55	{ GB_REGION_VRAM, "vram", "VRAM", "Video RAM (8kiB)", GB_BASE_VRAM, GB_BASE_VRAM + GB_SIZE_VRAM, GB_SIZE_VRAM * 2, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED, 1 },
  56	{ GB_REGION_EXTERNAL_RAM, "sram", "SRAM", "External RAM (8kiB)", GB_BASE_EXTERNAL_RAM, GB_BASE_EXTERNAL_RAM + GB_SIZE_EXTERNAL_RAM, GB_SIZE_EXTERNAL_RAM * 4, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED, 3 },
  57	{ GB_REGION_WORKING_RAM_BANK0, "wram", "WRAM", "Working RAM (8kiB)", GB_BASE_WORKING_RAM_BANK0, GB_BASE_WORKING_RAM_BANK0 + GB_SIZE_WORKING_RAM_BANK0 * 2, GB_SIZE_WORKING_RAM_BANK0 * 8, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED, 7, GB_BASE_WORKING_RAM_BANK0 + GB_SIZE_WORKING_RAM_BANK0 },
  58	{ GB_BASE_OAM, "oam", "OAM", "OBJ Attribute Memory", GB_BASE_OAM, GB_BASE_OAM + GB_SIZE_OAM, GB_SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
  59	{ GB_BASE_IO, "io", "MMIO", "Memory-Mapped I/O", GB_BASE_IO, GB_BASE_IO + GB_SIZE_IO, GB_SIZE_IO, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
  60	{ GB_BASE_HRAM, "hram", "HRAM", "High RAM", GB_BASE_HRAM, GB_BASE_HRAM + GB_SIZE_HRAM, GB_SIZE_HRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
  61};
  62
  63struct mVideoLogContext;
  64struct GBCore {
  65	struct mCore d;
  66	struct GBVideoRenderer dummyRenderer;
  67	struct GBVideoSoftwareRenderer renderer;
  68#ifndef MINIMAL_CORE
  69	struct GBVideoProxyRenderer proxyRenderer;
  70	struct mVideoLogContext* logContext;
  71#endif
  72	struct mCoreCallbacks logCallbacks;
  73	uint8_t keys;
  74	struct mCPUComponent* components[CPU_COMPONENT_MAX];
  75	const struct Configuration* overrides;
  76	struct mDebuggerPlatform* debuggerPlatform;
  77	struct mCheatDevice* cheatDevice;
  78	struct mCoreMemoryBlock memoryBlocks[8];
  79};
  80
  81static bool _GBCoreInit(struct mCore* core) {
  82	struct GBCore* gbcore = (struct GBCore*) core;
  83
  84	struct SM83Core* cpu = anonymousMemoryMap(sizeof(struct SM83Core));
  85	struct GB* gb = anonymousMemoryMap(sizeof(struct GB));
  86	if (!cpu || !gb) {
  87		free(cpu);
  88		free(gb);
  89		return false;
  90	}
  91	core->cpu = cpu;
  92	core->board = gb;
  93	core->timing = &gb->timing;
  94	gbcore->overrides = NULL;
  95	gbcore->debuggerPlatform = NULL;
  96	gbcore->cheatDevice = NULL;
  97#ifndef MINIMAL_CORE
  98	gbcore->logContext = NULL;
  99#endif
 100	memcpy(gbcore->memoryBlocks, _GBMemoryBlocks, sizeof(_GBMemoryBlocks));
 101
 102	GBCreate(gb);
 103	memset(gbcore->components, 0, sizeof(gbcore->components));
 104	SM83SetComponents(cpu, &gb->d, CPU_COMPONENT_MAX, gbcore->components);
 105	SM83Init(cpu);
 106	mRTCGenericSourceInit(&core->rtc, core);
 107	gb->memory.rtc = &core->rtc.d;
 108
 109	GBVideoDummyRendererCreate(&gbcore->dummyRenderer);
 110	GBVideoAssociateRenderer(&gb->video, &gbcore->dummyRenderer);
 111
 112	GBVideoSoftwareRendererCreate(&gbcore->renderer);
 113	gbcore->renderer.outputBuffer = NULL;
 114
 115#ifndef MINIMAL_CORE
 116	gbcore->proxyRenderer.logger = NULL;
 117#endif
 118
 119	gbcore->keys = 0;
 120	gb->keySource = &gbcore->keys;
 121
 122#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
 123	mDirectorySetInit(&core->dirs);
 124#endif
 125	
 126	return true;
 127}
 128
 129static void _GBCoreDeinit(struct mCore* core) {
 130	SM83Deinit(core->cpu);
 131	GBDestroy(core->board);
 132	mappedMemoryFree(core->cpu, sizeof(struct SM83Core));
 133	mappedMemoryFree(core->board, sizeof(struct GB));
 134#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
 135	mDirectorySetDeinit(&core->dirs);
 136#endif
 137#ifdef USE_DEBUGGERS
 138	if (core->symbolTable) {
 139		mDebuggerSymbolTableDestroy(core->symbolTable);
 140	}
 141#endif
 142
 143	struct GBCore* gbcore = (struct GBCore*) core;
 144	free(gbcore->debuggerPlatform);
 145	if (gbcore->cheatDevice) {
 146		mCheatDeviceDestroy(gbcore->cheatDevice);
 147	}
 148	mCoreConfigFreeOpts(&core->opts);
 149	free(core);
 150}
 151
 152static enum mPlatform _GBCorePlatform(const struct mCore* core) {
 153	UNUSED(core);
 154	return mPLATFORM_GB;
 155}
 156
 157static bool _GBCoreSupportsFeature(const struct mCore* core, enum mCoreFeature feature) {
 158	UNUSED(core);
 159	switch (feature) {
 160	default:
 161		return false;
 162	}
 163}
 164
 165static void _GBCoreSetSync(struct mCore* core, struct mCoreSync* sync) {
 166	struct GB* gb = core->board;
 167	gb->sync = sync;
 168}
 169
 170static void _GBCoreLoadConfig(struct mCore* core, const struct mCoreConfig* config) {
 171	UNUSED(config);
 172
 173	struct GB* gb = core->board;
 174	if (core->opts.mute) {
 175		gb->audio.masterVolume = 0;
 176	} else {
 177		gb->audio.masterVolume = core->opts.volume;
 178	}
 179	gb->video.frameskip = core->opts.frameskip;
 180
 181	int color;
 182	if (mCoreConfigGetIntValue(config, "gb.pal[0]", &color)) {
 183		GBVideoSetPalette(&gb->video, 0, color);
 184	}
 185	if (mCoreConfigGetIntValue(config, "gb.pal[1]", &color)) {
 186		GBVideoSetPalette(&gb->video, 1, color);
 187	}
 188	if (mCoreConfigGetIntValue(config, "gb.pal[2]", &color)) {
 189		GBVideoSetPalette(&gb->video, 2, color);
 190	}
 191	if (mCoreConfigGetIntValue(config, "gb.pal[3]", &color)) {
 192		GBVideoSetPalette(&gb->video, 3, color);
 193	}
 194	if (mCoreConfigGetIntValue(config, "gb.pal[4]", &color)) {
 195		GBVideoSetPalette(&gb->video, 4, color);
 196	}
 197	if (mCoreConfigGetIntValue(config, "gb.pal[5]", &color)) {
 198		GBVideoSetPalette(&gb->video, 5, color);
 199	}
 200	if (mCoreConfigGetIntValue(config, "gb.pal[6]", &color)) {
 201		GBVideoSetPalette(&gb->video, 6, color);
 202	}
 203	if (mCoreConfigGetIntValue(config, "gb.pal[7]", &color)) {
 204		GBVideoSetPalette(&gb->video, 7, color);
 205	}
 206	if (mCoreConfigGetIntValue(config, "gb.pal[8]", &color)) {
 207		GBVideoSetPalette(&gb->video, 8, color);
 208	}
 209	if (mCoreConfigGetIntValue(config, "gb.pal[9]", &color)) {
 210		GBVideoSetPalette(&gb->video, 9, color);
 211	}
 212	if (mCoreConfigGetIntValue(config, "gb.pal[10]", &color)) {
 213		GBVideoSetPalette(&gb->video, 10, color);
 214	}
 215	if (mCoreConfigGetIntValue(config, "gb.pal[11]", &color)) {
 216		GBVideoSetPalette(&gb->video, 11, color);
 217	}
 218
 219	mCoreConfigCopyValue(&core->config, config, "gb.bios");
 220	mCoreConfigCopyValue(&core->config, config, "sgb.bios");
 221	mCoreConfigCopyValue(&core->config, config, "gbc.bios");
 222	mCoreConfigCopyValue(&core->config, config, "gb.model");
 223	mCoreConfigCopyValue(&core->config, config, "sgb.model");
 224	mCoreConfigCopyValue(&core->config, config, "cgb.model");
 225	mCoreConfigCopyValue(&core->config, config, "cgb.hybridModel");
 226	mCoreConfigCopyValue(&core->config, config, "cgb.sgbModel");
 227	mCoreConfigCopyValue(&core->config, config, "useCgbColors");
 228	mCoreConfigCopyValue(&core->config, config, "allowOpposingDirections");
 229
 230	int fakeBool = 0;
 231	mCoreConfigGetIntValue(config, "allowOpposingDirections", &fakeBool);
 232	gb->allowOpposingDirections = fakeBool;
 233
 234	if (mCoreConfigGetIntValue(config, "sgb.borders", &fakeBool)) {
 235		gb->video.sgbBorders = fakeBool;
 236		gb->video.renderer->enableSGBBorder(gb->video.renderer, fakeBool);
 237	}
 238
 239#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
 240	struct GBCore* gbcore = (struct GBCore*) core;
 241	gbcore->overrides = mCoreConfigGetOverridesConst(config);
 242#endif
 243}
 244
 245static void _GBCoreReloadConfigOption(struct mCore* core, const char* option, const struct mCoreConfig* config) {
 246	struct GB* gb = core->board;
 247	if (!config) {
 248		config = &core->config;
 249	}
 250
 251	if (!option) {
 252		// Reload options from opts
 253		if (core->opts.mute) {
 254			gb->audio.masterVolume = 0;
 255		} else {
 256			gb->audio.masterVolume = core->opts.volume;
 257		}
 258		gb->video.frameskip = core->opts.frameskip;
 259		return;
 260	}
 261
 262	int fakeBool;
 263	if (strcmp("mute", option) == 0) {
 264		if (mCoreConfigGetIntValue(config, "mute", &fakeBool)) {
 265			core->opts.mute = fakeBool;
 266
 267			if (core->opts.mute) {
 268				gb->audio.masterVolume = 0;
 269			} else {
 270				gb->audio.masterVolume = core->opts.volume;
 271			}
 272		}
 273		return;
 274	}
 275	if (strcmp("volume", option) == 0) {
 276		if (mCoreConfigGetIntValue(config, "volume", &core->opts.volume) && !core->opts.mute) {
 277			gb->audio.masterVolume = core->opts.volume;
 278		}
 279		return;
 280	}
 281	if (strcmp("frameskip", option) == 0) {
 282		if (mCoreConfigGetIntValue(config, "frameskip", &core->opts.frameskip)) {
 283			gb->video.frameskip = core->opts.frameskip;
 284		}
 285		return;
 286	}
 287	if (strcmp("allowOpposingDirections", option) == 0) {
 288		if (config != &core->config) {
 289			mCoreConfigCopyValue(&core->config, config, "allowOpposingDirections");
 290		}
 291		if (mCoreConfigGetIntValue(config, "allowOpposingDirections", &fakeBool)) {
 292			gb->allowOpposingDirections = fakeBool;
 293		}
 294		return;
 295	}
 296	if (strcmp("sgb.borders", option) == 0) {
 297		if (mCoreConfigGetIntValue(config, "sgb.borders", &fakeBool)) {
 298			gb->video.sgbBorders = fakeBool;
 299			gb->video.renderer->enableSGBBorder(gb->video.renderer, fakeBool);
 300		}
 301	}
 302}
 303
 304static void _GBCoreDesiredVideoDimensions(const struct mCore* core, unsigned* width, unsigned* height) {
 305	const struct GB* gb = core->board;
 306	if (gb && (!(gb->model & GB_MODEL_SGB) || !gb->video.sgbBorders)) {
 307		*width = GB_VIDEO_HORIZONTAL_PIXELS;
 308		*height = GB_VIDEO_VERTICAL_PIXELS;
 309	} else {
 310		*width = 256;
 311		*height = 224;
 312	}
 313}
 314
 315static void _GBCoreSetVideoBuffer(struct mCore* core, color_t* buffer, size_t stride) {
 316	struct GBCore* gbcore = (struct GBCore*) core;
 317	gbcore->renderer.outputBuffer = buffer;
 318	gbcore->renderer.outputBufferStride = stride;
 319}
 320
 321static void _GBCoreSetVideoGLTex(struct mCore* core, unsigned texid) {
 322	UNUSED(core);
 323	UNUSED(texid);
 324}
 325
 326static void _GBCoreGetPixels(struct mCore* core, const void** buffer, size_t* stride) {
 327	struct GBCore* gbcore = (struct GBCore*) core;
 328	gbcore->renderer.d.getPixels(&gbcore->renderer.d, stride, buffer);
 329}
 330
 331static void _GBCorePutPixels(struct mCore* core, const void* buffer, size_t stride) {
 332	struct GBCore* gbcore = (struct GBCore*) core;
 333	gbcore->renderer.d.putPixels(&gbcore->renderer.d, stride, buffer);
 334}
 335
 336static struct blip_t* _GBCoreGetAudioChannel(struct mCore* core, int ch) {
 337	struct GB* gb = core->board;
 338	switch (ch) {
 339	case 0:
 340		return gb->audio.left;
 341	case 1:
 342		return gb->audio.right;
 343	default:
 344		return NULL;
 345	}
 346}
 347
 348static void _GBCoreSetAudioBufferSize(struct mCore* core, size_t samples) {
 349	struct GB* gb = core->board;
 350	GBAudioResizeBuffer(&gb->audio, samples);
 351}
 352
 353static size_t _GBCoreGetAudioBufferSize(struct mCore* core) {
 354	struct GB* gb = core->board;
 355	return gb->audio.samples;
 356}
 357
 358static void _GBCoreAddCoreCallbacks(struct mCore* core, struct mCoreCallbacks* coreCallbacks) {
 359	struct GB* gb = core->board;
 360	*mCoreCallbacksListAppend(&gb->coreCallbacks) = *coreCallbacks;
 361}
 362
 363static void _GBCoreClearCoreCallbacks(struct mCore* core) {
 364	struct GB* gb = core->board;
 365	mCoreCallbacksListClear(&gb->coreCallbacks);
 366}
 367
 368static void _GBCoreSetAVStream(struct mCore* core, struct mAVStream* stream) {
 369	struct GB* gb = core->board;
 370	gb->stream = stream;
 371	if (stream && stream->videoDimensionsChanged) {
 372		unsigned width, height;
 373		core->desiredVideoDimensions(core, &width, &height);
 374		stream->videoDimensionsChanged(stream, width, height);
 375	}
 376}
 377
 378static bool _GBCoreLoadROM(struct mCore* core, struct VFile* vf) {
 379	return GBLoadROM(core->board, vf);
 380}
 381
 382static bool _GBCoreLoadBIOS(struct mCore* core, struct VFile* vf, int type) {
 383	UNUSED(type);
 384	GBLoadBIOS(core->board, vf);
 385	return true;
 386}
 387
 388static bool _GBCoreLoadSave(struct mCore* core, struct VFile* vf) {
 389	return GBLoadSave(core->board, vf);
 390}
 391
 392static bool _GBCoreLoadTemporarySave(struct mCore* core, struct VFile* vf) {
 393	struct GB* gb = core->board;
 394	GBSavedataMask(gb, vf, false);
 395	return true; // TODO: Return a real value
 396}
 397
 398static bool _GBCoreLoadPatch(struct mCore* core, struct VFile* vf) {
 399	if (!vf) {
 400		return false;
 401	}
 402	struct Patch patch;
 403	if (!loadPatch(vf, &patch)) {
 404		return false;
 405	}
 406	GBApplyPatch(core->board, &patch);
 407	return true;
 408}
 409
 410static void _GBCoreUnloadROM(struct mCore* core) {
 411	struct GBCore* gbcore = (struct GBCore*) core;
 412	struct SM83Core* cpu = core->cpu;
 413	if (gbcore->cheatDevice) {
 414		SM83HotplugDetach(cpu, CPU_COMPONENT_CHEAT_DEVICE);
 415		cpu->components[CPU_COMPONENT_CHEAT_DEVICE] = NULL;
 416		mCheatDeviceDestroy(gbcore->cheatDevice);
 417		gbcore->cheatDevice = NULL;
 418	}
 419	return GBUnloadROM(core->board);
 420}
 421
 422static void _GBCoreChecksum(const struct mCore* core, void* data, enum mCoreChecksumType type) {
 423	struct GB* gb = (struct GB*) core->board;
 424	switch (type) {
 425	case mCHECKSUM_CRC32:
 426		memcpy(data, &gb->romCrc32, sizeof(gb->romCrc32));
 427		break;
 428	}
 429	return;
 430}
 431
 432static void _GBCoreReset(struct mCore* core) {
 433	struct GBCore* gbcore = (struct GBCore*) core;
 434	struct GB* gb = (struct GB*) core->board;
 435	if (gbcore->renderer.outputBuffer) {
 436		GBVideoAssociateRenderer(&gb->video, &gbcore->renderer.d);
 437	}
 438
 439	if (gb->memory.rom) {
 440		int doColorOverride = 0;
 441		mCoreConfigGetIntValue(&core->config, "useCgbColors", &doColorOverride);
 442
 443		struct GBCartridgeOverride override;
 444		const struct GBCartridge* cart = (const struct GBCartridge*) &gb->memory.rom[0x100];
 445		override.headerCrc32 = doCrc32(cart, sizeof(*cart));
 446		if (GBOverrideFind(gbcore->overrides, &override) || (doColorOverride && GBOverrideColorFind(&override))) {
 447			GBOverrideApply(gb, &override);
 448		}
 449
 450		const char* modelGB = mCoreConfigGetValue(&core->config, "gb.model");
 451		const char* modelSGB = mCoreConfigGetValue(&core->config, "sgb.model");
 452		const char* modelCGB = mCoreConfigGetValue(&core->config, "cgb.model");
 453		const char* modelCGBHybrid = mCoreConfigGetValue(&core->config, "cgb.hybridModel");
 454		const char* modelCGBSGB = mCoreConfigGetValue(&core->config, "cgb.sgbModel");
 455		if (modelGB || modelCGB || modelSGB || modelCGBHybrid || modelCGBSGB) {
 456			int models = GBValidModels(gb->memory.rom);
 457			switch (models) {
 458			case GB_MODEL_SGB | GB_MODEL_MGB:
 459				if (modelSGB) {
 460					gb->model = GBNameToModel(modelSGB);
 461				}
 462				break;
 463			case GB_MODEL_MGB:
 464				if (modelGB) {
 465					gb->model = GBNameToModel(modelGB);
 466				}
 467				break;
 468			case GB_MODEL_MGB | GB_MODEL_CGB:
 469				if (modelCGBHybrid) {
 470					gb->model = GBNameToModel(modelCGBHybrid);
 471				}
 472				break;
 473			case GB_MODEL_SGB | GB_MODEL_CGB: // TODO: Do these even exist?
 474			case GB_MODEL_MGB | GB_MODEL_SGB | GB_MODEL_CGB:
 475				if (modelCGBSGB) {
 476					gb->model = GBNameToModel(modelCGBSGB);
 477				}
 478				break;
 479			case GB_MODEL_CGB:
 480				if (modelCGB) {
 481					gb->model = GBNameToModel(modelCGB);
 482				}
 483				break;
 484			}
 485		}
 486	}
 487
 488#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
 489	if (!gb->biosVf && core->opts.useBios) {
 490		struct VFile* bios = NULL;
 491		bool found = false;
 492		if (core->opts.bios) {
 493			bios = VFileOpen(core->opts.bios, O_RDONLY);
 494			if (bios && GBIsBIOS(bios)) {
 495				found = true;
 496			} else if (bios) {
 497				bios->close(bios);
 498				bios = NULL;
 499			}
 500		}
 501		if (!found) {
 502			GBDetectModel(gb);
 503			const char* configPath = NULL;
 504
 505			switch (gb->model) {
 506			case GB_MODEL_DMG:
 507			case GB_MODEL_MGB: // TODO
 508				configPath = mCoreConfigGetValue(&core->config, "gb.bios");
 509				break;
 510			case GB_MODEL_SGB:
 511			case GB_MODEL_SGB2: // TODO
 512				configPath = mCoreConfigGetValue(&core->config, "sgb.bios");
 513				break;
 514			case GB_MODEL_CGB:
 515			case GB_MODEL_AGB:
 516				configPath = mCoreConfigGetValue(&core->config, "gbc.bios");
 517				break;
 518			default:
 519				break;
 520			};
 521			if (configPath) {
 522				bios = VFileOpen(configPath, O_RDONLY);
 523			}
 524			if (bios && GBIsBIOS(bios)) {
 525				found = true;
 526			} else if (bios) {
 527				bios->close(bios);
 528				bios = NULL;
 529			}
 530		}
 531		if (!found) {
 532			char path[PATH_MAX];
 533			mCoreConfigDirectory(path, PATH_MAX);
 534			switch (gb->model) {
 535			case GB_MODEL_DMG:
 536			case GB_MODEL_MGB: // TODO
 537				strncat(path, PATH_SEP "gb_bios.bin", PATH_MAX - strlen(path));
 538				break;
 539			case GB_MODEL_SGB:
 540			case GB_MODEL_SGB2: // TODO
 541				strncat(path, PATH_SEP "sgb_bios.bin", PATH_MAX - strlen(path));
 542				break;
 543			case GB_MODEL_CGB:
 544			case GB_MODEL_AGB:
 545				strncat(path, PATH_SEP "gbc_bios.bin", PATH_MAX - strlen(path));
 546				break;
 547			default:
 548				break;
 549			};
 550			bios = VFileOpen(path, O_RDONLY);
 551			if (bios && GBIsBIOS(bios)) {
 552				found = true;
 553			} else if (bios) {
 554				bios->close(bios);
 555				bios = NULL;
 556			}
 557		}
 558		if (found && bios) {
 559			GBLoadBIOS(gb, bios);
 560		}
 561	}
 562#endif
 563
 564	if (gb->model < GB_MODEL_CGB) {
 565		memcpy(gbcore->memoryBlocks, _GBMemoryBlocks, sizeof(_GBMemoryBlocks));
 566	} else {
 567		memcpy(gbcore->memoryBlocks, _GBCMemoryBlocks, sizeof(_GBCMemoryBlocks));
 568	}
 569
 570	size_t i;
 571	for (i = 0; i < sizeof(gbcore->memoryBlocks) / sizeof(*gbcore->memoryBlocks); ++i) {
 572		if (gbcore->memoryBlocks[i].id == GB_REGION_CART_BANK0) {
 573			gbcore->memoryBlocks[i].maxSegment = gb->memory.romSize / GB_SIZE_CART_BANK0;
 574		} else if (gbcore->memoryBlocks[i].id == GB_REGION_EXTERNAL_RAM) {
 575			gbcore->memoryBlocks[i].maxSegment = gb->sramSize / GB_SIZE_EXTERNAL_RAM;
 576		} else {
 577			continue;
 578		}
 579		if (gbcore->memoryBlocks[i].maxSegment) {
 580			--gbcore->memoryBlocks[i].maxSegment;
 581		}
 582	}
 583
 584	SM83Reset(core->cpu);
 585
 586	if (core->opts.skipBios) {
 587		GBSkipBIOS(core->board);
 588	}
 589}
 590
 591static void _GBCoreRunFrame(struct mCore* core) {
 592	struct GB* gb = core->board;
 593	int32_t frameCounter = gb->video.frameCounter;
 594	while (gb->video.frameCounter == frameCounter) {
 595		SM83Run(core->cpu);
 596	}
 597}
 598
 599static void _GBCoreRunLoop(struct mCore* core) {
 600	SM83Run(core->cpu);
 601}
 602
 603static void _GBCoreStep(struct mCore* core) {
 604	struct SM83Core* cpu = core->cpu;
 605	do {
 606		SM83Tick(cpu);
 607	} while (cpu->executionState != SM83_CORE_FETCH);
 608}
 609
 610static size_t _GBCoreStateSize(struct mCore* core) {
 611	UNUSED(core);
 612	return sizeof(struct GBSerializedState);
 613}
 614
 615static bool _GBCoreLoadState(struct mCore* core, const void* state) {
 616	return GBDeserialize(core->board, state);
 617}
 618
 619static bool _GBCoreSaveState(struct mCore* core, void* state) {
 620	struct SM83Core* cpu = core->cpu;
 621	while (cpu->executionState != SM83_CORE_FETCH) {
 622		SM83Tick(cpu);
 623	}
 624	GBSerialize(core->board, state);
 625	return true;
 626}
 627
 628static void _GBCoreSetKeys(struct mCore* core, uint32_t keys) {
 629	struct GBCore* gbcore = (struct GBCore*) core;
 630	gbcore->keys = keys;
 631	GBTestKeypadIRQ(core->board);
 632}
 633
 634static void _GBCoreAddKeys(struct mCore* core, uint32_t keys) {
 635	struct GBCore* gbcore = (struct GBCore*) core;
 636	gbcore->keys |= keys;
 637	GBTestKeypadIRQ(core->board);
 638}
 639
 640static void _GBCoreClearKeys(struct mCore* core, uint32_t keys) {
 641	struct GBCore* gbcore = (struct GBCore*) core;
 642	gbcore->keys &= ~keys;
 643}
 644
 645static int32_t _GBCoreFrameCounter(const struct mCore* core) {
 646	const struct GB* gb = core->board;
 647	return gb->video.frameCounter;
 648}
 649
 650static int32_t _GBCoreFrameCycles(const  struct mCore* core) {
 651	UNUSED(core);
 652	return GB_VIDEO_TOTAL_LENGTH;
 653}
 654
 655static int32_t _GBCoreFrequency(const struct mCore* core) {
 656	UNUSED(core);
 657	// TODO: GB differences
 658	return DMG_SM83_FREQUENCY;
 659}
 660
 661static void _GBCoreGetGameTitle(const struct mCore* core, char* title) {
 662	GBGetGameTitle(core->board, title);
 663}
 664
 665static void _GBCoreGetGameCode(const struct mCore* core, char* title) {
 666	GBGetGameCode(core->board, title);
 667}
 668
 669static void _GBCoreSetPeripheral(struct mCore* core, int type, void* periph) {
 670	struct GB* gb = core->board;
 671	switch (type) {
 672	case mPERIPH_ROTATION:
 673		gb->memory.rotation = periph;
 674		break;
 675	case mPERIPH_RUMBLE:
 676		gb->memory.rumble = periph;
 677		break;
 678	case mPERIPH_IMAGE_SOURCE:
 679		gb->memory.cam = periph;
 680		break;
 681	default:
 682		return;
 683	}
 684}
 685
 686static uint32_t _GBCoreBusRead8(struct mCore* core, uint32_t address) {
 687	struct SM83Core* cpu = core->cpu;
 688	return cpu->memory.load8(cpu, address);
 689}
 690
 691static uint32_t _GBCoreBusRead16(struct mCore* core, uint32_t address) {
 692	struct SM83Core* cpu = core->cpu;
 693	return cpu->memory.load8(cpu, address) | (cpu->memory.load8(cpu, address + 1) << 8);
 694}
 695
 696static uint32_t _GBCoreBusRead32(struct mCore* core, uint32_t address) {
 697	struct SM83Core* cpu = core->cpu;
 698	return cpu->memory.load8(cpu, address) | (cpu->memory.load8(cpu, address + 1) << 8) |
 699	       (cpu->memory.load8(cpu, address + 2) << 16) | (cpu->memory.load8(cpu, address + 3) << 24);
 700}
 701
 702static void _GBCoreBusWrite8(struct mCore* core, uint32_t address, uint8_t value) {
 703	struct SM83Core* cpu = core->cpu;
 704	cpu->memory.store8(cpu, address, value);
 705}
 706
 707static void _GBCoreBusWrite16(struct mCore* core, uint32_t address, uint16_t value) {
 708	struct SM83Core* cpu = core->cpu;
 709	cpu->memory.store8(cpu, address, value);
 710	cpu->memory.store8(cpu, address + 1, value >> 8);
 711}
 712
 713static void _GBCoreBusWrite32(struct mCore* core, uint32_t address, uint32_t value) {
 714	struct SM83Core* cpu = core->cpu;
 715	cpu->memory.store8(cpu, address, value);
 716	cpu->memory.store8(cpu, address + 1, value >> 8);
 717	cpu->memory.store8(cpu, address + 2, value >> 16);
 718	cpu->memory.store8(cpu, address + 3, value >> 24);
 719}
 720
 721static uint32_t _GBCoreRawRead8(struct mCore* core, uint32_t address, int segment) {
 722	struct SM83Core* cpu = core->cpu;
 723	return GBView8(cpu, address, segment);
 724}
 725
 726static uint32_t _GBCoreRawRead16(struct mCore* core, uint32_t address, int segment) {
 727	struct SM83Core* cpu = core->cpu;
 728	return GBView8(cpu, address, segment) | (GBView8(cpu, address + 1, segment) << 8);
 729}
 730
 731static uint32_t _GBCoreRawRead32(struct mCore* core, uint32_t address, int segment) {
 732	struct SM83Core* cpu = core->cpu;
 733	return GBView8(cpu, address, segment) | (GBView8(cpu, address + 1, segment) << 8) |
 734	       (GBView8(cpu, address + 2, segment) << 16) | (GBView8(cpu, address + 3, segment) << 24);
 735}
 736
 737static void _GBCoreRawWrite8(struct mCore* core, uint32_t address, int segment, uint8_t value) {
 738	struct SM83Core* cpu = core->cpu;
 739	GBPatch8(cpu, address, value, NULL, segment);
 740}
 741
 742static void _GBCoreRawWrite16(struct mCore* core, uint32_t address, int segment, uint16_t value) {
 743	struct SM83Core* cpu = core->cpu;
 744	GBPatch8(cpu, address, value, NULL, segment);
 745	GBPatch8(cpu, address + 1, value >> 8, NULL, segment);
 746}
 747
 748static void _GBCoreRawWrite32(struct mCore* core, uint32_t address, int segment, uint32_t value) {
 749	struct SM83Core* cpu = core->cpu;
 750	GBPatch8(cpu, address, value, NULL, segment);
 751	GBPatch8(cpu, address + 1, value >> 8, NULL, segment);
 752	GBPatch8(cpu, address + 2, value >> 16, NULL, segment);
 753	GBPatch8(cpu, address + 3, value >> 24, NULL, segment);
 754}
 755
 756size_t _GBListMemoryBlocks(const struct mCore* core, const struct mCoreMemoryBlock** blocks) {
 757	struct GBCore* gbcore = (struct GBCore*) core;
 758	*blocks = gbcore->memoryBlocks;
 759	return sizeof(gbcore->memoryBlocks) / sizeof(*gbcore->memoryBlocks);
 760}
 761
 762void* _GBGetMemoryBlock(struct mCore* core, size_t id, size_t* sizeOut) {
 763	struct GB* gb = core->board;
 764	bool isCgb = gb->model >= GB_MODEL_CGB;
 765	switch (id) {
 766	default:
 767		return NULL;
 768	case GB_REGION_CART_BANK0:
 769		*sizeOut = gb->memory.romSize;
 770		return gb->memory.rom;
 771	case GB_REGION_VRAM:
 772		*sizeOut = GB_SIZE_WORKING_RAM_BANK0 * (isCgb ? 1 : 2);
 773		return gb->video.vram;
 774	case GB_REGION_EXTERNAL_RAM:
 775		*sizeOut = gb->sramSize;
 776		return gb->memory.sram;
 777	case GB_REGION_WORKING_RAM_BANK0:
 778		*sizeOut = GB_SIZE_VRAM * (isCgb ? 8 : 2);
 779		return gb->memory.wram;
 780	case GB_BASE_OAM:
 781		*sizeOut = GB_SIZE_OAM;
 782		return gb->video.oam.raw;
 783	case GB_BASE_HRAM:
 784		*sizeOut = GB_SIZE_HRAM;
 785		return gb->memory.hram;
 786	}
 787}
 788
 789#ifdef USE_DEBUGGERS
 790static bool _GBCoreSupportsDebuggerType(struct mCore* core, enum mDebuggerType type) {
 791	UNUSED(core);
 792	switch (type) {
 793	case DEBUGGER_CUSTOM:
 794	case DEBUGGER_CLI:
 795		return true;
 796	default:
 797		return false;
 798	}
 799}
 800
 801static struct mDebuggerPlatform* _GBCoreDebuggerPlatform(struct mCore* core) {
 802	struct GBCore* gbcore = (struct GBCore*) core;
 803	struct GB* gb = core->board;
 804	if (!gbcore->debuggerPlatform) {
 805		struct SM83Debugger* platform = (struct SM83Debugger*) GBDebuggerCreate(gb);
 806		gbcore->debuggerPlatform = &platform->d;
 807	}
 808	return gbcore->debuggerPlatform;
 809}
 810
 811static struct CLIDebuggerSystem* _GBCoreCliDebuggerSystem(struct mCore* core) {
 812	return GBCLIDebuggerCreate(core);
 813}
 814
 815static void _GBCoreAttachDebugger(struct mCore* core, struct mDebugger* debugger) {
 816	struct SM83Core* cpu = core->cpu;
 817	if (core->debugger) {
 818		SM83HotplugDetach(cpu, CPU_COMPONENT_DEBUGGER);
 819	}
 820	cpu->components[CPU_COMPONENT_DEBUGGER] = &debugger->d;
 821	SM83HotplugAttach(cpu, CPU_COMPONENT_DEBUGGER);
 822	core->debugger = debugger;
 823}
 824
 825static void _GBCoreDetachDebugger(struct mCore* core) {
 826	struct SM83Core* cpu = core->cpu;
 827	if (core->debugger) {
 828		SM83HotplugDetach(cpu, CPU_COMPONENT_DEBUGGER);
 829	}
 830	cpu->components[CPU_COMPONENT_DEBUGGER] = NULL;
 831	core->debugger = NULL;
 832}
 833
 834static void _GBCoreLoadSymbols(struct mCore* core, struct VFile* vf) {
 835	core->symbolTable = mDebuggerSymbolTableCreate();
 836#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
 837	if (!vf) {
 838		vf = mDirectorySetOpenSuffix(&core->dirs, core->dirs.base, ".sym", O_RDONLY);
 839	}
 840#endif
 841	if (!vf) {
 842		return;
 843	}
 844	GBLoadSymbols(core->symbolTable, vf);
 845}
 846
 847static bool _GBCoreLookupIdentifier(struct mCore* core, const char* name, int32_t* value, int* segment) {
 848	UNUSED(core);
 849	*segment = -1;
 850	int i;
 851	for (i = 0; i < GB_REG_MAX; ++i) {
 852		const char* reg = GBIORegisterNames[i];
 853		if (reg && strcasecmp(reg, name) == 0) {
 854			*value = GB_BASE_IO | i;
 855			return true;
 856		}
 857	}
 858	return false;
 859}
 860#endif
 861
 862static struct mCheatDevice* _GBCoreCheatDevice(struct mCore* core) {
 863	struct GBCore* gbcore = (struct GBCore*) core;
 864	if (!gbcore->cheatDevice) {
 865		gbcore->cheatDevice = GBCheatDeviceCreate();
 866		((struct SM83Core*) core->cpu)->components[CPU_COMPONENT_CHEAT_DEVICE] = &gbcore->cheatDevice->d;
 867		SM83HotplugAttach(core->cpu, CPU_COMPONENT_CHEAT_DEVICE);
 868		gbcore->cheatDevice->p = core;
 869	}
 870	return gbcore->cheatDevice;
 871}
 872
 873static size_t _GBCoreSavedataClone(struct mCore* core, void** sram) {
 874	struct GB* gb = core->board;
 875	struct VFile* vf = gb->sramVf;
 876	if (vf) {
 877		*sram = malloc(vf->size(vf));
 878		vf->seek(vf, 0, SEEK_SET);
 879		return vf->read(vf, *sram, vf->size(vf));
 880	}
 881	if (gb->sramSize) {
 882		*sram = malloc(gb->sramSize);
 883		memcpy(*sram, gb->memory.sram, gb->sramSize);
 884		return gb->sramSize;
 885	}
 886	*sram = NULL;
 887	return 0;
 888}
 889
 890static bool _GBCoreSavedataRestore(struct mCore* core, const void* sram, size_t size, bool writeback) {
 891	struct GB* gb = core->board;
 892	if (!writeback) {
 893		struct VFile* vf = VFileMemChunk(sram, size);
 894		GBSavedataMask(gb, vf, true);
 895		return true;
 896	}
 897	struct VFile* vf = gb->sramVf;
 898	if (vf) {
 899		vf->seek(vf, 0, SEEK_SET);
 900		return vf->write(vf, sram, size) > 0;
 901	}
 902	if (size > 0x20000) {
 903		size = 0x20000;
 904	}
 905	GBResizeSram(gb, size);
 906	memcpy(gb->memory.sram, sram, size);
 907	return true;
 908}
 909
 910static size_t _GBCoreListVideoLayers(const struct mCore* core, const struct mCoreChannelInfo** info) {
 911	UNUSED(core);
 912	if (info) {
 913		*info = _GBVideoLayers;
 914	}
 915	return sizeof(_GBVideoLayers) / sizeof(*_GBVideoLayers);
 916}
 917
 918static size_t _GBCoreListAudioChannels(const struct mCore* core, const struct mCoreChannelInfo** info) {
 919	UNUSED(core);
 920	if (info) {
 921		*info = _GBAudioChannels;
 922	}
 923	return sizeof(_GBAudioChannels) / sizeof(*_GBAudioChannels);
 924}
 925
 926static void _GBCoreEnableVideoLayer(struct mCore* core, size_t id, bool enable) {
 927	struct GB* gb = core->board;
 928	switch (id) {
 929	case GB_LAYER_BACKGROUND:
 930		gb->video.renderer->disableBG = !enable;
 931		break;
 932	case GB_LAYER_WINDOW:
 933		gb->video.renderer->disableWIN = !enable;
 934		break;
 935	case GB_LAYER_OBJ:
 936		gb->video.renderer->disableOBJ = !enable;
 937		break;
 938	default:
 939		break;
 940	}
 941}
 942
 943static void _GBCoreEnableAudioChannel(struct mCore* core, size_t id, bool enable) {
 944	struct GB* gb = core->board;
 945	switch (id) {
 946	case 0:
 947	case 1:
 948	case 2:
 949	case 3:
 950		gb->audio.forceDisableCh[id] = !enable;
 951		break;
 952	default:
 953		break;
 954	}
 955}
 956
 957static void _GBCoreAdjustVideoLayer(struct mCore* core, size_t id, int32_t x, int32_t y) {
 958	struct GBCore* gbcore = (struct GBCore*) core;
 959	switch (id) {
 960	case GB_LAYER_BACKGROUND:
 961		gbcore->renderer.offsetScx = x;
 962		gbcore->renderer.offsetScy = y;
 963		break;
 964	case GB_LAYER_WINDOW:
 965		gbcore->renderer.offsetWx = x;
 966		gbcore->renderer.offsetWy = y;
 967		break;
 968	case GB_LAYER_OBJ:
 969		gbcore->renderer.objOffsetX = x;
 970		gbcore->renderer.objOffsetY = y;
 971		break;
 972	default:
 973		return;
 974	}
 975}
 976
 977#ifndef MINIMAL_CORE
 978static void _GBCoreStartVideoLog(struct mCore* core, struct mVideoLogContext* context) {
 979	struct GBCore* gbcore = (struct GBCore*) core;
 980	struct GB* gb = core->board;
 981	gbcore->logContext = context;
 982
 983	int channelId = mVideoLoggerAddChannel(context);
 984	gbcore->proxyRenderer.logger = malloc(sizeof(struct mVideoLogger));
 985	mVideoLoggerRendererCreate(gbcore->proxyRenderer.logger, false);
 986	mVideoLoggerAttachChannel(gbcore->proxyRenderer.logger, context, channelId);
 987	gbcore->proxyRenderer.logger->block = false;
 988
 989	GBVideoProxyRendererCreate(&gbcore->proxyRenderer, &gbcore->renderer.d);
 990	GBVideoProxyRendererShim(&gb->video, &gbcore->proxyRenderer);
 991}
 992
 993static void _GBCoreEndVideoLog(struct mCore* core) {
 994	struct GBCore* gbcore = (struct GBCore*) core;
 995	struct GB* gb = core->board;
 996	if (gbcore->proxyRenderer.logger) {
 997		GBVideoProxyRendererUnshim(&gb->video, &gbcore->proxyRenderer);
 998		free(gbcore->proxyRenderer.logger);
 999		gbcore->proxyRenderer.logger = NULL;
1000	}
1001}
1002#endif
1003
1004struct mCore* GBCoreCreate(void) {
1005	struct GBCore* gbcore = malloc(sizeof(*gbcore));
1006	struct mCore* core = &gbcore->d;
1007	memset(&core->opts, 0, sizeof(core->opts));
1008	core->cpu = NULL;
1009	core->board = NULL;
1010	core->debugger = NULL;
1011	core->symbolTable = NULL;
1012	core->init = _GBCoreInit;
1013	core->deinit = _GBCoreDeinit;
1014	core->platform = _GBCorePlatform;
1015	core->supportsFeature = _GBCoreSupportsFeature;
1016	core->setSync = _GBCoreSetSync;
1017	core->loadConfig = _GBCoreLoadConfig;
1018	core->reloadConfigOption = _GBCoreReloadConfigOption;
1019	core->desiredVideoDimensions = _GBCoreDesiredVideoDimensions;
1020	core->setVideoBuffer = _GBCoreSetVideoBuffer;
1021	core->setVideoGLTex = _GBCoreSetVideoGLTex;
1022	core->getPixels = _GBCoreGetPixels;
1023	core->putPixels = _GBCorePutPixels;
1024	core->getAudioChannel = _GBCoreGetAudioChannel;
1025	core->setAudioBufferSize = _GBCoreSetAudioBufferSize;
1026	core->getAudioBufferSize = _GBCoreGetAudioBufferSize;
1027	core->setAVStream = _GBCoreSetAVStream;
1028	core->addCoreCallbacks = _GBCoreAddCoreCallbacks;
1029	core->clearCoreCallbacks = _GBCoreClearCoreCallbacks;
1030	core->isROM = GBIsROM;
1031	core->loadROM = _GBCoreLoadROM;
1032	core->loadBIOS = _GBCoreLoadBIOS;
1033	core->loadSave = _GBCoreLoadSave;
1034	core->loadTemporarySave = _GBCoreLoadTemporarySave;
1035	core->loadPatch = _GBCoreLoadPatch;
1036	core->unloadROM = _GBCoreUnloadROM;
1037	core->checksum = _GBCoreChecksum;
1038	core->reset = _GBCoreReset;
1039	core->runFrame = _GBCoreRunFrame;
1040	core->runLoop = _GBCoreRunLoop;
1041	core->step = _GBCoreStep;
1042	core->stateSize = _GBCoreStateSize;
1043	core->loadState = _GBCoreLoadState;
1044	core->saveState = _GBCoreSaveState;
1045	core->setKeys = _GBCoreSetKeys;
1046	core->addKeys = _GBCoreAddKeys;
1047	core->clearKeys = _GBCoreClearKeys;
1048	core->frameCounter = _GBCoreFrameCounter;
1049	core->frameCycles = _GBCoreFrameCycles;
1050	core->frequency = _GBCoreFrequency;
1051	core->getGameTitle = _GBCoreGetGameTitle;
1052	core->getGameCode = _GBCoreGetGameCode;
1053	core->setPeripheral = _GBCoreSetPeripheral;
1054	core->busRead8 = _GBCoreBusRead8;
1055	core->busRead16 = _GBCoreBusRead16;
1056	core->busRead32 = _GBCoreBusRead32;
1057	core->busWrite8 = _GBCoreBusWrite8;
1058	core->busWrite16 = _GBCoreBusWrite16;
1059	core->busWrite32 = _GBCoreBusWrite32;
1060	core->rawRead8 = _GBCoreRawRead8;
1061	core->rawRead16 = _GBCoreRawRead16;
1062	core->rawRead32 = _GBCoreRawRead32;
1063	core->rawWrite8 = _GBCoreRawWrite8;
1064	core->rawWrite16 = _GBCoreRawWrite16;
1065	core->rawWrite32 = _GBCoreRawWrite32;
1066	core->listMemoryBlocks = _GBListMemoryBlocks;
1067	core->getMemoryBlock = _GBGetMemoryBlock;
1068#ifdef USE_DEBUGGERS
1069	core->supportsDebuggerType = _GBCoreSupportsDebuggerType;
1070	core->debuggerPlatform = _GBCoreDebuggerPlatform;
1071	core->cliDebuggerSystem = _GBCoreCliDebuggerSystem;
1072	core->attachDebugger = _GBCoreAttachDebugger;
1073	core->detachDebugger = _GBCoreDetachDebugger;
1074	core->loadSymbols = _GBCoreLoadSymbols;
1075	core->lookupIdentifier = _GBCoreLookupIdentifier;
1076#endif
1077	core->cheatDevice = _GBCoreCheatDevice;
1078	core->savedataClone = _GBCoreSavedataClone;
1079	core->savedataRestore = _GBCoreSavedataRestore;
1080	core->listVideoLayers = _GBCoreListVideoLayers;
1081	core->listAudioChannels = _GBCoreListAudioChannels;
1082	core->enableVideoLayer = _GBCoreEnableVideoLayer;
1083	core->enableAudioChannel = _GBCoreEnableAudioChannel;
1084	core->adjustVideoLayer = _GBCoreAdjustVideoLayer;
1085#ifndef MINIMAL_CORE
1086	core->startVideoLog = _GBCoreStartVideoLog;
1087	core->endVideoLog = _GBCoreEndVideoLog;
1088#endif
1089	return core;
1090}
1091
1092#ifndef MINIMAL_CORE
1093static void _GBVLPStartFrameCallback(void *context) {
1094	struct mCore* core = context;
1095	struct GBCore* gbcore = (struct GBCore*) core;
1096	struct GB* gb = core->board;
1097
1098	if (!mVideoLoggerRendererRun(gbcore->proxyRenderer.logger, true)) {
1099		GBVideoProxyRendererUnshim(&gb->video, &gbcore->proxyRenderer);
1100		mVideoLogContextRewind(gbcore->logContext, core);
1101		GBVideoProxyRendererShim(&gb->video, &gbcore->proxyRenderer);
1102		gb->earlyExit = true;
1103	}
1104}
1105
1106static bool _GBVLPInit(struct mCore* core) {
1107	struct GBCore* gbcore = (struct GBCore*) core;
1108	if (!_GBCoreInit(core)) {
1109		return false;
1110	}
1111	gbcore->proxyRenderer.logger = malloc(sizeof(struct mVideoLogger));
1112	mVideoLoggerRendererCreate(gbcore->proxyRenderer.logger, true);
1113	GBVideoProxyRendererCreate(&gbcore->proxyRenderer, NULL);
1114	memset(&gbcore->logCallbacks, 0, sizeof(gbcore->logCallbacks));
1115	gbcore->logCallbacks.videoFrameStarted = _GBVLPStartFrameCallback;
1116	gbcore->logCallbacks.context = core;
1117	core->addCoreCallbacks(core, &gbcore->logCallbacks);
1118	core->videoLogger = gbcore->proxyRenderer.logger;
1119	return true;
1120}
1121
1122static void _GBVLPDeinit(struct mCore* core) {
1123	struct GBCore* gbcore = (struct GBCore*) core;
1124	if (gbcore->logContext) {
1125		mVideoLogContextDestroy(core, gbcore->logContext, true);
1126	}
1127	_GBCoreDeinit(core);
1128}
1129
1130static void _GBVLPReset(struct mCore* core) {
1131	struct GBCore* gbcore = (struct GBCore*) core;
1132	struct GB* gb = (struct GB*) core->board;
1133	if (gb->video.renderer == &gbcore->proxyRenderer.d) {
1134		GBVideoProxyRendererUnshim(&gb->video, &gbcore->proxyRenderer);
1135	} else if (gbcore->renderer.outputBuffer) {
1136		struct GBVideoRenderer* renderer = &gbcore->renderer.d;
1137		GBVideoAssociateRenderer(&gb->video, renderer);
1138	}
1139
1140	SM83Reset(core->cpu);
1141	mVideoLogContextRewind(gbcore->logContext, core);
1142	GBVideoProxyRendererShim(&gb->video, &gbcore->proxyRenderer);
1143
1144	// Make sure CPU loop never spins
1145	gb->memory.ie = 0;
1146	gb->memory.ime = false;
1147	GBHalt(gb->cpu);
1148}
1149
1150static bool _GBVLPLoadROM(struct mCore* core, struct VFile* vf) {
1151	struct GBCore* gbcore = (struct GBCore*) core;
1152	gbcore->logContext = mVideoLogContextCreate(NULL);
1153	if (!mVideoLogContextLoad(gbcore->logContext, vf)) {
1154		mVideoLogContextDestroy(core, gbcore->logContext, false);
1155		gbcore->logContext = NULL;
1156		return false;
1157	}
1158	mVideoLoggerAttachChannel(gbcore->proxyRenderer.logger, gbcore->logContext, 0);
1159	return true;
1160}
1161
1162static bool _GBVLPLoadState(struct mCore* core, const void* buffer) {
1163	struct GB* gb = (struct GB*) core->board;
1164	const struct GBSerializedState* state = buffer;
1165
1166	gb->timing.root = NULL;
1167	gb->model = state->model;
1168
1169	gb->cpu->pc = GB_BASE_HRAM;
1170	gb->cpu->memory.setActiveRegion(gb->cpu, gb->cpu->pc);
1171
1172	GBVideoReset(&gb->video);
1173	GBVideoDeserialize(&gb->video, state);
1174	GBIODeserialize(gb, state);
1175	GBAudioReset(&gb->audio);
1176	if (gb->model & GB_MODEL_SGB) {
1177		GBSGBDeserialize(gb, state);
1178	}
1179
1180	// Make sure CPU loop never spins
1181	gb->memory.ie = 0;
1182	gb->memory.ime = false;
1183	GBHalt(gb->cpu);
1184
1185	return true;
1186}
1187
1188static bool _returnTrue(struct VFile* vf) {
1189	UNUSED(vf);
1190	return true;
1191}
1192
1193struct mCore* GBVideoLogPlayerCreate(void) {
1194	struct mCore* core = GBCoreCreate();
1195	core->init = _GBVLPInit;
1196	core->deinit = _GBVLPDeinit;
1197	core->reset = _GBVLPReset;
1198	core->loadROM = _GBVLPLoadROM;
1199	core->loadState = _GBVLPLoadState;
1200	core->isROM = _returnTrue;
1201	return core;
1202}
1203#else
1204struct mCore* GBVideoLogPlayerCreate(void) {
1205	return false;
1206}
1207#endif