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