forth-cpu

Форк
0
/
gui.c 
1403 строки · 44.7 Кб
1
/**@file      gui.c
2
 * @brief     GUI Simulator for the H2 SoC
3
 * @copyright Richard James Howe (2017-2019)
4
 * @license   MIT
5
 * @note It would have been better to make this in SDL, se
6
 * <https://www.libsdl.org/>.  */
7

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
#define TRON (0)
25

26
/* ====================================== 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
#define TRACE_FILE       ("trace.csv")
47
#define TRACE_BUFFER_LEN (16*4096)
48

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
	void *font_scaled;
57
	uint64_t cycle_count;
58
	uint64_t cycles;
59
	volatile unsigned tick;
60
	unsigned arena_tick_ms;
61
	volatile bool halt_simulation;
62
	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
static FILE *trace_file = NULL; /* NB. !NULL turns tracing on */
88
static char trace_buffer[TRACE_BUFFER_LEN];
89

90
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
static double seconds_to_ticks(const world_t *world, double s) {
124
	assert(world);
125
	return s * (1000. / (double)world->arena_tick_ms);
126
}
127

128
static double rad2deg(const double rad) {
129
	return (rad / (2.0 * PI)) * 360.0;
130
}
131

132
static void set_color(const color_t color, const bool light) {
133
	double ON = light ? 0.8 : 0.4;
134
	static const double OFF = 0.0;
135
	switch (color) {      /* RED  GRN  BLU */
136
	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
		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
	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
		if (lines) {
162
			glLineWidth(thickness);
163
			glBegin(GL_LINE_LOOP);
164
		} else {
165
			glBegin(GL_POLYGON);
166
		}
167
			for (double i = 0; i < 2.0 * PI; i += PI / sides)
168
				glVertex3d(cos(i) * radius, sin(i) * radius, 0.0);
169
		glEnd();
170
	glPopMatrix();
171
}
172

173
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
	glMatrixMode(GL_MODELVIEW);
179
	glPushMatrix();
180
		glLoadIdentity();
181
		glRasterPos2d(x, y);
182
		set_color(color, true);
183
		if (lines) {
184
			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
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
}
200

201
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
}
204

205
static double shape_to_sides(shape_t shape) {
206
	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
	if (shape >= INVALID_SHAPE)
218
		fatal("invalid shape '%d'", shape);
219
	return sides[shape % INVALID_SHAPE];
220
}
221

222
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
}
225

226
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
}
229

230
static void draw_char(uint8_t c) {
231
	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
static int draw_block(const uint8_t *msg, const size_t len) {
239
	assert(msg);
240
	for (size_t i = 0; i < len; i++)
241
		draw_char(msg[i]);
242
	return len;
243
}
244

245
static int draw_string(const char *msg) {
246
	assert(msg);
247
	return draw_block((uint8_t*)msg, strlen(msg));
248
}
249

250
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
		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
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
	/*scale_t scale = font_attributes();
268
	double char_width  = scale.x / X_MAX;
269
       	double char_height = scale.y / Y_MAX;*/
270

271
	if (blink && attr->blink)
272
		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
	if (BACKGROUND_ON)
285
		draw_rectangle_filled(x, y, 1.20, 1.55, attr->background_color);
286
}
287

288
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
		draw_vt100_char(x+char_width*i, y, scale_x, scale_y, orientation, msg[i], &attr[i], blink);
298
	return len;
299
}
300

301
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
	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
		for (size_t i = 0; i < len; i++) {
314
			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
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
	assert(msg);
328
	return draw_block_scaled(x, y, scale_x, scale_y, orientation, (uint8_t*)msg, strlen(msg), color);
329
}
330

