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 "gba/gba.h"
#include "gba/interface.h"
#include "gba/renderers/video-software.h"
#include "gba/serialize.h"
#include "gba/supervisor/overrides.h"
#include "gba/video.h"
#include "gba/supervisor/context.h"
#include "util/circle-buffer.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 void _updateLux(struct GBALuminanceSource* lux);
static struct GBA gba;
static struct ARMCore cpu;
static struct GBAContext context;
static struct GBAVideoSoftwareRenderer renderer;
static struct VFile* rom;
static void* data;
static struct VFile* save;
static void* savedata;
static struct VFile* bios;
static struct GBAAVStream stream;
static int rumbleLevel;
static struct CircleBuffer rumbleHistory;
@ -162,53 +155,49 @@ void retro_init(void) {
stream.postAudioBuffer = _postAudioBuffer;
stream.postVideoFrame = _postVideoFrame;
GBACreate(&gba);
ARMSetComponents(&cpu, &gba.d, 0, 0);
ARMInit(&cpu);
gba.logLevel = 0; // TODO: Settings
gba.logHandler = GBARetroLog;
gba.stream = &stream;
gba.idleOptimization = IDLE_LOOP_REMOVE; // TODO: Settings
GBAContextInit(&context, 0);
struct GBAOptions opts = {
.useBios = true,
.logLevel = 0,
.idleOptimization = IDLE_LOOP_REMOVE
};
GBAConfigLoadDefaults(&context.config, &opts);
context.gba->logHandler = GBARetroLog;
context.gba->stream = &stream;
if (rumbleCallback) {
gba.rumble = &rumble;
context.gba->rumble = &rumble;
}
gba.luminanceSource = &lux;
rom = 0;
context.gba->luminanceSource = &lux;
const char* sysDir = 0;
if (environCallback(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &sysDir)) {
char biosPath[PATH_MAX];
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) {
GBALoadBIOS(&gba, bios);
GBAContextLoadBIOSFromVFile(&context, bios);
}
}
GBAVideoSoftwareRendererCreate(&renderer);
renderer.outputBuffer = malloc(256 * VIDEO_VERTICAL_PIXELS * BYTES_PER_PIXEL);
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
blip_set_rates(gba.audio.left, GBA_ARM7TDMI_FREQUENCY, 32768);
blip_set_rates(gba.audio.right, GBA_ARM7TDMI_FREQUENCY, 32768);
blip_set_rates(context.gba->audio.left, GBA_ARM7TDMI_FREQUENCY, 32768);
blip_set_rates(context.gba->audio.right, GBA_ARM7TDMI_FREQUENCY, 32768);
#endif
}
void retro_deinit(void) {
if (bios) {
bios->close(bios);
bios = 0;
}
GBADestroy(&gba);
GBAContextDeinit(&context);
}
void retro_run(void) {
int keys;
gba.keySource = &keys;
uint16_t keys;
inputPollCallback();
keys = 0;
@ -243,14 +232,11 @@ void retro_run(void) {
}
}
int frameCount = gba.video.frameCounter;
while (gba.video.frameCounter == frameCount) {
ARMRunLoop(&cpu);
}
GBAContextFrame(&context, keys);
}
void retro_reset(void) {
ARMReset(&cpu);
ARMReset(context.cpu);
if (rumbleCallback) {
CircleBufferClear(&rumbleHistory);
@ -258,6 +244,7 @@ void retro_reset(void) {
}
bool retro_load_game(const struct retro_game_info* game) {
struct VFile* rom;
if (game->data) {
data = malloc(game->size);
memcpy(data, game->data, game->size);
@ -270,26 +257,23 @@ bool retro_load_game(const struct retro_game_info* game) {
return false;
}
if (!GBAIsROM(rom)) {
rom->close(rom);
free(data);
return false;
}
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);
GBAOverrideApplyDefaults(&gba);
ARMReset(&cpu);
GBAContextLoadROMFromVFile(&context, rom, save);
GBAContextStart(&context);
return true;
}
void retro_unload_game(void) {
rom->close(rom);
rom = 0;
GBAContextStop(&context);
free(data);
data = 0;
save->close(save);
save = 0;
free(savedata);
savedata = 0;
CircleBufferDeinit(&rumbleHistory);
@ -303,7 +287,7 @@ bool retro_serialize(void* data, size_t size) {
if (size != retro_serialize_size()) {
return false;
}
GBASerialize(&gba, data);
GBASerialize(context.gba, data);
return true;
}
@ -311,7 +295,7 @@ bool retro_unserialize(const void* data, size_t size) {
if (size != retro_serialize_size()) {
return false;
}
GBADeserialize(&gba, data);
GBADeserialize(context.gba, data);
return true;
}
@ -353,7 +337,7 @@ size_t retro_get_memory_size(unsigned id) {
if (id != RETRO_MEMORY_SAVE_RAM) {
return 0;
}
switch (gba.memory.savedata.type) {
switch (context.gba->memory.savedata.type) {
case SAVEDATA_AUTODETECT:
case SAVEDATA_FLASH1M:
return SIZE_CART_FLASH1M;