all repos — mgba @ 228b6aaa016e7e249ac54504f9c42b2732261cd3

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		gba.savefile = savedata;
 60		GBALoadROM(&gba, threadContext->fd, threadContext->fname);
 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	pthread_mutex_lock(&threadContext->stateMutex);
 81	threadContext->state = THREAD_RUNNING;
 82	pthread_cond_broadcast(&threadContext->stateCond);
 83	pthread_mutex_unlock(&threadContext->stateMutex);
 84
 85	while (threadContext->state < THREAD_EXITING) {
 86#ifdef USE_DEBUGGER
 87		if (threadContext->useDebugger) {
 88			ARMDebuggerRun(&debugger);
 89			if (debugger.state == DEBUGGER_SHUTDOWN) {
 90				pthread_mutex_lock(&threadContext->stateMutex);
 91				threadContext->state = THREAD_EXITING;
 92				pthread_mutex_unlock(&threadContext->stateMutex);
 93			}
 94		} else {
 95#endif
 96			while (threadContext->state == THREAD_RUNNING) {
 97				ARMRun(&gba.cpu);
 98			}
 99#ifdef USE_DEBUGGER
100		}
101#endif
102		while (threadContext->state == THREAD_PAUSED) {
103			pthread_mutex_lock(&threadContext->stateMutex);
104			pthread_cond_wait(&threadContext->stateCond, &threadContext->stateMutex);
105			pthread_mutex_unlock(&threadContext->stateMutex);
106		}
107	}
108
109	while (threadContext->state != THREAD_SHUTDOWN) {
110		pthread_mutex_lock(&threadContext->stateMutex);
111		threadContext->state = THREAD_SHUTDOWN;
112		pthread_mutex_unlock(&threadContext->stateMutex);
113	}
114
115	if (threadContext->cleanCallback) {
116		threadContext->cleanCallback(threadContext);
117	}
118
119	GBADeinit(&gba);
120
121	pthread_cond_broadcast(&threadContext->sync.videoFrameAvailableCond);
122	pthread_cond_broadcast(&threadContext->sync.audioRequiredCond);
123	free(savedata);
124
125	return 0;
126}
127
128int GBAThreadStart(struct GBAThread* threadContext) {
129	// TODO: error check
130	pthread_mutex_init(&threadContext->stateMutex, 0);
131	pthread_cond_init(&threadContext->stateCond, 0);
132
133	pthread_mutex_init(&threadContext->sync.videoFrameMutex, 0);
134	pthread_cond_init(&threadContext->sync.videoFrameAvailableCond, 0);
135	pthread_cond_init(&threadContext->sync.videoFrameRequiredCond, 0);
136	pthread_cond_init(&threadContext->sync.audioRequiredCond, 0);
137
138	pthread_mutex_lock(&threadContext->stateMutex);
139	threadContext->activeKeys = 0;
140	threadContext->state = THREAD_INITIALIZED;
141	threadContext->sync.videoFrameOn = 1;
142	threadContext->sync.videoFrameSkip = 0;
143	pthread_create(&threadContext->thread, 0, _GBAThreadRun, threadContext);
144	pthread_cond_wait(&threadContext->stateCond, &threadContext->stateMutex);
145	pthread_mutex_unlock(&threadContext->stateMutex);
146
147	return 0;
148}
149
150void GBAThreadJoin(struct GBAThread* threadContext) {
151	pthread_mutex_lock(&threadContext->sync.videoFrameMutex);
152	threadContext->sync.videoFrameWait = 0;
153	pthread_cond_broadcast(&threadContext->sync.videoFrameRequiredCond);
154	pthread_mutex_unlock(&threadContext->sync.videoFrameMutex);
155
156	pthread_join(threadContext->thread, 0);
157
158	pthread_mutex_destroy(&threadContext->stateMutex);
159	pthread_cond_destroy(&threadContext->stateCond);
160
161	pthread_mutex_destroy(&threadContext->sync.videoFrameMutex);
162	pthread_cond_broadcast(&threadContext->sync.videoFrameAvailableCond);
163	pthread_cond_destroy(&threadContext->sync.videoFrameAvailableCond);
164	pthread_cond_broadcast(&threadContext->sync.videoFrameRequiredCond);
165	pthread_cond_destroy(&threadContext->sync.videoFrameRequiredCond);
166
167	pthread_cond_broadcast(&threadContext->sync.audioRequiredCond);
168	pthread_cond_destroy(&threadContext->sync.audioRequiredCond);
169}
170
171void GBAThreadPause(struct GBAThread* threadContext) {
172	int frameOn = 1;
173	pthread_mutex_lock(&threadContext->stateMutex);
174	if (threadContext->state == THREAD_RUNNING) {
175		if (threadContext->debugger && threadContext->debugger->state == DEBUGGER_RUNNING) {
176			threadContext->debugger->state = DEBUGGER_EXITING;
177		}
178		threadContext->state = THREAD_PAUSED;
179		frameOn = 0;
180	}
181	pthread_mutex_unlock(&threadContext->stateMutex);
182	pthread_mutex_lock(&threadContext->sync.videoFrameMutex);
183	if (frameOn != threadContext->sync.videoFrameOn) {
184		threadContext->sync.videoFrameOn = frameOn;
185		pthread_cond_broadcast(&threadContext->sync.videoFrameAvailableCond);
186	}
187	pthread_mutex_unlock(&threadContext->sync.videoFrameMutex);
188}
189
190void GBAThreadUnpause(struct GBAThread* threadContext) {
191	int frameOn = 1;
192	pthread_mutex_lock(&threadContext->stateMutex);
193	if (threadContext->state == THREAD_PAUSED) {
194		threadContext->state = THREAD_RUNNING;
195		pthread_cond_broadcast(&threadContext->stateCond);
196	}
197	pthread_mutex_unlock(&threadContext->stateMutex);
198	pthread_mutex_lock(&threadContext->sync.videoFrameMutex);
199	if (frameOn != threadContext->sync.videoFrameOn) {
200		threadContext->sync.videoFrameOn = frameOn;
201		pthread_cond_broadcast(&threadContext->sync.videoFrameAvailableCond);
202	}
203	pthread_mutex_unlock(&threadContext->sync.videoFrameMutex);
204}
205
206void GBAThreadTogglePause(struct GBAThread* threadContext) {
207	int frameOn = 1;
208	pthread_mutex_lock(&threadContext->stateMutex);
209	if (threadContext->state == THREAD_PAUSED) {
210		threadContext->state = THREAD_RUNNING;
211		pthread_cond_broadcast(&threadContext->stateCond);
212	} else if (threadContext->state == THREAD_RUNNING) {
213		if (threadContext->debugger && threadContext->debugger->state == DEBUGGER_RUNNING) {
214			threadContext->debugger->state = DEBUGGER_EXITING;
215		}
216		threadContext->state = THREAD_PAUSED;
217		frameOn = 0;
218	}
219	pthread_mutex_unlock(&threadContext->stateMutex);
220	pthread_mutex_lock(&threadContext->sync.videoFrameMutex);
221	if (frameOn != threadContext->sync.videoFrameOn) {
222		threadContext->sync.videoFrameOn = frameOn;
223		pthread_cond_broadcast(&threadContext->sync.videoFrameAvailableCond);
224	}
225	pthread_mutex_unlock(&threadContext->sync.videoFrameMutex);
226}
227
228struct GBAThread* GBAThreadGetContext(void) {
229	return pthread_getspecific(contextKey);
230}
231
232void GBASyncPostFrame(struct GBASync* sync) {
233	if (!sync) {
234		return;
235	}
236
237	pthread_mutex_lock(&sync->videoFrameMutex);
238	++sync->videoFramePending;
239	--sync->videoFrameSkip;
240	if (sync->videoFrameSkip < 0) {
241		pthread_cond_broadcast(&sync->videoFrameAvailableCond);
242		if (sync->videoFrameWait) {
243			pthread_cond_wait(&sync->videoFrameRequiredCond, &sync->videoFrameMutex);
244		}
245	}
246	pthread_mutex_unlock(&sync->videoFrameMutex);
247}
248
249int GBASyncWaitFrameStart(struct GBASync* sync, int frameskip) {
250	if (!sync) {
251		return 1;
252	}
253
254	pthread_mutex_lock(&sync->videoFrameMutex);
255	pthread_cond_broadcast(&sync->videoFrameRequiredCond);
256	if (!sync->videoFrameOn) {
257		return 0;
258	}
259	pthread_cond_wait(&sync->videoFrameAvailableCond, &sync->videoFrameMutex);
260	sync->videoFramePending = 0;
261	sync->videoFrameSkip = frameskip;
262	return 1;
263}
264
265void GBASyncWaitFrameEnd(struct GBASync* sync) {
266	if (!sync) {
267		return;
268	}
269
270	pthread_mutex_unlock(&sync->videoFrameMutex);
271}
272
273int GBASyncDrawingFrame(struct GBASync* sync) {
274	return sync->videoFrameSkip <= 0;
275}
276
277void GBASyncProduceAudio(struct GBASync* sync, pthread_mutex_t* mutex) {
278	if (&sync->audioWait) {
279		pthread_cond_wait(&sync->audioRequiredCond, mutex);
280	}
281}
282
283void GBASyncConsumeAudio(struct GBASync* sync) {
284	pthread_cond_broadcast(&sync->audioRequiredCond);
285}