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