331
static int vdraw_text(const color_t color, const double x, const double y, const char *fmt, va_list ap) {
332
	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
	while (*fmt) {
343
		char f = *fmt++;
344
		if ('%' != f) {
345
			glutStrokeCharacter(world.font_scaled, f);
346
			r++;
347
			continue;
348
		}
349
		switch ((f = *fmt++)) {
350
		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
			const unsigned d = va_arg(ap, unsigned);
366
			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
			const int d = va_arg(ap, int);
375
			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
			const double f = va_arg(ap, double);
383
			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
static void fill_textbox(textbox_t *t, const char *fmt, ...) {
399
	va_list ap;
400
	assert(t);
401
	assert(fmt);
402

403
	scale_t scale = font_attributes();
404
	const double char_width = scale.x / X_MAX;
405
	const double char_height = scale.y / Y_MAX;
406
	assert(t && fmt);
407
	va_start(ap, fmt);
408
	double r = vdraw_text(t->color_text, t->x, t->y - t->height, fmt, ap);
409
	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
static void draw_textbox(textbox_t *t) {
417
	assert(t);
418
	scale_t scale = font_attributes();
419
	const double char_height = scale.y / Y_MAX;
420
	if (!(t->draw_border))
421
		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
		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
}
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
static void draw_led(const led_t * const l) {
447
	assert(l);
448
	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
	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
static void draw_switch(const switch_t * const s) {
463
	assert(s);
464
	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
	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
static uint8_t convert_to_segments(const uint8_t segment) {
492
	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
static void draw_led_8_segment(const led_8_segment_t * const l) {
502
	assert(l);
503
	const uint8_t sgs = convert_to_segments(l->segment);
504

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
static void draw_dpad(const dpad_t * const d) {
534
	assert(d);
535
	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
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
		return DPAN_COL_RIGHT;
557
	if (detect_circle_circle_collision(x, y, radius, d->x - (d->radius*2.0), d->y,                   d->radius))
558
		return DPAN_COL_LEFT;
559
	if (detect_circle_circle_collision(x, y, radius, d->x,                   d->y + (d->radius*2.0), d->radius))
560
		return DPAN_COL_UP;
561
	if (detect_circle_circle_collision(x, y, radius, d->x,                   d->y - (d->radius*2.0), d->radius))
562
		return DPAN_COL_DOWN;
563
	if (detect_circle_circle_collision(x, y, radius, d->x,                   d->y,                   d->radius))
564
		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
static void texture_background(const terminal_t * const t) {
590
	assert(t);
591
	const vt100_background_texture_t *v = t->texture;
592
	const vt100_t *vt = &t->vt100;
593
	uint8_t *const img = v->image;
594
	const unsigned h = v->height;
595
	const unsigned w = v->width;
596

597
	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
			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
static void draw_texture(const terminal_t * const t, const bool update) {
614
	assert(t);
615
	vt100_background_texture_t *v = t->texture;
616
	if (!v)
617
		return;
618

619
	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

627
	glEnable(GL_TEXTURE_2D);
628
	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
629

630
	if (update) {
631
		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
void draw_terminal(const world_t *world, terminal_t *t, const char * const name) {
660
	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
	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

675
	if (now > seconds_to_ticks(world, 1.0)) {
676
		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
	if ((!(v->blinks) || t->blink_on) && v->cursor_on) /* fudge factor of 1.10? */
682
		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
	for (size_t i = 0; i < t->vt100.height; i++)
686
		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
static uint16_t h2_io_get_gui(h2_soc_state_t * const soc, const uint16_t addr, bool *debug_on) {
846
	assert(soc);
847
	assert(ps2_rx_fifo);
848
	assert(uart_tx_fifo);
849
	assert(uart_rx_fifo);
850

851
	if (debug_on)
852
		*debug_on = false;
853
	switch (addr) {
854
	case iUart:
855
		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
	case iVT100:
861
		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
	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
static void h2_io_set_gui(h2_soc_state_t *soc, const uint16_t addr, const uint16_t value, bool *debug_on) {
878
	assert(soc);
879
	assert(uart_tx_fifo);
880
	assert(uart_rx_fifo);
881

882
	if (debug_on)
883
		*debug_on = false;
884

885
	switch (addr) {
886
	case oUart:
887
		if (value & UART_TX_WE) {
888
			fifo_push(uart_tx_fifo, value);
889
		}
890
		if (value & UART_RX_RE) {
891
			uint8_t c = 0;
892
			fifo_pop(uart_rx_fifo, &c);
893
			soc->uart_getchar_register = c;
894
		}
895
		break;
896
	case oVT100:
897
		if (value & UART_TX_WE) {
898
			vt100_update(&vga_terminal.vt100, value & 0xff);
899
			vt100_update(&soc->vt100, value & 0xff);
900
		}
901
		if (value & UART_RX_RE) {
902
			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
		for (size_t i = 0; i < LEDS_COUNT; i++)
910
			leds[i].on = value & (1 << i);
911
		break;
912
	case oTimerCtrl:
913
		soc->timer_control  = value;
914
		break;
915
	case o7SegLED:
916
		for (size_t i = 0; i < SEGMENT_COUNT; i++)
917
			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
			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
				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
	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
	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
static double fps(void) {
947
	static unsigned frame = 0, timebase = 0;
948
	static double fps = 0;
949
	const int time = glutGet(GLUT_ELAPSED_TIME);
950
	frame++;
951
	if (time - timebase > 1000) {
952
		fps = frame*1000.0/(time-timebase);
953
		timebase = time;
954
		frame = 0;
955
	}
956
	return fps;
957
}
958

959
static void draw_debug_info(const world_t *world, double fps, double x, double y) {
960
	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
	if (world->debug_extra) {
970
		char buf[256] = { 0 };
971
		fill_textbox(&t, "Mode:               %s", world->debug_mode ? "step" : "continue");
972
		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

979
		sprintf(buf, "%08lu", (unsigned long)(world->cycle_count));
980
		fill_textbox(&t, "cycles:             %s", buf);
981
		fill_textbox(&t, "cycles/tick:        %u", (unsigned)(world->cycles));
982
	}
983
	draw_textbox(&t);
984
}
985

986
static void fill_textbox_memory(textbox_t *t, const uint16_t * const m, const size_t length) {
987
	assert(t);
988
	assert(m);
989
	assert((length % 4) == 0);
990
	for (size_t i = 0; i < length; i+=4)
991
		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
static void draw_debug_h2_screen_1(h2_t *h, double x, double y) {
995
	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
static void draw_debug_h2_screen_2(const h2_t * const h, const double x, const double y) {
1008
	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
static void draw_debug_h2_screen_3(const h2_io_t * const io, const double x, const double y) {
1016
	textbox_t t = { .x = x, .y = y, .draw_border = true, .color_text = WHITE, .color_box = WHITE };
1017
	assert(io);
1018
	assert(io->soc);
1019
	const h2_soc_state_t * const s = io->soc;
1020
	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
	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
	draw_textbox(&t);
1048
}
1049

1050
static void keyboard_handler(const unsigned char key, const int x, const int y) {
1051
	UNUSED(x);
1052
	UNUSED(y);
1053
	assert(uart_tx_fifo);
1054
	assert(ps2_rx_fifo);
1055
	if (key == ESCAPE) {
1056
		world.halt_simulation = true;
1057
	} else {
1058
		if (world.use_uart_input)
1059
			fifo_push(uart_rx_fifo, key);
1060
		else
1061
			fifo_push(ps2_rx_fifo, key);
1062
	}
1063
}
1064

1065
static void keyboard_special_handler(const int key, const int x, const int y) {
1066
	UNUSED(x);
1067
	UNUSED(y);
1068
	switch (key) {
1069
	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
static void keyboard_special_up_handler(const int key, const int x, const int y) {
1093
	UNUSED(x);
1094
	UNUSED(y);
1095
	switch (key) {
1096
	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
static double abs_diff(const double a, const double b) {
1111
	return fabsl(fabsl(a) - fabsl(b));
1112
}
1113

1114
static void resize_window(int w, int h) {
1115
	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
static coordinate_t pixels_to_coordinates(const world_t *world, const int x, const int y) {
1148
	assert(world);
1149
	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
	return c;
1158
}
1159

1160
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

1163
	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
				switches[i].on = true;
1167
			if (button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN)
1168
				switches[i].on = false;
1169
			return;
1170
		}
1171
	}
1172

1173
	if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
1174
		switch (dpad_collision(&dpad, c.x, c.y, 0.1)) {
1175
		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
	} else if (button == GLUT_LEFT_BUTTON && state == GLUT_UP) {
1183
		dpad.right  = false;
1184
		dpad.left   = false;
1185
		dpad.down   = false;
1186
		dpad.up     = false;
1187
		dpad.center = false;
1188
	}
1189
}
1190

1191
static void timer_callback(const int value) {
1192
	world.tick++;
1193
	glutTimerFunc(world.arena_tick_ms, timer_callback, value);
1194
}
1195

1196
static void update_switches(void) {
1197
	h2_io->soc->switches = 0;
1198
	for (size_t i = 0; i < SWITCHES_COUNT; i++)
1199
		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
static void draw_scene(void) {
1208
	static uint64_t next = 0;  // @warning static!
1209
	static uint64_t count = 0; // @warning static!
1210
	double f = fps();
1211
	if (world.halt_simulation)
1212
		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
	if (next != world.tick) {
1221
		unsigned long increment = 0;
1222
		next = world.tick;
1223
		count++;
1224
		for (;!fifo_is_empty(uart_tx_fifo);) {
1225
			uint8_t c = 0;
1226
			fifo_pop(uart_tx_fifo, &c);
1227
			vt100_update(&uart_terminal.vt100, c);
1228
		}
1229

1230
		if (world.debug_mode && world.step)
1231
			increment = 1;
1232
		else if (!(world.debug_mode))
1233
			increment = world.cycles;
1234

1235
		if (!CYCLE_MODE_FIXED && increment) {
1236
			uint64_t n = world.cycles + (f > TARGET_FPS ? CYCLE_INCREMENT : -CYCLE_DECREMENT);
1237
			if (f > (TARGET_FPS + CYCLE_HYSTERESIS)) {
1238
				world.cycles = MIN(((uint64_t)-1), n);
1239
			} else if (f < (TARGET_FPS - CYCLE_HYSTERESIS)) {
1240
				world.cycles = MAX(CYCLE_MINIMUM, n);
1241
			}
1242
		}
1243

1244
		if (increment)
1245
			if (h2_run(h, h2_io, stderr, increment, NULL, false, trace_file) < 0)
1246
				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
	if (world.debug_extra) {
1253
		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
	for (size_t i = 0; i < SWITCHES_COUNT; i++)
1261
		draw_switch(&switches[i]);
1262

1263
	for (size_t i = 0; i < LEDS_COUNT; i++)
1264
		draw_led(&leds[i]);
1265

1266
	for (size_t i = 0; i < SEGMENT_COUNT; i++)
1267
		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
	if (!world.debug_extra)
1287
		draw_texture(&vga_terminal,  !(count % 2));
1288
	draw_texture(&uart_terminal, !(count % 2));
1289

1290
	glFlush();
1291
	glutSwapBuffers();
1292
	glutPostRedisplay();
1293
}
1294

1295
static void initialize_rendering(char *arg_0) {
1296
	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
static void vt100_initialize(vt100_t * const v) {
1316
	assert(v);
1317
	memset(&v->attribute, 0, sizeof(v->attribute));
1318
	v->attribute.foreground_color = WHITE;
1319
	v->attribute.background_color = BLACK;
1320
	for (size_t i = 0; i < v->size; i++)
1321
		v->attributes[i] = v->attribute;
1322
}
1323

1324
static void finalize(void) {
1325
	nvram_save(h2_io, FLASH_INIT_FILE);
1326
	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
	if (trace_file)
1332
		fclose(trace_file);
1333
}
1334

1335
int main(int argc, char **argv) {
1336
	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
	if (argc != 2) {
1345
		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
	if (r < 0) {
1354
		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
		if (vga_init) {
1367
			memory_load(vga_init, vga_initial_contents, VGA_BUFFER_LENGTH);
1368
			for (size_t i = 0; i < VGA_BUFFER_LENGTH; i++) {
1369
				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
	nvram_load_and_transfer(h2_io, FLASH_INIT_FILE, true);
1385

1386
	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
	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

1406

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.