all repos — mgba @ 678c5465ffc017d4dfa8b999cbdd3869db717cb1

mGBA Game Boy Advance Emulator

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

  1#include "gba-thread.h"
  2
  3#include "arm.h"
  4#include "gba.h"
  5#include "gba-serialize.h"
  6
  7#include "debugger/debugger.h"
  8
  9#include <signal.h>
 10
 11#ifdef USE_PTHREADS
 12static pthread_key_t _contextKey;
 13static pthread_once_t _contextOnce = PTHREAD_ONCE_INIT;
 14
 15static void _createTLS(void) {
 16	pthread_key_create(&_contextKey, 0);
 17}
 18#else
 19static DWORD _contextKey;
 20static INIT_ONCE _contextOnce = INIT_ONCE_STATIC_INIT;
 21
 22static BOOL CALLBACK _createTLS(PINIT_ONCE once, PVOID param, PVOID* context) {
 23	(void) (once);
 24	(void) (param);
 25	(void) (context);
 26	_contextKey = TlsAlloc();
 27	return TRUE;
 28}
 29#endif
 30
 31static void _changeState(struct GBAThread* threadContext, enum ThreadState newState, int broadcast) {
 32	MutexLock(&threadContext->stateMutex);
 33	threadContext->state = newState;
 34	if (broadcast) {
 35		ConditionWake(&threadContext->stateCond);
 36	}
 37	MutexUnlock(&threadContext->stateMutex);
 38}
 39
 40static void _waitOnInterrupt(struct GBAThread* threadContext) {
 41	while (threadContext->state == THREAD_INTERRUPTED) {
 42		ConditionWait(&threadContext->stateCond, &threadContext->stateMutex);
 43	}
 44}
 45
 46static THREAD_ENTRY _GBAThreadRun(void* context) {
 47#ifdef USE_PTHREADS
 48	pthread_once(&_contextOnce, _createTLS);
 49#else
 50	InitOnceExecuteOnce(&_contextOnce, _createTLS, NULL, 0);
 51#endif
 52
 53	struct GBA gba;
 54	struct ARMCore cpu;
 55	struct GBAThread* threadContext = context;
 56	struct ARMComponent* components[1] = {};
 57	int numComponents = 0;
 58	char* savedata = 0;
 59
 60	if (threadContext->debugger) {
 61		components[numComponents] = &threadContext->debugger->d;
 62		++numComponents;
 63	}
 64
 65#if !defined(_WIN32) && defined(USE_PTHREADS)
 66	sigset_t signals;
 67	sigemptyset(&signals);
 68	pthread_sigmask(SIG_SETMASK, &signals, 0);
 69#endif
 70
 71	gba.logHandler = threadContext->logHandler;
 72	GBACreate(&gba);
 73	ARMSetComponents(&cpu, &gba.d, numComponents, components);
 74	ARMInit(&cpu);
 75	ARMReset(&cpu);
 76	threadContext->gba = &gba;
 77	gba.sync = &threadContext->sync;
 78	gba.logLevel = threadContext->logLevel;
 79#ifdef USE_PTHREADS
 80	pthread_setspecific(_contextKey, threadContext);
 81#else
 82	TlsSetValue(_contextKey, threadContext);
 83#endif
 84	if (threadContext->renderer) {
 85		GBAVideoAssociateRenderer(&gba.video, threadContext->renderer);
 86	}
 87
 88	if (threadContext->fd >= 0) {
 89		if (threadContext->fname) {
 90			char* dotPoint = strrchr(threadContext->fname, '.');
 91			if (dotPoint > strrchr(threadContext->fname, '/') && dotPoint[1] && dotPoint[2] && dotPoint[3]) {
 92				savedata = strdup(threadContext->fname);
 93				dotPoint = strrchr(savedata, '.');
 94				dotPoint[1] = 's';
 95				dotPoint[2] = 'a';
 96				dotPoint[3] = 'v';
 97				dotPoint[4] = '\0';
 98			} else if (dotPoint) {
 99				savedata = malloc((dotPoint - threadContext->fname + 5) * sizeof(char));
100				strncpy(savedata, threadContext->fname, dotPoint - threadContext->fname + 1);
101				strcat(savedata, "sav");
102			} else {
103				savedata = malloc(strlen(threadContext->fname + 5));
104				strcpy(savedata, threadContext->fname);
105				strcat(savedata, "sav");
106			}
107		}
108		gba.savefile = savedata;
109		GBALoadROM(&gba, threadContext->fd, threadContext->fname);
110		if (threadContext->biosFd >= 0) {
111			GBALoadBIOS(&gba, threadContext->biosFd);
112		}
113	}
114
115	if (threadContext->debugger) {
116		GBAAttachDebugger(&gba, threadContext->debugger);
117		ARMDebuggerEnter(threadContext->debugger, DEBUGGER_ENTER_ATTACHED);
118	}
119
120	GBASIOSetDriverSet(&gba.sio, &threadContext->sioDrivers);
121
122	gba.keySource = &threadContext->activeKeys;
123
124	if (threadContext->startCallback) {
125		threadContext->startCallback(threadContext);
126	}
127
128	_changeState(threadContext, THREAD_RUNNING, 1);
129
130	while (threadContext->state < THREAD_EXITING) {
131		if (threadContext->debugger) {
132			struct ARMDebugger* debugger = threadContext->debugger;
133			ARMDebuggerRun(debugger);
134			if (debugger->state == DEBUGGER_SHUTDOWN) {
135				_changeState(threadContext, THREAD_EXITING, 0);
136			}
137		} else {
138			while (threadContext->state == THREAD_RUNNING) {
139				ARMRun(&cpu);
140			}
141		}
142		MutexLock(&threadContext->stateMutex);
143		while (threadContext->state == THREAD_PAUSED || threadContext->state == THREAD_INTERRUPTED) {
144			ConditionWait(&threadContext->stateCond, &threadContext->stateMutex);
145		}
146		MutexUnlock(&threadContext->stateMutex);
147	}
148
149	while (threadContext->state != THREAD_SHUTDOWN) {
150		_changeState(threadContext, THREAD_SHUTDOWN, 0);
151	}
152
153	if (threadContext->cleanCallback) {
154		threadContext->cleanCallback(threadContext);
155	}
156
157	threadContext->gba = 0;
158	ARMDeinit(&cpu);
159	GBADestroy(&gba);
160
161	ConditionWake(&threadContext->sync.videoFrameAvailableCond);
162	ConditionWake(&threadContext->sync.audioRequiredCond);
163	free(savedata);
164
165	return 0;
166}
167
168void GBAMapOptionsToContext(struct StartupOptions* opts, struct GBAThread* threadContext) {
169	threadContext->fd = opts->fd;
170	threadContext->fname = opts->fname;
171	threadContext->biosFd = opts->biosFd;
172	threadContext->frameskip = opts->frameskip;
173	threadContext->logLevel = opts->logLevel;
174	threadContext->rewindBufferCapacity = opts->rewindBufferCapacity;
175	threadContext->rewindBufferInterval = opts->rewindBufferInterval;
176}
177
178int GBAThreadStart(struct GBAThread* threadContext) {
179	// TODO: error check
180	threadContext->activeKeys = 0;
181	threadContext->state = THREAD_INITIALIZED;
182	threadContext->sync.videoFrameOn = 1;
183	threadContext->sync.videoFrameSkip = 0;
184
185	threadContext->rewindBufferNext = threadContext->rewindBufferInterval;
186	threadContext->rewindBufferSize = 0;
187	if (threadContext->rewindBufferCapacity) {
188		threadContext->rewindBuffer = calloc(threadContext->rewindBufferCapacity, sizeof(void*));
189	} else {
190		threadContext->rewindBuffer = 0;
191	}
192
193	MutexInit(&threadContext->stateMutex);
194	ConditionInit(&threadContext->stateCond);
195
196	MutexInit(&threadContext->sync.videoFrameMutex);
197	ConditionInit(&threadContext->sync.videoFrameAvailableCond);
198	ConditionInit(&threadContext->sync.videoFrameRequiredCond);
199	MutexInit(&threadContext->sync.audioBufferMutex);
200	ConditionInit(&threadContext->sync.audioRequiredCond);
201
202#ifndef _WIN32
203	sigset_t signals;
204	sigemptyset(&signals);
205	sigaddset(&signals, SIGINT);
206	sigaddset(&signals, SIGTRAP);
207	pthread_sigmask(SIG_BLOCK, &signals, 0);
208#endif
209
210	MutexLock(&threadContext->stateMutex);
211	ThreadCreate(&threadContext->thread, _GBAThreadRun, threadContext);
212	while (threadContext->state < THREAD_RUNNING) {
213		ConditionWait(&threadContext->stateCond, &threadContext->stateMutex);
214	}
215	MutexUnlock(&threadContext->stateMutex);
216
217	return 0;
218}
219
220int GBAThreadHasStarted(struct GBAThread* threadContext) {
221	int hasStarted;
222	MutexLock(&threadContext->stateMutex);
223	hasStarted = threadContext->state > THREAD_INITIALIZED;
224	MutexUnlock(&threadContext->stateMutex);
225	return hasStarted;
226}
227
228void GBAThreadEnd(struct GBAThread* threadContext) {
229	MutexLock(&threadContext->stateMutex);
230	if (threadContext->debugger && threadContext->debugger->state == DEBUGGER_RUNNING) {
231		threadContext->debugger->state = DEBUGGER_EXITING;
232	}
233	threadContext->state = THREAD_EXITING;
234	MutexUnlock(&threadContext->stateMutex);
235	MutexLock(&threadContext->sync.audioBufferMutex);
236	threadContext->sync.audioWait = 0;
237	ConditionWake(&threadContext->sync.audioRequiredCond);
238	MutexUnlock(&threadContext->sync.audioBufferMutex);
239}
240
241void GBAThreadJoin(struct GBAThread* threadContext) {
242	MutexLock(&threadContext->sync.videoFrameMutex);
243	threadContext->sync.videoFrameWait = 0;
244	ConditionWake(&threadContext->sync.videoFrameRequiredCond);
245	MutexUnlock(&threadContext->sync.videoFrameMutex);
246
247	ThreadJoin(threadContext->thread);
248
249	MutexDeinit(&threadContext->stateMutex);
250	ConditionDeinit(&threadContext->stateCond);
251
252	MutexDeinit(&threadContext->sync.videoFrameMutex);
253	ConditionWake(&threadContext->sync.videoFrameAvailableCond);
254	ConditionDeinit(&threadContext->sync.videoFrameAvailableCond);
255	ConditionWake(&threadContext->sync.videoFrameRequiredCond);
256	ConditionDeinit(&threadContext->sync.videoFrameRequiredCond);
257
258	ConditionWake(&threadContext->sync.audioRequiredCond);
259	ConditionDeinit(&threadContext->sync.audioRequiredCond);
260	MutexDeinit(&threadContext->sync.audioBufferMutex);
261
262	int i;
263	for (i = 0; i < threadContext->rewindBufferCapacity; ++i) {
264		if (threadContext->rewindBuffer[i]) {
265			GBADeallocateState(threadContext->rewindBuffer[i]);
266		}
267	}
268	free(threadContext->rewindBuffer);
269}
270
271void GBAThreadInterrupt(struct GBAThread* threadContext) {
272	MutexLock(&threadContext->stateMutex);
273	_waitOnInterrupt(threadContext);
274	threadContext->savedState = threadContext->state;
275	threadContext->state = THREAD_INTERRUPTED;
276	if (threadContext->debugger && threadContext->debugger->state == DEBUGGER_RUNNING) {
277		threadContext->debugger->state = DEBUGGER_EXITING;
278	}
279	MutexUnlock(&threadContext->stateMutex);
280}
281
282void GBAThreadContinue(struct GBAThread* threadContext) {
283	_changeState(threadContext, threadContext->savedState, 1);
284}
285
286void GBAThreadPause(struct GBAThread* threadContext) {
287	int frameOn = 1;
288	MutexLock(&threadContext->stateMutex);
289	_waitOnInterrupt(threadContext);
290	if (threadContext->state == THREAD_RUNNING) {
291		if (threadContext->debugger && threadContext->debugger->state == DEBUGGER_RUNNING) {
292			threadContext->debugger->state = DEBUGGER_EXITING;
293		}
294		threadContext->state = THREAD_PAUSED;
295		frameOn = 0;
296	}
297	MutexUnlock(&threadContext->stateMutex);
298	MutexLock(&threadContext->sync.videoFrameMutex);
299	if (frameOn != threadContext->sync.videoFrameOn) {
300		threadContext->sync.videoFrameOn = frameOn;
301		ConditionWake(&threadContext->sync.videoFrameAvailableCond);
302	}
303	MutexUnlock(&threadContext->sync.videoFrameMutex);
304}
305
306void GBAThreadUnpause(struct GBAThread* threadContext) {
307	int frameOn = 1;
308	MutexLock(&threadContext->stateMutex);
309	_waitOnInterrupt(threadContext);
310	if (threadContext->state == THREAD_PAUSED) {
311		threadContext->state = THREAD_RUNNING;
312		ConditionWake(&threadContext->stateCond);
313	}
314	MutexUnlock(&threadContext->stateMutex);
315	MutexLock(&threadContext->sync.videoFrameMutex);
316	if (frameOn != threadContext->sync.videoFrameOn) {
317		threadContext->sync.videoFrameOn = frameOn;
318		ConditionWake(&threadContext->sync.videoFrameAvailableCond);
319	}
320	MutexUnlock(&threadContext->sync.videoFrameMutex);
321}
322
323int GBAThreadIsPaused(struct GBAThread* threadContext) {
324	int isPaused;
325	MutexLock(&threadContext->stateMutex);
326	_waitOnInterrupt(threadContext);
327	isPaused = threadContext->state == THREAD_PAUSED;
328	MutexUnlock(&threadContext->stateMutex);
329	return isPaused;
330}
331
332void GBAThreadTogglePause(struct GBAThread* threadContext) {
333	int frameOn = 1;
334	MutexLock(&threadContext->stateMutex);
335	_waitOnInterrupt(threadContext);
336	if (threadContext->state == THREAD_PAUSED) {
337		threadContext->state = THREAD_RUNNING;
338		ConditionWake(&threadContext->stateCond);
339	} else if (threadContext->state == THREAD_RUNNING) {
340		if (threadContext->debugger && threadContext->debugger->state == DEBUGGER_RUNNING) {
341			threadContext->debugger->state = DEBUGGER_EXITING;
342		}
343		threadContext->state = THREAD_PAUSED;
344		frameOn = 0;
345	}
346	MutexUnlock(&threadContext->stateMutex);
347	MutexLock(&threadContext->sync.videoFrameMutex);
348	if (frameOn != threadContext->sync.videoFrameOn) {
349		threadContext->sync.videoFrameOn = frameOn;
350		ConditionWake(&threadContext->sync.videoFrameAvailableCond);
351	}
352	MutexUnlock(&threadContext->sync.videoFrameMutex);
353}
354
355#ifdef USE_PTHREADS
356struct GBAThread* GBAThreadGetContext(void) {
357	pthread_once(&_contextOnce, _createTLS);
358	return pthread_getspecific(_contextKey);
359}
360#else
361struct GBAThread* GBAThreadGetContext(void) {
362	InitOnceExecuteOnce(&_contextOnce, _createTLS, NULL, 0);
363	return TlsGetValue(_contextKey);
364}
365#endif
366
367void GBASyncPostFrame(struct GBASync* sync) {
368	if (!sync) {
369		return;
370	}
371
372	MutexLock(&sync->videoFrameMutex);
373	++sync->videoFramePending;
374	--sync->videoFrameSkip;
375	if (sync->videoFrameSkip < 0) {
376		ConditionWake(&sync->videoFrameAvailableCond);
377		while (sync->videoFrameWait && sync->videoFramePending) {
378			ConditionWait(&sync->videoFrameRequiredCond, &sync->videoFrameMutex);
379		}
380	}
381	MutexUnlock(&sync->videoFrameMutex);
382
383	struct GBAThread* thread = GBAThreadGetContext();
384	if (thread->rewindBuffer) {
385		--thread->rewindBufferNext;
386		if (thread->rewindBufferNext <= 0) {
387			thread->rewindBufferNext = thread->rewindBufferInterval;
388			GBARecordFrame(thread);
389		}
390	}
391	if (thread->frameCallback) {
392		thread->frameCallback(thread);
393	}
394}
395
396int GBASyncWaitFrameStart(struct GBASync* sync, int frameskip) {
397	if (!sync) {
398		return 1;
399	}
400
401	MutexLock(&sync->videoFrameMutex);
402	ConditionWake(&sync->videoFrameRequiredCond);
403	if (!sync->videoFrameOn) {
404		return 0;
405	}
406	ConditionWait(&sync->videoFrameAvailableCond, &sync->videoFrameMutex);
407	sync->videoFramePending = 0;
408	sync->videoFrameSkip = frameskip;
409	return 1;
410}
411
412void GBASyncWaitFrameEnd(struct GBASync* sync) {
413	if (!sync) {
414		return;
415	}
416
417	MutexUnlock(&sync->videoFrameMutex);
418}
419
420int GBASyncDrawingFrame(struct GBASync* sync) {
421	return sync->videoFrameSkip <= 0;
422}
423
424void GBASyncProduceAudio(struct GBASync* sync, int wait) {
425	if (sync->audioWait && wait) {
426		// TODO loop properly in event of spurious wakeups
427		ConditionWait(&sync->audioRequiredCond, &sync->audioBufferMutex);
428	}
429	MutexUnlock(&sync->audioBufferMutex);
430}
431
432void GBASyncLockAudio(struct GBASync* sync) {
433	MutexLock(&sync->audioBufferMutex);
434}
435
436void GBASyncConsumeAudio(struct GBASync* sync) {
437	ConditionWake(&sync->audioRequiredCond);
438	MutexUnlock(&sync->audioBufferMutex);
439}