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 #define BYTES_PER_PIXEL 4
#endif #endif
struct mCoreSync;
struct mCore { struct mCore {
void* cpu; void* cpu;
void* board; void* board;
@ -26,6 +27,8 @@ struct mCore {
bool (*init)(struct mCore*); bool (*init)(struct mCore*);
void (*deinit)(struct mCore*); void (*deinit)(struct mCore*);
void (*setSync)(struct mCore*, struct mCoreSync*);
void (*desiredVideoDimensions)(struct mCore*, unsigned* width, unsigned* height); void (*desiredVideoDimensions)(struct mCore*, unsigned* width, unsigned* height);
void (*setVideoBuffer)(struct mCore*, color_t* buffer, size_t stride); 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "log.h" #include "log.h"
#include "core/thread.h"
#define MAX_CATEGORY 64 #define MAX_CATEGORY 64
struct mLogger* mLogGetContext(void) { struct mLogger* mLogGetContext(void) {
struct mLogger* logger = mCoreThreadLogger();
if (logger) {
return logger;
}
return NULL; // TODO return NULL; // TODO
} }

View File

@ -19,8 +19,7 @@ enum mLogLevel {
}; };
struct mLogger { struct mLogger {
ATTRIBUTE_FORMAT(printf, 4, 5) void (*log)(struct mLogger*, int category, enum mLogLevel level, const char* format, va_list args);
void (*log)(struct mLogger*, int category, enum mLogLevel level, const char* format, ...);
}; };
struct mLogger* mLogGetContext(void); 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)); 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) { static void _GBCoreDesiredVideoDimensions(struct mCore* core, unsigned* width, unsigned* height) {
UNUSED(core); UNUSED(core);
*width = GB_VIDEO_HORIZONTAL_PIXELS; *width = GB_VIDEO_HORIZONTAL_PIXELS;
@ -136,6 +141,7 @@ struct mCore* GBCoreCreate(void) {
core->board = 0; core->board = 0;
core->init = _GBCoreInit; core->init = _GBCoreInit;
core->deinit = _GBCoreDeinit; core->deinit = _GBCoreDeinit;
core->setSync = _GBCoreSetSync;
core->desiredVideoDimensions = _GBCoreDesiredVideoDimensions; core->desiredVideoDimensions = _GBCoreDesiredVideoDimensions;
core->setVideoBuffer = _GBCoreSetVideoBuffer; core->setVideoBuffer = _GBCoreSetVideoBuffer;
core->isROM = _GBCoreIsROM; core->isROM = _GBCoreIsROM;

View File

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

View File

@ -10,6 +10,7 @@
#include "core/directories.h" #include "core/directories.h"
#include "core/sync.h" #include "core/sync.h"
#include "core/thread.h"
#include "gba/gba.h" #include "gba/gba.h"
#include "gba/input.h" #include "gba/input.h"
#include "gba/context/overrides.h" #include "gba/context/overrides.h"
@ -21,26 +22,12 @@ struct GBAArguments;
struct GBACheatSet; struct GBACheatSet;
struct GBAOptions; struct GBAOptions;
typedef void (*ThreadCallback)(struct GBAThread* threadContext); typedef void (*GBAThreadCallback)(struct GBAThread* threadContext);
typedef bool (*ThreadStopCallback)(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 { struct GBAThread {
// Output // Output
enum ThreadState state; enum mCoreThreadState state;
struct GBA* gba; struct GBA* gba;
struct ARMCore* cpu; struct ARMCore* cpu;
@ -80,15 +67,15 @@ struct GBAThread {
Mutex stateMutex; Mutex stateMutex;
Condition stateCond; Condition stateCond;
enum ThreadState savedState; enum mCoreThreadState savedState;
int interruptDepth; int interruptDepth;
bool frameWasOn; bool frameWasOn;
GBALogHandler logHandler; GBALogHandler logHandler;
int logLevel; int logLevel;
ThreadCallback startCallback; GBAThreadCallback startCallback;
ThreadCallback cleanCallback; GBAThreadCallback cleanCallback;
ThreadCallback frameCallback; GBAThreadCallback frameCallback;
ThreadStopCallback stopCallback; ThreadStopCallback stopCallback;
void* userData; void* userData;
void (*run)(struct GBAThread*); void (*run)(struct GBAThread*);

View File

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

View File

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

View File

@ -47,6 +47,7 @@ bool GBSDLInitAudio(struct GBSDLAudio* context, struct GBAThread* threadContext)
if (context->samples > threadContext->audioBuffers) { if (context->samples > threadContext->audioBuffers) {
threadContext->audioBuffers = context->samples * 2; threadContext->audioBuffers = context->samples * 2;
} }
context->sync = &threadContext->sync;
#if SDL_VERSION_ATLEAST(2, 0, 0) #if SDL_VERSION_ATLEAST(2, 0, 0)
SDL_PauseAudioDevice(context->deviceId, 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); blip_read_samples(psg->right, ((short*) data) + 1, available, 1);
} }
if (audioContext->thread) { if (audioContext->sync) {
mCoreSyncConsumeAudio(&audioContext->thread->sync); mCoreSyncConsumeAudio(audioContext->sync);
} }
if (available < len) { if (available < len) {
memset(((short*) data) + audioContext->obtainedSpec.channels * available, 0, (len - available) * audioContext->obtainedSpec.channels * sizeof(short)); 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 "sdl-events.h"
#include "core/input.h" #include "core/input.h"
#include "core/thread.h"
#include "debugger/debugger.h" #include "debugger/debugger.h"
#include "gba/input.h" #include "gba/input.h"
#include "gba/io.h" #include "gba/io.h"
@ -542,7 +543,7 @@ static void _mSDLHandleJoyAxisGBA(struct GBAThread* context, struct mSDLPlayer*
context->activeKeys = keys; 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; int key = -1;
if (!event->keysym.mod) { if (!event->keysym.mod) {
#if !defined(BUILD_PANDORA) && SDL_VERSION_ATLEAST(2, 0, 0) #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 (key != -1) {
if (event->type == SDL_KEYDOWN) { if (event->type == SDL_KEYDOWN) {
core->addKeys(core, 1 << key); context->core->addKeys(context->core, 1 << key);
} else { } else {
core->clearKeys(core, 1 << key); context->core->clearKeys(context->core, 1 << key);
} }
return; return;
} }
if (event->keysym.sym == SDLK_TAB) {
context->sync.audioWait = event->type != SDL_KEYDOWN;
return;
}
// TODO: Put back events // 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) { switch (event->type) {
case SDL_QUIT: case SDL_QUIT:
// TODO mCoreThreadEnd(context);
break; break;
#if SDL_VERSION_ATLEAST(2, 0, 0) #if SDL_VERSION_ATLEAST(2, 0, 0)
case SDL_WINDOWEVENT: case SDL_WINDOWEVENT:
@ -638,17 +643,17 @@ void mSDLHandleEvent(struct mCore* core, struct mSDLPlayer* sdlContext, const un
#endif #endif
case SDL_KEYDOWN: case SDL_KEYDOWN:
case SDL_KEYUP: case SDL_KEYUP:
_mSDLHandleKeypress(core, sdlContext, &event->key); _mSDLHandleKeypress(context, sdlContext, &event->key);
break; break;
case SDL_JOYBUTTONDOWN: case SDL_JOYBUTTONDOWN:
case SDL_JOYBUTTONUP: case SDL_JOYBUTTONUP:
_mSDLHandleJoyButton(core, sdlContext, &event->jbutton); _mSDLHandleJoyButton(context->core, sdlContext, &event->jbutton);
break; break;
case SDL_JOYHATMOTION: case SDL_JOYHATMOTION:
// TODO // TODO
break; break;
case SDL_JOYAXISMOTION: case SDL_JOYAXISMOTION:
_mSDLHandleJoyAxis(core, sdlContext, &event->jaxis); _mSDLHandleJoyAxis(context->core, sdlContext, &event->jaxis);
break; break;
} }
} }

View File

@ -97,8 +97,8 @@ struct GBAThread;
void mSDLInitBindingsGBA(struct mInputMap* inputMap); void mSDLInitBindingsGBA(struct mInputMap* inputMap);
void mSDLHandleEventGBA(struct GBAThread* context, struct mSDLPlayer* sdlContext, const union SDL_Event* event); void mSDLHandleEventGBA(struct GBAThread* context, struct mSDLPlayer* sdlContext, const union SDL_Event* event);
struct mCore; struct mCoreThread;
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);
#if SDL_VERSION_ATLEAST(2, 0, 0) #if SDL_VERSION_ATLEAST(2, 0, 0)
void mSDLSuspendScreensaver(struct mSDLEvents*); void mSDLSuspendScreensaver(struct mSDLEvents*);