all repos — mgba @ 45501658b5b9492996141149ade4ad29aa14ffd4

mGBA Game Boy Advance Emulator

src/gba/gba-thread.c (view raw)

  1#include "gba-thread.h"
  2
  3#include "arm.h"
  4#include "debugger.h"
  5#include "gba.h"
  6
  7#include <stdlib.h>
  8#include <signal.h>
  9
 10static pthread_key_t contextKey;
 11
 12static void _createTLS(void) {
 13	pthread_key_create(&contextKey, 0);
 14}
 15
 16static void* _GBAThreadRun(void* context) {
 17	static pthread_once_t once = PTHREAD_ONCE_INIT;
 18	pthread_once(&once, _createTLS);
 19
 20	struct ARMDebugger debugger;
 21	struct GBA gba;
 22	struct GBAThread* threadContext = context;
 23	char* savedata = 0;
 24
 25	sigset_t signals;
 26	sigfillset(&signals);
 27	pthread_sigmask(SIG_UNBLOCK, &signals, 0);
 28
 29	GBAInit(&gba);
 30	threadContext->gba = &gba;
 31	gba.sync = &threadContext->sync;
 32	pthread_setspecific(contextKey, threadContext);
 33	if (threadContext->renderer) {
 34		GBAVideoAssociateRenderer(&gba.video, threadContext->renderer);
 35	}
 36
 37	if (threadContext->fd >= 0) {
 38		if (threadContext->fname) {
 39			char* dotPoint = strrchr(threadContext->fname, '.');
 40			if (dotPoint > strrchr(threadContext->fname, '/') && dotPoint[1] && dotPoint[2] && dotPoint[3]) {
 41				savedata = strdup(threadContext->fname);
 42				dotPoint = strrchr(savedata, '.');
 43				dotPoint[1] = 's';
 44				dotPoint[2] = 'a';
 45				dotPoint[3] = 'v';
 46				dotPoint[4] = '\0';
 47			} else if (dotPoint) {
 48				savedata = malloc((dotPoint - threadContext->fname + 5) * sizeof(char));
 49				strncpy(savedata, threadContext->fname, dotPoint - threadContext->fname + 1);
 50				strcat(savedata, "sav");
 51			} else {
 52				savedata = malloc(strlen(threadContext->fname + 5));
 53				strcpy(savedata, threadContext->fname);
 54				strcat(savedata, "sav");
 55			}
 56		}
 57		GBALoadROM(&gba, threadContext->fd, threadContext->fname);
 58		gba.savefile = savedata;
 59	}
 60	if (threadContext->useDebugger) {
 61		threadContext->debugger = &debugger;
 62		GBAAttachDebugger(&gba, &debugger);
 63	} else {
 64		threadContext->debugger = 0;
 65	}
 66	gba.keySource = &threadContext->activeKeys;
 67
 68	threadContext->started = 1;
 69	pthread_mutex_lock(&threadContext->startMutex);
 70	pthread_cond_broadcast(&threadContext->startCond);
 71	pthread_mutex_unlock(&threadContext->startMutex);
 72
 73	if (threadContext->useDebugger) {
 74		ARMDebuggerRun(&debugger);
 75		threadContext->started = 0;
 76	} else {
 77		while (threadContext->started) {
 78			ARMRun(&gba.cpu);
 79		}
 80	}
 81	GBADeinit(&gba);
 82	free(savedata);
 83
 84	return 0;
 85}
 86
 87int GBAThreadStart(struct GBAThread* threadContext) {
 88	// TODO: error check
 89	pthread_mutex_init(&threadContext->startMutex, 0);
 90	pthread_cond_init(&threadContext->startCond, 0);
 91
 92	pthread_mutex_init(&threadContext->sync.videoFrameMutex, 0);
 93	pthread_cond_init(&threadContext->sync.videoFrameAvailableCond, 0);
 94	pthread_cond_init(&threadContext->sync.videoFrameRequiredCond, 0);
 95	pthread_cond_init(&threadContext->sync.audioAvailableCond, 0);
 96	pthread_cond_init(&threadContext->sync.audioRequiredCond, 0);
 97
 98	pthread_mutex_lock(&threadContext->startMutex);
 99	threadContext->activeKeys = 0;
100	threadContext->started = 0;
101	pthread_create(&threadContext->thread, 0, _GBAThreadRun, threadContext);
102	pthread_cond_wait(&threadContext->startCond, &threadContext->startMutex);
103	pthread_mutex_unlock(&threadContext->startMutex);
104
105	return 0;
106}
107
108void GBAThreadJoin(struct GBAThread* threadContext) {
109	pthread_mutex_lock(&threadContext->sync.videoFrameMutex);
110	threadContext->sync.videoFrameWait = 0;
111	pthread_cond_broadcast(&threadContext->sync.videoFrameRequiredCond);
112	pthread_mutex_unlock(&threadContext->sync.videoFrameMutex);
113
114	pthread_join(threadContext->thread, 0);
115
116	pthread_mutex_destroy(&threadContext->startMutex);
117	pthread_cond_destroy(&threadContext->startCond);
118
119	pthread_mutex_destroy(&threadContext->sync.videoFrameMutex);
120	pthread_cond_broadcast(&threadContext->sync.videoFrameAvailableCond);
121	pthread_cond_destroy(&threadContext->sync.videoFrameAvailableCond);
122	pthread_cond_broadcast(&threadContext->sync.videoFrameRequiredCond);
123	pthread_cond_destroy(&threadContext->sync.videoFrameRequiredCond);
124
125	pthread_cond_broadcast(&threadContext->sync.audioAvailableCond);
126	pthread_cond_destroy(&threadContext->sync.audioAvailableCond);
127	pthread_cond_broadcast(&threadContext->sync.audioRequiredCond);
128	pthread_cond_destroy(&threadContext->sync.audioRequiredCond);
129}
130
131struct GBAThread* GBAThreadGetContext(void) {
132	return pthread_getspecific(contextKey);
133}
134
135void GBASyncPostFrame(struct GBASync* sync) {
136	if (!sync) {
137		return;
138	}
139
140	pthread_mutex_lock(&sync->videoFrameMutex);
141	++sync->videoFramePending;
142	--sync->videoFrameSkip;
143	pthread_cond_broadcast(&sync->videoFrameAvailableCond);
144	if (sync->videoFrameWait) {
145		pthread_cond_wait(&sync->videoFrameRequiredCond, &sync->videoFrameMutex);
146	}
147	pthread_mutex_unlock(&sync->videoFrameMutex);
148}
149
150void GBASyncWaitFrameStart(struct GBASync* sync, int frameskip) {
151	if (!sync) {
152		return;
153	}
154
155	pthread_mutex_lock(&sync->videoFrameMutex);
156	pthread_cond_broadcast(&sync->videoFrameRequiredCond);
157	pthread_cond_wait(&sync->videoFrameAvailableCond, &sync->videoFrameMutex);
158	sync->videoFramePending = 0;
159	sync->videoFrameSkip = frameskip;
160}
161
162void GBASyncWaitFrameEnd(struct GBASync* sync) {
163	if (!sync) {
164		return;
165	}
166
167	pthread_mutex_unlock(&sync->videoFrameMutex);
168}
169
170int GBASyncDrawingFrame(struct GBASync* sync) {
171	return sync->videoFrameSkip <= 0;
172}
173
174void GBASyncProduceAudio(struct GBASync* sync, pthread_mutex_t* mutex) {
175	pthread_cond_broadcast(&sync->audioAvailableCond);
176	if (&sync->audioWait) {
177		pthread_cond_wait(&sync->audioRequiredCond, mutex);
178	}
179}
180
181void GBASyncConsumeAudio(struct GBASync* sync) {
182	pthread_cond_broadcast(&sync->audioRequiredCond);
183}