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