all repos — mgba @ 290b64b171be380c131898fdaad5106caffb90c7

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