1 |
3 |
howe.r.j.8 |
/**@file gui.c
|
2 |
5 |
howe.r.j.8 |
* @brief GUI Simulator for the H2 SoC
|
3 |
|
|
* @copyright Richard James Howe (2017-2019)
|
4 |
3 |
howe.r.j.8 |
* @license MIT
|
5 |
5 |
howe.r.j.8 |
* @note It would have been better to make this in SDL, se
|
6 |
|
|
* <https://www.libsdl.org/>. */
|
7 |
3 |
howe.r.j.8 |
|
8 |
|
|
#include "h2.h"
|
9 |
|
|
#include <assert.h>
|
10 |
|
|
#include <errno.h>
|
11 |
|
|
#include <ctype.h>
|
12 |
|
|
#include <stdlib.h>
|
13 |
|
|
#include <stdio.h>
|
14 |
|
|
#include <stdbool.h>
|
15 |
|
|
#include <math.h>
|
16 |
|
|
#include <string.h>
|
17 |
|
|
#include <stdint.h>
|
18 |
|
|
#include <inttypes.h>
|
19 |
|
|
#include <GL/gl.h>
|
20 |
|
|
#include <GL/glut.h>
|
21 |
|
|
#include <GL/freeglut_ext.h> /* for glutStrokeHeight */
|
22 |
|
|
#include <stdarg.h>
|
23 |
|
|
|
24 |
5 |
howe.r.j.8 |
#define TRON (0)
|
25 |
|
|
|
26 |
3 |
howe.r.j.8 |
/* ====================================== Utility Functions ==================================== */
|
27 |
|
|
|
28 |
|
|
#define PI (3.1415926535897932384626433832795)
|
29 |
|
|
#define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
|
30 |
|
|
#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
|
31 |
|
|
#define UNUSED(X) ((void)(X))
|
32 |
|
|
#define X_MAX (100.0)
|
33 |
|
|
#define X_MIN (0.0)
|
34 |
|
|
#define Y_MAX (100.0)
|
35 |
|
|
#define Y_MIN (0.0)
|
36 |
|
|
#define LINE_WIDTH (0.5)
|
37 |
|
|
#define CYCLE_MODE_FIXED (false)
|
38 |
|
|
#define CYCLE_INITIAL (100000)
|
39 |
|
|
#define CYCLE_INCREMENT (10000)
|
40 |
|
|
#define CYCLE_DECREMENT (500)
|
41 |
|
|
#define CYCLE_MINIMUM (10000)
|
42 |
|
|
#define CYCLE_HYSTERESIS (2.0)
|
43 |
|
|
#define TARGET_FPS (30.0)
|
44 |
|
|
#define BACKGROUND_ON (false)
|
45 |
|
|
#define SIM_HACKS (true)
|
46 |
5 |
howe.r.j.8 |
#define TRACE_FILE ("trace.csv")
|
47 |
|
|
#define TRACE_BUFFER_LEN (16*4096)
|
48 |
3 |
howe.r.j.8 |
|
49 |
|
|
typedef struct {
|
50 |
|
|
double window_height;
|
51 |
|
|
double window_width;
|
52 |
|
|
double window_x_starting_position;
|
53 |
|
|
double window_y_starting_position;
|
54 |
|
|
double window_scale_x;
|
55 |
|
|
double window_scale_y;
|
56 |
5 |
howe.r.j.8 |
void *font_scaled;
|
57 |
|
|
uint64_t cycle_count;
|
58 |
|
|
uint64_t cycles;
|
59 |
3 |
howe.r.j.8 |
volatile unsigned tick;
|
60 |
|
|
unsigned arena_tick_ms;
|
61 |
5 |
howe.r.j.8 |
volatile bool halt_simulation;
|
62 |
3 |
howe.r.j.8 |
bool use_uart_input;
|
63 |
|
|
bool debug_extra;
|
64 |
|
|
bool step;
|
65 |
|
|
bool debug_mode;
|
66 |
|
|
} world_t;
|
67 |
|
|
|
68 |
|
|
static world_t world = {
|
69 |
|
|
.window_height = 800.0,
|
70 |
|
|
.window_width = 800.0,
|
71 |
|
|
.window_x_starting_position = 60.0,
|
72 |
|
|
.window_y_starting_position = 20.0,
|
73 |
|
|
.window_scale_x = 1.0,
|
74 |
|
|
.window_scale_y = 1.0,
|
75 |
|
|
.tick = 0,
|
76 |
|
|
.halt_simulation = false,
|
77 |
|
|
.arena_tick_ms = 30,
|
78 |
|
|
.use_uart_input = true,
|
79 |
|
|
.debug_extra = false,
|
80 |
|
|
.step = false,
|
81 |
|
|
.debug_mode = false,
|
82 |
|
|
.cycle_count = 0,
|
83 |
|
|
.cycles = CYCLE_INITIAL,
|
84 |
|
|
.font_scaled = GLUT_STROKE_MONO_ROMAN
|
85 |
|
|
};
|
86 |
|
|
|
87 |
5 |
howe.r.j.8 |
static FILE *trace_file = NULL; /* NB. !NULL turns tracing on */
|
88 |
|
|
static char trace_buffer[TRACE_BUFFER_LEN];
|
89 |
|
|
|
90 |
3 |
howe.r.j.8 |
typedef enum {
|
91 |
|
|
TRIANGLE,
|
92 |
|
|
SQUARE,
|
93 |
|
|
PENTAGON,
|
94 |
|
|
HEXAGON,
|
95 |
|
|
SEPTAGON,
|
96 |
|
|
OCTAGON,
|
97 |
|
|
DECAGON,
|
98 |
|
|
CIRCLE,
|
99 |
|
|
INVALID_SHAPE
|
100 |
|
|
} shape_e;
|
101 |
|
|
|
102 |
|
|
typedef shape_e shape_t;
|
103 |
|
|
|
104 |
|
|
typedef struct {
|
105 |
|
|
double x;
|
106 |
|
|
double y;
|
107 |
|
|
} scale_t;
|
108 |
|
|
|
109 |
|
|
typedef struct {
|
110 |
|
|
double x, y;
|
111 |
|
|
bool draw_border;
|
112 |
|
|
color_t color_text, color_box;
|
113 |
|
|
double width, height;
|
114 |
|
|
} textbox_t;
|
115 |
|
|
|
116 |
|
|
typedef struct { /**@note it might be worth translating some functions to use points*/
|
117 |
|
|
double x, y;
|
118 |
|
|
} point_t;
|
119 |
|
|
|
120 |
|
|
|
121 |
|
|
/**@bug not quite correct, arena_tick_ms is what we request, not want the arena
|
122 |
|
|
* tick actually is */
|
123 |
5 |
howe.r.j.8 |
static double seconds_to_ticks(const world_t *world, double s) {
|
124 |
3 |
howe.r.j.8 |
assert(world);
|
125 |
|
|
return s * (1000. / (double)world->arena_tick_ms);
|
126 |
|
|
}
|
127 |
|
|
|
128 |
5 |
howe.r.j.8 |
static double rad2deg(const double rad) {
|
129 |
3 |
howe.r.j.8 |
return (rad / (2.0 * PI)) * 360.0;
|
130 |
|
|
}
|
131 |
|
|
|
132 |
5 |
howe.r.j.8 |
static void set_color(const color_t color, const bool light) {
|
133 |
3 |
howe.r.j.8 |
double ON = light ? 0.8 : 0.4;
|
134 |
|
|
static const double OFF = 0.0;
|
135 |
5 |
howe.r.j.8 |
switch (color) { /* RED GRN BLU */
|
136 |
3 |
howe.r.j.8 |
case WHITE: glColor3f( ON, ON, ON); break;
|
137 |
|
|
case RED: glColor3f( ON, OFF, OFF); break;
|
138 |
|
|
case YELLOW: glColor3f( ON, ON, OFF); break;
|
139 |
|
|
case GREEN: glColor3f(OFF, ON, OFF); break;
|
140 |
|
|
case CYAN: glColor3f(OFF, ON, ON); break;
|
141 |
|
|
case BLUE: glColor3f(OFF, OFF, ON); break;
|
142 |
|
|
case MAGENTA: glColor3f( ON, OFF, ON); break;
|
143 |
|
|
case BLACK: glColor3f(OFF, OFF, OFF); break;
|
144 |
|
|
default: fatal("invalid color '%d'", color);
|
145 |
|
|
}
|
146 |
|
|
}
|
147 |
|
|
|
148 |
|
|
/* see: https://www.opengl.org/discussion_boards/showthread.php/160784-Drawing-Circles-in-OpenGL */
|
149 |
|
|
static void _draw_regular_polygon(
|
150 |
5 |
howe.r.j.8 |
const double x, const double y,
|
151 |
|
|
const double orientation,
|
152 |
|
|
const double radius, const double sides,
|
153 |
|
|
const bool lines, const double thickness,
|
154 |
|
|
const color_t color) {
|
155 |
3 |
howe.r.j.8 |
glMatrixMode(GL_MODELVIEW);
|
156 |
|
|
glPushMatrix();
|
157 |
|
|
glLoadIdentity();
|
158 |
|
|
glTranslatef(x, y, 0.0);
|
159 |
|
|
glRotated(rad2deg(orientation), 0, 0, 1);
|
160 |
|
|
set_color(color, true);
|
161 |
5 |
howe.r.j.8 |
if (lines) {
|
162 |
3 |
howe.r.j.8 |
glLineWidth(thickness);
|
163 |
|
|
glBegin(GL_LINE_LOOP);
|
164 |
|
|
} else {
|
165 |
|
|
glBegin(GL_POLYGON);
|
166 |
|
|
}
|
167 |
5 |
howe.r.j.8 |
for (double i = 0; i < 2.0 * PI; i += PI / sides)
|
168 |
3 |
howe.r.j.8 |
glVertex3d(cos(i) * radius, sin(i) * radius, 0.0);
|
169 |
|
|
glEnd();
|
170 |
|
|
glPopMatrix();
|
171 |
|
|
}
|
172 |
|
|
|
173 |
5 |
howe.r.j.8 |
static void _draw_rectangle(
|
174 |
|
|
const double x, const double y,
|
175 |
|
|
const double width, const double height,
|
176 |
|
|
const bool lines, const double thickness,
|
177 |
|
|
const color_t color) {
|
178 |
3 |
howe.r.j.8 |
glMatrixMode(GL_MODELVIEW);
|
179 |
|
|
glPushMatrix();
|
180 |
|
|
glLoadIdentity();
|
181 |
|
|
glRasterPos2d(x, y);
|
182 |
|
|
set_color(color, true);
|
183 |
5 |
howe.r.j.8 |
if (lines) {
|
184 |
3 |
howe.r.j.8 |
glLineWidth(thickness);
|
185 |
|
|
glBegin(GL_LINE_LOOP);
|
186 |
|
|
} else {
|
187 |
|
|
glBegin(GL_POLYGON);
|
188 |
|
|
}
|
189 |
|
|
glVertex3d(x, y, 0);
|
190 |
|
|
glVertex3d(x+width, y, 0);
|
191 |
|
|
glVertex3d(x+width, y+height, 0);
|
192 |
|
|
glVertex3d(x, y+height, 0);
|
193 |
|
|
glEnd();
|
194 |
|
|
glPopMatrix();
|
195 |
|
|
}
|
196 |
|
|
|
197 |
5 |
howe.r.j.8 |
static void draw_rectangle_filled(const double x, const double y, const double width, const double height, const color_t color) {
|
198 |
|
|
_draw_rectangle(x, y, width, height, false, 0, color);
|
199 |
3 |
howe.r.j.8 |
}
|
200 |
|
|
|
201 |
5 |
howe.r.j.8 |
static void draw_rectangle_line(const double x, const double y, const double width, const double height, const double thickness, const color_t color) {
|
202 |
|
|
_draw_rectangle(x, y, width, height, true, thickness, color);
|
203 |
3 |
howe.r.j.8 |
}
|
204 |
|
|
|
205 |
5 |
howe.r.j.8 |
static double shape_to_sides(shape_t shape) {
|
206 |
3 |
howe.r.j.8 |
static const double sides[] =
|
207 |
|
|
{
|
208 |
|
|
[TRIANGLE] = 1.5,
|
209 |
|
|
[SQUARE] = 2,
|
210 |
|
|
[PENTAGON] = 2.5,
|
211 |
|
|
[HEXAGON] = 3,
|
212 |
|
|
[SEPTAGON] = 3.5,
|
213 |
|
|
[OCTAGON] = 4,
|
214 |
|
|
[DECAGON] = 5,
|
215 |
|
|
[CIRCLE] = 24
|
216 |
|
|
};
|
217 |
5 |
howe.r.j.8 |
if (shape >= INVALID_SHAPE)
|
218 |
3 |
howe.r.j.8 |
fatal("invalid shape '%d'", shape);
|
219 |
|
|
return sides[shape % INVALID_SHAPE];
|
220 |
|
|
}
|
221 |
|
|
|
222 |
5 |
howe.r.j.8 |
static void draw_regular_polygon_filled(const double x, const double y, const double orientation, const double radius, const shape_t shape, const color_t color) {
|
223 |
|
|
_draw_regular_polygon(x, y, orientation, radius, shape_to_sides(shape), false, 0, color);
|
224 |
3 |
howe.r.j.8 |
}
|
225 |
|
|
|
226 |
5 |
howe.r.j.8 |
static void draw_regular_polygon_line(const double x, const double y, const double orientation, const double radius, const shape_t shape, const double thickness, const color_t color) {
|
227 |
|
|
_draw_regular_polygon(x, y, orientation, radius, shape_to_sides(shape), true, thickness, color);
|
228 |
3 |
howe.r.j.8 |
}
|
229 |
|
|
|
230 |
5 |
howe.r.j.8 |
static void draw_char(uint8_t c) {
|
231 |
3 |
howe.r.j.8 |
c = c >= 32 && c <= 127 ? c : '?';
|
232 |
|
|
glutStrokeCharacter(world.font_scaled, c);
|
233 |
|
|
}
|
234 |
|
|
|
235 |
|
|
/* see: https://en.wikibooks.org/wiki/OpenGL_Programming/Modern_OpenGL_Tutorial_Text_Rendering_01
|
236 |
|
|
* https://stackoverflow.com/questions/538661/how-do-i-draw-text-with-glut-opengl-in-c
|
237 |
|
|
* https://stackoverflow.com/questions/20866508/using-glut-to-simply-print-text */
|
238 |
5 |
howe.r.j.8 |
static int draw_block(const uint8_t *msg, const size_t len) {
|
239 |
3 |
howe.r.j.8 |
assert(msg);
|
240 |
5 |
howe.r.j.8 |
for (size_t i = 0; i < len; i++)
|
241 |
3 |
howe.r.j.8 |
draw_char(msg[i]);
|
242 |
|
|
return len;
|
243 |
|
|
}
|
244 |
|
|
|
245 |
5 |
howe.r.j.8 |
static int draw_string(const char *msg) {
|
246 |
3 |
howe.r.j.8 |
assert(msg);
|
247 |
|
|
return draw_block((uint8_t*)msg, strlen(msg));
|
248 |
|
|
}
|
249 |
|
|
|
250 |
5 |
howe.r.j.8 |
static scale_t font_attributes(void) {
|
251 |
|
|
static bool initialized = false; // @warning static!
|
252 |
|
|
static scale_t scale = { 0., 0.}; // @warning static!
|
253 |
|
|
if (initialized)
|
254 |
3 |
howe.r.j.8 |
return scale;
|
255 |
|
|
scale.y = glutStrokeHeight(world.font_scaled);
|
256 |
|
|
scale.x = glutStrokeWidth(world.font_scaled, 'M');
|
257 |
|
|
initialized = true;
|
258 |
|
|
return scale;
|
259 |
|
|
}
|
260 |
|
|
|
261 |
5 |
howe.r.j.8 |
static void draw_vt100_char(
|
262 |
|
|
const double x, const double y,
|
263 |
|
|
const double scale_x, const double scale_y,
|
264 |
|
|
const double orientation, const uint8_t c,
|
265 |
|
|
const vt100_attribute_t *attr, const bool blink) {
|
266 |
|
|
assert(attr);
|
267 |
3 |
howe.r.j.8 |
/*scale_t scale = font_attributes();
|
268 |
|
|
double char_width = scale.x / X_MAX;
|
269 |
|
|
double char_height = scale.y / Y_MAX;*/
|
270 |
|
|
|
271 |
5 |
howe.r.j.8 |
if (blink && attr->blink)
|
272 |
3 |
howe.r.j.8 |
return;
|
273 |
|
|
|
274 |
|
|
glMatrixMode(GL_MODELVIEW);
|
275 |
|
|
glPushMatrix();
|
276 |
|
|
glLoadIdentity();
|
277 |
|
|
glTranslatef(x, y, 0.0);
|
278 |
|
|
glScaled(scale_x, scale_y, 1.0);
|
279 |
|
|
glRotated(rad2deg(orientation), 0, 0, 1);
|
280 |
|
|
set_color(attr->foreground_color, attr->bold);
|
281 |
|
|
draw_char(attr->conceal ? '*' : c);
|
282 |
|
|
glEnd();
|
283 |
|
|
glPopMatrix();
|
284 |
5 |
howe.r.j.8 |
if (BACKGROUND_ON)
|
285 |
3 |
howe.r.j.8 |
draw_rectangle_filled(x, y, 1.20, 1.55, attr->background_color);
|
286 |
|
|
}
|
287 |
|
|
|
288 |
5 |
howe.r.j.8 |
static int draw_vt100_block(
|
289 |
|
|
const double x, const double y,
|
290 |
|
|
const double scale_x, const double scale_y,
|
291 |
|
|
const double orientation, const uint8_t *msg,
|
292 |
|
|
const size_t len, const vt100_attribute_t * const attr, const bool blink) {
|
293 |
|
|
assert(attr);
|
294 |
|
|
const scale_t scale = font_attributes();
|
295 |
|
|
const double char_width = (scale.x / X_MAX)*1.1;
|
296 |
|
|
for (size_t i = 0; i < len; i++)
|
297 |
3 |
howe.r.j.8 |
draw_vt100_char(x+char_width*i, y, scale_x, scale_y, orientation, msg[i], &attr[i], blink);
|
298 |
|
|
return len;
|
299 |
|
|
}
|
300 |
|
|
|
301 |
5 |
howe.r.j.8 |
static int draw_block_scaled(
|
302 |
|
|
const double x, const double y,
|
303 |
|
|
const double scale_x, const double scale_y,
|
304 |
|
|
const double orientation, const uint8_t *msg, const size_t len, const color_t color) {
|
305 |
3 |
howe.r.j.8 |
assert(msg);
|
306 |
|
|
glMatrixMode(GL_MODELVIEW);
|
307 |
|
|
glPushMatrix();
|
308 |
|
|
glLoadIdentity();
|
309 |
|
|
glTranslatef(x, y, 0.0);
|
310 |
|
|
glScaled(scale_x, scale_y, 1.0);
|
311 |
|
|
glRotated(rad2deg(orientation), 0, 0, 1);
|
312 |
|
|
set_color(color, true);
|
313 |
5 |
howe.r.j.8 |
for (size_t i = 0; i < len; i++) {
|
314 |
3 |
howe.r.j.8 |
uint8_t c = msg[i];
|
315 |
|
|
c = c >= 32 && c <= 127 ? c : '?';
|
316 |
|
|
glutStrokeCharacter(world.font_scaled, c);
|
317 |
|
|
}
|
318 |
|
|
glEnd();
|
319 |
|
|
glPopMatrix();
|
320 |
|
|
return len;
|
321 |
|
|
}
|
322 |
|
|
|
323 |
5 |
howe.r.j.8 |
static int draw_string_scaled(
|
324 |
|
|
const double x, const double y,
|
325 |
|
|
const double scale_x, const double scale_y,
|
326 |
|
|
const double orientation, const char *msg, const color_t color) {
|
327 |
3 |
howe.r.j.8 |
assert(msg);
|
328 |
|
|
return draw_block_scaled(x, y, scale_x, scale_y, orientation, (uint8_t*)msg, strlen(msg), color);
|
329 |
|
|
}
|
330 |
|
|
|
331 |
5 |
howe.r.j.8 |
static int vdraw_text(const color_t color, const double x, const double y, const char *fmt, va_list ap) {
|
332 |
3 |
howe.r.j.8 |
int r = 0;
|
333 |
|
|
assert(fmt);
|
334 |
|
|
static const double scale_x = 0.011;
|
335 |
|
|
static const double scale_y = 0.011;
|
336 |
|
|
|
337 |
|
|
glMatrixMode(GL_MODELVIEW);
|
338 |
|
|
glPushMatrix();
|
339 |
|
|
set_color(color, true);
|
340 |
|
|
glTranslatef(x, y, 0);
|
341 |
|
|
glScaled(scale_x, scale_y, 1.0);
|
342 |
5 |
howe.r.j.8 |
while (*fmt) {
|
343 |
|
|
char f = *fmt++;
|
344 |
|
|
if ('%' != f) {
|
345 |
3 |
howe.r.j.8 |
glutStrokeCharacter(world.font_scaled, f);
|
346 |
|
|
r++;
|
347 |
|
|
continue;
|
348 |
|
|
}
|
349 |
5 |
howe.r.j.8 |
switch ((f = *fmt++)) {
|
350 |
3 |
howe.r.j.8 |
case 'c':
|
351 |
|
|
{
|
352 |
|
|
char x[2] = {0, 0};
|
353 |
|
|
x[0] = va_arg(ap, int);
|
354 |
|
|
r += draw_string(x);
|
355 |
|
|
break;
|
356 |
|
|
}
|
357 |
|
|
case 's':
|
358 |
|
|
{
|
359 |
|
|
char *s = va_arg(ap, char*);
|
360 |
|
|
r += draw_string(s);
|
361 |
|
|
break;
|
362 |
|
|
}
|
363 |
|
|
case 'x':
|
364 |
|
|
{
|
365 |
5 |
howe.r.j.8 |
const unsigned d = va_arg(ap, unsigned);
|
366 |
3 |
howe.r.j.8 |
char s[64] = {0};
|
367 |
|
|
sprintf(s, "%04x", d);
|
368 |
|
|
r += draw_string(s);
|
369 |
|
|
break;
|
370 |
|
|
}
|
371 |
|
|
case 'u':
|
372 |
|
|
case 'd':
|
373 |
|
|
{
|
374 |
5 |
howe.r.j.8 |
const int d = va_arg(ap, int);
|
375 |
3 |
howe.r.j.8 |
char s[64] = {0};
|
376 |
|
|
sprintf(s, f == 'u' ? "%u": "%d", d);
|
377 |
|
|
r += draw_string(s);
|
378 |
|
|
break;
|
379 |
|
|
}
|
380 |
|
|
case 'f':
|
381 |
|
|
{
|
382 |
5 |
howe.r.j.8 |
const double f = va_arg(ap, double);
|
383 |
3 |
howe.r.j.8 |
char s[512] = {0};
|
384 |
|
|
sprintf(s, "%.2f", f);
|
385 |
|
|
r += draw_string(s);
|
386 |
|
|
break;
|
387 |
|
|
}
|
388 |
|
|
case 0:
|
389 |
|
|
default:
|
390 |
|
|
fatal("invalid format specifier '%c'", f);
|
391 |
|
|
}
|
392 |
|
|
|
393 |
|
|
}
|
394 |
|
|
glPopMatrix();
|
395 |
|
|
return r;
|
396 |
|
|
}
|
397 |
|
|
|
398 |
5 |
howe.r.j.8 |
static void fill_textbox(textbox_t *t, const char *fmt, ...) {
|
399 |
3 |
howe.r.j.8 |
va_list ap;
|
400 |
|
|
assert(t);
|
401 |
|
|
assert(fmt);
|
402 |
|
|
|
403 |
|
|
scale_t scale = font_attributes();
|
404 |
5 |
howe.r.j.8 |
const double char_width = scale.x / X_MAX;
|
405 |
|
|
const double char_height = scale.y / Y_MAX;
|
406 |
3 |
howe.r.j.8 |
assert(t && fmt);
|
407 |
|
|
va_start(ap, fmt);
|
408 |
5 |
howe.r.j.8 |
double r = vdraw_text(t->color_text, t->x, t->y - t->height, fmt, ap);
|
409 |
3 |
howe.r.j.8 |
r *= char_width * 1.11;
|
410 |
|
|
r += 1;
|
411 |
|
|
va_end(ap);
|
412 |
|
|
t->width = MAX(t->width, r);
|
413 |
|
|
t->height += (char_height); /*correct?*/
|
414 |
|
|
}
|
415 |
|
|
|
416 |
5 |
howe.r.j.8 |
static void draw_textbox(textbox_t *t) {
|
417 |
3 |
howe.r.j.8 |
assert(t);
|
418 |
|
|
scale_t scale = font_attributes();
|
419 |
5 |
howe.r.j.8 |
const double char_height = scale.y / Y_MAX;
|
420 |
|
|
if (!(t->draw_border))
|
421 |
3 |
howe.r.j.8 |
return;
|
422 |
|
|
draw_rectangle_line(t->x - LINE_WIDTH, t->y - t->height + char_height - 1, t->width, t->height + 1, LINE_WIDTH, t->color_box);
|
423 |
|
|
}
|
424 |
|
|
|
425 |
|
|
static bool detect_circle_circle_collision(
|
426 |
5 |
howe.r.j.8 |
const double ax, const double ay, const double aradius,
|
427 |
|
|
const double bx, const double by, const double bradius) {
|
428 |
|
|
const double dx = ax - bx;
|
429 |
|
|
const double dy = ay - by;
|
430 |
|
|
const double distance = hypot(dx, dy);
|
431 |
|
|
return distance < (aradius + bradius);
|
432 |
3 |
howe.r.j.8 |
}
|
433 |
|
|
|
434 |
|
|
/* ====================================== Utility Functions ==================================== */
|
435 |
|
|
|
436 |
|
|
/* ====================================== Simulator Objects ==================================== */
|
437 |
|
|
|
438 |
|
|
typedef struct {
|
439 |
|
|
double x;
|
440 |
|
|
double y;
|
441 |
|
|
double angle;
|
442 |
|
|
double radius;
|
443 |
|
|
bool on;
|
444 |
|
|
} led_t;
|
445 |
|
|
|
446 |
5 |
howe.r.j.8 |
static void draw_led(const led_t * const l) {
|
447 |
3 |
howe.r.j.8 |
assert(l);
|
448 |
5 |
howe.r.j.8 |
const double msz = l->radius * 0.75;
|
449 |
|
|
const double off = (l->radius - msz) / 2.0;
|
450 |
|
|
draw_rectangle_filled(l->x + off, l->y + off, msz, msz, l->on ? GREEN : RED);
|
451 |
3 |
howe.r.j.8 |
draw_rectangle_filled(l->x, l->y, l->radius, l->radius, BLUE);
|
452 |
|
|
}
|
453 |
|
|
|
454 |
|
|
typedef struct {
|
455 |
|
|
double x;
|
456 |
|
|
double y;
|
457 |
|
|
double angle;
|
458 |
|
|
double radius;
|
459 |
|
|
bool on;
|
460 |
|
|
} switch_t;
|
461 |
|
|
|
462 |
5 |
howe.r.j.8 |
static void draw_switch(const switch_t * const s) {
|
463 |
3 |
howe.r.j.8 |
assert(s);
|
464 |
5 |
howe.r.j.8 |
const double msz = s->radius * 0.6;
|
465 |
|
|
const double off = (s->radius - msz) / 2.0;
|
466 |
|
|
draw_rectangle_filled(s->x + off, s->on ? (s->y + s->radius) - off : s->y+off, msz, msz, s->on ? GREEN : RED);
|
467 |
|
|
draw_rectangle_filled(s->x + off, s->y + off, msz, msz * 2., BLACK);
|
468 |
3 |
howe.r.j.8 |
draw_rectangle_filled(s->x, s->y, s->radius, s->radius * 2, BLUE);
|
469 |
|
|
}
|
470 |
|
|
|
471 |
|
|
typedef enum {
|
472 |
|
|
LED_SEGMENT_A,
|
473 |
|
|
LED_SEGMENT_B,
|
474 |
|
|
LED_SEGMENT_C,
|
475 |
|
|
LED_SEGMENT_D,
|
476 |
|
|
LED_SEGMENT_E,
|
477 |
|
|
LED_SEGMENT_F,
|
478 |
|
|
LED_SEGMENT_G,
|
479 |
|
|
LED_SEGMENT_DP,
|
480 |
|
|
} led_segment_e;
|
481 |
|
|
|
482 |
|
|
typedef struct {
|
483 |
|
|
double x;
|
484 |
|
|
double y;
|
485 |
|
|
/*double angle;*/
|
486 |
|
|
double width;
|
487 |
|
|
double height;
|
488 |
|
|
uint8_t segment;
|
489 |
|
|
} led_8_segment_t;
|
490 |
|
|
|
491 |
5 |
howe.r.j.8 |
static uint8_t convert_to_segments(const uint8_t segment) {
|
492 |
3 |
howe.r.j.8 |
static const uint8_t c2s[16] = {
|
493 |
|
|
0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07,
|
494 |
|
|
0x7F, 0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71
|
495 |
|
|
};
|
496 |
|
|
return c2s[segment & 0xf];
|
497 |
|
|
}
|
498 |
|
|
|
499 |
|
|
#define SEG_CLR(SG,BIT) (((SG) & (1 << BIT)) ? RED : BLACK)
|
500 |
|
|
|
501 |
5 |
howe.r.j.8 |
static void draw_led_8_segment(const led_8_segment_t * const l) {
|
502 |
3 |
howe.r.j.8 |
assert(l);
|
503 |
5 |
howe.r.j.8 |
const uint8_t sgs = convert_to_segments(l->segment);
|
504 |
3 |
howe.r.j.8 |
|
505 |
|
|
draw_rectangle_filled(l->x + l->width * 0.20, l->y + l->height * 0.45, l->width * 0.5, l->height * 0.1, SEG_CLR(sgs, LED_SEGMENT_G)); /* Center */
|
506 |
|
|
draw_rectangle_filled(l->x + l->width * 0.20, l->y + l->height * 0.1, l->width * 0.5, l->height * 0.1, SEG_CLR(sgs, LED_SEGMENT_D)); /* Bottom */
|
507 |
|
|
draw_rectangle_filled(l->x + l->width * 0.20, l->y + l->height * 0.8, l->width * 0.5, l->height * 0.1, SEG_CLR(sgs, LED_SEGMENT_A)); /* Top */
|
508 |
|
|
|
509 |
|
|
draw_rectangle_filled(l->x + l->width * 0.05, l->y + l->height * 0.15, l->width * 0.1, l->height * 0.3, SEG_CLR(sgs, LED_SEGMENT_E)); /* Bottom Left */
|
510 |
|
|
draw_rectangle_filled(l->x + l->width * 0.75, l->y + l->height * 0.15, l->width * 0.1, l->height * 0.3, SEG_CLR(sgs, LED_SEGMENT_C)); /* Bottom Right */
|
511 |
|
|
|
512 |
|
|
draw_rectangle_filled(l->x + l->width * 0.05, l->y + l->height * 0.50, l->width * 0.1, l->height * 0.3, SEG_CLR(sgs, LED_SEGMENT_F)); /* Top Left */
|
513 |
|
|
draw_rectangle_filled(l->x + l->width * 0.75, l->y + l->height * 0.50, l->width * 0.1, l->height * 0.3, SEG_CLR(sgs, LED_SEGMENT_B)); /* Top Right */
|
514 |
|
|
|
515 |
|
|
draw_regular_polygon_filled(l->x + l->width * 0.9, l->y + l->height * 0.07, 0.0, sqrt(l->width*l->height)*.06, CIRCLE, SEG_CLR(sgs, LED_SEGMENT_DP));
|
516 |
|
|
|
517 |
|
|
draw_rectangle_filled(l->x, l->y, l->width, l->height, WHITE);
|
518 |
|
|
}
|
519 |
|
|
|
520 |
|
|
typedef struct {
|
521 |
|
|
double x;
|
522 |
|
|
double y;
|
523 |
|
|
double angle;
|
524 |
|
|
double radius;
|
525 |
|
|
|
526 |
|
|
bool up;
|
527 |
|
|
bool down;
|
528 |
|
|
bool left;
|
529 |
|
|
bool right;
|
530 |
|
|
bool center;
|
531 |
|
|
} dpad_t;
|
532 |
|
|
|
533 |
5 |
howe.r.j.8 |
static void draw_dpad(const dpad_t * const d) {
|
534 |
|
|
assert(d);
|
535 |
3 |
howe.r.j.8 |
draw_regular_polygon_filled(d->x + (d->radius*2.0), d->y, d->angle, d->radius, TRIANGLE, d->right ? GREEN : RED);
|
536 |
|
|
draw_regular_polygon_filled(d->x - (d->radius*2.0), d->y, d->angle + (PI/3.0), d->radius, TRIANGLE, d->left ? GREEN : RED);
|
537 |
|
|
draw_regular_polygon_filled(d->x, d->y - (d->radius*2.0), d->angle - (PI/2.0), d->radius, TRIANGLE, d->down ? GREEN : RED);
|
538 |
|
|
draw_regular_polygon_filled(d->x, d->y + (d->radius*2.0), d->angle + (PI/2.0), d->radius, TRIANGLE, d->up ? GREEN : RED);
|
539 |
|
|
draw_regular_polygon_filled(d->x, d->y, d->angle, d->radius, CIRCLE, d->center ? GREEN : RED);
|
540 |
|
|
|
541 |
|
|
draw_regular_polygon_line(d->x, d->y, d->angle, d->radius * 3.1, CIRCLE, LINE_WIDTH, WHITE);
|
542 |
|
|
}
|
543 |
|
|
|
544 |
|
|
typedef enum {
|
545 |
|
|
DPAN_COL_NONE,
|
546 |
|
|
DPAN_COL_RIGHT,
|
547 |
|
|
DPAN_COL_LEFT,
|
548 |
|
|
DPAN_COL_DOWN,
|
549 |
|
|
DPAN_COL_UP,
|
550 |
|
|
DPAN_COL_CENTER
|
551 |
|
|
} dpad_collision_e;
|
552 |
|
|
|
553 |
5 |
howe.r.j.8 |
static dpad_collision_e dpad_collision(const dpad_t * const d, const double x, const double y, const double radius) {
|
554 |
|
|
assert(d);
|
555 |
|
|
if (detect_circle_circle_collision(x, y, radius, d->x + (d->radius*2.0), d->y, d->radius))
|
556 |
3 |
howe.r.j.8 |
return DPAN_COL_RIGHT;
|
557 |
5 |
howe.r.j.8 |
if (detect_circle_circle_collision(x, y, radius, d->x - (d->radius*2.0), d->y, d->radius))
|
558 |
3 |
howe.r.j.8 |
return DPAN_COL_LEFT;
|
559 |
5 |
howe.r.j.8 |
if (detect_circle_circle_collision(x, y, radius, d->x, d->y + (d->radius*2.0), d->radius))
|
560 |
3 |
howe.r.j.8 |
return DPAN_COL_UP;
|
561 |
5 |
howe.r.j.8 |
if (detect_circle_circle_collision(x, y, radius, d->x, d->y - (d->radius*2.0), d->radius))
|
562 |
3 |
howe.r.j.8 |
return DPAN_COL_DOWN;
|
563 |
5 |
howe.r.j.8 |
if (detect_circle_circle_collision(x, y, radius, d->x, d->y, d->radius))
|
564 |
3 |
howe.r.j.8 |
return DPAN_COL_CENTER;
|
565 |
|
|
return DPAN_COL_NONE;
|
566 |
|
|
}
|
567 |
|
|
|
568 |
|
|
#define TERMINAL_WIDTH (80)
|
569 |
|
|
#define TERMINAL_HEIGHT (10)
|
570 |
|
|
#define TERMINAL_SIZE (TERMINAL_WIDTH*TERMINAL_HEIGHT)
|
571 |
|
|
|
572 |
|
|
typedef struct {
|
573 |
|
|
unsigned width;
|
574 |
|
|
unsigned height;
|
575 |
|
|
GLuint name;
|
576 |
|
|
uint8_t *image;
|
577 |
|
|
} vt100_background_texture_t;
|
578 |
|
|
|
579 |
|
|
typedef struct {
|
580 |
|
|
uint64_t blink_count;
|
581 |
|
|
double x;
|
582 |
|
|
double y;
|
583 |
|
|
bool blink_on;
|
584 |
|
|
color_t color;
|
585 |
|
|
vt100_t vt100;
|
586 |
|
|
vt100_background_texture_t *texture;
|
587 |
|
|
} terminal_t;
|
588 |
|
|
|
589 |
5 |
howe.r.j.8 |
static void texture_background(const terminal_t * const t) {
|
590 |
3 |
howe.r.j.8 |
assert(t);
|
591 |
5 |
howe.r.j.8 |
const vt100_background_texture_t *v = t->texture;
|
592 |
|
|
const vt100_t *vt = &t->vt100;
|
593 |
|
|
uint8_t *const img = v->image;
|
594 |
3 |
howe.r.j.8 |
const unsigned h = v->height;
|
595 |
|
|
const unsigned w = v->width;
|
596 |
|
|
|
597 |
5 |
howe.r.j.8 |
for (unsigned i = 0; i < h; i++) {
|
598 |
|
|
uint8_t * const row = &img[i * 4];
|
599 |
|
|
const unsigned ii = ((h - i - 1) * vt->height) / h;
|
600 |
|
|
for (unsigned j = 0; j < w; j++) {
|
601 |
|
|
uint8_t * const column = &row[j * h * 4];
|
602 |
|
|
const unsigned jj = (vt->width * j) / w;
|
603 |
|
|
const unsigned idx = jj + (ii * vt->width);
|
604 |
3 |
howe.r.j.8 |
column[0] = 255 * (vt->attributes[idx].background_color & 1);
|
605 |
|
|
column[1] = 255 * (vt->attributes[idx].background_color & 2);
|
606 |
|
|
column[2] = 255 * (vt->attributes[idx].background_color & 4);
|
607 |
|
|
column[3] = 255;
|
608 |
|
|
}
|
609 |
|
|
}
|
610 |
|
|
}
|
611 |
|
|
|
612 |
|
|
/* See <http://www.glprogramming.com/red/chapter09.html> */
|
613 |
5 |
howe.r.j.8 |
static void draw_texture(const terminal_t * const t, const bool update) {
|
614 |
|
|
assert(t);
|
615 |
3 |
howe.r.j.8 |
vt100_background_texture_t *v = t->texture;
|
616 |
5 |
howe.r.j.8 |
if (!v)
|
617 |
3 |
howe.r.j.8 |
return;
|
618 |
|
|
|
619 |
5 |
howe.r.j.8 |
const scale_t scale = font_attributes();
|
620 |
|
|
const double char_width = scale.x / X_MAX;
|
621 |
|
|
const double char_height = scale.y / Y_MAX;
|
622 |
|
|
const double x = t->x;
|
623 |
|
|
const double y = t->y - (char_height * (t->vt100.height-1.0));
|
624 |
|
|
const double width = char_width * t->vt100.width * 1.10;
|
625 |
|
|
const double height = char_height * t->vt100.height;
|
626 |
3 |
howe.r.j.8 |
|
627 |
|
|
glEnable(GL_TEXTURE_2D);
|
628 |
|
|
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
|
629 |
|
|
|
630 |
5 |
howe.r.j.8 |
if (update) {
|
631 |
3 |
howe.r.j.8 |
glClearColor (0.0, 0.0, 0.0, 0.0);
|
632 |
|
|
glShadeModel(GL_FLAT);
|
633 |
|
|
glEnable(GL_DEPTH_TEST);
|
634 |
|
|
|
635 |
|
|
texture_background(t);
|
636 |
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
637 |
|
|
|
638 |
|
|
glGenTextures(1, &v->name);
|
639 |
|
|
glBindTexture(GL_TEXTURE_2D, v->name);
|
640 |
|
|
|
641 |
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
642 |
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
643 |
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
644 |
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
645 |
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, v->width, v->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, v->image);
|
646 |
|
|
}
|
647 |
|
|
|
648 |
|
|
glBindTexture(GL_TEXTURE_2D, v->name);
|
649 |
|
|
glMatrixMode(GL_MODELVIEW);
|
650 |
|
|
glBegin(GL_QUADS);
|
651 |
|
|
glTexCoord2f(1.0, 0.0); glVertex3f(x, y+height, 0.0);
|
652 |
|
|
glTexCoord2f(1.0, 1.0); glVertex3f(x+width, y+height, 0.0);
|
653 |
|
|
glTexCoord2f(0.0, 1.0); glVertex3f(x+width, y, 0.0);
|
654 |
|
|
glTexCoord2f(0.0, 0.0); glVertex3f(x, y, 0.0);
|
655 |
|
|
glEnd();
|
656 |
|
|
glDisable(GL_TEXTURE_2D);
|
657 |
|
|
}
|
658 |
|
|
|
659 |
5 |
howe.r.j.8 |
void draw_terminal(const world_t *world, terminal_t *t, const char * const name) {
|
660 |
3 |
howe.r.j.8 |
assert(world);
|
661 |
|
|
assert(t);
|
662 |
|
|
glMatrixMode(GL_MODELVIEW);
|
663 |
|
|
glPushMatrix();
|
664 |
|
|
|
665 |
|
|
static const double scale_x = 0.011;
|
666 |
|
|
static const double scale_y = 0.011;
|
667 |
5 |
howe.r.j.8 |
const vt100_t * const v = &t->vt100;
|
668 |
|
|
const scale_t scale = font_attributes();
|
669 |
|
|
const double now = world->tick - t->blink_count;
|
670 |
|
|
const double char_width = scale.x / X_MAX;
|
671 |
|
|
const double char_height = scale.y / Y_MAX;
|
672 |
|
|
const size_t cursor_x = v->cursor % v->width;
|
673 |
|
|
const size_t cursor_y = v->cursor / v->width;
|
674 |
3 |
howe.r.j.8 |
|
675 |
5 |
howe.r.j.8 |
if (now > seconds_to_ticks(world, 1.0)) {
|
676 |
3 |
howe.r.j.8 |
t->blink_on = !(t->blink_on);
|
677 |
|
|
t->blink_count = world->tick;
|
678 |
|
|
}
|
679 |
|
|
|
680 |
|
|
/**@note the cursor is deliberately in a different position compared to draw_vga(), due to how the VGA cursor behaves in hardware */
|
681 |
5 |
howe.r.j.8 |
if ((!(v->blinks) || t->blink_on) && v->cursor_on) /* fudge factor of 1.10? */
|
682 |
3 |
howe.r.j.8 |
draw_rectangle_filled(t->x + (char_width * 1.10 * (cursor_x)) , t->y - (char_height * cursor_y), char_width, char_height, WHITE);
|
683 |
|
|
|
684 |
|
|
|
685 |
5 |
howe.r.j.8 |
for (size_t i = 0; i < t->vt100.height; i++)
|
686 |
3 |
howe.r.j.8 |
draw_vt100_block(t->x, t->y - ((double)i * char_height), scale_x, scale_y, 0, v->m + (i*v->width), v->width, v->attributes + (i*v->width), t->blink_on);
|
687 |
|
|
draw_string_scaled(t->x, t->y - (v->height * char_height), scale_x, scale_y, 0, name, t->color);
|
688 |
|
|
|
689 |
|
|
/* fudge factor = 1/((1/scale_x)/X_MAX) ??? */
|
690 |
|
|
|
691 |
|
|
glPopMatrix();
|
692 |
|
|
|
693 |
|
|
draw_rectangle_line(t->x, t->y - (char_height * (v->height-1.0)), char_width * v->width * 1.10, char_height * v->height, LINE_WIDTH, t->color);
|
694 |
|
|
}
|
695 |
|
|
|
696 |
|
|
/* ====================================== Simulator Objects ==================================== */
|
697 |
|
|
|
698 |
|
|
/* ====================================== Simulator Instances ================================== */
|
699 |
|
|
|
700 |
|
|
#define SWITCHES_X (10.0)
|
701 |
|
|
#define SWITCHES_SPACING (0.6)
|
702 |
|
|
#define SWITCHES_Y (5.0)
|
703 |
|
|
#define SWITCHES_ANGLE (0.0)
|
704 |
|
|
#define SWITCHES_RADIUS (2.0)
|
705 |
|
|
#define SWITCHES_COUNT (8)
|
706 |
|
|
|
707 |
|
|
static switch_t switches[SWITCHES_COUNT] = {
|
708 |
|
|
{ .x = SWITCHES_X * SWITCHES_SPACING * 8.0, .y = SWITCHES_Y, .angle = SWITCHES_ANGLE, .radius = SWITCHES_RADIUS, .on = false },
|
709 |
|
|
{ .x = SWITCHES_X * SWITCHES_SPACING * 7.0, .y = SWITCHES_Y, .angle = SWITCHES_ANGLE, .radius = SWITCHES_RADIUS, .on = false },
|
710 |
|
|
{ .x = SWITCHES_X * SWITCHES_SPACING * 6.0, .y = SWITCHES_Y, .angle = SWITCHES_ANGLE, .radius = SWITCHES_RADIUS, .on = false },
|
711 |
|
|
{ .x = SWITCHES_X * SWITCHES_SPACING * 5.0, .y = SWITCHES_Y, .angle = SWITCHES_ANGLE, .radius = SWITCHES_RADIUS, .on = false },
|
712 |
|
|
{ .x = SWITCHES_X * SWITCHES_SPACING * 4.0, .y = SWITCHES_Y, .angle = SWITCHES_ANGLE, .radius = SWITCHES_RADIUS, .on = false },
|
713 |
|
|
{ .x = SWITCHES_X * SWITCHES_SPACING * 3.0, .y = SWITCHES_Y, .angle = SWITCHES_ANGLE, .radius = SWITCHES_RADIUS, .on = false },
|
714 |
|
|
{ .x = SWITCHES_X * SWITCHES_SPACING * 2.0, .y = SWITCHES_Y, .angle = SWITCHES_ANGLE, .radius = SWITCHES_RADIUS, .on = false },
|
715 |
|
|
{ .x = SWITCHES_X * SWITCHES_SPACING * 1.0, .y = SWITCHES_Y, .angle = SWITCHES_ANGLE, .radius = SWITCHES_RADIUS, .on = false },
|
716 |
|
|
};
|
717 |
|
|
|
718 |
|
|
#define LEDS_X (10.0)
|
719 |
|
|
#define LEDS_SPACING (0.6)
|
720 |
|
|
#define LEDS_Y (10.0)
|
721 |
|
|
#define LEDS_ANGLE (0.0)
|
722 |
|
|
#define LEDS_RADIUS (2.0)
|
723 |
|
|
#define LEDS_COUNT (8)
|
724 |
|
|
|
725 |
|
|
static led_t leds[LEDS_COUNT] = {
|
726 |
|
|
{ .x = LEDS_X * LEDS_SPACING * 8.0, .y = LEDS_Y, .angle = LEDS_ANGLE, .radius = LEDS_RADIUS, .on = false },
|
727 |
|
|
{ .x = LEDS_X * LEDS_SPACING * 7.0, .y = LEDS_Y, .angle = LEDS_ANGLE, .radius = LEDS_RADIUS, .on = false },
|
728 |
|
|
{ .x = LEDS_X * LEDS_SPACING * 6.0, .y = LEDS_Y, .angle = LEDS_ANGLE, .radius = LEDS_RADIUS, .on = false },
|
729 |
|
|
{ .x = LEDS_X * LEDS_SPACING * 5.0, .y = LEDS_Y, .angle = LEDS_ANGLE, .radius = LEDS_RADIUS, .on = false },
|
730 |
|
|
|
731 |
|
|
{ .x = LEDS_X * LEDS_SPACING * 4.0, .y = LEDS_Y, .angle = LEDS_ANGLE, .radius = LEDS_RADIUS, .on = false },
|
732 |
|
|
{ .x = LEDS_X * LEDS_SPACING * 3.0, .y = LEDS_Y, .angle = LEDS_ANGLE, .radius = LEDS_RADIUS, .on = false },
|
733 |
|
|
{ .x = LEDS_X * LEDS_SPACING * 2.0, .y = LEDS_Y, .angle = LEDS_ANGLE, .radius = LEDS_RADIUS, .on = false },
|
734 |
|
|
{ .x = LEDS_X * LEDS_SPACING * 1.0, .y = LEDS_Y, .angle = LEDS_ANGLE, .radius = LEDS_RADIUS, .on = false },
|
735 |
|
|
};
|
736 |
|
|
|
737 |
|
|
static dpad_t dpad = {
|
738 |
|
|
.x = X_MAX - 8.0,
|
739 |
|
|
.y = Y_MIN + 8.0,
|
740 |
|
|
.angle = 0.0,
|
741 |
|
|
.radius = 2.0,
|
742 |
|
|
.up = false,
|
743 |
|
|
.down = false,
|
744 |
|
|
.left = false,
|
745 |
|
|
.right = false,
|
746 |
|
|
.center = false,
|
747 |
|
|
};
|
748 |
|
|
|
749 |
|
|
#define VGA_TEXTURE_WIDTH (256)
|
750 |
|
|
#define VGA_TEXTURE_HEIGHT (256)
|
751 |
|
|
static uint8_t vga_background_image[VGA_TEXTURE_WIDTH*VGA_TEXTURE_HEIGHT*4];
|
752 |
|
|
|
753 |
|
|
static vt100_background_texture_t vga_background_texture = {
|
754 |
|
|
.width = VGA_TEXTURE_WIDTH,
|
755 |
|
|
.height = VGA_TEXTURE_HEIGHT,
|
756 |
|
|
.name = 0,
|
757 |
|
|
.image = (uint8_t*)vga_background_image
|
758 |
|
|
};
|
759 |
|
|
|
760 |
|
|
static terminal_t vga_terminal = {
|
761 |
|
|
.blink_count = 0,
|
762 |
|
|
.x = X_MIN + 2.0,
|
763 |
|
|
.y = Y_MAX - 8.0,
|
764 |
|
|
.color = GREEN, /* WHITE */
|
765 |
|
|
.blink_on = false,
|
766 |
|
|
|
767 |
|
|
.vt100 = {
|
768 |
|
|
.width = VGA_WIDTH,
|
769 |
|
|
.height = VGA_HEIGHT,
|
770 |
|
|
.size = VGA_WIDTH * VGA_HEIGHT,
|
771 |
|
|
.cursor = 0,
|
772 |
|
|
.cursor_saved = 0,
|
773 |
|
|
.state = TERMINAL_NORMAL_MODE,
|
774 |
|
|
.cursor_on = true,
|
775 |
|
|
.blinks = false,
|
776 |
|
|
.n1 = 1,
|
777 |
|
|
.n2 = 1,
|
778 |
|
|
.m = { 0 },
|
779 |
|
|
.attribute = { 0 },
|
780 |
|
|
.attributes = { { 0 } },
|
781 |
|
|
},
|
782 |
|
|
.texture = &vga_background_texture
|
783 |
|
|
};
|
784 |
|
|
|
785 |
|
|
#define SEGMENT_COUNT (4)
|
786 |
|
|
#define SEGMENT_SPACING (1.1)
|
787 |
|
|
#define SEGMENT_X (50)
|
788 |
|
|
#define SEGMENT_Y (3)
|
789 |
|
|
#define SEGMENT_WIDTH (6)
|
790 |
|
|
#define SEGMENT_HEIGHT (8)
|
791 |
|
|
|
792 |
|
|
static led_8_segment_t segments[SEGMENT_COUNT] = {
|
793 |
|
|
{ .x = SEGMENT_X + (SEGMENT_SPACING * SEGMENT_WIDTH * 1.0), .y = SEGMENT_Y, .width = SEGMENT_WIDTH, .height = SEGMENT_HEIGHT, .segment = 0 },
|
794 |
|
|
{ .x = SEGMENT_X + (SEGMENT_SPACING * SEGMENT_WIDTH * 2.0), .y = SEGMENT_Y, .width = SEGMENT_WIDTH, .height = SEGMENT_HEIGHT, .segment = 0 },
|
795 |
|
|
{ .x = SEGMENT_X + (SEGMENT_SPACING * SEGMENT_WIDTH * 3.0), .y = SEGMENT_Y, .width = SEGMENT_WIDTH, .height = SEGMENT_HEIGHT, .segment = 0 },
|
796 |
|
|
{ .x = SEGMENT_X + (SEGMENT_SPACING * SEGMENT_WIDTH * 4.0), .y = SEGMENT_Y, .width = SEGMENT_WIDTH, .height = SEGMENT_HEIGHT, .segment = 0 },
|
797 |
|
|
};
|
798 |
|
|
|
799 |
|
|
#define UART_TEXTURE_WIDTH (256)
|
800 |
|
|
#define UART_TEXTURE_HEIGHT (256)
|
801 |
|
|
static uint8_t uart_background_image[UART_TEXTURE_WIDTH*UART_TEXTURE_HEIGHT*4];
|
802 |
|
|
|
803 |
|
|
static vt100_background_texture_t uart_background_texture = {
|
804 |
|
|
.width = UART_TEXTURE_WIDTH,
|
805 |
|
|
.height = UART_TEXTURE_HEIGHT,
|
806 |
|
|
.name = 0,
|
807 |
|
|
.image = (uint8_t*)uart_background_image
|
808 |
|
|
};
|
809 |
|
|
|
810 |
|
|
static terminal_t uart_terminal = {
|
811 |
|
|
.blink_count = 0,
|
812 |
|
|
.x = X_MIN + 2.0,
|
813 |
|
|
.y = Y_MIN + 28.5,
|
814 |
|
|
.color = BLUE,
|
815 |
|
|
.blink_on = false,
|
816 |
|
|
|
817 |
|
|
.vt100 = {
|
818 |
|
|
.width = TERMINAL_WIDTH,
|
819 |
|
|
.height = TERMINAL_HEIGHT,
|
820 |
|
|
.size = TERMINAL_SIZE,
|
821 |
|
|
.cursor = 0,
|
822 |
|
|
.cursor_saved = 0,
|
823 |
|
|
.state = TERMINAL_NORMAL_MODE,
|
824 |
|
|
.cursor_on = true,
|
825 |
|
|
.n1 = 1,
|
826 |
|
|
.n2 = 1,
|
827 |
|
|
.blinks = false,
|
828 |
|
|
.m = { 0 },
|
829 |
|
|
.attribute = { 0 },
|
830 |
|
|
.attributes = { { 0 } },
|
831 |
|
|
},
|
832 |
|
|
.texture = &uart_background_texture
|
833 |
|
|
};
|
834 |
|
|
|
835 |
|
|
static h2_t *h = NULL;
|
836 |
|
|
static h2_io_t *h2_io = NULL;
|
837 |
|
|
static fifo_t *uart_rx_fifo = NULL;
|
838 |
|
|
static fifo_t *uart_tx_fifo = NULL;
|
839 |
|
|
static fifo_t *ps2_rx_fifo = NULL;
|
840 |
|
|
|
841 |
|
|
/* ====================================== Simulator Instances ================================== */
|
842 |
|
|
|
843 |
|
|
/* ====================================== H2 I/O Handling ====================================== */
|
844 |
|
|
|
845 |
5 |
howe.r.j.8 |
static uint16_t h2_io_get_gui(h2_soc_state_t * const soc, const uint16_t addr, bool *debug_on) {
|
846 |
3 |
howe.r.j.8 |
assert(soc);
|
847 |
|
|
assert(ps2_rx_fifo);
|
848 |
|
|
assert(uart_tx_fifo);
|
849 |
|
|
assert(uart_rx_fifo);
|
850 |
|
|
|
851 |
5 |
howe.r.j.8 |
if (debug_on)
|
852 |
3 |
howe.r.j.8 |
*debug_on = false;
|
853 |
5 |
howe.r.j.8 |
switch (addr) {
|
854 |
3 |
howe.r.j.8 |
case iUart:
|
855 |
5 |
howe.r.j.8 |
return (fifo_is_empty(uart_tx_fifo) << UART_TX_FIFO_EMPTY_BIT)
|
856 |
|
|
| (fifo_is_full(uart_tx_fifo) << UART_TX_FIFO_FULL_BIT)
|
857 |
|
|
| (fifo_is_empty(uart_rx_fifo) << UART_RX_FIFO_EMPTY_BIT)
|
858 |
|
|
| (fifo_is_full(uart_rx_fifo) << UART_RX_FIFO_FULL_BIT)
|
859 |
|
|
| soc->uart_getchar_register;
|
860 |
3 |
howe.r.j.8 |
case iVT100:
|
861 |
5 |
howe.r.j.8 |
return (1u << UART_TX_FIFO_EMPTY_BIT)
|
862 |
|
|
| (0u << UART_TX_FIFO_FULL_BIT)
|
863 |
|
|
| (fifo_is_empty(ps2_rx_fifo) << UART_RX_FIFO_EMPTY_BIT)
|
864 |
|
|
| (fifo_is_full(ps2_rx_fifo) << UART_RX_FIFO_FULL_BIT)
|
865 |
|
|
| soc->ps2_getchar_register;
|
866 |
3 |
howe.r.j.8 |
case iSwitches:
|
867 |
|
|
return soc->switches;
|
868 |
|
|
case iTimerDin: return soc->timer;
|
869 |
|
|
case iMemDin: return h2_io_memory_read_operation(soc);
|
870 |
|
|
default:
|
871 |
|
|
warning("invalid read from %04"PRIx16, addr);
|
872 |
|
|
break;
|
873 |
|
|
}
|
874 |
|
|
return 0;
|
875 |
|
|
}
|
876 |
|
|
|
877 |
5 |
howe.r.j.8 |
static void h2_io_set_gui(h2_soc_state_t *soc, const uint16_t addr, const uint16_t value, bool *debug_on) {
|
878 |
3 |
howe.r.j.8 |
assert(soc);
|
879 |
|
|
assert(uart_tx_fifo);
|
880 |
|
|
assert(uart_rx_fifo);
|
881 |
|
|
|
882 |
5 |
howe.r.j.8 |
if (debug_on)
|
883 |
3 |
howe.r.j.8 |
*debug_on = false;
|
884 |
|
|
|
885 |
5 |
howe.r.j.8 |
switch (addr) {
|
886 |
3 |
howe.r.j.8 |
case oUart:
|
887 |
5 |
howe.r.j.8 |
if (value & UART_TX_WE) {
|
888 |
3 |
howe.r.j.8 |
fifo_push(uart_tx_fifo, value);
|
889 |
|
|
}
|
890 |
5 |
howe.r.j.8 |
if (value & UART_RX_RE) {
|
891 |
3 |
howe.r.j.8 |
uint8_t c = 0;
|
892 |
|
|
fifo_pop(uart_rx_fifo, &c);
|
893 |
|
|
soc->uart_getchar_register = c;
|
894 |
|
|
}
|
895 |
|
|
break;
|
896 |
|
|
case oVT100:
|
897 |
5 |
howe.r.j.8 |
if (value & UART_TX_WE) {
|
898 |
3 |
howe.r.j.8 |
vt100_update(&vga_terminal.vt100, value & 0xff);
|
899 |
|
|
vt100_update(&soc->vt100, value & 0xff);
|
900 |
|
|
}
|
901 |
5 |
howe.r.j.8 |
if (value & UART_RX_RE) {
|
902 |
3 |
howe.r.j.8 |
uint8_t c = 0;
|
903 |
|
|
fifo_pop(ps2_rx_fifo, &c);
|
904 |
|
|
soc->ps2_getchar_register = c;
|
905 |
|
|
}
|
906 |
|
|
break;
|
907 |
|
|
case oLeds:
|
908 |
|
|
soc->leds = value;
|
909 |
5 |
howe.r.j.8 |
for (size_t i = 0; i < LEDS_COUNT; i++)
|
910 |
3 |
howe.r.j.8 |
leds[i].on = value & (1 << i);
|
911 |
|
|
break;
|
912 |
|
|
case oTimerCtrl:
|
913 |
|
|
soc->timer_control = value;
|
914 |
|
|
break;
|
915 |
|
|
case o7SegLED:
|
916 |
5 |
howe.r.j.8 |
for (size_t i = 0; i < SEGMENT_COUNT; i++)
|
917 |
3 |
howe.r.j.8 |
segments[i].segment = (value >> ((SEGMENT_COUNT - i - 1) * 4)) & 0xf;
|
918 |
|
|
soc->led_7_segments = value;
|
919 |
|
|
break;
|
920 |
|
|
case oIrcMask: soc->irc_mask = value; break;
|
921 |
|
|
case oMemControl:
|
922 |
|
|
{
|
923 |
|
|
soc->mem_control = value;
|
924 |
5 |
howe.r.j.8 |
const bool sram_cs = soc->mem_control & SRAM_CHIP_SELECT;
|
925 |
|
|
const bool oe = soc->mem_control & FLASH_MEMORY_OE;
|
926 |
|
|
const bool we = soc->mem_control & FLASH_MEMORY_WE;
|
927 |
|
|
if (sram_cs && !oe && we)
|
928 |
3 |
howe.r.j.8 |
soc->vram[(((uint32_t)(soc->mem_control & FLASH_MASK_ADDR_UPPER_MASK) << 16) | soc->mem_addr_low) >> 1] = soc->mem_dout;
|
929 |
|
|
break;
|
930 |
|
|
}
|
931 |
5 |
howe.r.j.8 |
case oMemAddrLow: soc->mem_addr_low = value; break;
|
932 |
|
|
case oMemDout: soc->mem_dout = value; break;
|
933 |
|
|
case oUartTxBaud: soc->uart_tx_baud = value; break;
|
934 |
|
|
case oUartRxBaud: soc->uart_rx_baud = value; break;
|
935 |
|
|
case oUartControl: soc->uart_control = value; break;
|
936 |
3 |
howe.r.j.8 |
default:
|
937 |
|
|
warning("invalid write to %04"PRIx16 ":%04"PRIx16, addr, value);
|
938 |
|
|
break;
|
939 |
|
|
}
|
940 |
|
|
}
|
941 |
|
|
|
942 |
|
|
/* ====================================== H2 I/O Handling ====================================== */
|
943 |
|
|
|
944 |
|
|
/* ====================================== Main Loop ============================================ */
|
945 |
|
|
|
946 |
5 |
howe.r.j.8 |
static double fps(void) {
|
947 |
3 |
howe.r.j.8 |
static unsigned frame = 0, timebase = 0;
|
948 |
|
|
static double fps = 0;
|
949 |
5 |
howe.r.j.8 |
const int time = glutGet(GLUT_ELAPSED_TIME);
|
950 |
3 |
howe.r.j.8 |
frame++;
|
951 |
5 |
howe.r.j.8 |
if (time - timebase > 1000) {
|
952 |
3 |
howe.r.j.8 |
fps = frame*1000.0/(time-timebase);
|
953 |
|
|
timebase = time;
|
954 |
|
|
frame = 0;
|
955 |
|
|
}
|
956 |
|
|
return fps;
|
957 |
|
|
}
|
958 |
|
|
|
959 |
5 |
howe.r.j.8 |
static void draw_debug_info(const world_t *world, double fps, double x, double y) {
|
960 |
3 |
howe.r.j.8 |
textbox_t t = { .x = x, .y = y, .draw_border = true, .color_text = WHITE, .color_box = WHITE };
|
961 |
|
|
assert(world);
|
962 |
|
|
fifo_t *f = world->use_uart_input ? uart_rx_fifo : ps2_rx_fifo;
|
963 |
|
|
const char *fifo_str = world->use_uart_input ? "UART" : "PS/2";
|
964 |
|
|
|
965 |
|
|
fill_textbox(&t, "tick: %u", world->tick);
|
966 |
|
|
//fill_textbox(&t, "seconds: %f", ticks_to_seconds(world->tick));
|
967 |
|
|
fill_textbox(&t, "fps: %f", fps);
|
968 |
|
|
|
969 |
5 |
howe.r.j.8 |
if (world->debug_extra) {
|
970 |
|
|
char buf[256] = { 0 };
|
971 |
3 |
howe.r.j.8 |
fill_textbox(&t, "Mode: %s", world->debug_mode ? "step" : "continue");
|
972 |
5 |
howe.r.j.8 |
fill_textbox(&t, "%s RX FIFO full: %s", fifo_str, fifo_is_full(f) ? "true" : "false");
|
973 |
|
|
fill_textbox(&t, "%s RX FIFO empty: %s", fifo_str, fifo_is_empty(f) ? "true" : "false");
|
974 |
|
|
fill_textbox(&t, "%s RX FIFO count: %u", fifo_str, (unsigned)fifo_count(f));
|
975 |
|
|
fill_textbox(&t, "UART TX FIFO full: %s", fifo_is_full(uart_tx_fifo) ? "true" : "false");
|
976 |
|
|
fill_textbox(&t, "UART TX FIFO empty: %s", fifo_is_empty(uart_tx_fifo) ? "true" : "false");
|
977 |
|
|
fill_textbox(&t, "UART TX FIFO count: %u", (unsigned)fifo_count(uart_tx_fifo));
|
978 |
3 |
howe.r.j.8 |
|
979 |
|
|
sprintf(buf, "%08lu", (unsigned long)(world->cycle_count));
|
980 |
|
|
fill_textbox(&t, "cycles: %s", buf);
|
981 |
5 |
howe.r.j.8 |
fill_textbox(&t, "cycles/tick: %u", (unsigned)(world->cycles));
|
982 |
3 |
howe.r.j.8 |
}
|
983 |
|
|
draw_textbox(&t);
|
984 |
|
|
}
|
985 |
|
|
|
986 |
5 |
howe.r.j.8 |
static void fill_textbox_memory(textbox_t *t, const uint16_t * const m, const size_t length) {
|
987 |
3 |
howe.r.j.8 |
assert(t);
|
988 |
|
|
assert(m);
|
989 |
|
|
assert((length % 4) == 0);
|
990 |
5 |
howe.r.j.8 |
for (size_t i = 0; i < length; i+=4)
|
991 |
3 |
howe.r.j.8 |
fill_textbox(t, "%s%u: %x %x %x %x", i < 10 ? " " : "", i, m[i], m[i+1], m[i+2], m[i+3]);
|
992 |
|
|
}
|
993 |
|
|
|
994 |
5 |
howe.r.j.8 |
static void draw_debug_h2_screen_1(h2_t *h, double x, double y) {
|
995 |
3 |
howe.r.j.8 |
assert(h);
|
996 |
|
|
textbox_t t = { .x = x, .y = y, .draw_border = true, .color_text = WHITE, .color_box = WHITE };
|
997 |
|
|
fill_textbox(&t, "H2 CPU State", h->tos);
|
998 |
|
|
fill_textbox(&t, "tp: %u", h->tos);
|
999 |
|
|
fill_textbox_memory(&t, h->dstk, STK_SIZE);
|
1000 |
|
|
fill_textbox(&t, "pc: %u", h->pc);
|
1001 |
|
|
fill_textbox(&t, "rp: %u (max %u)", h->rp, h->rpm);
|
1002 |
|
|
fill_textbox(&t, "dp: %u (max %u)", h->sp, h->spm);
|
1003 |
|
|
fill_textbox(&t, "ie: %s", h->ie ? "true" : "false");
|
1004 |
|
|
draw_textbox(&t);
|
1005 |
|
|
}
|
1006 |
|
|
|
1007 |
5 |
howe.r.j.8 |
static void draw_debug_h2_screen_2(const h2_t * const h, const double x, const double y) {
|
1008 |
3 |
howe.r.j.8 |
textbox_t t = { .x = x, .y = y, .draw_border = true, .color_text = WHITE, .color_box = WHITE };
|
1009 |
|
|
assert(h);
|
1010 |
|
|
fill_textbox(&t, "H2 CPU Return Stack");
|
1011 |
|
|
fill_textbox_memory(&t, h->rstk, STK_SIZE);
|
1012 |
|
|
draw_textbox(&t);
|
1013 |
|
|
}
|
1014 |
|
|
|
1015 |
5 |
howe.r.j.8 |
static void draw_debug_h2_screen_3(const h2_io_t * const io, const double x, const double y) {
|
1016 |
3 |
howe.r.j.8 |
textbox_t t = { .x = x, .y = y, .draw_border = true, .color_text = WHITE, .color_box = WHITE };
|
1017 |
|
|
assert(io);
|
1018 |
|
|
assert(io->soc);
|
1019 |
5 |
howe.r.j.8 |
const h2_soc_state_t * const s = io->soc;
|
1020 |
3 |
howe.r.j.8 |
fill_textbox(&t, "I/O");
|
1021 |
|
|
fill_textbox(&t, "LED %x", (unsigned)s->leds);
|
1022 |
|
|
/*fill_textbox(&t, "VGA Cursor: %x", (unsigned)s->vga_cursor);*/
|
1023 |
|
|
fill_textbox(&t, "Timer Control: %x", (unsigned)s->timer_control);
|
1024 |
|
|
fill_textbox(&t, "Timer Count: %x", (unsigned)s->timer);
|
1025 |
|
|
fill_textbox(&t, "IRQ Mask: %x", (unsigned)s->irc_mask);
|
1026 |
|
|
fill_textbox(&t, "LED 7 Segments: %x", (unsigned)s->led_7_segments);
|
1027 |
|
|
fill_textbox(&t, "Switches: %x", (unsigned)s->switches);
|
1028 |
|
|
fill_textbox(&t, "Memory Control: %x", (unsigned)s->mem_control);
|
1029 |
|
|
fill_textbox(&t, "Memory Address: %x", (unsigned)s->mem_addr_low);
|
1030 |
|
|
fill_textbox(&t, "Memory Output: %x", (unsigned)s->mem_dout);
|
1031 |
|
|
fill_textbox(&t, "Wait: %s", s->wait ? "yes" : "no");
|
1032 |
|
|
fill_textbox(&t, "Interrupt: %s", s->interrupt ? "yes" : "no");
|
1033 |
|
|
fill_textbox(&t, "IRQ Selector: %x", (unsigned)s->interrupt_selector);
|
1034 |
|
|
fill_textbox(&t, "");
|
1035 |
|
|
fill_textbox(&t, "Flash");
|
1036 |
|
|
fill_textbox(&t, "we: %s", s->flash.we ? "on" : "off");
|
1037 |
|
|
fill_textbox(&t, "cs: %s", s->flash.cs ? "on" : "off");
|
1038 |
|
|
fill_textbox(&t, "mode: %x", (unsigned)s->flash.mode);
|
1039 |
|
|
fill_textbox(&t, "status: %x", (unsigned)s->flash.status);
|
1040 |
|
|
fill_textbox(&t, "address arg 1: %x", (unsigned)s->flash.arg1_address);
|
1041 |
|
|
fill_textbox(&t, "data %x", (unsigned)s->flash.data);
|
1042 |
|
|
fill_textbox(&t, "cycle: %x", (unsigned)s->flash.cycle);
|
1043 |
5 |
howe.r.j.8 |
fill_textbox(&t, "UART Control");
|
1044 |
|
|
fill_textbox(&t, "UART TX Baud: %x", (unsigned)s->uart_tx_baud);
|
1045 |
|
|
fill_textbox(&t, "UART RX Baud: %x", (unsigned)s->uart_rx_baud);
|
1046 |
|
|
fill_textbox(&t, "UART Control: %x", (unsigned)s->uart_control);
|
1047 |
3 |
howe.r.j.8 |
draw_textbox(&t);
|
1048 |
|
|
}
|
1049 |
|
|
|
1050 |
5 |
howe.r.j.8 |
static void keyboard_handler(const unsigned char key, const int x, const int y) {
|
1051 |
3 |
howe.r.j.8 |
UNUSED(x);
|
1052 |
|
|
UNUSED(y);
|
1053 |
|
|
assert(uart_tx_fifo);
|
1054 |
|
|
assert(ps2_rx_fifo);
|
1055 |
5 |
howe.r.j.8 |
if (key == ESCAPE) {
|
1056 |
3 |
howe.r.j.8 |
world.halt_simulation = true;
|
1057 |
|
|
} else {
|
1058 |
5 |
howe.r.j.8 |
if (world.use_uart_input)
|
1059 |
3 |
howe.r.j.8 |
fifo_push(uart_rx_fifo, key);
|
1060 |
|
|
else
|
1061 |
|
|
fifo_push(ps2_rx_fifo, key);
|
1062 |
|
|
}
|
1063 |
|
|
}
|
1064 |
|
|
|
1065 |
5 |
howe.r.j.8 |
static void keyboard_special_handler(const int key, const int x, const int y) {
|
1066 |
3 |
howe.r.j.8 |
UNUSED(x);
|
1067 |
|
|
UNUSED(y);
|
1068 |
5 |
howe.r.j.8 |
switch (key) {
|
1069 |
3 |
howe.r.j.8 |
case GLUT_KEY_UP: dpad.up = true; break;
|
1070 |
|
|
case GLUT_KEY_LEFT: dpad.left = true; break;
|
1071 |
|
|
case GLUT_KEY_RIGHT: dpad.right = true; break;
|
1072 |
|
|
case GLUT_KEY_DOWN: dpad.down = true; break;
|
1073 |
|
|
case GLUT_KEY_F1: switches[7].on = !(switches[7].on); break;
|
1074 |
|
|
case GLUT_KEY_F2: switches[6].on = !(switches[6].on); break;
|
1075 |
|
|
case GLUT_KEY_F3: switches[5].on = !(switches[5].on); break;
|
1076 |
|
|
case GLUT_KEY_F4: switches[4].on = !(switches[4].on); break;
|
1077 |
|
|
case GLUT_KEY_F5: switches[3].on = !(switches[3].on); break;
|
1078 |
|
|
case GLUT_KEY_F6: switches[2].on = !(switches[2].on); break;
|
1079 |
|
|
case GLUT_KEY_F7: switches[1].on = !(switches[1].on); break;
|
1080 |
|
|
case GLUT_KEY_F8: switches[0].on = !(switches[0].on); break;
|
1081 |
|
|
case GLUT_KEY_F9: world.step = true;
|
1082 |
|
|
world.debug_mode = true;
|
1083 |
|
|
break;
|
1084 |
|
|
case GLUT_KEY_F10: world.debug_mode = !(world.debug_mode); break;
|
1085 |
|
|
case GLUT_KEY_F11: world.use_uart_input = !(world.use_uart_input); break;
|
1086 |
|
|
case GLUT_KEY_F12: world.debug_extra = !(world.debug_extra); break;
|
1087 |
|
|
default:
|
1088 |
|
|
break;
|
1089 |
|
|
}
|
1090 |
|
|
}
|
1091 |
|
|
|
1092 |
5 |
howe.r.j.8 |
static void keyboard_special_up_handler(const int key, const int x, const int y) {
|
1093 |
3 |
howe.r.j.8 |
UNUSED(x);
|
1094 |
|
|
UNUSED(y);
|
1095 |
5 |
howe.r.j.8 |
switch (key) {
|
1096 |
3 |
howe.r.j.8 |
case GLUT_KEY_UP: dpad.up = false; break;
|
1097 |
|
|
case GLUT_KEY_LEFT: dpad.left = false; break;
|
1098 |
|
|
case GLUT_KEY_RIGHT: dpad.right = false; break;
|
1099 |
|
|
case GLUT_KEY_DOWN: dpad.down = false; break;
|
1100 |
|
|
default:
|
1101 |
|
|
break;
|
1102 |
|
|
}
|
1103 |
|
|
}
|
1104 |
|
|
|
1105 |
|
|
typedef struct {
|
1106 |
|
|
double x;
|
1107 |
|
|
double y;
|
1108 |
|
|
} coordinate_t;
|
1109 |
|
|
|
1110 |
5 |
howe.r.j.8 |
static double abs_diff(const double a, const double b) {
|
1111 |
3 |
howe.r.j.8 |
return fabsl(fabsl(a) - fabsl(b));
|
1112 |
|
|
}
|
1113 |
|
|
|
1114 |
5 |
howe.r.j.8 |
static void resize_window(int w, int h) {
|
1115 |
3 |
howe.r.j.8 |
double window_x_min, window_x_max, window_y_min, window_y_max;
|
1116 |
|
|
double scale, center;
|
1117 |
|
|
world.window_width = w;
|
1118 |
|
|
world.window_height = h;
|
1119 |
|
|
|
1120 |
|
|
glViewport(0, 0, w, h);
|
1121 |
|
|
|
1122 |
|
|
w = (w == 0) ? 1 : w;
|
1123 |
|
|
h = (h == 0) ? 1 : h;
|
1124 |
|
|
if ((X_MAX - X_MIN) / w < (Y_MAX - Y_MIN) / h) {
|
1125 |
|
|
scale = ((Y_MAX - Y_MIN) / h) / ((X_MAX - X_MIN) / w);
|
1126 |
|
|
center = (X_MAX + X_MIN) / 2;
|
1127 |
|
|
window_x_min = center - (center - X_MIN) * scale;
|
1128 |
|
|
window_x_max = center + (X_MAX - center) * scale;
|
1129 |
|
|
world.window_scale_x = scale;
|
1130 |
|
|
window_y_min = Y_MIN;
|
1131 |
|
|
window_y_max = Y_MAX;
|
1132 |
|
|
} else {
|
1133 |
|
|
scale = ((X_MAX - X_MIN) / w) / ((Y_MAX - Y_MIN) / h);
|
1134 |
|
|
center = (Y_MAX + Y_MIN) / 2;
|
1135 |
|
|
window_y_min = center - (center - Y_MIN) * scale;
|
1136 |
|
|
window_y_max = center + (Y_MAX - center) * scale;
|
1137 |
|
|
world.window_scale_y = scale;
|
1138 |
|
|
window_x_min = X_MIN;
|
1139 |
|
|
window_x_max = X_MAX;
|
1140 |
|
|
}
|
1141 |
|
|
|
1142 |
|
|
glMatrixMode(GL_PROJECTION);
|
1143 |
|
|
glLoadIdentity();
|
1144 |
|
|
glOrtho(window_x_min, window_x_max, window_y_min, window_y_max, -1, 1);
|
1145 |
|
|
}
|
1146 |
|
|
|
1147 |
5 |
howe.r.j.8 |
static coordinate_t pixels_to_coordinates(const world_t *world, const int x, const int y) {
|
1148 |
3 |
howe.r.j.8 |
assert(world);
|
1149 |
5 |
howe.r.j.8 |
const double xd = abs_diff(X_MAX, X_MIN);
|
1150 |
|
|
const double yd = abs_diff(Y_MAX, Y_MIN);
|
1151 |
|
|
const double xs = world->window_width / world->window_scale_x;
|
1152 |
|
|
const double ys = world->window_height / world->window_scale_y;
|
1153 |
|
|
const coordinate_t c = {
|
1154 |
|
|
.x = Y_MIN + (xd * ((x - (world->window_width - xs)/2.) / xs)),
|
1155 |
|
|
.y = Y_MAX - (yd * ((y - (world->window_height - ys)/2.) / ys))
|
1156 |
|
|
};
|
1157 |
3 |
howe.r.j.8 |
return c;
|
1158 |
|
|
}
|
1159 |
|
|
|
1160 |
5 |
howe.r.j.8 |
static void mouse_handler(const int button, const int state, const int x, const int y) {
|
1161 |
|
|
const coordinate_t c = pixels_to_coordinates(&world, x, y);
|
1162 |
3 |
howe.r.j.8 |
|
1163 |
5 |
howe.r.j.8 |
for (size_t i = 0; i < SWITCHES_COUNT; i++) {
|
1164 |
|
|
if (detect_circle_circle_collision(c.x, c.y, 0.1, switches[i].x, switches[i].y, switches[i].radius)) {
|
1165 |
|
|
if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
|
1166 |
3 |
howe.r.j.8 |
switches[i].on = true;
|
1167 |
5 |
howe.r.j.8 |
if (button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN)
|
1168 |
3 |
howe.r.j.8 |
switches[i].on = false;
|
1169 |
|
|
return;
|
1170 |
|
|
}
|
1171 |
|
|
}
|
1172 |
|
|
|
1173 |
5 |
howe.r.j.8 |
if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
|
1174 |
|
|
switch (dpad_collision(&dpad, c.x, c.y, 0.1)) {
|
1175 |
3 |
howe.r.j.8 |
case DPAN_COL_NONE: break;
|
1176 |
|
|
case DPAN_COL_RIGHT: dpad.right = true; break;
|
1177 |
|
|
case DPAN_COL_LEFT: dpad.left = true; break;
|
1178 |
|
|
case DPAN_COL_DOWN: dpad.down = true; break;
|
1179 |
|
|
case DPAN_COL_UP: dpad.up = true; break;
|
1180 |
|
|
case DPAN_COL_CENTER: dpad.center = true; break;
|
1181 |
|
|
}
|
1182 |
5 |
howe.r.j.8 |
} else if (button == GLUT_LEFT_BUTTON && state == GLUT_UP) {
|
1183 |
3 |
howe.r.j.8 |
dpad.right = false;
|
1184 |
|
|
dpad.left = false;
|
1185 |
|
|
dpad.down = false;
|
1186 |
|
|
dpad.up = false;
|
1187 |
|
|
dpad.center = false;
|
1188 |
|
|
}
|
1189 |
|
|
}
|
1190 |
|
|
|
1191 |
5 |
howe.r.j.8 |
static void timer_callback(const int value) {
|
1192 |
3 |
howe.r.j.8 |
world.tick++;
|
1193 |
|
|
glutTimerFunc(world.arena_tick_ms, timer_callback, value);
|
1194 |
|
|
}
|
1195 |
|
|
|
1196 |
5 |
howe.r.j.8 |
static void update_switches(void) {
|
1197 |
3 |
howe.r.j.8 |
h2_io->soc->switches = 0;
|
1198 |
5 |
howe.r.j.8 |
for (size_t i = 0; i < SWITCHES_COUNT; i++)
|
1199 |
3 |
howe.r.j.8 |
h2_io->soc->switches |= switches[i].on << i;
|
1200 |
|
|
h2_io->soc->switches |= dpad.center << (SWITCHES_COUNT+0);
|
1201 |
|
|
h2_io->soc->switches |= dpad.right << (SWITCHES_COUNT+1);
|
1202 |
|
|
h2_io->soc->switches |= dpad.left << (SWITCHES_COUNT+2);
|
1203 |
|
|
h2_io->soc->switches |= dpad.down << (SWITCHES_COUNT+3);
|
1204 |
|
|
h2_io->soc->switches |= dpad.up << (SWITCHES_COUNT+4);
|
1205 |
|
|
}
|
1206 |
|
|
|
1207 |
5 |
howe.r.j.8 |
static void draw_scene(void) {
|
1208 |
|
|
static uint64_t next = 0; // @warning static!
|
1209 |
|
|
static uint64_t count = 0; // @warning static!
|
1210 |
3 |
howe.r.j.8 |
double f = fps();
|
1211 |
5 |
howe.r.j.8 |
if (world.halt_simulation)
|
1212 |
3 |
howe.r.j.8 |
exit(EXIT_SUCCESS);
|
1213 |
|
|
|
1214 |
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
1215 |
|
|
|
1216 |
|
|
draw_regular_polygon_line(X_MAX/2, Y_MAX/2, PI/4, sqrt(Y_MAX*Y_MAX/2)*0.99, SQUARE, LINE_WIDTH, WHITE);
|
1217 |
|
|
|
1218 |
|
|
update_switches();
|
1219 |
|
|
|
1220 |
5 |
howe.r.j.8 |
if (next != world.tick) {
|
1221 |
3 |
howe.r.j.8 |
unsigned long increment = 0;
|
1222 |
|
|
next = world.tick;
|
1223 |
|
|
count++;
|
1224 |
5 |
howe.r.j.8 |
for (;!fifo_is_empty(uart_tx_fifo);) {
|
1225 |
3 |
howe.r.j.8 |
uint8_t c = 0;
|
1226 |
|
|
fifo_pop(uart_tx_fifo, &c);
|
1227 |
|
|
vt100_update(&uart_terminal.vt100, c);
|
1228 |
|
|
}
|
1229 |
|
|
|
1230 |
5 |
howe.r.j.8 |
if (world.debug_mode && world.step)
|
1231 |
3 |
howe.r.j.8 |
increment = 1;
|
1232 |
5 |
howe.r.j.8 |
else if (!(world.debug_mode))
|
1233 |
3 |
howe.r.j.8 |
increment = world.cycles;
|
1234 |
|
|
|
1235 |
5 |
howe.r.j.8 |
if (!CYCLE_MODE_FIXED && increment) {
|
1236 |
3 |
howe.r.j.8 |
uint64_t n = world.cycles + (f > TARGET_FPS ? CYCLE_INCREMENT : -CYCLE_DECREMENT);
|
1237 |
5 |
howe.r.j.8 |
if (f > (TARGET_FPS + CYCLE_HYSTERESIS)) {
|
1238 |
3 |
howe.r.j.8 |
world.cycles = MIN(((uint64_t)-1), n);
|
1239 |
5 |
howe.r.j.8 |
} else if (f < (TARGET_FPS - CYCLE_HYSTERESIS)) {
|
1240 |
3 |
howe.r.j.8 |
world.cycles = MAX(CYCLE_MINIMUM, n);
|
1241 |
|
|
}
|
1242 |
|
|
}
|
1243 |
|
|
|
1244 |
5 |
howe.r.j.8 |
if (increment)
|
1245 |
|
|
if (h2_run(h, h2_io, stderr, increment, NULL, false, trace_file) < 0)
|
1246 |
3 |
howe.r.j.8 |
world.halt_simulation = true;
|
1247 |
|
|
|
1248 |
|
|
world.step = false;
|
1249 |
|
|
world.cycle_count += increment;
|
1250 |
|
|
}
|
1251 |
|
|
draw_debug_info(&world, f, X_MIN + X_MAX/40., Y_MAX - Y_MAX/40.);
|
1252 |
5 |
howe.r.j.8 |
if (world.debug_extra) {
|
1253 |
3 |
howe.r.j.8 |
draw_debug_h2_screen_1(h, X_MIN + X_MAX/40., Y_MAX*0.70);
|
1254 |
|
|
draw_debug_h2_screen_2(h, X_MAX / 3.0, Y_MAX*0.70);
|
1255 |
|
|
draw_debug_h2_screen_3(h2_io, X_MAX / 1.55, Y_MAX*0.70);
|
1256 |
|
|
} else {
|
1257 |
|
|
draw_terminal(&world, &vga_terminal, "VGA");
|
1258 |
|
|
}
|
1259 |
|
|
|
1260 |
5 |
howe.r.j.8 |
for (size_t i = 0; i < SWITCHES_COUNT; i++)
|
1261 |
3 |
howe.r.j.8 |
draw_switch(&switches[i]);
|
1262 |
|
|
|
1263 |
5 |
howe.r.j.8 |
for (size_t i = 0; i < LEDS_COUNT; i++)
|
1264 |
3 |
howe.r.j.8 |
draw_led(&leds[i]);
|
1265 |
|
|
|
1266 |
5 |
howe.r.j.8 |
for (size_t i = 0; i < SEGMENT_COUNT; i++)
|
1267 |
3 |
howe.r.j.8 |
draw_led_8_segment(&segments[i]);
|
1268 |
|
|
|
1269 |
|
|
draw_dpad(&dpad);
|
1270 |
|
|
|
1271 |
|
|
draw_terminal(&world, &uart_terminal, world.use_uart_input ? "UART RX / TX" : "PS/2 KBD RX / UART TX");
|
1272 |
|
|
|
1273 |
|
|
{
|
1274 |
|
|
textbox_t t = { .x = X_MAX-50, .y = Y_MAX-2, .draw_border = false, .color_text = WHITE, .color_box = WHITE };
|
1275 |
|
|
fill_textbox(&t, "EXIT/QUIT ESCAPE");
|
1276 |
|
|
fill_textbox(&t, "SWITCHES F-1...8");
|
1277 |
|
|
fill_textbox(&t, "SINGLE STEP F-9");
|
1278 |
|
|
}
|
1279 |
|
|
{
|
1280 |
|
|
textbox_t t = { .x = X_MAX-25, .y = Y_MAX-2, .draw_border = false, .color_text = WHITE, .color_box = WHITE };
|
1281 |
|
|
fill_textbox(&t, "CPU PAUSE/RESUME F-10");
|
1282 |
|
|
fill_textbox(&t, "SWITCH INPUT F-11");
|
1283 |
|
|
fill_textbox(&t, "CHANGE DISPLAY F-12");
|
1284 |
|
|
}
|
1285 |
|
|
|
1286 |
5 |
howe.r.j.8 |
if (!world.debug_extra)
|
1287 |
3 |
howe.r.j.8 |
draw_texture(&vga_terminal, !(count % 2));
|
1288 |
|
|
draw_texture(&uart_terminal, !(count % 2));
|
1289 |
|
|
|
1290 |
|
|
glFlush();
|
1291 |
|
|
glutSwapBuffers();
|
1292 |
|
|
glutPostRedisplay();
|
1293 |
|
|
}
|
1294 |
|
|
|
1295 |
5 |
howe.r.j.8 |
static void initialize_rendering(char *arg_0) {
|
1296 |
3 |
howe.r.j.8 |
char *glut_argv[] = { arg_0, NULL };
|
1297 |
|
|
int glut_argc = 0;
|
1298 |
|
|
memset(uart_terminal.vt100.m, ' ', uart_terminal.vt100.size);
|
1299 |
|
|
glutInit(&glut_argc, glut_argv);
|
1300 |
|
|
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH );
|
1301 |
|
|
glutInitWindowPosition(world.window_x_starting_position, world.window_y_starting_position);
|
1302 |
|
|
glutInitWindowSize(world.window_width, world.window_height);
|
1303 |
|
|
glutCreateWindow("H2 Simulator (GUI)");
|
1304 |
|
|
glShadeModel(GL_FLAT);
|
1305 |
|
|
glEnable(GL_DEPTH_TEST);
|
1306 |
|
|
glutKeyboardFunc(keyboard_handler);
|
1307 |
|
|
glutSpecialFunc(keyboard_special_handler);
|
1308 |
|
|
glutSpecialUpFunc(keyboard_special_up_handler);
|
1309 |
|
|
glutMouseFunc(mouse_handler);
|
1310 |
|
|
glutReshapeFunc(resize_window);
|
1311 |
|
|
glutDisplayFunc(draw_scene);
|
1312 |
|
|
glutTimerFunc(world.arena_tick_ms, timer_callback, 0);
|
1313 |
|
|
}
|
1314 |
|
|
|
1315 |
5 |
howe.r.j.8 |
static void vt100_initialize(vt100_t * const v) {
|
1316 |
3 |
howe.r.j.8 |
assert(v);
|
1317 |
|
|
memset(&v->attribute, 0, sizeof(v->attribute));
|
1318 |
|
|
v->attribute.foreground_color = WHITE;
|
1319 |
|
|
v->attribute.background_color = BLACK;
|
1320 |
5 |
howe.r.j.8 |
for (size_t i = 0; i < v->size; i++)
|
1321 |
3 |
howe.r.j.8 |
v->attributes[i] = v->attribute;
|
1322 |
|
|
}
|
1323 |
|
|
|
1324 |
5 |
howe.r.j.8 |
static void finalize(void) {
|
1325 |
|
|
nvram_save(h2_io, FLASH_INIT_FILE);
|
1326 |
3 |
howe.r.j.8 |
h2_free(h);
|
1327 |
|
|
h2_io_free(h2_io);
|
1328 |
|
|
fifo_free(uart_tx_fifo);
|
1329 |
|
|
fifo_free(uart_rx_fifo);
|
1330 |
|
|
fifo_free(ps2_rx_fifo);
|
1331 |
5 |
howe.r.j.8 |
if (trace_file)
|
1332 |
|
|
fclose(trace_file);
|
1333 |
3 |
howe.r.j.8 |
}
|
1334 |
|
|
|
1335 |
5 |
howe.r.j.8 |
int main(int argc, char **argv) {
|
1336 |
3 |
howe.r.j.8 |
FILE *hexfile = NULL;
|
1337 |
|
|
int r = 0;
|
1338 |
|
|
|
1339 |
|
|
assert(Y_MAX > 0. && Y_MIN < Y_MAX && Y_MIN >= 0.);
|
1340 |
|
|
assert(X_MAX > 0. && X_MIN < X_MAX && X_MIN >= 0.);
|
1341 |
|
|
|
1342 |
|
|
log_level = LOG_NOTE;
|
1343 |
|
|
|
1344 |
5 |
howe.r.j.8 |
if (argc != 2) {
|
1345 |
3 |
howe.r.j.8 |
fprintf(stderr, "usage %s h2.hex\n", argv[0]);
|
1346 |
|
|
return -1;
|
1347 |
|
|
}
|
1348 |
|
|
hexfile = fopen_or_die(argv[1], "rb");
|
1349 |
|
|
|
1350 |
|
|
h = h2_new(START_ADDR);
|
1351 |
|
|
r = h2_load(h, hexfile);
|
1352 |
|
|
fclose(hexfile);
|
1353 |
5 |
howe.r.j.8 |
if (r < 0) {
|
1354 |
3 |
howe.r.j.8 |
fprintf(stderr, "h2 load failed\n");
|
1355 |
|
|
goto fail;
|
1356 |
|
|
}
|
1357 |
|
|
h2_io = h2_io_new();
|
1358 |
|
|
h2_io->in = h2_io_get_gui;
|
1359 |
|
|
h2_io->out = h2_io_set_gui;
|
1360 |
|
|
|
1361 |
|
|
{ /* attempt to load initial contents of VGA memory */
|
1362 |
|
|
errno = 0;
|
1363 |
|
|
FILE *vga_init = fopen(VGA_INIT_FILE, "rb");
|
1364 |
|
|
static uint16_t vga_initial_contents[VGA_BUFFER_LENGTH] = { 0 };
|
1365 |
|
|
assert(VGA_BUFFER_LENGTH <= VT100_MAX_SIZE);
|
1366 |
5 |
howe.r.j.8 |
if (vga_init) {
|
1367 |
3 |
howe.r.j.8 |
memory_load(vga_init, vga_initial_contents, VGA_BUFFER_LENGTH);
|
1368 |
5 |
howe.r.j.8 |
for (size_t i = 0; i < VGA_BUFFER_LENGTH; i++) {
|
1369 |
3 |
howe.r.j.8 |
vga_terminal.vt100.m[i] = vga_initial_contents[i];
|
1370 |
|
|
h2_io->soc->vt100.m[i] = vga_initial_contents[i];
|
1371 |
|
|
}
|
1372 |
|
|
fclose(vga_init);
|
1373 |
|
|
} else {
|
1374 |
|
|
warning("could not load initial VGA memory file %s: %s", VGA_INIT_FILE, strerror(errno));
|
1375 |
|
|
}
|
1376 |
|
|
vt100_initialize(&vga_terminal.vt100);
|
1377 |
|
|
vt100_initialize(&uart_terminal.vt100);
|
1378 |
|
|
}
|
1379 |
|
|
|
1380 |
|
|
uart_rx_fifo = fifo_new(UART_FIFO_DEPTH);
|
1381 |
|
|
uart_tx_fifo = fifo_new(UART_FIFO_DEPTH * 100); /** @note x100 to speed things up */
|
1382 |
|
|
ps2_rx_fifo = fifo_new(8 /** @bug should be 1 - but this does not work, FIFO implementation needs correcting */);
|
1383 |
|
|
|
1384 |
5 |
howe.r.j.8 |
nvram_load_and_transfer(h2_io, FLASH_INIT_FILE, true);
|
1385 |
3 |
howe.r.j.8 |
|
1386 |
5 |
howe.r.j.8 |
if (TRON) {
|
1387 |
|
|
errno = 0;
|
1388 |
|
|
trace_file = fopen(TRACE_FILE, "wb");
|
1389 |
|
|
if (trace_file)
|
1390 |
|
|
setvbuf(trace_file, trace_buffer, _IOFBF, TRACE_BUFFER_LEN);
|
1391 |
|
|
else
|
1392 |
|
|
warning("could not open %s for writing: %s", TRACE_FILE, strerror(errno));
|
1393 |
|
|
}
|
1394 |
|
|
|
1395 |
3 |
howe.r.j.8 |
atexit(finalize);
|
1396 |
|
|
initialize_rendering(argv[0]);
|
1397 |
|
|
glutMainLoop();
|
1398 |
|
|
|
1399 |
|
|
return 0;
|
1400 |
|
|
fail:
|
1401 |
|
|
h2_free(h);
|
1402 |
|
|
return -1;
|
1403 |
|
|
}
|
1404 |
|
|
|
1405 |
|
|
|