all repos — mgba @ 9dfcef3f45efe580e252ec8bc3852257e9de9fd6

mGBA Game Boy Advance Emulator

src/gba/core.c (view raw)

   1/* Copyright (c) 2013-2016 Jeffrey Pfau
   2 *
   3 * This Source Code Form is subject to the terms of the Mozilla Public
   4 * License, v. 2.0. If a copy of the MPL was not distributed with this
   5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
   6#include <mgba/gba/core.h>
   7
   8#include <mgba/core/core.h>
   9#include <mgba/core/log.h>
  10#include <mgba/internal/arm/debugger/debugger.h>
  11#include <mgba/internal/debugger/symbols.h>
  12#include <mgba/internal/gba/cheats.h>
  13#include <mgba/internal/gba/gba.h>
  14#include <mgba/internal/gba/io.h>
  15#include <mgba/internal/gba/extra/cli.h>
  16#include <mgba/internal/gba/overrides.h>
  17#ifndef DISABLE_THREADING
  18#include <mgba/feature/thread-proxy.h>
  19#endif
  20#include <mgba/internal/gba/renderers/proxy.h>
  21#include <mgba/internal/gba/renderers/video-software.h>
  22#include <mgba/internal/gba/savedata.h>
  23#include <mgba/internal/gba/serialize.h>
  24#ifdef USE_ELF
  25#include <mgba-util/elf-read.h>
  26#endif
  27#include <mgba-util/memory.h>
  28#include <mgba-util/patch.h>
  29#include <mgba-util/vfs.h>
  30
  31#ifndef MINIMAL_CORE
  32#include <mgba/internal/gba/input.h>
  33#endif
  34
  35static const struct mCoreChannelInfo _GBAVideoLayers[] = {
  36	{ 0, "bg0", "Background 0", NULL },
  37	{ 1, "bg1", "Background 1", NULL },
  38	{ 2, "bg2", "Background 2", NULL },
  39	{ 3, "bg3", "Background 3", NULL },
  40	{ 4, "obj", "Objects", NULL },
  41};
  42
  43static const struct mCoreChannelInfo _GBAAudioChannels[] = {
  44	{ 0, "ch1", "PSG Channel 1", "Square/Sweep" },
  45	{ 1, "ch2", "PSG Channel 2", "Square" },
  46	{ 2, "ch3", "PSG Channel 3", "PCM" },
  47	{ 3, "ch4", "PSG Channel 4", "Noise" },
  48	{ 4, "chA", "FIFO Channel A", NULL },
  49	{ 5, "chB", "FIFO Channel B", NULL },
  50};
  51
  52static const struct mCoreMemoryBlock _GBAMemoryBlocks[] = {
  53	{ -1, "mem", "All", "All", 0, 0x10000000, 0x10000000, mCORE_MEMORY_VIRTUAL },
  54	{ REGION_BIOS, "bios", "BIOS", "BIOS (16kiB)", BASE_BIOS, SIZE_BIOS, SIZE_BIOS, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
  55	{ REGION_WORKING_RAM, "wram", "EWRAM", "Working RAM (256kiB)", BASE_WORKING_RAM, BASE_WORKING_RAM + SIZE_WORKING_RAM, SIZE_WORKING_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
  56	{ REGION_WORKING_IRAM, "iwram", "IWRAM", "Internal Working RAM (32kiB)", BASE_WORKING_IRAM, BASE_WORKING_IRAM + SIZE_WORKING_IRAM, SIZE_WORKING_IRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
  57	{ REGION_IO, "io", "MMIO", "Memory-Mapped I/O", BASE_IO, BASE_IO + SIZE_IO, SIZE_IO, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
  58	{ REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", BASE_PALETTE_RAM, BASE_PALETTE_RAM + SIZE_PALETTE_RAM, SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
  59	{ REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", BASE_VRAM, BASE_VRAM + SIZE_VRAM, SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
  60	{ REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", BASE_OAM, BASE_OAM + SIZE_OAM, SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
  61	{ REGION_CART0, "cart0", "ROM", "Game Pak (32MiB)", BASE_CART0, BASE_CART0 + SIZE_CART0, SIZE_CART0, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
  62	{ REGION_CART1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", BASE_CART1, BASE_CART1 + SIZE_CART1, SIZE_CART1, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
  63	{ REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
  64};
  65
  66static const struct mCoreMemoryBlock _GBAMemoryBlocksSRAM[] = {
  67	{ -1, "mem", "All", "All", 0, 0x10000000, 0x10000000, mCORE_MEMORY_VIRTUAL },
  68	{ REGION_BIOS, "bios", "BIOS", "BIOS (16kiB)", BASE_BIOS, SIZE_BIOS, SIZE_BIOS, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
  69	{ REGION_WORKING_RAM, "wram", "EWRAM", "Working RAM (256kiB)", BASE_WORKING_RAM, BASE_WORKING_RAM + SIZE_WORKING_RAM, SIZE_WORKING_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
  70	{ REGION_WORKING_IRAM, "iwram", "IWRAM", "Internal Working RAM (32kiB)", BASE_WORKING_IRAM, BASE_WORKING_IRAM + SIZE_WORKING_IRAM, SIZE_WORKING_IRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
  71	{ REGION_IO, "io", "MMIO", "Memory-Mapped I/O", BASE_IO, BASE_IO + SIZE_IO, SIZE_IO, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
  72	{ REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", BASE_PALETTE_RAM, BASE_PALETTE_RAM + SIZE_PALETTE_RAM, SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
  73	{ REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", BASE_VRAM, BASE_VRAM + SIZE_VRAM, SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
  74	{ REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", BASE_OAM, BASE_OAM + SIZE_OAM, SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
  75	{ REGION_CART0, "cart0", "ROM", "Game Pak (32MiB)", BASE_CART0, BASE_CART0 + SIZE_CART0, SIZE_CART0, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
  76	{ REGION_CART1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", BASE_CART1, BASE_CART1 + SIZE_CART1, SIZE_CART1, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
  77	{ REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
  78	{ REGION_CART_SRAM, "sram", "SRAM", "Static RAM (64kiB)", BASE_CART_SRAM, BASE_CART_SRAM + SIZE_CART_SRAM, SIZE_CART_SRAM, true },
  79};
  80
  81static const struct mCoreMemoryBlock _GBAMemoryBlocksFlash512[] = {
  82	{ -1, "mem", "All", "All", 0, 0x10000000, 0x10000000, mCORE_MEMORY_VIRTUAL },
  83	{ REGION_BIOS, "bios", "BIOS", "BIOS (16kiB)", BASE_BIOS, SIZE_BIOS, SIZE_BIOS, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
  84	{ REGION_WORKING_RAM, "wram", "EWRAM", "Working RAM (256kiB)", BASE_WORKING_RAM, BASE_WORKING_RAM + SIZE_WORKING_RAM, SIZE_WORKING_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
  85	{ REGION_WORKING_IRAM, "iwram", "IWRAM", "Internal Working RAM (32kiB)", BASE_WORKING_IRAM, BASE_WORKING_IRAM + SIZE_WORKING_IRAM, SIZE_WORKING_IRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
  86	{ REGION_IO, "io", "MMIO", "Memory-Mapped I/O", BASE_IO, BASE_IO + SIZE_IO, SIZE_IO, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
  87	{ REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", BASE_PALETTE_RAM, BASE_PALETTE_RAM + SIZE_PALETTE_RAM, SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
  88	{ REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", BASE_VRAM, BASE_VRAM + SIZE_VRAM, SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
  89	{ REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", BASE_OAM, BASE_OAM + SIZE_OAM, SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
  90	{ REGION_CART0, "cart0", "ROM", "Game Pak (32MiB)", BASE_CART0, BASE_CART0 + SIZE_CART0, SIZE_CART0, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
  91	{ REGION_CART1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", BASE_CART1, BASE_CART1 + SIZE_CART1, SIZE_CART1, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
  92	{ REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
  93	{ REGION_CART_SRAM, "sram", "Flash", "Flash Memory (64kiB)", BASE_CART_SRAM, BASE_CART_SRAM + SIZE_CART_FLASH512, SIZE_CART_FLASH512, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
  94};
  95
  96static const struct mCoreMemoryBlock _GBAMemoryBlocksFlash1M[] = {
  97	{ -1, "mem", "All", "All", 0, 0x10000000, 0x10000000, mCORE_MEMORY_VIRTUAL },
  98	{ REGION_BIOS, "bios", "BIOS", "BIOS (16kiB)", BASE_BIOS, SIZE_BIOS, SIZE_BIOS, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
  99	{ REGION_WORKING_RAM, "wram", "EWRAM", "Working RAM (256kiB)", BASE_WORKING_RAM, BASE_WORKING_RAM + SIZE_WORKING_RAM, SIZE_WORKING_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
 100	{ REGION_WORKING_IRAM, "iwram", "IWRAM", "Internal Working RAM (32kiB)", BASE_WORKING_IRAM, BASE_WORKING_IRAM + SIZE_WORKING_IRAM, SIZE_WORKING_IRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
 101	{ REGION_IO, "io", "MMIO", "Memory-Mapped I/O", BASE_IO, BASE_IO + SIZE_IO, SIZE_IO, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
 102	{ REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", BASE_PALETTE_RAM, BASE_PALETTE_RAM + SIZE_PALETTE_RAM, SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
 103	{ REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", BASE_VRAM, BASE_VRAM + SIZE_VRAM, SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
 104	{ REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", BASE_OAM, BASE_OAM + SIZE_OAM, SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
 105	{ REGION_CART0, "cart0", "ROM", "Game Pak (32MiB)", BASE_CART0, BASE_CART0 + SIZE_CART0, SIZE_CART0, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
 106	{ REGION_CART1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", BASE_CART1, BASE_CART1 + SIZE_CART1, SIZE_CART1, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
 107	{ REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
 108	{ REGION_CART_SRAM, "sram", "Flash", "Flash Memory (64kiB)", BASE_CART_SRAM, BASE_CART_SRAM + SIZE_CART_FLASH512, SIZE_CART_FLASH1M, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED, 1 },
 109};
 110
 111static const struct mCoreMemoryBlock _GBAMemoryBlocksEEPROM[] = {
 112	{ -1, "mem", "All", "All", 0, 0x10000000, 0x10000000, mCORE_MEMORY_VIRTUAL },
 113	{ REGION_BIOS, "bios", "BIOS", "BIOS (16kiB)", BASE_BIOS, SIZE_BIOS, SIZE_BIOS, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
 114	{ REGION_WORKING_RAM, "wram", "EWRAM", "Working RAM (256kiB)", BASE_WORKING_RAM, BASE_WORKING_RAM + SIZE_WORKING_RAM, SIZE_WORKING_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
 115	{ REGION_WORKING_IRAM, "iwram", "IWRAM", "Internal Working RAM (32kiB)", BASE_WORKING_IRAM, BASE_WORKING_IRAM + SIZE_WORKING_IRAM, SIZE_WORKING_IRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
 116	{ REGION_IO, "io", "MMIO", "Memory-Mapped I/O", BASE_IO, BASE_IO + SIZE_IO, SIZE_IO, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
 117	{ REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", BASE_PALETTE_RAM, BASE_PALETTE_RAM + SIZE_PALETTE_RAM, SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
 118	{ REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", BASE_VRAM, BASE_VRAM + SIZE_VRAM, SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
 119	{ REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", BASE_OAM, BASE_OAM + SIZE_OAM, SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
 120	{ REGION_CART0, "cart0", "ROM", "Game Pak (32MiB)", BASE_CART0, BASE_CART0 + SIZE_CART0, SIZE_CART0, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
 121	{ REGION_CART1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", BASE_CART1, BASE_CART1 + SIZE_CART1, SIZE_CART1, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
 122	{ REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
 123	{ REGION_CART_SRAM_MIRROR, "eeprom", "EEPROM", "EEPROM (8kiB)", 0, SIZE_CART_EEPROM, SIZE_CART_EEPROM, mCORE_MEMORY_RW },
 124};
 125
 126struct mVideoLogContext;
 127struct GBACore {
 128	struct mCore d;
 129	struct GBAVideoSoftwareRenderer renderer;
 130	struct GBAVideoProxyRenderer proxyRenderer;
 131	struct mVideoLogContext* logContext;
 132	struct mCoreCallbacks logCallbacks;
 133#ifndef DISABLE_THREADING
 134	struct mVideoThreadProxy threadProxy;
 135	int threadedVideo;
 136#endif
 137	int keys;
 138	struct mCPUComponent* components[CPU_COMPONENT_MAX];
 139	const struct Configuration* overrides;
 140	struct mDebuggerPlatform* debuggerPlatform;
 141	struct mCheatDevice* cheatDevice;
 142};
 143
 144static bool _GBACoreInit(struct mCore* core) {
 145	struct GBACore* gbacore = (struct GBACore*) core;
 146
 147	struct ARMCore* cpu = anonymousMemoryMap(sizeof(struct ARMCore));
 148	struct GBA* gba = anonymousMemoryMap(sizeof(struct GBA));
 149	if (!cpu || !gba) {
 150		free(cpu);
 151		free(gba);
 152		return false;
 153	}
 154	core->cpu = cpu;
 155	core->board = gba;
 156	core->debugger = NULL;
 157	core->symbolTable = NULL;
 158	gbacore->overrides = NULL;
 159	gbacore->debuggerPlatform = NULL;
 160	gbacore->cheatDevice = NULL;
 161	gbacore->logContext = NULL;
 162
 163	GBACreate(gba);
 164	// TODO: Restore cheats
 165	memset(gbacore->components, 0, sizeof(gbacore->components));
 166	ARMSetComponents(cpu, &gba->d, CPU_COMPONENT_MAX, gbacore->components);
 167	ARMInit(cpu);
 168	mRTCGenericSourceInit(&core->rtc, core);
 169	gba->rtcSource = &core->rtc.d;
 170
 171	GBAVideoSoftwareRendererCreate(&gbacore->renderer);
 172	gbacore->renderer.outputBuffer = NULL;
 173
 174#ifndef DISABLE_THREADING
 175	gbacore->threadedVideo = false;
 176	mVideoThreadProxyCreate(&gbacore->threadProxy);
 177#endif
 178	gbacore->proxyRenderer.logger = NULL;
 179
 180	gbacore->keys = 0;
 181	gba->keySource = &gbacore->keys;
 182
 183#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
 184	mDirectorySetInit(&core->dirs);
 185#endif
 186
 187#ifndef MINIMAL_CORE
 188	core->inputInfo = &GBAInputInfo;
 189#endif
 190	
 191	return true;
 192}
 193
 194static void _GBACoreDeinit(struct mCore* core) {
 195	ARMDeinit(core->cpu);
 196	GBADestroy(core->board);
 197	mappedMemoryFree(core->cpu, sizeof(struct ARMCore));
 198	mappedMemoryFree(core->board, sizeof(struct GBA));
 199#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
 200	mDirectorySetDeinit(&core->dirs);
 201#endif
 202
 203	struct GBACore* gbacore = (struct GBACore*) core;
 204	free(gbacore->debuggerPlatform);
 205	if (gbacore->cheatDevice) {
 206		mCheatDeviceDestroy(gbacore->cheatDevice);
 207	}
 208	free(gbacore->cheatDevice);
 209	mCoreConfigFreeOpts(&core->opts);
 210	free(core);
 211}
 212
 213static enum mPlatform _GBACorePlatform(const struct mCore* core) {
 214	UNUSED(core);
 215	return PLATFORM_GBA;
 216}
 217
 218static void _GBACoreSetSync(struct mCore* core, struct mCoreSync* sync) {
 219	struct GBA* gba = core->board;
 220	gba->sync = sync;
 221}
 222
 223static void _GBACoreLoadConfig(struct mCore* core, const struct mCoreConfig* config) {
 224	struct GBA* gba = core->board;
 225	if (core->opts.mute) {
 226		gba->audio.masterVolume = 0;
 227	} else {
 228		gba->audio.masterVolume = core->opts.volume;
 229	}
 230	gba->video.frameskip = core->opts.frameskip;
 231
 232#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
 233	struct GBACore* gbacore = (struct GBACore*) core;
 234	gbacore->overrides = mCoreConfigGetOverridesConst(config);
 235#endif
 236
 237	const char* idleOptimization = mCoreConfigGetValue(config, "idleOptimization");
 238	if (idleOptimization) {
 239		if (strcasecmp(idleOptimization, "ignore") == 0) {
 240			gba->idleOptimization = IDLE_LOOP_IGNORE;
 241		} else if (strcasecmp(idleOptimization, "remove") == 0) {
 242			gba->idleOptimization = IDLE_LOOP_REMOVE;
 243		} else if (strcasecmp(idleOptimization, "detect") == 0) {
 244			if (gba->idleLoop == IDLE_LOOP_NONE) {
 245				gba->idleOptimization = IDLE_LOOP_DETECT;
 246			} else {
 247				gba->idleOptimization = IDLE_LOOP_REMOVE;
 248			}
 249		}
 250	}
 251
 252	mCoreConfigCopyValue(&core->config, config, "gba.bios");
 253
 254#ifndef DISABLE_THREADING
 255	mCoreConfigGetIntValue(config, "threadedVideo", &gbacore->threadedVideo);
 256#endif
 257}
 258
 259static void _GBACoreDesiredVideoDimensions(struct mCore* core, unsigned* width, unsigned* height) {
 260	UNUSED(core);
 261	*width = VIDEO_HORIZONTAL_PIXELS;
 262	*height = VIDEO_VERTICAL_PIXELS;
 263}
 264
 265static void _GBACoreSetVideoBuffer(struct mCore* core, color_t* buffer, size_t stride) {
 266	struct GBACore* gbacore = (struct GBACore*) core;
 267	gbacore->renderer.outputBuffer = buffer;
 268	gbacore->renderer.outputBufferStride = stride;
 269}
 270
 271static void _GBACoreGetPixels(struct mCore* core, const void** buffer, size_t* stride) {
 272	struct GBACore* gbacore = (struct GBACore*) core;
 273	gbacore->renderer.d.getPixels(&gbacore->renderer.d, stride, buffer);
 274}
 275
 276static void _GBACorePutPixels(struct mCore* core, const void* buffer, size_t stride) {
 277	struct GBACore* gbacore = (struct GBACore*) core;
 278	gbacore->renderer.d.putPixels(&gbacore->renderer.d, stride, buffer);
 279}
 280
 281static struct blip_t* _GBACoreGetAudioChannel(struct mCore* core, int ch) {
 282	struct GBA* gba = core->board;
 283	switch (ch) {
 284	case 0:
 285		return gba->audio.psg.left;
 286	case 1:
 287		return gba->audio.psg.right;
 288	default:
 289		return NULL;
 290	}
 291}
 292
 293static void _GBACoreSetAudioBufferSize(struct mCore* core, size_t samples) {
 294	struct GBA* gba = core->board;
 295	GBAAudioResizeBuffer(&gba->audio, samples);
 296}
 297
 298static size_t _GBACoreGetAudioBufferSize(struct mCore* core) {
 299	struct GBA* gba = core->board;
 300	return gba->audio.samples;
 301}
 302
 303static void _GBACoreAddCoreCallbacks(struct mCore* core, struct mCoreCallbacks* coreCallbacks) {
 304	struct GBA* gba = core->board;
 305	*mCoreCallbacksListAppend(&gba->coreCallbacks) = *coreCallbacks;
 306}
 307
 308static void _GBACoreClearCoreCallbacks(struct mCore* core) {
 309	struct GBA* gba = core->board;
 310	mCoreCallbacksListClear(&gba->coreCallbacks);
 311}
 312
 313static void _GBACoreSetAVStream(struct mCore* core, struct mAVStream* stream) {
 314	struct GBA* gba = core->board;
 315	gba->stream = stream;
 316	if (stream && stream->videoDimensionsChanged) {
 317		stream->videoDimensionsChanged(stream, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
 318	}
 319	if (stream && stream->videoFrameRateChanged) {
 320		stream->videoFrameRateChanged(stream, core->frameCycles(core), core->frequency(core));
 321	}
 322}
 323
 324static bool _GBACoreLoadROM(struct mCore* core, struct VFile* vf) {
 325#ifdef USE_ELF
 326	struct ELF* elf = ELFOpen(vf);
 327	if (elf) {
 328		GBALoadNull(core->board);
 329		bool success = mCoreLoadELF(core, elf);
 330		ELFClose(elf);
 331		return success;
 332	}
 333#endif
 334	if (GBAIsMB(vf)) {
 335		return GBALoadMB(core->board, vf);
 336	}
 337	return GBALoadROM(core->board, vf);
 338}
 339
 340static bool _GBACoreLoadBIOS(struct mCore* core, struct VFile* vf, int type) {
 341	UNUSED(type);
 342	if (!GBAIsBIOS(vf)) {
 343		return false;
 344	}
 345	GBALoadBIOS(core->board, vf);
 346	return true;
 347}
 348
 349static bool _GBACoreLoadSave(struct mCore* core, struct VFile* vf) {
 350	return GBALoadSave(core->board, vf);
 351}
 352
 353static bool _GBACoreLoadTemporarySave(struct mCore* core, struct VFile* vf) {
 354	struct GBA* gba = core->board;
 355	GBASavedataMask(&gba->memory.savedata, vf, false);
 356	return true; // TODO: Return a real value
 357}
 358
 359static bool _GBACoreLoadPatch(struct mCore* core, struct VFile* vf) {
 360	if (!vf) {
 361		return false;
 362	}
 363	struct Patch patch;
 364	if (!loadPatch(vf, &patch)) {
 365		return false;
 366	}
 367	GBAApplyPatch(core->board, &patch);
 368	return true;
 369}
 370
 371static void _GBACoreUnloadROM(struct mCore* core) {
 372	struct GBACore* gbacore = (struct GBACore*) core;
 373	struct ARMCore* cpu = core->cpu;
 374	if (gbacore->cheatDevice) {
 375		ARMHotplugDetach(cpu, CPU_COMPONENT_CHEAT_DEVICE);
 376		cpu->components[CPU_COMPONENT_CHEAT_DEVICE] = NULL;
 377		mCheatDeviceDestroy(gbacore->cheatDevice);
 378		gbacore->cheatDevice = NULL;
 379	}
 380	return GBAUnloadROM(core->board);
 381}
 382
 383static void _GBACoreChecksum(const struct mCore* core, void* data, enum mCoreChecksumType type) {
 384	struct GBA* gba = (struct GBA*) core->board;
 385	switch (type) {
 386	case CHECKSUM_CRC32:
 387		memcpy(data, &gba->romCrc32, sizeof(gba->romCrc32));
 388		break;
 389	}
 390	return;
 391}
 392
 393static void _GBACoreReset(struct mCore* core) {
 394	struct GBACore* gbacore = (struct GBACore*) core;
 395	struct GBA* gba = (struct GBA*) core->board;
 396	if (gbacore->renderer.outputBuffer) {
 397		struct GBAVideoRenderer* renderer = &gbacore->renderer.d;
 398#ifndef DISABLE_THREADING
 399		if (gbacore->threadedVideo) {
 400			gbacore->proxyRenderer.logger = &gbacore->threadProxy.d;
 401			GBAVideoProxyRendererCreate(&gbacore->proxyRenderer, renderer);
 402			renderer = &gbacore->proxyRenderer.d;
 403		}
 404#endif
 405		GBAVideoAssociateRenderer(&gba->video, renderer);
 406	}
 407
 408	struct GBACartridgeOverride override;
 409	const struct GBACartridge* cart = (const struct GBACartridge*) gba->memory.rom;
 410	if (cart) {
 411		memcpy(override.id, &cart->id, sizeof(override.id));
 412		if (GBAOverrideFind(gbacore->overrides, &override)) {
 413			GBAOverrideApply(gba, &override);
 414		}
 415	}
 416
 417#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
 418	if (!gba->biosVf && core->opts.useBios) {
 419		struct VFile* bios = NULL;
 420		bool found = false;
 421		if (core->opts.bios) {
 422			bios = VFileOpen(core->opts.bios, O_RDONLY);
 423			if (bios && GBAIsBIOS(bios)) {
 424				found = true;
 425			} else if (bios) {
 426				bios->close(bios);
 427				bios = NULL;
 428			}
 429		}
 430		if (!found) {
 431			const char* configPath = mCoreConfigGetValue(&core->config, "gba.bios");
 432			if (configPath) {
 433				bios = VFileOpen(configPath, O_RDONLY);
 434			}
 435			if (bios && GBAIsBIOS(bios)) {
 436				found = true;
 437			} else if (bios) {
 438				bios->close(bios);
 439				bios = NULL;
 440			}
 441		}
 442		if (!found) {
 443			char path[PATH_MAX];
 444			mCoreConfigDirectory(path, PATH_MAX);
 445			strncat(path, PATH_SEP "gba_bios.bin", PATH_MAX - strlen(path));
 446			bios = VFileOpen(path, O_RDONLY);
 447			if (bios && GBAIsBIOS(bios)) {
 448				found = true;
 449			} else if (bios) {
 450				bios->close(bios);
 451				bios = NULL;
 452			}
 453		}
 454		if (bios) {
 455			GBALoadBIOS(gba, bios);
 456		}
 457	}
 458#endif
 459
 460	ARMReset(core->cpu);
 461	if (core->opts.skipBios && gba->isPristine) {
 462		GBASkipBIOS(core->board);
 463	}
 464}
 465
 466static void _GBACoreRunFrame(struct mCore* core) {
 467	struct GBA* gba = core->board;
 468	int32_t frameCounter = gba->video.frameCounter;
 469	while (gba->video.frameCounter == frameCounter) {
 470		ARMv4RunLoop(core->cpu);
 471	}
 472}
 473
 474static void _GBACoreRunLoop(struct mCore* core) {
 475	ARMv4RunLoop(core->cpu);
 476}
 477
 478static void _GBACoreStep(struct mCore* core) {
 479	ARMv4Run(core->cpu);
 480}
 481
 482static size_t _GBACoreStateSize(struct mCore* core) {
 483	UNUSED(core);
 484	return sizeof(struct GBASerializedState);
 485}
 486
 487static bool _GBACoreLoadState(struct mCore* core, const void* state) {
 488	return GBADeserialize(core->board, state);
 489}
 490
 491static bool _GBACoreSaveState(struct mCore* core, void* state) {
 492	GBASerialize(core->board, state);
 493	return true;
 494}
 495
 496static void _GBACoreSetKeys(struct mCore* core, uint32_t keys) {
 497	struct GBACore* gbacore = (struct GBACore*) core;
 498	gbacore->keys = keys;
 499	GBATestKeypadIRQ(core->board);
 500}
 501
 502static void _GBACoreAddKeys(struct mCore* core, uint32_t keys) {
 503	struct GBACore* gbacore = (struct GBACore*) core;
 504	gbacore->keys |= keys;
 505	GBATestKeypadIRQ(core->board);
 506}
 507
 508static void _GBACoreClearKeys(struct mCore* core, uint32_t keys) {
 509	struct GBACore* gbacore = (struct GBACore*) core;
 510	gbacore->keys &= ~keys;
 511	GBATestKeypadIRQ(core->board);
 512}
 513
 514static void _GBACoreSetCursorLocation(struct mCore* core, int x, int y) {
 515	UNUSED(core);
 516	UNUSED(x);
 517	UNUSED(y);
 518}
 519
 520static void _GBACoreSetCursorDown(struct mCore* core, bool down) {
 521	UNUSED(core);
 522	UNUSED(down);
 523}
 524
 525static int32_t _GBACoreFrameCounter(const struct mCore* core) {
 526	const struct GBA* gba = core->board;
 527	return gba->video.frameCounter;
 528}
 529
 530static int32_t _GBACoreFrameCycles(const struct mCore* core) {
 531	UNUSED(core);
 532	return VIDEO_TOTAL_LENGTH;
 533}
 534
 535static int32_t _GBACoreFrequency(const struct mCore* core) {
 536	UNUSED(core);
 537	return GBA_ARM7TDMI_FREQUENCY;
 538}
 539
 540static void _GBACoreGetGameTitle(const struct mCore* core, char* title) {
 541	GBAGetGameTitle(core->board, title);
 542}
 543
 544static void _GBACoreGetGameCode(const struct mCore* core, char* title) {
 545	GBAGetGameCode(core->board, title);
 546}
 547
 548static void _GBACoreSetPeripheral(struct mCore* core, int type, void* periph) {
 549	struct GBA* gba = core->board;
 550	switch (type) {
 551	case mPERIPH_ROTATION:
 552		gba->rotationSource = periph;
 553		break;
 554	case mPERIPH_RUMBLE:
 555		gba->rumble = periph;
 556		break;
 557	case mPERIPH_GBA_LUMINANCE:
 558		gba->luminanceSource = periph;
 559		break;
 560	default:
 561		return;
 562	}
 563}
 564
 565static uint32_t _GBACoreBusRead8(struct mCore* core, uint32_t address) {
 566	struct ARMCore* cpu = core->cpu;
 567	return cpu->memory.load8(cpu, address, 0);
 568}
 569
 570static uint32_t _GBACoreBusRead16(struct mCore* core, uint32_t address) {
 571	struct ARMCore* cpu = core->cpu;
 572	return cpu->memory.load16(cpu, address, 0);
 573
 574}
 575
 576static uint32_t _GBACoreBusRead32(struct mCore* core, uint32_t address) {
 577	struct ARMCore* cpu = core->cpu;
 578	return cpu->memory.load32(cpu, address, 0);
 579}
 580
 581static void _GBACoreBusWrite8(struct mCore* core, uint32_t address, uint8_t value) {
 582	struct ARMCore* cpu = core->cpu;
 583	cpu->memory.store8(cpu, address, value, 0);
 584}
 585
 586static void _GBACoreBusWrite16(struct mCore* core, uint32_t address, uint16_t value) {
 587	struct ARMCore* cpu = core->cpu;
 588	cpu->memory.store16(cpu, address, value, 0);
 589}
 590
 591static void _GBACoreBusWrite32(struct mCore* core, uint32_t address, uint32_t value) {
 592	struct ARMCore* cpu = core->cpu;
 593	cpu->memory.store32(cpu, address, value, 0);
 594}
 595
 596static uint32_t _GBACoreRawRead8(struct mCore* core, uint32_t address, int segment) {
 597	UNUSED(segment);
 598	struct ARMCore* cpu = core->cpu;
 599	return GBAView8(cpu, address);
 600}
 601
 602static uint32_t _GBACoreRawRead16(struct mCore* core, uint32_t address, int segment) {
 603	UNUSED(segment);
 604	struct ARMCore* cpu = core->cpu;
 605	return GBAView16(cpu, address);
 606}
 607
 608static uint32_t _GBACoreRawRead32(struct mCore* core, uint32_t address, int segment) {
 609	UNUSED(segment);
 610	struct ARMCore* cpu = core->cpu;
 611	return GBAView32(cpu, address);
 612}
 613
 614static void _GBACoreRawWrite8(struct mCore* core, uint32_t address, int segment, uint8_t value) {
 615	UNUSED(segment);
 616	struct ARMCore* cpu = core->cpu;
 617	GBAPatch8(cpu, address, value, NULL);
 618}
 619
 620static void _GBACoreRawWrite16(struct mCore* core, uint32_t address, int segment, uint16_t value) {
 621	UNUSED(segment);
 622	struct ARMCore* cpu = core->cpu;
 623	GBAPatch16(cpu, address, value, NULL);
 624}
 625
 626static void _GBACoreRawWrite32(struct mCore* core, uint32_t address, int segment, uint32_t value) {
 627	UNUSED(segment);
 628	struct ARMCore* cpu = core->cpu;
 629	GBAPatch32(cpu, address, value, NULL);
 630}
 631
 632size_t _GBAListMemoryBlocks(const struct mCore* core, const struct mCoreMemoryBlock** blocks) {
 633	const struct GBA* gba = core->board;
 634	switch (gba->memory.savedata.type) {
 635	case SAVEDATA_SRAM:
 636		*blocks = _GBAMemoryBlocksSRAM;
 637		return sizeof(_GBAMemoryBlocksSRAM) / sizeof(*_GBAMemoryBlocksSRAM);
 638	case SAVEDATA_FLASH512:
 639		*blocks = _GBAMemoryBlocksFlash512;
 640		return sizeof(_GBAMemoryBlocksFlash512) / sizeof(*_GBAMemoryBlocksFlash512);
 641	case SAVEDATA_FLASH1M:
 642		*blocks = _GBAMemoryBlocksFlash1M;
 643		return sizeof(_GBAMemoryBlocksFlash1M) / sizeof(*_GBAMemoryBlocksFlash1M);
 644	case SAVEDATA_EEPROM:
 645		*blocks = _GBAMemoryBlocksEEPROM;
 646		return sizeof(_GBAMemoryBlocksEEPROM) / sizeof(*_GBAMemoryBlocksEEPROM);
 647	default:
 648		*blocks = _GBAMemoryBlocks;
 649		return sizeof(_GBAMemoryBlocks) / sizeof(*_GBAMemoryBlocks);
 650	}
 651}
 652
 653void* _GBAGetMemoryBlock(struct mCore* core, size_t id, size_t* sizeOut) {
 654	struct GBA* gba = core->board;
 655	switch (id) {
 656	default:
 657		return NULL;
 658	case REGION_BIOS:
 659		*sizeOut = SIZE_BIOS;
 660		return gba->memory.bios;
 661	case REGION_WORKING_RAM:
 662		*sizeOut = SIZE_WORKING_RAM;
 663		return gba->memory.wram;
 664	case REGION_WORKING_IRAM:
 665		*sizeOut = SIZE_WORKING_IRAM;
 666		return gba->memory.iwram;
 667	case REGION_PALETTE_RAM:
 668		*sizeOut = SIZE_PALETTE_RAM;
 669		return gba->video.palette;
 670	case REGION_VRAM:
 671		*sizeOut = SIZE_VRAM;
 672		return gba->video.vram;
 673	case REGION_OAM:
 674		*sizeOut = SIZE_OAM;
 675		return gba->video.oam.raw;
 676	case REGION_CART0:
 677	case REGION_CART1:
 678	case REGION_CART2:
 679		*sizeOut = gba->memory.romSize;
 680		return gba->memory.rom;
 681	case REGION_CART_SRAM:
 682		if (gba->memory.savedata.type == SAVEDATA_FLASH1M) {
 683			*sizeOut = SIZE_CART_FLASH1M;
 684			return gba->memory.savedata.currentBank;
 685		}
 686		// Fall through
 687	case REGION_CART_SRAM_MIRROR:
 688		*sizeOut = GBASavedataSize(&gba->memory.savedata);
 689		return gba->memory.savedata.data;
 690	}
 691}
 692
 693#ifdef USE_DEBUGGERS
 694static bool _GBACoreSupportsDebuggerType(struct mCore* core, enum mDebuggerType type) {
 695	UNUSED(core);
 696	switch (type) {
 697	case DEBUGGER_CLI:
 698		return true;
 699#ifdef USE_GDB_STUB
 700	case DEBUGGER_GDB:
 701		return true;
 702#endif
 703	default:
 704		return false;
 705	}
 706}
 707
 708static struct mDebuggerPlatform* _GBACoreDebuggerPlatform(struct mCore* core) {
 709	struct GBACore* gbacore = (struct GBACore*) core;
 710	if (!gbacore->debuggerPlatform) {
 711		gbacore->debuggerPlatform = ARMDebuggerPlatformCreate();
 712	}
 713	return gbacore->debuggerPlatform;
 714}
 715
 716static struct CLIDebuggerSystem* _GBACoreCliDebuggerSystem(struct mCore* core) {
 717	return &GBACLIDebuggerCreate(core)->d;
 718}
 719
 720static void _GBACoreAttachDebugger(struct mCore* core, struct mDebugger* debugger) {
 721	if (core->debugger) {
 722		GBADetachDebugger(core->board);
 723	}
 724	GBAAttachDebugger(core->board, debugger);
 725	core->debugger = debugger;
 726}
 727
 728static void _GBACoreDetachDebugger(struct mCore* core) {
 729	GBADetachDebugger(core->board);
 730	core->debugger = NULL;
 731}
 732
 733static void _GBACoreLoadSymbols(struct mCore* core, struct VFile* vf) {
 734#ifdef USE_ELF
 735	bool closeAfter = false;
 736	core->symbolTable = mDebuggerSymbolTableCreate();
 737#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
 738	if (!vf) {
 739		closeAfter = true;
 740		vf = mDirectorySetOpenSuffix(&core->dirs, core->dirs.base, ".elf", O_RDONLY);
 741	}
 742#endif
 743	if (!vf) {
 744		return;
 745	}
 746	struct ELF* elf = ELFOpen(vf);
 747	if (elf) {
 748		mCoreLoadELFSymbols(core->symbolTable, elf);
 749		ELFClose(elf);
 750	}
 751	if (closeAfter) {
 752		vf->close(vf);
 753	}
 754#endif
 755}
 756#endif
 757
 758static struct mCheatDevice* _GBACoreCheatDevice(struct mCore* core) {
 759	struct GBACore* gbacore = (struct GBACore*) core;
 760	if (!gbacore->cheatDevice) {
 761		gbacore->cheatDevice = GBACheatDeviceCreate();
 762		((struct ARMCore*) core->cpu)->components[CPU_COMPONENT_CHEAT_DEVICE] = &gbacore->cheatDevice->d;
 763		ARMHotplugAttach(core->cpu, CPU_COMPONENT_CHEAT_DEVICE);
 764		gbacore->cheatDevice->p = core;
 765	}
 766	return gbacore->cheatDevice;
 767}
 768
 769static size_t _GBACoreSavedataClone(struct mCore* core, void** sram) {
 770	struct GBA* gba = core->board;
 771	size_t size = GBASavedataSize(&gba->memory.savedata);
 772	if (!size) {
 773		*sram = NULL;
 774		return 0;
 775	}
 776	*sram = malloc(size);
 777	struct VFile* vf = VFileFromMemory(*sram, size);
 778	if (!vf) {
 779		free(*sram);
 780		*sram = NULL;
 781		return 0;
 782	}
 783	bool success = GBASavedataClone(&gba->memory.savedata, vf);
 784	vf->close(vf);
 785	if (!success) {
 786		free(*sram);
 787		*sram = NULL;
 788		return 0;
 789	}
 790	return size;
 791}
 792
 793static bool _GBACoreSavedataRestore(struct mCore* core, const void* sram, size_t size, bool writeback) {
 794	struct VFile* vf = VFileMemChunk(sram, size);
 795	if (!vf) {
 796		return false;
 797	}
 798	struct GBA* gba = core->board;
 799	bool success = true;
 800	if (writeback) {
 801		success = GBASavedataLoad(&gba->memory.savedata, vf);
 802		vf->close(vf);
 803	} else {
 804		GBASavedataMask(&gba->memory.savedata, vf, true);
 805	}
 806	return success;
 807}
 808
 809static size_t _GBACoreListVideoLayers(const struct mCore* core, const struct mCoreChannelInfo** info) {
 810	UNUSED(core);
 811	*info = _GBAVideoLayers;
 812	return sizeof(_GBAVideoLayers) / sizeof(*_GBAVideoLayers);
 813}
 814
 815static size_t _GBACoreListAudioChannels(const struct mCore* core, const struct mCoreChannelInfo** info) {
 816	UNUSED(core);
 817	*info = _GBAAudioChannels;
 818	return sizeof(_GBAAudioChannels) / sizeof(*_GBAAudioChannels);
 819}
 820
 821static void _GBACoreEnableVideoLayer(struct mCore* core, size_t id, bool enable) {
 822	struct GBA* gba = core->board;
 823	switch (id) {
 824	case 0:
 825	case 1:
 826	case 2:
 827	case 3:
 828		gba->video.renderer->disableBG[id] = !enable;
 829		break;
 830	case 4:
 831		gba->video.renderer->disableOBJ = !enable;
 832		break;
 833	default:
 834		break;
 835	}
 836}
 837
 838static void _GBACoreEnableAudioChannel(struct mCore* core, size_t id, bool enable) {
 839	struct GBA* gba = core->board;
 840	switch (id) {
 841	case 0:
 842	case 1:
 843	case 2:
 844	case 3:
 845		gba->audio.psg.forceDisableCh[id] = !enable;
 846		break;
 847	case 4:
 848		gba->audio.forceDisableChA = !enable;
 849	case 5:
 850		gba->audio.forceDisableChB = !enable;
 851		break;
 852	default:
 853		break;
 854	}
 855}
 856
 857static void _GBACoreStartVideoLog(struct mCore* core, struct mVideoLogContext* context) {
 858	struct GBACore* gbacore = (struct GBACore*) core;
 859	struct GBA* gba = core->board;
 860	gbacore->logContext = context;
 861
 862	struct GBASerializedState* state = mVideoLogContextInitialState(context, NULL);
 863	state->id = 0;
 864	state->cpu.gprs[ARM_PC] = BASE_WORKING_RAM;
 865
 866	int channelId = mVideoLoggerAddChannel(context);
 867	gbacore->proxyRenderer.logger = malloc(sizeof(struct mVideoLogger));
 868	mVideoLoggerRendererCreate(gbacore->proxyRenderer.logger, false);
 869	mVideoLoggerAttachChannel(gbacore->proxyRenderer.logger, context, channelId);
 870	gbacore->proxyRenderer.logger->block = false;
 871
 872	GBAVideoProxyRendererCreate(&gbacore->proxyRenderer, &gbacore->renderer.d);
 873	GBAVideoProxyRendererShim(&gba->video, &gbacore->proxyRenderer);
 874}
 875
 876static void _GBACoreEndVideoLog(struct mCore* core) {
 877	struct GBACore* gbacore = (struct GBACore*) core;
 878	struct GBA* gba = core->board;
 879	GBAVideoProxyRendererUnshim(&gba->video, &gbacore->proxyRenderer);
 880	free(gbacore->proxyRenderer.logger);
 881	gbacore->proxyRenderer.logger = NULL;
 882}
 883
 884struct mCore* GBACoreCreate(void) {
 885	struct GBACore* gbacore = malloc(sizeof(*gbacore));
 886	struct mCore* core = &gbacore->d;
 887	memset(&core->opts, 0, sizeof(core->opts));
 888	core->cpu = NULL;
 889	core->board = NULL;
 890	core->debugger = NULL;
 891	core->init = _GBACoreInit;
 892	core->deinit = _GBACoreDeinit;
 893	core->platform = _GBACorePlatform;
 894	core->setSync = _GBACoreSetSync;
 895	core->loadConfig = _GBACoreLoadConfig;
 896	core->desiredVideoDimensions = _GBACoreDesiredVideoDimensions;
 897	core->setVideoBuffer = _GBACoreSetVideoBuffer;
 898	core->getPixels = _GBACoreGetPixels;
 899	core->putPixels = _GBACorePutPixels;
 900	core->getAudioChannel = _GBACoreGetAudioChannel;
 901	core->setAudioBufferSize = _GBACoreSetAudioBufferSize;
 902	core->getAudioBufferSize = _GBACoreGetAudioBufferSize;
 903	core->addCoreCallbacks = _GBACoreAddCoreCallbacks;
 904	core->clearCoreCallbacks = _GBACoreClearCoreCallbacks;
 905	core->setAVStream = _GBACoreSetAVStream;
 906	core->isROM = GBAIsROM;
 907	core->loadROM = _GBACoreLoadROM;
 908	core->loadBIOS = _GBACoreLoadBIOS;
 909	core->loadSave = _GBACoreLoadSave;
 910	core->loadTemporarySave = _GBACoreLoadTemporarySave;
 911	core->loadPatch = _GBACoreLoadPatch;
 912	core->unloadROM = _GBACoreUnloadROM;
 913	core->checksum = _GBACoreChecksum;
 914	core->reset = _GBACoreReset;
 915	core->runFrame = _GBACoreRunFrame;
 916	core->runLoop = _GBACoreRunLoop;
 917	core->step = _GBACoreStep;
 918	core->stateSize = _GBACoreStateSize;
 919	core->loadState = _GBACoreLoadState;
 920	core->saveState = _GBACoreSaveState;
 921	core->setKeys = _GBACoreSetKeys;
 922	core->addKeys = _GBACoreAddKeys;
 923	core->clearKeys = _GBACoreClearKeys;
 924	core->setCursorLocation = _GBACoreSetCursorLocation;
 925	core->setCursorDown = _GBACoreSetCursorDown;
 926	core->frameCounter = _GBACoreFrameCounter;
 927	core->frameCycles = _GBACoreFrameCycles;
 928	core->frequency = _GBACoreFrequency;
 929	core->getGameTitle = _GBACoreGetGameTitle;
 930	core->getGameCode = _GBACoreGetGameCode;
 931	core->setPeripheral = _GBACoreSetPeripheral;
 932	core->busRead8 = _GBACoreBusRead8;
 933	core->busRead16 = _GBACoreBusRead16;
 934	core->busRead32 = _GBACoreBusRead32;
 935	core->busWrite8 = _GBACoreBusWrite8;
 936	core->busWrite16 = _GBACoreBusWrite16;
 937	core->busWrite32 = _GBACoreBusWrite32;
 938	core->rawRead8 = _GBACoreRawRead8;
 939	core->rawRead16 = _GBACoreRawRead16;
 940	core->rawRead32 = _GBACoreRawRead32;
 941	core->rawWrite8 = _GBACoreRawWrite8;
 942	core->rawWrite16 = _GBACoreRawWrite16;
 943	core->rawWrite32 = _GBACoreRawWrite32;
 944	core->listMemoryBlocks = _GBAListMemoryBlocks;
 945	core->getMemoryBlock = _GBAGetMemoryBlock;
 946#ifdef USE_DEBUGGERS
 947	core->supportsDebuggerType = _GBACoreSupportsDebuggerType;
 948	core->debuggerPlatform = _GBACoreDebuggerPlatform;
 949	core->cliDebuggerSystem = _GBACoreCliDebuggerSystem;
 950	core->attachDebugger = _GBACoreAttachDebugger;
 951	core->detachDebugger = _GBACoreDetachDebugger;
 952	core->loadSymbols = _GBACoreLoadSymbols;
 953#endif
 954	core->cheatDevice = _GBACoreCheatDevice;
 955	core->savedataClone = _GBACoreSavedataClone;
 956	core->savedataRestore = _GBACoreSavedataRestore;
 957	core->listVideoLayers = _GBACoreListVideoLayers;
 958	core->listAudioChannels = _GBACoreListAudioChannels;
 959	core->enableVideoLayer = _GBACoreEnableVideoLayer;
 960	core->enableAudioChannel = _GBACoreEnableAudioChannel;
 961#ifndef MINIMAL_CORE
 962	core->startVideoLog = _GBACoreStartVideoLog;
 963	core->endVideoLog = _GBACoreEndVideoLog;
 964#endif
 965	return core;
 966}
 967
 968#ifndef MINIMAL_CORE
 969static void _GBAVLPStartFrameCallback(void *context) {
 970	struct mCore* core = context;
 971	struct GBACore* gbacore = (struct GBACore*) core;
 972	struct GBA* gba = core->board;
 973
 974	if (!mVideoLoggerRendererRun(gbacore->proxyRenderer.logger, true)) {
 975		GBAVideoProxyRendererUnshim(&gba->video, &gbacore->proxyRenderer);
 976		mVideoLogContextRewind(gbacore->logContext, core);
 977		GBAVideoProxyRendererShim(&gba->video, &gbacore->proxyRenderer);
 978	}
 979}
 980
 981static bool _GBAVLPInit(struct mCore* core) {
 982	struct GBACore* gbacore = (struct GBACore*) core;
 983	if (!_GBACoreInit(core)) {
 984		return false;
 985	}
 986	gbacore->proxyRenderer.logger = malloc(sizeof(struct mVideoLogger));
 987	mVideoLoggerRendererCreate(gbacore->proxyRenderer.logger, true);
 988	GBAVideoProxyRendererCreate(&gbacore->proxyRenderer, NULL);
 989	memset(&gbacore->logCallbacks, 0, sizeof(gbacore->logCallbacks));
 990	gbacore->logCallbacks.videoFrameStarted = _GBAVLPStartFrameCallback;
 991	gbacore->logCallbacks.context = core;
 992	core->addCoreCallbacks(core, &gbacore->logCallbacks);
 993	return true;
 994}
 995
 996static void _GBAVLPDeinit(struct mCore* core) {
 997	struct GBACore* gbacore = (struct GBACore*) core;
 998	if (gbacore->logContext) {
 999		mVideoLogContextDestroy(core, gbacore->logContext);
1000	}
1001	_GBACoreDeinit(core);
1002}
1003
1004static void _GBAVLPReset(struct mCore* core) {
1005	struct GBACore* gbacore = (struct GBACore*) core;
1006	struct GBA* gba = (struct GBA*) core->board;
1007	if (gba->video.renderer == &gbacore->proxyRenderer.d) {
1008		GBAVideoProxyRendererUnshim(&gba->video, &gbacore->proxyRenderer);
1009	} else if (gbacore->renderer.outputBuffer) {
1010		struct GBAVideoRenderer* renderer = &gbacore->renderer.d;
1011		GBAVideoAssociateRenderer(&gba->video, renderer);
1012	}
1013
1014	ARMReset(core->cpu);
1015	mVideoLogContextRewind(gbacore->logContext, core);
1016	GBAVideoProxyRendererShim(&gba->video, &gbacore->proxyRenderer);
1017
1018	// Make sure CPU loop never spins
1019	GBAHalt(gba);
1020	gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IME, 0, NULL);
1021	gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IE, 0, NULL);
1022}
1023
1024static bool _GBAVLPLoadROM(struct mCore* core, struct VFile* vf) {
1025	struct GBACore* gbacore = (struct GBACore*) core;
1026	gbacore->logContext = mVideoLogContextCreate(NULL);
1027	if (!mVideoLogContextLoad(gbacore->logContext, vf)) {
1028		mVideoLogContextDestroy(core, gbacore->logContext);
1029		gbacore->logContext = NULL;
1030		return false;
1031	}
1032	mVideoLoggerAttachChannel(gbacore->proxyRenderer.logger, gbacore->logContext, 0);
1033	return true;
1034}
1035
1036static bool _GBAVLPLoadState(struct mCore* core, const void* state) {
1037	struct GBA* gba = (struct GBA*) core->board;
1038
1039	gba->timing.root = NULL;
1040	gba->cpu->gprs[ARM_PC] = BASE_WORKING_RAM;
1041	gba->cpu->memory.setActiveRegion(gba->cpu, gba->cpu->gprs[ARM_PC]);
1042
1043	// Make sure CPU loop never spins
1044	GBAHalt(gba);
1045	gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IME, 0, NULL);
1046	gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IE, 0, NULL);
1047	GBAVideoDeserialize(&gba->video, state);
1048	GBAIODeserialize(gba, state);
1049	GBAAudioReset(&gba->audio);
1050
1051	return true;
1052}
1053
1054static bool _returnTrue(struct VFile* vf) {
1055	UNUSED(vf);
1056	return true;
1057}
1058
1059struct mCore* GBAVideoLogPlayerCreate(void) {
1060	struct mCore* core = GBACoreCreate();
1061	core->init = _GBAVLPInit;
1062	core->deinit = _GBAVLPDeinit;
1063	core->reset = _GBAVLPReset;
1064	core->loadROM = _GBAVLPLoadROM;
1065	core->loadState = _GBAVLPLoadState;
1066	core->isROM = _returnTrue;
1067	return core;
1068}
1069#else
1070struct mCore* GBAVideoLogPlayerCreate(void) {
1071	return false;
1072}
1073#endif