all repos — mgba @ 85d30ac27174392c415c275d67a95badec64436a

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;
 11static pthread_once_t _contextOnce = PTHREAD_ONCE_INIT;
 12
 13static void _createTLS(void) {
 14	pthread_key_create(&_contextKey, 0);
 15}
 16
 17static void* _GBAThreadRun(void* context) {
 18	pthread_once(&_contextOnce, _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#ifndef _WIN32
 28	sigset_t signals;
 29	sigfillset(&signals);
 30	pthread_sigmask(SIG_UNBLOCK, &signals, 0);
 31#endif
 32
 33	GBAInit(&gba);
 34	threadContext->gba = &gba;
 35	gba.sync = &threadContext->sync;
 36	pthread_setspecific(_contextKey, threadContext);
 37	if (threadContext->renderer) {
 38		GBAVideoAssociateRenderer(&gba.video, threadContext->renderer);
 39	}
 40
 41	if (threadContext->fd >= 0) {
 42		if (threadContext->fname) {
 43			char* dotPoint = strrchr(threadContext->fname, '.');
 44			if (dotPoint > strrchr(threadContext->fname, '/') && dotPoint[1] && dotPoint[2] && dotPoint[3]) {
 45				savedata = strdup(threadContext->fname);
 46				dotPoint = strrchr(savedata, '.');
 47				dotPoint[1] = 's';
 48				dotPoint[2] = 'a';
 49				dotPoint[3] = 'v';
 50				dotPoint[4] = '\0';
 51			} else if (dotPoint) {
 52				savedata = malloc((dotPoint - threadContext->fname + 5) * sizeof(char));
 53				strncpy(savedata, threadContext->fname, dotPoint - threadContext->fname + 1);
 54				strcat(savedata, "sav");
 55			} else {
 56				savedata = malloc(strlen(threadContext->fname + 5));
 57				strcpy(savedata, threadContext->fname);
 58				strcat(savedata, "sav");
 59			}
 60		}
 61		gba.savefile = savedata;
 62		GBALoadROM(&gba, threadContext->fd, threadContext->fname);
 63	}
 64
 65#ifdef USE_DEBUGGER
 66	if (threadContext->useDebugger) {
 67		threadContext->debugger = &debugger;
 68		GBAAttachDebugger(&gba, &debugger);
 69	} else {
 70		threadContext->debugger = 0;
 71	}
 72#else
 73	threadContext->debugger = 0;
 74#endif
 75
 76	gba.keySource = &threadContext->activeKeys;
 77
 78	if (threadContext->startCallback) {
 79		threadContext->startCallback(threadContext);
 80	}
 81
 82	pthread_mutex_lock(&threadContext->stateMutex);
 83	threadContext->state = THREAD_RUNNING;
 84	pthread_cond_broadcast(&threadContext->stateCond);
 85	pthread_mutex_unlock(&threadContext->stateMutex);
 86
 87	while (threadContext->state < THREAD_EXITING) {
 88#ifdef USE_DEBUGGER
 89		if (threadContext->useDebugger) {
 90			ARMDebuggerRun(&debugger);
 91			if (debugger.state == DEBUGGER_SHUTDOWN) {
 92				pthread_mutex_lock(&threadContext->stateMutex);
 93				threadContext->state = THREAD_EXITING;
 94				pthread_mutex_unlock(&threadContext->stateMutex);
 95			}
 96		} else {
 97#endif
 98			while (threadContext->state == THREAD_RUNNING) {
 99				ARMRun(&gba.cpu);
100			}
101#ifdef USE_DEBUGGER
102		}
103#endif
104		while (threadContext->state == THREAD_PAUSED) {
105			pthread_mutex_lock(&threadContext->stateMutex);
106			pthread_cond_wait(&threadContext->stateCond, &threadContext->stateMutex);
107			pthread_mutex_unlock(&threadContext->stateMutex);
108		}
109	}
110
111	while (threadContext->state != THREAD_SHUTDOWN) {
112		pthread_mutex_lock(&threadContext->stateMutex);
113		threadContext->state = THREAD_SHUTDOWN;
114		pthread_mutex_unlock(&threadContext->stateMutex);
115	}
116
117	if (threadContext->cleanCallback) {
118		threadContext->cleanCallback(threadContext);
119	}
120
121	GBADeinit(&gba);
122
123	pthread_cond_broadcast(&threadContext->sync.videoFrameAvailableCond);
124	pthread_cond_broadcast(&threadContext->sync.audioRequiredCond);
125	free(savedata);
126
127	return 0;
128}
129
130int GBAThreadStart(struct GBAThread* threadContext) {
131	// TODO: error check
132	pthread_mutex_init(&threadContext->stateMutex, 0);
133	pthread_cond_init(&threadContext->stateCond, 0);
134
135	pthread_mutex_init(&threadContext->sync.videoFrameMutex, 0);
136	pthread_cond_init(&threadContext->sync.videoFrameAvailableCond, 0);
137	pthread_cond_init(&threadContext->sync.videoFrameRequiredCond, 0);
138	pthread_mutex_init(&threadContext->sync.audioBufferMutex, 0);
139	pthread_cond_init(&threadContext->sync.audioRequiredCond, 0);
140
141	pthread_mutex_lock(&threadContext->stateMutex);
142	threadContext->activeKeys = 0;
143	threadContext->state = THREAD_INITIALIZED;
144	threadContext->sync.videoFrameOn = 1;
145	threadContext->sync.videoFrameSkip = 0;
146	pthread_create(&threadContext->thread, 0, _GBAThreadRun, threadContext);
147	pthread_cond_wait(&threadContext->stateCond, &threadContext->stateMutex);
148	pthread_mutex_unlock(&threadContext->stateMutex);
149
150	return 0;
151}
152
153void GBAThreadJoin(struct GBAThread* threadContext) {
154	pthread_mutex_lock(&threadContext->sync.videoFrameMutex);
155	threadContext->sync.videoFrameWait = 0;
156	pthread_cond_broadcast(&threadContext->sync.videoFrameRequiredCond);
157	pthread_mutex_unlock(&threadContext->sync.videoFrameMutex);
158
159	pthread_join(threadContext->thread, 0);
160
161	pthread_mutex_destroy(&threadContext->stateMutex);
162	pthread_cond_destroy(&threadContext->stateCond);
163
164	pthread_mutex_destroy(&threadContext->sync.videoFrameMutex);
165	pthread_cond_broadcast(&threadContext->sync.videoFrameAvailableCond);
166	pthread_cond_destroy(&threadContext->sync.videoFrameAvailableCond);
167	pthread_cond_broadcast(&threadContext->sync.videoFrameRequiredCond);
168	pthread_cond_destroy(&threadContext->sync.videoFrameRequiredCond);
169
170	pthread_cond_broadcast(&threadContext->sync.audioRequiredCond);
171	pthread_cond_destroy(&threadContext->sync.audioRequiredCond);
172	pthread_mutex_destroy(&threadContext->sync.audioBufferMutex);
173}
174
175void GBAThreadPause(struct GBAThread* threadContext) {
176	int frameOn = 1;
177	pthread_mutex_lock(&threadContext->stateMutex);
178	if (threadContext->state == THREAD_RUNNING) {
179		if (threadContext->debugger && threadContext->debugger->state == DEBUGGER_RUNNING) {
180			threadContext->debugger->state = DEBUGGER_EXITING;
181		}
182		threadContext->state = THREAD_PAUSED;
183		frameOn = 0;
184	}
185	pthread_mutex_unlock(&threadContext->stateMutex);
186	pthread_mutex_lock(&threadContext->sync.videoFrameMutex);
187	if (frameOn != threadContext->sync.videoFrameOn) {
188		threadContext->sync.videoFrameOn = frameOn;
189		pthread_cond_broadcast(&threadContext->sync.videoFrameAvailableCond);
190	}
191	pthread_mutex_unlock(&threadContext->sync.videoFrameMutex);
192}
193
194void GBAThreadUnpause(struct GBAThread* threadContext) {
195	int frameOn = 1;
196	pthread_mutex_lock(&threadContext->stateMutex);
197	if (threadContext->state == THREAD_PAUSED) {
198		threadContext->state = THREAD_RUNNING;
199		pthread_cond_broadcast(&threadContext->stateCond);
200	}
201	pthread_mutex_unlock(&threadContext->stateMutex);
202	pthread_mutex_lock(&threadContext->sync.videoFrameMutex);
203	if (frameOn != threadContext->sync.videoFrameOn) {
204		threadContext->sync.videoFrameOn = frameOn;
205		pthread_cond_broadcast(&threadContext->sync.videoFrameAvailableCond);
206	}
207	pthread_mutex_unlock(&threadContext->sync.videoFrameMutex);
208}
209
210void GBAThreadTogglePause(struct GBAThread* threadContext) {
211	int frameOn = 1;
212	pthread_mutex_lock(&threadContext->stateMutex);
213	if (threadContext->state == THREAD_PAUSED) {
214		threadContext->state = THREAD_RUNNING;
215		pthread_cond_broadcast(&threadContext->stateCond);
216	} else if (threadContext->state == THREAD_RUNNING) {
217		if (threadContext->debugger && threadContext->debugger->state == DEBUGGER_RUNNING) {
218			threadContext->debugger->state = DEBUGGER_EXITING;
219		}
220		threadContext->state = THREAD_PAUSED;
221		frameOn = 0;
222	}
223	pthread_mutex_unlock(&threadContext->stateMutex);
224	pthread_mutex_lock(&threadContext->sync.videoFrameMutex);
225	if (frameOn != threadContext->sync.videoFrameOn) {
226		threadContext->sync.videoFrameOn = frameOn;
227		pthread_cond_broadcast(&threadContext->sync.videoFrameAvailableCond);
228	}
229	pthread_mutex_unlock(&threadContext->sync.videoFrameMutex);
230}
231
232struct GBAThread* GBAThreadGetContext(void) {
233	pthread_once(&_contextOnce, _createTLS);
234	return pthread_getspecific(_contextKey);
235}
236
237void GBASyncPostFrame(struct GBASync* sync) {
238	if (!sync) {
239		return;
240	}
241
242	pthread_mutex_lock(&sync->videoFrameMutex);
243	++sync->videoFramePending;
244	--sync->videoFrameSkip;
245	if (sync->videoFrameSkip < 0) {
246		pthread_cond_broadcast(&sync->videoFrameAvailableCond);
247		if (sync->videoFrameWait) {
248			pthread_cond_wait(&sync->videoFrameRequiredCond, &sync->videoFrameMutex);
249		}
250	}
251	pthread_mutex_unlock(&sync->videoFrameMutex);
252}
253
254int GBASyncWaitFrameStart(struct GBASync* sync, int frameskip) {
255	if (!sync) {
256		return 1;
257	}
258
259	pthread_mutex_lock(&sync->videoFrameMutex);
260	pthread_cond_broadcast(&sync->videoFrameRequiredCond);
261	if (!sync->videoFrameOn) {
262		return 0;
263	}
264	pthread_cond_wait(&sync->videoFrameAvailableCond, &sync->videoFrameMutex);
265	sync->videoFramePending = 0;
266	sync->videoFrameSkip = frameskip;
267	return 1;
268}
269
270void GBASyncWaitFrameEnd(struct GBASync* sync) {
271	if (!sync) {
272		return;
273	}
274
275	pthread_mutex_unlock(&sync->videoFrameMutex);
276}
277
278int GBASyncDrawingFrame(struct GBASync* sync) {
279	return sync->videoFrameSkip <= 0;
280}
281
282void GBASyncProduceAudio(struct GBASync* sync, int wait) {
283	if (sync->audioWait && wait) {
284		pthread_cond_wait(&sync->audioRequiredCond, &sync->audioBufferMutex);
285	}
286	pthread_mutex_unlock(&sync->audioBufferMutex);
287}
288
289void GBASyncLockAudio(struct GBASync* sync) {
290	pthread_mutex_lock(&sync->audioBufferMutex);
291}
292
293void GBASyncConsumeAudio(struct GBASync* sync) {
294	pthread_cond_broadcast(&sync->audioRequiredCond);
295	pthread_mutex_unlock(&sync->audioBufferMutex);
296}