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