GBA: Add "GBAContext" for threadless operation and use with libretro

This commit is contained in:
Jeffrey Pfau 2015-08-21 00:14:22 -07:00
parent 534c9ca8f8
commit cb7bc35113
3 changed files with 227 additions and 49 deletions

View File

@ -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);
}
}

View File

@ -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

View File

@ -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;