mirror of https://github.com/mgba-emu/mgba.git
Core: Add mCoreThread
This commit is contained in:
parent
cfd031f140
commit
192f85259a
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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*);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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*);
|
||||
|
|
Loading…
Reference in New Issue