all repos — mgba @ 192f85259aa0f0e2c0eff2f48bd091f66ed15518

mGBA Game Boy Advance Emulator

Core: Add mCoreThread
Jeffrey Pfau jeffrey@endrift.com
Wed, 03 Feb 2016 20:03:04 -0800
commit

192f85259aa0f0e2c0eff2f48bd091f66ed15518

parent

cfd031f140d51da742ae5c85e6313f007f2a1cf3

M src/core/core.hsrc/core/core.h

@@ -19,12 +19,15 @@ typedef uint32_t color_t;

#define BYTES_PER_PIXEL 4 #endif +struct mCoreSync; struct mCore { void* cpu; void* board; 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);
M src/core/log.csrc/core/log.c

@@ -5,9 +5,15 @@ * 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 "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 }
M src/core/log.hsrc/core/log.h

@@ -19,8 +19,7 @@ mLOG_GAME_ERROR = 0x40

}; 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);
A src/core/thread.c

@@ -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; +} +
A src/core/thread.h

@@ -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
M src/gb/core.csrc/gb/core.c

@@ -48,6 +48,11 @@ mappedMemoryFree(core->cpu, sizeof(struct LR35902Core));

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 @@ core->cpu = 0;

core->board = 0; core->init = _GBCoreInit; core->deinit = _GBCoreDeinit; + core->setSync = _GBCoreSetSync; core->desiredVideoDimensions = _GBCoreDesiredVideoDimensions; core->setVideoBuffer = _GBCoreSetVideoBuffer; core->isROM = _GBCoreIsROM;
M src/gb/video.csrc/gb/video.c

@@ -5,6 +5,7 @@ * 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 "video.h" +#include "core/sync.h" #include "gb/gb.h" #include "gb/io.h"

@@ -112,6 +113,7 @@ video->nextMode = GB_VIDEO_HORIZONTAL_LENGTH;

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); }
M src/gba/supervisor/thread.csrc/gba/supervisor/thread.c

@@ -77,7 +77,7 @@ return TRUE;

} #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 @@ ConditionWait(&threadContext->stateCond, &threadContext->stateMutex);

} } -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;
M src/gba/supervisor/thread.hsrc/gba/supervisor/thread.h

@@ -10,6 +10,7 @@ #include "util/common.h"

#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 @@ Thread thread;

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*);
M src/platform/sdl/gl-sdl.csrc/platform/sdl/gl-sdl.c

@@ -129,18 +129,14 @@ return true;

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

#endif } - v->postFrame(v, renderer->outputBuffer); + if (mCoreSyncWaitFrameStart(&context->sync)) { + v->postFrame(v, renderer->outputBuffer); + } + mCoreSyncWaitFrameEnd(&context->sync); v->drawFrame(v); v->swap(v); }
M src/platform/sdl/main.csrc/platform/sdl/main.c

@@ -15,6 +15,7 @@ #endif

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

#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 @@ renderer->audio.samples = 1024;

renderer->audio.sampleRate = 44100; GBSDLInitAudio(&renderer->audio, 0); + renderer->audio.sync = &thread.sync; { char savepath[PATH_MAX];

@@ -295,10 +303,10 @@ savVf = VFileOpen(savepath, O_RDWR | O_CREAT);

} 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;
M src/platform/sdl/sdl-audio.csrc/platform/sdl/sdl-audio.c

@@ -47,6 +47,7 @@ threadContext->audioBuffers = context->samples / ratio;

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 @@ if (audioContext->obtainedSpec.channels == 2) {

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));
M src/platform/sdl/sdl-events.csrc/platform/sdl/sdl-events.c

@@ -6,6 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

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

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,10 +554,14 @@ #endif

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

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

#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; } }
M src/platform/sdl/sdl-events.hsrc/platform/sdl/sdl-events.h

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