tis100

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

commit 5ff5bb25cc864e7fa06aa8fed0c4ec92e99f103a
parent 70d48f0db2ff618436df7c8c52ead5a0701fd7ab
Author: Louis Burda <quent.burda@gmail.com>
Date:   Mon, 25 Dec 2023 01:46:55 +0100

Make mode/state behaviour compatible with game

Diffstat:
Masm.c | 40+++++++++++++++++++++++++++++-----------
Masm.h | 4++--
Mtis100-curses.c | 88++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Mtis100.c | 21++++++++++++++++-----
Mtpu.c | 256+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Mtpu.h | 47++++++++++++++++++++++++++++++++---------------
6 files changed, 286 insertions(+), 170 deletions(-)

diff --git a/asm.c b/asm.c @@ -250,6 +250,7 @@ tok_next(struct asm_tokenizer *tok) } else if (asm_is_lit(s)) { return TOK_LIT; } else if (*s == '#') { + tok->tokstr = tok->linebuf + tok->off; tok->off += strlen(tok->linebuf + tok->off); return TOK_COMMENT; } else if (len && strchr(TEXTALPH, *s) @@ -321,7 +322,7 @@ tpu_validate(struct tpu *tpu) } void -tis_load(struct tis *tis, const char *filepath) +tis_load_asm(struct tis *tis, const char *filepath) { struct tpu_io_port *io_port; struct asm_tokenizer tokenizer; @@ -338,9 +339,6 @@ tis_load(struct tis *tis, const char *filepath) size_t len; char c; - tis_deinit(tis); - tis_init(tis); - tokenizer.filepath = filepath; tokenizer.file = fopen(filepath, "r"); if (!tokenizer.file) die("load: fopen '%s':", filepath); @@ -452,6 +450,8 @@ tis_load(struct tis *tis, const char *filepath) tokenizer.lineno-1); break; case TOK_COMMENT: + if (tpu && !strcmp(tokenizer.tokstr, "DISABLED")) + tpu->disabled = true; tok_next_in(&tokenizer, TOK_NL, -1); break; case TOK_LABEL: @@ -459,7 +459,7 @@ tis_load(struct tis *tis, const char *filepath) die("load: line %lu, label too long", tokenizer.lineno); if (!label_map_add(&tpu->label_map, - tokenizer.tokstr, tpu->inst_cnt)) + tokenizer.tokstr, (int) tpu->inst_cnt)) die("load: line %lu, duplicate label (pos)", tokenizer.lineno); tpu->label_cnt += 1; @@ -491,11 +491,9 @@ tis_load(struct tis *tis, const char *filepath) dir_reprs[io_port->dir]); } port->attached = true; - port->dst_tpu = NULL; port->dst_port = &io_port->port; port->type = io_port->type; io_port->port.attached = true; - io_port->port.dst_tpu = link->tpu; io_port->port.dst_port = port; } } @@ -525,13 +523,35 @@ disallowed: } void -tis_load_io(struct tis *tis, const char **argv) +tis_load(struct tis *tis, const char **argv) { const char **arg; int n, i; char c; - for (arg = argv; *arg; arg++) { + if (!*argv) die("missing argv[0]"); + + for (arg = argv + 1; *arg; arg++) { + if (!strcmp(*arg, "-h") || !strcmp(*arg, "--help")) { + fprintf(stderr, "Usage: %s [-h] [-s] " + "[--in.X IN].. [--out.X OUT].. ASM\n", argv[0]); + } else if (!strcmp(*arg, "-s") || !strcmp(*arg, "--stats")) { + tis->show_stats = true; + } else if (sscanf(*arg, "--in.%c%n", &c, &n) == 1 && n == 6) { + /* after tis_load_asm.. */ + arg++; + } else if (sscanf(*arg, "--out.%c%n", &c, &n) == 1 && n == 7) { + /* after tis_load_asm.. */ + arg++; + } else { + if (tis->asm_init) + die("multiple asm files"); + tis_load_asm(tis, *arg); + tis->asm_init = true; + } + } + + for (arg = argv + 1; *arg; arg++) { if (sscanf(*arg, "--in.%c%n", &c, &n) == 1 && n == 6) { i = asm_port_char_to_index(c); if (!tis->in_ports[i]) @@ -548,8 +568,6 @@ tis_load_io(struct tis *tis, const char **argv) if (!tis->out_ports[i]->file) die("fopen '%s':", *arg); setvbuf(tis->out_ports[i]->file, NULL, _IONBF, 0); - } else { - die("parse: unknown cmdline option '%s'", *arg); } } } diff --git a/asm.h b/asm.h @@ -5,5 +5,5 @@ #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); -void tis_load_io(struct tis *tis, const char **argv); +void tis_load_asm(struct tis *tis, const char *filepath); +void tis_load(struct tis *tis, const char **argv); diff --git a/tis100-curses.c b/tis100-curses.c @@ -37,8 +37,8 @@ enum { }; enum { - COLOR_HEADING, - COLOR_VAL + THEME_DEFAULT, + THEME_DISABLED }; static const char *mode_repr[] = { @@ -182,6 +182,7 @@ tui_draw_tpu(struct tpu *tpu) int idle; attr = (tpu_sel == tpu && input_mode == TPU_NAV) ? A_BOLD : 0; + attr |= tpu->disabled ? COLOR_PAIR(THEME_DISABLED) : COLOR_PAIR(THEME_DEFAULT); sx = tpu_pos_x(tpu); sy = tpu_pos_y(tpu); @@ -189,7 +190,12 @@ tui_draw_tpu(struct tpu *tpu) WACS_D_ULCORNER, WACS_D_URCORNER, WACS_D_LLCORNER, WACS_D_LRCORNER); - if (tpu->inst_cnt > 0) { + if (tpu->disabled) { + tui_draw_text(sx + 4, sy + 1 + 1, attr | A_REVERSE, " "); + tui_draw_text(sx + 4, sy + 1 + 3, attr | A_BOLD, "COMMUNICATION"); + tui_draw_text(sx + 4, sy + 1 + 4, attr | A_BOLD, " FAILURE "); + tui_draw_text(sx + 4, sy + 1 + 6, attr | A_REVERSE, " "); + } else 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; ) { @@ -219,98 +225,98 @@ tui_draw_tpu(struct tpu *tpu) 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_lit(x + 1, y + 2, 0, tpu->acc); + tui_draw_text(x + 2, y + 1, attr | A_BOLD, "ACC"); + tui_draw_lit(x + 1, y + 2, attr, 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_lit(x + 1, y + 2, 0, tpu->bak); + tui_draw_text(x + 2, y + 1, attr | A_BOLD, "BAK"); + tui_draw_lit(x + 1, y + 2, attr, 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"); + tui_draw_text(x + 2, y + 1, attr | A_BOLD, "LST"); if (tpu->last < 0) { - tui_draw_text(x + 2, y + 2, 0, "N/A"); + tui_draw_text(x + 2, y + 2, attr, "N/A"); } else { - tui_draw_wch(x + 2, y + 2, 0, + tui_draw_wch(x + 2, y + 2, attr, dir_to_arrow((enum tpu_port_dir) tpu->last)); - tui_draw_wch(x + 3, y + 2, 0, + tui_draw_wch(x + 3, y + 2, attr, dir_to_arrow((enum tpu_port_dir) tpu->last)); - tui_draw_wch(x + 4, y + 2, 0, + tui_draw_wch(x + 4, y + 2, attr, 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_text(x + 2, y + 1, attr | A_BOLD, "MOD"); + tui_draw_text(x + 2, y + 2, attr, "%s", mode_repr[tpu->mode]); tui_draw_box(x, (y += TPU_INFO_H - 1), w, h, attr, WACS_D_LTEE, WACS_D_RTEE, WACS_D_BTEE, WACS_D_RTEE); - tui_draw_text(x + 2, y + 1, A_BOLD, "IDL"); + tui_draw_text(x + 2, y + 1, attr | 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); + idle = 0; + tui_draw_text(x + 2, y + 2, attr, "%03i", idle); tui_draw_box(x, (y += TPU_INFO_H - 1), w, 2, attr, WACS_D_LTEE, WACS_D_RTEE, WACS_D_BTEE, WACS_D_LRCORNER); if (tpu->ports[DIR_LEFT].attached) { port = &tpu->ports[DIR_LEFT]; - if (port->in_set) + if (port->avail) tui_draw_lit(sx - 4, sy + 6, A_BOLD, port->in); if (port->type & PORT_IN) tui_draw_wch(sx - 1, sy + 7, - port->in_set ? A_BOLD : 0, WACS_RARROW); + port->avail ? A_BOLD : 0, WACS_RARROW); if (port->type & PORT_OUT) tui_draw_wch(sx - 1, sy + 8, - port->out_set ? A_BOLD : 0, WACS_LARROW); - if (port->out_set) + port->writing ? A_BOLD : 0, WACS_LARROW); + if (port->writing) tui_draw_lit(sx - 4, sy + 10, A_BOLD, port->out); } if (tpu->ports[DIR_RIGHT].attached) { port = &tpu->ports[DIR_RIGHT]; - if (port->out_set) + if (port->writing) tui_draw_lit(sx + TPU_W, sy + 5, A_BOLD, port->out); if (port->type & PORT_OUT) tui_draw_wch(sx + TPU_W, sy + 7, - port->out_set ? A_BOLD : 0, WACS_RARROW); + port->writing ? A_BOLD : 0, WACS_RARROW); if (port->type & PORT_IN) tui_draw_wch(sx + TPU_W, sy + 8, - port->in_set ? A_BOLD : 0, WACS_LARROW); - if (port->in_set) + port->avail ? A_BOLD : 0, WACS_LARROW); + if (port->avail) tui_draw_lit(sx + TPU_W, sy + 9, A_BOLD, port->in); } if (tpu->ports[DIR_UP].attached) { port = &tpu->ports[DIR_UP]; - if (port->out_set) + if (port->writing) tui_draw_lit(sx + 8, sy - 1, A_BOLD, port->out); if (port->type & PORT_OUT) tui_draw_wch(sx + 13, sy - 1, - port->out_set ? A_BOLD : 0, WACS_UARROW); + port->writing ? A_BOLD : 0, WACS_UARROW); if (port->type & PORT_IN) tui_draw_wch(sx + 15, sy - 1, - port->in_set ? A_BOLD : 0, WACS_DARROW); - if (port->in_set) + port->avail ? A_BOLD : 0, WACS_DARROW); + if (port->avail) tui_draw_lit(sx + 16, sy - 1, A_BOLD, port->in); } if (tpu->ports[DIR_DOWN].attached) { port = &tpu->ports[DIR_DOWN]; - if (port->in_set) + if (port->avail) tui_draw_lit(sx + 8, sy + TPU_H, A_BOLD, port->in); if (port->type & PORT_IN) tui_draw_wch(sx + 13, sy + TPU_H, - port->in_set ? A_BOLD : 0, WACS_UARROW); + port->avail ? A_BOLD : 0, WACS_UARROW); if (port->type & PORT_OUT) tui_draw_wch(sx + 15, sy + TPU_H, - port->out_set ? A_BOLD : 0, WACS_DARROW); - if (port->out_set) + port->writing ? A_BOLD : 0, WACS_DARROW); + if (port->writing) tui_draw_lit(sx + 16, sy + TPU_H, A_BOLD, port->out); } } @@ -404,7 +410,6 @@ handlekey(int key) scrx += 4; break; case 's': - tis_communicate(&tis); tis_step(&tis); break; } @@ -418,8 +423,8 @@ handlekey(int key) 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; + if (tpu_sel && tpu_sel->ports[dir].dst_port->tpu) + tpu_sel = tpu_sel->ports[dir].dst_port->tpu; tui_seek(tpu_sel, MID, MID); break; } @@ -442,12 +447,14 @@ first_tpu(void) static void reset(int ifd, int argc, const char **argv, bool watch) { - tis_load(&tis, argv[1]); - tis_load_io(&tis, argv + 2); + tis_deinit(&tis); + tis_init(&tis); + tis_load(&tis, argv); - if (watch) + if (watch) { if (inotify_add_watch(ifd, argv[1], IN_MODIFY) < 0) die("inotify_add_watch '%s':", argv[1]); + } tpu_sel = first_tpu(); } @@ -474,6 +481,8 @@ main(int argc, const char **argv) noecho(); keypad(stdscr, TRUE); start_color(); + init_pair(THEME_DEFAULT, COLOR_WHITE, COLOR_BLACK); + init_pair(THEME_DISABLED, COLOR_RED, COLOR_BLACK); curs_set(0); tui_resize(); timeout(TIMEOUT); @@ -510,6 +519,7 @@ main(int argc, const char **argv) case 'r': reset(ifd, argc, argv, false); break; + case 'q': case KEY_CTRL('c'): quit = true; break; diff --git a/tis100.c b/tis100.c @@ -24,16 +24,18 @@ io_active(struct tis *tis) for (i = 0; i < TIS_MAX_IO_PORTS; i++) { io_port = tis->in_ports[i]; if (!io_port || !io_port->file) continue; - if (!feof(io_port->file) && io_port->port.attached - && io_port->port.dst_port->reading) + if (io_port->port.attached && !io_port->port.writing + && !feof(io_port->file)) return true; } + return false; } int main(int argc, const char **argv) { + struct tis_stats stats; bool idle, prev_idle; if (argc < 2) { @@ -43,15 +45,24 @@ main(int argc, const char **argv) tis_init(&tis); - tis_load(&tis, argv[1]); - tis_load_io(&tis, argv + 2); + tis_load(&tis, argv); idle = false; while (!idle || !prev_idle || io_active(&tis)) { - tis_communicate(&tis); prev_idle = idle; idle = !tis_step(&tis); } + tis.steps -= 1; /* remove last idle step */ + + if (tis.show_stats) { + stats = tis_gen_stats(&tis); + printf("=== stats ===\n"); + printf(" cycles: %zu\n", stats.steps); + printf(" blocks: %zu\n", stats.blocks); + printf(" insts: %zu\n", stats.blocks); + printf(" idle: %2.1f%%\n", stats.idle * 100); + printf("=============\n"); + } tis_deinit(&tis); } diff --git a/tpu.c b/tpu.c @@ -2,6 +2,7 @@ #include "util.h" #include <ctype.h> +#include <assert.h> #include <stdio.h> #include <stdbool.h> #include <stddef.h> @@ -11,7 +12,7 @@ const char *dir_reprs[] = { "LEFT", "RIGHT", "UP", "DOWN" }; -const char *status_reprs[] = { +const char *mode_reprs[] = { "IDLE", "RUN", "READ", "WRITE" }; @@ -129,18 +130,18 @@ label_map_get(struct label_map *map, const char *name) } void -tpu_port_init(struct tpu_port *port) +tpu_port_init(struct tpu_port *port, struct tpu *tpu) { + port->tpu = tpu; port->attached = false; port->dst_port = NULL; - port->dst_tpu = NULL; port->type = PORT_BIDI; - port->clr_post_run = false; + port->reset_in = false; port->reading = false; port->writing = false; - port->in_set = false; + port->avail = false; port->in = -1; - port->out_set = false; + port->writing = false; port->out = -1; } @@ -151,10 +152,35 @@ tpu_port_deinit(struct tpu_port *port) } void +tpu_port_update(struct tpu_port *port) +{ + struct tpu *tpu; + + if (!port->attached) return; + + if (port->writing && !port->dst_port->avail) { + port->dst_port->in = port->out; + port->dst_port->avail = true; + } + + if (port->reset_in && (!port->tpu || !port->tpu->idle)) { + port->avail = false; + port->dst_port->writing = false; + port->reset_in = false; + if ((tpu = port->dst_port->tpu)) { + /* hacky: only allow mode to be RUN after receive */ + tpu->mode = MODE_RUN; + tpu->idle = false; + if (++tpu->pc >= tpu->inst_cnt) tpu->pc = 0; + } + } +} + +void tpu_io_port_init(struct tpu_io_port *io_port, char c, enum tpu_port_dir dir, enum tpu_port_type type, int x, int y) { - tpu_port_init(&io_port->port); + tpu_port_init(&io_port->port, NULL); io_port->type = type; io_port->file = NULL; io_port->dir = dir; @@ -175,7 +201,10 @@ tpu_init(struct tpu *tpu) { size_t i; - tpu->status = STATUS_IDLE; + tpu->disabled = false; + + tpu->mode = MODE_IDLE; + tpu->idle = true; tpu->x = 0; tpu->y = 0; @@ -193,7 +222,7 @@ tpu_init(struct tpu *tpu) tpu->last = -1; tpu->io_port = -1; for (i = 0; i < 4; i++) - tpu_port_init(&tpu->ports[i]); + tpu_port_init(&tpu->ports[i], tpu); } void @@ -226,6 +255,7 @@ tpu_init_ports(struct tpu *tpu, struct tpu_map *map) for (i = 0; i < 4; i++) { if (tpu->ports[i].attached) continue; + if (tpu->disabled) continue; switch (i) { case DIR_LEFT: @@ -247,45 +277,25 @@ tpu_init_ports(struct tpu *tpu, struct tpu_map *map) } neighbor = tpu_map_get(map, x, y); - if (neighbor) { + if (neighbor && !neighbor->disabled) { odir = opposite_dir((enum tpu_port_dir) i); tpu->ports[i].attached = true; - tpu->ports[i].dst_tpu = neighbor; tpu->ports[i].dst_port = &neighbor->ports[odir]; neighbor->ports[odir].attached = true; - neighbor->ports[odir].dst_tpu = tpu; neighbor->ports[odir].dst_port = &tpu->ports[i]; } } } void -tpu_update_ports(struct tpu *tpu) +tpu_update(struct tpu *tpu) { struct tpu_port *port; int i; for (i = 0; i < 4; i++) { port = &tpu->ports[i]; - if (!port->attached) continue; - if (port->out_set && !port->dst_port->in_set) { - port->dst_port->in = port->out; - port->dst_port->in_set = true; - port->dst_port->reading = false; - port->out_set = false; - port->writing = false; - } - if (port->dst_port->out_set && !port->in_set) { - port->in = port->dst_port->out; - port->in_set = true; - port->reading = false; - port->dst_port->out_set = false; - port->dst_port->writing = false; - } - if (tpu->status == STATUS_RUN && port->clr_post_run) { - port->in_set = false; - port->clr_post_run = false; - } + tpu_port_update(port); } } @@ -342,38 +352,40 @@ tpu_add_inst(struct tpu *tpu, enum tpu_inst_type inst_type, return &tpu->insts[tpu->inst_cnt++]; } -/* tpu can always write to an empty port (->out), but only - * 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 */ +/* tpu can only read values if there is a writer attached and offering a + * value (->dst_port->out) -- which is cleared on successful read */ static bool tpu_port_read(struct tpu *tpu, enum tpu_port_dir dir, int *lit) { - struct tpu_port *port; + struct tpu_port *port = &tpu->ports[dir]; - port = &tpu->ports[dir]; - port->clr_post_run = true; - if (!port->in_set) { + if (!port->avail) { port->reading = true; return false; } + *lit = port->in; + port->reset_in = true; + return true; } +/* tpu can always write values (->out) but only finish writing once the + * written value is read and cleared by the reader (->dst_port->in) */ + static bool tpu_port_write(struct tpu *tpu, enum tpu_port_dir dir, int lit) { - struct tpu_port *port; + struct tpu_port *port = &tpu->ports[dir]; - port = &tpu->ports[dir]; - if (port->out_set) { - port->writing = true; + if (port->writing) { return false; } - port->out_set = true; + + port->writing = true; port->out = lit; + return true; } @@ -447,117 +459,106 @@ tpu_exec_put(struct tpu *tpu, struct tpu_inst_op *op, int lit) } } -enum tpu_status -tpu_exec_mov(struct tpu *tpu, struct tpu_inst *inst) -{ - int lit; - - if (inst->type != INST_MOV) abort(); - - if (!tpu_exec_get(tpu, &inst->ops[0], &lit)) - return STATUS_READ; - - if (!tpu_exec_put(tpu, &inst->ops[1], lit)) - return STATUS_WRITE; - - return STATUS_RUN; -} - -enum tpu_status +enum tpu_mode tpu_exec(struct tpu *tpu, struct tpu_inst *inst) { - enum tpu_status status; int lit; int val; + tpu->idle = false; switch (inst->type) { case INST_NOP: tpu->pc += 1; - return STATUS_RUN; + return MODE_RUN; case INST_MOV: - status = tpu_exec_mov(tpu, inst); - if (status == STATUS_RUN) - tpu->pc += 1; - return status; + if (tpu->mode != MODE_WRITE) { + if (!tpu_exec_get(tpu, &inst->ops[0], &lit)) { + tpu->idle = true; + return MODE_READ; + } + } + /* tpu->idle/mode fixed up via tpu->reset_in if value is read */ + if (!tpu_exec_put(tpu, &inst->ops[1], lit)) + tpu->idle = true; + return MODE_WRITE; case INST_SWP: lit = tpu->acc; tpu->acc = tpu->bak; tpu->bak = lit; tpu->pc += 1; - return STATUS_RUN; + return MODE_RUN; case INST_SAV: tpu->bak = tpu->acc; tpu->pc += 1; - return STATUS_RUN; + return MODE_RUN; case INST_ADD: - if (!tpu_exec_get(tpu, &inst->ops[0], &val)) - return STATUS_READ; + if (!tpu_exec_get(tpu, &inst->ops[0], &val)) { + tpu->idle = true; + return MODE_READ; + } tpu->acc = MIN(MAX(tpu->acc + val, -999), 999); tpu->pc += 1; - return STATUS_RUN; + return MODE_RUN; case INST_SUB: if (!tpu_exec_get(tpu, &inst->ops[0], &val)) - return STATUS_READ; + return MODE_READ; tpu->acc = MIN(MAX(tpu->acc - val, -999), 999); tpu->pc += 1; - return STATUS_RUN; + return MODE_RUN; case INST_NEG: tpu->acc = -tpu->acc; tpu->pc += 1; - return STATUS_RUN; + return MODE_RUN; case INST_JMP: tpu_jmp_label(tpu, inst->ops[0].val.label); - return STATUS_RUN; + return MODE_RUN; case INST_JEZ: if (tpu->acc == 0) tpu_jmp_label(tpu, inst->ops[0].val.label); else tpu->pc += 1; - return STATUS_RUN; + return MODE_RUN; case INST_JNZ: if (tpu->acc != 0) tpu_jmp_label(tpu, inst->ops[0].val.label); else tpu->pc += 1; - return STATUS_RUN; + return MODE_RUN; case INST_JGZ: if (tpu->acc > 0) tpu_jmp_label(tpu, inst->ops[0].val.label); else tpu->pc += 1; - return STATUS_RUN; + return MODE_RUN; case INST_JLZ: if (tpu->acc < 0) tpu_jmp_label(tpu, inst->ops[0].val.label); else tpu->pc += 1; - return STATUS_RUN; + return MODE_RUN; case INST_JRO: tpu->pc += inst->ops[0].val.lit; if (tpu->pc < 0) tpu->pc = 0; if (tpu->pc >= tpu->inst_cnt) tpu->pc = (int) tpu->inst_cnt - 1; - return STATUS_RUN; + return MODE_RUN; default: abort(); } } -enum tpu_status +void tpu_step(struct tpu *tpu) { + int prev_mode; + + prev_mode = tpu->mode; if (tpu->pc < tpu->inst_cnt) { - tpu->status = tpu_exec(tpu, &tpu->insts[tpu->pc]); + tpu->mode = tpu_exec(tpu, &tpu->insts[tpu->pc]); if (tpu->pc >= tpu->inst_cnt) tpu->pc = 0; } else { - tpu->status = STATUS_IDLE; + tpu->mode = MODE_IDLE; } - - tpu->steps += 1; - if (tpu->status != STATUS_RUN) - tpu->idle_steps += 1; - - return tpu->status; } void @@ -633,6 +634,9 @@ tis_init(struct tis *tis) tpu_map_init(&tis->tpu_map); memset(&tis->in_ports, 0, TIS_MAX_IO_PORTS * sizeof(void *)); memset(&tis->out_ports, 0, TIS_MAX_IO_PORTS * sizeof(void *)); + tis->steps = 0; + tis->show_stats = false; + tis->asm_init = false; } void @@ -660,19 +664,43 @@ tis_step(struct tis *tis) bool running; size_t i; + tis_communicate(tis); + 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); + tpu_step(link->tpu); } for (i = 0; i < TPU_MAP_BUCKETS; i++) { link = tis->tpu_map.buckets[i]; - for (; link; link = link->next) - tpu_update_ports(link->tpu); + for (; link; link = link->next) { + tpu_update(link->tpu); + running |= !link->tpu->idle; + } + } + + for (i = 0; i < TPU_MAP_BUCKETS; i++) { + link = tis->tpu_map.buckets[i]; + for (; link; link = link->next) { + if (link->tpu->pc < link->tpu->inst_cnt) { + link->tpu->steps += 1; + if (link->tpu->idle) + link->tpu->idle_steps += 1; + } + } + } + + for (i = 0; i < TIS_MAX_IO_PORTS; i++) { + if (tis->in_ports[i]) + tpu_port_update(&tis->in_ports[i]->port); + if (tis->out_ports[i]) + tpu_port_update(&tis->out_ports[i]->port); } + tis->steps += 1; + return running; } @@ -686,7 +714,7 @@ tis_communicate(struct tis *tis) for (i = 0; i < TIS_MAX_IO_PORTS; i++) { io_port = tis->in_ports[i]; if (!io_port) continue; - if (!io_port->port.attached || io_port->port.out_set) + if (!io_port->port.attached || io_port->port.writing) continue; if (!io_port->file) continue; while ((s = fgets(buf, sizeof(buf), io_port->file))) { @@ -696,7 +724,7 @@ tis_communicate(struct tis *tis) if (!s || *s != '\0') die("communicate: invalid input '%s'", buf); io_port->port.out = val; - io_port->port.out_set = true; + io_port->port.writing = true; break; } } @@ -704,10 +732,42 @@ tis_communicate(struct tis *tis) for (i = 0; i < TIS_MAX_IO_PORTS; i++) { io_port = tis->out_ports[i]; if (!io_port) continue; - if (!io_port->port.attached || !io_port->port.in_set) + if (!io_port->port.attached || !io_port->port.avail) continue; if (io_port->file) fprintf(io_port->file, "%i\n", io_port->port.in); - io_port->port.in_set = false; + io_port->port.reset_in = true; + tpu_port_update(&io_port->port); + } +} + +struct tis_stats +tis_gen_stats(struct tis *tis) +{ + struct tis_stats stats; + struct tpu_map_link *link; + size_t idle_steps; + size_t all_steps; + int i; + + stats.steps = tis->steps; + stats.blocks = 0; + stats.insts = 0; + stats.idle = 0; + + all_steps = 0; + idle_steps = 0; + for (i = 0; i < TPU_MAP_BUCKETS; i++) { + link = tis->tpu_map.buckets[i]; + for (; link; link = link->next) { + stats.insts += link->tpu->inst_cnt; + idle_steps += link->tpu->idle_steps; + all_steps += link->tpu->steps; + stats.blocks += (link->tpu->inst_cnt > 0); + } } + + stats.idle = (float) idle_steps / (float) all_steps; + + return stats; } diff --git a/tpu.h b/tpu.h @@ -11,10 +11,9 @@ #define TPU_MAX_INST_LEN 18 #define TIS_MAX_IO_PORTS 36 -/* enum order is important ! */ - -enum tpu_status { - STATUS_IDLE, STATUS_RUN, STATUS_READ, STATUS_WRITE +/* what tpu will attempt next (order important) */ +enum tpu_mode { + MODE_IDLE, MODE_RUN, MODE_READ, MODE_WRITE }; enum tpu_inst_type { @@ -65,18 +64,26 @@ struct tpu_inst { }; struct tpu_port { - struct tpu *dst_tpu; + struct tpu *tpu; enum tpu_port_type type; + + /* NOTE: dont access dst_port during execution, + * values are transfered in post execution phase */ struct tpu_port *dst_port; - bool clr_post_run; - bool reading, writing; bool attached; - bool in_set, out_set; + int in, out; + bool avail, reading, writing; + + bool reset_in; }; struct tpu { - enum tpu_status status; + enum tpu_mode mode; + bool idle; + + bool disabled; + int x, y; struct tpu_port ports[4]; @@ -91,7 +98,7 @@ struct tpu { struct label_map label_map; struct tpu_inst insts[TPU_MAX_INST_CNT]; - int inst_cnt, label_cnt; + size_t inst_cnt, label_cnt; }; struct tpu_map_link { @@ -117,6 +124,16 @@ struct tis { struct tpu_map tpu_map; struct tpu_io_port *in_ports[TIS_MAX_IO_PORTS]; struct tpu_io_port *out_ports[TIS_MAX_IO_PORTS]; + size_t steps; + bool show_stats; + bool asm_init; +}; + +struct tis_stats { + size_t steps; + size_t blocks; + size_t insts; + float idle; }; void label_map_init(struct label_map *map); @@ -124,7 +141,7 @@ void label_map_deinit(struct label_map *map); bool label_map_add(struct label_map *map, const char *name, int pc); int label_map_get(struct label_map *map, const char *name); -void tpu_port_init(struct tpu_port *port); +void tpu_port_init(struct tpu_port *port, struct tpu *tpu); void tpu_port_deinit(struct tpu_port *port); void tpu_io_port_init(struct tpu_io_port *io_port, char c, @@ -142,9 +159,8 @@ bool tpu_set_inst(struct tpu *tpu, int pc, enum tpu_inst_type inst, struct tpu_inst *tpu_add_inst(struct tpu *tpu, enum tpu_inst_type inst, unsigned opcnt, struct tpu_inst_op op1, struct tpu_inst_op op2); 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); -enum tpu_status tpu_step(struct tpu *tpu); +enum tpu_mode 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_deinit(struct tpu_map *map); @@ -155,8 +171,9 @@ void tis_init(struct tis *tis); void tis_deinit(struct tis *tis); bool tis_step(struct tis *tis); void tis_communicate(struct tis *tis); +struct tis_stats tis_gen_stats(struct tis *tis); extern const char *dir_reprs[]; -extern const char *status_reprs[]; +extern const char *mode_reprs[]; extern const char *inst_reprs[]; extern const char *op_reprs[];