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 struct ARMDebugger debugger;
21 struct GBA gba;
22 struct GBAThread* threadContext = context;
23 char* savedata = 0;
24
25 sigset_t signals;
26 sigfillset(&signals);
27 pthread_sigmask(SIG_UNBLOCK, &signals, 0);
28
29 GBAInit(&gba);
30 threadContext->gba = &gba;
31 gba.sync = &threadContext->sync;
32 pthread_setspecific(contextKey, threadContext);
33 if (threadContext->renderer) {
34 GBAVideoAssociateRenderer(&gba.video, threadContext->renderer);
35 }
36
37 if (threadContext->fd >= 0) {
38 if (threadContext->fname) {
39 char* dotPoint = strrchr(threadContext->fname, '.');
40 if (dotPoint > strrchr(threadContext->fname, '/') && dotPoint[1] && dotPoint[2] && dotPoint[3]) {
41 savedata = strdup(threadContext->fname);
42 dotPoint = strrchr(savedata, '.');
43 dotPoint[1] = 's';
44 dotPoint[2] = 'a';
45 dotPoint[3] = 'v';
46 dotPoint[4] = '\0';
47 } else if (dotPoint) {
48 savedata = malloc((dotPoint - threadContext->fname + 5) * sizeof(char));
49 strncpy(savedata, threadContext->fname, dotPoint - threadContext->fname + 1);
50 strcat(savedata, "sav");
51 } else {
52 savedata = malloc(strlen(threadContext->fname + 5));
53 strcpy(savedata, threadContext->fname);
54 strcat(savedata, "sav");
55 }
56 }
57 GBALoadROM(&gba, threadContext->fd, threadContext->fname);
58 gba.savefile = savedata;
59 }
60 if (threadContext->useDebugger) {
61 threadContext->debugger = &debugger;
62 GBAAttachDebugger(&gba, &debugger);
63 } else {
64 threadContext->debugger = 0;
65 }
66 gba.keySource = &threadContext->activeKeys;
67
68 if (threadContext->startCallback) {
69 threadContext->startCallback(threadContext);
70 }
71
72 threadContext->started = 1;
73 pthread_mutex_lock(&threadContext->startMutex);
74 pthread_cond_broadcast(&threadContext->startCond);
75 pthread_mutex_unlock(&threadContext->startMutex);
76
77 if (threadContext->useDebugger) {
78 ARMDebuggerRun(&debugger);
79 threadContext->started = 0;
80 } else {
81 while (threadContext->started) {
82 ARMRun(&gba.cpu);
83 }
84 }
85
86 if (threadContext->cleanCallback) {
87 threadContext->cleanCallback(threadContext);
88 }
89
90 GBADeinit(&gba);
91
92 pthread_cond_broadcast(&threadContext->sync.videoFrameAvailableCond);
93 pthread_cond_broadcast(&threadContext->sync.audioRequiredCond);
94 free(savedata);
95
96 return 0;
97}
98
99int GBAThreadStart(struct GBAThread* threadContext) {
100 // TODO: error check
101 pthread_mutex_init(&threadContext->startMutex, 0);
102 pthread_cond_init(&threadContext->startCond, 0);
103
104 pthread_mutex_init(&threadContext->sync.videoFrameMutex, 0);
105 pthread_cond_init(&threadContext->sync.videoFrameAvailableCond, 0);
106 pthread_cond_init(&threadContext->sync.videoFrameRequiredCond, 0);
107 pthread_cond_init(&threadContext->sync.audioRequiredCond, 0);
108
109 pthread_mutex_lock(&threadContext->startMutex);
110 threadContext->activeKeys = 0;
111 threadContext->started = 0;
112 pthread_create(&threadContext->thread, 0, _GBAThreadRun, threadContext);
113 pthread_cond_wait(&threadContext->startCond, &threadContext->startMutex);
114 pthread_mutex_unlock(&threadContext->startMutex);
115
116 return 0;
117}
118
119void GBAThreadJoin(struct GBAThread* threadContext) {
120 pthread_mutex_lock(&threadContext->sync.videoFrameMutex);
121 threadContext->sync.videoFrameWait = 0;
122 pthread_cond_broadcast(&threadContext->sync.videoFrameRequiredCond);
123 pthread_mutex_unlock(&threadContext->sync.videoFrameMutex);
124
125 pthread_join(threadContext->thread, 0);
126
127 pthread_mutex_destroy(&threadContext->startMutex);
128 pthread_cond_destroy(&threadContext->startCond);
129
130 pthread_mutex_destroy(&threadContext->sync.videoFrameMutex);
131 pthread_cond_broadcast(&threadContext->sync.videoFrameAvailableCond);
132 pthread_cond_destroy(&threadContext->sync.videoFrameAvailableCond);
133 pthread_cond_broadcast(&threadContext->sync.videoFrameRequiredCond);
134 pthread_cond_destroy(&threadContext->sync.videoFrameRequiredCond);
135
136 pthread_cond_broadcast(&threadContext->sync.audioRequiredCond);
137 pthread_cond_destroy(&threadContext->sync.audioRequiredCond);
138}
139
140struct GBAThread* GBAThreadGetContext(void) {
141 return pthread_getspecific(contextKey);
142}
143
144void GBASyncPostFrame(struct GBASync* sync) {
145 if (!sync) {
146 return;
147 }
148
149 pthread_mutex_lock(&sync->videoFrameMutex);
150 ++sync->videoFramePending;
151 --sync->videoFrameSkip;
152 pthread_cond_broadcast(&sync->videoFrameAvailableCond);
153 if (sync->videoFrameWait) {
154 pthread_cond_wait(&sync->videoFrameRequiredCond, &sync->videoFrameMutex);
155 }
156 pthread_mutex_unlock(&sync->videoFrameMutex);
157}
158
159void GBASyncWaitFrameStart(struct GBASync* sync, int frameskip) {
160 if (!sync) {
161 return;
162 }
163
164 pthread_mutex_lock(&sync->videoFrameMutex);
165 pthread_cond_broadcast(&sync->videoFrameRequiredCond);
166 pthread_cond_wait(&sync->videoFrameAvailableCond, &sync->videoFrameMutex);
167 sync->videoFramePending = 0;
168 sync->videoFrameSkip = frameskip;
169}
170
171void GBASyncWaitFrameEnd(struct GBASync* sync) {
172 if (!sync) {
173 return;
174 }
175
176 pthread_mutex_unlock(&sync->videoFrameMutex);
177}
178
179int GBASyncDrawingFrame(struct GBASync* sync) {
180 return sync->videoFrameSkip <= 0;
181}
182
183void GBASyncProduceAudio(struct GBASync* sync, pthread_mutex_t* mutex) {
184 if (&sync->audioWait) {
185 pthread_cond_wait(&sync->audioRequiredCond, mutex);
186 }
187}
188
189void GBASyncConsumeAudio(struct GBASync* sync) {
190 pthread_cond_broadcast(&sync->audioRequiredCond);
191}