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 threadContext->started = 1;
69 pthread_mutex_lock(&threadContext->startMutex);
70 pthread_cond_broadcast(&threadContext->startCond);
71 pthread_mutex_unlock(&threadContext->startMutex);
72
73 if (threadContext->useDebugger) {
74 ARMDebuggerRun(&debugger);
75 threadContext->started = 0;
76 } else {
77 while (threadContext->started) {
78 ARMRun(&gba.cpu);
79 }
80 }
81 GBADeinit(&gba);
82 free(savedata);
83
84 return 0;
85}
86
87int GBAThreadStart(struct GBAThread* threadContext) {
88 // TODO: error check
89 pthread_mutex_init(&threadContext->startMutex, 0);
90 pthread_cond_init(&threadContext->startCond, 0);
91
92 pthread_mutex_init(&threadContext->sync.videoFrameMutex, 0);
93 pthread_cond_init(&threadContext->sync.videoFrameAvailableCond, 0);
94 pthread_cond_init(&threadContext->sync.videoFrameRequiredCond, 0);
95 pthread_cond_init(&threadContext->sync.audioAvailableCond, 0);
96 pthread_cond_init(&threadContext->sync.audioRequiredCond, 0);
97
98 pthread_mutex_lock(&threadContext->startMutex);
99 threadContext->activeKeys = 0;
100 threadContext->started = 0;
101 pthread_create(&threadContext->thread, 0, _GBAThreadRun, threadContext);
102 pthread_cond_wait(&threadContext->startCond, &threadContext->startMutex);
103 pthread_mutex_unlock(&threadContext->startMutex);
104
105 return 0;
106}
107
108void GBAThreadJoin(struct GBAThread* threadContext) {
109 pthread_mutex_lock(&threadContext->sync.videoFrameMutex);
110 threadContext->sync.videoFrameWait = 0;
111 pthread_cond_broadcast(&threadContext->sync.videoFrameRequiredCond);
112 pthread_mutex_unlock(&threadContext->sync.videoFrameMutex);
113
114 pthread_join(threadContext->thread, 0);
115
116 pthread_mutex_destroy(&threadContext->startMutex);
117 pthread_cond_destroy(&threadContext->startCond);
118
119 pthread_mutex_destroy(&threadContext->sync.videoFrameMutex);
120 pthread_cond_broadcast(&threadContext->sync.videoFrameAvailableCond);
121 pthread_cond_destroy(&threadContext->sync.videoFrameAvailableCond);
122 pthread_cond_broadcast(&threadContext->sync.videoFrameRequiredCond);
123 pthread_cond_destroy(&threadContext->sync.videoFrameRequiredCond);
124
125 pthread_cond_broadcast(&threadContext->sync.audioAvailableCond);
126 pthread_cond_destroy(&threadContext->sync.audioAvailableCond);
127 pthread_cond_broadcast(&threadContext->sync.audioRequiredCond);
128 pthread_cond_destroy(&threadContext->sync.audioRequiredCond);
129}
130
131struct GBAThread* GBAThreadGetContext(void) {
132 return pthread_getspecific(contextKey);
133}
134
135void GBASyncPostFrame(struct GBASync* sync) {
136 if (!sync) {
137 return;
138 }
139
140 pthread_mutex_lock(&sync->videoFrameMutex);
141 ++sync->videoFramePending;
142 --sync->videoFrameSkip;
143 pthread_cond_broadcast(&sync->videoFrameAvailableCond);
144 if (sync->videoFrameWait) {
145 pthread_cond_wait(&sync->videoFrameRequiredCond, &sync->videoFrameMutex);
146 }
147 pthread_mutex_unlock(&sync->videoFrameMutex);
148}
149
150void GBASyncWaitFrameStart(struct GBASync* sync, int frameskip) {
151 if (!sync) {
152 return;
153 }
154
155 pthread_mutex_lock(&sync->videoFrameMutex);
156 pthread_cond_broadcast(&sync->videoFrameRequiredCond);
157 pthread_cond_wait(&sync->videoFrameAvailableCond, &sync->videoFrameMutex);
158 sync->videoFramePending = 0;
159 sync->videoFrameSkip = frameskip;
160}
161
162void GBASyncWaitFrameEnd(struct GBASync* sync) {
163 if (!sync) {
164 return;
165 }
166
167 pthread_mutex_unlock(&sync->videoFrameMutex);
168}
169
170int GBASyncDrawingFrame(struct GBASync* sync) {
171 return sync->videoFrameSkip <= 0;
172}
173
174void GBASyncProduceAudio(struct GBASync* sync, pthread_mutex_t* mutex) {
175 pthread_cond_broadcast(&sync->audioAvailableCond);
176 if (&sync->audioWait) {
177 pthread_cond_wait(&sync->audioRequiredCond, mutex);
178 }
179}
180
181void GBASyncConsumeAudio(struct GBASync* sync) {
182 pthread_cond_broadcast(&sync->audioRequiredCond);
183}