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 bios = VFileOpen(configPath, O_RDONLY);
307 if (bios && GBAIsBIOS(bios)) {
308 found = true;
309 } else if (bios) {
310 bios->close(bios);
311 bios = NULL;
312 }
313 }
314 if (!found) {
315 char path[PATH_MAX];
316 mCoreConfigDirectory(path, PATH_MAX);
317 strncat(path, PATH_SEP "gba_bios.bin", PATH_MAX - strlen(path));
318 bios = VFileOpen(path, O_RDONLY);
319 if (bios && GBIsBIOS(bios)) {
320 found = true;
321 } else if (bios) {
322 bios->close(bios);
323 bios = NULL;
324 }
325 }
326 if (bios) {
327 GBALoadBIOS(gba, bios);
328 }
329 }
330#endif
331
332 ARMReset(core->cpu);
333 if (core->opts.skipBios && gba->isPristine) {
334 GBASkipBIOS(core->board);
335 }
336}
337
338static void _GBACoreRunFrame(struct mCore* core) {
339 struct GBA* gba = core->board;
340 int32_t frameCounter = gba->video.frameCounter;
341 while (gba->video.frameCounter == frameCounter) {
342 ARMRunLoop(core->cpu);
343 }
344}
345
346static void _GBACoreRunLoop(struct mCore* core) {
347 ARMRunLoop(core->cpu);
348}
349
350static void _GBACoreStep(struct mCore* core) {
351 ARMRun(core->cpu);
352}
353
354static size_t _GBACoreStateSize(struct mCore* core) {
355 UNUSED(core);
356 return sizeof(struct GBASerializedState);
357}
358
359static bool _GBACoreLoadState(struct mCore* core, const void* state) {
360 return GBADeserialize(core->board, state);
361}
362
363static bool _GBACoreSaveState(struct mCore* core, void* state) {
364 GBASerialize(core->board, state);
365 return true;
366}
367
368static void _GBACoreSetKeys(struct mCore* core, uint32_t keys) {
369 struct GBACore* gbacore = (struct GBACore*) core;
370 gbacore->keys = keys;
371}
372
373static void _GBACoreAddKeys(struct mCore* core, uint32_t keys) {
374 struct GBACore* gbacore = (struct GBACore*) core;
375 gbacore->keys |= keys;
376}
377
378static void _GBACoreClearKeys(struct mCore* core, uint32_t keys) {
379 struct GBACore* gbacore = (struct GBACore*) core;
380 gbacore->keys &= ~keys;
381}
382
383static int32_t _GBACoreFrameCounter(const struct mCore* core) {
384 const struct GBA* gba = core->board;
385 return gba->video.frameCounter;
386}
387
388static int32_t _GBACoreFrameCycles(const struct mCore* core) {
389 UNUSED(core);
390 return VIDEO_TOTAL_LENGTH;
391}
392
393static int32_t _GBACoreFrequency(const struct mCore* core) {
394 UNUSED(core);
395 return GBA_ARM7TDMI_FREQUENCY;
396}
397
398static void _GBACoreGetGameTitle(const struct mCore* core, char* title) {
399 GBAGetGameTitle(core->board, title);
400}
401
402static void _GBACoreGetGameCode(const struct mCore* core, char* title) {
403 GBAGetGameCode(core->board, title);
404}
405
406static void _GBACoreSetRotation(struct mCore* core, struct mRotationSource* rotation) {
407 struct GBA* gba = core->board;
408 gba->rotationSource = rotation;
409}
410
411static void _GBACoreSetRumble(struct mCore* core, struct mRumble* rumble) {
412 struct GBA* gba = core->board;
413 gba->rumble = rumble;
414}
415
416static uint32_t _GBACoreBusRead8(struct mCore* core, uint32_t address) {
417 struct ARMCore* cpu = core->cpu;
418 return cpu->memory.load8(cpu, address, 0);
419}
420
421static uint32_t _GBACoreBusRead16(struct mCore* core, uint32_t address) {
422 struct ARMCore* cpu = core->cpu;
423 return cpu->memory.load16(cpu, address, 0);
424
425}
426
427static uint32_t _GBACoreBusRead32(struct mCore* core, uint32_t address) {
428 struct ARMCore* cpu = core->cpu;
429 return cpu->memory.load32(cpu, address, 0);
430}
431
432static void _GBACoreBusWrite8(struct mCore* core, uint32_t address, uint8_t value) {
433 struct ARMCore* cpu = core->cpu;
434 cpu->memory.store8(cpu, address, value, 0);
435}
436
437static void _GBACoreBusWrite16(struct mCore* core, uint32_t address, uint16_t value) {
438 struct ARMCore* cpu = core->cpu;
439 cpu->memory.store16(cpu, address, value, 0);
440}
441
442static void _GBACoreBusWrite32(struct mCore* core, uint32_t address, uint32_t value) {
443 struct ARMCore* cpu = core->cpu;
444 cpu->memory.store32(cpu, address, value, 0);
445}
446
447static uint32_t _GBACoreRawRead8(struct mCore* core, uint32_t address, int segment) {
448 UNUSED(segment);
449 struct ARMCore* cpu = core->cpu;
450 return GBAView8(cpu, address);
451}
452
453static uint32_t _GBACoreRawRead16(struct mCore* core, uint32_t address, int segment) {
454 UNUSED(segment);
455 struct ARMCore* cpu = core->cpu;
456 return GBAView16(cpu, address);
457}
458
459static uint32_t _GBACoreRawRead32(struct mCore* core, uint32_t address, int segment) {
460 UNUSED(segment);
461 struct ARMCore* cpu = core->cpu;
462 return GBAView32(cpu, address);
463}
464
465static void _GBACoreRawWrite8(struct mCore* core, uint32_t address, int segment, uint8_t value) {
466 UNUSED(segment);
467 struct ARMCore* cpu = core->cpu;
468 GBAPatch8(cpu, address, value, NULL);
469}
470
471static void _GBACoreRawWrite16(struct mCore* core, uint32_t address, int segment, uint16_t value) {
472 UNUSED(segment);
473 struct ARMCore* cpu = core->cpu;
474 GBAPatch16(cpu, address, value, NULL);
475}
476
477static void _GBACoreRawWrite32(struct mCore* core, uint32_t address, int segment, uint32_t value) {
478 UNUSED(segment);
479 struct ARMCore* cpu = core->cpu;
480 GBAPatch32(cpu, address, value, NULL);
481}
482
483#ifdef USE_DEBUGGERS
484static bool _GBACoreSupportsDebuggerType(struct mCore* core, enum mDebuggerType type) {
485 UNUSED(core);
486 switch (type) {
487 case DEBUGGER_CLI:
488 return true;
489#ifdef USE_GDB_STUB
490 case DEBUGGER_GDB:
491 return true;
492#endif
493 default:
494 return false;
495 }
496}
497
498static struct mDebuggerPlatform* _GBACoreDebuggerPlatform(struct mCore* core) {
499 struct GBACore* gbacore = (struct GBACore*) core;
500 if (!gbacore->debuggerPlatform) {
501 gbacore->debuggerPlatform = ARMDebuggerPlatformCreate();
502 }
503 return gbacore->debuggerPlatform;
504}
505
506static struct CLIDebuggerSystem* _GBACoreCliDebuggerSystem(struct mCore* core) {
507 return &GBACLIDebuggerCreate(core)->d;
508}
509
510static void _GBACoreAttachDebugger(struct mCore* core, struct mDebugger* debugger) {
511 if (core->debugger) {
512 GBADetachDebugger(core->board);
513 }
514 GBAAttachDebugger(core->board, debugger);
515 core->debugger = debugger;
516}
517
518static void _GBACoreDetachDebugger(struct mCore* core) {
519 GBADetachDebugger(core->board);
520 core->debugger = NULL;
521}
522#endif
523
524static struct mCheatDevice* _GBACoreCheatDevice(struct mCore* core) {
525 struct GBACore* gbacore = (struct GBACore*) core;
526 if (!gbacore->cheatDevice) {
527 gbacore->cheatDevice = GBACheatDeviceCreate();
528 ((struct ARMCore*) core->cpu)->components[CPU_COMPONENT_CHEAT_DEVICE] = &gbacore->cheatDevice->d;
529 ARMHotplugAttach(core->cpu, CPU_COMPONENT_CHEAT_DEVICE);
530 gbacore->cheatDevice->p = core;
531 }
532 return gbacore->cheatDevice;
533}
534
535static size_t _GBACoreSavedataClone(struct mCore* core, void** sram) {
536 struct GBA* gba = core->board;
537 size_t size = GBASavedataSize(&gba->memory.savedata);
538 if (!size) {
539 *sram = NULL;
540 return 0;
541 }
542 *sram = malloc(size);
543 struct VFile* vf = VFileFromMemory(*sram, size);
544 if (!vf) {
545 free(*sram);
546 *sram = NULL;
547 return 0;
548 }
549 bool success = GBASavedataClone(&gba->memory.savedata, vf);
550 vf->close(vf);
551 if (!success) {
552 free(*sram);
553 *sram = NULL;
554 return 0;
555 }
556 return size;
557}
558
559static bool _GBACoreSavedataRestore(struct mCore* core, const void* sram, size_t size, bool writeback) {
560 struct VFile* vf = VFileMemChunk(sram, size);
561 if (!vf) {
562 return false;
563 }
564 struct GBA* gba = core->board;
565 bool success = true;
566 if (writeback) {
567 success = GBASavedataLoad(&gba->memory.savedata, vf);
568 vf->close(vf);
569 } else {
570 GBASavedataMask(&gba->memory.savedata, vf, true);
571 }
572 return success;
573}
574
575struct mCore* GBACoreCreate(void) {
576 struct GBACore* gbacore = malloc(sizeof(*gbacore));
577 struct mCore* core = &gbacore->d;
578 memset(&core->opts, 0, sizeof(core->opts));
579 core->cpu = NULL;
580 core->board = NULL;
581 core->debugger = NULL;
582 core->init = _GBACoreInit;
583 core->deinit = _GBACoreDeinit;
584 core->platform = _GBACorePlatform;
585 core->setSync = _GBACoreSetSync;
586 core->loadConfig = _GBACoreLoadConfig;
587 core->desiredVideoDimensions = _GBACoreDesiredVideoDimensions;
588 core->setVideoBuffer = _GBACoreSetVideoBuffer;
589 core->getPixels = _GBACoreGetPixels;
590 core->putPixels = _GBACorePutPixels;
591 core->getAudioChannel = _GBACoreGetAudioChannel;
592 core->setAudioBufferSize = _GBACoreSetAudioBufferSize;
593 core->getAudioBufferSize = _GBACoreGetAudioBufferSize;
594 core->addCoreCallbacks = _GBACoreAddCoreCallbacks;
595 core->clearCoreCallbacks = _GBACoreClearCoreCallbacks;
596 core->setAVStream = _GBACoreSetAVStream;
597 core->isROM = GBAIsROM;
598 core->loadROM = _GBACoreLoadROM;
599 core->loadBIOS = _GBACoreLoadBIOS;
600 core->loadSave = _GBACoreLoadSave;
601 core->loadTemporarySave = _GBACoreLoadTemporarySave;
602 core->loadPatch = _GBACoreLoadPatch;
603 core->unloadROM = _GBACoreUnloadROM;
604 core->checksum = _GBACoreChecksum;
605 core->reset = _GBACoreReset;
606 core->runFrame = _GBACoreRunFrame;
607 core->runLoop = _GBACoreRunLoop;
608 core->step = _GBACoreStep;
609 core->stateSize = _GBACoreStateSize;
610 core->loadState = _GBACoreLoadState;
611 core->saveState = _GBACoreSaveState;
612 core->setKeys = _GBACoreSetKeys;
613 core->addKeys = _GBACoreAddKeys;
614 core->clearKeys = _GBACoreClearKeys;
615 core->frameCounter = _GBACoreFrameCounter;
616 core->frameCycles = _GBACoreFrameCycles;
617 core->frequency = _GBACoreFrequency;
618 core->getGameTitle = _GBACoreGetGameTitle;
619 core->getGameCode = _GBACoreGetGameCode;
620 core->setRotation = _GBACoreSetRotation;
621 core->setRumble = _GBACoreSetRumble;
622 core->busRead8 = _GBACoreBusRead8;
623 core->busRead16 = _GBACoreBusRead16;
624 core->busRead32 = _GBACoreBusRead32;
625 core->busWrite8 = _GBACoreBusWrite8;
626 core->busWrite16 = _GBACoreBusWrite16;
627 core->busWrite32 = _GBACoreBusWrite32;
628 core->rawRead8 = _GBACoreRawRead8;
629 core->rawRead16 = _GBACoreRawRead16;
630 core->rawRead32 = _GBACoreRawRead32;
631 core->rawWrite8 = _GBACoreRawWrite8;
632 core->rawWrite16 = _GBACoreRawWrite16;
633 core->rawWrite32 = _GBACoreRawWrite32;
634#ifdef USE_DEBUGGERS
635 core->supportsDebuggerType = _GBACoreSupportsDebuggerType;
636 core->debuggerPlatform = _GBACoreDebuggerPlatform;
637 core->cliDebuggerSystem = _GBACoreCliDebuggerSystem;
638 core->attachDebugger = _GBACoreAttachDebugger;
639 core->detachDebugger = _GBACoreDetachDebugger;
640#endif
641 core->cheatDevice = _GBACoreCheatDevice;
642 core->savedataClone = _GBACoreSavedataClone;
643 core->savedataRestore = _GBACoreSavedataRestore;
644 return core;
645}