all repos — mgba @ 42818c764da9400a8dc9aef368711c786cff5884

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/audio-mixer.h>
  16#include <mgba/internal/gba/extra/cli.h>
  17#include <mgba/internal/gba/overrides.h>
  18#ifndef DISABLE_THREADING
  19#include <mgba/feature/thread-proxy.h>
  20#endif
  21#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
  22#include <mgba/internal/gba/renderers/gl.h>
  23#endif
  24#include <mgba/internal/gba/renderers/proxy.h>
  25#include <mgba/internal/gba/renderers/video-software.h>
  26#include <mgba/internal/gba/savedata.h>
  27#include <mgba/internal/gba/serialize.h>
  28#ifdef USE_ELF
  29#include <mgba-util/elf-read.h>
  30#endif
  31#include <mgba-util/memory.h>
  32#include <mgba-util/patch.h>
  33#include <mgba-util/vfs.h>
  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;
 127
 128#define CPU_COMPONENT_AUDIO_MIXER CPU_COMPONENT_MISC_1
 129
 130struct GBACore {
 131	struct mCore d;
 132	struct GBAVideoSoftwareRenderer renderer;
 133#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
 134	struct GBAVideoGLRenderer glRenderer;
 135#endif
 136	struct GBAVideoProxyRenderer vlProxy;
 137	struct GBAVideoProxyRenderer proxyRenderer;
 138	struct mVideoLogContext* logContext;
 139	struct mCoreCallbacks logCallbacks;
 140#ifndef DISABLE_THREADING
 141	struct mVideoThreadProxy threadProxy;
 142#endif
 143	int keys;
 144	struct mCPUComponent* components[CPU_COMPONENT_MAX];
 145	const struct Configuration* overrides;
 146	struct mDebuggerPlatform* debuggerPlatform;
 147	struct mCheatDevice* cheatDevice;
 148	struct GBAAudioMixer* audioMixer;
 149};
 150
 151static bool _GBACoreInit(struct mCore* core) {
 152	struct GBACore* gbacore = (struct GBACore*) core;
 153
 154	struct ARMCore* cpu = anonymousMemoryMap(sizeof(struct ARMCore));
 155	struct GBA* gba = anonymousMemoryMap(sizeof(struct GBA));
 156	if (!cpu || !gba) {
 157		free(cpu);
 158		free(gba);
 159		return false;
 160	}
 161	core->cpu = cpu;
 162	core->board = gba;
 163	core->timing = &gba->timing;
 164	core->debugger = NULL;
 165	core->symbolTable = NULL;
 166	core->videoLogger = NULL;
 167	gbacore->overrides = NULL;
 168	gbacore->debuggerPlatform = NULL;
 169	gbacore->cheatDevice = NULL;
 170	gbacore->logContext = NULL;
 171	gbacore->audioMixer = NULL;
 172
 173	GBACreate(gba);
 174	// TODO: Restore cheats
 175	memset(gbacore->components, 0, sizeof(gbacore->components));
 176	ARMSetComponents(cpu, &gba->d, CPU_COMPONENT_MAX, gbacore->components);
 177	ARMInit(cpu);
 178	mRTCGenericSourceInit(&core->rtc, core);
 179	gba->rtcSource = &core->rtc.d;
 180
 181	GBAVideoSoftwareRendererCreate(&gbacore->renderer);
 182	gbacore->renderer.outputBuffer = NULL;
 183
 184#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
 185	GBAVideoGLRendererCreate(&gbacore->glRenderer);
 186	gbacore->glRenderer.outputTex = -1;
 187#endif
 188
 189#ifndef DISABLE_THREADING
 190	mVideoThreadProxyCreate(&gbacore->threadProxy);
 191#endif
 192	gbacore->vlProxy.logger = NULL;
 193	gbacore->proxyRenderer.logger = NULL;
 194
 195	gbacore->keys = 0;
 196	gba->keySource = &gbacore->keys;
 197
 198#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
 199	mDirectorySetInit(&core->dirs);
 200#endif
 201	
 202	return true;
 203}
 204
 205static void _GBACoreDeinit(struct mCore* core) {
 206	ARMDeinit(core->cpu);
 207	GBADestroy(core->board);
 208	mappedMemoryFree(core->cpu, sizeof(struct ARMCore));
 209	mappedMemoryFree(core->board, sizeof(struct GBA));
 210#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
 211	mDirectorySetDeinit(&core->dirs);
 212#endif
 213
 214	struct GBACore* gbacore = (struct GBACore*) core;
 215	free(gbacore->debuggerPlatform);
 216	if (gbacore->cheatDevice) {
 217		mCheatDeviceDestroy(gbacore->cheatDevice);
 218	}
 219	free(gbacore->cheatDevice);
 220	free(gbacore->audioMixer);
 221	mCoreConfigFreeOpts(&core->opts);
 222	free(core);
 223}
 224
 225static enum mPlatform _GBACorePlatform(const struct mCore* core) {
 226	UNUSED(core);
 227	return PLATFORM_GBA;
 228}
 229
 230static bool _GBACoreSupportsFeature(const struct mCore* core, enum mCoreFeature feature) {
 231	UNUSED(core);
 232	switch (feature) {
 233	case mCORE_FEATURE_OPENGL:
 234#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
 235		return true;
 236#else
 237		return false;
 238#endif
 239	default:
 240		return false;
 241	}
 242}
 243
 244static void _GBACoreSetSync(struct mCore* core, struct mCoreSync* sync) {
 245	struct GBA* gba = core->board;
 246	gba->sync = sync;
 247}
 248
 249static void _GBACoreLoadConfig(struct mCore* core, const struct mCoreConfig* config) {
 250	struct GBA* gba = core->board;
 251	if (core->opts.mute) {
 252		gba->audio.masterVolume = 0;
 253	} else {
 254		gba->audio.masterVolume = core->opts.volume;
 255	}
 256	gba->video.frameskip = core->opts.frameskip;
 257
 258#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
 259	struct GBACore* gbacore = (struct GBACore*) core;
 260	gbacore->overrides = mCoreConfigGetOverridesConst(config);
 261#endif
 262
 263	const char* idleOptimization = mCoreConfigGetValue(config, "idleOptimization");
 264	if (idleOptimization) {
 265		if (strcasecmp(idleOptimization, "ignore") == 0) {
 266			gba->idleOptimization = IDLE_LOOP_IGNORE;
 267		} else if (strcasecmp(idleOptimization, "remove") == 0) {
 268			gba->idleOptimization = IDLE_LOOP_REMOVE;
 269		} else if (strcasecmp(idleOptimization, "detect") == 0) {
 270			if (gba->idleLoop == IDLE_LOOP_NONE) {
 271				gba->idleOptimization = IDLE_LOOP_DETECT;
 272			} else {
 273				gba->idleOptimization = IDLE_LOOP_REMOVE;
 274			}
 275		}
 276	}
 277
 278	int fakeBool = 0;
 279	mCoreConfigGetIntValue(config, "allowOpposingDirections", &fakeBool);
 280	gba->allowOpposingDirections = fakeBool;
 281
 282	mCoreConfigCopyValue(&core->config, config, "allowOpposingDirections");
 283	mCoreConfigCopyValue(&core->config, config, "gba.bios");
 284	mCoreConfigCopyValue(&core->config, config, "gba.audioHle");
 285
 286#ifndef DISABLE_THREADING
 287	mCoreConfigCopyValue(&core->config, config, "threadedVideo");
 288#endif
 289	mCoreConfigCopyValue(&core->config, config, "hwaccelVideo");
 290	mCoreConfigCopyValue(&core->config, config, "videoScale");
 291}
 292
 293static void _GBACoreDesiredVideoDimensions(struct mCore* core, unsigned* width, unsigned* height) {
 294#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
 295	struct GBACore* gbacore = (struct GBACore*) core;
 296	int scale = gbacore->glRenderer.scale;
 297#else
 298	int scale = 1;
 299#endif
 300
 301	*width = GBA_VIDEO_HORIZONTAL_PIXELS * scale;
 302	*height = GBA_VIDEO_VERTICAL_PIXELS * scale;
 303}
 304
 305static void _GBACoreSetVideoBuffer(struct mCore* core, color_t* buffer, size_t stride) {
 306	struct GBACore* gbacore = (struct GBACore*) core;
 307	gbacore->renderer.outputBuffer = buffer;
 308	gbacore->renderer.outputBufferStride = stride;
 309	memset(gbacore->renderer.scanlineDirty, 0xFFFFFFFF, sizeof(gbacore->renderer.scanlineDirty));
 310}
 311
 312static void _GBACoreSetVideoGLTex(struct mCore* core, unsigned texid) {
 313#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
 314	struct GBACore* gbacore = (struct GBACore*) core;
 315	gbacore->glRenderer.outputTex = texid;
 316#else
 317	UNUSED(core);
 318	UNUSED(texid);
 319#endif
 320}
 321
 322static void _GBACoreGetPixels(struct mCore* core, const void** buffer, size_t* stride) {
 323	struct GBA* gba = core->board;
 324	gba->video.renderer->getPixels(gba->video.renderer, stride, buffer);
 325}
 326
 327static void _GBACorePutPixels(struct mCore* core, const void* buffer, size_t stride) {
 328	struct GBA* gba = core->board;
 329	gba->video.renderer->putPixels(gba->video.renderer, stride, buffer);
 330}
 331
 332static struct blip_t* _GBACoreGetAudioChannel(struct mCore* core, int ch) {
 333	struct GBA* gba = core->board;
 334	switch (ch) {
 335	case 0:
 336		return gba->audio.psg.left;
 337	case 1:
 338		return gba->audio.psg.right;
 339	default:
 340		return NULL;
 341	}
 342}
 343
 344static void _GBACoreSetAudioBufferSize(struct mCore* core, size_t samples) {
 345	struct GBA* gba = core->board;
 346	GBAAudioResizeBuffer(&gba->audio, samples);
 347}
 348
 349static size_t _GBACoreGetAudioBufferSize(struct mCore* core) {
 350	struct GBA* gba = core->board;
 351	return gba->audio.samples;
 352}
 353
 354static void _GBACoreAddCoreCallbacks(struct mCore* core, struct mCoreCallbacks* coreCallbacks) {
 355	struct GBA* gba = core->board;
 356	*mCoreCallbacksListAppend(&gba->coreCallbacks) = *coreCallbacks;
 357}
 358
 359static void _GBACoreClearCoreCallbacks(struct mCore* core) {
 360	struct GBA* gba = core->board;
 361	mCoreCallbacksListClear(&gba->coreCallbacks);
 362}
 363
 364static void _GBACoreSetAVStream(struct mCore* core, struct mAVStream* stream) {
 365	struct GBA* gba = core->board;
 366	gba->stream = stream;
 367	if (stream && stream->videoDimensionsChanged) {
 368		unsigned width, height;
 369		core->desiredVideoDimensions(core, &width, &height);
 370		stream->videoDimensionsChanged(stream, width, height);
 371	}
 372}
 373
 374static bool _GBACoreLoadROM(struct mCore* core, struct VFile* vf) {
 375#ifdef USE_ELF
 376	struct ELF* elf = ELFOpen(vf);
 377	if (elf) {
 378		GBALoadNull(core->board);
 379		bool success = mCoreLoadELF(core, elf);
 380		ELFClose(elf);
 381		return success;
 382	}
 383#endif
 384	if (GBAIsMB(vf)) {
 385		return GBALoadMB(core->board, vf);
 386	}
 387	return GBALoadROM(core->board, vf);
 388}
 389
 390static bool _GBACoreLoadBIOS(struct mCore* core, struct VFile* vf, int type) {
 391	UNUSED(type);
 392	if (!GBAIsBIOS(vf)) {
 393		return false;
 394	}
 395	GBALoadBIOS(core->board, vf);
 396	return true;
 397}
 398
 399static bool _GBACoreLoadSave(struct mCore* core, struct VFile* vf) {
 400	return GBALoadSave(core->board, vf);
 401}
 402
 403static bool _GBACoreLoadTemporarySave(struct mCore* core, struct VFile* vf) {
 404	struct GBA* gba = core->board;
 405	GBASavedataMask(&gba->memory.savedata, vf, false);
 406	return true; // TODO: Return a real value
 407}
 408
 409static bool _GBACoreLoadPatch(struct mCore* core, struct VFile* vf) {
 410	if (!vf) {
 411		return false;
 412	}
 413	struct Patch patch;
 414	if (!loadPatch(vf, &patch)) {
 415		return false;
 416	}
 417	GBAApplyPatch(core->board, &patch);
 418	return true;
 419}
 420
 421static void _GBACoreUnloadROM(struct mCore* core) {
 422	struct GBACore* gbacore = (struct GBACore*) core;
 423	struct ARMCore* cpu = core->cpu;
 424	if (gbacore->cheatDevice) {
 425		ARMHotplugDetach(cpu, CPU_COMPONENT_CHEAT_DEVICE);
 426		cpu->components[CPU_COMPONENT_CHEAT_DEVICE] = NULL;
 427		mCheatDeviceDestroy(gbacore->cheatDevice);
 428		gbacore->cheatDevice = NULL;
 429	}
 430	return GBAUnloadROM(core->board);
 431}
 432
 433static void _GBACoreChecksum(const struct mCore* core, void* data, enum mCoreChecksumType type) {
 434	struct GBA* gba = (struct GBA*) core->board;
 435	switch (type) {
 436	case CHECKSUM_CRC32:
 437		memcpy(data, &gba->romCrc32, sizeof(gba->romCrc32));
 438		break;
 439	}
 440	return;
 441}
 442
 443static void _GBACoreReset(struct mCore* core) {
 444	struct GBACore* gbacore = (struct GBACore*) core;
 445	struct GBA* gba = (struct GBA*) core->board;
 446	if (gbacore->renderer.outputBuffer
 447#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
 448	    || gbacore->glRenderer.outputTex != (unsigned) -1
 449#endif
 450	) {
 451		struct GBAVideoRenderer* renderer;
 452		if (gbacore->renderer.outputBuffer) {
 453			renderer = &gbacore->renderer.d;
 454		}
 455		int fakeBool;
 456#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
 457		if (gbacore->glRenderer.outputTex != (unsigned) -1 && mCoreConfigGetIntValue(&core->config, "hwaccelVideo", &fakeBool) && fakeBool) {
 458			renderer = &gbacore->glRenderer.d;
 459			mCoreConfigGetIntValue(&core->config, "videoScale", &gbacore->glRenderer.scale);
 460		}
 461#endif
 462#ifndef DISABLE_THREADING
 463		if (mCoreConfigGetIntValue(&core->config, "threadedVideo", &fakeBool) && fakeBool) {
 464			if (!core->videoLogger) {
 465				core->videoLogger = &gbacore->threadProxy.d;
 466			}
 467		}
 468#endif
 469		if (core->videoLogger) {
 470			gbacore->proxyRenderer.logger = core->videoLogger;
 471			GBAVideoProxyRendererCreate(&gbacore->proxyRenderer, renderer);
 472			renderer = &gbacore->proxyRenderer.d;
 473		}
 474		GBAVideoAssociateRenderer(&gba->video, renderer);
 475	}
 476
 477#ifndef MINIMAL_CORE
 478	int useAudioMixer;
 479	if (!gbacore->audioMixer && mCoreConfigGetIntValue(&core->config, "gba.audioHle", &useAudioMixer) && useAudioMixer) {
 480		gbacore->audioMixer = malloc(sizeof(*gbacore->audioMixer));
 481		GBAAudioMixerCreate(gbacore->audioMixer);
 482		((struct ARMCore*) core->cpu)->components[CPU_COMPONENT_AUDIO_MIXER] = &gbacore->audioMixer->d;
 483		ARMHotplugAttach(core->cpu, CPU_COMPONENT_AUDIO_MIXER);
 484	}
 485#endif
 486
 487	GBAOverrideApplyDefaults(gba, gbacore->overrides);
 488
 489#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
 490	if (!gba->biosVf && core->opts.useBios) {
 491		struct VFile* bios = NULL;
 492		bool found = false;
 493		if (core->opts.bios) {
 494			bios = VFileOpen(core->opts.bios, O_RDONLY);
 495			if (bios && GBAIsBIOS(bios)) {
 496				found = true;
 497			} else if (bios) {
 498				bios->close(bios);
 499				bios = NULL;
 500			}
 501		}
 502		if (!found) {
 503			const char* configPath = mCoreConfigGetValue(&core->config, "gba.bios");
 504			if (configPath) {
 505				bios = VFileOpen(configPath, O_RDONLY);
 506			}
 507			if (bios && GBAIsBIOS(bios)) {
 508				found = true;
 509			} else if (bios) {
 510				bios->close(bios);
 511				bios = NULL;
 512			}
 513		}
 514		if (!found) {
 515			char path[PATH_MAX];
 516			mCoreConfigDirectory(path, PATH_MAX);
 517			strncat(path, PATH_SEP "gba_bios.bin", PATH_MAX - strlen(path));
 518			bios = VFileOpen(path, O_RDONLY);
 519			if (bios && GBAIsBIOS(bios)) {
 520				found = true;
 521			} else if (bios) {
 522				bios->close(bios);
 523				bios = NULL;
 524			}
 525		}
 526		if (bios) {
 527			GBALoadBIOS(gba, bios);
 528		}
 529	}
 530#endif
 531
 532	ARMReset(core->cpu);
 533	if (core->opts.skipBios && (gba->romVf || gba->memory.rom)) {
 534		GBASkipBIOS(core->board);
 535	}
 536}
 537
 538static void _GBACoreRunFrame(struct mCore* core) {
 539	struct GBA* gba = core->board;
 540	int32_t frameCounter = gba->video.frameCounter;
 541	while (gba->video.frameCounter == frameCounter) {
 542		ARMRunLoop(core->cpu);
 543	}
 544}
 545
 546static void _GBACoreRunLoop(struct mCore* core) {
 547	ARMRunLoop(core->cpu);
 548}
 549
 550static void _GBACoreStep(struct mCore* core) {
 551	ARMRun(core->cpu);
 552}
 553
 554static size_t _GBACoreStateSize(struct mCore* core) {
 555	UNUSED(core);
 556	return sizeof(struct GBASerializedState);
 557}
 558
 559static bool _GBACoreLoadState(struct mCore* core, const void* state) {
 560	return GBADeserialize(core->board, state);
 561}
 562
 563static bool _GBACoreSaveState(struct mCore* core, void* state) {
 564	GBASerialize(core->board, state);
 565	return true;
 566}
 567
 568static void _GBACoreSetKeys(struct mCore* core, uint32_t keys) {
 569	struct GBACore* gbacore = (struct GBACore*) core;
 570	gbacore->keys = keys;
 571	GBATestKeypadIRQ(core->board);
 572}
 573
 574static void _GBACoreAddKeys(struct mCore* core, uint32_t keys) {
 575	struct GBACore* gbacore = (struct GBACore*) core;
 576	gbacore->keys |= keys;
 577	GBATestKeypadIRQ(core->board);
 578}
 579
 580static void _GBACoreClearKeys(struct mCore* core, uint32_t keys) {
 581	struct GBACore* gbacore = (struct GBACore*) core;
 582	gbacore->keys &= ~keys;
 583}
 584
 585static int32_t _GBACoreFrameCounter(const struct mCore* core) {
 586	const struct GBA* gba = core->board;
 587	return gba->video.frameCounter;
 588}
 589
 590static int32_t _GBACoreFrameCycles(const struct mCore* core) {
 591	UNUSED(core);
 592	return VIDEO_TOTAL_LENGTH;
 593}
 594
 595static int32_t _GBACoreFrequency(const struct mCore* core) {
 596	UNUSED(core);
 597	return GBA_ARM7TDMI_FREQUENCY;
 598}
 599
 600static void _GBACoreGetGameTitle(const struct mCore* core, char* title) {
 601	GBAGetGameTitle(core->board, title);
 602}
 603
 604static void _GBACoreGetGameCode(const struct mCore* core, char* title) {
 605	GBAGetGameCode(core->board, title);
 606}
 607
 608static void _GBACoreSetPeripheral(struct mCore* core, int type, void* periph) {
 609	struct GBA* gba = core->board;
 610	switch (type) {
 611	case mPERIPH_ROTATION:
 612		gba->rotationSource = periph;
 613		break;
 614	case mPERIPH_RUMBLE:
 615		gba->rumble = periph;
 616		break;
 617	case mPERIPH_GBA_LUMINANCE:
 618		gba->luminanceSource = periph;
 619		break;
 620	case mPERIPH_GBA_BATTLECHIP_GATE:
 621		GBASIOSetDriver(&gba->sio, periph, SIO_MULTI);
 622		GBASIOSetDriver(&gba->sio, periph, SIO_NORMAL_32);
 623		break;
 624	default:
 625		return;
 626	}
 627}
 628
 629static uint32_t _GBACoreBusRead8(struct mCore* core, uint32_t address) {
 630	struct ARMCore* cpu = core->cpu;
 631	return cpu->memory.load8(cpu, address, 0);
 632}
 633
 634static uint32_t _GBACoreBusRead16(struct mCore* core, uint32_t address) {
 635	struct ARMCore* cpu = core->cpu;
 636	return cpu->memory.load16(cpu, address, 0);
 637
 638}
 639
 640static uint32_t _GBACoreBusRead32(struct mCore* core, uint32_t address) {
 641	struct ARMCore* cpu = core->cpu;
 642	return cpu->memory.load32(cpu, address, 0);
 643}
 644
 645static void _GBACoreBusWrite8(struct mCore* core, uint32_t address, uint8_t value) {
 646	struct ARMCore* cpu = core->cpu;
 647	cpu->memory.store8(cpu, address, value, 0);
 648}
 649
 650static void _GBACoreBusWrite16(struct mCore* core, uint32_t address, uint16_t value) {
 651	struct ARMCore* cpu = core->cpu;
 652	cpu->memory.store16(cpu, address, value, 0);
 653}
 654
 655static void _GBACoreBusWrite32(struct mCore* core, uint32_t address, uint32_t value) {
 656	struct ARMCore* cpu = core->cpu;
 657	cpu->memory.store32(cpu, address, value, 0);
 658}
 659
 660static uint32_t _GBACoreRawRead8(struct mCore* core, uint32_t address, int segment) {
 661	UNUSED(segment);
 662	struct ARMCore* cpu = core->cpu;
 663	return GBAView8(cpu, address);
 664}
 665
 666static uint32_t _GBACoreRawRead16(struct mCore* core, uint32_t address, int segment) {
 667	UNUSED(segment);
 668	struct ARMCore* cpu = core->cpu;
 669	return GBAView16(cpu, address);
 670}
 671
 672static uint32_t _GBACoreRawRead32(struct mCore* core, uint32_t address, int segment) {
 673	UNUSED(segment);
 674	struct ARMCore* cpu = core->cpu;
 675	return GBAView32(cpu, address);
 676}
 677
 678static void _GBACoreRawWrite8(struct mCore* core, uint32_t address, int segment, uint8_t value) {
 679	UNUSED(segment);
 680	struct ARMCore* cpu = core->cpu;
 681	GBAPatch8(cpu, address, value, NULL);
 682}
 683
 684static void _GBACoreRawWrite16(struct mCore* core, uint32_t address, int segment, uint16_t value) {
 685	UNUSED(segment);
 686	struct ARMCore* cpu = core->cpu;
 687	GBAPatch16(cpu, address, value, NULL);
 688}
 689
 690static void _GBACoreRawWrite32(struct mCore* core, uint32_t address, int segment, uint32_t value) {
 691	UNUSED(segment);
 692	struct ARMCore* cpu = core->cpu;
 693	GBAPatch32(cpu, address, value, NULL);
 694}
 695
 696size_t _GBAListMemoryBlocks(const struct mCore* core, const struct mCoreMemoryBlock** blocks) {
 697	const struct GBA* gba = core->board;
 698	switch (gba->memory.savedata.type) {
 699	case SAVEDATA_SRAM:
 700		*blocks = _GBAMemoryBlocksSRAM;
 701		return sizeof(_GBAMemoryBlocksSRAM) / sizeof(*_GBAMemoryBlocksSRAM);
 702	case SAVEDATA_FLASH512:
 703		*blocks = _GBAMemoryBlocksFlash512;
 704		return sizeof(_GBAMemoryBlocksFlash512) / sizeof(*_GBAMemoryBlocksFlash512);
 705	case SAVEDATA_FLASH1M:
 706		*blocks = _GBAMemoryBlocksFlash1M;
 707		return sizeof(_GBAMemoryBlocksFlash1M) / sizeof(*_GBAMemoryBlocksFlash1M);
 708	case SAVEDATA_EEPROM:
 709		*blocks = _GBAMemoryBlocksEEPROM;
 710		return sizeof(_GBAMemoryBlocksEEPROM) / sizeof(*_GBAMemoryBlocksEEPROM);
 711	default:
 712		*blocks = _GBAMemoryBlocks;
 713		return sizeof(_GBAMemoryBlocks) / sizeof(*_GBAMemoryBlocks);
 714	}
 715}
 716
 717void* _GBAGetMemoryBlock(struct mCore* core, size_t id, size_t* sizeOut) {
 718	struct GBA* gba = core->board;
 719	switch (id) {
 720	default:
 721		return NULL;
 722	case REGION_BIOS:
 723		*sizeOut = SIZE_BIOS;
 724		return gba->memory.bios;
 725	case REGION_WORKING_RAM:
 726		*sizeOut = SIZE_WORKING_RAM;
 727		return gba->memory.wram;
 728	case REGION_WORKING_IRAM:
 729		*sizeOut = SIZE_WORKING_IRAM;
 730		return gba->memory.iwram;
 731	case REGION_PALETTE_RAM:
 732		*sizeOut = SIZE_PALETTE_RAM;
 733		return gba->video.palette;
 734	case REGION_VRAM:
 735		*sizeOut = SIZE_VRAM;
 736		return gba->video.vram;
 737	case REGION_OAM:
 738		*sizeOut = SIZE_OAM;
 739		return gba->video.oam.raw;
 740	case REGION_CART0:
 741	case REGION_CART1:
 742	case REGION_CART2:
 743		*sizeOut = gba->memory.romSize;
 744		return gba->memory.rom;
 745	case REGION_CART_SRAM:
 746		if (gba->memory.savedata.type == SAVEDATA_FLASH1M) {
 747			*sizeOut = SIZE_CART_FLASH1M;
 748			return gba->memory.savedata.currentBank;
 749		}
 750		// Fall through
 751	case REGION_CART_SRAM_MIRROR:
 752		*sizeOut = GBASavedataSize(&gba->memory.savedata);
 753		return gba->memory.savedata.data;
 754	}
 755}
 756
 757#ifdef USE_DEBUGGERS
 758static bool _GBACoreSupportsDebuggerType(struct mCore* core, enum mDebuggerType type) {
 759	UNUSED(core);
 760	switch (type) {
 761	case DEBUGGER_CLI:
 762		return true;
 763#ifdef USE_GDB_STUB
 764	case DEBUGGER_GDB:
 765		return true;
 766#endif
 767	default:
 768		return false;
 769	}
 770}
 771
 772static struct mDebuggerPlatform* _GBACoreDebuggerPlatform(struct mCore* core) {
 773	struct GBACore* gbacore = (struct GBACore*) core;
 774	if (!gbacore->debuggerPlatform) {
 775		gbacore->debuggerPlatform = ARMDebuggerPlatformCreate();
 776	}
 777	return gbacore->debuggerPlatform;
 778}
 779
 780static struct CLIDebuggerSystem* _GBACoreCliDebuggerSystem(struct mCore* core) {
 781	return &GBACLIDebuggerCreate(core)->d;
 782}
 783
 784static void _GBACoreAttachDebugger(struct mCore* core, struct mDebugger* debugger) {
 785	if (core->debugger) {
 786		GBADetachDebugger(core->board);
 787	}
 788	GBAAttachDebugger(core->board, debugger);
 789	core->debugger = debugger;
 790}
 791
 792static void _GBACoreDetachDebugger(struct mCore* core) {
 793	GBADetachDebugger(core->board);
 794	core->debugger = NULL;
 795}
 796
 797static void _GBACoreLoadSymbols(struct mCore* core, struct VFile* vf) {
 798	bool closeAfter = false;
 799	core->symbolTable = mDebuggerSymbolTableCreate();
 800#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
 801#ifdef USE_ELF
 802	if (!vf) {
 803		closeAfter = true;
 804		vf = mDirectorySetOpenSuffix(&core->dirs, core->dirs.base, ".elf", O_RDONLY);
 805	}
 806#endif
 807	if (!vf) {
 808		closeAfter = true;
 809		vf = mDirectorySetOpenSuffix(&core->dirs, core->dirs.base, ".sym", O_RDONLY);
 810	}
 811#endif
 812	if (!vf) {
 813		return;
 814	}
 815#ifdef USE_ELF
 816	struct ELF* elf = ELFOpen(vf);
 817	if (elf) {
 818#ifdef USE_DEBUGGERS
 819		mCoreLoadELFSymbols(core->symbolTable, elf);
 820#endif
 821		ELFClose(elf);
 822	} else
 823#endif
 824	{
 825		mDebuggerLoadARMIPSSymbols(core->symbolTable, vf);
 826	}
 827	if (closeAfter) {
 828		vf->close(vf);
 829	}
 830}
 831
 832static bool _GBACoreLookupIdentifier(struct mCore* core, const char* name, int32_t* value, int* segment) {
 833	UNUSED(core);
 834	*segment = -1;
 835	int i;
 836	for (i = 0; i < REG_MAX; i += 2) {
 837		const char* reg = GBAIORegisterNames[i >> 1];
 838		if (reg && strcasecmp(reg, name) == 0) {
 839			*value = BASE_IO | i;
 840			return true;
 841		}
 842	}
 843	return false;
 844}
 845#endif
 846
 847static struct mCheatDevice* _GBACoreCheatDevice(struct mCore* core) {
 848	struct GBACore* gbacore = (struct GBACore*) core;
 849	if (!gbacore->cheatDevice) {
 850		gbacore->cheatDevice = GBACheatDeviceCreate();
 851		((struct ARMCore*) core->cpu)->components[CPU_COMPONENT_CHEAT_DEVICE] = &gbacore->cheatDevice->d;
 852		ARMHotplugAttach(core->cpu, CPU_COMPONENT_CHEAT_DEVICE);
 853		gbacore->cheatDevice->p = core;
 854	}
 855	return gbacore->cheatDevice;
 856}
 857
 858static size_t _GBACoreSavedataClone(struct mCore* core, void** sram) {
 859	struct GBA* gba = core->board;
 860	size_t size = GBASavedataSize(&gba->memory.savedata);
 861	if (!size) {
 862		*sram = NULL;
 863		return 0;
 864	}
 865	*sram = malloc(size);
 866	struct VFile* vf = VFileFromMemory(*sram, size);
 867	if (!vf) {
 868		free(*sram);
 869		*sram = NULL;
 870		return 0;
 871	}
 872	bool success = GBASavedataClone(&gba->memory.savedata, vf);
 873	vf->close(vf);
 874	if (!success) {
 875		free(*sram);
 876		*sram = NULL;
 877		return 0;
 878	}
 879	return size;
 880}
 881
 882static bool _GBACoreSavedataRestore(struct mCore* core, const void* sram, size_t size, bool writeback) {
 883	struct VFile* vf = VFileMemChunk(sram, size);
 884	if (!vf) {
 885		return false;
 886	}
 887	struct GBA* gba = core->board;
 888	bool success = true;
 889	if (writeback) {
 890		success = GBASavedataLoad(&gba->memory.savedata, vf);
 891		vf->close(vf);
 892	} else {
 893		GBASavedataMask(&gba->memory.savedata, vf, true);
 894	}
 895	return success;
 896}
 897
 898static size_t _GBACoreListVideoLayers(const struct mCore* core, const struct mCoreChannelInfo** info) {
 899	UNUSED(core);
 900	if (info) {
 901		*info = _GBAVideoLayers;
 902	}
 903	return sizeof(_GBAVideoLayers) / sizeof(*_GBAVideoLayers);
 904}
 905
 906static size_t _GBACoreListAudioChannels(const struct mCore* core, const struct mCoreChannelInfo** info) {
 907	UNUSED(core);
 908	if (info) {
 909		*info = _GBAAudioChannels;
 910	}
 911	return sizeof(_GBAAudioChannels) / sizeof(*_GBAAudioChannels);
 912}
 913
 914static void _GBACoreEnableVideoLayer(struct mCore* core, size_t id, bool enable) {
 915	struct GBA* gba = core->board;
 916	switch (id) {
 917	case 0:
 918	case 1:
 919	case 2:
 920	case 3:
 921		gba->video.renderer->disableBG[id] = !enable;
 922		break;
 923	case 4:
 924		gba->video.renderer->disableOBJ = !enable;
 925		break;
 926	default:
 927		break;
 928	}
 929}
 930
 931static void _GBACoreEnableAudioChannel(struct mCore* core, size_t id, bool enable) {
 932	struct GBA* gba = core->board;
 933	switch (id) {
 934	case 0:
 935	case 1:
 936	case 2:
 937	case 3:
 938		gba->audio.psg.forceDisableCh[id] = !enable;
 939		break;
 940	case 4:
 941		gba->audio.forceDisableChA = !enable;
 942		break;
 943	case 5:
 944		gba->audio.forceDisableChB = !enable;
 945		break;
 946	default:
 947		break;
 948	}
 949}
 950
 951static void _GBACoreAdjustVideoLayer(struct mCore* core, size_t id, int32_t x, int32_t y) {
 952	struct GBACore* gbacore = (struct GBACore*) core;
 953	switch (id) {
 954	case 0:
 955	case 1:
 956	case 2:
 957	case 3:
 958		gbacore->renderer.bg[id].offsetX = x;
 959		gbacore->renderer.bg[id].offsetY = y;
 960		break;
 961	case 4:
 962		gbacore->renderer.objOffsetX = x;
 963		gbacore->renderer.objOffsetY = y;
 964		gbacore->renderer.oamDirty = 1;
 965		break;
 966	default:
 967		return;
 968	}
 969	memset(gbacore->renderer.scanlineDirty, 0xFFFFFFFF, sizeof(gbacore->renderer.scanlineDirty));
 970}
 971
 972#ifndef MINIMAL_CORE
 973static void _GBACoreStartVideoLog(struct mCore* core, struct mVideoLogContext* context) {
 974	struct GBACore* gbacore = (struct GBACore*) core;
 975	struct GBA* gba = core->board;
 976	gbacore->logContext = context;
 977
 978	struct GBASerializedState* state = mVideoLogContextInitialState(context, NULL);
 979	state->id = 0;
 980	state->cpu.gprs[ARM_PC] = BASE_WORKING_RAM;
 981
 982	int channelId = mVideoLoggerAddChannel(context);
 983	gbacore->vlProxy.logger = malloc(sizeof(struct mVideoLogger));
 984	mVideoLoggerRendererCreate(gbacore->vlProxy.logger, false);
 985	mVideoLoggerAttachChannel(gbacore->vlProxy.logger, context, channelId);
 986	gbacore->vlProxy.logger->block = false;
 987
 988	GBAVideoProxyRendererCreate(&gbacore->vlProxy, gba->video.renderer);
 989	GBAVideoProxyRendererShim(&gba->video, &gbacore->vlProxy);
 990}
 991
 992static void _GBACoreEndVideoLog(struct mCore* core) {
 993	struct GBACore* gbacore = (struct GBACore*) core;
 994	struct GBA* gba = core->board;
 995	if (gbacore->vlProxy.logger) {
 996		GBAVideoProxyRendererUnshim(&gba->video, &gbacore->vlProxy);
 997		free(gbacore->vlProxy.logger);
 998		gbacore->vlProxy.logger = NULL;
 999	}
1000}
1001#endif
1002
1003struct mCore* GBACoreCreate(void) {
1004	struct GBACore* gbacore = malloc(sizeof(*gbacore));
1005	struct mCore* core = &gbacore->d;
1006	memset(&core->opts, 0, sizeof(core->opts));
1007	core->cpu = NULL;
1008	core->board = NULL;
1009	core->debugger = NULL;
1010	core->init = _GBACoreInit;
1011	core->deinit = _GBACoreDeinit;
1012	core->platform = _GBACorePlatform;
1013	core->supportsFeature = _GBACoreSupportsFeature;
1014	core->setSync = _GBACoreSetSync;
1015	core->loadConfig = _GBACoreLoadConfig;
1016	core->desiredVideoDimensions = _GBACoreDesiredVideoDimensions;
1017	core->setVideoBuffer = _GBACoreSetVideoBuffer;
1018	core->setVideoGLTex = _GBACoreSetVideoGLTex;
1019	core->getPixels = _GBACoreGetPixels;
1020	core->putPixels = _GBACorePutPixels;
1021	core->getAudioChannel = _GBACoreGetAudioChannel;
1022	core->setAudioBufferSize = _GBACoreSetAudioBufferSize;
1023	core->getAudioBufferSize = _GBACoreGetAudioBufferSize;
1024	core->addCoreCallbacks = _GBACoreAddCoreCallbacks;
1025	core->clearCoreCallbacks = _GBACoreClearCoreCallbacks;
1026	core->setAVStream = _GBACoreSetAVStream;
1027	core->isROM = GBAIsROM;
1028	core->loadROM = _GBACoreLoadROM;
1029	core->loadBIOS = _GBACoreLoadBIOS;
1030	core->loadSave = _GBACoreLoadSave;
1031	core->loadTemporarySave = _GBACoreLoadTemporarySave;
1032	core->loadPatch = _GBACoreLoadPatch;
1033	core->unloadROM = _GBACoreUnloadROM;
1034	core->checksum = _GBACoreChecksum;
1035	core->reset = _GBACoreReset;
1036	core->runFrame = _GBACoreRunFrame;
1037	core->runLoop = _GBACoreRunLoop;
1038	core->step = _GBACoreStep;
1039	core->stateSize = _GBACoreStateSize;
1040	core->loadState = _GBACoreLoadState;
1041	core->saveState = _GBACoreSaveState;
1042	core->setKeys = _GBACoreSetKeys;
1043	core->addKeys = _GBACoreAddKeys;
1044	core->clearKeys = _GBACoreClearKeys;
1045	core->frameCounter = _GBACoreFrameCounter;
1046	core->frameCycles = _GBACoreFrameCycles;
1047	core->frequency = _GBACoreFrequency;
1048	core->getGameTitle = _GBACoreGetGameTitle;
1049	core->getGameCode = _GBACoreGetGameCode;
1050	core->setPeripheral = _GBACoreSetPeripheral;
1051	core->busRead8 = _GBACoreBusRead8;
1052	core->busRead16 = _GBACoreBusRead16;
1053	core->busRead32 = _GBACoreBusRead32;
1054	core->busWrite8 = _GBACoreBusWrite8;
1055	core->busWrite16 = _GBACoreBusWrite16;
1056	core->busWrite32 = _GBACoreBusWrite32;
1057	core->rawRead8 = _GBACoreRawRead8;
1058	core->rawRead16 = _GBACoreRawRead16;
1059	core->rawRead32 = _GBACoreRawRead32;
1060	core->rawWrite8 = _GBACoreRawWrite8;
1061	core->rawWrite16 = _GBACoreRawWrite16;
1062	core->rawWrite32 = _GBACoreRawWrite32;
1063	core->listMemoryBlocks = _GBAListMemoryBlocks;
1064	core->getMemoryBlock = _GBAGetMemoryBlock;
1065#ifdef USE_DEBUGGERS
1066	core->supportsDebuggerType = _GBACoreSupportsDebuggerType;
1067	core->debuggerPlatform = _GBACoreDebuggerPlatform;
1068	core->cliDebuggerSystem = _GBACoreCliDebuggerSystem;
1069	core->attachDebugger = _GBACoreAttachDebugger;
1070	core->detachDebugger = _GBACoreDetachDebugger;
1071	core->loadSymbols = _GBACoreLoadSymbols;
1072	core->lookupIdentifier = _GBACoreLookupIdentifier;
1073#endif
1074	core->cheatDevice = _GBACoreCheatDevice;
1075	core->savedataClone = _GBACoreSavedataClone;
1076	core->savedataRestore = _GBACoreSavedataRestore;
1077	core->listVideoLayers = _GBACoreListVideoLayers;
1078	core->listAudioChannels = _GBACoreListAudioChannels;
1079	core->enableVideoLayer = _GBACoreEnableVideoLayer;
1080	core->enableAudioChannel = _GBACoreEnableAudioChannel;
1081	core->adjustVideoLayer = _GBACoreAdjustVideoLayer;
1082#ifndef MINIMAL_CORE
1083	core->startVideoLog = _GBACoreStartVideoLog;
1084	core->endVideoLog = _GBACoreEndVideoLog;
1085#endif
1086	return core;
1087}
1088
1089#ifndef MINIMAL_CORE
1090static void _GBAVLPStartFrameCallback(void *context) {
1091	struct mCore* core = context;
1092	struct GBACore* gbacore = (struct GBACore*) core;
1093	struct GBA* gba = core->board;
1094
1095	if (!mVideoLoggerRendererRun(gbacore->vlProxy.logger, true)) {
1096		GBAVideoProxyRendererUnshim(&gba->video, &gbacore->vlProxy);
1097		mVideoLogContextRewind(gbacore->logContext, core);
1098		GBAVideoProxyRendererShim(&gba->video, &gbacore->vlProxy);
1099		gba->earlyExit = true;
1100	}
1101}
1102
1103static bool _GBAVLPInit(struct mCore* core) {
1104	struct GBACore* gbacore = (struct GBACore*) core;
1105	if (!_GBACoreInit(core)) {
1106		return false;
1107	}
1108	gbacore->vlProxy.logger = malloc(sizeof(struct mVideoLogger));
1109	mVideoLoggerRendererCreate(gbacore->vlProxy.logger, true);
1110	GBAVideoProxyRendererCreate(&gbacore->vlProxy, NULL);
1111	memset(&gbacore->logCallbacks, 0, sizeof(gbacore->logCallbacks));
1112	gbacore->logCallbacks.videoFrameStarted = _GBAVLPStartFrameCallback;
1113	gbacore->logCallbacks.context = core;
1114	core->addCoreCallbacks(core, &gbacore->logCallbacks);
1115	core->videoLogger = gbacore->vlProxy.logger;
1116	return true;
1117}
1118
1119static void _GBAVLPDeinit(struct mCore* core) {
1120	struct GBACore* gbacore = (struct GBACore*) core;
1121	if (gbacore->logContext) {
1122		mVideoLogContextDestroy(core, gbacore->logContext);
1123	}
1124	_GBACoreDeinit(core);
1125}
1126
1127static void _GBAVLPReset(struct mCore* core) {
1128	struct GBACore* gbacore = (struct GBACore*) core;
1129	struct GBA* gba = (struct GBA*) core->board;
1130	if (gba->video.renderer == &gbacore->vlProxy.d) {
1131		GBAVideoProxyRendererUnshim(&gba->video, &gbacore->vlProxy);
1132	} else if (gbacore->renderer.outputBuffer) {
1133		struct GBAVideoRenderer* renderer = &gbacore->renderer.d;
1134		GBAVideoAssociateRenderer(&gba->video, renderer);
1135	}
1136
1137	ARMReset(core->cpu);
1138	mVideoLogContextRewind(gbacore->logContext, core);
1139	GBAVideoProxyRendererShim(&gba->video, &gbacore->vlProxy);
1140
1141	// Make sure CPU loop never spins
1142	GBAHalt(gba);
1143	gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IME, 0, NULL);
1144	gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IE, 0, NULL);
1145}
1146
1147static bool _GBAVLPLoadROM(struct mCore* core, struct VFile* vf) {
1148	struct GBACore* gbacore = (struct GBACore*) core;
1149	gbacore->logContext = mVideoLogContextCreate(NULL);
1150	if (!mVideoLogContextLoad(gbacore->logContext, vf)) {
1151		mVideoLogContextDestroy(core, gbacore->logContext);
1152		gbacore->logContext = NULL;
1153		return false;
1154	}
1155	mVideoLoggerAttachChannel(gbacore->vlProxy.logger, gbacore->logContext, 0);
1156	return true;
1157}
1158
1159static bool _GBAVLPLoadState(struct mCore* core, const void* state) {
1160	struct GBA* gba = (struct GBA*) core->board;
1161
1162	gba->timing.root = NULL;
1163	gba->cpu->gprs[ARM_PC] = BASE_WORKING_RAM;
1164	gba->cpu->memory.setActiveRegion(gba->cpu, gba->cpu->gprs[ARM_PC]);
1165
1166	// Make sure CPU loop never spins
1167	GBAHalt(gba);
1168	gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IME, 0, NULL);
1169	gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IE, 0, NULL);
1170	GBAVideoDeserialize(&gba->video, state);
1171	GBAIODeserialize(gba, state);
1172	GBAAudioReset(&gba->audio);
1173
1174	return true;
1175}
1176
1177static bool _returnTrue(struct VFile* vf) {
1178	UNUSED(vf);
1179	return true;
1180}
1181
1182struct mCore* GBAVideoLogPlayerCreate(void) {
1183	struct mCore* core = GBACoreCreate();
1184	core->init = _GBAVLPInit;
1185	core->deinit = _GBAVLPDeinit;
1186	core->reset = _GBAVLPReset;
1187	core->loadROM = _GBAVLPLoadROM;
1188	core->loadState = _GBAVLPLoadState;
1189	core->isROM = _returnTrue;
1190	return core;
1191}
1192#else
1193struct mCore* GBAVideoLogPlayerCreate(void) {
1194	return false;
1195}
1196#endif