tis100

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

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:
Masm.c | 69++++++++++++++++++++++++++++++++++++++++++++++-----------------------
Aout | 0
Mtest/test.asm | 3+++
Mtpu.c | 44+++++++++++++++++++++++++-------------------
Mtpu.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);