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