all repos — mgba @ 5d3669d22f756cd4711dbac6863335e8f74fac8d

mGBA Game Boy Advance Emulator

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

  1/* Copyright (c) 2013-2015 Jeffrey Pfau
  2 *
  3 * This Source Code Form is subject to the terms of the Mozilla Public
  4 * License, v. 2.0. If a copy of the MPL was not distributed with this
  5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  6#include "thread.h"
  7
  8#include "arm.h"
  9#include "gba/gba.h"
 10#include "gba/cheats.h"
 11#include "gba/serialize.h"
 12#include "gba/context/config.h"
 13#include "gba/rr/mgm.h"
 14#include "gba/rr/vbm.h"
 15
 16#include "debugger/debugger.h"
 17
 18#include "util/patch.h"
 19#include "util/vfs.h"
 20
 21#include "platform/commandline.h"
 22
 23#include <signal.h>
 24
 25static const float _defaultFPSTarget = 60.f;
 26
 27#ifndef DISABLE_THREADING
 28
 29static bool _reloadDirectories(struct GBAThread* threadContext) {
 30	GBADirectorySetDetachBase(&threadContext->dirs);
 31
 32	char basename[PATH_MAX];
 33	if (threadContext->fname) {
 34		char dirname[PATH_MAX];
 35		separatePath(threadContext->fname, dirname, basename, 0);
 36		GBADirectorySetAttachBase(&threadContext->dirs, VDirOpen(dirname));
 37	} else {
 38		return false;
 39	}
 40
 41	char path[PATH_MAX];
 42	snprintf(path, sizeof(path), "%s.sav", basename);
 43	threadContext->save = threadContext->dirs.save->openFile(threadContext->dirs.save, path, O_CREAT | O_RDWR);
 44
 45	if (!threadContext->patch) {
 46		snprintf(path, sizeof(path), "%s.ups", basename);
 47		threadContext->patch = threadContext->dirs.patch->openFile(threadContext->dirs.patch, path, O_RDONLY);
 48	}
 49	if (!threadContext->patch) {
 50		snprintf(path, sizeof(path), "%s.ips", basename);
 51		threadContext->patch = threadContext->dirs.patch->openFile(threadContext->dirs.patch, path, O_RDONLY);
 52	}
 53	if (!threadContext->patch) {
 54		snprintf(path, sizeof(path), "%s.bps", basename);
 55		threadContext->patch = threadContext->dirs.patch->openFile(threadContext->dirs.patch, path, O_RDONLY);
 56	}
 57	return true;
 58}
 59
 60#ifdef USE_PTHREADS
 61static pthread_key_t _contextKey;
 62static pthread_once_t _contextOnce = PTHREAD_ONCE_INIT;
 63
 64static void _createTLS(void) {
 65	pthread_key_create(&_contextKey, 0);
 66}
 67#elif _WIN32
 68static DWORD _contextKey;
 69static INIT_ONCE _contextOnce = INIT_ONCE_STATIC_INIT;
 70
 71static BOOL CALLBACK _createTLS(PINIT_ONCE once, PVOID param, PVOID* context) {
 72	UNUSED(once);
 73	UNUSED(param);
 74	UNUSED(context);
 75	_contextKey = TlsAlloc();
 76	return TRUE;
 77}
 78#endif
 79
 80static void _changeState(struct GBAThread* threadContext, enum ThreadState newState, bool broadcast) {
 81	MutexLock(&threadContext->stateMutex);
 82	threadContext->state = newState;
 83	if (broadcast) {
 84		ConditionWake(&threadContext->stateCond);
 85	}
 86	MutexUnlock(&threadContext->stateMutex);
 87}
 88
 89static void _waitOnInterrupt(struct GBAThread* threadContext) {
 90	while (threadContext->state == THREAD_INTERRUPTED) {
 91		ConditionWait(&threadContext->stateCond, &threadContext->stateMutex);
 92	}
 93}
 94
 95static void _waitUntilNotState(struct GBAThread* threadContext, enum ThreadState oldState) {
 96	MutexLock(&threadContext->sync.videoFrameMutex);
 97	bool videoFrameWait = threadContext->sync.videoFrameWait;
 98	threadContext->sync.videoFrameWait = false;
 99	MutexUnlock(&threadContext->sync.videoFrameMutex);
100
101	while (threadContext->state == oldState) {
102		MutexUnlock(&threadContext->stateMutex);
103
104		if (!MutexTryLock(&threadContext->sync.videoFrameMutex)) {
105			ConditionWake(&threadContext->sync.videoFrameRequiredCond);
106			MutexUnlock(&threadContext->sync.videoFrameMutex);
107		}
108
109		if (!MutexTryLock(&threadContext->sync.audioBufferMutex)) {
110			ConditionWake(&threadContext->sync.audioRequiredCond);
111			MutexUnlock(&threadContext->sync.audioBufferMutex);
112		}
113
114		MutexLock(&threadContext->stateMutex);
115		ConditionWake(&threadContext->stateCond);
116	}
117
118	MutexLock(&threadContext->sync.videoFrameMutex);
119	threadContext->sync.videoFrameWait = videoFrameWait;
120	MutexUnlock(&threadContext->sync.videoFrameMutex);
121}
122
123static void _pauseThread(struct GBAThread* threadContext, bool onThread) {
124	threadContext->state = THREAD_PAUSING;
125	if (!onThread) {
126		_waitUntilNotState(threadContext, THREAD_PAUSING);
127	}
128}
129
130struct GBAThreadStop {
131	struct GBAStopCallback d;
132	struct GBAThread* p;
133};
134
135static void _stopCallback(struct GBAStopCallback* stop) {
136	struct GBAThreadStop* callback = (struct GBAThreadStop*) stop;
137	if (callback->p->stopCallback(callback->p)) {
138		_changeState(callback->p, THREAD_EXITING, false);
139	}
140}
141
142static THREAD_ENTRY _GBAThreadRun(void* context) {
143#ifdef USE_PTHREADS
144	pthread_once(&_contextOnce, _createTLS);
145#elif _WIN32
146	InitOnceExecuteOnce(&_contextOnce, _createTLS, NULL, 0);
147#endif
148
149	struct GBA gba;
150	struct ARMCore cpu;
151	struct Patch patch;
152	struct GBACheatDevice cheatDevice;
153	struct GBAThread* threadContext = context;
154	struct ARMComponent* components[GBA_COMPONENT_MAX] = { 0 };
155	struct GBARRContext* movie = 0;
156	int numComponents = GBA_COMPONENT_MAX;
157
158	ThreadSetName("CPU Thread");
159
160#if !defined(_WIN32) && defined(USE_PTHREADS)
161	sigset_t signals;
162	sigemptyset(&signals);
163	pthread_sigmask(SIG_SETMASK, &signals, 0);
164#endif
165
166	GBACreate(&gba);
167	ARMSetComponents(&cpu, &gba.d, numComponents, components);
168	ARMInit(&cpu);
169	gba.sync = &threadContext->sync;
170	threadContext->gba = &gba;
171	threadContext->cpu = &cpu;
172	gba.logLevel = threadContext->logLevel;
173	gba.logHandler = threadContext->logHandler;
174	gba.stream = threadContext->stream;
175	gba.video.frameskip = threadContext->frameskip;
176
177	struct GBAThreadStop stop;
178	if (threadContext->stopCallback) {
179		stop.d.stop = _stopCallback;
180		stop.p = threadContext;
181		gba.stopCallback = &stop.d;
182	}
183
184	gba.idleOptimization = threadContext->idleOptimization;
185#ifdef USE_PTHREADS
186	pthread_setspecific(_contextKey, threadContext);
187#elif _WIN32
188	TlsSetValue(_contextKey, threadContext);
189#endif
190
191	if (threadContext->audioBuffers) {
192		GBAAudioResizeBuffer(&gba.audio, threadContext->audioBuffers);
193	} else {
194		threadContext->audioBuffers = GBA_AUDIO_SAMPLES;
195	}
196
197	if (threadContext->renderer) {
198		GBAVideoAssociateRenderer(&gba.video, threadContext->renderer);
199	}
200
201	if (threadContext->rom) {
202		if (GBAIsMB(threadContext->rom)) {
203			GBALoadMB(&gba, threadContext->rom, threadContext->fname);
204		} else {
205			GBALoadROM(&gba, threadContext->rom, threadContext->save, threadContext->fname);
206		}
207
208		struct GBACartridgeOverride override;
209		const struct GBACartridge* cart = (const struct GBACartridge*) gba.pristineRom;
210		memcpy(override.id, &cart->id, sizeof(override.id));
211		if (GBAOverrideFind(threadContext->overrides, &override)) {
212			GBAOverrideApply(&gba, &override);
213		}
214		if (threadContext->hasOverride) {
215			GBAOverrideApply(&gba, &threadContext->override);
216		}
217
218		if (threadContext->patch && loadPatch(threadContext->patch, &patch)) {
219			GBAApplyPatch(&gba, &patch);
220		}
221	}
222
223	if (threadContext->bios && GBAIsBIOS(threadContext->bios)) {
224		GBALoadBIOS(&gba, threadContext->bios);
225	}
226
227	if (threadContext->movie) {
228		struct VDir* movieDir = VDirOpen(threadContext->movie);
229		if (!movieDir) {
230			movieDir = VDirOpenArchive(threadContext->movie);
231		}
232		if (movieDir) {
233			struct GBAMGMContext* mgm = malloc(sizeof(*mgm));
234			GBAMGMContextCreate(mgm);
235			if (!GBAMGMSetStream(mgm, movieDir)) {
236				mgm->d.destroy(&mgm->d);
237			} else {
238				movie = &mgm->d;
239			}
240		} else {
241			struct VFile* movieFile = VFileOpen(threadContext->movie, O_RDONLY);
242			if (movieFile) {
243				struct GBAVBMContext* vbm = malloc(sizeof(*vbm));
244				GBAVBMContextCreate(vbm);
245				if (!GBAVBMSetStream(vbm, movieFile)) {
246					vbm->d.destroy(&vbm->d);
247				} else {
248					movie = &vbm->d;
249				}
250			}
251		}
252	}
253
254	ARMReset(&cpu);
255
256	if (movie) {
257		gba.rr = movie;
258		movie->startPlaying(movie, false);
259		GBARRInitPlay(&gba);
260	}
261
262	if (threadContext->skipBios && gba.pristineRom) {
263		GBASkipBIOS(&gba);
264	}
265
266	if (!threadContext->cheats) {
267		GBACheatDeviceCreate(&cheatDevice);
268		threadContext->cheats = &cheatDevice;
269	}
270	if (threadContext->cheatsFile) {
271		GBACheatParseFile(threadContext->cheats, threadContext->cheatsFile);
272	}
273	GBACheatAttachDevice(&gba, threadContext->cheats);
274
275	if (threadContext->debugger) {
276		threadContext->debugger->log = GBADebuggerLogShim;
277		GBAAttachDebugger(&gba, threadContext->debugger);
278		ARMDebuggerEnter(threadContext->debugger, DEBUGGER_ENTER_ATTACHED, 0);
279	}
280
281	GBASIOSetDriverSet(&gba.sio, &threadContext->sioDrivers);
282
283	if (threadContext->volume == 0) {
284		threadContext->volume = GBA_AUDIO_VOLUME_MAX;
285	}
286	if (threadContext->mute) {
287		gba.audio.masterVolume = 0;
288	} else {
289		gba.audio.masterVolume = threadContext->volume;
290	}
291
292	gba.keySource = &threadContext->activeKeys;
293
294	if (threadContext->startCallback) {
295		threadContext->startCallback(threadContext);
296	}
297
298	_changeState(threadContext, THREAD_RUNNING, true);
299
300	while (threadContext->state < THREAD_EXITING) {
301		if (threadContext->debugger) {
302			struct ARMDebugger* debugger = threadContext->debugger;
303			ARMDebuggerRun(debugger);
304			if (debugger->state == DEBUGGER_SHUTDOWN) {
305				_changeState(threadContext, THREAD_EXITING, false);
306			}
307		} else {
308			while (threadContext->state == THREAD_RUNNING) {
309				ARMRunLoop(&cpu);
310			}
311		}
312
313		int resetScheduled = 0;
314		MutexLock(&threadContext->stateMutex);
315		while (threadContext->state > THREAD_RUNNING && threadContext->state < THREAD_EXITING) {
316			if (threadContext->state == THREAD_PAUSING) {
317				threadContext->state = THREAD_PAUSED;
318				ConditionWake(&threadContext->stateCond);
319			}
320			if (threadContext->state == THREAD_INTERRUPTING) {
321				threadContext->state = THREAD_INTERRUPTED;
322				ConditionWake(&threadContext->stateCond);
323			}
324			if (threadContext->state == THREAD_RUN_ON) {
325				if (threadContext->run) {
326					threadContext->run(threadContext);
327				}
328				threadContext->state = threadContext->savedState;
329				ConditionWake(&threadContext->stateCond);
330			}
331			if (threadContext->state == THREAD_RESETING) {
332				threadContext->state = THREAD_RUNNING;
333				resetScheduled = 1;
334			}
335			while (threadContext->state == THREAD_PAUSED || threadContext->state == THREAD_INTERRUPTED) {
336				ConditionWait(&threadContext->stateCond, &threadContext->stateMutex);
337			}
338		}
339		MutexUnlock(&threadContext->stateMutex);
340		if (resetScheduled) {
341			ARMReset(&cpu);
342			if (threadContext->skipBios && gba.pristineRom) {
343				GBASkipBIOS(&gba);
344			}
345		}
346	}
347
348	while (threadContext->state < THREAD_SHUTDOWN) {
349		_changeState(threadContext, THREAD_SHUTDOWN, false);
350	}
351
352	if (threadContext->cleanCallback) {
353		threadContext->cleanCallback(threadContext);
354	}
355
356	threadContext->gba = 0;
357	ARMDeinit(&cpu);
358	GBADestroy(&gba);
359	if (&cheatDevice == threadContext->cheats) {
360		GBACheatDeviceDestroy(&cheatDevice);
361	}
362
363	if (movie) {
364		movie->destroy(movie);
365		free(movie);
366	}
367
368	threadContext->sync.videoFrameOn = false;
369	ConditionWake(&threadContext->sync.videoFrameAvailableCond);
370	ConditionWake(&threadContext->sync.audioRequiredCond);
371
372	return 0;
373}
374
375void GBAMapOptionsToContext(const struct GBAOptions* opts, struct GBAThread* threadContext) {
376	if (opts->useBios) {
377		threadContext->bios = VFileOpen(opts->bios, O_RDONLY);
378	} else {
379		threadContext->bios = 0;
380	}
381	threadContext->frameskip = opts->frameskip;
382	threadContext->volume = opts->volume;
383	threadContext->mute = opts->mute;
384	threadContext->logLevel = opts->logLevel;
385	if (opts->rewindEnable) {
386		threadContext->rewindBufferCapacity = opts->rewindBufferCapacity;
387		threadContext->rewindBufferInterval = opts->rewindBufferInterval;
388	} else {
389		threadContext->rewindBufferCapacity = 0;
390	}
391	threadContext->skipBios = opts->skipBios;
392	threadContext->sync.audioWait = opts->audioSync;
393	threadContext->sync.videoFrameWait = opts->videoSync;
394
395	if (opts->fpsTarget) {
396		threadContext->fpsTarget = opts->fpsTarget;
397	}
398
399	if (opts->audioBuffers) {
400		threadContext->audioBuffers = opts->audioBuffers;
401	}
402
403	threadContext->idleOptimization = opts->idleOptimization;
404}
405
406void GBAMapArgumentsToContext(const struct GBAArguments* args, struct GBAThread* threadContext) {
407	GBAThreadLoadROM(threadContext, args->fname);
408	threadContext->fname = args->fname;
409	threadContext->patch = VFileOpen(args->patch, O_RDONLY);
410	threadContext->cheatsFile = VFileOpen(args->cheatsFile, O_RDONLY);
411	threadContext->movie = args->movie;
412}
413
414bool GBAThreadStart(struct GBAThread* threadContext) {
415	// TODO: error check
416	threadContext->activeKeys = 0;
417	threadContext->state = THREAD_INITIALIZED;
418	threadContext->sync.videoFrameOn = true;
419
420	threadContext->rewindBuffer = 0;
421	threadContext->rewindScreenBuffer = 0;
422	int newCapacity = threadContext->rewindBufferCapacity;
423	int newInterval = threadContext->rewindBufferInterval;
424	threadContext->rewindBufferCapacity = 0;
425	threadContext->rewindBufferInterval = 0;
426	GBARewindSettingsChanged(threadContext, newCapacity, newInterval);
427
428	if (!threadContext->fpsTarget) {
429		threadContext->fpsTarget = _defaultFPSTarget;
430	}
431
432	bool bootBios = threadContext->bootBios && threadContext->bios;
433
434	if (threadContext->rom && (!GBAIsROM(threadContext->rom) || bootBios)) {
435		threadContext->rom->close(threadContext->rom);
436		threadContext->rom = 0;
437	}
438
439	if (!threadContext->rom && !bootBios) {
440		threadContext->state = THREAD_SHUTDOWN;
441		return false;
442	}
443
444	GBADirectorySetInit(&threadContext->dirs);
445	_reloadDirectories(threadContext);
446
447	MutexInit(&threadContext->stateMutex);
448	ConditionInit(&threadContext->stateCond);
449
450	MutexInit(&threadContext->sync.videoFrameMutex);
451	ConditionInit(&threadContext->sync.videoFrameAvailableCond);
452	ConditionInit(&threadContext->sync.videoFrameRequiredCond);
453	MutexInit(&threadContext->sync.audioBufferMutex);
454	ConditionInit(&threadContext->sync.audioRequiredCond);
455
456	threadContext->interruptDepth = 0;
457
458#ifdef USE_PTHREADS
459	sigset_t signals;
460	sigemptyset(&signals);
461	sigaddset(&signals, SIGINT);
462	sigaddset(&signals, SIGTRAP);
463	pthread_sigmask(SIG_BLOCK, &signals, 0);
464#endif
465
466	MutexLock(&threadContext->stateMutex);
467	ThreadCreate(&threadContext->thread, _GBAThreadRun, threadContext);
468	while (threadContext->state < THREAD_RUNNING) {
469		ConditionWait(&threadContext->stateCond, &threadContext->stateMutex);
470	}
471	MutexUnlock(&threadContext->stateMutex);
472
473	return true;
474}
475
476bool GBAThreadHasStarted(struct GBAThread* threadContext) {
477	bool hasStarted;
478	MutexLock(&threadContext->stateMutex);
479	hasStarted = threadContext->state > THREAD_INITIALIZED;
480	MutexUnlock(&threadContext->stateMutex);
481	return hasStarted;
482}
483
484bool GBAThreadHasExited(struct GBAThread* threadContext) {
485	bool hasExited;
486	MutexLock(&threadContext->stateMutex);
487	hasExited = threadContext->state > THREAD_EXITING;
488	MutexUnlock(&threadContext->stateMutex);
489	return hasExited;
490}
491
492bool GBAThreadHasCrashed(struct GBAThread* threadContext) {
493	bool hasExited;
494	MutexLock(&threadContext->stateMutex);
495	hasExited = threadContext->state == THREAD_CRASHED;
496	MutexUnlock(&threadContext->stateMutex);
497	return hasExited;
498}
499
500void GBAThreadEnd(struct GBAThread* threadContext) {
501	MutexLock(&threadContext->stateMutex);
502	_waitOnInterrupt(threadContext);
503	threadContext->state = THREAD_EXITING;
504	if (threadContext->gba) {
505		threadContext->gba->cpu->halted = false;
506	}
507	ConditionWake(&threadContext->stateCond);
508	MutexUnlock(&threadContext->stateMutex);
509	MutexLock(&threadContext->sync.audioBufferMutex);
510	threadContext->sync.audioWait = 0;
511	ConditionWake(&threadContext->sync.audioRequiredCond);
512	MutexUnlock(&threadContext->sync.audioBufferMutex);
513
514	MutexLock(&threadContext->sync.videoFrameMutex);
515	threadContext->sync.videoFrameWait = false;
516	threadContext->sync.videoFrameOn = false;
517	ConditionWake(&threadContext->sync.videoFrameRequiredCond);
518	ConditionWake(&threadContext->sync.videoFrameAvailableCond);
519	MutexUnlock(&threadContext->sync.videoFrameMutex);
520}
521
522void GBAThreadReset(struct GBAThread* threadContext) {
523	MutexLock(&threadContext->stateMutex);
524	_waitOnInterrupt(threadContext);
525	threadContext->state = THREAD_RESETING;
526	ConditionWake(&threadContext->stateCond);
527	MutexUnlock(&threadContext->stateMutex);
528}
529
530void GBAThreadJoin(struct GBAThread* threadContext) {
531	ThreadJoin(threadContext->thread);
532
533	MutexDeinit(&threadContext->stateMutex);
534	ConditionDeinit(&threadContext->stateCond);
535
536	MutexDeinit(&threadContext->sync.videoFrameMutex);
537	ConditionWake(&threadContext->sync.videoFrameAvailableCond);
538	ConditionDeinit(&threadContext->sync.videoFrameAvailableCond);
539	ConditionWake(&threadContext->sync.videoFrameRequiredCond);
540	ConditionDeinit(&threadContext->sync.videoFrameRequiredCond);
541
542	ConditionWake(&threadContext->sync.audioRequiredCond);
543	ConditionDeinit(&threadContext->sync.audioRequiredCond);
544	MutexDeinit(&threadContext->sync.audioBufferMutex);
545
546	int i;
547	for (i = 0; i < threadContext->rewindBufferCapacity; ++i) {
548		if (threadContext->rewindBuffer[i]) {
549			GBADeallocateState(threadContext->rewindBuffer[i]);
550		}
551	}
552	free(threadContext->rewindBuffer);
553	free(threadContext->rewindScreenBuffer);
554
555	if (threadContext->rom) {
556		threadContext->rom->close(threadContext->rom);
557		threadContext->rom = 0;
558	}
559
560	if (threadContext->save) {
561		threadContext->save->close(threadContext->save);
562		threadContext->save = 0;
563	}
564
565	if (threadContext->bios) {
566		threadContext->bios->close(threadContext->bios);
567		threadContext->bios = 0;
568	}
569
570	if (threadContext->patch) {
571		threadContext->patch->close(threadContext->patch);
572		threadContext->patch = 0;
573	}
574
575	GBADirectorySetDeinit(&threadContext->dirs);
576}
577
578bool GBAThreadIsActive(struct GBAThread* threadContext) {
579	return threadContext->state >= THREAD_RUNNING && threadContext->state < THREAD_EXITING;
580}
581
582void GBAThreadInterrupt(struct GBAThread* threadContext) {
583	MutexLock(&threadContext->stateMutex);
584	++threadContext->interruptDepth;
585	if (threadContext->interruptDepth > 1 || !GBAThreadIsActive(threadContext)) {
586		MutexUnlock(&threadContext->stateMutex);
587		return;
588	}
589	threadContext->savedState = threadContext->state;
590	_waitOnInterrupt(threadContext);
591	threadContext->state = THREAD_INTERRUPTING;
592	threadContext->gba->cpu->nextEvent = 0;
593	ConditionWake(&threadContext->stateCond);
594	_waitUntilNotState(threadContext, THREAD_INTERRUPTING);
595	MutexUnlock(&threadContext->stateMutex);
596}
597
598void GBAThreadContinue(struct GBAThread* threadContext) {
599	MutexLock(&threadContext->stateMutex);
600	--threadContext->interruptDepth;
601	if (threadContext->interruptDepth < 1 && GBAThreadIsActive(threadContext)) {
602		threadContext->state = threadContext->savedState;
603		ConditionWake(&threadContext->stateCond);
604	}
605	MutexUnlock(&threadContext->stateMutex);
606}
607
608void GBARunOnThread(struct GBAThread* threadContext, void (*run)(struct GBAThread*)) {
609	MutexLock(&threadContext->stateMutex);
610	threadContext->run = run;
611	_waitOnInterrupt(threadContext);
612	threadContext->savedState = threadContext->state;
613	threadContext->state = THREAD_RUN_ON;
614	threadContext->gba->cpu->nextEvent = 0;
615	ConditionWake(&threadContext->stateCond);
616	_waitUntilNotState(threadContext, THREAD_RUN_ON);
617	MutexUnlock(&threadContext->stateMutex);
618}
619
620void GBAThreadPause(struct GBAThread* threadContext) {
621	bool frameOn = threadContext->sync.videoFrameOn;
622	MutexLock(&threadContext->stateMutex);
623	_waitOnInterrupt(threadContext);
624	if (threadContext->state == THREAD_RUNNING) {
625		_pauseThread(threadContext, false);
626		threadContext->frameWasOn = frameOn;
627		frameOn = false;
628	}
629	MutexUnlock(&threadContext->stateMutex);
630
631	GBASyncSetVideoSync(&threadContext->sync, frameOn);
632}
633
634void GBAThreadUnpause(struct GBAThread* threadContext) {
635	bool frameOn = threadContext->sync.videoFrameOn;
636	MutexLock(&threadContext->stateMutex);
637	_waitOnInterrupt(threadContext);
638	if (threadContext->state == THREAD_PAUSED || threadContext->state == THREAD_PAUSING) {
639		threadContext->state = THREAD_RUNNING;
640		ConditionWake(&threadContext->stateCond);
641		frameOn = threadContext->frameWasOn;
642	}
643	MutexUnlock(&threadContext->stateMutex);
644
645	GBASyncSetVideoSync(&threadContext->sync, frameOn);
646}
647
648bool GBAThreadIsPaused(struct GBAThread* threadContext) {
649	bool isPaused;
650	MutexLock(&threadContext->stateMutex);
651	_waitOnInterrupt(threadContext);
652	isPaused = threadContext->state == THREAD_PAUSED;
653	MutexUnlock(&threadContext->stateMutex);
654	return isPaused;
655}
656
657void GBAThreadTogglePause(struct GBAThread* threadContext) {
658	bool frameOn = threadContext->sync.videoFrameOn;
659	MutexLock(&threadContext->stateMutex);
660	_waitOnInterrupt(threadContext);
661	if (threadContext->state == THREAD_PAUSED || threadContext->state == THREAD_PAUSING) {
662		threadContext->state = THREAD_RUNNING;
663		ConditionWake(&threadContext->stateCond);
664		frameOn = threadContext->frameWasOn;
665	} else if (threadContext->state == THREAD_RUNNING) {
666		_pauseThread(threadContext, false);
667		threadContext->frameWasOn = frameOn;
668		frameOn = false;
669	}
670	MutexUnlock(&threadContext->stateMutex);
671
672	GBASyncSetVideoSync(&threadContext->sync, frameOn);
673}
674
675void GBAThreadPauseFromThread(struct GBAThread* threadContext) {
676	bool frameOn = true;
677	MutexLock(&threadContext->stateMutex);
678	_waitOnInterrupt(threadContext);
679	if (threadContext->state == THREAD_RUNNING) {
680		_pauseThread(threadContext, true);
681		frameOn = false;
682	}
683	MutexUnlock(&threadContext->stateMutex);
684
685	GBASyncSetVideoSync(&threadContext->sync, frameOn);
686}
687
688void GBAThreadLoadROM(struct GBAThread* threadContext, const char* fname) {
689	threadContext->rom = GBADirectorySetOpenPath(&threadContext->dirs, fname, GBAIsROM);
690}
691
692void GBAThreadReplaceROM(struct GBAThread* threadContext, const char* fname) {
693	GBAUnloadROM(threadContext->gba);
694
695	if (threadContext->rom) {
696		threadContext->rom->close(threadContext->rom);
697		threadContext->rom = 0;
698	}
699
700	if (threadContext->save) {
701		threadContext->save->close(threadContext->save);
702		threadContext->save = 0;
703	}
704
705	if (threadContext->dirs.archive) {
706		threadContext->dirs.archive->close(threadContext->dirs.archive);
707		threadContext->dirs.archive = 0;
708	}
709
710	GBAThreadLoadROM(threadContext, fname);
711
712	threadContext->fname = fname;
713	_reloadDirectories(threadContext);
714
715	GBARaiseIRQ(threadContext->gba, IRQ_GAMEPAK);
716	GBALoadROM(threadContext->gba, threadContext->rom, threadContext->save, threadContext->fname);
717}
718
719#ifdef USE_PTHREADS
720struct GBAThread* GBAThreadGetContext(void) {
721	pthread_once(&_contextOnce, _createTLS);
722	return pthread_getspecific(_contextKey);
723}
724#elif _WIN32
725struct GBAThread* GBAThreadGetContext(void) {
726	InitOnceExecuteOnce(&_contextOnce, _createTLS, NULL, 0);
727	return TlsGetValue(_contextKey);
728}
729#endif
730
731void GBAThreadTakeScreenshot(struct GBAThread* threadContext) {
732	GBATakeScreenshot(threadContext->gba, threadContext->dirs.screenshot);
733}
734
735#else
736struct GBAThread* GBAThreadGetContext(void) {
737	return 0;
738}
739#endif