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/gba/cheats.h>
12#include <mgba/internal/gba/gba.h>
13#include <mgba/internal/gba/extra/cli.h>
14#include <mgba/internal/gba/overrides.h>
15#ifndef DISABLE_THREADING
16#include <mgba/internal/gba/renderers/thread-proxy.h>
17#endif
18#include <mgba/internal/gba/renderers/video-software.h>
19#include <mgba/internal/gba/savedata.h>
20#include <mgba/internal/gba/serialize.h>
21#include <mgba-util/memory.h>
22#include <mgba-util/patch.h>
23#include <mgba-util/vfs.h>
24
25#ifndef MINIMAL_CORE
26#include <mgba/internal/gba/input.h>
27#endif
28
29struct GBACore {
30 struct mCore d;
31 struct GBAVideoSoftwareRenderer renderer;
32#ifndef DISABLE_THREADING
33 struct GBAVideoThreadProxyRenderer threadProxy;
34 int threadedVideo;
35#endif
36 int keys;
37 struct mCPUComponent* components[CPU_COMPONENT_MAX];
38 const struct Configuration* overrides;
39 struct mDebuggerPlatform* debuggerPlatform;
40 struct mCheatDevice* cheatDevice;
41};
42
43static bool _GBACoreInit(struct mCore* core) {
44 struct GBACore* gbacore = (struct GBACore*) core;
45
46 struct ARMCore* cpu = anonymousMemoryMap(sizeof(struct ARMCore));
47 struct GBA* gba = anonymousMemoryMap(sizeof(struct GBA));
48 if (!cpu || !gba) {
49 free(cpu);
50 free(gba);
51 return false;
52 }
53 core->cpu = cpu;
54 core->board = gba;
55 core->debugger = NULL;
56 gbacore->overrides = NULL;
57 gbacore->debuggerPlatform = NULL;
58 gbacore->cheatDevice = NULL;
59
60 GBACreate(gba);
61 // TODO: Restore cheats
62 memset(gbacore->components, 0, sizeof(gbacore->components));
63 ARMSetComponents(cpu, &gba->d, CPU_COMPONENT_MAX, gbacore->components);
64 ARMInit(cpu);
65 mRTCGenericSourceInit(&core->rtc, core);
66 gba->rtcSource = &core->rtc.d;
67
68 GBAVideoSoftwareRendererCreate(&gbacore->renderer);
69 gbacore->renderer.outputBuffer = NULL;
70
71#ifndef DISABLE_THREADING
72 gbacore->threadedVideo = false;
73 GBAVideoThreadProxyRendererCreate(&gbacore->threadProxy, &gbacore->renderer.d);
74#endif
75
76 gbacore->keys = 0;
77 gba->keySource = &gbacore->keys;
78
79#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
80 mDirectorySetInit(&core->dirs);
81#endif
82
83#ifndef MINIMAL_CORE
84 core->inputInfo = &GBAInputInfo;
85#endif
86
87 return true;
88}
89
90static void _GBACoreDeinit(struct mCore* core) {
91 ARMDeinit(core->cpu);
92 GBADestroy(core->board);
93 mappedMemoryFree(core->cpu, sizeof(struct ARMCore));
94 mappedMemoryFree(core->board, sizeof(struct GBA));
95#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
96 mDirectorySetDeinit(&core->dirs);
97#endif
98
99 struct GBACore* gbacore = (struct GBACore*) core;
100 free(gbacore->debuggerPlatform);
101 if (gbacore->cheatDevice) {
102 mCheatDeviceDestroy(gbacore->cheatDevice);
103 }
104 free(gbacore->cheatDevice);
105 mCoreConfigFreeOpts(&core->opts);
106 free(core);
107}
108
109static enum mPlatform _GBACorePlatform(const struct mCore* core) {
110 UNUSED(core);
111 return PLATFORM_GBA;
112}
113
114static void _GBACoreSetSync(struct mCore* core, struct mCoreSync* sync) {
115 struct GBA* gba = core->board;
116 gba->sync = sync;
117}
118
119static void _GBACoreLoadConfig(struct mCore* core, const struct mCoreConfig* config) {
120 struct GBA* gba = core->board;
121 if (core->opts.mute) {
122 gba->audio.masterVolume = 0;
123 } else {
124 gba->audio.masterVolume = core->opts.volume;
125 }
126 gba->video.frameskip = core->opts.frameskip;
127
128#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
129 struct GBACore* gbacore = (struct GBACore*) core;
130 gbacore->overrides = mCoreConfigGetOverridesConst(config);
131#endif
132
133 const char* idleOptimization = mCoreConfigGetValue(config, "idleOptimization");
134 if (idleOptimization) {
135 if (strcasecmp(idleOptimization, "ignore") == 0) {
136 gba->idleOptimization = IDLE_LOOP_IGNORE;
137 } else if (strcasecmp(idleOptimization, "remove") == 0) {
138 gba->idleOptimization = IDLE_LOOP_REMOVE;
139 } else if (strcasecmp(idleOptimization, "detect") == 0) {
140 if (gba->idleLoop == IDLE_LOOP_NONE) {
141 gba->idleOptimization = IDLE_LOOP_DETECT;
142 } else {
143 gba->idleOptimization = IDLE_LOOP_REMOVE;
144 }
145 }
146 }
147
148 mCoreConfigCopyValue(&core->config, config, "gba.bios");
149
150#ifndef DISABLE_THREADING
151 mCoreConfigGetIntValue(config, "threadedVideo", &gbacore->threadedVideo);
152#endif
153}
154
155static void _GBACoreDesiredVideoDimensions(struct mCore* core, unsigned* width, unsigned* height) {
156 UNUSED(core);
157 *width = VIDEO_HORIZONTAL_PIXELS;
158 *height = VIDEO_VERTICAL_PIXELS;
159}
160
161static void _GBACoreSetVideoBuffer(struct mCore* core, color_t* buffer, size_t stride) {
162 struct GBACore* gbacore = (struct GBACore*) core;
163 gbacore->renderer.outputBuffer = buffer;
164 gbacore->renderer.outputBufferStride = stride;
165}
166
167static void _GBACoreGetPixels(struct mCore* core, const void** buffer, size_t* stride) {
168 struct GBACore* gbacore = (struct GBACore*) core;
169 gbacore->renderer.d.getPixels(&gbacore->renderer.d, stride, buffer);
170}
171
172static void _GBACorePutPixels(struct mCore* core, const void* buffer, size_t stride) {
173 struct GBACore* gbacore = (struct GBACore*) core;
174 gbacore->renderer.d.putPixels(&gbacore->renderer.d, stride, buffer);
175}
176
177static struct blip_t* _GBACoreGetAudioChannel(struct mCore* core, int ch) {
178 struct GBA* gba = core->board;
179 switch (ch) {
180 case 0:
181 return gba->audio.psg.left;
182 case 1:
183 return gba->audio.psg.right;
184 default:
185 return NULL;
186 }
187}
188
189static void _GBACoreSetAudioBufferSize(struct mCore* core, size_t samples) {
190 struct GBA* gba = core->board;
191 GBAAudioResizeBuffer(&gba->audio, samples);
192}
193
194static size_t _GBACoreGetAudioBufferSize(struct mCore* core) {
195 struct GBA* gba = core->board;
196 return gba->audio.samples;
197}
198
199static void _GBACoreAddCoreCallbacks(struct mCore* core, struct mCoreCallbacks* coreCallbacks) {
200 struct GBA* gba = core->board;
201 *mCoreCallbacksListAppend(&gba->coreCallbacks) = *coreCallbacks;
202}
203
204static void _GBACoreClearCoreCallbacks(struct mCore* core) {
205 struct GBA* gba = core->board;
206 mCoreCallbacksListClear(&gba->coreCallbacks);
207}
208
209static void _GBACoreSetAVStream(struct mCore* core, struct mAVStream* stream) {
210 struct GBA* gba = core->board;
211 gba->stream = stream;
212 if (stream && stream->videoDimensionsChanged) {
213 stream->videoDimensionsChanged(stream, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
214 }
215}
216
217static bool _GBACoreLoadROM(struct mCore* core, struct VFile* vf) {
218 if (GBAIsMB(vf)) {
219 return GBALoadMB(core->board, vf);
220 }
221 return GBALoadROM(core->board, vf);
222}
223
224static bool _GBACoreLoadBIOS(struct mCore* core, struct VFile* vf, int type) {
225 UNUSED(type);
226 if (!GBAIsBIOS(vf)) {
227 return false;
228 }
229 GBALoadBIOS(core->board, vf);
230 return true;
231}
232
233static bool _GBACoreLoadSave(struct mCore* core, struct VFile* vf) {
234 return GBALoadSave(core->board, vf);
235}
236
237static bool _GBACoreLoadTemporarySave(struct mCore* core, struct VFile* vf) {
238 struct GBA* gba = core->board;
239 GBASavedataMask(&gba->memory.savedata, vf, false);
240 return true; // TODO: Return a real value
241}
242
243static bool _GBACoreLoadPatch(struct mCore* core, struct VFile* vf) {
244 if (!vf) {
245 return false;
246 }
247 struct Patch patch;
248 if (!loadPatch(vf, &patch)) {
249 return false;
250 }
251 GBAApplyPatch(core->board, &patch);
252 return true;
253}
254
255static void _GBACoreUnloadROM(struct mCore* core) {
256 struct GBACore* gbacore = (struct GBACore*) core;
257 struct ARMCore* cpu = core->cpu;
258 if (gbacore->cheatDevice) {
259 ARMHotplugDetach(cpu, CPU_COMPONENT_CHEAT_DEVICE);
260 cpu->components[CPU_COMPONENT_CHEAT_DEVICE] = NULL;
261 mCheatDeviceDestroy(gbacore->cheatDevice);
262 gbacore->cheatDevice = NULL;
263 }
264 return GBAUnloadROM(core->board);
265}
266
267static void _GBACoreChecksum(const struct mCore* core, void* data, enum mCoreChecksumType type) {
268 struct GBA* gba = (struct GBA*) core->board;
269 switch (type) {
270 case CHECKSUM_CRC32:
271 memcpy(data, &gba->romCrc32, sizeof(gba->romCrc32));
272 break;
273 }
274 return;
275}
276
277static void _GBACoreReset(struct mCore* core) {
278 struct GBACore* gbacore = (struct GBACore*) core;
279 struct GBA* gba = (struct GBA*) core->board;
280 if (gbacore->renderer.outputBuffer) {
281 struct GBAVideoRenderer* renderer = &gbacore->renderer.d;
282#ifndef DISABLE_THREADING
283 if (gbacore->threadedVideo) {
284 renderer = &gbacore->threadProxy.d;
285 }
286#endif
287 GBAVideoAssociateRenderer(&gba->video, renderer);
288 }
289
290 struct GBACartridgeOverride override;
291 const struct GBACartridge* cart = (const struct GBACartridge*) gba->memory.rom;
292 if (cart) {
293 memcpy(override.id, &cart->id, sizeof(override.id));
294 if (GBAOverrideFind(gbacore->overrides, &override)) {
295 GBAOverrideApply(gba, &override);
296 }
297 }
298
299#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
300 if (!gba->biosVf && core->opts.useBios) {
301 struct VFile* bios = NULL;
302 bool found = false;
303 if (core->opts.bios) {
304 bios = VFileOpen(core->opts.bios, O_RDONLY);
305 if (bios && GBAIsBIOS(bios)) {
306 found = true;
307 } else if (bios) {
308 bios->close(bios);
309 bios = NULL;
310 }
311 }
312 if (!found) {
313 const char* configPath = mCoreConfigGetValue(&core->config, "gba.bios");
314 if (configPath) {
315 bios = VFileOpen(configPath, O_RDONLY);
316 }
317 if (bios && GBAIsBIOS(bios)) {
318 found = true;
319 } else if (bios) {
320 bios->close(bios);
321 bios = NULL;
322 }
323 }
324 if (!found) {
325 char path[PATH_MAX];
326 mCoreConfigDirectory(path, PATH_MAX);
327 strncat(path, PATH_SEP "gba_bios.bin", PATH_MAX - strlen(path));
328 bios = VFileOpen(path, O_RDONLY);
329 if (bios && GBIsBIOS(bios)) {
330 found = true;
331 } else if (bios) {
332 bios->close(bios);
333 bios = NULL;
334 }
335 }
336 if (bios) {
337 GBALoadBIOS(gba, bios);
338 }
339 }
340#endif
341
342 ARMReset(core->cpu);
343 if (core->opts.skipBios && gba->isPristine) {
344 GBASkipBIOS(core->board);
345 }
346}
347
348static void _GBACoreRunFrame(struct mCore* core) {
349 struct GBA* gba = core->board;
350 int32_t frameCounter = gba->video.frameCounter;
351 while (gba->video.frameCounter == frameCounter) {
352 ARMv4RunLoop(core->cpu);
353 }
354}
355
356static void _GBACoreRunLoop(struct mCore* core) {
357 ARMv4RunLoop(core->cpu);
358}
359
360static void _GBACoreStep(struct mCore* core) {
361 ARMv4Run(core->cpu);
362}
363
364static size_t _GBACoreStateSize(struct mCore* core) {
365 UNUSED(core);
366 return sizeof(struct GBASerializedState);
367}
368
369static bool _GBACoreLoadState(struct mCore* core, const void* state) {
370 return GBADeserialize(core->board, state);
371}
372
373static bool _GBACoreSaveState(struct mCore* core, void* state) {
374 GBASerialize(core->board, state);
375 return true;
376}
377
378static void _GBACoreSetKeys(struct mCore* core, uint32_t keys) {
379 struct GBACore* gbacore = (struct GBACore*) core;
380 gbacore->keys = keys;
381}
382
383static void _GBACoreAddKeys(struct mCore* core, uint32_t keys) {
384 struct GBACore* gbacore = (struct GBACore*) core;
385 gbacore->keys |= keys;
386}
387
388static void _GBACoreClearKeys(struct mCore* core, uint32_t keys) {
389 struct GBACore* gbacore = (struct GBACore*) core;
390 gbacore->keys &= ~keys;
391}
392
393static void _GBACoreSetCursorLocation(struct mCore* core, int x, int y) {
394 UNUSED(core);
395 UNUSED(x);
396 UNUSED(y);
397}
398
399static void _GBACoreSetCursorDown(struct mCore* core, bool down) {
400 UNUSED(core);
401 UNUSED(down);
402}
403
404static int32_t _GBACoreFrameCounter(const struct mCore* core) {
405 const struct GBA* gba = core->board;
406 return gba->video.frameCounter;
407}
408
409static int32_t _GBACoreFrameCycles(const struct mCore* core) {
410 UNUSED(core);
411 return VIDEO_TOTAL_LENGTH;
412}
413
414static int32_t _GBACoreFrequency(const struct mCore* core) {
415 UNUSED(core);
416 return GBA_ARM7TDMI_FREQUENCY;
417}
418
419static void _GBACoreGetGameTitle(const struct mCore* core, char* title) {
420 GBAGetGameTitle(core->board, title);
421}
422
423static void _GBACoreGetGameCode(const struct mCore* core, char* title) {
424 GBAGetGameCode(core->board, title);
425}
426
427static void _GBACoreSetRotation(struct mCore* core, struct mRotationSource* rotation) {
428 struct GBA* gba = core->board;
429 gba->rotationSource = rotation;
430}
431
432static void _GBACoreSetRumble(struct mCore* core, struct mRumble* rumble) {
433 struct GBA* gba = core->board;
434 gba->rumble = rumble;
435}
436
437static uint32_t _GBACoreBusRead8(struct mCore* core, uint32_t address) {
438 struct ARMCore* cpu = core->cpu;
439 return cpu->memory.load8(cpu, address, 0);
440}
441
442static uint32_t _GBACoreBusRead16(struct mCore* core, uint32_t address) {
443 struct ARMCore* cpu = core->cpu;
444 return cpu->memory.load16(cpu, address, 0);
445
446}
447
448static uint32_t _GBACoreBusRead32(struct mCore* core, uint32_t address) {
449 struct ARMCore* cpu = core->cpu;
450 return cpu->memory.load32(cpu, address, 0);
451}
452
453static void _GBACoreBusWrite8(struct mCore* core, uint32_t address, uint8_t value) {
454 struct ARMCore* cpu = core->cpu;
455 cpu->memory.store8(cpu, address, value, 0);
456}
457
458static void _GBACoreBusWrite16(struct mCore* core, uint32_t address, uint16_t value) {
459 struct ARMCore* cpu = core->cpu;
460 cpu->memory.store16(cpu, address, value, 0);
461}
462
463static void _GBACoreBusWrite32(struct mCore* core, uint32_t address, uint32_t value) {
464 struct ARMCore* cpu = core->cpu;
465 cpu->memory.store32(cpu, address, value, 0);
466}
467
468static uint32_t _GBACoreRawRead8(struct mCore* core, uint32_t address, int segment) {
469 UNUSED(segment);
470 struct ARMCore* cpu = core->cpu;
471 return GBAView8(cpu, address);
472}
473
474static uint32_t _GBACoreRawRead16(struct mCore* core, uint32_t address, int segment) {
475 UNUSED(segment);
476 struct ARMCore* cpu = core->cpu;
477 return GBAView16(cpu, address);
478}
479
480static uint32_t _GBACoreRawRead32(struct mCore* core, uint32_t address, int segment) {
481 UNUSED(segment);
482 struct ARMCore* cpu = core->cpu;
483 return GBAView32(cpu, address);
484}
485
486static void _GBACoreRawWrite8(struct mCore* core, uint32_t address, int segment, uint8_t value) {
487 UNUSED(segment);
488 struct ARMCore* cpu = core->cpu;
489 GBAPatch8(cpu, address, value, NULL);
490}
491
492static void _GBACoreRawWrite16(struct mCore* core, uint32_t address, int segment, uint16_t value) {
493 UNUSED(segment);
494 struct ARMCore* cpu = core->cpu;
495 GBAPatch16(cpu, address, value, NULL);
496}
497
498static void _GBACoreRawWrite32(struct mCore* core, uint32_t address, int segment, uint32_t value) {
499 UNUSED(segment);
500 struct ARMCore* cpu = core->cpu;
501 GBAPatch32(cpu, address, value, NULL);
502}
503
504#ifdef USE_DEBUGGERS
505static bool _GBACoreSupportsDebuggerType(struct mCore* core, enum mDebuggerType type) {
506 UNUSED(core);
507 switch (type) {
508 case DEBUGGER_CLI:
509 return true;
510#ifdef USE_GDB_STUB
511 case DEBUGGER_GDB:
512 return true;
513#endif
514 default:
515 return false;
516 }
517}
518
519static struct mDebuggerPlatform* _GBACoreDebuggerPlatform(struct mCore* core) {
520 struct GBACore* gbacore = (struct GBACore*) core;
521 if (!gbacore->debuggerPlatform) {
522 gbacore->debuggerPlatform = ARMDebuggerPlatformCreate();
523 }
524 return gbacore->debuggerPlatform;
525}
526
527static struct CLIDebuggerSystem* _GBACoreCliDebuggerSystem(struct mCore* core) {
528 return &GBACLIDebuggerCreate(core)->d;
529}
530
531static void _GBACoreAttachDebugger(struct mCore* core, struct mDebugger* debugger) {
532 if (core->debugger) {
533 GBADetachDebugger(core->board);
534 }
535 GBAAttachDebugger(core->board, debugger);
536 core->debugger = debugger;
537}
538
539static void _GBACoreDetachDebugger(struct mCore* core) {
540 GBADetachDebugger(core->board);
541 core->debugger = NULL;
542}
543#endif
544
545static struct mCheatDevice* _GBACoreCheatDevice(struct mCore* core) {
546 struct GBACore* gbacore = (struct GBACore*) core;
547 if (!gbacore->cheatDevice) {
548 gbacore->cheatDevice = GBACheatDeviceCreate();
549 ((struct ARMCore*) core->cpu)->components[CPU_COMPONENT_CHEAT_DEVICE] = &gbacore->cheatDevice->d;
550 ARMHotplugAttach(core->cpu, CPU_COMPONENT_CHEAT_DEVICE);
551 gbacore->cheatDevice->p = core;
552 }
553 return gbacore->cheatDevice;
554}
555
556static size_t _GBACoreSavedataClone(struct mCore* core, void** sram) {
557 struct GBA* gba = core->board;
558 size_t size = GBASavedataSize(&gba->memory.savedata);
559 if (!size) {
560 *sram = NULL;
561 return 0;
562 }
563 *sram = malloc(size);
564 struct VFile* vf = VFileFromMemory(*sram, size);
565 if (!vf) {
566 free(*sram);
567 *sram = NULL;
568 return 0;
569 }
570 bool success = GBASavedataClone(&gba->memory.savedata, vf);
571 vf->close(vf);
572 if (!success) {
573 free(*sram);
574 *sram = NULL;
575 return 0;
576 }
577 return size;
578}
579
580static bool _GBACoreSavedataRestore(struct mCore* core, const void* sram, size_t size, bool writeback) {
581 struct VFile* vf = VFileMemChunk(sram, size);
582 if (!vf) {
583 return false;
584 }
585 struct GBA* gba = core->board;
586 bool success = true;
587 if (writeback) {
588 success = GBASavedataLoad(&gba->memory.savedata, vf);
589 vf->close(vf);
590 } else {
591 GBASavedataMask(&gba->memory.savedata, vf, true);
592 }
593 return success;
594}
595
596struct mCore* GBACoreCreate(void) {
597 struct GBACore* gbacore = malloc(sizeof(*gbacore));
598 struct mCore* core = &gbacore->d;
599 memset(&core->opts, 0, sizeof(core->opts));
600 core->cpu = NULL;
601 core->board = NULL;
602 core->debugger = NULL;
603 core->init = _GBACoreInit;
604 core->deinit = _GBACoreDeinit;
605 core->platform = _GBACorePlatform;
606 core->setSync = _GBACoreSetSync;
607 core->loadConfig = _GBACoreLoadConfig;
608 core->desiredVideoDimensions = _GBACoreDesiredVideoDimensions;
609 core->setVideoBuffer = _GBACoreSetVideoBuffer;
610 core->getPixels = _GBACoreGetPixels;
611 core->putPixels = _GBACorePutPixels;
612 core->getAudioChannel = _GBACoreGetAudioChannel;
613 core->setAudioBufferSize = _GBACoreSetAudioBufferSize;
614 core->getAudioBufferSize = _GBACoreGetAudioBufferSize;
615 core->addCoreCallbacks = _GBACoreAddCoreCallbacks;
616 core->clearCoreCallbacks = _GBACoreClearCoreCallbacks;
617 core->setAVStream = _GBACoreSetAVStream;
618 core->isROM = GBAIsROM;
619 core->loadROM = _GBACoreLoadROM;
620 core->loadBIOS = _GBACoreLoadBIOS;
621 core->loadSave = _GBACoreLoadSave;
622 core->loadTemporarySave = _GBACoreLoadTemporarySave;
623 core->loadPatch = _GBACoreLoadPatch;
624 core->unloadROM = _GBACoreUnloadROM;
625 core->checksum = _GBACoreChecksum;
626 core->reset = _GBACoreReset;
627 core->runFrame = _GBACoreRunFrame;
628 core->runLoop = _GBACoreRunLoop;
629 core->step = _GBACoreStep;
630 core->stateSize = _GBACoreStateSize;
631 core->loadState = _GBACoreLoadState;
632 core->saveState = _GBACoreSaveState;
633 core->setKeys = _GBACoreSetKeys;
634 core->addKeys = _GBACoreAddKeys;
635 core->clearKeys = _GBACoreClearKeys;
636 core->setCursorLocation = _GBACoreSetCursorLocation;
637 core->setCursorDown = _GBACoreSetCursorDown;
638 core->frameCounter = _GBACoreFrameCounter;
639 core->frameCycles = _GBACoreFrameCycles;
640 core->frequency = _GBACoreFrequency;
641 core->getGameTitle = _GBACoreGetGameTitle;
642 core->getGameCode = _GBACoreGetGameCode;
643 core->setRotation = _GBACoreSetRotation;
644 core->setRumble = _GBACoreSetRumble;
645 core->busRead8 = _GBACoreBusRead8;
646 core->busRead16 = _GBACoreBusRead16;
647 core->busRead32 = _GBACoreBusRead32;
648 core->busWrite8 = _GBACoreBusWrite8;
649 core->busWrite16 = _GBACoreBusWrite16;
650 core->busWrite32 = _GBACoreBusWrite32;
651 core->rawRead8 = _GBACoreRawRead8;
652 core->rawRead16 = _GBACoreRawRead16;
653 core->rawRead32 = _GBACoreRawRead32;
654 core->rawWrite8 = _GBACoreRawWrite8;
655 core->rawWrite16 = _GBACoreRawWrite16;
656 core->rawWrite32 = _GBACoreRawWrite32;
657#ifdef USE_DEBUGGERS
658 core->supportsDebuggerType = _GBACoreSupportsDebuggerType;
659 core->debuggerPlatform = _GBACoreDebuggerPlatform;
660 core->cliDebuggerSystem = _GBACoreCliDebuggerSystem;
661 core->attachDebugger = _GBACoreAttachDebugger;
662 core->detachDebugger = _GBACoreDetachDebugger;
663#endif
664 core->cheatDevice = _GBACoreCheatDevice;
665 core->savedataClone = _GBACoreSavedataClone;
666 core->savedataRestore = _GBACoreSavedataRestore;
667 return core;
668}