Core: Add mCoreThread

This commit is contained in:
Jeffrey Pfau 2016-02-03 20:03:04 -08:00
parent cfd031f140
commit 192f85259a
14 changed files with 536 additions and 46 deletions

View File

@ -19,6 +19,7 @@ typedef uint32_t color_t;
#define BYTES_PER_PIXEL 4
#endif
struct mCoreSync;
struct mCore {
void* cpu;
void* board;
@ -26,6 +27,8 @@ struct mCore {
bool (*init)(struct mCore*);
void (*deinit)(struct mCore*);
void (*setSync)(struct mCore*, struct mCoreSync*);
void (*desiredVideoDimensions)(struct mCore*, unsigned* width, unsigned* height);
void (*setVideoBuffer)(struct mCore*, color_t* buffer, size_t stride);

View File

@ -5,9 +5,15 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "log.h"
#include "core/thread.h"
#define MAX_CATEGORY 64
struct mLogger* mLogGetContext(void) {
struct mLogger* logger = mCoreThreadLogger();
if (logger) {
return logger;
}
return NULL; // TODO
}

View File

@ -19,8 +19,7 @@ enum mLogLevel {
};
struct mLogger {
ATTRIBUTE_FORMAT(printf, 4, 5)
void (*log)(struct mLogger*, int category, enum mLogLevel level, const char* format, ...);
void (*log)(struct mLogger*, int category, enum mLogLevel level, const char* format, va_list args);
};
struct mLogger* mLogGetContext(void);

392
src/core/thread.c Normal file
View File

@ -0,0 +1,392 @@
/* Copyright (c) 2013-2016 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 "thread.h"
#include "core/core.h"
#include "util/patch.h"
#include "util/vfs.h"
#include "platform/commandline.h"
#include <signal.h>
#ifndef DISABLE_THREADING
#ifdef USE_PTHREADS
static pthread_key_t _contextKey;
static pthread_once_t _contextOnce = PTHREAD_ONCE_INIT;
static void _createTLS(void) {
pthread_key_create(&_contextKey, 0);
}
#elif _WIN32
static DWORD _contextKey;
static INIT_ONCE _contextOnce = INIT_ONCE_STATIC_INIT;
static BOOL CALLBACK _createTLS(PINIT_ONCE once, PVOID param, PVOID* context) {
UNUSED(once);
UNUSED(param);
UNUSED(context);
_contextKey = TlsAlloc();
return TRUE;
}
#endif
static void _changeState(struct mCoreThread* threadContext, enum mCoreThreadState newState, bool broadcast) {
MutexLock(&threadContext->stateMutex);
threadContext->state = newState;
if (broadcast) {
ConditionWake(&threadContext->stateCond);
}
MutexUnlock(&threadContext->stateMutex);
}
static void _waitOnInterrupt(struct mCoreThread* threadContext) {
while (threadContext->state == THREAD_INTERRUPTED) {
ConditionWait(&threadContext->stateCond, &threadContext->stateMutex);
}
}
static void _waitUntilNotState(struct mCoreThread* threadContext, enum mCoreThreadState oldState) {
while (threadContext->state == oldState) {
MutexUnlock(&threadContext->stateMutex);
MutexLock(&threadContext->stateMutex);
ConditionWake(&threadContext->stateCond);
}
}
static void _pauseThread(struct mCoreThread* threadContext, bool onThread) {
threadContext->state = THREAD_PAUSING;
if (!onThread) {
_waitUntilNotState(threadContext, THREAD_PAUSING);
}
}
static THREAD_ENTRY _mCoreThreadRun(void* context) {
struct mCoreThread* threadContext = context;
#ifdef USE_PTHREADS
pthread_once(&_contextOnce, _createTLS);
pthread_setspecific(_contextKey, threadContext);
#elif _WIN32
InitOnceExecuteOnce(&_contextOnce, _createTLS, NULL, 0);
TlsSetValue(_contextKey, threadContext);
#endif
ThreadSetName("CPU Thread");
#if !defined(_WIN32) && defined(USE_PTHREADS)
sigset_t signals;
sigemptyset(&signals);
pthread_sigmask(SIG_SETMASK, &signals, 0);
#endif
struct mCore* core = threadContext->core;
core->setSync(core, &threadContext->sync);
core->reset(core);
if (threadContext->startCallback) {
threadContext->startCallback(threadContext);
}
_changeState(threadContext, THREAD_RUNNING, true);
while (threadContext->state < THREAD_EXITING) {
core->runLoop(core);
int resetScheduled = 0;
MutexLock(&threadContext->stateMutex);
while (threadContext->state > THREAD_RUNNING && threadContext->state < THREAD_EXITING) {
if (threadContext->state == THREAD_PAUSING) {
threadContext->state = THREAD_PAUSED;
ConditionWake(&threadContext->stateCond);
}
if (threadContext->state == THREAD_INTERRUPTING) {
threadContext->state = THREAD_INTERRUPTED;
ConditionWake(&threadContext->stateCond);
}
if (threadContext->state == THREAD_RUN_ON) {
if (threadContext->run) {
threadContext->run(threadContext);
}
threadContext->state = threadContext->savedState;
ConditionWake(&threadContext->stateCond);
}
if (threadContext->state == THREAD_RESETING) {
threadContext->state = THREAD_RUNNING;
resetScheduled = 1;
}
while (threadContext->state == THREAD_PAUSED || threadContext->state == THREAD_INTERRUPTED) {
ConditionWait(&threadContext->stateCond, &threadContext->stateMutex);
}
}
MutexUnlock(&threadContext->stateMutex);
if (resetScheduled) {
core->reset(core);
}
}
while (threadContext->state < THREAD_SHUTDOWN) {
_changeState(threadContext, THREAD_SHUTDOWN, false);
}
if (threadContext->cleanCallback) {
threadContext->cleanCallback(threadContext);
}
return 0;
}
bool mCoreThreadStart(struct mCoreThread* threadContext) {
threadContext->state = THREAD_INITIALIZED;
MutexInit(&threadContext->stateMutex);
ConditionInit(&threadContext->stateCond);
MutexInit(&threadContext->sync.videoFrameMutex);
ConditionInit(&threadContext->sync.videoFrameAvailableCond);
ConditionInit(&threadContext->sync.videoFrameRequiredCond);
MutexInit(&threadContext->sync.audioBufferMutex);
ConditionInit(&threadContext->sync.audioRequiredCond);
threadContext->interruptDepth = 0;
#ifdef USE_PTHREADS
sigset_t signals;
sigemptyset(&signals);
sigaddset(&signals, SIGINT);
sigaddset(&signals, SIGTRAP);
pthread_sigmask(SIG_BLOCK, &signals, 0);
#endif
MutexLock(&threadContext->stateMutex);
ThreadCreate(&threadContext->thread, _mCoreThreadRun, threadContext);
while (threadContext->state < THREAD_RUNNING) {
ConditionWait(&threadContext->stateCond, &threadContext->stateMutex);
}
MutexUnlock(&threadContext->stateMutex);
return true;
}
bool mCoreThreadHasStarted(struct mCoreThread* threadContext) {
bool hasStarted;
MutexLock(&threadContext->stateMutex);
hasStarted = threadContext->state > THREAD_INITIALIZED;
MutexUnlock(&threadContext->stateMutex);
return hasStarted;
}
bool mCoreThreadHasExited(struct mCoreThread* threadContext) {
bool hasExited;
MutexLock(&threadContext->stateMutex);
hasExited = threadContext->state > THREAD_EXITING;
MutexUnlock(&threadContext->stateMutex);
return hasExited;
}
bool mCoreThreadHasCrashed(struct mCoreThread* threadContext) {
bool hasExited;
MutexLock(&threadContext->stateMutex);
hasExited = threadContext->state == THREAD_CRASHED;
MutexUnlock(&threadContext->stateMutex);
return hasExited;
}
void mCoreThreadEnd(struct mCoreThread* threadContext) {
MutexLock(&threadContext->stateMutex);
_waitOnInterrupt(threadContext);
threadContext->state = THREAD_EXITING;
ConditionWake(&threadContext->stateCond);
MutexUnlock(&threadContext->stateMutex);
MutexLock(&threadContext->sync.audioBufferMutex);
threadContext->sync.audioWait = 0;
ConditionWake(&threadContext->sync.audioRequiredCond);
MutexUnlock(&threadContext->sync.audioBufferMutex);
MutexLock(&threadContext->sync.videoFrameMutex);
threadContext->sync.videoFrameWait = false;
threadContext->sync.videoFrameOn = false;
ConditionWake(&threadContext->sync.videoFrameRequiredCond);
ConditionWake(&threadContext->sync.videoFrameAvailableCond);
MutexUnlock(&threadContext->sync.videoFrameMutex);
}
void mCoreThreadReset(struct mCoreThread* threadContext) {
MutexLock(&threadContext->stateMutex);
_waitOnInterrupt(threadContext);
threadContext->state = THREAD_RESETING;
ConditionWake(&threadContext->stateCond);
MutexUnlock(&threadContext->stateMutex);
}
void mCoreThreadJoin(struct mCoreThread* threadContext) {
ThreadJoin(threadContext->thread);
MutexDeinit(&threadContext->stateMutex);
ConditionDeinit(&threadContext->stateCond);
MutexDeinit(&threadContext->sync.videoFrameMutex);
ConditionWake(&threadContext->sync.videoFrameAvailableCond);
ConditionDeinit(&threadContext->sync.videoFrameAvailableCond);
ConditionWake(&threadContext->sync.videoFrameRequiredCond);
ConditionDeinit(&threadContext->sync.videoFrameRequiredCond);
ConditionWake(&threadContext->sync.audioRequiredCond);
ConditionDeinit(&threadContext->sync.audioRequiredCond);
MutexDeinit(&threadContext->sync.audioBufferMutex);
}
bool mCoreThreadIsActive(struct mCoreThread* threadContext) {
return threadContext->state >= THREAD_RUNNING && threadContext->state < THREAD_EXITING;
}
void mCoreThreadInterrupt(struct mCoreThread* threadContext) {
if (!threadContext) {
return;
}
MutexLock(&threadContext->stateMutex);
++threadContext->interruptDepth;
if (threadContext->interruptDepth > 1 || !mCoreThreadIsActive(threadContext)) {
MutexUnlock(&threadContext->stateMutex);
return;
}
threadContext->savedState = threadContext->state;
_waitOnInterrupt(threadContext);
threadContext->state = THREAD_INTERRUPTING;
ConditionWake(&threadContext->stateCond);
_waitUntilNotState(threadContext, THREAD_INTERRUPTING);
MutexUnlock(&threadContext->stateMutex);
}
void mCoreThreadContinue(struct mCoreThread* threadContext) {
if (!threadContext) {
return;
}
MutexLock(&threadContext->stateMutex);
--threadContext->interruptDepth;
if (threadContext->interruptDepth < 1 && mCoreThreadIsActive(threadContext)) {
threadContext->state = threadContext->savedState;
ConditionWake(&threadContext->stateCond);
}
MutexUnlock(&threadContext->stateMutex);
}
void mCoreThreadRunFunction(struct mCoreThread* threadContext, void (*run)(struct mCoreThread*)) {
MutexLock(&threadContext->stateMutex);
threadContext->run = run;
_waitOnInterrupt(threadContext);
threadContext->savedState = threadContext->state;
threadContext->state = THREAD_RUN_ON;
ConditionWake(&threadContext->stateCond);
_waitUntilNotState(threadContext, THREAD_RUN_ON);
MutexUnlock(&threadContext->stateMutex);
}
void mCoreThreadPause(struct mCoreThread* threadContext) {
bool frameOn = threadContext->sync.videoFrameOn;
MutexLock(&threadContext->stateMutex);
_waitOnInterrupt(threadContext);
if (threadContext->state == THREAD_RUNNING) {
_pauseThread(threadContext, false);
threadContext->frameWasOn = frameOn;
frameOn = false;
}
MutexUnlock(&threadContext->stateMutex);
mCoreSyncSetVideoSync(&threadContext->sync, frameOn);
}
void mCoreThreadUnpause(struct mCoreThread* threadContext) {
bool frameOn = threadContext->sync.videoFrameOn;
MutexLock(&threadContext->stateMutex);
_waitOnInterrupt(threadContext);
if (threadContext->state == THREAD_PAUSED || threadContext->state == THREAD_PAUSING) {
threadContext->state = THREAD_RUNNING;
ConditionWake(&threadContext->stateCond);
frameOn = threadContext->frameWasOn;
}
MutexUnlock(&threadContext->stateMutex);
mCoreSyncSetVideoSync(&threadContext->sync, frameOn);
}
bool mCoreThreadIsPaused(struct mCoreThread* threadContext) {
bool isPaused;
MutexLock(&threadContext->stateMutex);
_waitOnInterrupt(threadContext);
isPaused = threadContext->state == THREAD_PAUSED;
MutexUnlock(&threadContext->stateMutex);
return isPaused;
}
void mCoreThreadTogglePause(struct mCoreThread* threadContext) {
bool frameOn = threadContext->sync.videoFrameOn;
MutexLock(&threadContext->stateMutex);
_waitOnInterrupt(threadContext);
if (threadContext->state == THREAD_PAUSED || threadContext->state == THREAD_PAUSING) {
threadContext->state = THREAD_RUNNING;
ConditionWake(&threadContext->stateCond);
frameOn = threadContext->frameWasOn;
} else if (threadContext->state == THREAD_RUNNING) {
_pauseThread(threadContext, false);
threadContext->frameWasOn = frameOn;
frameOn = false;
}
MutexUnlock(&threadContext->stateMutex);
mCoreSyncSetVideoSync(&threadContext->sync, frameOn);
}
void mCoreThreadPauseFromThread(struct mCoreThread* threadContext) {
bool frameOn = true;
MutexLock(&threadContext->stateMutex);
_waitOnInterrupt(threadContext);
if (threadContext->state == THREAD_RUNNING) {
_pauseThread(threadContext, true);
frameOn = false;
}
MutexUnlock(&threadContext->stateMutex);
mCoreSyncSetVideoSync(&threadContext->sync, frameOn);
}
#ifdef USE_PTHREADS
struct mCoreThread* mCoreThreadGet(void) {
pthread_once(&_contextOnce, _createTLS);
return pthread_getspecific(_contextKey);
}
#elif _WIN32
struct mCoreThread* mCoreThreadGet(void) {
InitOnceExecuteOnce(&_contextOnce, _createTLS, NULL, 0);
return TlsGetValue(_contextKey);
}
#endif
#else
struct mCoreThread* mCoreThreadGet(void) {
return NULL;
}
#endif
static void _mCoreLog(struct mLogger* logger, int category, enum mLogLevel level, const char* format, va_list args) {
printf("%s: ", mLogCategoryName(category));
vprintf(format, args);
printf("\n");
}
struct mLogger* mCoreThreadLogger(void) {
struct mCoreThread* thread = mCoreThreadGet();
if (thread) {
if (!thread->logger.log) {
thread->logger.log = _mCoreLog;
}
return &thread->logger;
}
return NULL;
}

82
src/core/thread.h Normal file
View File

@ -0,0 +1,82 @@
/* Copyright (c) 2013-2016 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 M_CORE_THREAD_H
#define M_CORE_THREAD_H
#include "util/common.h"
#include "core/log.h"
#include "core/sync.h"
#include "util/threading.h"
struct mCoreThread;
struct mCore;
typedef void (*ThreadCallback)(struct mCoreThread* threadContext);
enum mCoreThreadState {
THREAD_INITIALIZED = -1,
THREAD_RUNNING = 0,
THREAD_INTERRUPTED,
THREAD_INTERRUPTING,
THREAD_PAUSED,
THREAD_PAUSING,
THREAD_RUN_ON,
THREAD_RESETING,
THREAD_EXITING,
THREAD_SHUTDOWN,
THREAD_CRASHED
};
struct mCoreThread {
// Input
struct mCore* core;
// Threading state
Thread thread;
enum mCoreThreadState state;
Mutex stateMutex;
Condition stateCond;
enum mCoreThreadState savedState;
int interruptDepth;
bool frameWasOn;
struct mLogger logger;
enum mLogLevel logLevel;
ThreadCallback startCallback;
ThreadCallback cleanCallback;
ThreadCallback frameCallback;
void* userData;
void (*run)(struct mCoreThread*);
struct mCoreSync sync;
};
bool mCoreThreadStart(struct mCoreThread* threadContext);
bool mCoreThreadHasStarted(struct mCoreThread* threadContext);
bool mCoreThreadHasExited(struct mCoreThread* threadContext);
bool mCoreThreadHasCrashed(struct mCoreThread* threadContext);
void mCoreThreadEnd(struct mCoreThread* threadContext);
void mCoreThreadReset(struct mCoreThread* threadContext);
void mCoreThreadJoin(struct mCoreThread* threadContext);
bool mCoreThreadIsActive(struct mCoreThread* threadContext);
void mCoreThreadInterrupt(struct mCoreThread* threadContext);
void mCoreThreadContinue(struct mCoreThread* threadContext);
void mCoreThreadRunFunction(struct mCoreThread* threadContext, void (*run)(struct mCoreThread*));
void mCoreThreadPause(struct mCoreThread* threadContext);
void mCoreThreadUnpause(struct mCoreThread* threadContext);
bool mCoreThreadIsPaused(struct mCoreThread* threadContext);
void mCoreThreadTogglePause(struct mCoreThread* threadContext);
void mCoreThreadPauseFromThread(struct mCoreThread* threadContext);
struct mCoreThread* mCoreThreadGet(void);
struct mLogger* mCoreThreadLogger(void);
#endif

View File

@ -48,6 +48,11 @@ static void _GBCoreDeinit(struct mCore* core) {
mappedMemoryFree(core->board, sizeof(struct GB));
}
static void _GBCoreSetSync(struct mCore* core, struct mCoreSync* sync) {
struct GB* gb = core->board;
gb->sync = sync;
}
static void _GBCoreDesiredVideoDimensions(struct mCore* core, unsigned* width, unsigned* height) {
UNUSED(core);
*width = GB_VIDEO_HORIZONTAL_PIXELS;
@ -136,6 +141,7 @@ struct mCore* GBCoreCreate(void) {
core->board = 0;
core->init = _GBCoreInit;
core->deinit = _GBCoreDeinit;
core->setSync = _GBCoreSetSync;
core->desiredVideoDimensions = _GBCoreDesiredVideoDimensions;
core->setVideoBuffer = _GBCoreSetVideoBuffer;
core->isROM = _GBCoreIsROM;

View File

@ -5,6 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "video.h"
#include "core/sync.h"
#include "gb/gb.h"
#include "gb/io.h"
@ -112,6 +113,7 @@ int32_t GBVideoProcessEvents(struct GBVideo* video, int32_t cycles) {
video->mode = 1;
++video->frameCounter;
video->renderer->finishFrame(video->renderer);
mCoreSyncPostFrame(video->p->sync);
if (GBRegisterSTATIsVblankIRQ(video->stat) || GBRegisterSTATIsOAMIRQ(video->stat)) {
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
}

View File

@ -77,7 +77,7 @@ static BOOL CALLBACK _createTLS(PINIT_ONCE once, PVOID param, PVOID* context) {
}
#endif
static void _changeState(struct GBAThread* threadContext, enum ThreadState newState, bool broadcast) {
static void _changeState(struct GBAThread* threadContext, enum mCoreThreadState newState, bool broadcast) {
MutexLock(&threadContext->stateMutex);
threadContext->state = newState;
if (broadcast) {
@ -92,7 +92,7 @@ static void _waitOnInterrupt(struct GBAThread* threadContext) {
}
}
static void _waitUntilNotState(struct GBAThread* threadContext, enum ThreadState oldState) {
static void _waitUntilNotState(struct GBAThread* threadContext, enum mCoreThreadState oldState) {
MutexLock(&threadContext->sync.videoFrameMutex);
bool videoFrameWait = threadContext->sync.videoFrameWait;
threadContext->sync.videoFrameWait = false;

View File

@ -10,6 +10,7 @@
#include "core/directories.h"
#include "core/sync.h"
#include "core/thread.h"
#include "gba/gba.h"
#include "gba/input.h"
#include "gba/context/overrides.h"
@ -21,26 +22,12 @@ struct GBAArguments;
struct GBACheatSet;
struct GBAOptions;
typedef void (*ThreadCallback)(struct GBAThread* threadContext);
typedef void (*GBAThreadCallback)(struct GBAThread* threadContext);
typedef bool (*ThreadStopCallback)(struct GBAThread* threadContext);
enum ThreadState {
THREAD_INITIALIZED = -1,
THREAD_RUNNING = 0,
THREAD_INTERRUPTED,
THREAD_INTERRUPTING,
THREAD_PAUSED,
THREAD_PAUSING,
THREAD_RUN_ON,
THREAD_RESETING,
THREAD_EXITING,
THREAD_SHUTDOWN,
THREAD_CRASHED
};
struct GBAThread {
// Output
enum ThreadState state;
enum mCoreThreadState state;
struct GBA* gba;
struct ARMCore* cpu;
@ -80,15 +67,15 @@ struct GBAThread {
Mutex stateMutex;
Condition stateCond;
enum ThreadState savedState;
enum mCoreThreadState savedState;
int interruptDepth;
bool frameWasOn;
GBALogHandler logHandler;
int logLevel;
ThreadCallback startCallback;
ThreadCallback cleanCallback;
ThreadCallback frameCallback;
GBAThreadCallback startCallback;
GBAThreadCallback cleanCallback;
GBAThreadCallback frameCallback;
ThreadStopCallback stopCallback;
void* userData;
void (*run)(struct GBAThread*);

View File

@ -129,18 +129,14 @@ bool mSDLGLInitGB(struct mSDLRenderer* renderer) {
}
void mSDLGLRunloopGB(struct mSDLRenderer* renderer, void* user) {
UNUSED(user);
struct mCoreThread* context = user;
SDL_Event event;
struct VideoBackend* v = &renderer->gl.d;
renderer->audio.psg = &((struct GB*) renderer->core->board)->audio;
while (true) {
renderer->core->runFrame(renderer->core);
while (context->state < THREAD_EXITING) {
while (SDL_PollEvent(&event)) {
mSDLHandleEvent(renderer->core, &renderer->player, &event);
if (event.type == SDL_QUIT) {
return;
}
mSDLHandleEvent(context, &renderer->player, &event);
#if SDL_VERSION_ATLEAST(2, 0, 0)
// Event handling can change the size of the screen
if (renderer->player.windowUpdated) {
@ -151,7 +147,10 @@ void mSDLGLRunloopGB(struct mSDLRenderer* renderer, void* user) {
#endif
}
v->postFrame(v, renderer->outputBuffer);
if (mCoreSyncWaitFrameStart(&context->sync)) {
v->postFrame(v, renderer->outputBuffer);
}
mCoreSyncWaitFrameEnd(&context->sync);
v->drawFrame(v);
v->swap(v);
}

View File

@ -15,6 +15,7 @@
#include "core/core.h"
#include "core/config.h"
#include "core/thread.h"
#ifdef M_CORE_GBA
#include "gba/gba.h"
#include "gba/supervisor/thread.h"
@ -277,6 +278,12 @@ int mSDLRunGBA(struct mSDLRenderer* renderer, struct GBAArguments* args, struct
#ifdef M_CORE_GB
int mSDLRunGB(struct mSDLRenderer* renderer, struct GBAArguments* args) {
struct mCoreThread thread = {
.core = renderer->core,
.sync = {
.audioWait = true
}
};
struct VFile* vf = VFileOpen(args->fname, O_RDONLY);
struct VFile* savVf = 0;
@ -284,6 +291,7 @@ int mSDLRunGB(struct mSDLRenderer* renderer, struct GBAArguments* args) {
renderer->audio.sampleRate = 44100;
GBSDLInitAudio(&renderer->audio, 0);
renderer->audio.sync = &thread.sync;
{
char savepath[PATH_MAX];
@ -295,10 +303,10 @@ int mSDLRunGB(struct mSDLRenderer* renderer, struct GBAArguments* args) {
}
renderer->core->loadROM(renderer->core, vf, savVf, args->fname);
renderer->core->reset(renderer->core);
mCoreThreadStart(&thread);
renderer->audio.psg = 0;
GBSDLResumeAudio(&renderer->audio);
renderer->runloop(renderer, NULL);
renderer->runloop(renderer, &thread);
renderer->core->unloadROM(renderer->core);
vf->close(vf);
return 0;

View File

@ -47,6 +47,7 @@ bool GBSDLInitAudio(struct GBSDLAudio* context, struct GBAThread* threadContext)
if (context->samples > threadContext->audioBuffers) {
threadContext->audioBuffers = context->samples * 2;
}
context->sync = &threadContext->sync;
#if SDL_VERSION_ATLEAST(2, 0, 0)
SDL_PauseAudioDevice(context->deviceId, 0);
@ -116,8 +117,8 @@ static void _GBSDLAudioCallback(void* context, Uint8* data, int len) {
blip_read_samples(psg->right, ((short*) data) + 1, available, 1);
}
if (audioContext->thread) {
mCoreSyncConsumeAudio(&audioContext->thread->sync);
if (audioContext->sync) {
mCoreSyncConsumeAudio(audioContext->sync);
}
if (available < len) {
memset(((short*) data) + audioContext->obtainedSpec.channels * available, 0, (len - available) * audioContext->obtainedSpec.channels * sizeof(short));

View File

@ -6,6 +6,7 @@
#include "sdl-events.h"
#include "core/input.h"
#include "core/thread.h"
#include "debugger/debugger.h"
#include "gba/input.h"
#include "gba/io.h"
@ -542,7 +543,7 @@ static void _mSDLHandleJoyAxisGBA(struct GBAThread* context, struct mSDLPlayer*
context->activeKeys = keys;
}
static void _mSDLHandleKeypress(struct mCore* core, struct mSDLPlayer* sdlContext, const struct SDL_KeyboardEvent* event) {
static void _mSDLHandleKeypress(struct mCoreThread* context, struct mSDLPlayer* sdlContext, const struct SDL_KeyboardEvent* event) {
int key = -1;
if (!event->keysym.mod) {
#if !defined(BUILD_PANDORA) && SDL_VERSION_ATLEAST(2, 0, 0)
@ -553,12 +554,16 @@ static void _mSDLHandleKeypress(struct mCore* core, struct mSDLPlayer* sdlContex
}
if (key != -1) {
if (event->type == SDL_KEYDOWN) {
core->addKeys(core, 1 << key);
context->core->addKeys(context->core, 1 << key);
} else {
core->clearKeys(core, 1 << key);
context->core->clearKeys(context->core, 1 << key);
}
return;
}
if (event->keysym.sym == SDLK_TAB) {
context->sync.audioWait = event->type != SDL_KEYDOWN;
return;
}
// TODO: Put back events
}
@ -626,10 +631,10 @@ void mSDLHandleEventGBA(struct GBAThread* context, struct mSDLPlayer* sdlContext
}
}
void mSDLHandleEvent(struct mCore* core, struct mSDLPlayer* sdlContext, const union SDL_Event* event) {
void mSDLHandleEvent(struct mCoreThread* context, struct mSDLPlayer* sdlContext, const union SDL_Event* event) {
switch (event->type) {
case SDL_QUIT:
// TODO
mCoreThreadEnd(context);
break;
#if SDL_VERSION_ATLEAST(2, 0, 0)
case SDL_WINDOWEVENT:
@ -638,17 +643,17 @@ void mSDLHandleEvent(struct mCore* core, struct mSDLPlayer* sdlContext, const un
#endif
case SDL_KEYDOWN:
case SDL_KEYUP:
_mSDLHandleKeypress(core, sdlContext, &event->key);
_mSDLHandleKeypress(context, sdlContext, &event->key);
break;
case SDL_JOYBUTTONDOWN:
case SDL_JOYBUTTONUP:
_mSDLHandleJoyButton(core, sdlContext, &event->jbutton);
_mSDLHandleJoyButton(context->core, sdlContext, &event->jbutton);
break;
case SDL_JOYHATMOTION:
// TODO
break;
case SDL_JOYAXISMOTION:
_mSDLHandleJoyAxis(core, sdlContext, &event->jaxis);
_mSDLHandleJoyAxis(context->core, sdlContext, &event->jaxis);
break;
}
}

View File

@ -97,8 +97,8 @@ struct GBAThread;
void mSDLInitBindingsGBA(struct mInputMap* inputMap);
void mSDLHandleEventGBA(struct GBAThread* context, struct mSDLPlayer* sdlContext, const union SDL_Event* event);
struct mCore;
void mSDLHandleEvent(struct mCore* core, struct mSDLPlayer* sdlContext, const union SDL_Event* event);
struct mCoreThread;
void mSDLHandleEvent(struct mCoreThread* context, struct mSDLPlayer* sdlContext, const union SDL_Event* event);
#if SDL_VERSION_ATLEAST(2, 0, 0)
void mSDLSuspendScreensaver(struct mSDLEvents*);