Support pausing
@@ -158,7 +158,7 @@ }
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 @@ el_end(debugger->elstate);
} 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 @@ _checkBreakpoints(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,25 +217,26 @@
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); } }
@@ -77,23 +77,40 @@ if (threadContext->startCallback) {
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 - } + } #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 @@ }
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 @@ pthread_mutex_unlock(&threadContext->sync.videoFrameMutex);
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 @@ pthread_cond_broadcast(&threadContext->sync.audioRequiredCond);
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 @@
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) {
@@ -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 @@
// 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 GBASync {
int videoFramePending; int videoFrameWait; int videoFrameSkip; + int videoFrameOn; pthread_mutex_t videoFrameMutex; pthread_cond_t videoFrameAvailableCond; pthread_cond_t videoFrameRequiredCond;@@ -45,10 +54,11 @@ };
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,22 +136,23 @@ glTexCoordPointer(2, GL_INT, 0, _glTexCoords);
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); } }
@@ -76,7 +76,7 @@ static void _GBASDLRunloop(struct GBAThread* context, struct GBAVideoGLSLRenderer* renderer) {
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); - 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); } }
@@ -55,6 +55,14 @@ case SDLK_TAB:
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 @@ case SDL_QUIT:
// 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: