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