summaryrefslogtreecommitdiffstats
path: root/tis100-curses.c
diff options
context:
space:
mode:
authorLouis Burda <quent.burda@gmail.com>2023-07-26 17:59:43 +0200
committerLouis Burda <quent.burda@gmail.com>2023-07-26 17:59:43 +0200
commited666b4fd98ad168f735b814cf74e77ef5c52794 (patch)
tree1a9e0f4920060f193d5add588503253a2635a19a /tis100-curses.c
parentce2b644f251d5b22dc2228a0ffc8479408c49c38 (diff)
downloadtis100-ed666b4fd98ad168f735b814cf74e77ef5c52794.tar.gz
tis100-ed666b4fd98ad168f735b814cf74e77ef5c52794.zip
Fork tis256 to create version like game
Diffstat (limited to 'tis100-curses.c')
-rw-r--r--tis100-curses.c565
1 files changed, 565 insertions, 0 deletions
diff --git a/tis100-curses.c b/tis100-curses.c
new file mode 100644
index 0000000..157b102
--- /dev/null
+++ b/tis100-curses.c
@@ -0,0 +1,565 @@
+#define NCURSES_WIDECHAR 1
+
+#include "tpu.h"
+#include "util.h"
+#include "asm.h"
+
+#include <curses.h>
+#include <sys/inotify.h>
+#include <unistd.h>
+#include <locale.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#define KEY_ESC 0x1b
+#define KEY_CTRL(c) ((c) & ~0x60)
+
+#define TPU_INPUT_ROWS 14
+#define TPU_INPUT_COLS 20
+#define TPU_INFO_W 6
+#define TPU_INFO_H 4
+#define TPU_CNT 6
+#define TPU_W (TPU_INPUT_COLS + 2 + 6)
+#define TPU_H (TPU_INPUT_ROWS + 2)
+
+#define TIMEOUT 50
+
+enum input_mode {
+ MAIN,
+ TPU_NAV
+};
+
+enum {
+ NONE, MIN, MAX, MID
+};
+
+enum {
+ COLOR_HEADING,
+ COLOR_VAL
+};
+
+static const char *mode_repr[] = {
+ "IDL", "RUN", "REA", "WRI"
+};
+
+static int scrx = 0;
+static int scry = 0;
+static int scrw = 80;
+static int scrh = 40;
+
+static struct tis tis;
+static FILE *tis_stdin = NULL;
+static FILE *tis_stdout = NULL;
+
+static int show_reloaded = 0;
+
+static enum input_mode input_mode = MAIN;
+
+static struct tpu *tpu_sel = NULL;
+
+int (*cleanup)(void) = endwin;
+const char *progname = "tis-curses";
+
+static enum tpu_port_dir
+key_to_dir(int key)
+{
+ switch (key) {
+ case KEY_LEFT:
+ return DIR_LEFT;
+ case KEY_RIGHT:
+ return DIR_RIGHT;
+ case KEY_UP:
+ return DIR_UP;
+ case KEY_DOWN:
+ return DIR_DOWN;
+ default:
+ abort();
+ }
+}
+
+static const cchar_t *
+dir_to_arrow(enum tpu_port_dir dir)
+{
+ switch (dir) {
+ case DIR_UP:
+ return WACS_UARROW;
+ case DIR_DOWN:
+ return WACS_DARROW;
+ case DIR_LEFT:
+ return WACS_LARROW;
+ case DIR_RIGHT:
+ return WACS_RARROW;
+ }
+}
+
+static int
+tpu_pos_x(struct tpu *tpu)
+{
+ return 2 + (int) tpu->x * (TPU_W + 4);
+}
+
+static int
+tpu_pos_y(struct tpu *tpu)
+{
+ return 2 + (int) tpu->y * (TPU_H + 2);
+}
+
+static void
+tui_draw_box(int sx, int sy, int w, int h, attr_t attr,
+ const cchar_t *ul, const cchar_t *ur,
+ const cchar_t *ll, const cchar_t *lr)
+{
+ int x, y;
+
+ if (sx + w < scrx || sx >= scrx + scrw) return;
+ if (sy + h < scry || sy >= scry + scrh) return;
+
+ sx -= scrx;
+ sy -= scry;
+
+ attron(attr);
+ mvadd_wch(sy, sx, ul);
+ mvadd_wch(sy, sx + w - 1, ur);
+ mvadd_wch(sy + h - 1, sx, ll);
+ mvadd_wch(sy + h - 1, sx + w - 1, lr);
+ for (x = sx + 1; x < sx + w - 1; x++)
+ mvadd_wch(sy, x, WACS_D_HLINE);
+ for (x = sx + 1; x < sx + w - 1; x++)
+ mvadd_wch(sy + h - 1, x, WACS_D_HLINE);
+ for (y = sy + 1; y < sy + h - 1; y++)
+ mvadd_wch(y, sx, WACS_D_VLINE);
+ for (y = sy + 1; y < sy + h - 1; y++)
+ mvadd_wch(y, sx + w - 1, WACS_D_VLINE);
+ attroff(attr);
+}
+
+static void
+__attribute__((format(printf, 4, 5)))
+tui_draw_text(int x, int y, attr_t attr, const char *fmt, ...)
+{
+ char buf[256];
+ va_list ap;
+ int i;
+
+ va_start(ap, fmt);
+ vsnprintf(buf, 256, fmt, ap);
+ va_end(ap);
+
+ attron(attr);
+ for (i = 0; i < strlen(buf) && x + i < scrx + scrw; i++)
+ mvaddch(y - scry, x + i - scrx, (chtype) buf[i]);
+ attroff(attr);
+}
+
+static void
+tui_draw_wch(int x, int y, attr_t attr, const cchar_t *c)
+{
+ attron(attr);
+ mvadd_wch(y - scry, x - scrx, c);
+ attroff(attr);
+}
+
+static void
+tui_draw_tpu(struct tpu *tpu)
+{
+ char linebuf[TPU_INPUT_COLS + 1];
+ struct tpu_port *port;
+ int sx, sy, x, y, w, h;
+ int off, start, inst;
+ size_t len;
+ attr_t attr;
+ int idle;
+
+ attr = (tpu_sel == tpu && input_mode == TPU_NAV) ? A_BOLD : 0;
+
+ sx = tpu_pos_x(tpu);
+ sy = tpu_pos_y(tpu);
+ tui_draw_box(sx, sy, TPU_W, TPU_H, attr,
+ WACS_D_ULCORNER, WACS_D_URCORNER,
+ WACS_D_LLCORNER, WACS_D_LRCORNER);
+
+ if (tpu->inst_cnt > 0) {
+ start = MAX(0, MIN(tpu->pc - 4, (int) tpu->inst_cnt - TPU_INPUT_ROWS));
+ inst = start;
+ for (off = 0; off < TPU_INPUT_ROWS && inst < tpu->inst_cnt; ) {
+ if (tpu->label_map.labels[inst]) {
+ len = strlen(tpu->label_map.labels[inst]);
+ if (len > TPU_INPUT_COLS - 1) {
+ tui_draw_text(sx + 2, sy + 1 + off, A_DIM,
+ "%.*s..:", TPU_INPUT_COLS - 3,
+ tpu->label_map.labels[inst]);
+ } else {
+ tui_draw_text(sx + 2, sy + 1 + off, A_DIM,
+ "%s:", tpu->label_map.labels[inst]);
+ }
+ off++;
+ }
+ asm_print_inst(linebuf, sizeof(linebuf), &tpu->insts[inst]);
+ tui_draw_text(sx + 2, sy + 1 + off,
+ inst == tpu->pc ? A_STANDOUT : 0, "%-*.*s",
+ TPU_INPUT_COLS, TPU_INPUT_COLS, linebuf);
+ inst++; off++;
+ }
+ }
+
+ x = sx + TPU_W - TPU_INFO_W;
+ y = sy;
+ w = TPU_INFO_W;
+ h = TPU_INFO_H;
+ tui_draw_box(x, y, w, h, attr,
+ WACS_D_TTEE, WACS_D_URCORNER, WACS_D_LTEE, WACS_D_RTEE);
+ tui_draw_text(x + 2, y + 1, A_BOLD, "ACC");
+ tui_draw_text(x + 2, y + 2, 0, "%03i", tpu->acc);
+
+ tui_draw_box(x, (y += TPU_INFO_H - 1), w, h, attr,
+ WACS_D_LTEE, WACS_D_RTEE, WACS_D_LTEE, WACS_D_RTEE);
+ tui_draw_text(x + 2, y + 1, A_BOLD, "BAK");
+ tui_draw_text(x + 2, y + 2, 0, "%03i", tpu->bak);
+
+ tui_draw_box(x, (y += TPU_INFO_H - 1), w, h, attr,
+ WACS_D_LTEE, WACS_D_RTEE, WACS_D_LTEE, WACS_D_RTEE);
+ tui_draw_text(x + 2, y + 1, A_BOLD, "LST");
+ if (tpu->last < 0) {
+ tui_draw_text(x + 2, y + 2, 0, "N/A");
+ } else {
+ tui_draw_wch(x + 2, y + 2, 0,
+ dir_to_arrow((enum tpu_port_dir) tpu->last));
+ tui_draw_wch(x + 3, y + 2, 0,
+ dir_to_arrow((enum tpu_port_dir) tpu->last));
+ tui_draw_wch(x + 4, y + 2, 0,
+ dir_to_arrow((enum tpu_port_dir) tpu->last));
+ }
+
+ tui_draw_box(x, (y += TPU_INFO_H - 1), w, h, attr,
+ WACS_D_LTEE, WACS_D_RTEE, WACS_D_LTEE, WACS_D_RTEE);
+ tui_draw_text(x + 2, y + 1, A_BOLD, "MOD");
+ tui_draw_text(x + 2, y + 2, 0, "%s", mode_repr[tpu->status]);
+
+ tui_draw_box(x, (y += TPU_INFO_H - 1), w, h, attr,
+ WACS_D_LTEE, WACS_D_RTEE, WACS_D_BTEE, WACS_D_LRCORNER);
+ tui_draw_text(x + 2, y + 1, A_BOLD, "IDL");
+ if (tpu->steps > 0)
+ idle = (int) ((double) tpu->idle_steps * 100 / (double) tpu->steps);
+ else
+ idle = 100;
+ tui_draw_text(x + 2, y + 2, 0, "%03i", idle);
+
+ if (tpu->ports[DIR_LEFT].attached) {
+ port = &tpu->ports[DIR_LEFT];
+ if (port->in >= 0)
+ tui_draw_text(sx - 3, sy + 6,
+ A_BOLD, "%03i", port->in);
+ if (port->type & PORT_IN)
+ tui_draw_wch(sx - 1, sy + 7,
+ port->in >= 0 ? A_BOLD : 0, WACS_RARROW);
+ if (port->type & PORT_OUT)
+ tui_draw_wch(sx - 1, sy + 8,
+ port->out >= 0 ? A_BOLD : 0, WACS_LARROW);
+ if (port->out >= 0)
+ tui_draw_text(sx - 3, sy + 10,
+ A_BOLD, "%03i", port->out);
+ }
+
+ if (tpu->ports[DIR_RIGHT].attached) {
+ port = &tpu->ports[DIR_RIGHT];
+ if (port->out >= 0)
+ tui_draw_text(sx + TPU_W + 1, sy + 5,
+ A_BOLD, "%03i", port->out);
+ if (port->type & PORT_OUT)
+ tui_draw_wch(sx + TPU_W + 1, sy + 7,
+ port->out >= 0 ? A_BOLD : 0, WACS_RARROW);
+ if (port->type & PORT_IN)
+ tui_draw_wch(sx + TPU_W + 1, sy + 8,
+ port->in >= 0 ? A_BOLD : 0, WACS_LARROW);
+ if (port->in >= 0)
+ tui_draw_text(sx + TPU_W + 1, sy + 9,
+ A_BOLD, "%03i", port->in);
+ }
+
+ if (tpu->ports[DIR_UP].attached) {
+ port = &tpu->ports[DIR_UP];
+ if (port->out >= 0)
+ tui_draw_text(sx + 9, sy - 1,
+ A_BOLD, "%03i", port->out);
+ if (port->type & PORT_OUT)
+ tui_draw_wch(sx + 13, sy - 1,
+ port->out >= 0 ? A_BOLD : 0, WACS_UARROW);
+ if (port->type & PORT_IN)
+ tui_draw_wch(sx + 15, sy - 1,
+ port->in >= 0 ? A_BOLD : 0, WACS_DARROW);
+ if (port->in >= 0)
+ tui_draw_text(sx + 17, sy - 1,
+ A_BOLD, "%03i", port->in);
+ }
+
+ if (tpu->ports[DIR_DOWN].attached) {
+ port = &tpu->ports[DIR_DOWN];
+ if (port->in >= 0)
+ tui_draw_text(sx + 9, sy + TPU_H,
+ A_BOLD, "%03i", port->in);
+ if (port->type & PORT_IN)
+ tui_draw_wch(sx + 13, sy + TPU_H,
+ port->in >= 0 ? A_BOLD : 0, WACS_UARROW);
+ if (port->type & PORT_OUT)
+ tui_draw_wch(sx + 15, sy + TPU_H,
+ port->out >= 0 ? A_BOLD : 0, WACS_DARROW);
+ if (port->out >= 0)
+ tui_draw_text(sx + 17, sy + TPU_H,
+ A_BOLD, "%03i", port->out);
+ }
+}
+
+static void
+tui_draw(void)
+{
+ struct tpu_map_link *link;
+ size_t i;
+ int tx;
+
+ clear();
+ for (i = 0; i < TPU_MAP_BUCKETS; i++) {
+ for (link = tis.tpu_map.buckets[i]; link; link = link->next)
+ tui_draw_tpu(link->tpu);
+ }
+ if (show_reloaded > 0) {
+ tx = MAX(0, scrx + scrw / 2 - 4);
+ tui_draw_text(scrx, scry, A_STANDOUT, "%-*s", scrw, "");
+ tui_draw_text(tx, scry, A_STANDOUT, "RELOADED");
+ show_reloaded--;
+ }
+ refresh();
+}
+
+static void
+tui_resize(void)
+{
+ scrw = getmaxx(stdscr);
+ scrh = getmaxy(stdscr);
+}
+
+static void
+tui_seek(struct tpu *tpu, int dx, int dy)
+{
+ struct tpu_map_link *link;
+ int minx, miny, maxx, maxy;
+ int x, y;
+ size_t i;
+
+ if (tpu) {
+ minx = maxx = tpu_pos_x(tpu);
+ miny = maxy = tpu_pos_y(tpu);
+ } else {
+ minx = miny = maxx = maxy = -1;
+ for (i = 0; i < TPU_MAP_BUCKETS; i++) {
+ link = tis.tpu_map.buckets[i];
+ for (; link; link = link->next) {
+ if (tpu && tpu != link->tpu)
+ continue;
+ x = tpu_pos_x(link->tpu);
+ if (minx == -1 || x < minx) minx = x;
+ if (maxx == -1 || x > maxx) maxx = x;
+ y = tpu_pos_y(link->tpu);
+ if (miny == -1 || y < miny) miny = y;
+ if (maxy == -1 || y > maxy) maxy = y;
+ }
+ }
+ if (minx == -1 || miny == -1) return;
+ }
+
+ if (dx == MIN) scrx = minx - 2;
+ else if (dx == MAX) scrx = maxx + TPU_W + 2 - scrw;
+ else if (dx == MID) scrx = (minx + maxx + TPU_W - scrw) / 2;
+
+ if (dy == MIN) scry = miny - 2;
+ else if (dy == MAX) scry = maxy + TPU_H + 2 - scrh;
+ else if (dy == MID) scry = (miny + maxy + TPU_H - scrh) / 2;
+}
+
+static void
+handlekey(int key)
+{
+ enum tpu_port_dir dir;
+ int c;
+
+ if (input_mode == MAIN) {
+ switch (key) {
+ case 'I':
+ input_mode = TPU_NAV;
+ break;
+ case KEY_UP:
+ scry -= 2;
+ break;
+ case KEY_DOWN:
+ scry += 2;
+ break;
+ case KEY_LEFT:
+ scrx -= 4;
+ break;
+ case KEY_RIGHT:
+ scrx += 4;
+ break;
+ case 's':
+ if (tis_stdin && tis.stdin_port.attached
+ && tis.stdin_port.out < 0) {
+ c = getc(tis_stdin);
+ if (c >= 0) tis.stdin_port.out = c;
+ }
+
+ if (tis.stdout_port.attached && tis.stdout_port.in >= 0) {
+ if (tis_stdout)
+ putc(tis.stdout_port.in, tis_stdout);
+ tis.stdout_port.in = -1;
+ }
+
+ tis_step(&tis);
+ break;
+ }
+ } else {
+ switch (key) {
+ case KEY_ESC:
+ input_mode = MAIN;
+ break;
+ case KEY_UP:
+ case KEY_DOWN:
+ case KEY_LEFT:
+ case KEY_RIGHT:
+ dir = key_to_dir(key);
+ if (tpu_sel && tpu_sel->ports[dir].dst_tpu)
+ tpu_sel = tpu_sel->ports[dir].dst_tpu;
+ tui_seek(tpu_sel, MID, MID);
+ break;
+ }
+ }
+}
+
+static struct tpu *
+first_tpu(void)
+{
+ size_t i;
+
+ for (i = 0; i < TPU_MAP_BUCKETS; i++) {
+ if (tis.tpu_map.buckets[i])
+ return tis.tpu_map.buckets[i]->tpu;
+ }
+
+ return NULL;
+}
+
+static void
+reset(int ifd, int argc, char **argv, bool watch)
+{
+ tis_load(&tis, argv[1]);
+
+ if (watch)
+ if (inotify_add_watch(ifd, argv[1], IN_MODIFY) < 0)
+ die("inotify_add_watch '%s':", argv[1]);
+
+ if (argc >= 3) {
+ if (tis_stdin) fclose(tis_stdin);
+ tis_stdin = fopen(argv[2], "r");
+ if (!tis_stdin) die("fopen '%s':", argv[2]);
+ }
+
+ if (argc >= 4) {
+ if (tis_stdout) fclose(tis_stdout);
+ tis_stdout = fopen(argv[3], "w+");
+ if (!tis_stdout) die("fopen '%s':", argv[3]);
+ }
+
+ if (tis.stdin_port.attached) {
+ tpu_sel = tis.stdin_port.dst_tpu;
+ } else {
+ tpu_sel = first_tpu();
+ }
+ tui_seek(NULL, MID, MID);
+
+}
+
+int
+main(int argc, char **argv)
+{
+ struct inotify_event event;
+ ssize_t len;
+ bool quit;
+ int key;
+ int ifd;
+
+ if (argc < 2 || argc > 4) {
+ fprintf(stderr, "Usage: tis-curses FILE [STDIN] [STDOUT]\n");
+ exit(1);
+ }
+
+ setlocale(LC_ALL, "");
+
+ initscr();
+
+ raw();
+ noecho();
+ keypad(stdscr, TRUE);
+ start_color();
+ curs_set(0);
+ tui_resize();
+ timeout(TIMEOUT);
+ ESCDELAY = 0;
+
+ tis_init(&tis);
+
+ ifd = inotify_init1(IN_NONBLOCK);
+
+ reset(ifd, argc, argv, true);
+
+ quit = false;
+ while (!quit) {
+ len = read(ifd, &event, sizeof(event));
+ if (len < 0 && errno != EAGAIN)
+ die("inotify_read:");
+ if (len >= 0) {
+ reset(ifd, argc, argv, true);
+ show_reloaded = 1000 / TIMEOUT;
+ }
+
+ tui_draw();
+ key = getch();
+ switch (key) {
+ case KEY_RESIZE:
+ tui_resize();
+ break;
+ case 'g':
+ tui_seek(tpu_sel, MID, MID);
+ break;
+ case 'h':
+ tui_seek(NULL, MID, MID);
+ break;
+ case 'i':
+ if (tis.stdin_port.attached)
+ tui_seek(tis.stdin_port.dst_tpu, MID, MID);
+ break;
+ case 'o':
+ if (tis.stdout_port.attached)
+ tui_seek(tis.stdout_port.dst_tpu, MID, MID);
+ break;
+ case 'r':
+ reset(ifd, argc, argv, false);
+ break;
+ case KEY_CTRL('c'):
+ quit = true;
+ break;
+ default:
+ handlekey(key);
+ break;
+ }
+ }
+
+ tis_deinit(&tis);
+
+ close(ifd);
+
+ if (tis_stdin) fclose(tis_stdin);
+ if (tis_stdout) fclose(tis_stdout);
+
+ endwin();
+}