diff options
| author | Louis Burda <quent.burda@gmail.com> | 2023-07-26 17:59:43 +0200 |
|---|---|---|
| committer | Louis Burda <quent.burda@gmail.com> | 2023-07-26 17:59:43 +0200 |
| commit | ed666b4fd98ad168f735b814cf74e77ef5c52794 (patch) | |
| tree | 1a9e0f4920060f193d5add588503253a2635a19a /tis100-curses.c | |
| parent | ce2b644f251d5b22dc2228a0ffc8479408c49c38 (diff) | |
| download | tis100-ed666b4fd98ad168f735b814cf74e77ef5c52794.tar.gz tis100-ed666b4fd98ad168f735b814cf74e77ef5c52794.zip | |
Fork tis256 to create version like game
Diffstat (limited to 'tis100-curses.c')
| -rw-r--r-- | tis100-curses.c | 565 |
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(); +} |
