OpenCores
URL https://opencores.org/ocsvn/forth-cpu/forth-cpu/trunk

Subversion Repositories forth-cpu

[/] [forth-cpu/] [trunk/] [gui.c] - Blame information for rev 3

Go to most recent revision | Details | Compare with Previous | View Log

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

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.