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;
11
12static void _createTLS(void) {
13 pthread_key_create(&contextKey, 0);
14}
15
16static void* _GBAThreadRun(void* context) {
17 static pthread_once_t once = PTHREAD_ONCE_INIT;
18 pthread_once(&once, _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 GBALoadROM(&gba, threadContext->fd, threadContext->fname);
60 gba.savefile = savedata;
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 threadContext->started = 1;
81 pthread_mutex_lock(&threadContext->startMutex);
82 pthread_cond_broadcast(&threadContext->startCond);
83 pthread_mutex_unlock(&threadContext->startMutex);
84
85#ifdef USE_DEBUGGER
86 if (threadContext->useDebugger) {
87 ARMDebuggerRun(&debugger);
88 threadContext->started = 0;
89 } else {
90#endif
91 while (threadContext->started) {
92 ARMRun(&gba.cpu);
93 }
94#ifdef USE_DEBUGGER
95 }
96#endif
97
98 if (threadContext->cleanCallback) {
99 threadContext->cleanCallback(threadContext);
100 }
101
102 GBADeinit(&gba);
103
104 pthread_cond_broadcast(&threadContext->sync.videoFrameAvailableCond);
105 pthread_cond_broadcast(&threadContext->sync.audioRequiredCond);
106 free(savedata);
107
108 return 0;
109}
110
111int GBAThreadStart(struct GBAThread* threadContext) {
112 // TODO: error check
113 pthread_mutex_init(&threadContext->startMutex, 0);
114 pthread_cond_init(&threadContext->startCond, 0);
115
116 pthread_mutex_init(&threadContext->sync.videoFrameMutex, 0);
117 pthread_cond_init(&threadContext->sync.videoFrameAvailableCond, 0);
118 pthread_cond_init(&threadContext->sync.videoFrameRequiredCond, 0);
119 pthread_cond_init(&threadContext->sync.audioRequiredCond, 0);
120
121 pthread_mutex_lock(&threadContext->startMutex);
122 threadContext->activeKeys = 0;
123 threadContext->started = 0;
124 pthread_create(&threadContext->thread, 0, _GBAThreadRun, threadContext);
125 pthread_cond_wait(&threadContext->startCond, &threadContext->startMutex);
126 pthread_mutex_unlock(&threadContext->startMutex);
127
128 return 0;
129}
130
131void GBAThreadJoin(struct GBAThread* threadContext) {
132 pthread_mutex_lock(&threadContext->sync.videoFrameMutex);
133 threadContext->sync.videoFrameWait = 0;
134 pthread_cond_broadcast(&threadContext->sync.videoFrameRequiredCond);
135 pthread_mutex_unlock(&threadContext->sync.videoFrameMutex);
136
137 pthread_join(threadContext->thread, 0);
138
139 pthread_mutex_destroy(&threadContext->startMutex);
140 pthread_cond_destroy(&threadContext->startCond);
141
142 pthread_mutex_destroy(&threadContext->sync.videoFrameMutex);
143 pthread_cond_broadcast(&threadContext->sync.videoFrameAvailableCond);
144 pthread_cond_destroy(&threadContext->sync.videoFrameAvailableCond);
145 pthread_cond_broadcast(&threadContext->sync.videoFrameRequiredCond);
146 pthread_cond_destroy(&threadContext->sync.videoFrameRequiredCond);
147
148 pthread_cond_broadcast(&threadContext->sync.audioRequiredCond);
149 pthread_cond_destroy(&threadContext->sync.audioRequiredCond);
150}
151
152struct GBAThread* GBAThreadGetContext(void) {
153 return pthread_getspecific(contextKey);
154}
155
156void GBASyncPostFrame(struct GBASync* sync) {
157 if (!sync) {
158 return;
159 }
160
161 pthread_mutex_lock(&sync->videoFrameMutex);
162 ++sync->videoFramePending;
163 --sync->videoFrameSkip;
164 pthread_cond_broadcast(&sync->videoFrameAvailableCond);
165 if (sync->videoFrameWait) {
166 pthread_cond_wait(&sync->videoFrameRequiredCond, &sync->videoFrameMutex);
167 }
168 pthread_mutex_unlock(&sync->videoFrameMutex);
169}
170
171void GBASyncWaitFrameStart(struct GBASync* sync, int frameskip) {
172 if (!sync) {
173 return;
174 }
175
176 pthread_mutex_lock(&sync->videoFrameMutex);
177 pthread_cond_broadcast(&sync->videoFrameRequiredCond);
178 pthread_cond_wait(&sync->videoFrameAvailableCond, &sync->videoFrameMutex);
179 sync->videoFramePending = 0;
180 sync->videoFrameSkip = frameskip;
181}
182
183void GBASyncWaitFrameEnd(struct GBASync* sync) {
184 if (!sync) {
185 return;
186 }
187
188 pthread_mutex_unlock(&sync->videoFrameMutex);
189}
190
191int GBASyncDrawingFrame(struct GBASync* sync) {
192 return sync->videoFrameSkip <= 0;
193}
194
195void GBASyncProduceAudio(struct GBASync* sync, pthread_mutex_t* mutex) {
196 if (&sync->audioWait) {
197 pthread_cond_wait(&sync->audioRequiredCond, mutex);
198 }
199}
200
201void GBASyncConsumeAudio(struct GBASync* sync) {
202 pthread_cond_broadcast(&sync->audioRequiredCond);
203}