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:
M | Makefile | | | 4 | ++-- |
M | test/test.asm | | | 4 | ++-- |
M | tis-as.c | | | 145 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------ |
M | tis-curses.c | | | 45 | ++++++++++++++------------------------------- |
M | tpu.c | | | 512 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- |
M | tpu.h | | | 63 | +++++++++++++++++++++++++++++++++++++++++++++------------------ |
A | util.c | | | 25 | +++++++++++++++++++++++++ |
A | util.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;