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