commit 7d73b738a5703d5263a84dcbe3564e2267af6804
parent 41760436d528552d64122bb0c837f4d8274a0bdd
Author: Louis Burda <quent.burda@gmail.com>
Date: Tue, 25 Jul 2023 05:56:08 +0200
Improve parsing, make stdin / stdout port optional
Diffstat:
M | asm.c | | | 110 | +++++++++++++++++++++++++++++++++++++++++++------------------------------------ |
A | test/and.asm | | | 27 | +++++++++++++++++++++++++++ |
A | test/xor.asm | | | 26 | ++++++++++++++++++++++++++ |
M | tis-as.c | | | 7 | ++++--- |
M | tis-curses.c | | | 48 | ++++++++++++++++++++++++++++++++++++------------ |
M | tpu.c | | | 48 | ++++++++++++++++++++---------------------------- |
M | tpu.h | | | 14 | ++++++-------- |
7 files changed, 179 insertions(+), 101 deletions(-)
diff --git a/asm.c b/asm.c
@@ -3,6 +3,7 @@
#include "tpu.h"
#include <stdarg.h>
+#include <stdbool.h>
#include <string.h>
#include <stdint.h>
@@ -80,6 +81,19 @@ is_lit(const char *str)
return v < 256;
}
+static bool
+is_int(const char *str)
+{
+ char *end;
+ long v;
+
+ v = strtol(str, &end, 10);
+ if (!end || *end) return false;
+ if (v < INT32_MIN || v > INT32_MAX) return false;
+
+ return true;
+}
+
static uint8_t
str_to_lit(const char *str)
{
@@ -100,6 +114,22 @@ tok_to_optype(enum asm_tok tok)
return tok - TOK_ACC + OP_ACC;
}
+struct tpu_inst_op
+tok_to_op(struct asm_tokenizer *tokenizer, enum asm_tok tok)
+{
+ struct tpu_inst_op op;
+
+ op.type = tok_to_optype(tok);
+ if (op.type == OP_LIT) {
+ op.val.lit = str_to_lit(tokenizer->tokstr);
+ } else if (op.type == OP_LABEL) {
+ op.val.label = strdup(tokenizer->tokstr);
+ if (!op.val.label) die("strdup:");
+ }
+
+ return op;
+}
+
static size_t
strlcat_op_name(char *buf, struct tpu_inst_op *op, size_t n)
{
@@ -184,9 +214,9 @@ tok_next(struct asm_tokenizer *tok)
&& strspn(s, NAMEALPH) == len-1 && s[len-1] == ':') {
s[len-1] = '\0';
return TOK_LABEL;
- } else if (*s == 'X' && atoi(s+1) > 0) {
+ } else if (*s == 'X' && is_int(s+1)) {
return TOK_XPOS;
- } else if (*s == 'Y' && atoi(s+1) > 0) {
+ } else if (*s == 'Y' && is_int(s+1)) {
return TOK_YPOS;
} else if (strchr(TEXTALPH, *s)
&& strspn(s, NAMEALPH) == strlen(s)) {
@@ -242,7 +272,7 @@ tpu_validate(struct tpu *tpu)
dst = label_map_get(&tpu->label_map,
tpu->insts[i].ops[0].val.label);
if (dst == TPU_MAX_INST)
- die("load: tpu X%lu Y%lu, label '%s' not defined",
+ die("load: tpu X%i Y%i, label '%s' not defined",
tpu->x, tpu->y,
tpu->insts[i].ops[0].val.label);
}
@@ -253,23 +283,21 @@ void
tis_load(struct tis *tis, const char *filepath)
{
struct asm_tokenizer tokenizer;
- int op1, op2;
- enum asm_tok op1_tok, op2_tok;
- union tpu_inst_op_val op1v, op2v;
+ struct tpu_inst_op op1, op2;
enum tpu_inst_type inst;
struct tpu *tpu = NULL;
struct tpu_map_link *link;
struct tpu_port *port;
+ bool stdin_set, stdout_set;
int stdin_x, stdin_y;
int stdout_x, stdout_y;
- enum asm_tok tok;
+ enum asm_tok tok, optok;
size_t i;
tis_deinit(tis);
tis_init(tis);
- stdin_x = stdin_y = 0;
- stdout_x = stdout_y = 0;
+ stdin_set = stdout_set = false;
tokenizer.filepath = filepath;
tokenizer.file = fopen(filepath, "r");
@@ -282,20 +310,22 @@ tis_load(struct tis *tis, const char *filepath)
while ((tok = tok_next(&tokenizer)) != TOK_EOF) {
switch (tok) {
case TOK_STDIN:
- if (tpu) goto disallowed;
+ if (tpu || stdin_set) 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);
+ stdin_set = true;
break;
case TOK_STDOUT:
- if (tpu) goto disallowed;
+ if (tpu || stdout_set) 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);
+ stdout_set = true;
break;
case TOK_TPU:
if (tpu) goto disallowed;
@@ -303,9 +333,9 @@ tis_load(struct tis *tis, const char *filepath)
if (!tpu) die("malloc:");
tpu_init(tpu);
tok_next_in(&tokenizer, TOK_XPOS, -1);
- tpu->x = (size_t) atoi(tokenizer.tokstr + 1);
+ tpu->x = atoi(tokenizer.tokstr + 1);
tok_next_in(&tokenizer, TOK_YPOS, -1);
- tpu->y = (size_t) atoi(tokenizer.tokstr + 1);
+ tpu->y = atoi(tokenizer.tokstr + 1);
tok_next_in(&tokenizer, TOK_NL, -1);
tpu_map_add(&tis->tpu_map, tpu);
break;
@@ -322,46 +352,32 @@ tis_load(struct tis *tis, const char *filepath)
if (!tpu) goto disallowed;
inst = tok_to_inst(tok);
- op1_tok = tok_next_in(&tokenizer, TOK_ACC, TOK_BAK,
+ optok = 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) {
- if (!tpu_add_inst(tpu, inst, -1, op1v, -1, op2v))
+ if (optok == TOK_NL) {
+ if (!tpu_add_inst(tpu, inst, 0, op1, op2))
die("load: line %lu, invalid instruction",
- tokenizer.lineno);
+ tokenizer.lineno-1);
break;
}
+ op1 = tok_to_op(&tokenizer, optok);
- op1 = (int) tok_to_optype(op1_tok);
- if (op1 == OP_LIT)
- op1v.lit = str_to_lit(tokenizer.tokstr);
- else if (op1 == OP_LABEL) {
- op1v.label = strdup(tokenizer.tokstr);
- if (!op1v.label) die("strdup:");
- }
-
- op2_tok = tok_next_in(&tokenizer, TOK_ACC, TOK_BAK,
+ optok = 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) {
- if (!tpu_add_inst(tpu, inst, op1, op1v, -1, op2v))
+ if (optok == TOK_NL) {
+ if (!tpu_add_inst(tpu, inst, 1, op1, op2))
die("load: line %lu, invalid instruction",
- tokenizer.lineno);
+ tokenizer.lineno-1);
break;
}
+ op2 = tok_to_op(&tokenizer, optok);
- op2 = (int) tok_to_optype(op2_tok);
- if (op2 == OP_LIT)
- op2v.lit = str_to_lit(tokenizer.tokstr);
- else if (op2 == OP_LABEL) {
- op2v.label = strdup(tokenizer.tokstr);
- if (!op2v.label) die("strdup:");
- }
-
- if (!tpu_add_inst(tpu, inst, op1, op1v, op2, op2v))
- die("load: line %lu, invalid instruction",
- tokenizer.lineno);
tok_next_in(&tokenizer, TOK_NL, -1);
+ if (!tpu_add_inst(tpu, inst, 2, op1, op2))
+ die("load: line %lu, invalid instruction",
+ tokenizer.lineno-1);
break;
case TOK_COMMENT:
tok_next_in(&tokenizer, TOK_NL, -1);
@@ -379,17 +395,11 @@ tis_load(struct tis *tis, const char *filepath)
}
}
- if (stdin_x == 0 || stdin_y == 0)
- die("load: stdin tpu not set");
-
- if (stdout_x == 0 || stdout_y == 0)
- die("load: stdout tpu not set");
-
for (i = 0; i < TPU_MAP_BUCKETS; i++) {
for (link = tis->tpu_map.buckets[i]; link; link = link->next) {
tpu_init_ports(link->tpu, &tis->tpu_map);
- if (link->x == stdin_x && link->y == stdin_y) {
+ if (stdin_set && link->x == stdin_x && link->y == stdin_y) {
port = &link->tpu->ports[DIR_UP];
if (port->attached)
die("load: stdin port in use");
@@ -402,7 +412,7 @@ tis_load(struct tis *tis, const char *filepath)
tis->stdin_port.dst_port = port;
}
- if (link->x == stdout_x && link->y == stdout_y) {
+ if (stdout_set && link->x == stdout_x && link->y == stdout_y) {
port = &link->tpu->ports[DIR_DOWN];
if (port->attached)
die("load: stdout port in use");
@@ -417,11 +427,11 @@ tis_load(struct tis *tis, const char *filepath)
}
}
- if (!tis->stdin_port.attached)
+ if (stdin_set && !tis->stdin_port.attached)
die("load: stdin tpu (X%i Y%i) not found",
stdin_x, stdin_y);
- if (!tis->stdout_port.attached)
+ if (stdout_set && !tis->stdout_port.attached)
die("load: stdout tpu (X%i Y%i) not found",
stdout_x, stdout_y);
diff --git a/test/and.asm b/test/and.asm
@@ -0,0 +1,27 @@
+stdout X1 Y1
+
+tpu X1 Y0
+ mov 0, DOWN
+ mov 1, DOWN
+ mov 0, DOWN
+ mov 1, DOWN
+ MOV UP, NIL
+end
+
+tpu X0 Y1
+ mov 0, RIGHT
+ mov 0, RIGHT
+ mov 1, RIGHT
+ mov 1, RIGHT
+ MOV UP, NIL
+end
+
+tpu X1 Y1
+ mov 2, ACC
+ sub UP
+ sub LEFT
+ jez H
+ mov 0, DOWN
+ jro 2
+H: mov 1, DOWN
+end
diff --git a/test/xor.asm b/test/xor.asm
@@ -0,0 +1,26 @@
+stdout X1 Y1
+
+tpu X1 Y0
+ mov 0, DOWN
+ mov 1, DOWN
+ mov 0, DOWN
+ mov 1, DOWN
+ MOV UP, NIL
+end
+
+tpu X0 Y1
+ mov 0, RIGHT
+ mov 0, RIGHT
+ mov 1, RIGHT
+ mov 1, RIGHT
+ MOV UP, NIL
+end
+
+tpu X1 Y1
+ MOV UP, ACC
+ sub LEFT
+ jnz H
+ mov 0, DOWN
+ jro 2
+H: mov 1, DOWN
+end
diff --git a/tis-as.c b/tis-as.c
@@ -50,13 +50,14 @@ main(int argc, const char **argv)
tis.stdin_port.out = -1;
idle = false;
- while (!idle || !prev_idle || tis.stdin_port.reading && !feof(tis_stdin)) {
- if (tis.stdin_port.out < 0) {
+ while (!idle || !prev_idle || tis.stdin_port.attached
+ && tis.stdin_port.reading && !feof(tis_stdin)) {
+ if (tis.stdin_port.attached && tis.stdin_port.out < 0) {
c = getc(tis_stdin);
if (c >= 0) tis.stdin_port.out = c;
}
- if (tis.stdout_port.in >= 0) {
+ if (tis.stdout_port.attached && tis.stdout_port.in >= 0) {
putc(tis.stdout_port.in, tis_stdout);
tis.stdout_port.in = -1;
}
diff --git a/tis-curses.c b/tis-curses.c
@@ -353,6 +353,7 @@ tui_seek(struct tpu *tpu, int dx, int dy)
if (maxy == -1 || y > maxy) maxy = y;
}
}
+ if (minx == -1 || miny == -1) return;
}
if (dx == MIN) scrx = minx - 2;
@@ -388,12 +389,12 @@ handlekey(int key)
scrx += 4;
break;
case 's':
- if (tis.stdin_port.out < 0) {
+ if (tis.stdin_port.attached && tis.stdin_port.out < 0) {
c = getc(tis_stdin);
if (c >= 0) tis.stdin_port.out = c;
}
- if (tis.stdout_port.in >= 0) {
+ if (tis.stdout_port.attached && tis.stdout_port.in >= 0) {
putc(tis.stdout_port.in, tis_stdout);
tis.stdout_port.in = -1;
}
@@ -411,7 +412,7 @@ handlekey(int key)
case KEY_LEFT:
case KEY_RIGHT:
dir = key_to_dir(key);
- if (tpu_sel->ports[dir].dst_tpu)
+ if (tpu_sel && tpu_sel->ports[dir].dst_tpu)
tpu_sel = tpu_sel->ports[dir].dst_tpu;
tui_seek(tpu_sel, MID, MID);
break;
@@ -419,13 +420,27 @@ handlekey(int key)
}
}
-void
-reset(int ifd, int argc, char **argv)
+static struct tpu *
+first_tpu(void)
+{
+ size_t i;
+
+ for (i = 0; i < TPU_MAP_BUCKETS; i++) {
+ if (tis.tpu_map.buckets[i])
+ return tis.tpu_map.buckets[i]->tpu;
+ }
+
+ return NULL;
+}
+
+static void
+reset(int ifd, int argc, char **argv, bool watch)
{
tis_load(&tis, argv[1]);
- if (inotify_add_watch(ifd, argv[1], IN_MODIFY) < 0)
- die("inotify_add_watch '%s':", argv[1]);
+ if (watch)
+ if (inotify_add_watch(ifd, argv[1], IN_MODIFY) < 0)
+ die("inotify_add_watch '%s':", argv[1]);
if (tis_stdin) fclose(tis_stdin);
tis_stdin = fopen(argv[2], "r");
@@ -435,7 +450,11 @@ reset(int ifd, int argc, char **argv)
tis_stdout = fopen(argv[3], "w+");
if (!tis_stdout) die("fopen '%s':", argv[3]);
- tpu_sel = tis.stdin_port.dst_tpu;
+ if (tis.stdin_port.attached) {
+ tpu_sel = tis.stdin_port.dst_tpu;
+ } else {
+ tpu_sel = first_tpu();
+ }
tui_seek(NULL, MID, MID);
}
@@ -471,7 +490,7 @@ main(int argc, char **argv)
ifd = inotify_init1(IN_NONBLOCK);
- reset(ifd, argc, argv);
+ reset(ifd, argc, argv, true);
quit = false;
while (!quit) {
@@ -479,7 +498,7 @@ main(int argc, char **argv)
if (len < 0 && errno != EAGAIN)
die("inotify_read:");
if (len >= 0) {
- reset(ifd, argc, argv);
+ reset(ifd, argc, argv, true);
show_reloaded = 1000 / TIMEOUT;
}
@@ -496,10 +515,15 @@ main(int argc, char **argv)
tui_seek(NULL, MID, MID);
break;
case 'i':
- tui_seek(tis.stdin_port.dst_tpu, MID, MID);
+ if (tis.stdin_port.attached)
+ tui_seek(tis.stdin_port.dst_tpu, MID, MID);
break;
case 'o':
- tui_seek(tis.stdout_port.dst_tpu, MID, MID);
+ if (tis.stdout_port.attached)
+ tui_seek(tis.stdout_port.dst_tpu, MID, MID);
+ break;
+ case 'r':
+ reset(ifd, argc, argv, false);
break;
case KEY_CTRL('c'):
quit = true;
diff --git a/tpu.c b/tpu.c
@@ -199,8 +199,7 @@ tpu_init_ports(struct tpu *tpu, struct tpu_map *map)
{
struct tpu *neighbor;
enum tpu_port_dir odir;
- size_t x, y;
- int i;
+ int x, y, i;
for (i = 0; i < 4; i++) {
switch (i) {
@@ -262,36 +261,30 @@ tpu_update_ports(struct tpu *tpu)
bool
tpu_set_inst(struct tpu *tpu, uint8_t pc, enum tpu_inst_type inst_type,
- int op1, union tpu_inst_op_val v1,
- int op2, union tpu_inst_op_val v2)
+ unsigned opcnt, struct tpu_inst_op op1, struct tpu_inst_op op2)
{
struct tpu_inst *inst;
inst = &tpu->insts[pc];
inst->type = inst_type;
-
- if (op2 >= 0) {
- inst->ops[1].type = (enum tpu_inst_op_type) op2;
- inst->ops[1].val = v1;
- inst->ops[0].type = (enum tpu_inst_op_type) op1;
- inst->ops[0].val = v2;
- inst->opcnt = 2;
- } else if (op1 >= 0) {
- inst->ops[0].type = (enum tpu_inst_op_type) op1;
- inst->ops[0].val = v1;
- inst->opcnt = 1;
- } else {
- inst->opcnt = 0;
- }
+ inst->opcnt = opcnt;
+ inst->ops[0] = op1;
+ inst->ops[1] = op2;
switch (inst->type) {
case INST_NOP: case INST_SAV:
case INST_SWP: case INST_NEG:
- return inst->opcnt == 0;
+ if (inst->opcnt != 0) return false;
+ break;
case INST_ADD: case INST_SUB:
- return inst->opcnt == 1;
+ if (inst->opcnt != 1) return false;
+ break;
+ case INST_JRO:
+ if (inst->opcnt != 1) return false;
+ if (inst->ops[0].type != OP_LIT) return false;
+ break;
case INST_JMP: case INST_JEZ: case INST_JNZ:
- case INST_JGZ: case INST_JLZ: case INST_JRO:
+ case INST_JGZ: case INST_JLZ:
if (inst->opcnt != 1) return false;
if (inst->ops[0].type != OP_LABEL) return false;
break;
@@ -308,14 +301,13 @@ tpu_set_inst(struct tpu *tpu, uint8_t pc, enum tpu_inst_type inst_type,
bool
tpu_add_inst(struct tpu *tpu, enum tpu_inst_type inst_type,
- int op1, union tpu_inst_op_val v1,
- int op2, union tpu_inst_op_val v2)
+ unsigned opcnt, struct tpu_inst_op op1, struct tpu_inst_op op2)
{
if (tpu->inst_cnt >= TPU_MAX_INST)
- die("tpu_add_inst: tpu X%lu Y%lu, >= max %i instructions",
+ die("tpu_add_inst: tpu X%i Y%i, >= max %i instructions",
tpu->x, tpu->y, TPU_MAX_INST);
return tpu_set_inst(tpu, (uint8_t) tpu->inst_cnt++,
- inst_type, op1, v1, op2, v2);
+ inst_type, opcnt, op1, op2);
}
/* tpu can always write to an empty port (->out), but only
@@ -575,12 +567,12 @@ tpu_map_deinit(struct tpu_map *map)
}
static struct tpu_map_link **
-tpu_map_link_pos(struct tpu_map *map, size_t x, size_t y)
+tpu_map_link_pos(struct tpu_map *map, int x, int y)
{
struct tpu_map_link **link;
size_t i;
- i = (x + y) % TPU_MAP_BUCKETS;
+ i = (size_t) (x + y) % TPU_MAP_BUCKETS;
link = &map->buckets[i];
while (*link && !((*link)->x == x && (*link)->y == y))
link = &(*link)->next;
@@ -603,7 +595,7 @@ 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)
+tpu_map_get(struct tpu_map *map, int x, int y)
{
struct tpu_map_link **link;
diff --git a/tpu.h b/tpu.h
@@ -58,7 +58,7 @@ struct tpu_inst_op {
struct tpu_inst {
enum tpu_inst_type type;
struct tpu_inst_op ops[2];
- uint8_t opcnt;
+ unsigned opcnt;
};
struct tpu_port {
@@ -73,7 +73,7 @@ struct tpu_port {
struct tpu {
enum tpu_status status;
- size_t x, y;
+ int x, y;
struct tpu_port ports[4];
int io_port;
@@ -90,7 +90,7 @@ struct tpu {
};
struct tpu_map_link {
- size_t x, y;
+ int x, y;
struct tpu *tpu;
struct tpu_map_link *next;
};
@@ -119,11 +119,9 @@ struct tpu_inst *tpu_current_inst(struct tpu *tpu);
void tpu_init_ports(struct tpu *tpu, struct tpu_map *map);
void tpu_update_ports(struct tpu *tpu);
bool tpu_set_inst(struct tpu *tpu, uint8_t pc, enum tpu_inst_type inst,
- int op1, union tpu_inst_op_val v1,
- int op2, union tpu_inst_op_val v2);
+ unsigned opcnt, struct tpu_inst_op op1, struct tpu_inst_op op2);
bool tpu_add_inst(struct tpu *tpu, enum tpu_inst_type inst,
- int op1, union tpu_inst_op_val v1,
- int op2, union tpu_inst_op_val v2);
+ 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);
@@ -132,7 +130,7 @@ 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);
+struct tpu *tpu_map_get(struct tpu_map *map, int x, int y);
void tis_init(struct tis *tis);
void tis_deinit(struct tis *tis);