all repos — mgba @ 96985317020741eb19ae5c734b4fa533a1588ea6

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	if (threadContext->startCallback) {
 69		threadContext->startCallback(threadContext);
 70	}
 71
 72	threadContext->started = 1;
 73	pthread_mutex_lock(&threadContext->startMutex);
 74	pthread_cond_broadcast(&threadContext->startCond);
 75	pthread_mutex_unlock(&threadContext->startMutex);
 76
 77	if (threadContext->useDebugger) {
 78		ARMDebuggerRun(&debugger);
 79		threadContext->started = 0;
 80	} else {
 81		while (threadContext->started) {
 82			ARMRun(&gba.cpu);
 83		}
 84	}
 85
 86	if (threadContext->cleanCallback) {
 87		threadContext->cleanCallback(threadContext);
 88	}
 89
 90	GBADeinit(&gba);
 91
 92	pthread_cond_broadcast(&threadContext->sync.videoFrameAvailableCond);
 93	pthread_cond_broadcast(&threadContext->sync.audioRequiredCond);
 94	free(savedata);
 95
 96	return 0;
 97}
 98
 99int GBAThreadStart(struct GBAThread* threadContext) {
100	// TODO: error check
101	pthread_mutex_init(&threadContext->startMutex, 0);
102	pthread_cond_init(&threadContext->startCond, 0);
103
104	pthread_mutex_init(&threadContext->sync.videoFrameMutex, 0);
105	pthread_cond_init(&threadContext->sync.videoFrameAvailableCond, 0);
106	pthread_cond_init(&threadContext->sync.videoFrameRequiredCond, 0);
107	pthread_cond_init(&threadContext->sync.audioRequiredCond, 0);
108
109	pthread_mutex_lock(&threadContext->startMutex);
110	threadContext->activeKeys = 0;
111	threadContext->started = 0;
112	pthread_create(&threadContext->thread, 0, _GBAThreadRun, threadContext);
113	pthread_cond_wait(&threadContext->startCond, &threadContext->startMutex);
114	pthread_mutex_unlock(&threadContext->startMutex);
115
116	return 0;
117}
118
119void GBAThreadJoin(struct GBAThread* threadContext) {
120	pthread_mutex_lock(&threadContext->sync.videoFrameMutex);
121	threadContext->sync.videoFrameWait = 0;
122	pthread_cond_broadcast(&threadContext->sync.videoFrameRequiredCond);
123	pthread_mutex_unlock(&threadContext->sync.videoFrameMutex);
124
125	pthread_join(threadContext->thread, 0);
126
127	pthread_mutex_destroy(&threadContext->startMutex);
128	pthread_cond_destroy(&threadContext->startCond);
129
130	pthread_mutex_destroy(&threadContext->sync.videoFrameMutex);
131	pthread_cond_broadcast(&threadContext->sync.videoFrameAvailableCond);
132	pthread_cond_destroy(&threadContext->sync.videoFrameAvailableCond);
133	pthread_cond_broadcast(&threadContext->sync.videoFrameRequiredCond);
134	pthread_cond_destroy(&threadContext->sync.videoFrameRequiredCond);
135
136	pthread_cond_broadcast(&threadContext->sync.audioRequiredCond);
137	pthread_cond_destroy(&threadContext->sync.audioRequiredCond);
138}
139
140struct GBAThread* GBAThreadGetContext(void) {
141	return pthread_getspecific(contextKey);
142}
143
144void GBASyncPostFrame(struct GBASync* sync) {
145	if (!sync) {
146		return;
147	}
148
149	pthread_mutex_lock(&sync->videoFrameMutex);
150	++sync->videoFramePending;
151	--sync->videoFrameSkip;
152	pthread_cond_broadcast(&sync->videoFrameAvailableCond);
153	if (sync->videoFrameWait) {
154		pthread_cond_wait(&sync->videoFrameRequiredCond, &sync->videoFrameMutex);
155	}
156	pthread_mutex_unlock(&sync->videoFrameMutex);
157}
158
159void GBASyncWaitFrameStart(struct GBASync* sync, int frameskip) {
160	if (!sync) {
161		return;
162	}
163
164	pthread_mutex_lock(&sync->videoFrameMutex);
165	pthread_cond_broadcast(&sync->videoFrameRequiredCond);
166	pthread_cond_wait(&sync->videoFrameAvailableCond, &sync->videoFrameMutex);
167	sync->videoFramePending = 0;
168	sync->videoFrameSkip = frameskip;
169}
170
171void GBASyncWaitFrameEnd(struct GBASync* sync) {
172	if (!sync) {
173		return;
174	}
175
176	pthread_mutex_unlock(&sync->videoFrameMutex);
177}
178
179int GBASyncDrawingFrame(struct GBASync* sync) {
180	return sync->videoFrameSkip <= 0;
181}
182
183void GBASyncProduceAudio(struct GBASync* sync, pthread_mutex_t* mutex) {
184	if (&sync->audioWait) {
185		pthread_cond_wait(&sync->audioRequiredCond, mutex);
186	}
187}
188
189void GBASyncConsumeAudio(struct GBASync* sync) {
190	pthread_cond_broadcast(&sync->audioRequiredCond);
191}