tis100

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

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

Reimplement value passing and improve curses ui

Diffstat:
MMakefile | 4++--
Aasm.c | 433+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aasm.h | 8++++++++
Mtest/test.asm | 5++++-
Mtis-as.c | 471+++++++------------------------------------------------------------------------
Mtis-curses.c | 406++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
Mtpu.c | 161++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
Mtpu.h | 36++++++++++++++++++++++++++++++------
Mutil.c | 19+++++++++++++++++++
Mutil.h | 10++++++++++
10 files changed, 960 insertions(+), 593 deletions(-)

diff --git a/Makefile b/Makefile @@ -9,10 +9,10 @@ all: tis-as tis-curses clean: rm -f tis-as tis-curses -tis-as: tis-as.c tpu.c util.c +tis-as: tis-as.c tpu.c util.c asm.c $(CC) -o $@ $^ $(CFLAGS) -tis-curses: tis-curses.c tpu.c util.c +tis-curses: tis-curses.c tpu.c util.c asm.c $(CC) -o $@ $^ $(CFLAGS) -lncursesw install: diff --git a/asm.c b/asm.c @@ -0,0 +1,433 @@ +#include "asm.h" +#include "util.h" +#include "tpu.h" + +#include <stdarg.h> +#include <string.h> +#include <stdint.h> + +#define NAMEALPH "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_" +#define WHITESPACE " \t\v\r\n" + +enum asm_tok { + /* Global */ + TOK_STDIN, TOK_STDOUT, TOK_TPU, TOK_END, + + /* Operands (order like OP_*) */ + TOK_ACC, TOK_BAK, TOK_NIL, TOK_LEFT, TOK_RIGHT, + TOK_UP, TOK_DOWN, TOK_ANY, TOK_LAST, TOK_LIT, TOK_NAME, + + /* Instructions (order like INST_*) */ + TOK_NOP, TOK_MOV, TOK_SWP, TOK_SAV, TOK_ADD, TOK_SUB, + TOK_NEG, TOK_JMP, TOK_JEZ, TOK_JNZ, TOK_JGZ, TOK_JLZ, TOK_JRO, + + /* Misc */ + TOK_COMMENT, TOK_LABEL, TOK_XPOS, TOK_YPOS, TOK_NL, TOK_EOF +}; + +struct asm_tokenizer { + const char *filepath; + FILE *file; + enum asm_tok tok; + char *tokstr; + size_t lineno, off; + char linebuf[256]; +}; + +static const char *tok_reprs[] = { + /* Global */ + "'STDIN'", "'STDOUT'", "'TPU'", "'END'", + + /* Operands (order like OP_*) */ + "'ACC'", "'BAK'", "'NIL'", "'LEFT'", "'RIGHT'", + "'UP'", "'DOWN'", "'ANY'", "'LAST'", "<LIT>", "<NAME>", + + /* Instructions (order like INST_*) */ + "'NOP'", "'MOV'", "'SWP'", "'SAV'", "'ADD'", "'SUB'", + "'NEG'", "'JMP'", "'JEZ'", "'JNZ'", "'JGZ'", "'JLZ'", "'JRO'", + + /* Misc */ + "#<COMMENT>", "<LABEL>:", "X<INT>", "Y<INT>", "<NL>", "<EOF>" +}; + +static bool +is_lit(const char *str) +{ + unsigned long v; + char *end; + + v = strtoul(str, &end, 10); + if (!end || *end) return false; + + return v < 256; +} + +static uint8_t +str_to_lit(const char *str) +{ + return (uint8_t) atoi(str); +} + +enum tpu_inst_type +tok_to_inst(enum asm_tok tok) +{ + if (tok < TOK_NOP || tok > TOK_JRO) abort(); + return tok - TOK_NOP + INST_NOP; +} + +enum tpu_inst_op_type +tok_to_optype(enum asm_tok tok) +{ + if (tok < TOK_ACC || tok > TOK_NAME) abort(); + return tok - TOK_ACC + OP_ACC; +} + +static size_t +strlcat_op_name(char *buf, struct tpu_inst_op *op, size_t n) +{ + size_t len; + + if (op->type == OP_LIT) { + len = strlen(buf); + snprintf(buf + len, n - len, "%hhu", op->lit); + return op->lit; + } else if (op->type == OP_LABEL) { + return strdcat(buf, op->label, n); + } else { + return strdcat(buf, op_reprs[op->type], n); + } +} + +size_t +asm_print_inst(char *buf, size_t n, struct tpu_inst *inst) +{ + size_t len; + + len = strdcpy(buf, inst_reprs[inst->type], n); + if (inst->opcnt >= 1) { + len += strdcat(buf, " ", n); + len += strlcat_op_name(buf, &inst->ops[0], n); + } + if (inst->opcnt >= 2) { + len += strdcat(buf, ", ", n); + len += strlcat_op_name(buf, &inst->ops[1], n); + } + + return len; +} + +static enum asm_tok +tok_next(struct asm_tokenizer *tok) +{ + size_t len; + char *s; + + if (!tok->linebuf[tok->off]) { + if (feof(tok->file)) return TOK_EOF; + s = fgets(tok->linebuf, sizeof(tok->linebuf), tok->file); + if (!s && !feof(tok->file)) die("fgets:"); + if (!s) return TOK_NL; + + len = strlen(s); + if (len && s[len-1] != '\n' && !feof(tok->file)) + die("tis_load: line %lu too long", tok->lineno); + if (len && s[len-1] == '\n') s[len-1] = '\0'; + + tok->lineno += 1; + tok->tokstr = s; + tok->off = 0; + return TOK_NL; + } + + s = tok->linebuf + tok->off; + len = strspn(s, WHITESPACE); + tok->off += len; + s += len; + tok->tokstr = s; + if (!*s) return TOK_NL; + + len = strcspn(s, WHITESPACE ","); + tok->off += len; + if (s[len]) { + s[len] = '\0'; + tok->off += 1; + } + + if (!strcasecmp(s, "stdin")) { + return TOK_STDIN; + } else if (!strcasecmp(s, "stdout")) { + return TOK_STDOUT; + } else if (!strcasecmp(s, "tpu")) { + return TOK_TPU; + } else if (!strcasecmp(s, "end")) { + return TOK_END; + } else if (!strcasecmp(s, "acc")) { + return TOK_ACC; + } else if (!strcasecmp(s, "bak")) { + return TOK_BAK; + } else if (!strcasecmp(s, "nil")) { + return TOK_NIL; + } else if (!strcasecmp(s, "left")) { + return TOK_LEFT; + } else if (!strcasecmp(s, "right")) { + return TOK_RIGHT; + } else if (!strcasecmp(s, "up")) { + return TOK_UP; + } else if (!strcasecmp(s, "down")) { + return TOK_DOWN; + } else if (!strcasecmp(s, "any")) { + return TOK_ANY; + } else if (!strcasecmp(s, "last")) { + return TOK_LAST; + } else if (is_lit(s)) { + return TOK_LIT; + } else if (!strcasecmp(s, "nop")) { + return TOK_NOP; + } else if (!strcasecmp(s, "mov")) { + return TOK_MOV; + } else if (!strcasecmp(s, "swp")) { + return TOK_SWP; + } else if (!strcasecmp(s, "sav")) { + return TOK_SAV; + } else if (!strcasecmp(s, "add")) { + return TOK_ADD; + } else if (!strcasecmp(s, "sub")) { + return TOK_SUB; + } else if (!strcasecmp(s, "neg")) { + return TOK_NEG; + } else if (!strcasecmp(s, "jmp")) { + return TOK_JMP; + } else if (!strcasecmp(s, "jez")) { + return TOK_JEZ; + } else if (!strcasecmp(s, "jnz")) { + return TOK_JNZ; + } else if (!strcasecmp(s, "jgz")) { + return TOK_JGZ; + } else if (!strcasecmp(s, "jlz")) { + return TOK_JLZ; + } else if (!strcasecmp(s, "jro")) { + return TOK_JRO; + } else if (*s == '#') { + tok->off += strlen(tok->linebuf + tok->off); + return TOK_COMMENT; + } else if (len && strspn(s, NAMEALPH) == len-1 && s[len-1] == ':') { + return TOK_LABEL; + } else if (*s == 'X' && atoi(s+1) > 0) { + return TOK_XPOS; + } else if (*s == 'Y' && atoi(s+1) > 0) { + return TOK_YPOS; + } else if (strspn(s, NAMEALPH) == strlen(s)) { + return TOK_NAME; + } else { + die("tis_load: line %lu, invalid token '%s'", tok->lineno, s); + } +} + +static enum asm_tok +tok_next_in(struct asm_tokenizer *tokenizer, ...) +{ + va_list ap, cpy; + enum asm_tok tok; + bool first; + int arg; + + tok = tok_next(tokenizer); + + va_copy(cpy, ap); + + va_start(cpy, tokenizer); + while ((arg = va_arg(cpy, int)) > 0) { + if (tok == arg) return tok; + } + va_end(cpy); + + fprintf(stderr, "tis-as: load: "); + fprintf(stderr, "line %lu, got tok %s, expected one of (", + tokenizer->lineno, tok_reprs[tok]); + first = true; + va_start(ap, tokenizer); + while ((arg = va_arg(ap, int)) > 0) { + if (!first) fputc(',', stderr); + fputs(tok_reprs[arg], stderr); + first = false; + } + va_end(ap); + fputs(")\n", stderr); + + exit(1); +} + +void +tis_load(struct tis *tis, const char *filepath) +{ + struct asm_tokenizer tokenizer; + enum tpu_inst_op_type op1, op2; + enum asm_tok op1_tok, op2_tok; + uint8_t op1_lit, op2_lit; + enum tpu_inst_type inst; + struct tpu *tpu = NULL; + struct tpu_map_link *link; + struct tpu_port *port; + int stdin_x, stdin_y; + int stdout_x, stdout_y; + enum asm_tok tok; + char *label; + size_t i; + + 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); + + tokenizer.lineno = 0; + tokenizer.off = 0; + tokenizer.tokstr = NULL; + tokenizer.linebuf[tokenizer.off] = '\0'; + while ((tok = tok_next(&tokenizer)) != TOK_EOF) { + switch (tok) { + case TOK_STDIN: + if (tpu) goto disallowed; + tok_next_in(&tokenizer, TOK_XPOS, -1); + stdin_x = atoi(tokenizer.tokstr + 1); + tok_next_in(&tokenizer, TOK_YPOS, -1); + stdin_y = atoi(tokenizer.tokstr + 1); + tok_next_in(&tokenizer, TOK_NL, -1); + break; + case TOK_STDOUT: + if (tpu) goto disallowed; + tok_next_in(&tokenizer, TOK_XPOS, -1); + stdout_x = atoi(tokenizer.tokstr + 1); + tok_next_in(&tokenizer, TOK_YPOS, -1); + stdout_y = atoi(tokenizer.tokstr + 1); + tok_next_in(&tokenizer, TOK_NL, -1); + break; + case TOK_TPU: + if (tpu) goto disallowed; + tpu = malloc(sizeof(struct tpu)); + if (!tpu) die("malloc:"); + tpu_init(tpu); + tok_next_in(&tokenizer, TOK_XPOS, -1); + tpu->x = (size_t) atoi(tokenizer.tokstr + 1); + tok_next_in(&tokenizer, TOK_YPOS, -1); + tpu->y = (size_t) atoi(tokenizer.tokstr + 1); + tok_next_in(&tokenizer, TOK_NL, -1); + tpu_map_add(&tis->tpu_map, tpu); + break; + case TOK_END: + if (!tpu) goto disallowed; + tpu = NULL; + tok_next_in(&tokenizer, TOK_NL, -1); + break; + case TOK_NOP: case TOK_MOV: case TOK_SWP: case TOK_SAV: + case TOK_ADD: case TOK_SUB: case TOK_NEG: case TOK_JMP: + case TOK_JEZ: case TOK_JNZ: case TOK_JGZ: case TOK_JLZ: + case TOK_JRO: + if (!tpu) goto disallowed; + inst = tok_to_inst(tok); + + op1_tok = tok_next_in(&tokenizer, TOK_ACC, TOK_BAK, + 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); + break; + } + + op1 = tok_to_optype(op1_tok); + if (op1 == OP_LIT) + op1_lit = str_to_lit(tokenizer.tokstr); + + op2_tok = tok_next_in(&tokenizer, TOK_ACC, TOK_BAK, + 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); + break; + } + + op2 = tok_to_optype(op2_tok); + if (op2 == OP_LIT) + op2_lit = str_to_lit(tokenizer.tokstr); + 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); + break; + case TOK_LABEL: + label = strdup(tokenizer.tokstr); + if (!label_map_add(&tpu->labels, label, tpu->inst_cnt)) + die("tis_load: line %lu, duplicate label", + tokenizer.lineno); + tok_next_in(&tokenizer, TOK_NL, -1); + break; + case TOK_NL: + break; + default: + goto disallowed; + } + } + + if (stdin_x == 0 || stdin_y == 0) + die("tis_load: stdin tpu not set"); + + if (stdout_x == 0 || stdout_y == 0) + die("tis_load: stdout tpu not set"); + + for (i = 0; i < TPU_MAP_BUCKETS; i++) { + for (link = tis->tpu_map.buckets[i]; link; link = link->next) { + tpu_init_ports(link->tpu, &tis->tpu_map); + + 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"); + port->attached = true; + port->dst_tpu = NULL; + port->dst_port = &tis->stdin_port; + port->type = PORT_IN; + tis->stdin_port.attached = true; + tis->stdin_port.dst_tpu = link->tpu; + tis->stdin_port.dst_port = port; + } + + 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"); + port->attached = true; + port->dst_tpu = NULL; + port->dst_port = &tis->stdout_port; + port->type = PORT_OUT; + tis->stdout_port.attached = true; + tis->stdout_port.dst_tpu = link->tpu; + tis->stdout_port.dst_port = port; + } + } + } + + if (!tis->stdin_port.attached) + die("tis_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", + stdout_x, stdout_y); + + fclose(tokenizer.file); + + return; + +disallowed: + if (tok == TOK_NAME) { + die("tis_load: line %lu, unexpected token '%s'", + tokenizer.lineno, tokenizer.tokstr); + } else { + die("tis_load: line %lu, token %s not allowed here", + tokenizer.lineno, tok_reprs[tok]); + } +} diff --git a/asm.h b/asm.h @@ -0,0 +1,8 @@ +#pragma once + +#include "tpu.h" + +#include <stdio.h> + +size_t asm_print_inst(char *buf, size_t n, struct tpu_inst *inst); +void tis_load(struct tis *tis, const char *filepath); diff --git a/test/test.asm b/test/test.asm @@ -6,7 +6,10 @@ tpu X1 Y1 end tpu X2 Y1 - mov LEFT, RIGHT + mov LEFT, ACC + NEG + SUB 1 + mov ACC, RIGHT end tpu X3 Y1 diff --git a/tis-as.c b/tis-as.c @@ -1,7 +1,8 @@ +#include "asm.h" #include "tpu.h" #include "util.h" -#include <libgen.h> +#include <signal.h> #include <stdarg.h> #include <stdio.h> #include <string.h> @@ -9,458 +10,64 @@ #include <stdbool.h> #include <stdlib.h> -#define NAMEALPH "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_" -#define WHITESPACE " \t\v\r\n" - -enum tok { - /* Global */ - TOK_STDIN, TOK_STDOUT, TOK_TPU, TOK_END, - - /* Operands (order like OP_*) */ - TOK_ACC, TOK_BAK, TOK_NIL, TOK_LEFT, TOK_RIGHT, - TOK_UP, TOK_DOWN, TOK_ANY, TOK_LAST, TOK_LIT, TOK_NAME, - - /* Instructions (order like INST_*) */ - TOK_NOP, TOK_MOV, TOK_SWP, TOK_SAV, TOK_ADD, TOK_SUB, - TOK_NEG, TOK_JMP, TOK_JEZ, TOK_JNZ, TOK_JGZ, TOK_JLZ, TOK_JRO, - - /* Misc */ - TOK_COMMENT, TOK_LABEL, TOK_XPOS, TOK_YPOS, TOK_NL, TOK_EOF -}; - -struct tokenizer { - FILE *file; - enum tok tok; - char *tokstr; - size_t lineno, off; - char linebuf[256]; -}; - - -static int stdin_x = 0, stdin_y = 0; -static struct tpu_port stdin_port; - -static int stdout_x = 0, stdout_y = 0; -static struct tpu_port stdout_port; - -static struct tpu_map tpu_map; - -static const char *dir_names[] = { - "LEFT", "RIGHT", "UP", "DOWN" -}; - -static const char *status_names[] = { - "IDLE", "RUN", "READ", "WRITE" -}; - -static const char *tok_reprs[] = { - /* Global */ - "'STDIN'", "'STDOUT'", "'TPU'", "'END'", - - /* Operands (order like OP_*) */ - "'ACC'", "'BAK'", "'NIL'", "'LEFT'", "'RIGHT'", - "'UP'", "'DOWN'", "'ANY'", "'LAST'", "<LIT>", "<NAME>", - - /* Instructions (order like INST_*) */ - "'NOP'", "'MOV'", "'SWP'", "'SAV'", "'ADD'", "'SUB'", - "'NEG'", "'JMP'", "'JEZ'", "'JNZ'", "'JGZ'", "'JLZ'", "'JRO'", - - /* Misc */ - "#<COMMENT>", "<LABEL>:", "X<INT>", "Y<INT>", "<NL>", "<EOF>" -}; +static struct tis tis; +static FILE *tis_stdin = NULL; +static FILE *tis_stdout = NULL; +void (*cleanup)(void) = NULL; const char *progname = "tis-as"; -bool -is_lit(const char *str) -{ - unsigned long v; - char *end; - - v = strtoul(str, &end, 10); - if (!end || *end) return false; - - return v < 256; -} - -uint8_t -str2lit(const char *str) -{ - return (uint8_t) atoi(str); -} - -enum tpu_inst_type -tok_to_inst(enum tok tok) -{ - if (tok < TOK_NOP || tok > TOK_JRO) abort(); - return tok - TOK_NOP + INST_NOP; -} - -enum tpu_inst_op_type -tok_to_optype(enum tok tok) -{ - if (tok < TOK_ACC || tok > TOK_NAME) abort(); - return tok - TOK_ACC + OP_ACC; -} - -enum tok -tok_next(struct tokenizer *tok) +int +main(int argc, const char **argv) { - size_t len; - char *s; - - if (!tok->linebuf[tok->off]) { - if (feof(tok->file)) return TOK_EOF; - s = fgets(tok->linebuf, sizeof(tok->linebuf), tok->file); - if (!s && !feof(tok->file)) die("fgets:"); - if (!s) return TOK_NL; - - len = strlen(s); - if (len && s[len-1] != '\n' && !feof(tok->file)) - die("load: line %lu too long", tok->lineno); - if (len && s[len-1] == '\n') s[len-1] = '\0'; - - tok->lineno += 1; - tok->tokstr = s; - tok->off = 0; - return TOK_NL; - } - - s = tok->linebuf + tok->off; - len = strspn(s, WHITESPACE); - tok->off += len; - s += len; - tok->tokstr = s; - if (!*s) return TOK_NL; + bool idle, prev_idle; + int c; - len = strcspn(s, WHITESPACE ","); - tok->off += len; - printf("> %s\n", s); - if (s[len]) { - s[len] = '\0'; - tok->off += 1; + if (argc < 2 || argc > 4) { + fprintf(stderr, "Usage: tis-as FILE [STDIN] [STDOUT]\n"); + exit(1); } - if (!strcasecmp(s, "stdin")) { - return TOK_STDIN; - } else if (!strcasecmp(s, "stdout")) { - return TOK_STDOUT; - } else if (!strcasecmp(s, "tpu")) { - return TOK_TPU; - } else if (!strcasecmp(s, "end")) { - return TOK_END; - } else if (!strcasecmp(s, "acc")) { - return TOK_ACC; - } else if (!strcasecmp(s, "bak")) { - return TOK_BAK; - } else if (!strcasecmp(s, "nil")) { - return TOK_NIL; - } else if (!strcasecmp(s, "left")) { - return TOK_LEFT; - } else if (!strcasecmp(s, "right")) { - return TOK_RIGHT; - } else if (!strcasecmp(s, "up")) { - return TOK_UP; - } else if (!strcasecmp(s, "down")) { - return TOK_DOWN; - } else if (!strcasecmp(s, "any")) { - return TOK_ANY; - } else if (!strcasecmp(s, "last")) { - return TOK_LAST; - } else if (is_lit(s)) { - return TOK_LIT; - } else if (!strcasecmp(s, "nop")) { - return TOK_NOP; - } else if (!strcasecmp(s, "mov")) { - return TOK_MOV; - } else if (!strcasecmp(s, "swp")) { - return TOK_SWP; - } else if (!strcasecmp(s, "sav")) { - return TOK_SAV; - } else if (!strcasecmp(s, "add")) { - return TOK_ADD; - } else if (!strcasecmp(s, "sub")) { - return TOK_SUB; - } else if (!strcasecmp(s, "neg")) { - return TOK_NEG; - } else if (!strcasecmp(s, "jmp")) { - return TOK_JMP; - } else if (!strcasecmp(s, "jez")) { - return TOK_JEZ; - } else if (!strcasecmp(s, "jnz")) { - return TOK_JNZ; - } else if (!strcasecmp(s, "jgz")) { - return TOK_JGZ; - } else if (!strcasecmp(s, "jlz")) { - return TOK_JLZ; - } else if (!strcasecmp(s, "jro")) { - return TOK_JRO; - } else if (*s == '#') { - tok->off += strlen(tok->linebuf + tok->off); - return TOK_COMMENT; - } else if (len && strspn(s, NAMEALPH) == len-1 && s[len-1] == ':') { - return TOK_LABEL; - } else if (*s == 'X' && atoi(s+1) > 0) { - return TOK_XPOS; - } else if (*s == 'Y' && atoi(s+1) > 0) { - return TOK_YPOS; - } else if (strspn(s, NAMEALPH) == strlen(s)) { - return TOK_NAME; + if (argc >= 3) { + tis_stdin = fopen(argv[2], "r"); + if (!tis_stdin) die("fopen '%s':", argv[2]); } else { - die("load: line %lu, invalid token '%s'", tok->lineno, s); + tis_stdin = stdin; } -} - -enum tok -tok_next_in(struct tokenizer *tokenizer, ...) -{ - va_list ap, cpy; - enum tok tok; - bool first; - int arg; + setvbuf(tis_stdin, NULL, _IONBF, 0); - tok = tok_next(tokenizer); - - va_copy(cpy, ap); - - va_start(cpy, tokenizer); - while ((arg = va_arg(cpy, int)) > 0) { - if (tok == arg) return tok; - } - va_end(cpy); - - fprintf(stderr, "tis-as: load: "); - fprintf(stderr, "line %lu, got tok %s, expected one of (", - tokenizer->lineno, tok_reprs[tok]); - first = true; - va_start(ap, tokenizer); - while ((arg = va_arg(ap, int)) > 0) { - if (!first) fputc(',', stderr); - fputs(tok_reprs[arg], stderr); - first = false; - } - va_end(ap); - fputs(")\n", stderr); - - exit(1); -} - -static void -load(const char *fname) -{ - struct tokenizer tokenizer; - enum tpu_inst_op_type op1, op2; - enum tok op1_tok, op2_tok; - uint8_t op1_lit, op2_lit; - enum tpu_inst_type inst; - struct tpu *tpu = NULL; - struct tpu_map_link *link; - struct tpu_port *port; - enum tok tok; - char *label; - FILE *file; - size_t i; - - file = fopen(fname, "r"); - if (!file) die("fopen:"); - - tokenizer.lineno = 0; - tokenizer.file = file; - tokenizer.off = 0; - tokenizer.tokstr = NULL; - tokenizer.linebuf[tokenizer.off] = '\0'; - while ((tok = tok_next(&tokenizer)) != TOK_EOF) { - switch (tok) { - case TOK_STDIN: - if (tpu) goto disallowed; - tok_next_in(&tokenizer, TOK_XPOS, -1); - stdin_x = atoi(tokenizer.tokstr + 1); - tok_next_in(&tokenizer, TOK_YPOS, -1); - stdin_y = atoi(tokenizer.tokstr + 1); - tok_next_in(&tokenizer, TOK_NL, -1); - break; - case TOK_STDOUT: - if (tpu) goto disallowed; - tok_next_in(&tokenizer, TOK_XPOS, -1); - stdout_x = atoi(tokenizer.tokstr + 1); - tok_next_in(&tokenizer, TOK_YPOS, -1); - stdout_y = atoi(tokenizer.tokstr + 1); - tok_next_in(&tokenizer, TOK_NL, -1); - break; - case TOK_TPU: - if (tpu) goto disallowed; - tpu = malloc(sizeof(struct tpu)); - if (!tpu) die("malloc:"); - tpu_init(tpu); - tok_next_in(&tokenizer, TOK_XPOS, -1); - tpu->x = (size_t) atoi(tokenizer.tokstr + 1); - tok_next_in(&tokenizer, TOK_YPOS, -1); - tpu->y = (size_t) atoi(tokenizer.tokstr + 1); - tok_next_in(&tokenizer, TOK_NL, -1); - tpu_map_add(&tpu_map, tpu); - break; - case TOK_END: - if (!tpu) goto disallowed; - tpu = NULL; - tok_next_in(&tokenizer, TOK_NL, -1); - break; - case TOK_NOP: case TOK_MOV: case TOK_SWP: case TOK_SAV: - case TOK_ADD: case TOK_SUB: case TOK_NEG: case TOK_JMP: - case TOK_JEZ: case TOK_JNZ: case TOK_JGZ: case TOK_JLZ: - case TOK_JRO: - if (!tpu) goto disallowed; - inst = tok_to_inst(tok); - - op1_tok = tok_next_in(&tokenizer, TOK_ACC, TOK_BAK, - 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); - break; - } - - op1 = tok_to_optype(op1_tok); - if (op1 == OP_LIT) - op1_lit = str2lit(tokenizer.tokstr); - - op2_tok = tok_next_in(&tokenizer, TOK_ACC, TOK_BAK, - 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); - break; - } - - op2 = tok_to_optype(op2_tok); - if (op2 == OP_LIT) - op2_lit = str2lit(tokenizer.tokstr); - 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); - break; - case TOK_LABEL: - label = strdup(tokenizer.tokstr); - if (!label_map_add(&tpu->labels, label, tpu->inst_cnt)) - die("load: line %lu, duplicate label", - tokenizer.lineno); - tok_next_in(&tokenizer, TOK_NL, -1); - break; - case TOK_NL: - break; - default: - goto disallowed; - } - } - - if (stdin_x == 0 || stdin_y == 0 || stdout_x == 0 || stdout_y == 0) - die("load: stdin/stdout tpu not set"); - - for (i = 0; i < TPU_MAP_BUCKETS; i++) { - for (link = tpu_map.buckets[i]; link; link = link->next) { - tpu_init_ports(link->tpu, &tpu_map); - - if (link->x == stdin_x && link->y == stdin_y) { - port = &link->tpu->ports[DIR_UP]; - if (port->attached) - die("load: stdin port in use"); - port->attached = true; - port->dst_tpu = NULL; - port->dst_port = &stdin_port; - stdin_port.attached = true; - stdin_port.dst_tpu = NULL; - stdin_port.dst_port = port; - } - - if (link->x == stdout_x && link->y == stdout_y) { - port = &link->tpu->ports[DIR_DOWN]; - if (port->attached) - die("load: stdout port in use"); - port->attached = true; - port->dst_tpu = NULL; - port->dst_port = &stdout_port; - stdout_port.attached = true; - stdout_port.dst_tpu = NULL; - stdout_port.dst_port = port; - } - } - } - - if (!stdin_port.attached || !stdout_port.attached) - die("load: stdin/stdout tpu not found"); - - fclose(file); - - return; - -disallowed: - if (tok == TOK_NAME) { - die("load: line %lu, unexpected token '%s'", - tokenizer.lineno, tokenizer.tokstr); + if (argc >= 4) { + tis_stdout = fopen(argv[3], "w+"); + if (!tis_stdout) die("fopen '%s':", argv[3]); } else { - die("load: line %lu, token %s not allowed here", - tokenizer.lineno, tok_reprs[tok]); + tis_stdout = stdout; } -} - -void -run(void) -{ - struct tpu_map_link *link; - size_t i, k; - int c; + setvbuf(tis_stdout, NULL, _IONBF, 0); - stdin_port.out = -1; - stdout_port.reading = true; - while (1) { - if (stdin_port.dst_port->reading) { - c = getchar(); - if (c < 0) break; - stdin_port.out = c; - } + tis_init(&tis); - printf("===\n"); + tis_load(&tis, argv[1]); - for (i = 0; i < TPU_MAP_BUCKETS; i++) { - for (link = tpu_map.buckets[i]; link; link = link->next) { - tpu_step(link->tpu); - } + tis.stdin_port.out = -1; + idle = false; + while (!idle || !prev_idle || tis.stdin_port.reading && !feof(tis_stdin)) { + if (tis.stdin_port.out < 0) { + c = getc(tis_stdin); + if (c >= 0) tis.stdin_port.out = c; } - for (i = 0; i < TPU_MAP_BUCKETS; i++) { - for (link = tpu_map.buckets[i]; link; link = link->next) { - tpu_update_ports(link->tpu); - printf("FINAL %lu %lu: %s\n", link->x, link->y, - status_names[link->tpu->status]); - for (k = 0; k < 4; k++) { - if (link->tpu->ports[k].in >= 0) - printf("%lu %lu %5s: %i\n", - link->x, link->y, dir_names[k], - link->tpu->ports[k].in); - } - } + if (tis.stdout_port.in >= 0) { + putc(tis.stdout_port.in, tis_stdout); + tis.stdout_port.in = -1; } - if (stdout_port.dst_port->out >= 0) { - putchar(stdout_port.dst_port->out); - stdout_port.dst_port->out = -1; - } - } -} - -int -main(int argc, const char **argv) -{ - if (argc != 2) { - fprintf(stderr, "Usage: tis-as FILE\n"); - exit(1); + prev_idle = idle; + idle = !tis_step(&tis); } - load(argv[1]); + fclose(tis_stdin); + fclose(tis_stdout); - run(); + tis_deinit(&tis); } diff --git a/tis-curses.c b/tis-curses.c @@ -2,6 +2,7 @@ #include "tpu.h" #include "util.h" +#include "asm.h" #include <curses.h> @@ -17,45 +18,48 @@ #define KEY_ESC 0x1b #define KEY_CTRL(c) ((c) & ~0x60) -#define TCELL_INPUT_ROWS 14 -#define TCELL_INPUT_COLS 20 -#define TCELL_INFO_W 6 -#define TCELL_INFO_H 4 -#define TCELL_CNT 6 -#define TCELL_W (TCELL_INPUT_COLS + 2 + 6) -#define TCELL_H (TCELL_INPUT_ROWS + 2) +#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) + +enum input_mode { + MAIN, + TPU_NAV +}; enum { - COLOR_HEADING, - COLOR_VAL + NONE, MIN, MAX, MID }; -struct tpu_cell { - struct tpu tpu; - enum tpu_status status; - size_t x, y; - const char source[TCELL_INPUT_ROWS][TCELL_INPUT_COLS + 1]; - bool enabled; - char *warn; - float idle; +enum { + COLOR_HEADING, + COLOR_VAL }; -static size_t tpu_cell_rows = 2; -static size_t tpu_cell_cols = 3; - static int scrx = 0; static int scry = 0; static int scrw = 80; static int scrh = 40; -static struct tpu_cell *tpu_cells = NULL; +static struct tis tis; +static FILE *tis_stdin = NULL; +static FILE *tis_stdout = NULL; + +static enum input_mode input_mode = MAIN; + +static struct tpu *tpu_sel = NULL; +void (*cleanup)(void) = (void *) endwin; const char *progname = "tis-curses"; static const char * -cell_mode_str(struct tpu_cell *cell) +tpu_mode_str(struct tpu *tpu) { - switch (cell->status) { + switch (tpu->status) { case STATUS_READ: return "=R="; case STATUS_WRITE: @@ -67,14 +71,45 @@ cell_mode_str(struct tpu_cell *cell) } } +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 char * -cell_last_str(struct tpu_cell *cell) +tpu_last_str(struct tpu *tpu) +{ + if (tpu->last < 0) return "N/A"; + return dir_reprs[tpu->last]; +} + +static int +tpu_pos_x(struct tpu *tpu) { - return "N/A"; + return 2 + (int) tpu->x * (TPUT_W + 4); +} + +static int +tpu_pos_y(struct tpu *tpu) +{ + return 2 * (int) tpu->y * (TPUT_H + 2); } static void -tui_draw_box(int sx, int sy, int w, int h, const cchar_t *ul, const cchar_t *ur, +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; @@ -84,6 +119,8 @@ tui_draw_box(int sx, int sy, int w, int h, const cchar_t *ul, const cchar_t *ur, 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); @@ -96,26 +133,29 @@ tui_draw_box(int sx, int sy, int w, int h, const cchar_t *ul, const cchar_t *ur, 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, int attr, const char *fmt, ...) +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); - mvprintw(y - scry, x - scrx, "%s", buf); + 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, int attr, const cchar_t *c) +tui_draw_wch(int x, int y, attr_t attr, const cchar_t *c) { attron(attr); mvadd_wch(y - scry, x - scrx, c); @@ -123,120 +163,244 @@ tui_draw_wch(int x, int y, int attr, const cchar_t *c) } static void -tui_draw_tpu_cell(struct tpu_cell *cell) +tui_set_attr(int x, int y, attr_t attr) { - struct tpu_port *port; - int x, y, w, h; - - tui_draw_box((int) cell->x, (int) cell->y, TCELL_W, TCELL_H, - WACS_D_ULCORNER, WACS_D_URCORNER, WACS_D_LLCORNER, WACS_D_LRCORNER); + (void) mvchgat(y - scry, x - scrx, 1, attr, 0, NULL); +} - x = (int) cell->x + TCELL_W - TCELL_INFO_W; - w = TCELL_INFO_W; - h = TCELL_INFO_H; +static void +tui_draw_tpu(struct tpu *tpu) +{ + char linebuf[TPUT_INPUT_COLS + 1]; + struct tpu_port *port; + int sx, sy, x, y, w, h; + int i, idle, min; + attr_t attr;; + + 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, TPUT_W, TPUT_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++) { + 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); + } - tui_draw_box(x, (y = (int) cell->y), w, h, + x = sx + TPUT_W - TPUT_INFO_W; + y = sy; + w = TPUT_INFO_W; + h = TPUT_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", cell->tpu.acc); + tui_draw_text(x + 2, y + 2, 0, "%03i", tpu->acc); - tui_draw_box(x, (y += TCELL_INFO_H - 1), w, h, + tui_draw_box(x, (y += TPUT_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", cell->tpu.bak); + tui_draw_text(x + 2, y + 2, 0, "%03i", tpu->bak); - tui_draw_box(x, (y += TCELL_INFO_H - 1), w, h, + tui_draw_box(x, (y += TPUT_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", cell_last_str(cell)); + tui_draw_text(x + 2, y + 2, 0, "%s", tpu_last_str(tpu)); - tui_draw_box(x, (y += TCELL_INFO_H - 1), w, h, + tui_draw_box(x, (y += TPUT_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", cell_mode_str(cell)); + tui_draw_text(x + 2, y + 2, 0, "%s", tpu_mode_str(tpu)); - tui_draw_box(x, (y += TCELL_INFO_H - 1), w, h, + tui_draw_box(x, (y += TPUT_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"); - tui_draw_text(x + 2, y + 2, 0, "%03i", (int) (cell->idle * 100)); - - if (cell->tpu.ports[DIR_LEFT].attached) { - port = &cell->tpu.ports[DIR_LEFT]; + 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((int) cell->x - 1, (int) cell->y + 8, - port->reading ? A_BOLD : 0, WACS_RARROW); + tui_draw_wch(sx - 1, sy + 7, + port->in >= 0 ? A_BOLD : 0, WACS_RARROW); if (port->type & PORT_OUT) - tui_draw_wch((int) cell->x - 1, (int) cell->y + 7, - port->out > 0 ? A_BOLD : 0, WACS_LARROW); + tui_draw_wch(sx - 1, sy + 8, + port->out >= 0 ? A_BOLD : 0, WACS_LARROW); if (port->out >= 0) - tui_draw_text((int) cell->x - 3, (int) cell->y + 6, + tui_draw_text(sx - 3, sy + 10, A_BOLD, "%03i", port->out); } - if (cell->tpu.ports[DIR_RIGHT].attached) { - port = &cell->tpu.ports[DIR_RIGHT]; - if (port->type & PORT_IN) - tui_draw_wch((int) cell->x + TCELL_W + 1, (int) cell->y + 7, - port->reading ? A_BOLD : 0, WACS_LARROW); - if (port->type & PORT_OUT) - tui_draw_wch((int) cell->x + TCELL_W + 1, (int) cell->y + 8, - port->out > 0 ? A_BOLD : 0, WACS_RARROW); + if (tpu->ports[DIR_RIGHT].attached) { + port = &tpu->ports[DIR_RIGHT]; if (port->out >= 0) - tui_draw_text((int) cell->x + TCELL_W + 1, (int) cell->y + 9, + tui_draw_text(sx + TPUT_W + 1, sy + 5, A_BOLD, "%03i", port->out); + if (port->type & PORT_OUT) + tui_draw_wch(sx + TPUT_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, + port->in >= 0 ? A_BOLD : 0, WACS_LARROW); + if (port->in >= 0) + tui_draw_text(sx + TPUT_W + 1, sy + 9, + A_BOLD, "%03i", port->in); } - if (cell->tpu.ports[DIR_UP].attached) { - port = &cell->tpu.ports[DIR_UP]; - if (port->type & PORT_IN) - tui_draw_wch((int) cell->x + 13, (int) cell->y - 1, - port->reading ? A_BOLD : 0, WACS_DARROW); - if (port->type & PORT_OUT) - tui_draw_wch((int) cell->x + 15, (int) cell->y - 1, - port->out > 0 ? A_BOLD : 0, WACS_UARROW); + if (tpu->ports[DIR_UP].attached) { + port = &tpu->ports[DIR_UP]; if (port->out >= 0) - tui_draw_text((int) cell->x + 16, (int) cell->y - 1, + 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 (cell->tpu.ports[DIR_DOWN].attached) { - port = &cell->tpu.ports[DIR_DOWN]; - if (port->type & PORT_IN) - tui_draw_wch((int) cell->x + 13, (int) cell->y + TCELL_H, - port->out > 0 ? A_BOLD : 0, WACS_DARROW); - if (port->type & PORT_OUT) - tui_draw_wch((int) cell->x + 15, (int) cell->y + TCELL_H, - port->reading ? A_BOLD : 0, WACS_UARROW); + if (tpu->ports[DIR_DOWN].attached) { + port = &tpu->ports[DIR_DOWN]; if (port->out >= 0) - tui_draw_text((int) cell->x + 10, (int) cell->y + TCELL_H, + tui_draw_text(sx + 9, sy + TPUT_H, A_BOLD, "%03i", port->out); + if (port->type & PORT_IN) + tui_draw_wch(sx + 13, sy + TPUT_H, + port->in >= 0 ? A_BOLD : 0, WACS_UARROW); + if (port->type & PORT_OUT) + tui_draw_wch(sx + 15, sy + TPUT_H, + port->out >= 0 ? A_BOLD : 0, WACS_DARROW); + if (port->in >= 0) + tui_draw_text(sx + 17, sy + TPUT_H, + A_BOLD, "%03i", port->in); } } static void tui_draw(void) { - int i; + struct tpu_map_link *link; + size_t i; clear(); - for (i = 0; i < TCELL_CNT; i++) - tui_draw_tpu_cell(&tpu_cells[i]); + for (i = 0; i < TPU_MAP_BUCKETS; i++) { + for (link = tis.tpu_map.buckets[i]; link; link = link->next) + tui_draw_tpu(link->tpu); + } refresh(); } static void tui_resize(void) { - size_t x, y, i; - scrw = getmaxx(stdscr); scrh = getmaxy(stdscr); +} - for (y = 0; y < tpu_cell_rows; y++) { - for (x = 0; x < tpu_cell_cols; x++) { - i = y * tpu_cell_cols + x; - tpu_cells[i].x = 2 + x * (TCELL_W + 4); - tpu_cells[i].y = 2 + y * (TCELL_H + 2); +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 (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; + + 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; +} + +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_port.out < 0) { + c = getc(tis_stdin); + if (c >= 0) tis.stdin_port.out = c; + } + + if (tis.stdout_port.in >= 0) { + 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->ports[dir].dst_tpu) + tpu_sel = tpu_sel->ports[dir].dst_tpu; + tui_seek(tpu_sel, MID, MID); + break; } } } @@ -244,10 +408,14 @@ tui_resize(void) int main(int argc, char **argv) { - size_t x, y, i; bool quit; int key; + if (argc != 4) { + fprintf(stderr, "Usage: tis-curses FILE STDIN STDOUT\n"); + exit(1); + } + setlocale(LC_ALL, ""); initscr(); @@ -257,23 +425,21 @@ main(int argc, char **argv) keypad(stdscr, TRUE); start_color(); curs_set(0); + tui_resize(); + ESCDELAY = 0; - /* TODO load like tis-as */ + tis_init(&tis); + tis_load(&tis, argv[1]); - tpu_cells = calloc(tpu_cell_rows * tpu_cell_cols, sizeof(struct tpu_cell)); - if (!tpu_cells) die("malloc:"); + tis_stdin = fopen(argv[2], "r"); + if (!tis_stdin) die("fopen '%s':", argv[2]); - for (y = 0; y < tpu_cell_rows; y++) { - for (x = 0; x < tpu_cell_cols; x++) { - i = y * tpu_cell_cols + x; - tpu_cells[i].enabled = true; - tpu_init(&tpu_cells[i].tpu); - memset((void *) tpu_cells[i].source, 0, - TCELL_INPUT_ROWS * (TCELL_INPUT_COLS + 1)); - } - } + 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); - tui_resize(); quit = false; while (!quit) { tui_draw(); @@ -282,27 +448,31 @@ main(int argc, char **argv) case KEY_RESIZE: tui_resize(); break; - case KEY_UP: - scry -= 1; + case 'g': + tui_seek(tpu_sel, MID, MID); break; - case KEY_DOWN: - scry += 1; + case 'h': + tui_seek(NULL, MID, MID); break; - case KEY_LEFT: - scrx -= 2; + case 'i': + tui_seek(tis.stdin_port.dst_tpu, MID, MID); break; - case KEY_RIGHT: - scrx += 2; + case 'o': + tui_seek(tis.stdout_port.dst_tpu, MID, MID); break; case KEY_CTRL('c'): quit = true; break; + default: + handlekey(key); + break; } } - for (i = 0; i < TCELL_CNT; i++) - tpu_deinit(&tpu_cells[i].tpu); - free(tpu_cells); + tis_deinit(&tis); + + fclose(tis_stdin); + fclose(tis_stdout); endwin(); } diff --git a/tpu.c b/tpu.c @@ -6,9 +6,23 @@ #include <stddef.h> #include <string.h> -/* NOTES: - * do two passes (one with only movs) for 'async' ports, result is the same - */ +const char *dir_reprs[] = { + "LEFT", "RIGHT", "UP", "DOWN" +}; + +const char *status_reprs[] = { + "IDLE", "RUN", "READ", "WRITE" +}; + +const char *inst_reprs[] = { + "NOP", "MOV", "SWP", "SAV", "ADD", "SUB", + "NEG", "JMP", "JEZ", "JNZ", "JGZ", "JLZ", "JRO" +}; + +const char *op_reprs[] = { + "ACC", "BAK", "NIL", "LEFT", "RIGHT", + "UP", "DOWN", "ANY", "LAST", "LIT", "<NAME>" +}; static enum tpu_port_dir op_to_dir(enum tpu_inst_op_type op) @@ -109,28 +123,48 @@ label_map_get(struct label_map *map, const char *name) } void +tpu_port_init(struct tpu_port *port) +{ + port->attached = false; + port->dst_port = NULL; + port->dst_tpu = NULL; + port->type = PORT_BIDI; + port->clr_post_run = false; + port->reading = false; + port->writing = false; + port->in = -1; + port->out = -1; +} + +void +tpu_port_deinit(struct tpu_port *port) +{ + /* empty */ +} + +void tpu_init(struct tpu *tpu) { size_t i; + tpu->status = STATUS_IDLE; + tpu->x = 0; tpu->y = 0; - tpu->last = -1; + + tpu->steps = 0; + tpu->idle_steps = 0; + + tpu->pc = 0; tpu->acc = 0; tpu->bak = 0; tpu->inst_cnt = 0; - tpu->pc = 0; - tpu->status = STATUS_IDLE; label_map_init(&tpu->labels); - for (i = 0; i < 4; i++) { - tpu->ports[i].attached = false; - tpu->ports[i].dst_port = NULL; - tpu->ports[i].dst_tpu = NULL; - tpu->ports[i].type = PORT_BIDI; - tpu->ports[i].reading = false; - tpu->ports[i].in = -1; - tpu->ports[i].out = -1; - } + + tpu->last = -1; + tpu->io_port = -1; + for (i = 0; i < 4; i++) + tpu_port_init(&tpu->ports[i]); } void @@ -195,18 +229,26 @@ void tpu_update_ports(struct tpu *tpu) { struct tpu_port *port; + int i; - if (tpu->read_port >= 0) { - port = &tpu->ports[tpu->read_port]; - if (port->reading) { - if (port->attached && port->dst_port->out >= 0) { - port->in = port->dst_port->out; - port->dst_port->out = -1; - port->reading = false; - } - } else if (tpu->status == STATUS_RUN) { + for (i = 0; i < 4; i++) { + port = &tpu->ports[i]; + if (!port->attached) continue; + if (port->out >= 0 && port->dst_port->in < 0) { + port->dst_port->reading = false; + port->dst_port->in = port->out; + port->writing = false; + port->out = -1; + } + if (port->dst_port->out >= 0 && port->in < 0) { + port->reading = false; + port->in = port->dst_port->out; + port->dst_port->writing = false; + port->dst_port->out = -1; + } + if (tpu->status == STATUS_RUN && port->clr_post_run) { port->in = -1; - tpu->read_port = -1; + port->clr_post_run = false; } } } @@ -268,9 +310,9 @@ tpu_add_inst(struct tpu *tpu, enum tpu_inst_type inst_type, } /* tpu can always write to an empty port (->out), but only - * read values from a port (->in) if there is a writer attached - * and offering a value (->dst_port->out). the read value (->in) - * is cleared at the end of an instruction step */ + * read values if there is a writer attached and offering a value + * (->dst_port->out) -- the read value (->in) is cleared at the end of + * an instruction step */ static int tpu_port_read(struct tpu *tpu, enum tpu_port_dir dir) @@ -278,9 +320,9 @@ tpu_port_read(struct tpu *tpu, enum tpu_port_dir dir) struct tpu_port *port; port = &tpu->ports[dir]; + port->clr_post_run = true; if (port->in < 0) { port->reading = true; - tpu->read_port = (int) dir; return -1; } return port->in; @@ -292,7 +334,10 @@ tpu_port_write(struct tpu *tpu, enum tpu_port_dir dir, uint8_t lit) struct tpu_port *port; port = &tpu->ports[dir]; - if (port->out >= 0) return false; + if (port->out >= 0) { + port->writing = true; + return false; + } port->out = lit; return true; } @@ -479,12 +524,21 @@ tpu_exec(struct tpu *tpu, struct tpu_inst *inst) } } -void +enum tpu_status tpu_step(struct tpu *tpu) { - if (tpu->pc >= tpu->inst_cnt) return; - tpu->status = tpu_exec(tpu, &tpu->insts[tpu->pc]); - if (tpu->pc >= tpu->inst_cnt) tpu->pc = 0; + if (tpu->pc < tpu->inst_cnt) { + tpu->status = tpu_exec(tpu, &tpu->insts[tpu->pc]); + if (tpu->pc >= tpu->inst_cnt) tpu->pc = 0; + } else { + tpu->status = STATUS_IDLE; + } + + tpu->steps += 1; + if (tpu->status != STATUS_RUN) + tpu->idle_steps += 1; + + return tpu->status; } void @@ -551,3 +605,42 @@ tpu_map_get(struct tpu_map *map, size_t x, size_t y) return (*link)->tpu; } + +void +tis_init(struct tis *tis) +{ + tpu_map_init(&tis->tpu_map); + tpu_port_init(&tis->stdin_port); + tpu_port_init(&tis->stdout_port); +} + +void +tis_deinit(struct tis *tis) +{ + tpu_map_deinit(&tis->tpu_map); + tpu_port_deinit(&tis->stdin_port); + tpu_port_deinit(&tis->stdout_port); +} + +bool +tis_step(struct tis *tis) +{ + struct tpu_map_link *link; + bool running; + size_t i; + + running = false; + for (i = 0; i < TPU_MAP_BUCKETS; i++) { + link = tis->tpu_map.buckets[i]; + for (; link; link = link->next) + running |= (tpu_step(link->tpu) == STATUS_RUN); + } + + for (i = 0; i < TPU_MAP_BUCKETS; i++) { + link = tis->tpu_map.buckets[i]; + for (; link; link = link->next) + tpu_update_ports(link->tpu); + } + + return running; +} diff --git a/tpu.h b/tpu.h @@ -62,20 +62,26 @@ struct tpu_port { struct tpu *dst_tpu; enum tpu_port_type type; struct tpu_port *dst_port; - bool attached, reading; + bool clr_post_run; + bool reading, writing; + bool attached; int in, out; }; struct tpu { enum tpu_status status; - struct label_map labels; - struct tpu_port ports[4]; - int read_port; - uint8_t acc, bak; size_t x, y; + + struct tpu_port ports[4]; + int io_port; int last; + size_t steps; + size_t idle_steps; + + uint8_t acc, bak; uint8_t pc; + struct label_map labels; struct tpu_inst insts[TPU_MAX_INST]; size_t inst_cnt; }; @@ -90,11 +96,20 @@ struct tpu_map { struct tpu_map_link *buckets[TPU_MAP_BUCKETS]; }; +struct tis { + struct tpu_map tpu_map; + struct tpu_port stdin_port; + struct tpu_port stdout_port; +}; + void label_map_init(struct label_map *map); void label_map_deinit(struct label_map *map); bool label_map_add(struct label_map *map, const char *name, size_t pc); size_t label_map_get(struct label_map *map, const char *name); +void tpu_port_init(struct tpu_port *port); +void tpu_port_deinit(struct tpu_port *port); + void tpu_init(struct tpu *tpu); void tpu_deinit(struct tpu *tpu); struct tpu_inst *tpu_current_inst(struct tpu *tpu); @@ -107,9 +122,18 @@ bool tpu_add_inst(struct tpu *tpu, enum tpu_inst_type inst, void tpu_clear_ports(struct tpu *tpu); enum tpu_status tpu_exec_mov(struct tpu *tpu, struct tpu_inst *inst); enum tpu_status tpu_exec(struct tpu *tpu, struct tpu_inst *inst); -void tpu_step(struct tpu *tpu); +enum tpu_status tpu_step(struct tpu *tpu); void tpu_map_init(struct tpu_map *map); void tpu_map_deinit(struct tpu_map *map); void tpu_map_add(struct tpu_map *map, struct tpu *tpu); struct tpu *tpu_map_get(struct tpu_map *map, size_t x, size_t y); + +void tis_init(struct tis *tis); +void tis_deinit(struct tis *tis); +bool tis_step(struct tis *tis); + +extern const char *dir_reprs[]; +extern const char *status_reprs[]; +extern const char *inst_reprs[]; +extern const char *op_reprs[]; diff --git a/util.c b/util.c @@ -10,6 +10,8 @@ die(const char *fmt, ...) { va_list ap; + if (cleanup) cleanup(); + va_start(ap, fmt); fprintf(stderr, "%s: ", progname); vfprintf(stderr, fmt, ap); @@ -23,3 +25,20 @@ die(const char *fmt, ...) exit(1); } + +size_t +strdcpy(char *dst, const char *src, size_t n) +{ + strncpy(dst, src, n); + return strlen(src); +} + +size_t +strdcat(char *dst, const char *src, size_t n) +{ + size_t len; + + len = strlen(dst); + strncpy(dst + len, src, n - len); + return strlen(src); +} diff --git a/util.h b/util.h @@ -1,7 +1,17 @@ #pragma once +#include <stddef.h> + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) < (b) ? (b) : (a)) +#define ABS(a) ((a) > 0 ? (a) : -(a)) + __attribute__((noreturn)) __attribute__((format(printf, 1, 2))) void die(const char *fmt, ...); +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);