src/gb/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/gb/core.h>
7
8#include <mgba/core/core.h>
9#include <mgba/internal/gb/cheats.h>
10#include <mgba/internal/gb/extra/cli.h>
11#include <mgba/internal/gb/gb.h>
12#include <mgba/internal/gb/input.h>
13#include <mgba/internal/gb/mbc.h>
14#include <mgba/internal/gb/overrides.h>
15#include <mgba/internal/gb/renderers/software.h>
16#include <mgba/internal/gb/serialize.h>
17#include <mgba/internal/lr35902/lr35902.h>
18#include <mgba/internal/lr35902/debugger/debugger.h>
19#include <mgba-util/crc32.h>
20#include <mgba-util/memory.h>
21#include <mgba-util/patch.h>
22#include <mgba-util/vfs.h>
23
24#ifndef MINIMAL_CORE
25#include <mgba/internal/gba/input.h>
26#endif
27
28struct GBCore {
29 struct mCore d;
30 struct GBVideoSoftwareRenderer renderer;
31 uint8_t keys;
32 struct mCPUComponent* components[CPU_COMPONENT_MAX];
33 const struct Configuration* overrides;
34 struct mDebuggerPlatform* debuggerPlatform;
35 struct mCheatDevice* cheatDevice;
36};
37
38static bool _GBCoreInit(struct mCore* core) {
39 struct GBCore* gbcore = (struct GBCore*) core;
40
41 struct LR35902Core* cpu = anonymousMemoryMap(sizeof(struct LR35902Core));
42 struct GB* gb = anonymousMemoryMap(sizeof(struct GB));
43 if (!cpu || !gb) {
44 free(cpu);
45 free(gb);
46 return false;
47 }
48 core->cpu = cpu;
49 core->board = gb;
50 gbcore->overrides = NULL;
51 gbcore->debuggerPlatform = NULL;
52 gbcore->cheatDevice = NULL;
53
54 GBCreate(gb);
55 memset(gbcore->components, 0, sizeof(gbcore->components));
56 LR35902SetComponents(cpu, &gb->d, CPU_COMPONENT_MAX, gbcore->components);
57 LR35902Init(cpu);
58 mRTCGenericSourceInit(&core->rtc, core);
59 gb->memory.rtc = &core->rtc.d;
60
61 GBVideoSoftwareRendererCreate(&gbcore->renderer);
62 gbcore->renderer.outputBuffer = NULL;
63
64 gbcore->keys = 0;
65 gb->keySource = &gbcore->keys;
66
67#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
68 mDirectorySetInit(&core->dirs);
69#endif
70
71#ifndef MINIMAL_CORE
72 core->inputInfo = &GBInputInfo;
73#endif
74
75 return true;
76}
77
78static void _GBCoreDeinit(struct mCore* core) {
79 LR35902Deinit(core->cpu);
80 GBDestroy(core->board);
81 mappedMemoryFree(core->cpu, sizeof(struct LR35902Core));
82 mappedMemoryFree(core->board, sizeof(struct GB));
83#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
84 mDirectorySetDeinit(&core->dirs);
85#endif
86
87 struct GBCore* gbcore = (struct GBCore*) core;
88 free(gbcore->debuggerPlatform);
89 if (gbcore->cheatDevice) {
90 mCheatDeviceDestroy(gbcore->cheatDevice);
91 }
92 free(gbcore->cheatDevice);
93 mCoreConfigFreeOpts(&core->opts);
94 free(core);
95}
96
97static enum mPlatform _GBCorePlatform(const struct mCore* core) {
98 UNUSED(core);
99 return PLATFORM_GB;
100}
101
102static void _GBCoreSetSync(struct mCore* core, struct mCoreSync* sync) {
103 struct GB* gb = core->board;
104 gb->sync = sync;
105}
106
107static void _GBCoreLoadConfig(struct mCore* core, const struct mCoreConfig* config) {
108 UNUSED(config);
109
110 struct GB* gb = core->board;
111 if (core->opts.mute) {
112 gb->audio.masterVolume = 0;
113 } else {
114 gb->audio.masterVolume = core->opts.volume;
115 }
116 gb->video.frameskip = core->opts.frameskip;
117 mCoreConfigCopyValue(&core->config, config, "gb.bios");
118 mCoreConfigCopyValue(&core->config, config, "gbc.bios");
119
120#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
121 struct GBCore* gbcore = (struct GBCore*) core;
122 gbcore->overrides = mCoreConfigGetOverridesConst(config);
123#endif
124}
125
126static void _GBCoreDesiredVideoDimensions(struct mCore* core, unsigned* width, unsigned* height) {
127 UNUSED(core);
128 *width = GB_VIDEO_HORIZONTAL_PIXELS;
129 *height = GB_VIDEO_VERTICAL_PIXELS;
130}
131
132static void _GBCoreSetVideoBuffer(struct mCore* core, color_t* buffer, size_t stride) {
133 struct GBCore* gbcore = (struct GBCore*) core;
134 gbcore->renderer.outputBuffer = buffer;
135 gbcore->renderer.outputBufferStride = stride;
136}
137
138static void _GBCoreGetPixels(struct mCore* core, const void** buffer, size_t* stride) {
139 struct GBCore* gbcore = (struct GBCore*) core;
140 gbcore->renderer.d.getPixels(&gbcore->renderer.d, stride, buffer);
141}
142
143static void _GBCorePutPixels(struct mCore* core, const void* buffer, size_t stride) {
144 struct GBCore* gbcore = (struct GBCore*) core;
145 gbcore->renderer.d.putPixels(&gbcore->renderer.d, stride, buffer);
146}
147
148static struct blip_t* _GBCoreGetAudioChannel(struct mCore* core, int ch) {
149 struct GB* gb = core->board;
150 switch (ch) {
151 case 0:
152 return gb->audio.left;
153 case 1:
154 return gb->audio.right;
155 default:
156 return NULL;
157 }
158}
159
160static void _GBCoreSetAudioBufferSize(struct mCore* core, size_t samples) {
161 struct GB* gb = core->board;
162 GBAudioResizeBuffer(&gb->audio, samples);
163}
164
165static size_t _GBCoreGetAudioBufferSize(struct mCore* core) {
166 struct GB* gb = core->board;
167 return gb->audio.samples;
168}
169
170static void _GBCoreAddCoreCallbacks(struct mCore* core, struct mCoreCallbacks* coreCallbacks) {
171 struct GB* gb = core->board;
172 *mCoreCallbacksListAppend(&gb->coreCallbacks) = *coreCallbacks;
173}
174
175static void _GBCoreClearCoreCallbacks(struct mCore* core) {
176 struct GB* gb = core->board;
177 mCoreCallbacksListClear(&gb->coreCallbacks);
178}
179
180static void _GBCoreSetAVStream(struct mCore* core, struct mAVStream* stream) {
181 struct GB* gb = core->board;
182 gb->stream = stream;
183 if (stream && stream->videoDimensionsChanged) {
184 stream->videoDimensionsChanged(stream, GB_VIDEO_HORIZONTAL_PIXELS, GB_VIDEO_VERTICAL_PIXELS);
185 }
186}
187
188static bool _GBCoreLoadROM(struct mCore* core, struct VFile* vf) {
189 return GBLoadROM(core->board, vf);
190}
191
192static bool _GBCoreLoadBIOS(struct mCore* core, struct VFile* vf, int type) {
193 UNUSED(type);
194 GBLoadBIOS(core->board, vf);
195 return true;
196}
197
198static bool _GBCoreLoadSave(struct mCore* core, struct VFile* vf) {
199 return GBLoadSave(core->board, vf);
200}
201
202static bool _GBCoreLoadTemporarySave(struct mCore* core, struct VFile* vf) {
203 struct GB* gb = core->board;
204 GBSavedataMask(gb, vf, false);
205 return true; // TODO: Return a real value
206}
207
208static bool _GBCoreLoadPatch(struct mCore* core, struct VFile* vf) {
209 if (!vf) {
210 return false;
211 }
212 struct Patch patch;
213 if (!loadPatch(vf, &patch)) {
214 return false;
215 }
216 GBApplyPatch(core->board, &patch);
217 return true;
218}
219
220static void _GBCoreUnloadROM(struct mCore* core) {
221 struct GBCore* gbcore = (struct GBCore*) core;
222 struct LR35902Core* cpu = core->cpu;
223 if (gbcore->cheatDevice) {
224 LR35902HotplugDetach(cpu, CPU_COMPONENT_CHEAT_DEVICE);
225 cpu->components[CPU_COMPONENT_CHEAT_DEVICE] = NULL;
226 mCheatDeviceDestroy(gbcore->cheatDevice);
227 gbcore->cheatDevice = NULL;
228 }
229 return GBUnloadROM(core->board);
230}
231
232static void _GBCoreChecksum(const struct mCore* core, void* data, enum mCoreChecksumType type) {
233 struct GB* gb = (struct GB*) core->board;
234 switch (type) {
235 case CHECKSUM_CRC32:
236 memcpy(data, &gb->romCrc32, sizeof(gb->romCrc32));
237 break;
238 }
239 return;
240}
241
242static void _GBCoreReset(struct mCore* core) {
243 struct GBCore* gbcore = (struct GBCore*) core;
244 struct GB* gb = (struct GB*) core->board;
245 if (gbcore->renderer.outputBuffer) {
246 GBVideoAssociateRenderer(&gb->video, &gbcore->renderer.d);
247 }
248
249 if (gb->memory.rom) {
250 struct GBCartridgeOverride override;
251 const struct GBCartridge* cart = (const struct GBCartridge*) &gb->memory.rom[0x100];
252 override.headerCrc32 = doCrc32(cart, sizeof(*cart));
253 if (GBOverrideFind(gbcore->overrides, &override)) {
254 GBOverrideApply(gb, &override);
255 }
256 }
257
258#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
259 if (!gb->biosVf && core->opts.useBios) {
260 struct VFile* bios = NULL;
261 bool found = false;
262 if (core->opts.bios) {
263 bios = VFileOpen(core->opts.bios, O_RDONLY);
264 if (bios && GBIsBIOS(bios)) {
265 found = true;
266 } else if (bios) {
267 bios->close(bios);
268 bios = NULL;
269 }
270 }
271 if (!found) {
272 GBDetectModel(gb);
273 const char* configPath = NULL;
274
275 switch (gb->model) {
276 case GB_MODEL_DMG:
277 case GB_MODEL_SGB: // TODO
278 configPath = mCoreConfigGetValue(&core->config, "gb.bios");
279 break;
280 case GB_MODEL_CGB:
281 case GB_MODEL_AGB:
282 configPath = mCoreConfigGetValue(&core->config, "gbc.bios");
283 break;
284 default:
285 break;
286 };
287 if (configPath) {
288 bios = VFileOpen(configPath, O_RDONLY);
289 }
290 if (bios && GBIsBIOS(bios)) {
291 found = true;
292 } else if (bios) {
293 bios->close(bios);
294 bios = NULL;
295 }
296 }
297 if (!found) {
298 char path[PATH_MAX];
299 mCoreConfigDirectory(path, PATH_MAX);
300 switch (gb->model) {
301 case GB_MODEL_DMG:
302 case GB_MODEL_SGB: // TODO
303 strncat(path, PATH_SEP "gb_bios.bin", PATH_MAX - strlen(path));
304 break;
305 case GB_MODEL_CGB:
306 case GB_MODEL_AGB:
307 strncat(path, PATH_SEP "gbc_bios.bin", PATH_MAX - strlen(path));
308 break;
309 default:
310 break;
311 };
312 bios = VFileOpen(path, O_RDONLY);
313 if (bios && GBIsBIOS(bios)) {
314 found = true;
315 } else if (bios) {
316 bios->close(bios);
317 bios = NULL;
318 }
319 }
320 if (bios) {
321 GBLoadBIOS(gb, bios);
322 }
323 }
324#endif
325
326 LR35902Reset(core->cpu);
327}
328
329static void _GBCoreRunFrame(struct mCore* core) {
330 struct GB* gb = core->board;
331 int32_t frameCounter = gb->video.frameCounter;
332 while (gb->video.frameCounter == frameCounter) {
333 LR35902Run(core->cpu);
334 }
335}
336
337static void _GBCoreRunLoop(struct mCore* core) {
338 LR35902Run(core->cpu);
339}
340
341static void _GBCoreStep(struct mCore* core) {
342 struct LR35902Core* cpu = core->cpu;
343 do {
344 LR35902Tick(cpu);
345 } while (cpu->executionState != LR35902_CORE_FETCH);
346}
347
348static size_t _GBCoreStateSize(struct mCore* core) {
349 UNUSED(core);
350 return sizeof(struct GBSerializedState);
351}
352
353static bool _GBCoreLoadState(struct mCore* core, const void* state) {
354 return GBDeserialize(core->board, state);
355}
356
357static bool _GBCoreSaveState(struct mCore* core, void* state) {
358 struct LR35902Core* cpu = core->cpu;
359 while (cpu->executionState != LR35902_CORE_FETCH) {
360 LR35902Tick(cpu);
361 }
362 GBSerialize(core->board, state);
363 return true;
364}
365
366static void _GBCoreSetKeys(struct mCore* core, uint32_t keys) {
367 struct GBCore* gbcore = (struct GBCore*) core;
368 gbcore->keys = keys;
369}
370
371static void _GBCoreAddKeys(struct mCore* core, uint32_t keys) {
372 struct GBCore* gbcore = (struct GBCore*) core;
373 gbcore->keys |= keys;
374}
375
376static void _GBCoreClearKeys(struct mCore* core, uint32_t keys) {
377 struct GBCore* gbcore = (struct GBCore*) core;
378 gbcore->keys &= ~keys;
379}
380
381static int32_t _GBCoreFrameCounter(const struct mCore* core) {
382 const struct GB* gb = core->board;
383 return gb->video.frameCounter;
384}
385
386static int32_t _GBCoreFrameCycles(const struct mCore* core) {
387 UNUSED(core);
388 return GB_VIDEO_TOTAL_LENGTH;
389}
390
391static int32_t _GBCoreFrequency(const struct mCore* core) {
392 UNUSED(core);
393 // TODO: GB differences
394 return DMG_LR35902_FREQUENCY;
395}
396
397static void _GBCoreGetGameTitle(const struct mCore* core, char* title) {
398 GBGetGameTitle(core->board, title);
399}
400
401static void _GBCoreGetGameCode(const struct mCore* core, char* title) {
402 GBGetGameCode(core->board, title);
403}
404
405static void _GBCoreSetRotation(struct mCore* core, struct mRotationSource* rotation) {
406 struct GB* gb = core->board;
407 gb->memory.rotation = rotation;
408}
409
410static void _GBCoreSetRumble(struct mCore* core, struct mRumble* rumble) {
411 struct GB* gb = core->board;
412 gb->memory.rumble = rumble;
413}
414
415static uint32_t _GBCoreBusRead8(struct mCore* core, uint32_t address) {
416 struct LR35902Core* cpu = core->cpu;
417 return cpu->memory.load8(cpu, address);
418}
419
420static uint32_t _GBCoreBusRead16(struct mCore* core, uint32_t address) {
421 struct LR35902Core* cpu = core->cpu;
422 return cpu->memory.load8(cpu, address) | (cpu->memory.load8(cpu, address + 1) << 8);
423}
424
425static uint32_t _GBCoreBusRead32(struct mCore* core, uint32_t address) {
426 struct LR35902Core* cpu = core->cpu;
427 return cpu->memory.load8(cpu, address) | (cpu->memory.load8(cpu, address + 1) << 8) |
428 (cpu->memory.load8(cpu, address + 2) << 16) | (cpu->memory.load8(cpu, address + 3) << 24);
429}
430
431static void _GBCoreBusWrite8(struct mCore* core, uint32_t address, uint8_t value) {
432 struct LR35902Core* cpu = core->cpu;
433 cpu->memory.store8(cpu, address, value);
434}
435
436static void _GBCoreBusWrite16(struct mCore* core, uint32_t address, uint16_t value) {
437 struct LR35902Core* cpu = core->cpu;
438 cpu->memory.store8(cpu, address, value);
439 cpu->memory.store8(cpu, address + 1, value >> 8);
440}
441
442static void _GBCoreBusWrite32(struct mCore* core, uint32_t address, uint32_t value) {
443 struct LR35902Core* cpu = core->cpu;
444 cpu->memory.store8(cpu, address, value);
445 cpu->memory.store8(cpu, address + 1, value >> 8);
446 cpu->memory.store8(cpu, address + 2, value >> 16);
447 cpu->memory.store8(cpu, address + 3, value >> 24);
448}
449
450static uint32_t _GBCoreRawRead8(struct mCore* core, uint32_t address, int segment) {
451 struct LR35902Core* cpu = core->cpu;
452 return GBView8(cpu, address, segment);
453}
454
455static uint32_t _GBCoreRawRead16(struct mCore* core, uint32_t address, int segment) {
456 struct LR35902Core* cpu = core->cpu;
457 return GBView8(cpu, address, segment) | (GBView8(cpu, address + 1, segment) << 8);
458}
459
460static uint32_t _GBCoreRawRead32(struct mCore* core, uint32_t address, int segment) {
461 struct LR35902Core* cpu = core->cpu;
462 return GBView8(cpu, address, segment) | (GBView8(cpu, address + 1, segment) << 8) |
463 (GBView8(cpu, address + 2, segment) << 16) | (GBView8(cpu, address + 3, segment) << 24);
464}
465
466static void _GBCoreRawWrite8(struct mCore* core, uint32_t address, int segment, uint8_t value) {
467 struct LR35902Core* cpu = core->cpu;
468 GBPatch8(cpu, address, value, NULL, segment);
469}
470
471static void _GBCoreRawWrite16(struct mCore* core, uint32_t address, int segment, uint16_t value) {
472 struct LR35902Core* cpu = core->cpu;
473 GBPatch8(cpu, address, value, NULL, segment);
474 GBPatch8(cpu, address + 1, value >> 8, NULL, segment);
475}
476
477static void _GBCoreRawWrite32(struct mCore* core, uint32_t address, int segment, uint32_t value) {
478 struct LR35902Core* cpu = core->cpu;
479 GBPatch8(cpu, address, value, NULL, segment);
480 GBPatch8(cpu, address + 1, value >> 8, NULL, segment);
481 GBPatch8(cpu, address + 2, value >> 16, NULL, segment);
482 GBPatch8(cpu, address + 3, value >> 24, NULL, segment);
483}
484
485#ifdef USE_DEBUGGERS
486static bool _GBCoreSupportsDebuggerType(struct mCore* core, enum mDebuggerType type) {
487 UNUSED(core);
488 switch (type) {
489 case DEBUGGER_CLI:
490 return true;
491 default:
492 return false;
493 }
494}
495
496static struct mDebuggerPlatform* _GBCoreDebuggerPlatform(struct mCore* core) {
497 struct GBCore* gbcore = (struct GBCore*) core;
498 if (!gbcore->debuggerPlatform) {
499 gbcore->debuggerPlatform = LR35902DebuggerPlatformCreate();
500 }
501 return gbcore->debuggerPlatform;
502}
503
504static struct CLIDebuggerSystem* _GBCoreCliDebuggerSystem(struct mCore* core) {
505 return GBCLIDebuggerCreate(core);
506}
507
508static void _GBCoreAttachDebugger(struct mCore* core, struct mDebugger* debugger) {
509 struct LR35902Core* cpu = core->cpu;
510 if (core->debugger) {
511 LR35902HotplugDetach(cpu, CPU_COMPONENT_DEBUGGER);
512 }
513 cpu->components[CPU_COMPONENT_DEBUGGER] = &debugger->d;
514 LR35902HotplugAttach(cpu, CPU_COMPONENT_DEBUGGER);
515 core->debugger = debugger;
516}
517
518static void _GBCoreDetachDebugger(struct mCore* core) {
519 struct LR35902Core* cpu = core->cpu;
520 if (core->debugger) {
521 LR35902HotplugDetach(cpu, CPU_COMPONENT_DEBUGGER);
522 }
523 cpu->components[CPU_COMPONENT_DEBUGGER] = NULL;
524 core->debugger = NULL;
525}
526#endif
527
528static struct mCheatDevice* _GBCoreCheatDevice(struct mCore* core) {
529 struct GBCore* gbcore = (struct GBCore*) core;
530 if (!gbcore->cheatDevice) {
531 gbcore->cheatDevice = GBCheatDeviceCreate();
532 ((struct LR35902Core*) core->cpu)->components[CPU_COMPONENT_CHEAT_DEVICE] = &gbcore->cheatDevice->d;
533 LR35902HotplugAttach(core->cpu, CPU_COMPONENT_CHEAT_DEVICE);
534 gbcore->cheatDevice->p = core;
535 }
536 return gbcore->cheatDevice;
537}
538
539static size_t _GBCoreSavedataClone(struct mCore* core, void** sram) {
540 struct GB* gb = core->board;
541 struct VFile* vf = gb->sramVf;
542 if (vf) {
543 *sram = malloc(vf->size(vf));
544 vf->seek(vf, 0, SEEK_SET);
545 return vf->read(vf, *sram, vf->size(vf));
546 }
547 *sram = malloc(gb->sramSize);
548 memcpy(*sram, gb->memory.sram, gb->sramSize);
549 return gb->sramSize;
550}
551
552static bool _GBCoreSavedataRestore(struct mCore* core, const void* sram, size_t size, bool writeback) {
553 struct GB* gb = core->board;
554 if (!writeback) {
555 struct VFile* vf = VFileMemChunk(sram, size);
556 GBSavedataMask(gb, vf, true);
557 return true;
558 }
559 struct VFile* vf = gb->sramVf;
560 if (vf) {
561 vf->seek(vf, 0, SEEK_SET);
562 return vf->write(vf, sram, size) > 0;
563 }
564 if (size > 0x20000) {
565 size = 0x20000;
566 }
567 GBResizeSram(gb, size);
568 memcpy(gb->memory.sram, sram, size);
569 return true;
570}
571
572struct mCore* GBCoreCreate(void) {
573 struct GBCore* gbcore = malloc(sizeof(*gbcore));
574 struct mCore* core = &gbcore->d;
575 memset(&core->opts, 0, sizeof(core->opts));
576 core->cpu = NULL;
577 core->board = NULL;
578 core->debugger = NULL;
579 core->init = _GBCoreInit;
580 core->deinit = _GBCoreDeinit;
581 core->platform = _GBCorePlatform;
582 core->setSync = _GBCoreSetSync;
583 core->loadConfig = _GBCoreLoadConfig;
584 core->desiredVideoDimensions = _GBCoreDesiredVideoDimensions;
585 core->setVideoBuffer = _GBCoreSetVideoBuffer;
586 core->getPixels = _GBCoreGetPixels;
587 core->putPixels = _GBCorePutPixels;
588 core->getAudioChannel = _GBCoreGetAudioChannel;
589 core->setAudioBufferSize = _GBCoreSetAudioBufferSize;
590 core->getAudioBufferSize = _GBCoreGetAudioBufferSize;
591 core->setAVStream = _GBCoreSetAVStream;
592 core->addCoreCallbacks = _GBCoreAddCoreCallbacks;
593 core->clearCoreCallbacks = _GBCoreClearCoreCallbacks;
594 core->isROM = GBIsROM;
595 core->loadROM = _GBCoreLoadROM;
596 core->loadBIOS = _GBCoreLoadBIOS;
597 core->loadSave = _GBCoreLoadSave;
598 core->loadTemporarySave = _GBCoreLoadTemporarySave;
599 core->loadPatch = _GBCoreLoadPatch;
600 core->unloadROM = _GBCoreUnloadROM;
601 core->checksum = _GBCoreChecksum;
602 core->reset = _GBCoreReset;
603 core->runFrame = _GBCoreRunFrame;
604 core->runLoop = _GBCoreRunLoop;
605 core->step = _GBCoreStep;
606 core->stateSize = _GBCoreStateSize;
607 core->loadState = _GBCoreLoadState;
608 core->saveState = _GBCoreSaveState;
609 core->setKeys = _GBCoreSetKeys;
610 core->addKeys = _GBCoreAddKeys;
611 core->clearKeys = _GBCoreClearKeys;
612 core->frameCounter = _GBCoreFrameCounter;
613 core->frameCycles = _GBCoreFrameCycles;
614 core->frequency = _GBCoreFrequency;
615 core->getGameTitle = _GBCoreGetGameTitle;
616 core->getGameCode = _GBCoreGetGameCode;
617 core->setRotation = _GBCoreSetRotation;
618 core->setRumble = _GBCoreSetRumble;
619 core->busRead8 = _GBCoreBusRead8;
620 core->busRead16 = _GBCoreBusRead16;
621 core->busRead32 = _GBCoreBusRead32;
622 core->busWrite8 = _GBCoreBusWrite8;
623 core->busWrite16 = _GBCoreBusWrite16;
624 core->busWrite32 = _GBCoreBusWrite32;
625 core->rawRead8 = _GBCoreRawRead8;
626 core->rawRead16 = _GBCoreRawRead16;
627 core->rawRead32 = _GBCoreRawRead32;
628 core->rawWrite8 = _GBCoreRawWrite8;
629 core->rawWrite16 = _GBCoreRawWrite16;
630 core->rawWrite32 = _GBCoreRawWrite32;
631#ifdef USE_DEBUGGERS
632 core->supportsDebuggerType = _GBCoreSupportsDebuggerType;
633 core->debuggerPlatform = _GBCoreDebuggerPlatform;
634 core->cliDebuggerSystem = _GBCoreCliDebuggerSystem;
635 core->attachDebugger = _GBCoreAttachDebugger;
636 core->detachDebugger = _GBCoreDetachDebugger;
637#endif
638 core->cheatDevice = _GBCoreCheatDevice;
639 core->savedataClone = _GBCoreSavedataClone;
640 core->savedataRestore = _GBCoreSavedataRestore;
641 return core;
642}