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