tis100

Reimplementation of Zachtronics TIS-100 as a TUI game
git clone https://git.sinitax.com/sinitax/tis100
Log | Files | Refs | sfeed.txt

commit e32b439cfe7ca72098b2ebe9e1e93f97b7c10ada
parent 40d8eb449ed072b47bfbe953a191708f761c53a0
Author: Louis Burda <quent.burda@gmail.com>
Date:   Tue, 25 Jul 2023 01:59:25 +0200

Add inotify-based interactive file reload and fix asm parsing

Diffstat:
Masm.c | 59+++++++++++++++++++++++++++++++++++------------------------
Mtest/test.asm | 8+++++---
Mtis-as.c | 2+-
Mtis-curses.c | 134+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Mtpu.c | 4++--
Mutil.h | 2+-
6 files changed, 128 insertions(+), 81 deletions(-)

diff --git a/asm.c b/asm.c @@ -85,12 +85,11 @@ tok_to_optype(enum asm_tok tok) static size_t strlcat_op_name(char *buf, struct tpu_inst_op *op, size_t n) { - size_t len; + char hhbuf[4]; if (op->type == OP_LIT) { - len = strlen(buf); - snprintf(buf + len, n - len, "%hhu", op->lit); - return op->lit; + snprintf(hhbuf, 4, "%hhu", op->lit); + return strdcat(buf, hhbuf, n); } else if (op->type == OP_LABEL) { return strdcat(buf, op->label, n); } else { @@ -130,7 +129,7 @@ tok_next(struct asm_tokenizer *tok) len = strlen(s); if (len && s[len-1] != '\n' && !feof(tok->file)) - die("tis_load: line %lu too long", tok->lineno); + die("load: line %lu too long", tok->lineno); if (len && s[len-1] == '\n') s[len-1] = '\0'; tok->lineno += 1; @@ -142,9 +141,10 @@ tok_next(struct asm_tokenizer *tok) s = tok->linebuf + tok->off; len = strspn(s, WHITESPACE); tok->off += len; - s += len; - tok->tokstr = s; - if (!*s) return TOK_NL; + if (!s[len]) return TOK_NL; + tok->tokstr = (s += len); + + if (*s == ',') die("load: line %lu, misaligned comma", tok->lineno); len = strcspn(s, WHITESPACE ","); tok->off += len; @@ -153,6 +153,8 @@ tok_next(struct asm_tokenizer *tok) tok->off += 1; } + //printf("> %s\n", tok->tokstr); + if (!strcasecmp(s, "stdin")) { return TOK_STDIN; } else if (!strcasecmp(s, "stdout")) { @@ -219,7 +221,7 @@ tok_next(struct asm_tokenizer *tok) } else if (strspn(s, NAMEALPH) == strlen(s)) { return TOK_NAME; } else { - die("tis_load: line %lu, invalid token '%s'", tok->lineno, s); + die("load: line %lu, invalid token '%s'", tok->lineno, s); } } @@ -274,12 +276,15 @@ tis_load(struct tis *tis, const char *filepath) char *label; size_t i; + tis_deinit(tis); + tis_init(tis); + stdin_x = stdin_y = 0; stdout_x = stdout_y = 0; tokenizer.filepath = filepath; tokenizer.file = fopen(filepath, "r"); - if (!tokenizer.file) die("tis_load: fopen '%s':", filepath); + if (!tokenizer.file) die("load: fopen '%s':", filepath); tokenizer.lineno = 0; tokenizer.off = 0; @@ -331,7 +336,9 @@ tis_load(struct tis *tis, const char *filepath) TOK_NIL, TOK_LEFT, TOK_RIGHT, TOK_UP, TOK_DOWN, TOK_ANY, TOK_LAST, TOK_LIT, TOK_NAME, TOK_NL, -1); if (op1_tok == TOK_NL) { - tpu_add_inst(tpu, inst, -1, 0, -1, 0); + if (!tpu_add_inst(tpu, inst, -1, 0, -1, 0)) + die("load: line %lu, invalid instruction", + tokenizer.lineno); break; } @@ -343,17 +350,21 @@ tis_load(struct tis *tis, const char *filepath) TOK_NIL, TOK_LEFT, TOK_RIGHT, TOK_UP, TOK_DOWN, TOK_ANY, TOK_LAST, TOK_LIT, TOK_NAME, TOK_NL, -1); if (op2_tok == TOK_NL) { - tpu_add_inst(tpu, inst, (int) op1, - op1_lit, -1, 0); + if (!tpu_add_inst(tpu, inst, (int) op1, + op1_lit, -1, 0)) + die("load: line %lu, invalid instruction", + tokenizer.lineno); break; } op2 = tok_to_optype(op2_tok); if (op2 == OP_LIT) op2_lit = str_to_lit(tokenizer.tokstr); + if (!tpu_add_inst(tpu, inst, (int) op1, op1_lit, + (int) op2, op2_lit)) + die("load: line %lu, invalid instruction", + tokenizer.lineno); tok_next_in(&tokenizer, TOK_NL, -1); - tpu_add_inst(tpu, inst, (int) op1, op1_lit, - (int) op2, op2_lit); break; case TOK_COMMENT: tok_next_in(&tokenizer, TOK_NL, -1); @@ -361,7 +372,7 @@ tis_load(struct tis *tis, const char *filepath) case TOK_LABEL: label = strdup(tokenizer.tokstr); if (!label_map_add(&tpu->labels, label, tpu->inst_cnt)) - die("tis_load: line %lu, duplicate label", + die("load: line %lu, duplicate label", tokenizer.lineno); tok_next_in(&tokenizer, TOK_NL, -1); break; @@ -373,10 +384,10 @@ tis_load(struct tis *tis, const char *filepath) } if (stdin_x == 0 || stdin_y == 0) - die("tis_load: stdin tpu not set"); + die("load: stdin tpu not set"); if (stdout_x == 0 || stdout_y == 0) - die("tis_load: stdout tpu not set"); + die("load: stdout tpu not set"); for (i = 0; i < TPU_MAP_BUCKETS; i++) { for (link = tis->tpu_map.buckets[i]; link; link = link->next) { @@ -385,7 +396,7 @@ tis_load(struct tis *tis, const char *filepath) if (link->x == stdin_x && link->y == stdin_y) { port = &link->tpu->ports[DIR_UP]; if (port->attached) - die("tis_load: stdin port in use"); + die("load: stdin port in use"); port->attached = true; port->dst_tpu = NULL; port->dst_port = &tis->stdin_port; @@ -398,7 +409,7 @@ tis_load(struct tis *tis, const char *filepath) if (link->x == stdout_x && link->y == stdout_y) { port = &link->tpu->ports[DIR_DOWN]; if (port->attached) - die("tis_load: stdout port in use"); + die("load: stdout port in use"); port->attached = true; port->dst_tpu = NULL; port->dst_port = &tis->stdout_port; @@ -411,11 +422,11 @@ tis_load(struct tis *tis, const char *filepath) } if (!tis->stdin_port.attached) - die("tis_load: stdin tpu (X%i Y%i) not found", + die("load: stdin tpu (X%i Y%i) not found", stdin_x, stdin_y); if (!tis->stdout_port.attached) - die("tis_load: stdout tpu (X%i Y%i) not found", + die("load: stdout tpu (X%i Y%i) not found", stdout_x, stdout_y); fclose(tokenizer.file); @@ -424,10 +435,10 @@ tis_load(struct tis *tis, const char *filepath) disallowed: if (tok == TOK_NAME) { - die("tis_load: line %lu, unexpected token '%s'", + die("load: line %lu, unexpected token '%s'", tokenizer.lineno, tokenizer.tokstr); } else { - die("tis_load: line %lu, token %s not allowed here", + die("load: line %lu, token %s not allowed here", tokenizer.lineno, tok_reprs[tok]); } } diff --git a/test/test.asm b/test/test.asm @@ -1,5 +1,5 @@ stdin X1 Y1 -stdout X3 Y1 +stdout X3 Y2 tpu X1 Y1 mov UP, RIGHT @@ -7,11 +7,13 @@ end tpu X2 Y1 mov LEFT, ACC - NEG - SUB 1 mov ACC, RIGHT end tpu X3 Y1 mov LEFT, DOWN end + +tpu X3 Y2 + mov UP, DOWN +end diff --git a/tis-as.c b/tis-as.c @@ -14,7 +14,7 @@ static struct tis tis; static FILE *tis_stdin = NULL; static FILE *tis_stdout = NULL; -void (*cleanup)(void) = NULL; +int (*cleanup)(void) = NULL; const char *progname = "tis-as"; int diff --git a/tis-curses.c b/tis-curses.c @@ -5,8 +5,8 @@ #include "asm.h" #include <curses.h> - -#include <libgen.h> +#include <sys/inotify.h> +#include <unistd.h> #include <locale.h> #include <errno.h> #include <stdarg.h> @@ -18,13 +18,15 @@ #define KEY_ESC 0x1b #define KEY_CTRL(c) ((c) & ~0x60) -#define TPUT_INPUT_ROWS 14 -#define TPUT_INPUT_COLS 20 -#define TPUT_INFO_W 6 -#define TPUT_INFO_H 4 -#define TPUT_CNT 6 -#define TPUT_W (TPUT_INPUT_COLS + 2 + 6) -#define TPUT_H (TPUT_INPUT_ROWS + 2) +#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, @@ -49,11 +51,13 @@ 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; -void (*cleanup)(void) = (void *) endwin; +int (*cleanup)(void) = endwin; const char *progname = "tis-curses"; static const char * @@ -98,13 +102,13 @@ tpu_last_str(struct tpu *tpu) static int tpu_pos_x(struct tpu *tpu) { - return 2 + (int) tpu->x * (TPUT_W + 4); + return 2 + (int) tpu->x * (TPU_W + 4); } static int tpu_pos_y(struct tpu *tpu) { - return 2 * (int) tpu->y * (TPUT_H + 2); + return 2 + (int) tpu->y * (TPU_H + 2); } static void @@ -163,15 +167,9 @@ tui_draw_wch(int x, int y, attr_t attr, const cchar_t *c) } static void -tui_set_attr(int x, int y, attr_t attr) -{ - (void) mvchgat(y - scry, x - scrx, 1, attr, 0, NULL); -} - -static void tui_draw_tpu(struct tpu *tpu) { - char linebuf[TPUT_INPUT_COLS + 1]; + char linebuf[TPU_INPUT_COLS + 1]; struct tpu_port *port; int sx, sy, x, y, w, h; int i, idle, min; @@ -181,42 +179,42 @@ tui_draw_tpu(struct tpu *tpu) sx = tpu_pos_x(tpu); sy = tpu_pos_y(tpu); - tui_draw_box(sx, sy, TPUT_W, TPUT_H, attr, + tui_draw_box(sx, sy, TPU_W, TPU_H, attr, WACS_D_ULCORNER, WACS_D_URCORNER, WACS_D_LLCORNER, WACS_D_LRCORNER); - min = MIN(tpu->pc, MAX(0, (int) tpu->inst_cnt - TPUT_INPUT_ROWS)); - for (i = 0; i < MIN((int) tpu->inst_cnt - min, TPUT_INPUT_ROWS); i++) { + min = MIN(tpu->pc, MAX(0, (int) tpu->inst_cnt - TPU_INPUT_ROWS)); + for (i = 0; i < MIN((int) tpu->inst_cnt - min, TPU_INPUT_ROWS); i++) { asm_print_inst(linebuf, sizeof(linebuf), &tpu->insts[min + i]); tui_draw_text(sx + 2, sy + 1 + i, i == tpu->pc ? A_STANDOUT : 0, - "%-*s", TPUT_INPUT_COLS, linebuf); + "%-*s", TPU_INPUT_COLS, linebuf); } - x = sx + TPUT_W - TPUT_INFO_W; + x = sx + TPU_W - TPU_INFO_W; y = sy; - w = TPUT_INFO_W; - h = TPUT_INFO_H; + 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 += TPUT_INFO_H - 1), w, h, attr, + 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 += TPUT_INFO_H - 1), w, h, attr, + 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"); tui_draw_text(x + 2, y + 2, 0, "%s", tpu_last_str(tpu)); - tui_draw_box(x, (y += TPUT_INFO_H - 1), w, h, attr, + 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", tpu_mode_str(tpu)); - tui_draw_box(x, (y += TPUT_INFO_H - 1), w, h, attr, + 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) @@ -244,16 +242,16 @@ tui_draw_tpu(struct tpu *tpu) if (tpu->ports[DIR_RIGHT].attached) { port = &tpu->ports[DIR_RIGHT]; if (port->out >= 0) - tui_draw_text(sx + TPUT_W + 1, sy + 5, + tui_draw_text(sx + TPU_W + 1, sy + 5, A_BOLD, "%03i", port->out); if (port->type & PORT_OUT) - tui_draw_wch(sx + TPUT_W + 1, sy + 7, + 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 + TPUT_W + 1, sy + 8, + 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 + TPUT_W + 1, sy + 9, + tui_draw_text(sx + TPU_W + 1, sy + 9, A_BOLD, "%03i", port->in); } @@ -276,16 +274,16 @@ tui_draw_tpu(struct tpu *tpu) if (tpu->ports[DIR_DOWN].attached) { port = &tpu->ports[DIR_DOWN]; if (port->out >= 0) - tui_draw_text(sx + 9, sy + TPUT_H, + tui_draw_text(sx + 9, sy + TPU_H, A_BOLD, "%03i", port->out); if (port->type & PORT_IN) - tui_draw_wch(sx + 13, sy + TPUT_H, + 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 + TPUT_H, + tui_draw_wch(sx + 15, sy + TPU_H, port->out >= 0 ? A_BOLD : 0, WACS_DARROW); if (port->in >= 0) - tui_draw_text(sx + 17, sy + TPUT_H, + tui_draw_text(sx + 17, sy + TPU_H, A_BOLD, "%03i", port->in); } } @@ -295,12 +293,19 @@ 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(); } @@ -342,12 +347,12 @@ tui_seek(struct tpu *tpu, int dx, int dy) } if (dx == MIN) scrx = minx - 2; - else if (dx == MAX) scrx = maxx + TPUT_W + 2 - scrw; - else if (dx == MID) scrx = (minx + maxx + TPUT_W - scrw) / 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 + TPUT_H + 2 - scrh; - else if (dy == MID) scry = (miny + maxy + TPUT_H - scrh) / 2; + else if (dy == MAX) scry = maxy + TPU_H + 2 - scrh; + else if (dy == MID) scry = (miny + maxy + TPU_H - scrh) / 2; } static void @@ -405,11 +410,35 @@ handlekey(int key) } } +void +reset(int ifd, int argc, char **argv) +{ + tis_load(&tis, argv[1]); + + if (inotify_add_watch(ifd, argv[1], IN_MODIFY) < 0) + die("inotify_add_watch '%s':", argv[1]); + + if (tis_stdin) fclose(tis_stdin); + tis_stdin = fopen(argv[2], "r"); + if (!tis_stdin) die("fopen '%s':", argv[2]); + + if (tis_stdout) fclose(tis_stdout); + tis_stdout = fopen(argv[3], "w+"); + if (!tis_stdout) die("fopen '%s':", argv[3]); + + tpu_sel = tis.stdin_port.dst_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 != 4) { fprintf(stderr, "Usage: tis-curses FILE STDIN STDOUT\n"); @@ -426,22 +455,25 @@ main(int argc, char **argv) start_color(); curs_set(0); tui_resize(); + timeout(TIMEOUT); ESCDELAY = 0; tis_init(&tis); - tis_load(&tis, argv[1]); - tis_stdin = fopen(argv[2], "r"); - if (!tis_stdin) die("fopen '%s':", argv[2]); + ifd = inotify_init1(IN_NONBLOCK); - tis_stdout = fopen(argv[3], "w+"); - if (!tis_stdout) die("fopen '%s':", argv[3]); - - tpu_sel = tis.stdin_port.dst_tpu; - tui_seek(NULL, MID, MID); + reset(ifd, argc, argv); 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); + show_reloaded = 1000 / TIMEOUT; + } + tui_draw(); key = getch(); switch (key) { @@ -471,6 +503,8 @@ main(int argc, char **argv) tis_deinit(&tis); + close(ifd); + fclose(tis_stdin); fclose(tis_stdout); diff --git a/tpu.c b/tpu.c @@ -262,13 +262,13 @@ tpu_set_inst(struct tpu *tpu, uint8_t pc, enum tpu_inst_type inst_type, inst = &tpu->insts[pc]; inst->type = inst_type; - if (op2 > 0) { + if (op2 >= 0) { inst->ops[1].type = (enum tpu_inst_op_type) op2; inst->ops[1].lit = op2_lit; inst->ops[0].type = (enum tpu_inst_op_type) op1; inst->ops[0].lit = op1_lit; inst->opcnt = 2; - } else if (op1 > 0) { + } else if (op1 >= 0) { inst->ops[0].type = (enum tpu_inst_op_type) op1; inst->ops[0].lit = op1_lit; inst->opcnt = 1; diff --git a/util.h b/util.h @@ -14,4 +14,4 @@ size_t strdcpy(char *dst, const char *src, size_t n); size_t strdcat(char *dst, const char *src, size_t n); extern const char *progname; -extern void (*cleanup)(void); +extern int (*cleanup)(void);