all repos — mgba @ 6716b13621ef871e630e9f1c5c5bea040cc734c5

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