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