mirror of https://github.com/mgba-emu/mgba.git
GBA: Add "GBAContext" for threadless operation and use with libretro
This commit is contained in:
parent
534c9ca8f8
commit
cb7bc35113
|
@ -0,0 +1,156 @@
|
||||||
|
/* Copyright (c) 2013-2015 Jeffrey Pfau
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#include "gba/supervisor/context.h"
|
||||||
|
|
||||||
|
#include "gba/supervisor/overrides.h"
|
||||||
|
|
||||||
|
#include "util/memory.h"
|
||||||
|
#include "util/vfs.h"
|
||||||
|
|
||||||
|
bool GBAContextInit(struct GBAContext* context, const char* port) {
|
||||||
|
context->gba = anonymousMemoryMap(sizeof(struct GBA));
|
||||||
|
context->cpu = anonymousMemoryMap(sizeof(struct ARMCore));
|
||||||
|
context->rom = 0;
|
||||||
|
context->save = 0;
|
||||||
|
context->renderer = 0;
|
||||||
|
|
||||||
|
if (!context->gba || !context->cpu) {
|
||||||
|
if (context->gba) {
|
||||||
|
mappedMemoryFree(context->gba, sizeof(struct GBA));
|
||||||
|
}
|
||||||
|
if (context->cpu) {
|
||||||
|
mappedMemoryFree(context->cpu, sizeof(struct ARMCore));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
GBAConfigInit(&context->config, port);
|
||||||
|
if (port) {
|
||||||
|
GBAConfigLoad(&context->config);
|
||||||
|
}
|
||||||
|
|
||||||
|
GBACreate(context->gba);
|
||||||
|
ARMSetComponents(context->cpu, &context->gba->d, 0, 0);
|
||||||
|
ARMInit(context->cpu);
|
||||||
|
|
||||||
|
context->gba->sync = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GBAContextDeinit(struct GBAContext* context) {
|
||||||
|
if (context->bios) {
|
||||||
|
context->bios->close(context->bios);
|
||||||
|
context->bios = 0;
|
||||||
|
}
|
||||||
|
if (context->rom) {
|
||||||
|
context->rom->close(context->rom);
|
||||||
|
context->rom = 0;
|
||||||
|
}
|
||||||
|
if (context->save) {
|
||||||
|
context->save->close(context->save);
|
||||||
|
context->save = 0;
|
||||||
|
}
|
||||||
|
ARMDeinit(context->cpu);
|
||||||
|
GBADestroy(context->gba);
|
||||||
|
mappedMemoryFree(context->gba, 0);
|
||||||
|
mappedMemoryFree(context->cpu, 0);
|
||||||
|
GBAConfigDeinit(&context->config);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GBAContextLoadROM(struct GBAContext* context, const char* path, bool autoloadSave) {
|
||||||
|
context->rom = VFileOpen(path, O_RDONLY);
|
||||||
|
if (!context->rom) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!GBAIsROM(context->rom)) {
|
||||||
|
context->rom->close(context->rom);
|
||||||
|
context->rom = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (autoloadSave) {
|
||||||
|
context->save = VDirOptionalOpenFile(0, path, 0, ".sav", O_RDWR | O_CREAT);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GBAContextLoadROMFromVFile(struct GBAContext* context, struct VFile* rom, struct VFile* save) {
|
||||||
|
context->rom = rom;
|
||||||
|
if (!GBAIsROM(context->rom)) {
|
||||||
|
context->rom = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
context->save = save;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GBAContextLoadBIOS(struct GBAContext* context, const char* path) {
|
||||||
|
context->bios = VFileOpen(path, O_RDONLY);
|
||||||
|
if (!context->bios) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!GBAIsBIOS(context->bios)) {
|
||||||
|
context->bios->close(context->bios);
|
||||||
|
context->bios = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GBAContextLoadBIOSFromVFile(struct GBAContext* context, struct VFile* bios) {
|
||||||
|
context->bios = bios;
|
||||||
|
if (!GBAIsBIOS(context->bios)) {
|
||||||
|
context->bios = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GBAContextStart(struct GBAContext* context) {
|
||||||
|
struct GBAOptions opts = {};
|
||||||
|
GBAConfigMap(&context->config, &opts);
|
||||||
|
|
||||||
|
if (context->renderer) {
|
||||||
|
GBAVideoAssociateRenderer(&context->gba->video, context->renderer);
|
||||||
|
}
|
||||||
|
|
||||||
|
GBALoadROM(context->gba, context->rom, context->save, 0);
|
||||||
|
if (opts.useBios && context->bios) {
|
||||||
|
GBALoadBIOS(context->gba, context->bios);
|
||||||
|
}
|
||||||
|
|
||||||
|
ARMReset(context->cpu);
|
||||||
|
|
||||||
|
if (opts.skipBios) {
|
||||||
|
GBASkipBIOS(context->cpu);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct GBACartridgeOverride override;
|
||||||
|
const struct GBACartridge* cart = (const struct GBACartridge*) context->gba->memory.rom;
|
||||||
|
memcpy(override.id, &cart->id, sizeof(override.id));
|
||||||
|
if (GBAOverrideFind(GBAConfigGetOverrides(&context->config), &override)) {
|
||||||
|
GBAOverrideApply(context->gba, &override);
|
||||||
|
}
|
||||||
|
GBAConfigFreeOpts(&opts);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GBAContextStop(struct GBAContext* context) {
|
||||||
|
UNUSED(context);
|
||||||
|
// TODO?
|
||||||
|
}
|
||||||
|
|
||||||
|
void GBAContextFrame(struct GBAContext* context, uint16_t keys) {
|
||||||
|
int activeKeys = keys;
|
||||||
|
context->gba->keySource = &activeKeys;
|
||||||
|
|
||||||
|
int frameCounter = context->gba->video.frameCounter;
|
||||||
|
while (frameCounter == context->gba->video.frameCounter) {
|
||||||
|
ARMRunLoop(context->cpu);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
/* Copyright (c) 2013-2015 Jeffrey Pfau
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#ifndef CONTEXT_H
|
||||||
|
#define CONTEXT_H
|
||||||
|
|
||||||
|
#include "util/common.h"
|
||||||
|
|
||||||
|
#include "gba/supervisor/config.h"
|
||||||
|
#include "gba/input.h"
|
||||||
|
|
||||||
|
struct GBAContext {
|
||||||
|
struct GBA* gba;
|
||||||
|
struct ARMCore* cpu;
|
||||||
|
struct GBAVideoRenderer* renderer;
|
||||||
|
struct VFile* rom;
|
||||||
|
struct VFile* save;
|
||||||
|
struct VFile* bios;
|
||||||
|
struct GBAConfig config;
|
||||||
|
struct GBAOptions opts;
|
||||||
|
struct GBAInputMap inputMap;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool GBAContextInit(struct GBAContext* context, const char* port);
|
||||||
|
void GBAContextDeinit(struct GBAContext* context);
|
||||||
|
|
||||||
|
bool GBAContextLoadROM(struct GBAContext* context, const char* path, bool autoloadSave);
|
||||||
|
bool GBAContextLoadROMFromVFile(struct GBAContext* context, struct VFile* rom, struct VFile* save);
|
||||||
|
bool GBAContextLoadBIOS(struct GBAContext* context, const char* path);
|
||||||
|
bool GBAContextLoadBIOSFromVFile(struct GBAContext* context, struct VFile* bios);
|
||||||
|
|
||||||
|
bool GBAContextStart(struct GBAContext* context);
|
||||||
|
void GBAContextStop(struct GBAContext* context);
|
||||||
|
void GBAContextFrame(struct GBAContext* context, uint16_t keys);
|
||||||
|
|
||||||
|
#endif
|
|
@ -7,12 +7,9 @@
|
||||||
|
|
||||||
#include "util/common.h"
|
#include "util/common.h"
|
||||||
|
|
||||||
#include "gba/gba.h"
|
|
||||||
#include "gba/interface.h"
|
|
||||||
#include "gba/renderers/video-software.h"
|
#include "gba/renderers/video-software.h"
|
||||||
#include "gba/serialize.h"
|
#include "gba/serialize.h"
|
||||||
#include "gba/supervisor/overrides.h"
|
#include "gba/supervisor/context.h"
|
||||||
#include "gba/video.h"
|
|
||||||
#include "util/circle-buffer.h"
|
#include "util/circle-buffer.h"
|
||||||
#include "util/vfs.h"
|
#include "util/vfs.h"
|
||||||
|
|
||||||
|
@ -37,14 +34,10 @@ static void _setRumble(struct GBARumble* rumble, int enable);
|
||||||
static uint8_t _readLux(struct GBALuminanceSource* lux);
|
static uint8_t _readLux(struct GBALuminanceSource* lux);
|
||||||
static void _updateLux(struct GBALuminanceSource* lux);
|
static void _updateLux(struct GBALuminanceSource* lux);
|
||||||
|
|
||||||
static struct GBA gba;
|
static struct GBAContext context;
|
||||||
static struct ARMCore cpu;
|
|
||||||
static struct GBAVideoSoftwareRenderer renderer;
|
static struct GBAVideoSoftwareRenderer renderer;
|
||||||
static struct VFile* rom;
|
|
||||||
static void* data;
|
static void* data;
|
||||||
static struct VFile* save;
|
|
||||||
static void* savedata;
|
static void* savedata;
|
||||||
static struct VFile* bios;
|
|
||||||
static struct GBAAVStream stream;
|
static struct GBAAVStream stream;
|
||||||
static int rumbleLevel;
|
static int rumbleLevel;
|
||||||
static struct CircleBuffer rumbleHistory;
|
static struct CircleBuffer rumbleHistory;
|
||||||
|
@ -162,53 +155,49 @@ void retro_init(void) {
|
||||||
stream.postAudioBuffer = _postAudioBuffer;
|
stream.postAudioBuffer = _postAudioBuffer;
|
||||||
stream.postVideoFrame = _postVideoFrame;
|
stream.postVideoFrame = _postVideoFrame;
|
||||||
|
|
||||||
GBACreate(&gba);
|
GBAContextInit(&context, 0);
|
||||||
ARMSetComponents(&cpu, &gba.d, 0, 0);
|
struct GBAOptions opts = {
|
||||||
ARMInit(&cpu);
|
.useBios = true,
|
||||||
gba.logLevel = 0; // TODO: Settings
|
.logLevel = 0,
|
||||||
gba.logHandler = GBARetroLog;
|
.idleOptimization = IDLE_LOOP_REMOVE
|
||||||
gba.stream = &stream;
|
};
|
||||||
gba.idleOptimization = IDLE_LOOP_REMOVE; // TODO: Settings
|
GBAConfigLoadDefaults(&context.config, &opts);
|
||||||
|
context.gba->logHandler = GBARetroLog;
|
||||||
|
context.gba->stream = &stream;
|
||||||
if (rumbleCallback) {
|
if (rumbleCallback) {
|
||||||
gba.rumble = &rumble;
|
context.gba->rumble = &rumble;
|
||||||
}
|
}
|
||||||
gba.luminanceSource = &lux;
|
context.gba->luminanceSource = &lux;
|
||||||
rom = 0;
|
|
||||||
|
|
||||||
const char* sysDir = 0;
|
const char* sysDir = 0;
|
||||||
if (environCallback(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &sysDir)) {
|
if (environCallback(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &sysDir)) {
|
||||||
char biosPath[PATH_MAX];
|
char biosPath[PATH_MAX];
|
||||||
snprintf(biosPath, sizeof(biosPath), "%s%s%s", sysDir, PATH_SEP, "gba_bios.bin");
|
snprintf(biosPath, sizeof(biosPath), "%s%s%s", sysDir, PATH_SEP, "gba_bios.bin");
|
||||||
bios = VFileOpen(biosPath, O_RDONLY);
|
struct VFile* bios = VFileOpen(biosPath, O_RDONLY);
|
||||||
if (bios) {
|
if (bios) {
|
||||||
GBALoadBIOS(&gba, bios);
|
GBAContextLoadBIOSFromVFile(&context, bios);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GBAVideoSoftwareRendererCreate(&renderer);
|
GBAVideoSoftwareRendererCreate(&renderer);
|
||||||
renderer.outputBuffer = malloc(256 * VIDEO_VERTICAL_PIXELS * BYTES_PER_PIXEL);
|
renderer.outputBuffer = malloc(256 * VIDEO_VERTICAL_PIXELS * BYTES_PER_PIXEL);
|
||||||
renderer.outputBufferStride = 256;
|
renderer.outputBufferStride = 256;
|
||||||
GBAVideoAssociateRenderer(&gba.video, &renderer.d);
|
GBAVideoAssociateRenderer(&context.gba->video, &renderer.d);
|
||||||
|
|
||||||
GBAAudioResizeBuffer(&gba.audio, SAMPLES);
|
GBAAudioResizeBuffer(&context.gba->audio, SAMPLES);
|
||||||
|
|
||||||
#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
|
#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
|
||||||
blip_set_rates(gba.audio.left, GBA_ARM7TDMI_FREQUENCY, 32768);
|
blip_set_rates(context.gba->audio.left, GBA_ARM7TDMI_FREQUENCY, 32768);
|
||||||
blip_set_rates(gba.audio.right, GBA_ARM7TDMI_FREQUENCY, 32768);
|
blip_set_rates(context.gba->audio.right, GBA_ARM7TDMI_FREQUENCY, 32768);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void retro_deinit(void) {
|
void retro_deinit(void) {
|
||||||
if (bios) {
|
GBAContextDeinit(&context);
|
||||||
bios->close(bios);
|
|
||||||
bios = 0;
|
|
||||||
}
|
|
||||||
GBADestroy(&gba);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void retro_run(void) {
|
void retro_run(void) {
|
||||||
int keys;
|
uint16_t keys;
|
||||||
gba.keySource = &keys;
|
|
||||||
inputPollCallback();
|
inputPollCallback();
|
||||||
|
|
||||||
keys = 0;
|
keys = 0;
|
||||||
|
@ -243,14 +232,11 @@ void retro_run(void) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int frameCount = gba.video.frameCounter;
|
GBAContextFrame(&context, keys);
|
||||||
while (gba.video.frameCounter == frameCount) {
|
|
||||||
ARMRunLoop(&cpu);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void retro_reset(void) {
|
void retro_reset(void) {
|
||||||
ARMReset(&cpu);
|
ARMReset(context.cpu);
|
||||||
|
|
||||||
if (rumbleCallback) {
|
if (rumbleCallback) {
|
||||||
CircleBufferClear(&rumbleHistory);
|
CircleBufferClear(&rumbleHistory);
|
||||||
|
@ -258,6 +244,7 @@ void retro_reset(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool retro_load_game(const struct retro_game_info* game) {
|
bool retro_load_game(const struct retro_game_info* game) {
|
||||||
|
struct VFile* rom;
|
||||||
if (game->data) {
|
if (game->data) {
|
||||||
data = malloc(game->size);
|
data = malloc(game->size);
|
||||||
memcpy(data, game->data, game->size);
|
memcpy(data, game->data, game->size);
|
||||||
|
@ -270,26 +257,23 @@ bool retro_load_game(const struct retro_game_info* game) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!GBAIsROM(rom)) {
|
if (!GBAIsROM(rom)) {
|
||||||
|
rom->close(rom);
|
||||||
|
free(data);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
savedata = malloc(SIZE_CART_FLASH1M);
|
savedata = malloc(SIZE_CART_FLASH1M);
|
||||||
save = VFileFromMemory(savedata, SIZE_CART_FLASH1M);
|
struct VFile* save = VFileFromMemory(savedata, SIZE_CART_FLASH1M);
|
||||||
|
|
||||||
GBALoadROM(&gba, rom, save, game->path);
|
GBAContextLoadROMFromVFile(&context, rom, save);
|
||||||
GBAOverrideApplyDefaults(&gba);
|
GBAContextStart(&context);
|
||||||
|
|
||||||
ARMReset(&cpu);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void retro_unload_game(void) {
|
void retro_unload_game(void) {
|
||||||
rom->close(rom);
|
GBAContextStop(&context);
|
||||||
rom = 0;
|
|
||||||
free(data);
|
free(data);
|
||||||
data = 0;
|
data = 0;
|
||||||
save->close(save);
|
|
||||||
save = 0;
|
|
||||||
free(savedata);
|
free(savedata);
|
||||||
savedata = 0;
|
savedata = 0;
|
||||||
CircleBufferDeinit(&rumbleHistory);
|
CircleBufferDeinit(&rumbleHistory);
|
||||||
|
@ -303,7 +287,7 @@ bool retro_serialize(void* data, size_t size) {
|
||||||
if (size != retro_serialize_size()) {
|
if (size != retro_serialize_size()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
GBASerialize(&gba, data);
|
GBASerialize(context.gba, data);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -311,7 +295,7 @@ bool retro_unserialize(const void* data, size_t size) {
|
||||||
if (size != retro_serialize_size()) {
|
if (size != retro_serialize_size()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
GBADeserialize(&gba, data);
|
GBADeserialize(context.gba, data);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -353,7 +337,7 @@ size_t retro_get_memory_size(unsigned id) {
|
||||||
if (id != RETRO_MEMORY_SAVE_RAM) {
|
if (id != RETRO_MEMORY_SAVE_RAM) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
switch (gba.memory.savedata.type) {
|
switch (context.gba->memory.savedata.type) {
|
||||||
case SAVEDATA_AUTODETECT:
|
case SAVEDATA_AUTODETECT:
|
||||||
case SAVEDATA_FLASH1M:
|
case SAVEDATA_FLASH1M:
|
||||||
return SIZE_CART_FLASH1M;
|
return SIZE_CART_FLASH1M;
|
||||||
|
|
Loading…
Reference in New Issue