all repos — mgba @ 7e90eb2631c437ab2af551a09d0dbb529714a286

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