all repos — mgba @ 3a7350c8d2d7332713630b7b4cf47f6f11a74b8e

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