src/gba/gba-thread.c (view raw)
1#include "gba-thread.h"
2
3#include "arm.h"
4#include "gba.h"
5#include "gba-serialize.h"
6
7#include "debugger/debugger.h"
8
9#include "util/patch.h"
10
11#include <signal.h>
12
13#ifdef USE_PTHREADS
14static pthread_key_t _contextKey;
15static pthread_once_t _contextOnce = PTHREAD_ONCE_INIT;
16
17static void _createTLS(void) {
18 pthread_key_create(&_contextKey, 0);
19}
20#else
21static DWORD _contextKey;
22static INIT_ONCE _contextOnce = INIT_ONCE_STATIC_INIT;
23
24static BOOL CALLBACK _createTLS(PINIT_ONCE once, PVOID param, PVOID* context) {
25 UNUSED(once);
26 UNUSED(param);
27 UNUSED(context);
28 _contextKey = TlsAlloc();
29 return TRUE;
30}
31#endif
32
33static void _changeState(struct GBAThread* threadContext, enum ThreadState newState, int broadcast) {
34 MutexLock(&threadContext->stateMutex);
35 threadContext->state = newState;
36 if (broadcast) {
37 ConditionWake(&threadContext->stateCond);
38 }
39 MutexUnlock(&threadContext->stateMutex);
40}
41
42static void _waitOnInterrupt(struct GBAThread* threadContext) {
43 while (threadContext->state == THREAD_INTERRUPTED) {
44 ConditionWait(&threadContext->stateCond, &threadContext->stateMutex);
45 }
46}
47
48static THREAD_ENTRY _GBAThreadRun(void* context) {
49#ifdef USE_PTHREADS
50 pthread_once(&_contextOnce, _createTLS);
51#else
52 InitOnceExecuteOnce(&_contextOnce, _createTLS, NULL, 0);
53#endif
54
55 struct GBA gba;
56 struct ARMCore cpu;
57 struct Patch patch;
58 struct GBAThread* threadContext = context;
59 struct ARMComponent* components[1] = {};
60 int numComponents = 0;
61 char* savedata = 0;
62
63 if (threadContext->debugger) {
64 components[numComponents] = &threadContext->debugger->d;
65 ++numComponents;
66 }
67
68#if !defined(_WIN32) && defined(USE_PTHREADS)
69 sigset_t signals;
70 sigemptyset(&signals);
71 pthread_sigmask(SIG_SETMASK, &signals, 0);
72#endif
73
74 gba.logHandler = threadContext->logHandler;
75 GBACreate(&gba);
76 ARMSetComponents(&cpu, &gba.d, numComponents, components);
77 ARMInit(&cpu);
78 ARMReset(&cpu);
79 threadContext->gba = &gba;
80 gba.sync = &threadContext->sync;
81 gba.logLevel = threadContext->logLevel;
82#ifdef USE_PTHREADS
83 pthread_setspecific(_contextKey, threadContext);
84#else
85 TlsSetValue(_contextKey, threadContext);
86#endif
87 if (threadContext->renderer) {
88 GBAVideoAssociateRenderer(&gba.video, threadContext->renderer);
89 }
90
91 if (threadContext->fd >= 0) {
92 if (threadContext->fname) {
93 char* dotPoint = strrchr(threadContext->fname, '.');
94 if (dotPoint > strrchr(threadContext->fname, '/') && dotPoint[1] && dotPoint[2] && dotPoint[3]) {
95 savedata = strdup(threadContext->fname);
96 dotPoint = strrchr(savedata, '.');
97 dotPoint[1] = 's';
98 dotPoint[2] = 'a';
99 dotPoint[3] = 'v';
100 dotPoint[4] = '\0';
101 } else if (dotPoint) {
102 savedata = malloc((dotPoint - threadContext->fname + 5) * sizeof(char));
103 strncpy(savedata, threadContext->fname, dotPoint - threadContext->fname + 1);
104 strcat(savedata, "sav");
105 } else {
106 savedata = malloc(strlen(threadContext->fname + 5));
107 strcpy(savedata, threadContext->fname);
108 strcat(savedata, "sav");
109 }
110 }
111 gba.savefile = savedata;
112 GBALoadROM(&gba, threadContext->fd, threadContext->fname);
113 if (threadContext->biosFd >= 0) {
114 GBALoadBIOS(&gba, threadContext->biosFd);
115 }
116
117 if (threadContext->patchFd >= 0 && loadPatch(threadContext->patchFd, &patch)) {
118 GBAApplyPatch(&gba, &patch);
119 }
120 }
121
122 if (threadContext->debugger) {
123 GBAAttachDebugger(&gba, threadContext->debugger);
124 ARMDebuggerEnter(threadContext->debugger, DEBUGGER_ENTER_ATTACHED);
125 }
126
127 GBASIOSetDriverSet(&gba.sio, &threadContext->sioDrivers);
128
129 gba.keySource = &threadContext->activeKeys;
130
131 if (threadContext->startCallback) {
132 threadContext->startCallback(threadContext);
133 }
134
135 _changeState(threadContext, THREAD_RUNNING, 1);
136
137 while (threadContext->state < THREAD_EXITING) {
138 if (threadContext->debugger) {
139 struct ARMDebugger* debugger = threadContext->debugger;
140 ARMDebuggerRun(debugger);
141 if (debugger->state == DEBUGGER_SHUTDOWN) {
142 _changeState(threadContext, THREAD_EXITING, 0);
143 }
144 } else {
145 while (threadContext->state == THREAD_RUNNING) {
146 ARMRun(&cpu);
147 }
148 }
149
150 int resetScheduled = 0;
151 MutexLock(&threadContext->stateMutex);
152 if (threadContext->state == THREAD_PAUSING) {
153 threadContext->state = THREAD_PAUSED;
154 ConditionWake(&threadContext->stateCond);
155 }
156 if (threadContext->state == THREAD_INTERRUPTING) {
157 threadContext->state = THREAD_INTERRUPTED;
158 ConditionWake(&threadContext->stateCond);
159 }
160 if (threadContext->state == THREAD_RESETING) {
161 threadContext->state = THREAD_RUNNING;
162 resetScheduled = 1;
163 }
164 while (threadContext->state == THREAD_PAUSED) {
165 ConditionWait(&threadContext->stateCond, &threadContext->stateMutex);
166 }
167 MutexUnlock(&threadContext->stateMutex);
168 if (resetScheduled) {
169 ARMReset(&cpu);
170 }
171 }
172
173 while (threadContext->state != THREAD_SHUTDOWN) {
174 _changeState(threadContext, THREAD_SHUTDOWN, 0);
175 }
176
177 if (threadContext->cleanCallback) {
178 threadContext->cleanCallback(threadContext);
179 }
180
181 threadContext->gba = 0;
182 ARMDeinit(&cpu);
183 GBADestroy(&gba);
184
185 ConditionWake(&threadContext->sync.videoFrameAvailableCond);
186 ConditionWake(&threadContext->sync.audioRequiredCond);
187 free(savedata);
188
189 return 0;
190}
191
192void GBAMapOptionsToContext(struct StartupOptions* opts, struct GBAThread* threadContext) {
193 threadContext->fd = opts->fd;
194 threadContext->fname = opts->fname;
195 threadContext->biosFd = opts->biosFd;
196 threadContext->patchFd = opts->patchFd;
197 threadContext->frameskip = opts->frameskip;
198 threadContext->logLevel = opts->logLevel;
199 threadContext->rewindBufferCapacity = opts->rewindBufferCapacity;
200 threadContext->rewindBufferInterval = opts->rewindBufferInterval;
201}
202
203int GBAThreadStart(struct GBAThread* threadContext) {
204 // TODO: error check
205 threadContext->activeKeys = 0;
206 threadContext->state = THREAD_INITIALIZED;
207 threadContext->sync.videoFrameOn = 1;
208 threadContext->sync.videoFrameSkip = 0;
209
210 threadContext->rewindBufferNext = threadContext->rewindBufferInterval;
211 threadContext->rewindBufferSize = 0;
212 if (threadContext->rewindBufferCapacity) {
213 threadContext->rewindBuffer = calloc(threadContext->rewindBufferCapacity, sizeof(void*));
214 } else {
215 threadContext->rewindBuffer = 0;
216 }
217
218 MutexInit(&threadContext->stateMutex);
219 ConditionInit(&threadContext->stateCond);
220
221 MutexInit(&threadContext->sync.videoFrameMutex);
222 ConditionInit(&threadContext->sync.videoFrameAvailableCond);
223 ConditionInit(&threadContext->sync.videoFrameRequiredCond);
224 MutexInit(&threadContext->sync.audioBufferMutex);
225 ConditionInit(&threadContext->sync.audioRequiredCond);
226
227#ifndef _WIN32
228 sigset_t signals;
229 sigemptyset(&signals);
230 sigaddset(&signals, SIGINT);
231 sigaddset(&signals, SIGTRAP);
232 pthread_sigmask(SIG_BLOCK, &signals, 0);
233#endif
234
235 MutexLock(&threadContext->stateMutex);
236 ThreadCreate(&threadContext->thread, _GBAThreadRun, threadContext);
237 while (threadContext->state < THREAD_RUNNING) {
238 ConditionWait(&threadContext->stateCond, &threadContext->stateMutex);
239 }
240 MutexUnlock(&threadContext->stateMutex);
241
242 return 0;
243}
244
245int GBAThreadHasStarted(struct GBAThread* threadContext) {
246 int hasStarted;
247 MutexLock(&threadContext->stateMutex);
248 hasStarted = threadContext->state > THREAD_INITIALIZED;
249 MutexUnlock(&threadContext->stateMutex);
250 return hasStarted;
251}
252
253void GBAThreadEnd(struct GBAThread* threadContext) {
254 MutexLock(&threadContext->stateMutex);
255 if (threadContext->debugger && threadContext->debugger->state == DEBUGGER_RUNNING) {
256 threadContext->debugger->state = DEBUGGER_EXITING;
257 }
258 threadContext->state = THREAD_EXITING;
259 MutexUnlock(&threadContext->stateMutex);
260 MutexLock(&threadContext->sync.audioBufferMutex);
261 threadContext->sync.audioWait = 0;
262 ConditionWake(&threadContext->sync.audioRequiredCond);
263 MutexUnlock(&threadContext->sync.audioBufferMutex);
264}
265
266void GBAThreadReset(struct GBAThread* threadContext) {
267 MutexLock(&threadContext->stateMutex);
268 _waitOnInterrupt(threadContext);
269 threadContext->state = THREAD_RESETING;
270 ConditionWake(&threadContext->stateCond);
271 MutexUnlock(&threadContext->stateMutex);
272}
273
274void GBAThreadJoin(struct GBAThread* threadContext) {
275 MutexLock(&threadContext->sync.videoFrameMutex);
276 threadContext->sync.videoFrameWait = 0;
277 ConditionWake(&threadContext->sync.videoFrameRequiredCond);
278 MutexUnlock(&threadContext->sync.videoFrameMutex);
279
280 ThreadJoin(threadContext->thread);
281
282 MutexDeinit(&threadContext->stateMutex);
283 ConditionDeinit(&threadContext->stateCond);
284
285 MutexDeinit(&threadContext->sync.videoFrameMutex);
286 ConditionWake(&threadContext->sync.videoFrameAvailableCond);
287 ConditionDeinit(&threadContext->sync.videoFrameAvailableCond);
288 ConditionWake(&threadContext->sync.videoFrameRequiredCond);
289 ConditionDeinit(&threadContext->sync.videoFrameRequiredCond);
290
291 ConditionWake(&threadContext->sync.audioRequiredCond);
292 ConditionDeinit(&threadContext->sync.audioRequiredCond);
293 MutexDeinit(&threadContext->sync.audioBufferMutex);
294
295 int i;
296 for (i = 0; i < threadContext->rewindBufferCapacity; ++i) {
297 if (threadContext->rewindBuffer[i]) {
298 GBADeallocateState(threadContext->rewindBuffer[i]);
299 }
300 }
301 free(threadContext->rewindBuffer);
302}
303
304void GBAThreadInterrupt(struct GBAThread* threadContext) {
305 MutexLock(&threadContext->stateMutex);
306 threadContext->savedState = threadContext->state;
307 _waitOnInterrupt(threadContext);
308 threadContext->state = THREAD_INTERRUPTING;
309 if (threadContext->debugger && threadContext->debugger->state == DEBUGGER_RUNNING) {
310 threadContext->debugger->state = DEBUGGER_EXITING;
311 }
312 ConditionWake(&threadContext->stateCond);
313 while (threadContext->state == THREAD_INTERRUPTING) {
314 ConditionWait(&threadContext->stateCond, &threadContext->stateMutex);
315 }
316 MutexUnlock(&threadContext->stateMutex);
317}
318
319void GBAThreadContinue(struct GBAThread* threadContext) {
320 _changeState(threadContext, threadContext->savedState, 1);
321}
322
323void GBAThreadPause(struct GBAThread* threadContext) {
324 int frameOn = 1;
325 MutexLock(&threadContext->stateMutex);
326 _waitOnInterrupt(threadContext);
327 if (threadContext->state == THREAD_RUNNING) {
328 if (threadContext->debugger && threadContext->debugger->state == DEBUGGER_RUNNING) {
329 threadContext->debugger->state = DEBUGGER_EXITING;
330 }
331 threadContext->state = THREAD_PAUSING;
332 frameOn = 0;
333 }
334 MutexUnlock(&threadContext->stateMutex);
335 MutexLock(&threadContext->sync.videoFrameMutex);
336 if (frameOn != threadContext->sync.videoFrameOn) {
337 threadContext->sync.videoFrameOn = frameOn;
338 ConditionWake(&threadContext->sync.videoFrameAvailableCond);
339 }
340 MutexUnlock(&threadContext->sync.videoFrameMutex);
341}
342
343void GBAThreadUnpause(struct GBAThread* threadContext) {
344 int frameOn = 1;
345 MutexLock(&threadContext->stateMutex);
346 _waitOnInterrupt(threadContext);
347 if (threadContext->state == THREAD_PAUSED || threadContext->state == THREAD_PAUSING) {
348 threadContext->state = THREAD_RUNNING;
349 ConditionWake(&threadContext->stateCond);
350 }
351 MutexUnlock(&threadContext->stateMutex);
352 MutexLock(&threadContext->sync.videoFrameMutex);
353 if (frameOn != threadContext->sync.videoFrameOn) {
354 threadContext->sync.videoFrameOn = frameOn;
355 ConditionWake(&threadContext->sync.videoFrameAvailableCond);
356 }
357 MutexUnlock(&threadContext->sync.videoFrameMutex);
358}
359
360int GBAThreadIsPaused(struct GBAThread* threadContext) {
361 int isPaused;
362 MutexLock(&threadContext->stateMutex);
363 _waitOnInterrupt(threadContext);
364 isPaused = threadContext->state == THREAD_PAUSED;
365 MutexUnlock(&threadContext->stateMutex);
366 return isPaused;
367}
368
369void GBAThreadTogglePause(struct GBAThread* threadContext) {
370 int frameOn = 1;
371 MutexLock(&threadContext->stateMutex);
372 _waitOnInterrupt(threadContext);
373 if (threadContext->state == THREAD_PAUSED) {
374 threadContext->state = THREAD_RUNNING;
375 ConditionWake(&threadContext->stateCond);
376 } else if (threadContext->state == THREAD_RUNNING) {
377 if (threadContext->debugger && threadContext->debugger->state == DEBUGGER_RUNNING) {
378 threadContext->debugger->state = DEBUGGER_EXITING;
379 }
380 threadContext->state = THREAD_PAUSED;
381 frameOn = 0;
382 }
383 MutexUnlock(&threadContext->stateMutex);
384 MutexLock(&threadContext->sync.videoFrameMutex);
385 if (frameOn != threadContext->sync.videoFrameOn) {
386 threadContext->sync.videoFrameOn = frameOn;
387 ConditionWake(&threadContext->sync.videoFrameAvailableCond);
388 }
389 MutexUnlock(&threadContext->sync.videoFrameMutex);
390}
391
392#ifdef USE_PTHREADS
393struct GBAThread* GBAThreadGetContext(void) {
394 pthread_once(&_contextOnce, _createTLS);
395 return pthread_getspecific(_contextKey);
396}
397#else
398struct GBAThread* GBAThreadGetContext(void) {
399 InitOnceExecuteOnce(&_contextOnce, _createTLS, NULL, 0);
400 return TlsGetValue(_contextKey);
401}
402#endif
403
404void GBASyncPostFrame(struct GBASync* sync) {
405 if (!sync) {
406 return;
407 }
408
409 MutexLock(&sync->videoFrameMutex);
410 ++sync->videoFramePending;
411 --sync->videoFrameSkip;
412 if (sync->videoFrameSkip < 0) {
413 ConditionWake(&sync->videoFrameAvailableCond);
414 while (sync->videoFrameWait && sync->videoFramePending) {
415 ConditionWait(&sync->videoFrameRequiredCond, &sync->videoFrameMutex);
416 }
417 }
418 MutexUnlock(&sync->videoFrameMutex);
419
420 struct GBAThread* thread = GBAThreadGetContext();
421 if (thread->rewindBuffer) {
422 --thread->rewindBufferNext;
423 if (thread->rewindBufferNext <= 0) {
424 thread->rewindBufferNext = thread->rewindBufferInterval;
425 GBARecordFrame(thread);
426 }
427 }
428 if (thread->frameCallback) {
429 thread->frameCallback(thread);
430 }
431}
432
433int GBASyncWaitFrameStart(struct GBASync* sync, int frameskip) {
434 if (!sync) {
435 return 1;
436 }
437
438 MutexLock(&sync->videoFrameMutex);
439 ConditionWake(&sync->videoFrameRequiredCond);
440 if (!sync->videoFrameOn) {
441 return 0;
442 }
443 ConditionWait(&sync->videoFrameAvailableCond, &sync->videoFrameMutex);
444 sync->videoFramePending = 0;
445 sync->videoFrameSkip = frameskip;
446 return 1;
447}
448
449void GBASyncWaitFrameEnd(struct GBASync* sync) {
450 if (!sync) {
451 return;
452 }
453
454 MutexUnlock(&sync->videoFrameMutex);
455}
456
457int GBASyncDrawingFrame(struct GBASync* sync) {
458 return sync->videoFrameSkip <= 0;
459}
460
461void GBASyncProduceAudio(struct GBASync* sync, int wait) {
462 if (sync->audioWait && wait) {
463 // TODO loop properly in event of spurious wakeups
464 ConditionWait(&sync->audioRequiredCond, &sync->audioBufferMutex);
465 }
466 MutexUnlock(&sync->audioBufferMutex);
467}
468
469void GBASyncLockAudio(struct GBASync* sync) {
470 MutexLock(&sync->audioBufferMutex);
471}
472
473void GBASyncConsumeAudio(struct GBASync* sync) {
474 ConditionWake(&sync->audioRequiredCond);
475 MutexUnlock(&sync->audioBufferMutex);
476}