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 "core.h"
7
8#include "core/core.h"
9#include "core/log.h"
10#include "gba/gba.h"
11#include "gba/context/overrides.h"
12#include "gba/renderers/video-software.h"
13#include "gba/serialize.h"
14#include "util/memory.h"
15#include "util/patch.h"
16#include "util/vfs.h"
17
18struct GBACore {
19 struct mCore d;
20 struct GBAVideoSoftwareRenderer renderer;
21 int keys;
22 struct mCPUComponent* components[GBA_COMPONENT_MAX];
23 const struct Configuration* overrides;
24};
25
26static bool _GBACoreInit(struct mCore* core) {
27 struct GBACore* gbacore = (struct GBACore*) core;
28
29 struct ARMCore* cpu = anonymousMemoryMap(sizeof(struct ARMCore));
30 struct GBA* gba = anonymousMemoryMap(sizeof(struct GBA));
31 if (!cpu || !gba) {
32 free(cpu);
33 free(gba);
34 return false;
35 }
36 core->cpu = cpu;
37 core->board = gba;
38 gbacore->overrides = 0;
39
40 GBACreate(gba);
41 // TODO: Restore debugger and cheats
42 memset(gbacore->components, 0, sizeof(gbacore->components));
43 ARMSetComponents(cpu, &gba->d, GBA_COMPONENT_MAX, gbacore->components);
44 ARMInit(cpu);
45
46 GBAVideoSoftwareRendererCreate(&gbacore->renderer);
47 gbacore->renderer.outputBuffer = NULL;
48
49 gbacore->keys = 0;
50 gba->keySource = &gbacore->keys;
51
52#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
53 mDirectorySetInit(&core->dirs);
54#endif
55
56 return true;
57}
58
59static void _GBACoreDeinit(struct mCore* core) {
60 ARMDeinit(core->cpu);
61 GBADestroy(core->board);
62 mappedMemoryFree(core->cpu, sizeof(struct ARMCore));
63 mappedMemoryFree(core->board, sizeof(struct GBA));
64#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
65 mDirectorySetDeinit(&core->dirs);
66#endif
67 free(core);
68}
69
70static enum mPlatform _GBACorePlatform(struct mCore* core) {
71 UNUSED(core);
72 return PLATFORM_GBA;
73}
74
75static void _GBACoreSetSync(struct mCore* core, struct mCoreSync* sync) {
76 struct GBA* gba = core->board;
77 gba->sync = sync;
78}
79
80static void _GBACoreLoadConfig(struct mCore* core, const struct mCoreConfig* config) {
81 struct GBA* gba = core->board;
82 if (core->opts.mute) {
83 gba->audio.masterVolume = 0;
84 } else {
85 gba->audio.masterVolume = core->opts.volume;
86 }
87 gba->video.frameskip = core->opts.frameskip;
88
89#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
90 struct GBACore* gbacore = (struct GBACore*) core;
91 gbacore->overrides = mCoreConfigGetOverridesConst(config);
92
93 struct VFile* bios = 0;
94 if (core->opts.useBios && core->opts.bios) {
95 bios = VFileOpen(core->opts.bios, O_RDONLY);
96 }
97 if (bios) {
98 GBALoadBIOS(gba, bios);
99 }
100#endif
101
102 const char* idleOptimization = mCoreConfigGetValue(config, "idleOptimization");
103 if (idleOptimization) {
104 if (strcasecmp(idleOptimization, "ignore") == 0) {
105 gba->idleOptimization = IDLE_LOOP_IGNORE;
106 } else if (strcasecmp(idleOptimization, "remove") == 0) {
107 gba->idleOptimization = IDLE_LOOP_REMOVE;
108 } else if (strcasecmp(idleOptimization, "detect") == 0) {
109 gba->idleOptimization = IDLE_LOOP_DETECT;
110 }
111 }
112}
113
114static void _GBACoreDesiredVideoDimensions(struct mCore* core, unsigned* width, unsigned* height) {
115 UNUSED(core);
116 *width = VIDEO_HORIZONTAL_PIXELS;
117 *height = VIDEO_VERTICAL_PIXELS;
118}
119
120static void _GBACoreSetVideoBuffer(struct mCore* core, color_t* buffer, size_t stride) {
121 struct GBACore* gbacore = (struct GBACore*) core;
122 gbacore->renderer.outputBuffer = buffer;
123 gbacore->renderer.outputBufferStride = stride;
124}
125
126static void _GBACoreGetVideoBuffer(struct mCore* core, color_t** buffer, size_t* stride) {
127 struct GBACore* gbacore = (struct GBACore*) core;
128 *buffer = gbacore->renderer.outputBuffer;
129 *stride = gbacore->renderer.outputBufferStride;
130}
131
132static struct blip_t* _GBACoreGetAudioChannel(struct mCore* core, int ch) {
133 struct GBA* gba = core->board;
134 switch (ch) {
135 case 0:
136 return gba->audio.psg.left;
137 case 1:
138 return gba->audio.psg.right;
139 default:
140 return NULL;
141 }
142}
143
144static void _GBACoreSetAudioBufferSize(struct mCore* core, size_t samples) {
145 struct GBA* gba = core->board;
146 GBAAudioResizeBuffer(&gba->audio, samples);
147}
148
149static size_t _GBACoreGetAudioBufferSize(struct mCore* core) {
150 struct GBA* gba = core->board;
151 return gba->audio.samples;
152}
153
154static void _GBACoreSetAVStream(struct mCore* core, struct mAVStream* stream) {
155 struct GBA* gba = core->board;
156 gba->stream = stream;
157 if (stream && stream->videoDimensionsChanged) {
158 stream->videoDimensionsChanged(stream, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
159 }
160}
161
162static bool _GBACoreLoadROM(struct mCore* core, struct VFile* vf) {
163 return GBALoadROM(core->board, vf);
164}
165
166static bool _GBACoreLoadBIOS(struct mCore* core, struct VFile* vf, int type) {
167 UNUSED(type);
168 if (!GBAIsBIOS(vf)) {
169 return false;
170 }
171 GBALoadBIOS(core->board, vf);
172 return true;
173}
174
175static bool _GBACoreLoadSave(struct mCore* core, struct VFile* vf) {
176 return GBALoadSave(core->board, vf);
177}
178
179static bool _GBACoreLoadPatch(struct mCore* core, struct VFile* vf) {
180 if (!vf) {
181 return false;
182 }
183 struct Patch patch;
184 if (!loadPatch(vf, &patch)) {
185 return false;
186 }
187 GBAApplyPatch(core->board, &patch);
188 return true;
189}
190
191static void _GBACoreUnloadROM(struct mCore* core) {
192 return GBAUnloadROM(core->board);
193}
194
195static void _GBACoreReset(struct mCore* core) {
196 struct GBACore* gbacore = (struct GBACore*) core;
197 struct GBA* gba = (struct GBA*) core->board;
198 if (gbacore->renderer.outputBuffer) {
199 GBAVideoAssociateRenderer(&gba->video, &gbacore->renderer.d);
200 }
201 ARMReset(core->cpu);
202 if (core->opts.skipBios) {
203 GBASkipBIOS(core->board);
204 }
205
206 struct GBACartridgeOverride override;
207 const struct GBACartridge* cart = (const struct GBACartridge*) gba->memory.rom;
208 memcpy(override.id, &cart->id, sizeof(override.id));
209 if (GBAOverrideFind(gbacore->overrides, &override)) {
210 GBAOverrideApply(gba, &override);
211 }
212}
213
214static void _GBACoreRunFrame(struct mCore* core) {
215 struct GBA* gba = core->board;
216 int32_t frameCounter = gba->video.frameCounter;
217 while (gba->video.frameCounter == frameCounter) {
218 ARMRunLoop(core->cpu);
219 }
220}
221
222static void _GBACoreRunLoop(struct mCore* core) {
223 ARMRunLoop(core->cpu);
224}
225
226static void _GBACoreStep(struct mCore* core) {
227 ARMRun(core->cpu);
228}
229
230static bool _GBACoreLoadState(struct mCore* core, struct VFile* vf, int flags) {
231 return GBALoadStateNamed(core->board, vf, flags);
232}
233
234static bool _GBACoreSaveState(struct mCore* core, struct VFile* vf, int flags) {
235 return GBASaveStateNamed(core->board, vf, flags);
236}
237
238static void _GBACoreSetKeys(struct mCore* core, uint32_t keys) {
239 struct GBACore* gbacore = (struct GBACore*) core;
240 gbacore->keys = keys;
241}
242
243static void _GBACoreAddKeys(struct mCore* core, uint32_t keys) {
244 struct GBACore* gbacore = (struct GBACore*) core;
245 gbacore->keys |= keys;
246}
247
248static void _GBACoreClearKeys(struct mCore* core, uint32_t keys) {
249 struct GBACore* gbacore = (struct GBACore*) core;
250 gbacore->keys &= ~keys;
251}
252
253static int32_t _GBACoreFrameCounter(struct mCore* core) {
254 struct GBA* gba = core->board;
255 return gba->video.frameCounter;
256}
257
258static int32_t _GBACoreFrameCycles(struct mCore* core) {
259 UNUSED(core);
260 return VIDEO_TOTAL_LENGTH;
261}
262
263static int32_t _GBACoreFrequency(struct mCore* core) {
264 UNUSED(core);
265 return GBA_ARM7TDMI_FREQUENCY;
266}
267
268static void _GBACoreGetGameTitle(struct mCore* core, char* title) {
269 GBAGetGameTitle(core->board, title);
270}
271
272static void _GBACoreGetGameCode(struct mCore* core, char* title) {
273 GBAGetGameCode(core->board, title);
274}
275
276static void _GBACoreSetRTC(struct mCore* core, struct mRTCSource* rtc) {
277 struct GBA* gba = core->board;
278 gba->rtcSource = rtc;
279}
280
281static void _GBACoreSetRotation(struct mCore* core, struct mRotationSource* rotation) {
282 struct GBA* gba = core->board;
283 gba->rotationSource = rotation;
284}
285
286static void _GBACoreSetRumble(struct mCore* core, struct mRumble* rumble) {
287 struct GBA* gba = core->board;
288 gba->rumble = rumble;
289}
290
291struct mCore* GBACoreCreate(void) {
292 struct GBACore* gbacore = malloc(sizeof(*gbacore));
293 struct mCore* core = &gbacore->d;
294 memset(&core->opts, 0, sizeof(core->opts));
295 core->cpu = 0;
296 core->board = 0;
297 core->init = _GBACoreInit;
298 core->deinit = _GBACoreDeinit;
299 core->platform = _GBACorePlatform;
300 core->setSync = _GBACoreSetSync;
301 core->loadConfig = _GBACoreLoadConfig;
302 core->desiredVideoDimensions = _GBACoreDesiredVideoDimensions;
303 core->setVideoBuffer = _GBACoreSetVideoBuffer;
304 core->getVideoBuffer = _GBACoreGetVideoBuffer;
305 core->getAudioChannel = _GBACoreGetAudioChannel;
306 core->setAudioBufferSize = _GBACoreSetAudioBufferSize;
307 core->getAudioBufferSize = _GBACoreGetAudioBufferSize;
308 core->setAVStream = _GBACoreSetAVStream;
309 core->isROM = GBAIsROM;
310 core->loadROM = _GBACoreLoadROM;
311 core->loadBIOS = _GBACoreLoadBIOS;
312 core->loadSave = _GBACoreLoadSave;
313 core->loadPatch = _GBACoreLoadPatch;
314 core->unloadROM = _GBACoreUnloadROM;
315 core->reset = _GBACoreReset;
316 core->runFrame = _GBACoreRunFrame;
317 core->runLoop = _GBACoreRunLoop;
318 core->step = _GBACoreStep;
319 core->loadState = _GBACoreLoadState;
320 core->saveState = _GBACoreSaveState;
321 core->setKeys = _GBACoreSetKeys;
322 core->addKeys = _GBACoreAddKeys;
323 core->clearKeys = _GBACoreClearKeys;
324 core->frameCounter = _GBACoreFrameCounter;
325 core->frameCycles = _GBACoreFrameCycles;
326 core->frequency = _GBACoreFrequency;
327 core->getGameTitle = _GBACoreGetGameTitle;
328 core->getGameCode = _GBACoreGetGameCode;
329 core->setRTC = _GBACoreSetRTC;
330 core->setRotation = _GBACoreSetRotation;
331 core->setRumble = _GBACoreSetRumble;
332 return core;
333}