mirror of https://github.com/mgba-emu/mgba.git
Support pausing
This commit is contained in:
parent
8e43ffed90
commit
a107243c7a
|
@ -158,7 +158,7 @@ static void _printStatus(struct ARMDebugger* debugger, struct DebugVector* dv) {
|
|||
|
||||
static void _quit(struct ARMDebugger* debugger, struct DebugVector* dv) {
|
||||
(void)(dv);
|
||||
debugger->state = DEBUGGER_EXITING;
|
||||
debugger->state = DEBUGGER_SHUTDOWN;
|
||||
}
|
||||
|
||||
static void _readByte(struct ARMDebugger* debugger, struct DebugVector* dv) {
|
||||
|
@ -600,7 +600,10 @@ void ARMDebuggerDeinit(struct ARMDebugger* debugger) {
|
|||
}
|
||||
|
||||
void ARMDebuggerRun(struct ARMDebugger* debugger) {
|
||||
while (debugger->state != DEBUGGER_EXITING) {
|
||||
if (debugger->state == DEBUGGER_EXITING) {
|
||||
debugger->state = DEBUGGER_RUNNING;
|
||||
}
|
||||
while (debugger->state < DEBUGGER_EXITING) {
|
||||
if (!debugger->breakpoints) {
|
||||
while (debugger->state == DEBUGGER_RUNNING) {
|
||||
ARMRun(debugger->cpu);
|
||||
|
@ -612,14 +615,14 @@ void ARMDebuggerRun(struct ARMDebugger* debugger) {
|
|||
}
|
||||
}
|
||||
switch (debugger->state) {
|
||||
case DEBUGGER_RUNNING:
|
||||
break;
|
||||
case DEBUGGER_PAUSED:
|
||||
_commandLine(debugger);
|
||||
break;
|
||||
case DEBUGGER_EXITING:
|
||||
case DEBUGGER_SHUTDOWN:
|
||||
return;
|
||||
default:
|
||||
// Should never be reached
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,8 @@
|
|||
enum DebuggerState {
|
||||
DEBUGGER_PAUSED,
|
||||
DEBUGGER_RUNNING,
|
||||
DEBUGGER_EXITING
|
||||
DEBUGGER_EXITING,
|
||||
DEBUGGER_SHUTDOWN
|
||||
};
|
||||
|
||||
#ifdef USE_DEBUGGER
|
||||
|
|
|
@ -217,8 +217,8 @@ static int _GBAEGLInit(struct GBAVideoEGLRenderer* renderer) {
|
|||
static void _GBAEGLRunloop(struct GBAThread* context, struct GBAVideoEGLRenderer* renderer) {
|
||||
SDL_Event event;
|
||||
|
||||
while (context->started && (!context->debugger || context->debugger->state != DEBUGGER_EXITING)) {
|
||||
GBASyncWaitFrameStart(&context->sync, context->frameskip);
|
||||
while (context->state < THREAD_EXITING) {
|
||||
if (GBASyncWaitFrameStart(&context->sync, context->frameskip)) {
|
||||
glViewport(0, 0, 240, 160);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glUseProgram(renderer->program);
|
||||
|
@ -231,11 +231,12 @@ static void _GBAEGLRunloop(struct GBAThread* context, struct GBAVideoEGLRenderer
|
|||
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
||||
glUseProgram(0);
|
||||
eglSwapBuffers(renderer->display, renderer->surface);
|
||||
}
|
||||
GBASyncWaitFrameEnd(&context->sync);
|
||||
|
||||
while (SDL_PollEvent(&event)) {
|
||||
GBASDLHandleEvent(context, &event);
|
||||
}
|
||||
GBASyncWaitFrameEnd(&context->sync);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -77,23 +77,40 @@ static void* _GBAThreadRun(void* context) {
|
|||
threadContext->startCallback(threadContext);
|
||||
}
|
||||
|
||||
threadContext->started = 1;
|
||||
pthread_mutex_lock(&threadContext->startMutex);
|
||||
pthread_cond_broadcast(&threadContext->startCond);
|
||||
pthread_mutex_unlock(&threadContext->startMutex);
|
||||
pthread_mutex_lock(&threadContext->stateMutex);
|
||||
threadContext->state = THREAD_RUNNING;
|
||||
pthread_cond_broadcast(&threadContext->stateCond);
|
||||
pthread_mutex_unlock(&threadContext->stateMutex);
|
||||
|
||||
while (threadContext->state < THREAD_EXITING) {
|
||||
#ifdef USE_DEBUGGER
|
||||
if (threadContext->useDebugger) {
|
||||
ARMDebuggerRun(&debugger);
|
||||
threadContext->started = 0;
|
||||
if (debugger.state == DEBUGGER_SHUTDOWN) {
|
||||
pthread_mutex_lock(&threadContext->stateMutex);
|
||||
threadContext->state = THREAD_EXITING;
|
||||
pthread_mutex_unlock(&threadContext->stateMutex);
|
||||
}
|
||||
} else {
|
||||
#endif
|
||||
while (threadContext->started) {
|
||||
while (threadContext->state == THREAD_RUNNING) {
|
||||
ARMRun(&gba.cpu);
|
||||
}
|
||||
#ifdef USE_DEBUGGER
|
||||
}
|
||||
#endif
|
||||
while (threadContext->state == THREAD_PAUSED) {
|
||||
pthread_mutex_lock(&threadContext->stateMutex);
|
||||
pthread_cond_wait(&threadContext->stateCond, &threadContext->stateMutex);
|
||||
pthread_mutex_unlock(&threadContext->stateMutex);
|
||||
}
|
||||
}
|
||||
|
||||
while (threadContext->state != THREAD_SHUTDOWN) {
|
||||
pthread_mutex_lock(&threadContext->stateMutex);
|
||||
threadContext->state = THREAD_SHUTDOWN;
|
||||
pthread_mutex_unlock(&threadContext->stateMutex);
|
||||
}
|
||||
|
||||
if (threadContext->cleanCallback) {
|
||||
threadContext->cleanCallback(threadContext);
|
||||
|
@ -110,20 +127,21 @@ static void* _GBAThreadRun(void* context) {
|
|||
|
||||
int GBAThreadStart(struct GBAThread* threadContext) {
|
||||
// TODO: error check
|
||||
pthread_mutex_init(&threadContext->startMutex, 0);
|
||||
pthread_cond_init(&threadContext->startCond, 0);
|
||||
pthread_mutex_init(&threadContext->stateMutex, 0);
|
||||
pthread_cond_init(&threadContext->stateCond, 0);
|
||||
|
||||
pthread_mutex_init(&threadContext->sync.videoFrameMutex, 0);
|
||||
pthread_cond_init(&threadContext->sync.videoFrameAvailableCond, 0);
|
||||
pthread_cond_init(&threadContext->sync.videoFrameRequiredCond, 0);
|
||||
pthread_cond_init(&threadContext->sync.audioRequiredCond, 0);
|
||||
|
||||
pthread_mutex_lock(&threadContext->startMutex);
|
||||
pthread_mutex_lock(&threadContext->stateMutex);
|
||||
threadContext->activeKeys = 0;
|
||||
threadContext->started = 0;
|
||||
threadContext->state = THREAD_INITIALIZED;
|
||||
threadContext->sync.videoFrameOn = 1;
|
||||
pthread_create(&threadContext->thread, 0, _GBAThreadRun, threadContext);
|
||||
pthread_cond_wait(&threadContext->startCond, &threadContext->startMutex);
|
||||
pthread_mutex_unlock(&threadContext->startMutex);
|
||||
pthread_cond_wait(&threadContext->stateCond, &threadContext->stateMutex);
|
||||
pthread_mutex_unlock(&threadContext->stateMutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -136,8 +154,8 @@ void GBAThreadJoin(struct GBAThread* threadContext) {
|
|||
|
||||
pthread_join(threadContext->thread, 0);
|
||||
|
||||
pthread_mutex_destroy(&threadContext->startMutex);
|
||||
pthread_cond_destroy(&threadContext->startCond);
|
||||
pthread_mutex_destroy(&threadContext->stateMutex);
|
||||
pthread_cond_destroy(&threadContext->stateCond);
|
||||
|
||||
pthread_mutex_destroy(&threadContext->sync.videoFrameMutex);
|
||||
pthread_cond_broadcast(&threadContext->sync.videoFrameAvailableCond);
|
||||
|
@ -149,6 +167,28 @@ void GBAThreadJoin(struct GBAThread* threadContext) {
|
|||
pthread_cond_destroy(&threadContext->sync.audioRequiredCond);
|
||||
}
|
||||
|
||||
void GBAThreadTogglePause(struct GBAThread* threadContext) {
|
||||
int frameOn = 1;
|
||||
pthread_mutex_lock(&threadContext->stateMutex);
|
||||
if (threadContext->state == THREAD_PAUSED) {
|
||||
threadContext->state = THREAD_RUNNING;
|
||||
} else if (threadContext->state == THREAD_RUNNING) {
|
||||
if (threadContext->debugger && threadContext->debugger->state == DEBUGGER_RUNNING) {
|
||||
threadContext->debugger->state = DEBUGGER_EXITING;
|
||||
}
|
||||
threadContext->state = THREAD_PAUSED;
|
||||
frameOn = 0;
|
||||
}
|
||||
pthread_cond_broadcast(&threadContext->stateCond);
|
||||
pthread_mutex_unlock(&threadContext->stateMutex);
|
||||
pthread_mutex_lock(&threadContext->sync.videoFrameMutex);
|
||||
if (frameOn != threadContext->sync.videoFrameOn) {
|
||||
threadContext->sync.videoFrameOn = frameOn;
|
||||
pthread_cond_broadcast(&threadContext->sync.videoFrameAvailableCond);
|
||||
}
|
||||
pthread_mutex_unlock(&threadContext->sync.videoFrameMutex);
|
||||
}
|
||||
|
||||
struct GBAThread* GBAThreadGetContext(void) {
|
||||
return pthread_getspecific(contextKey);
|
||||
}
|
||||
|
@ -161,23 +201,29 @@ void GBASyncPostFrame(struct GBASync* sync) {
|
|||
pthread_mutex_lock(&sync->videoFrameMutex);
|
||||
++sync->videoFramePending;
|
||||
--sync->videoFrameSkip;
|
||||
if (sync->videoFrameSkip < 0) {
|
||||
pthread_cond_broadcast(&sync->videoFrameAvailableCond);
|
||||
if (sync->videoFrameWait) {
|
||||
pthread_cond_wait(&sync->videoFrameRequiredCond, &sync->videoFrameMutex);
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&sync->videoFrameMutex);
|
||||
}
|
||||
|
||||
void GBASyncWaitFrameStart(struct GBASync* sync, int frameskip) {
|
||||
int GBASyncWaitFrameStart(struct GBASync* sync, int frameskip) {
|
||||
if (!sync) {
|
||||
return;
|
||||
return 1;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&sync->videoFrameMutex);
|
||||
pthread_cond_broadcast(&sync->videoFrameRequiredCond);
|
||||
if (!sync->videoFrameOn) {
|
||||
return 0;
|
||||
}
|
||||
pthread_cond_wait(&sync->videoFrameAvailableCond, &sync->videoFrameMutex);
|
||||
sync->videoFramePending = 0;
|
||||
sync->videoFrameSkip = frameskip;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void GBASyncWaitFrameEnd(struct GBASync* sync) {
|
||||
|
|
|
@ -6,9 +6,17 @@
|
|||
struct GBAThread;
|
||||
typedef void (*ThreadCallback)(struct GBAThread* threadContext);
|
||||
|
||||
enum ThreadState {
|
||||
THREAD_INITIALIZED = -1,
|
||||
THREAD_RUNNING = 0,
|
||||
THREAD_PAUSED = 1,
|
||||
THREAD_EXITING = 2,
|
||||
THREAD_SHUTDOWN = 3
|
||||
};
|
||||
|
||||
struct GBAThread {
|
||||
// Output
|
||||
int started;
|
||||
enum ThreadState state;
|
||||
int useDebugger;
|
||||
struct GBA* gba;
|
||||
struct ARMDebugger* debugger;
|
||||
|
@ -23,8 +31,8 @@ struct GBAThread {
|
|||
// Threading state
|
||||
pthread_t thread;
|
||||
|
||||
pthread_mutex_t startMutex;
|
||||
pthread_cond_t startCond;
|
||||
pthread_mutex_t stateMutex;
|
||||
pthread_cond_t stateCond;
|
||||
|
||||
ThreadCallback startCallback;
|
||||
ThreadCallback cleanCallback;
|
||||
|
@ -34,6 +42,7 @@ struct GBAThread {
|
|||
int videoFramePending;
|
||||
int videoFrameWait;
|
||||
int videoFrameSkip;
|
||||
int videoFrameOn;
|
||||
pthread_mutex_t videoFrameMutex;
|
||||
pthread_cond_t videoFrameAvailableCond;
|
||||
pthread_cond_t videoFrameRequiredCond;
|
||||
|
@ -45,10 +54,11 @@ struct GBAThread {
|
|||
|
||||
int GBAThreadStart(struct GBAThread* threadContext);
|
||||
void GBAThreadJoin(struct GBAThread* threadContext);
|
||||
void GBAThreadTogglePause(struct GBAThread* threadContext);
|
||||
struct GBAThread* GBAThreadGetContext(void);
|
||||
|
||||
void GBASyncPostFrame(struct GBASync* sync);
|
||||
void GBASyncWaitFrameStart(struct GBASync* sync, int frameskip);
|
||||
int GBASyncWaitFrameStart(struct GBASync* sync, int frameskip);
|
||||
void GBASyncWaitFrameEnd(struct GBASync* sync);
|
||||
int GBASyncDrawingFrame(struct GBASync* sync);
|
||||
|
||||
|
|
|
@ -136,8 +136,8 @@ static void _GBASDLRunloop(struct GBAThread* context, struct GLSoftwareRenderer*
|
|||
glMatrixMode (GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
glOrtho(0, 240, 160, 0, 0, 1);
|
||||
while (context->started && (!context->debugger || context->debugger->state != DEBUGGER_EXITING)) {
|
||||
GBASyncWaitFrameStart(&context->sync, context->frameskip);
|
||||
while (context->state < THREAD_EXITING) {
|
||||
if (GBASyncWaitFrameStart(&context->sync, context->frameskip)) {
|
||||
glBindTexture(GL_TEXTURE_2D, renderer->tex);
|
||||
#ifdef COLOR_16_BIT
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, renderer->d.outputBuffer);
|
||||
|
@ -147,11 +147,12 @@ static void _GBASDLRunloop(struct GBAThread* context, struct GLSoftwareRenderer*
|
|||
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
||||
|
||||
SDL_GL_SwapBuffers();
|
||||
}
|
||||
GBASyncWaitFrameEnd(&context->sync);
|
||||
|
||||
while (SDL_PollEvent(&event)) {
|
||||
GBASDLHandleEvent(context, &event);
|
||||
}
|
||||
GBASyncWaitFrameEnd(&context->sync);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -76,7 +76,7 @@ static void _GBASDLRunloop(struct GBAThread* context, struct GBAVideoGLSLRendere
|
|||
SDL_Event event;
|
||||
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
while (context->started && context->debugger->state != DEBUGGER_EXITING) {
|
||||
while (context->state < THREAD_EXITING) {
|
||||
GBAVideoGLSLRendererProcessEvents(renderer);
|
||||
pthread_mutex_lock(&renderer->mutex);
|
||||
if (renderer->d.framesPending) {
|
||||
|
|
|
@ -97,16 +97,17 @@ static void _GBASDLRunloop(struct GBAThread* context) {
|
|||
SDL_Event event;
|
||||
SDL_Surface* surface = SDL_GetVideoSurface();
|
||||
|
||||
while (context->started && (!context->debugger || context->debugger->state != DEBUGGER_EXITING)) {
|
||||
GBASyncWaitFrameStart(&context->sync, context->frameskip);
|
||||
while (context->state < THREAD_EXITING) {
|
||||
if (GBASyncWaitFrameStart(&context->sync, context->frameskip)) {
|
||||
SDL_UnlockSurface(surface);
|
||||
SDL_Flip(surface);
|
||||
SDL_LockSurface(surface);
|
||||
}
|
||||
GBASyncWaitFrameEnd(&context->sync);
|
||||
|
||||
while (SDL_PollEvent(&event)) {
|
||||
GBASDLHandleEvent(context, &event);
|
||||
}
|
||||
GBASyncWaitFrameEnd(&context->sync);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -55,6 +55,14 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, const struct SDL_Ke
|
|||
context->sync.audioWait = !context->sync.audioWait;
|
||||
return;
|
||||
default:
|
||||
if (event->keysym.mod & KMOD_CTRL && event->type == SDL_KEYDOWN) {
|
||||
switch (event->keysym.sym) {
|
||||
case SDLK_p:
|
||||
GBAThreadTogglePause(context);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -124,9 +132,11 @@ void GBASDLHandleEvent(struct GBAThread* context, const union SDL_Event* event)
|
|||
// FIXME: this isn't thread-safe
|
||||
if (context->debugger) {
|
||||
context->debugger->state = DEBUGGER_EXITING;
|
||||
} else {
|
||||
context->started = 0;
|
||||
}
|
||||
pthread_mutex_lock(&context->stateMutex);
|
||||
context->state = THREAD_EXITING;
|
||||
pthread_cond_broadcast(&context->stateCond);
|
||||
pthread_mutex_unlock(&context->stateMutex);
|
||||
break;
|
||||
case SDL_KEYDOWN:
|
||||
case SDL_KEYUP:
|
||||
|
|
Loading…
Reference in New Issue