Support pausing

This commit is contained in:
Jeffrey Pfau 2013-10-16 00:46:09 -07:00
parent 8e43ffed90
commit a107243c7a
9 changed files with 140 additions and 67 deletions

View File

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

View File

@ -10,7 +10,8 @@
enum DebuggerState {
DEBUGGER_PAUSED,
DEBUGGER_RUNNING,
DEBUGGER_EXITING
DEBUGGER_EXITING,
DEBUGGER_SHUTDOWN
};
#ifdef USE_DEBUGGER

View File

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

View File

@ -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) {

View File

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

View File

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

View File

@ -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) {

View File

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

View File

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