diff --git a/src/debugger/debugger.c b/src/debugger/debugger.c index 62ba65d8a..0821713ae 100644 --- a/src/debugger/debugger.c +++ b/src/debugger/debugger.c @@ -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; } } } diff --git a/src/debugger/debugger.h b/src/debugger/debugger.h index 725340861..03b09b6a8 100644 --- a/src/debugger/debugger.h +++ b/src/debugger/debugger.h @@ -10,7 +10,8 @@ enum DebuggerState { DEBUGGER_PAUSED, DEBUGGER_RUNNING, - DEBUGGER_EXITING + DEBUGGER_EXITING, + DEBUGGER_SHUTDOWN }; #ifdef USE_DEBUGGER diff --git a/src/egl-main.c b/src/egl-main.c index e1fa453bc..ef8196b90 100644 --- a/src/egl-main.c +++ b/src/egl-main.c @@ -217,25 +217,26 @@ 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); - glViewport(0, 0, 240, 160); - glClear(GL_COLOR_BUFFER_BIT); - glUseProgram(renderer->program); - glUniform1i(renderer->texLocation, 0); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, renderer->tex); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, renderer->d.outputBuffer); - glVertexAttribPointer(renderer->positionLocation, 2, GL_FLOAT, GL_FALSE, 0, _vertices); - glEnableVertexAttribArray(renderer->positionLocation); - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - glUseProgram(0); - eglSwapBuffers(renderer->display, renderer->surface); + while (context->state < THREAD_EXITING) { + if (GBASyncWaitFrameStart(&context->sync, context->frameskip)) { + glViewport(0, 0, 240, 160); + glClear(GL_COLOR_BUFFER_BIT); + glUseProgram(renderer->program); + glUniform1i(renderer->texLocation, 0); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, renderer->tex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, renderer->d.outputBuffer); + glVertexAttribPointer(renderer->positionLocation, 2, GL_FLOAT, GL_FALSE, 0, _vertices); + glEnableVertexAttribArray(renderer->positionLocation); + 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); } } diff --git a/src/gba/gba-thread.c b/src/gba/gba-thread.c index c54dacad3..8bb1e642a 100644 --- a/src/gba/gba-thread.c +++ b/src/gba/gba-thread.c @@ -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; - } else { + if (threadContext->useDebugger) { + ARMDebuggerRun(&debugger); + if (debugger.state == DEBUGGER_SHUTDOWN) { + pthread_mutex_lock(&threadContext->stateMutex); + threadContext->state = THREAD_EXITING; + pthread_mutex_unlock(&threadContext->stateMutex); + } + } else { #endif - while (threadContext->started) { - ARMRun(&gba.cpu); + while (threadContext->state == THREAD_RUNNING) { + ARMRun(&gba.cpu); + } +#ifdef USE_DEBUGGER } -#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; - pthread_cond_broadcast(&sync->videoFrameAvailableCond); - if (sync->videoFrameWait) { - pthread_cond_wait(&sync->videoFrameRequiredCond, &sync->videoFrameMutex); + 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) { diff --git a/src/gba/gba-thread.h b/src/gba/gba-thread.h index a058b7daa..8974050ca 100644 --- a/src/gba/gba-thread.h +++ b/src/gba/gba-thread.h @@ -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); diff --git a/src/gl-main.c b/src/gl-main.c index bc503b031..a36b2c13d 100644 --- a/src/gl-main.c +++ b/src/gl-main.c @@ -136,22 +136,23 @@ 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); - glBindTexture(GL_TEXTURE_2D, renderer->tex); + 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); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, renderer->d.outputBuffer); #else - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, renderer->d.outputBuffer); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, renderer->d.outputBuffer); #endif - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - SDL_GL_SwapBuffers(); + SDL_GL_SwapBuffers(); + } + GBASyncWaitFrameEnd(&context->sync); while (SDL_PollEvent(&event)) { GBASDLHandleEvent(context, &event); } - GBASyncWaitFrameEnd(&context->sync); } } diff --git a/src/glsl-main.c b/src/glsl-main.c index d70fcea7d..eb015a9e4 100644 --- a/src/glsl-main.c +++ b/src/glsl-main.c @@ -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) { diff --git a/src/sdl-main.c b/src/sdl-main.c index 1e9b11d01..33cd9db00 100644 --- a/src/sdl-main.c +++ b/src/sdl-main.c @@ -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); - SDL_UnlockSurface(surface); - SDL_Flip(surface); - SDL_LockSurface(surface); + 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); } } diff --git a/src/sdl/sdl-events.c b/src/sdl/sdl-events.c index 754b99a61..33e8eb661 100644 --- a/src/sdl/sdl-events.c +++ b/src/sdl/sdl-events.c @@ -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: