src/main.c (view raw)
1#include "debugger.h"
2#include "gba-thread.h"
3#include "gba.h"
4#include "renderers/video-software.h"
5
6#include <sdl.h>
7#include <OpenGL/gl.h>
8
9#include <fcntl.h>
10#include <errno.h>
11#include <signal.h>
12#include <sys/time.h>
13#include <unistd.h>
14
15struct GLSoftwareRenderer {
16 struct GBAVideoSoftwareRenderer d;
17
18 GLuint tex;
19};
20
21static int _GBASDLInit(struct GLSoftwareRenderer* renderer);
22static void _GBASDLDeinit(struct GLSoftwareRenderer* renderer);
23static void _GBASDLRunloop(struct GBAThread* context, struct GLSoftwareRenderer* renderer);
24static void _GBASDLHandleKeypress(struct GBAThread* context, const struct SDL_KeyboardEvent* event);
25
26static const GLint _glVertices[] = {
27 0, 0,
28 256, 0,
29 256, 256,
30 0, 256
31};
32
33static const GLint _glTexCoords[] = {
34 0, 0,
35 1, 0,
36 1, 1,
37 0, 1
38};
39
40int main(int argc, char** argv) {
41 const char* fname = "test.rom";
42 if (argc > 1) {
43 fname = argv[1];
44 }
45 int fd = open(fname, O_RDONLY);
46 if (fd < 0) {
47 return 1;
48 }
49
50 sigset_t signals;
51 sigaddset(&signals, SIGINT);
52 sigaddset(&signals, SIGTRAP);
53 pthread_sigmask(SIG_BLOCK, &signals, 0);
54
55 struct GBAThread context;
56 struct GLSoftwareRenderer renderer;
57 GBAVideoSoftwareRendererCreate(&renderer.d);
58
59 if (!_GBASDLInit(&renderer)) {
60 return 1;
61 }
62
63 context.fd = fd;
64 context.renderer = &renderer.d.d;
65 GBAThreadStart(&context);
66
67 _GBASDLRunloop(&context, &renderer);
68
69 GBAThreadJoin(&context);
70 close(fd);
71
72 _GBASDLDeinit(&renderer);
73
74 return 0;
75}
76
77static int _GBASDLInit(struct GLSoftwareRenderer* renderer) {
78 if (SDL_Init(SDL_INIT_VIDEO) < 0) {
79 return 0;
80 }
81
82 SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1);
83 SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
84 SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);
85 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
86 SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
87 SDL_SetVideoMode(240, 160, 16, SDL_OPENGL);
88
89 renderer->d.outputBuffer = malloc(256 * 256 * 2);
90 renderer->d.outputBufferStride = 256;
91 glGenTextures(1, &renderer->tex);
92 glBindTexture(GL_TEXTURE_2D, renderer->tex);
93 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
94 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
95 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
96 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
97 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
98
99 glViewport(0, 0, 240, 160);
100
101 return 1;
102}
103
104static void _GBASDLRunloop(struct GBAThread* context, struct GLSoftwareRenderer* renderer) {
105 SDL_Event event;
106
107 int err;
108 glEnable(GL_TEXTURE_2D);
109 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
110 glEnableClientState(GL_VERTEX_ARRAY);
111 glVertexPointer(2, GL_INT, 0, _glVertices);
112 glTexCoordPointer(2, GL_INT, 0, _glTexCoords);
113 glMatrixMode (GL_PROJECTION);
114 glLoadIdentity();
115 glOrtho(0, 240, 160, 0, 0, 1);
116 while (context->started) {
117 pthread_mutex_lock(&renderer->d.mutex);
118 if (renderer->d.d.framesPending) {
119 renderer->d.d.framesPending = 0;
120 pthread_mutex_unlock(&renderer->d.mutex);
121 glBindTexture(GL_TEXTURE_2D, renderer->tex);
122 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB5, 256, 256, 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, renderer->d.outputBuffer);
123 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
124
125 SDL_GL_SwapBuffers();
126 pthread_mutex_lock(&renderer->d.mutex);
127 pthread_cond_broadcast(&renderer->d.downCond);
128 pthread_mutex_unlock(&renderer->d.mutex);
129 } else {
130 while (!renderer->d.d.framesPending) {
131 struct timeval tv;
132 struct timespec ts;
133 gettimeofday(&tv, 0);
134 ts.tv_sec = tv.tv_sec;
135 ts.tv_nsec = tv.tv_usec * 1000 + 800000;
136 int err = pthread_cond_timedwait(&renderer->d.upCond, &renderer->d.mutex, &ts);
137 if (err == ETIMEDOUT) {
138 break;
139 }
140 }
141 pthread_mutex_unlock(&renderer->d.mutex);
142 }
143 while (SDL_PollEvent(&event)) {
144 switch (event.type) {
145 case SDL_QUIT:
146 // FIXME: this isn't thread-safe
147 context->debugger->state = DEBUGGER_EXITING;
148 break;
149 case SDL_KEYDOWN:
150 case SDL_KEYUP:
151 _GBASDLHandleKeypress(context, &event.key);
152 break;
153 }
154 }
155 }
156}
157
158static void _GBASDLDeinit(struct GLSoftwareRenderer* renderer) {
159 free(renderer->d.outputBuffer);
160
161 SDL_Quit();
162}
163
164static void _GBASDLHandleKeypress(struct GBAThread* context, const struct SDL_KeyboardEvent* event) {
165 enum GBAKey key = 0;
166 switch (event->keysym.sym) {
167 case SDLK_z:
168 key = GBA_KEY_A;
169 break;
170 case SDLK_x:
171 key = GBA_KEY_B;
172 break;
173 case SDLK_a:
174 key = GBA_KEY_L;
175 break;
176 case SDLK_s:
177 key = GBA_KEY_R;
178 break;
179 case SDLK_RETURN:
180 key = GBA_KEY_START;
181 break;
182 case SDLK_BACKSPACE:
183 key = GBA_KEY_SELECT;
184 break;
185 case SDLK_UP:
186 key = GBA_KEY_UP;
187 break;
188 case SDLK_DOWN:
189 key = GBA_KEY_DOWN;
190 break;
191 case SDLK_LEFT:
192 key = GBA_KEY_LEFT;
193 break;
194 case SDLK_RIGHT:
195 key = GBA_KEY_RIGHT;
196 break;
197 case SDLK_TAB:
198 context->renderer->turbo = !context->renderer->turbo;
199 return;
200 default:
201 return;
202 }
203
204 if (event->type == SDL_KEYDOWN) {
205 context->activeKeys |= 1 << key;
206 } else {
207 context->activeKeys &= ~(1 << key);
208 }
209}