tis100

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

commit 5835917f57d4dc04b406c8e3e06bf3b046d709e3
parent d7865d956f9fe3a08ebe4429dce4428a9f74bc6c
Author: Louis Burda <quent.burda@gmail.com>
Date:   Mon, 24 Jul 2023 04:43:28 +0200

Add some more implementation details

Diffstat:
MMakefile | 4++--
Mtest/test.asm | 4++--
Mtis-as.c | 145+++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------
Mtis-curses.c | 45++++++++++++++-------------------------------
Mtpu.c | 512++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Mtpu.h | 63+++++++++++++++++++++++++++++++++++++++++++++------------------
Autil.c | 25+++++++++++++++++++++++++
Autil.h | 7+++++++
8 files changed, 703 insertions(+), 102 deletions(-)

diff --git a/Makefile b/Makefile @@ -8,10 +8,10 @@ all: tis-as tis-curses clean: rm -f tis-as tis-curses -tis-as: tis-as.c tpu.c +tis-as: tis-as.c tpu.c util.c $(CC) -o $@ $^ $(CFLAGS) -tis-curses: tis-curses.c tpu.c +tis-curses: tis-curses.c tpu.c util.c $(CC) -o $@ $^ $(CFLAGS) -lncursesw install: diff --git a/test/test.asm b/test/test.asm @@ -1,5 +1,5 @@ -in X1 Y1 -out X3 Y1 +stdin X1 Y1 +stdout X3 Y1 tpu X1 Y1 mov acc, bak diff --git a/tis-as.c b/tis-as.c @@ -1,5 +1,7 @@ #include "tpu.h" +#include "util.h" +#include <libgen.h> #include <stdarg.h> #include <stdio.h> #include <string.h> @@ -7,6 +9,7 @@ #include <stdbool.h> #include <stdlib.h> +#define NAMEALPH "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_" #define WHITESPACE " \t\v\r\n" enum tok { @@ -15,14 +18,15 @@ enum tok { /* 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_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_JLZ, TOK_JRO, + 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 + TOK_COMMENT, TOK_LABEL, TOK_XPOS, TOK_YPOS, + TOK_SEP, TOK_NL, TOK_EOF }; struct tokenizer { @@ -33,47 +37,32 @@ struct tokenizer { 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; -struct tpu_map map; +static struct tpu_map tpu_map; static const char *tok_reprs[] = { /* Global */ - "STDIN", "STDOUT", "BLOCK", "END", + "'STDIN'", "'STDOUT'", "'TPU'", "'END'", /* Operands */ - "ACC", "BAK", "NIL", "LEFT", "RIGHT", "UP", - "DOWN", "ANY", "LAST", "<LIT>", + "'ACC'", "'BAK'", "'NIL'", "'LEFT'", "'RIGHT'", "'UP'", + "'DOWN'", "'ANY'", "'LAST'", "<LIT>", "<NAME>", /* Instructions */ - "NOP", "MOV", "SWP", "SAV", "ADD", "SUB", - "NEG", "JMP", "JEZ", "JNZ", "JLZ", "JRO", + "'NOP'", "'MOV'", "'SWP'", "'SAV'", "'ADD'", "'SUB'", + "'NEG'", "'JMP'", "'JEZ'", "'JNZ'", "'JGZ'", "'JLZ'", "'JRO'", /* Misc */ - "#<COMMENT>", "<LABEL>", "X<INT>", "Y<INT>", "<NL>", "<EOF>" + "#<COMMENT>", "<LABEL>:", "X<INT>", "Y<INT>", "','", "<NL>", "<EOF>" }; -static void -__attribute__((format(printf, 1, 2))) -__attribute__((noreturn)) -die(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - fprintf(stderr, "tis: "); - vfprintf(stderr, fmt, ap); - if (*fmt && fmt[strlen(fmt)-1] == ':') { - fputc(' ', stderr); - perror(NULL); - } else { - fputc('\n', stderr); - } - va_end(ap); - - exit(1); -} +const char *progname = "tis-as"; bool is_lit(const char *str) @@ -96,14 +85,14 @@ str2lit(const char *str) enum tpu_inst_type tok_to_inst(enum tok tok) { - if (tok < TOK_NOP || tok >= TOK_JRO) abort(); + 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_LIT) abort(); + if (tok < TOK_ACC || tok > TOK_NAME) abort(); return tok - TOK_ACC + OP_ACC; } @@ -119,7 +108,7 @@ tok_next(struct tokenizer *tok) if (!s && !feof(tok->file)) die("fgets:"); if (!s) return TOK_EOF; if (*s && s[strlen(s)-1] != '\n') - die("tokenizer: line %lu too long", tok->lineno); + die("load: line %lu too long", tok->lineno); tok->off = 0; } @@ -137,7 +126,7 @@ tok_next(struct tokenizer *tok) return TOK_STDIN; } else if (!strcasecmp(s, "stdout")) { return TOK_STDIN; - } else if (!strcasecmp(s, "block")) { + } else if (!strcasecmp(s, "tpu")) { return TOK_TPU; } else if (!strcasecmp(s, "end")) { return TOK_END; @@ -161,6 +150,8 @@ tok_next(struct tokenizer *tok) return TOK_LAST; } else if (is_lit(s)) { return TOK_LIT; + } else if (strspn(s, NAMEALPH) == strlen(s)) { + return TOK_NAME; } else if (!strcasecmp(s, "nop")) { return TOK_NOP; } else if (!strcasecmp(s, "mov")) { @@ -181,6 +172,8 @@ tok_next(struct tokenizer *tok) 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")) { @@ -188,14 +181,14 @@ tok_next(struct tokenizer *tok) } else if (*s == '#') { tok->off = strlen(s); return TOK_COMMENT; - } else if (len && s[len-1] == ':') { + } 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 { - die("tokenizer: line %lu, invalid token '%s'", tok->lineno, s); + die("load: line %lu, invalid token '%s'", tok->lineno, s); } } @@ -217,7 +210,7 @@ tok_next_in(struct tokenizer *tokenizer, ...) } va_end(cpy); - fprintf(stderr, "tis-as: tokenizer: "); + fprintf(stderr, "tis-as: load: "); fprintf(stderr, "line %lu, got tok '%s', expected one of ", tokenizer->lineno, tok_reprs[tok]); @@ -243,8 +236,12 @@ load(const char *fname) 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:"); @@ -278,11 +275,11 @@ load(const char *fname) if (!tpu) die("malloc:"); tpu_init(tpu); tok_next_in(&tokenizer, TOK_XPOS, -1); - tpu->x = atoi(tokenizer.tokstr + 1); + tpu->x = (size_t) atoi(tokenizer.tokstr + 1); tok_next_in(&tokenizer, TOK_YPOS, -1); - tpu->y = atoi(tokenizer.tokstr + 1); + tpu->y = (size_t) atoi(tokenizer.tokstr + 1); tok_next_in(&tokenizer, TOK_NL, -1); - /* TODO insert tpu */ + tpu_map_add(&tpu_map, tpu); break; case TOK_END: if (!tpu) goto disallowed; @@ -290,7 +287,8 @@ load(const char *fname) 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_JLZ: case TOK_JRO: + case TOK_JEZ: case TOK_JNZ: case TOK_JGZ: case TOK_JLZ: + case TOK_JRO: if (!tpu) goto disallowed; inst = tok_to_inst(tok); @@ -328,24 +326,85 @@ load(const char *fname) tok_next_in(&tokenizer, TOK_NL, -1); break; case TOK_LABEL: - /* TODO */ + 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; 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_x) { + 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.dst_port = port; + 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); disallowed: - die("tokenizer: line %lu, token %s not allowed here", + die("load: line %lu, token %s not allowed here", tokenizer.lineno, tok_reprs[tok]); } void run(void) { + struct tpu_map_link *link; + struct tpu_inst *inst; + size_t i, pc; + + /* initial pass to propagate moves */ + for (i = 0; i < TPU_MAP_BUCKETS; i++) { + for (link = tpu_map.buckets[i]; link; link = link->next) { + inst = tpu_current_inst(link->tpu); + if (inst && inst->type == INST_MOV) { + pc = link->tpu->pc; + tpu_exec(link->tpu, inst); + link->tpu->pc = pc; + } + } + } + for (i = 0; i < TPU_MAP_BUCKETS; i++) { + for (link = tpu_map.buckets[i]; link; link = link->next) { + inst = tpu_current_inst(link->tpu); + tpu_step(link->tpu); + } + } } int diff --git a/tis-curses.c b/tis-curses.c @@ -1,9 +1,11 @@ #define NCURSES_WIDECHAR 1 #include "tpu.h" +#include "util.h" #include <curses.h> +#include <libgen.h> #include <locale.h> #include <errno.h> #include <stdarg.h> @@ -30,7 +32,7 @@ enum { struct tpu_cell { struct tpu tpu; - enum tpu_mode mode; + enum tpu_status status; size_t x, y; const char source[TCELL_INPUT_ROWS][TCELL_INPUT_COLS + 1]; bool enabled; @@ -46,38 +48,19 @@ static size_t screenh = 40; static struct tpu_cell *tpu_cells = NULL; -static void -__attribute__((format(printf, 1, 2))) -__attribute__((noreturn)) -die(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - fprintf(stderr, "tis: "); - vfprintf(stderr, fmt, ap); - if (*fmt && fmt[strlen(fmt)-1] == ':') { - fputc(' ', stderr); - perror(NULL); - } else { - fputc('\n', stderr); - } - va_end(ap); - - exit(1); -} +const char *progname = "tis-curses"; static const char * cell_mode_str(struct tpu_cell *cell) { - switch (cell->mode) { - case MODE_READ: + switch (cell->status) { + case STATUS_READ: return "=R="; - case MODE_WRITE: + case STATUS_WRITE: return "=W="; - case MODE_IDLE: + case STATUS_IDLE: return "=I="; - case MODE_RUN: + case STATUS_RUN: return "=X="; } } @@ -170,7 +153,7 @@ tui_draw_tpu_cell(struct tpu_cell *cell) 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].dst_tpu) { + if (cell->tpu.ports[DIR_LEFT].attached) { port = &cell->tpu.ports[DIR_LEFT]; if (port->type & PORT_IN) tui_draw_wch((int) cell->x - 1, (int) cell->y + 8, @@ -183,7 +166,7 @@ tui_draw_tpu_cell(struct tpu_cell *cell) A_BOLD, "%03i", port->val); } - if (cell->tpu.ports[DIR_RIGHT].dst_tpu) { + 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, @@ -196,7 +179,7 @@ tui_draw_tpu_cell(struct tpu_cell *cell) A_BOLD, "%03i", port->val); } - if (cell->tpu.ports[DIR_UP].dst_tpu) { + 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, @@ -209,7 +192,7 @@ tui_draw_tpu_cell(struct tpu_cell *cell) A_BOLD, "%03i", port->val); } - if (cell->tpu.ports[DIR_DOWN].dst_tpu) { + 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, @@ -252,7 +235,7 @@ tui_resize(void) } int -main(int argc, const char **argv) +main(int argc, char **argv) { size_t x, y, i; bool quit; diff --git a/tpu.c b/tpu.c @@ -1,27 +1,527 @@ #include "tpu.h" +#include "util.h" + +#include <stdbool.h> +#include <stddef.h> +#include <string.h> + +/* NOTES: + * do two passes (one with only movs) for 'async' ports, result is the same + */ + +static enum tpu_port_dir +op_to_dir(enum tpu_inst_op_type op) +{ + return op - OP_LEFT; +} + +static enum tpu_port_dir +opposite_dir(enum tpu_port_dir dir) +{ + switch (dir) { + case DIR_UP: + return DIR_DOWN; + case DIR_DOWN: + return DIR_UP; + case DIR_LEFT: + return DIR_RIGHT; + case DIR_RIGHT: + return DIR_LEFT; + } +} + +static uint32_t +djb_hash(const char *str) +{ + uint32_t hash; + const char *c; + + hash = 5381; + for (c = str; *c; c++) + hash = 33 * hash + (uint8_t) *c; + + return hash; +} + +void +label_map_init(struct label_map *map) +{ + memset(map->buckets, 0, sizeof(void *) * LABEL_MAP_BUCKETS); +} + +void +label_map_deinit(struct label_map *map) +{ + struct label_map_link *link, *next; + size_t i; + + for (i = 0; i < LABEL_MAP_BUCKETS; i++) { + link = map->buckets[i]; + while (link) { + next = link->next; + free(link->label); + free(link); + link = next; + } + } +} + +static struct label_map_link ** +label_map_link_pos(struct label_map *map, const char *name) +{ + struct label_map_link **link; + + link = &map->buckets[djb_hash(name) % LABEL_MAP_BUCKETS]; + while (*link && strcmp((*link)->label, name)) + link = &(*link)->next; + + return link; +} + +bool +label_map_add(struct label_map *map, const char *name, size_t pc) +{ + struct label_map_link **pos, *link; + + pos = label_map_link_pos(map, name); + if (*pos) return false; + + *pos = link = malloc(sizeof(struct label_map_link)); + if (!link) die("malloc:"); + link->label = strdup(name); + if (!link->label) die("strdup:"); + link->pc = pc; + link->next = NULL; + + return true; +} + +size_t +label_map_get(struct label_map *map, const char *name) +{ + struct label_map_link **link; + + link = label_map_link_pos(map, name); + if (!*link) return TPU_MAX_INST; + + return (*link)->pc; +} void tpu_init(struct tpu *tpu) { - + size_t i; + + tpu->x = 0; + tpu->y = 0; + tpu->last = -1; + tpu->acc = 0; + tpu->bak = 0; + tpu->inst_cnt = 0; + tpu->pc = 0; + 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].read = false; + tpu->ports[i].write = false; + tpu->ports[i].type = PORT_BIDI; + tpu->ports[i].val = 0; + } +} + +void +tpu_deinit(struct tpu *tpu) +{ + int i; + + label_map_deinit(&tpu->labels); + for (i = 0; i < TPU_MAX_INST; i++) { + if (tpu->insts[i].ops[0].type == OP_LABEL) + free(tpu->insts[i].ops[0].label); + } +} + +struct tpu_inst * +tpu_current_inst(struct tpu *tpu) +{ + if (tpu->pc >= tpu->inst_cnt) + return NULL; + return &tpu->insts[tpu->pc]; } void -tpu_add_inst(struct tpu *tpu, enum tpu_inst_type inst, +tpu_init_ports(struct tpu *tpu, struct tpu_map *map) +{ + struct tpu *neighbor; + enum tpu_port_dir odir; + size_t x, y; + int i; + + for (i = 0; i < 4; i++) { + switch (i) { + case DIR_LEFT: + x = tpu->x - 1; + y = tpu->y; + break; + case DIR_RIGHT: + x = tpu->x + 1; + y = tpu->y; + break; + case DIR_UP: + x = tpu->x; + y = tpu->y - 1; + break; + case DIR_DOWN: + x = tpu->x; + y = tpu->y + 1; + break; + } + + neighbor = tpu_map_get(map, x, y); + if (neighbor) { + tpu->ports[i].attached = true; + tpu->ports[i].dst_tpu = neighbor; + odir = opposite_dir((enum tpu_port_dir) i); + tpu->ports[i].dst_port = &neighbor->ports[odir]; + } + } +} + +bool +tpu_add_inst(struct tpu *tpu, enum tpu_inst_type inst_type, int op1, uint8_t op1_lit, int op2, uint8_t op2_lit) { + struct tpu_inst *inst; + inst = calloc(1, sizeof(struct tpu_inst)); + if (!inst) die("malloc:"); + inst->type = inst_type; + + 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) { + inst->ops[0].type = (enum tpu_inst_op_type) op1; + inst->ops[0].lit = op1_lit; + inst->opcnt = 1; + } else { + inst->opcnt = 0; + } + + switch (inst->type) { + case INST_NOP: case INST_SAV: + case INST_SWP: case INST_NEG: + return inst->opcnt == 0; + case INST_ADD: case INST_SUB: + return inst->opcnt == 1; + case INST_JMP: case INST_JEZ: case INST_JNZ: + case INST_JGZ: case INST_JLZ: case INST_JRO: + if (inst->opcnt != 1) return false; + if (inst->ops[0].type != OP_LABEL) return false; + break; + case INST_MOV: + if (inst->opcnt != 2) return false; + if (inst->ops[1].type == OP_LIT) return false; + break; + } + + return true; +} + +void +tpu_ports_clear(struct tpu *tpu) +{ + int i; + + for (i = 0; i < 4; i++) { + tpu->ports[i].read = false; + tpu->ports[i].write = false; + } +} + +int +tpu_exec_get(struct tpu *tpu, struct tpu_inst_op *op) +{ + struct tpu_port *port; + uint8_t lit; + int i; + + switch (op->type) { + case OP_ACC: + lit = tpu->acc; + break; + case OP_BAK: + lit = tpu->bak; + break; + case OP_NIL: + lit = 0; + break; + case OP_LEFT: case OP_RIGHT: case OP_UP: case OP_DOWN: + port = &tpu->ports[op_to_dir(op->type)]; + port->read = true; + if (!port->attached || !port->dst_port->write) + return -1; + tpu->last = (int) op_to_dir(op->type); + lit = port->dst_port->val; + break; + case OP_ANY: + for (i = 0; i < 4; i++) { + port = &tpu->ports[i]; + port->read = true; + if (port->attached && port->dst_port->write) { + tpu->last = i; + lit = port->dst_port->val; + break; + } + } + if (i == 4) return -1; + break; + case OP_LAST: + if (tpu->last < 0) return 0; + port = &tpu->ports[tpu->last]; + port->read = true; + if (!port->attached || !port->dst_port->write) + return -1; + lit = port->dst_port->val; + break; + case OP_LIT: + lit = op->lit; + break; + default: + abort(); + } + + tpu_ports_clear(tpu); + + return (int) lit; +} + +bool +tpu_exec_put(struct tpu *tpu, struct tpu_inst_op *op, uint8_t lit) +{ + struct tpu_port *port; + int i; + + switch (op->type) { + case OP_BAK: + tpu->bak = lit; + break; + case OP_NIL: + break; + case OP_LEFT: case OP_RIGHT: case OP_UP: case OP_DOWN: + port = &tpu->ports[op_to_dir(op->type)]; + port->write = true; + if (!port->attached || !port->dst_port->write) + return false; + lit = port->dst_port->val; + tpu->last = (int) op_to_dir(op->type); + break; + case OP_ANY: + for (i = 0; i < 4; i++) { + port = &tpu->ports[i]; + port->write = true; + if (port->attached && port->dst_port->write) { + port->dst_port->val = lit; + tpu->last = i; + break; + } + } + if (i == 4) return false; + break; + case OP_LAST: + if (tpu->last < 0) break; + port = &tpu->ports[tpu->last]; + port->write = true; + if (!port->attached || !port->dst_port->write) + return false; + break; + default: + abort(); + } + + tpu_ports_clear(tpu); + + return true; +} + +enum tpu_status +tpu_exec_mov(struct tpu *tpu, struct tpu_inst *inst) +{ + int lit; + + if (inst->type != INST_MOV) abort(); + + lit = tpu_exec_get(tpu, &inst->ops[0]); + if (lit < 0) return STATUS_READ; + + if (!tpu_exec_put(tpu, &inst->ops[0], (uint8_t) lit)) + return STATUS_WRITE; + + tpu->pc += 1; + return STATUS_RUN; +} + +enum tpu_status +tpu_exec(struct tpu *tpu, struct tpu_inst *inst) +{ + uint8_t lit; + int val; + + switch (inst->type) { + case INST_NOP: + tpu->pc += 1; + return STATUS_RUN; + case INST_MOV: + return tpu_exec_mov(tpu, inst); + case INST_SWP: + lit = tpu->acc; + tpu->acc = tpu->bak; + tpu->bak = lit; + tpu->pc += 1; + return STATUS_RUN; + case INST_SAV: + tpu->bak = tpu->acc; + tpu->pc += 1; + return STATUS_RUN; + case INST_ADD: + val = tpu_exec_get(tpu, &inst->ops[0]); + if (val < 0) return STATUS_READ; + tpu->acc += (uint8_t) val; + tpu->pc += 1; + return STATUS_RUN; + case INST_SUB: + val = tpu_exec_get(tpu, &inst->ops[0]); + if (val < 0) return STATUS_READ; + tpu->acc -= (uint8_t) val; + tpu->pc += 1; + return STATUS_RUN; + case INST_NEG: + tpu->acc = -tpu->acc; + tpu->pc += 1; + return STATUS_RUN; + case INST_JMP: + tpu->pc = label_map_get(&tpu->labels, inst->ops[0].label); + if (tpu->pc >= TPU_MAX_INST) abort(); + return STATUS_RUN; + case INST_JEZ: + if (tpu->acc == 0) { + tpu->pc = label_map_get(&tpu->labels, + inst->ops[0].label); + if (tpu->pc >= TPU_MAX_INST) abort(); + } else { + tpu->pc += 1; + } + return STATUS_RUN; + case INST_JNZ: + if (tpu->acc != 0) { + tpu->pc = label_map_get(&tpu->labels, + inst->ops[0].label); + if (tpu->pc >= TPU_MAX_INST) abort(); + } else { + tpu->pc += 1; + } + return STATUS_RUN; + case INST_JGZ: + if (tpu->acc > 0) { + tpu->pc = label_map_get(&tpu->labels, + inst->ops[0].label); + if (tpu->pc >= TPU_MAX_INST) abort(); + } else { + tpu->pc += 1; + } + return STATUS_RUN; + case INST_JLZ: + if (tpu->acc < 0) { + tpu->pc = label_map_get(&tpu->labels, + inst->ops[0].label); + if (tpu->pc >= TPU_MAX_INST) abort(); + } else { + tpu->pc += 1; + } + return STATUS_RUN; + case INST_JRO: + tpu->pc += inst->ops[0].lit; + if (tpu->pc >= tpu->inst_cnt) tpu->pc = 0; + return STATUS_RUN; + default: + abort(); + } } void -tpu_step(struct tpu *tpu, uint8_t *up, uint8_t *right, - uint8_t *down, uint8_t *left) +tpu_step(struct tpu *tpu) { + if (tpu->pc >= tpu->inst_cnt) return; + tpu_exec(tpu, &tpu->insts[tpu->pc]); + if (tpu->pc >= tpu->inst_cnt) tpu->pc = 0; +} +void +tpu_map_init(struct tpu_map *map) +{ + memset(map->buckets, 0, sizeof(void *) * TPU_MAP_BUCKETS); } void -tpu_deinit(struct tpu *tpu) +tpu_map_deinit(struct tpu_map *map) +{ + struct tpu_map_link *link, *next; + size_t i; + + for (i = 0; i < TPU_MAP_BUCKETS; i++) { + link = map->buckets[i]; + while (link) { + next = link->next; + tpu_deinit(link->tpu); + free(link->tpu); + free(link); + link = next; + } + } +} + +static struct tpu_map_link ** +tpu_map_link_pos(struct tpu_map *map, size_t x, size_t y) { - + struct tpu_map_link **link; + size_t i; + + i = (x + y) % TPU_MAP_BUCKETS; + link = &map->buckets[i]; + while (*link && !((*link)->x == x && (*link)->y == y)) + link = &(*link)->next; + + return link; +} + +void +tpu_map_add(struct tpu_map *map, struct tpu *tpu) +{ + struct tpu_map_link **pos, *link; + + pos = tpu_map_link_pos(map, tpu->x, tpu->y); + *pos = link = malloc(sizeof(struct tpu_map_link)); + if (!link) die("malloc:"); + link->tpu = tpu; + link->x = tpu->x; + link->y = tpu->y; + link->next = NULL; +} + +struct tpu * +tpu_map_get(struct tpu_map *map, size_t x, size_t y) +{ + struct tpu_map_link **link; + + if (x < 0 || y < 0) return NULL; + + link = tpu_map_link_pos(map, x, y); + if (!*link) return NULL; + + return (*link)->tpu; } diff --git a/tpu.h b/tpu.h @@ -5,38 +5,57 @@ #include <stdlib.h> #define TPU_MAP_BUCKETS 64 +#define LABEL_MAP_BUCKETS 64 +#define TPU_MAX_INST 256 -enum tpu_mode { - MODE_IDLE, MODE_RUN, MODE_READ, MODE_WRITE +/* enum order is important ! */ + +enum tpu_status { + STATUS_IDLE, STATUS_RUN, STATUS_READ, STATUS_WRITE }; enum tpu_inst_type { INST_NOP, INST_MOV, INST_SWP, INST_SAV, INST_ADD, INST_SUB, INST_NEG, INST_JMP, - INST_JEZ, INST_JNZ, INST_JLZ, INST_JRO + INST_JEZ, INST_JNZ, INST_JGZ, INST_JLZ, + INST_JRO }; enum tpu_inst_op_type { - OP_LIT, OP_UP, OP_DOWN, OP_LEFT, OP_RIGHT, - OP_ACC, OP_BAK, OP_NIL, OP_ANY, OP_LAST + OP_ACC, OP_BAK, OP_NIL, OP_LEFT, OP_RIGHT, + OP_UP, OP_DOWN, OP_ANY, OP_LAST, OP_LIT, OP_LABEL }; enum tpu_port_dir { - DIR_UP, DIR_RIGHT, DIR_DOWN, DIR_LEFT + DIR_LEFT, DIR_RIGHT, DIR_UP, DIR_DOWN }; enum tpu_port_type { PORT_IN = 0b01, PORT_OUT = 0b10, PORT_BIDI = 0b11 }; +struct label_map_link { + char *label; + size_t pc; + struct label_map_link *next; +}; + +struct label_map { + struct label_map_link *buckets[LABEL_MAP_BUCKETS]; +}; + struct tpu_inst_op { enum tpu_inst_op_type type; - uint8_t lit; + union { + uint8_t lit; + char *label; + }; }; struct tpu_inst { enum tpu_inst_type type; struct tpu_inst_op ops[2]; + uint8_t opcnt; }; struct tpu_port { @@ -45,18 +64,19 @@ struct tpu_port { struct tpu_port *dst_port; bool write, read; uint8_t val; + bool attached; }; struct tpu { + struct label_map labels; struct tpu_port ports[4]; uint8_t acc, bak; - int x, y; + size_t x, y; + int last; size_t pc; - struct inst *inst; - size_t inst_cap; - size_t inst_cnp; - + struct tpu_inst insts[TPU_MAX_INST]; + size_t inst_cnt; }; struct tpu_map_link { @@ -69,14 +89,21 @@ struct tpu_map { struct tpu_map_link *buckets[TPU_MAP_BUCKETS]; }; +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_init(struct tpu *tpu); -void tpu_add_inst(struct tpu *tpu, enum tpu_inst_type inst, - int op1, uint8_t op1_lit, int op2, uint8_t op2_lit); -void tpu_step(struct tpu *tpu, uint8_t *up, uint8_t *right, - uint8_t *down, uint8_t *left); void tpu_deinit(struct tpu *tpu); +struct tpu_inst *tpu_current_inst(struct tpu *tpu); +void tpu_init_ports(struct tpu *tpu, struct tpu_map *map); +bool tpu_add_inst(struct tpu *tpu, enum tpu_inst_type inst, + int op1, uint8_t op1_lit, int op2, uint8_t op2_lit); +enum tpu_status tpu_exec(struct tpu *tpu, struct tpu_inst *inst); +void tpu_step(struct tpu *tpu); void tpu_map_init(struct tpu_map *map); -void tpu_map_add(struct tpu_map *map, struct tpu *tpu); -struct tpu *tpu_map_get(int x, int y); 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); diff --git a/util.c b/util.c @@ -0,0 +1,25 @@ +#include "util.h" + +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <stdlib.h> + +void +die(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + fprintf(stderr, "%s: ", progname); + vfprintf(stderr, fmt, ap); + if (*fmt && fmt[strlen(fmt)-1] == ':') { + fputc(' ', stderr); + perror(NULL); + } else { + fputc('\n', stderr); + } + va_end(ap); + + exit(1); +} diff --git a/util.h b/util.h @@ -0,0 +1,7 @@ +#pragma once + +__attribute__((noreturn)) +__attribute__((format(printf, 1, 2))) +void die(const char *fmt, ...); + +extern const char *progname;