src/gba/gba-thread.c (view raw)
1#include "gba-thread.h"
2
3#include "arm.h"
4#include "debugger.h"
5#include "gba.h"
6
7#include <stdlib.h>
8#include <signal.h>
9
10static pthread_key_t _contextKey;
11static pthread_once_t _contextOnce = PTHREAD_ONCE_INIT;
12
13static void _createTLS(void) {
14 pthread_key_create(&_contextKey, 0);
15}
16
17static void* _GBAThreadRun(void* context) {
18 pthread_once(&_contextOnce, _createTLS);
19
20#ifdef USE_DEBUGGER
21 struct ARMDebugger debugger;
22#endif
23 struct GBA gba;
24 struct GBAThread* threadContext = context;
25 char* savedata = 0;
26
27#ifndef _WIN32
28 sigset_t signals;
29 sigfillset(&signals);
30 pthread_sigmask(SIG_UNBLOCK, &signals, 0);
31#endif
32
33 GBAInit(&gba);
34 threadContext->gba = &gba;
35 gba.sync = &threadContext->sync;
36 pthread_setspecific(_contextKey, threadContext);
37 if (threadContext->renderer) {
38 GBAVideoAssociateRenderer(&gba.video, threadContext->renderer);
39 }
40
41 if (threadContext->fd >= 0) {
42 if (threadContext->fname) {
43 char* dotPoint = strrchr(threadContext->fname, '.');
44 if (dotPoint > strrchr(threadContext->fname, '/') && dotPoint[1] && dotPoint[2] && dotPoint[3]) {
45 savedata = strdup(threadContext->fname);
46 dotPoint = strrchr(savedata, '.');
47 dotPoint[1] = 's';
48 dotPoint[2] = 'a';
49 dotPoint[3] = 'v';
50 dotPoint[4] = '\0';
51 } else if (dotPoint) {
52 savedata = malloc((dotPoint - threadContext->fname + 5) * sizeof(char));
53 strncpy(savedata, threadContext->fname, dotPoint - threadContext->fname + 1);
54 strcat(savedata, "sav");
55 } else {
56 savedata = malloc(strlen(threadContext->fname + 5));
57 strcpy(savedata, threadContext->fname);
58 strcat(savedata, "sav");
59 }
60 }
61 gba.savefile = savedata;
62 GBALoadROM(&gba, threadContext->fd, threadContext->fname);
63 }
64
65#ifdef USE_DEBUGGER
66 if (threadContext->useDebugger) {
67 threadContext->debugger = &debugger;
68 GBAAttachDebugger(&gba, &debugger);
69 } else {
70 threadContext->debugger = 0;
71 }
72#else
73 threadContext->debugger = 0;
74#endif
75
76 gba.keySource = &threadContext->activeKeys;
77
78 if (threadContext->startCallback) {
79 threadContext->startCallback(threadContext);
80 }
81
82 pthread_mutex_lock(&threadContext->stateMutex);
83 threadContext->state = THREAD_RUNNING;
84 pthread_cond_broadcast(&threadContext->stateCond);
85 pthread_mutex_unlock(&threadContext->stateMutex);
86
87 while (threadContext->state < THREAD_EXITING) {
88#ifdef USE_DEBUGGER
89 if (threadContext->useDebugger) {
90 ARMDebuggerRun(&debugger);
91 if (debugger.state == DEBUGGER_SHUTDOWN) {
92 pthread_mutex_lock(&threadContext->stateMutex);
93 threadContext->state = THREAD_EXITING;
94 pthread_mutex_unlock(&threadContext->stateMutex);
95 }
96 } else {
97#endif
98 while (threadContext->state == THREAD_RUNNING) {
99 ARMRun(&gba.cpu);
100 }
101#ifdef USE_DEBUGGER
102 }
103#endif
104 while (threadContext->state == THREAD_PAUSED) {
105 pthread_mutex_lock(&threadContext->stateMutex);
106 pthread_cond_wait(&threadContext->stateCond, &threadContext->stateMutex);
107 pthread_mutex_unlock(&threadContext->stateMutex);
108 }
109 }
110
111 while (threadContext->state != THREAD_SHUTDOWN) {
112 pthread_mutex_lock(&threadContext->stateMutex);
113 threadContext->state = THREAD_SHUTDOWN;
114 pthread_mutex_unlock(&threadContext->stateMutex);
115 }
116
117 if (threadContext->cleanCallback) {
118 threadContext->cleanCallback(threadContext);
119 }
120
121 GBADeinit(&gba);
122
123 pthread_cond_broadcast(&threadContext->sync.videoFrameAvailableCond);
124 pthread_cond_broadcast(&threadContext->sync.audioRequiredCond);
125 free(savedata);
126
127 return 0;
128}
129
130int GBAThreadStart(struct GBAThread* threadContext) {
131 // TODO: error check
132 pthread_mutex_init(&threadContext->stateMutex, 0);
133 pthread_cond_init(&threadContext->stateCond, 0);
134
135 pthread_mutex_init(&threadContext->sync.videoFrameMutex, 0);
136 pthread_cond_init(&threadContext->sync.videoFrameAvailableCond, 0);
137 pthread_cond_init(&threadContext->sync.videoFrameRequiredCond, 0);
138 pthread_mutex_init(&threadContext->sync.audioBufferMutex, 0);
139 pthread_cond_init(&threadContext->sync.audioRequiredCond, 0);
140
141 pthread_mutex_lock(&threadContext->stateMutex);
142 threadContext->activeKeys = 0;
143 threadContext->state = THREAD_INITIALIZED;
144 threadContext->sync.videoFrameOn = 1;
145 threadContext->sync.videoFrameSkip = 0;
146 pthread_create(&threadContext->thread, 0, _GBAThreadRun, threadContext);
147 pthread_cond_wait(&threadContext->stateCond, &threadContext->stateMutex);
148 pthread_mutex_unlock(&threadContext->stateMutex);
149
150 return 0;
151}
152
153void GBAThreadJoin(struct GBAThread* threadContext) {
154 pthread_mutex_lock(&threadContext->sync.videoFrameMutex);
155 threadContext->sync.videoFrameWait = 0;
156 pthread_cond_broadcast(&threadContext->sync.videoFrameRequiredCond);
157 pthread_mutex_unlock(&threadContext->sync.videoFrameMutex);
158
159 pthread_join(threadContext->thread, 0);
160
161 pthread_mutex_destroy(&threadContext->stateMutex);
162 pthread_cond_destroy(&threadContext->stateCond);
163
164 pthread_mutex_destroy(&threadContext->sync.videoFrameMutex);
165 pthread_cond_broadcast(&threadContext->sync.videoFrameAvailableCond);
166 pthread_cond_destroy(&threadContext->sync.videoFrameAvailableCond);
167 pthread_cond_broadcast(&threadContext->sync.videoFrameRequiredCond);
168 pthread_cond_destroy(&threadContext->sync.videoFrameRequiredCond);
169
170 pthread_cond_broadcast(&threadContext->sync.audioRequiredCond);
171 pthread_cond_destroy(&threadContext->sync.audioRequiredCond);
172 pthread_mutex_destroy(&threadContext->sync.audioBufferMutex);
173}
174
175void GBAThreadPause(struct GBAThread* threadContext) {
176 int frameOn = 1;
177 pthread_mutex_lock(&threadContext->stateMutex);
178 if (threadContext->state == THREAD_RUNNING) {
179 if (threadContext->debugger && threadContext->debugger->state == DEBUGGER_RUNNING) {
180 threadContext->debugger->state = DEBUGGER_EXITING;
181 }
182 threadContext->state = THREAD_PAUSED;
183 frameOn = 0;
184 }
185 pthread_mutex_unlock(&threadContext->stateMutex);
186 pthread_mutex_lock(&threadContext->sync.videoFrameMutex);
187 if (frameOn != threadContext->sync.videoFrameOn) {
188 threadContext->sync.videoFrameOn = frameOn;
189 pthread_cond_broadcast(&threadContext->sync.videoFrameAvailableCond);
190 }
191 pthread_mutex_unlock(&threadContext->sync.videoFrameMutex);
192}
193
194void GBAThreadUnpause(struct GBAThread* threadContext) {
195 int frameOn = 1;
196 pthread_mutex_lock(&threadContext->stateMutex);
197 if (threadContext->state == THREAD_PAUSED) {
198 threadContext->state = THREAD_RUNNING;
199 pthread_cond_broadcast(&threadContext->stateCond);
200 }
201 pthread_mutex_unlock(&threadContext->stateMutex);
202 pthread_mutex_lock(&threadContext->sync.videoFrameMutex);
203 if (frameOn != threadContext->sync.videoFrameOn) {
204 threadContext->sync.videoFrameOn = frameOn;
205 pthread_cond_broadcast(&threadContext->sync.videoFrameAvailableCond);
206 }
207 pthread_mutex_unlock(&threadContext->sync.videoFrameMutex);
208}
209
210void GBAThreadTogglePause(struct GBAThread* threadContext) {
211 int frameOn = 1;
212 pthread_mutex_lock(&threadContext->stateMutex);
213 if (threadContext->state == THREAD_PAUSED) {
214 threadContext->state = THREAD_RUNNING;
215 pthread_cond_broadcast(&threadContext->stateCond);
216 } else if (threadContext->state == THREAD_RUNNING) {
217 if (threadContext->debugger && threadContext->debugger->state == DEBUGGER_RUNNING) {
218 threadContext->debugger->state = DEBUGGER_EXITING;
219 }
220 threadContext->state = THREAD_PAUSED;
221 frameOn = 0;
222 }
223 pthread_mutex_unlock(&threadContext->stateMutex);
224 pthread_mutex_lock(&threadContext->sync.videoFrameMutex);
225 if (frameOn != threadContext->sync.videoFrameOn) {
226 threadContext->sync.videoFrameOn = frameOn;
227 pthread_cond_broadcast(&threadContext->sync.videoFrameAvailableCond);
228 }
229 pthread_mutex_unlock(&threadContext->sync.videoFrameMutex);
230}
231
232struct GBAThread* GBAThreadGetContext(void) {
233 pthread_once(&_contextOnce, _createTLS);
234 return pthread_getspecific(_contextKey);
235}
236
237void GBASyncPostFrame(struct GBASync* sync) {
238 if (!sync) {
239 return;
240 }
241
242 pthread_mutex_lock(&sync->videoFrameMutex);
243 ++sync->videoFramePending;
244 --sync->videoFrameSkip;
245 if (sync->videoFrameSkip < 0) {
246 pthread_cond_broadcast(&sync->videoFrameAvailableCond);
247 if (sync->videoFrameWait) {
248 pthread_cond_wait(&sync->videoFrameRequiredCond, &sync->videoFrameMutex);
249 }
250 }
251 pthread_mutex_unlock(&sync->videoFrameMutex);
252}
253
254int GBASyncWaitFrameStart(struct GBASync* sync, int frameskip) {
255 if (!sync) {
256 return 1;
257 }
258
259 pthread_mutex_lock(&sync->videoFrameMutex);
260 pthread_cond_broadcast(&sync->videoFrameRequiredCond);
261 if (!sync->videoFrameOn) {
262 return 0;
263 }
264 pthread_cond_wait(&sync->videoFrameAvailableCond, &sync->videoFrameMutex);
265 sync->videoFramePending = 0;
266 sync->videoFrameSkip = frameskip;
267 return 1;
268}
269
270void GBASyncWaitFrameEnd(struct GBASync* sync) {
271 if (!sync) {
272 return;
273 }
274
275 pthread_mutex_unlock(&sync->videoFrameMutex);
276}
277
278int GBASyncDrawingFrame(struct GBASync* sync) {
279 return sync->videoFrameSkip <= 0;
280}
281
282void GBASyncProduceAudio(struct GBASync* sync, int wait) {
283 if (sync->audioWait && wait) {
284 pthread_cond_wait(&sync->audioRequiredCond, &sync->audioBufferMutex);
285 }
286 pthread_mutex_unlock(&sync->audioBufferMutex);
287}
288
289void GBASyncLockAudio(struct GBASync* sync) {
290 pthread_mutex_lock(&sync->audioBufferMutex);
291}
292
293void GBASyncConsumeAudio(struct GBASync* sync) {
294 pthread_cond_broadcast(&sync->audioRequiredCond);
295 pthread_mutex_unlock(&sync->audioBufferMutex);
296}