commit d07257c9e92e8697915aaf40070c6228a5855104
parent e32b439cfe7ca72098b2ebe9e1e93f97b7c10ada
Author: Louis Burda <quent.burda@gmail.com>
Date: Tue, 25 Jul 2023 02:29:54 +0200
Validate jump target existence on tpu load
Diffstat:
M | asm.c | | | 69 | ++++++++++++++++++++++++++++++++++++++++++++++----------------------- |
A | out | | | 0 | |
M | test/test.asm | | | 3 | +++ |
M | tpu.c | | | 44 | +++++++++++++++++++++++++------------------- |
M | tpu.h | | | 16 | ++++++++++------ |
5 files changed, 84 insertions(+), 48 deletions(-)
diff --git a/asm.c b/asm.c
@@ -7,7 +7,7 @@
#include <stdint.h>
#define NAMEALPH "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"
-#define WHITESPACE " \t\v\r\n"
+#define WHITESPACE " \t\v\r\n,"
enum asm_tok {
/* Global */
@@ -88,10 +88,10 @@ strlcat_op_name(char *buf, struct tpu_inst_op *op, size_t n)
char hhbuf[4];
if (op->type == OP_LIT) {
- snprintf(hhbuf, 4, "%hhu", op->lit);
+ snprintf(hhbuf, 4, "%hhu", op->val.lit);
return strdcat(buf, hhbuf, n);
} else if (op->type == OP_LABEL) {
- return strdcat(buf, op->label, n);
+ return strdcat(buf, op->val.label, n);
} else {
return strdcat(buf, op_reprs[op->type], n);
}
@@ -144,9 +144,7 @@ tok_next(struct asm_tokenizer *tok)
if (!s[len]) return TOK_NL;
tok->tokstr = (s += len);
- if (*s == ',') die("load: line %lu, misaligned comma", tok->lineno);
-
- len = strcspn(s, WHITESPACE ",");
+ len = strcspn(s, WHITESPACE);
tok->off += len;
if (s[len]) {
s[len] = '\0';
@@ -181,8 +179,6 @@ tok_next(struct asm_tokenizer *tok)
return TOK_ANY;
} else if (!strcasecmp(s, "last")) {
return TOK_LAST;
- } else if (is_lit(s)) {
- return TOK_LIT;
} else if (!strcasecmp(s, "nop")) {
return TOK_NOP;
} else if (!strcasecmp(s, "mov")) {
@@ -209,10 +205,13 @@ tok_next(struct asm_tokenizer *tok)
return TOK_JLZ;
} else if (!strcasecmp(s, "jro")) {
return TOK_JRO;
+ } else if (is_lit(s)) {
+ return TOK_LIT;
} else if (*s == '#') {
tok->off += strlen(tok->linebuf + tok->off);
return TOK_COMMENT;
} else if (len && strspn(s, NAMEALPH) == len-1 && s[len-1] == ':') {
+ s[len-1] = '\0';
return TOK_LABEL;
} else if (*s == 'X' && atoi(s+1) > 0) {
return TOK_XPOS;
@@ -259,13 +258,31 @@ tok_next_in(struct asm_tokenizer *tokenizer, ...)
exit(1);
}
+static void
+tpu_validate(struct tpu *tpu)
+{
+ size_t dst;
+ int i;
+
+ for (i = 0; i < TPU_MAX_INST; i++) {
+ if (tpu->insts[i].ops[0].type == OP_LABEL) {
+ dst = label_map_get(&tpu->labels,
+ tpu->insts[i].ops[0].val.label);
+ if (dst == TPU_MAX_INST)
+ die("load: tpu X%lu Y%lu, label '%s' not defined",
+ tpu->x, tpu->y,
+ tpu->insts[i].ops[0].val.label);
+ }
+ }
+}
+
void
tis_load(struct tis *tis, const char *filepath)
{
struct asm_tokenizer tokenizer;
- enum tpu_inst_op_type op1, op2;
+ int op1, op2;
enum asm_tok op1_tok, op2_tok;
- uint8_t op1_lit, op2_lit;
+ union tpu_inst_op_val op1v, op2v;
enum tpu_inst_type inst;
struct tpu *tpu = NULL;
struct tpu_map_link *link;
@@ -273,7 +290,6 @@ tis_load(struct tis *tis, const char *filepath)
int stdin_x, stdin_y;
int stdout_x, stdout_y;
enum asm_tok tok;
- char *label;
size_t i;
tis_deinit(tis);
@@ -322,6 +338,7 @@ tis_load(struct tis *tis, const char *filepath)
break;
case TOK_END:
if (!tpu) goto disallowed;
+ tpu_validate(tpu);
tpu = NULL;
tok_next_in(&tokenizer, TOK_NL, -1);
break;
@@ -336,32 +353,39 @@ tis_load(struct tis *tis, const char *filepath)
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, 0, -1, 0))
+ if (!tpu_add_inst(tpu, inst, -1, op1v, -1, op2v))
die("load: line %lu, invalid instruction",
tokenizer.lineno);
break;
}
- op1 = tok_to_optype(op1_tok);
+ op1 = (int) tok_to_optype(op1_tok);
if (op1 == OP_LIT)
- op1_lit = str_to_lit(tokenizer.tokstr);
+ 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,
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, (int) op1,
- op1_lit, -1, 0))
+ if (!tpu_add_inst(tpu, inst, op1, op1v, -1, op2v))
die("load: line %lu, invalid instruction",
tokenizer.lineno);
break;
}
- op2 = tok_to_optype(op2_tok);
+ op2 = (int) tok_to_optype(op2_tok);
if (op2 == OP_LIT)
- op2_lit = str_to_lit(tokenizer.tokstr);
- if (!tpu_add_inst(tpu, inst, (int) op1, op1_lit,
- (int) op2, op2_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);
@@ -370,11 +394,10 @@ tis_load(struct tis *tis, const char *filepath)
tok_next_in(&tokenizer, TOK_NL, -1);
break;
case TOK_LABEL:
- label = strdup(tokenizer.tokstr);
- if (!label_map_add(&tpu->labels, label, tpu->inst_cnt))
+ if (!label_map_add(&tpu->labels,
+ tokenizer.tokstr, tpu->inst_cnt))
die("load: line %lu, duplicate label",
tokenizer.lineno);
- tok_next_in(&tokenizer, TOK_NL, -1);
break;
case TOK_NL:
break;
diff --git a/out b/out
diff --git a/test/test.asm b/test/test.asm
@@ -6,8 +6,11 @@ tpu X1 Y1
end
tpu X2 Y1
+start:
mov LEFT, ACC
+ add 5
mov ACC, RIGHT
+ jmp start
end
tpu X3 Y1
diff --git a/tpu.c b/tpu.c
@@ -1,6 +1,7 @@
#include "tpu.h"
#include "util.h"
+#include <ctype.h>
#include <stdio.h>
#include <stdbool.h>
#include <stddef.h>
@@ -53,7 +54,7 @@ djb_hash(const char *str)
hash = 5381;
for (c = str; *c; c++)
- hash = 33 * hash + (uint8_t) *c;
+ hash = 33 * hash + (uint8_t) tolower(*c);
return hash;
}
@@ -97,6 +98,7 @@ bool
label_map_add(struct label_map *map, const char *name, size_t pc)
{
struct label_map_link **pos, *link;
+ char *c;
pos = label_map_link_pos(map, name);
if (*pos) return false;
@@ -105,6 +107,8 @@ label_map_add(struct label_map *map, const char *name, size_t pc)
if (!link) die("malloc:");
link->label = strdup(name);
if (!link->label) die("strdup:");
+ for (c = link->label; *c; c++)
+ *c = (char) tolower(*c);
link->pc = pc;
link->next = NULL;
@@ -175,7 +179,7 @@ tpu_deinit(struct tpu *tpu)
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);
+ free(tpu->insts[i].ops[0].val.label);
}
}
@@ -255,7 +259,8 @@ tpu_update_ports(struct tpu *tpu)
bool
tpu_set_inst(struct tpu *tpu, uint8_t pc, enum tpu_inst_type inst_type,
- int op1, uint8_t op1_lit, int op2, uint8_t op2_lit)
+ int op1, union tpu_inst_op_val v1,
+ int op2, union tpu_inst_op_val v2)
{
struct tpu_inst *inst;
@@ -264,13 +269,13 @@ tpu_set_inst(struct tpu *tpu, uint8_t pc, enum tpu_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[1].val = v1;
inst->ops[0].type = (enum tpu_inst_op_type) op1;
- inst->ops[0].lit = op1_lit;
+ 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].lit = op1_lit;
+ inst->ops[0].val = v1;
inst->opcnt = 1;
} else {
inst->opcnt = 0;
@@ -300,13 +305,14 @@ 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, uint8_t op1_lit, int op2, uint8_t op2_lit)
+ int op1, union tpu_inst_op_val v1,
+ int op2, union tpu_inst_op_val v2)
{
- if (tpu->inst_cnt == TPU_MAX_INST)
- die("tpu_add_inst: tpu X%lu Y%lu, too many instructions",
- tpu->x, tpu->y);
- return tpu_set_inst(tpu, (uint8_t) tpu->inst_cnt++, inst_type,
- op1, op1_lit, op2, op2_lit);
+ if (tpu->inst_cnt >= TPU_MAX_INST)
+ die("tpu_add_inst: tpu X%lu Y%lu, >= 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);
}
/* tpu can always write to an empty port (->out), but only
@@ -385,7 +391,7 @@ tpu_exec_get(struct tpu *tpu, struct tpu_inst_op *op)
if (tpu->last < 0) return 0;
return tpu_port_read(tpu, (enum tpu_port_dir) tpu->last);
case OP_LIT:
- lit = op->lit;
+ lit = op->val.lit;
break;
case OP_LABEL:
abort();
@@ -489,34 +495,34 @@ tpu_exec(struct tpu *tpu, struct tpu_inst *inst)
tpu->pc += 1;
return STATUS_RUN;
case INST_JMP:
- tpu_jmp_label(tpu, inst->ops[0].label);
+ tpu_jmp_label(tpu, inst->ops[0].val.label);
return STATUS_RUN;
case INST_JEZ:
if (tpu->acc == 0)
- tpu_jmp_label(tpu, inst->ops[0].label);
+ tpu_jmp_label(tpu, inst->ops[0].val.label);
else
tpu->pc += 1;
return STATUS_RUN;
case INST_JNZ:
if (tpu->acc != 0)
- tpu_jmp_label(tpu, inst->ops[0].label);
+ tpu_jmp_label(tpu, inst->ops[0].val.label);
else
tpu->pc += 1;
return STATUS_RUN;
case INST_JGZ:
if (tpu->acc > 0)
- tpu_jmp_label(tpu, inst->ops[0].label);
+ tpu_jmp_label(tpu, inst->ops[0].val.label);
else
tpu->pc += 1;
return STATUS_RUN;
case INST_JLZ:
if (tpu->acc < 0)
- tpu_jmp_label(tpu, inst->ops[0].label);
+ tpu_jmp_label(tpu, inst->ops[0].val.label);
else
tpu->pc += 1;
return STATUS_RUN;
case INST_JRO:
- tpu->pc += inst->ops[0].lit;
+ tpu->pc += inst->ops[0].val.lit;
if (tpu->pc >= tpu->inst_cnt) tpu->pc = 0;
return STATUS_RUN;
default:
diff --git a/tpu.h b/tpu.h
@@ -34,6 +34,11 @@ enum tpu_port_type {
PORT_IN = 0b01, PORT_OUT = 0b10, PORT_BIDI = 0b11
};
+union tpu_inst_op_val {
+ uint8_t lit;
+ char *label;
+};
+
struct label_map_link {
char *label;
size_t pc;
@@ -46,10 +51,7 @@ struct label_map {
struct tpu_inst_op {
enum tpu_inst_op_type type;
- union {
- uint8_t lit;
- char *label;
- };
+ union tpu_inst_op_val val;
};
struct tpu_inst {
@@ -116,9 +118,11 @@ 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, uint8_t op1_lit, int op2, uint8_t op2_lit);
+ int op1, union tpu_inst_op_val v1,
+ int op2, union tpu_inst_op_val v2);
bool tpu_add_inst(struct tpu *tpu, enum tpu_inst_type inst,
- int op1, uint8_t op1_lit, int op2, uint8_t op2_lit);
+ int op1, union tpu_inst_op_val v1,
+ int op2, union tpu_inst_op_val v2);
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);