summaryrefslogtreecommitdiffstats
path: root/tpu.c
diff options
context:
space:
mode:
Diffstat (limited to 'tpu.c')
-rw-r--r--tpu.c196
1 files changed, 111 insertions, 85 deletions
diff --git a/tpu.c b/tpu.c
index 3fb88bc..64ede3e 100644
--- a/tpu.c
+++ b/tpu.c
@@ -1,6 +1,7 @@
#include "tpu.h"
#include "util.h"
+#include <stdio.h>
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
@@ -119,15 +120,16 @@ tpu_init(struct tpu *tpu)
tpu->bak = 0;
tpu->inst_cnt = 0;
tpu->pc = 0;
+ tpu->status = STATUS_IDLE;
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;
+ tpu->ports[i].reading = false;
+ tpu->ports[i].in = -1;
+ tpu->ports[i].out = -1;
}
}
@@ -189,14 +191,33 @@ tpu_init_ports(struct tpu *tpu, struct tpu_map *map)
}
}
+void
+tpu_update_ports(struct tpu *tpu)
+{
+ struct tpu_port *port;
+
+ if (tpu->read_port >= 0) {
+ port = &tpu->ports[tpu->read_port];
+ if (port->reading) {
+ if (port->attached && port->dst_port->out >= 0) {
+ port->in = port->dst_port->out;
+ port->dst_port->out = -1;
+ port->reading = false;
+ }
+ } else if (tpu->status == STATUS_RUN) {
+ port->in = -1;
+ tpu->read_port = -1;
+ }
+ }
+}
+
bool
-tpu_add_inst(struct tpu *tpu, enum tpu_inst_type inst_type,
+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)
{
struct tpu_inst *inst;
- inst = calloc(1, sizeof(struct tpu_inst));
- if (!inst) die("malloc:");
+ inst = &tpu->insts[pc];
inst->type = inst_type;
if (op2 > 0) {
@@ -226,6 +247,8 @@ tpu_add_inst(struct tpu *tpu, enum tpu_inst_type inst_type,
break;
case INST_MOV:
if (inst->opcnt != 2) return false;
+ if (inst->ops[0].type == OP_LABEL) return false;
+ if (inst->ops[1].type == OP_LABEL) return false;
if (inst->ops[1].type == OP_LIT) return false;
break;
}
@@ -233,23 +256,62 @@ tpu_add_inst(struct tpu *tpu, enum tpu_inst_type inst_type,
return true;
}
-void
-tpu_ports_clear(struct tpu *tpu)
+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 i;
+ 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);
+}
- for (i = 0; i < 4; i++) {
- tpu->ports[i].read = false;
- tpu->ports[i].write = false;
+/* tpu can always write to an empty port (->out), but only
+ * read values from a port (->in) 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 */
+
+static int
+tpu_port_read(struct tpu *tpu, enum tpu_port_dir dir)
+{
+ struct tpu_port *port;
+
+ port = &tpu->ports[dir];
+ if (port->in < 0) {
+ port->reading = true;
+ tpu->read_port = (int) dir;
+ return -1;
}
+ return port->in;
+}
+
+static bool
+tpu_port_write(struct tpu *tpu, enum tpu_port_dir dir, uint8_t lit)
+{
+ struct tpu_port *port;
+
+ port = &tpu->ports[dir];
+ if (port->out >= 0) return false;
+ port->out = lit;
+ return true;
+}
+
+static void
+tpu_jmp_label(struct tpu *tpu, const char *label)
+{
+ size_t pc;
+
+ pc = label_map_get(&tpu->labels, label);
+ if (pc >= TPU_MAX_INST) abort();
+ tpu->pc = (uint8_t) pc;
}
int
tpu_exec_get(struct tpu *tpu, struct tpu_inst_op *op)
{
- struct tpu_port *port;
uint8_t lit;
- int i;
+ int i, v;
switch (op->type) {
case OP_ACC:
@@ -262,71 +324,50 @@ tpu_exec_get(struct tpu *tpu, struct tpu_inst_op *op)
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;
+ return tpu_port_read(tpu, op_to_dir(op->type));
case OP_ANY:
for (i = 0; i < 4; i++) {
- port = &tpu->ports[i];
- port->read = true;
- if (port->attached && port->dst_port->write) {
+ v = tpu_port_read(tpu, (enum tpu_port_dir) i);
+ if (v >= 0) {
tpu->last = i;
- lit = port->dst_port->val;
break;
}
}
if (i == 4) return -1;
+ lit = (uint8_t) v;
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;
+ return tpu_port_read(tpu, (enum tpu_port_dir) tpu->last);
case OP_LIT:
lit = op->lit;
break;
- default:
+ case OP_LABEL:
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_ACC:
+ tpu->acc = lit;
+ break;
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;
+ return tpu_port_write(tpu, op_to_dir(op->type), lit);
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;
+ if (tpu_port_write(tpu, (enum tpu_port_dir) i, lit)) {
tpu->last = i;
break;
}
@@ -335,17 +376,12 @@ tpu_exec_put(struct tpu *tpu, struct tpu_inst_op *op, uint8_t lit)
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:
+ return tpu_port_write(tpu, (enum tpu_port_dir) tpu->last, lit);
+ case OP_LABEL:
+ case OP_LIT:
abort();
}
- tpu_ports_clear(tpu);
-
return true;
}
@@ -359,16 +395,16 @@ tpu_exec_mov(struct tpu *tpu, struct tpu_inst *inst)
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))
+ if (!tpu_exec_put(tpu, &inst->ops[1], (uint8_t) lit))
return STATUS_WRITE;
- tpu->pc += 1;
return STATUS_RUN;
}
enum tpu_status
tpu_exec(struct tpu *tpu, struct tpu_inst *inst)
{
+ enum tpu_status status;
uint8_t lit;
int val;
@@ -377,7 +413,10 @@ tpu_exec(struct tpu *tpu, struct tpu_inst *inst)
tpu->pc += 1;
return STATUS_RUN;
case INST_MOV:
- return tpu_exec_mov(tpu, inst);
+ status = tpu_exec_mov(tpu, inst);
+ if (status == STATUS_RUN)
+ tpu->pc += 1;
+ return status;
case INST_SWP:
lit = tpu->acc;
tpu->acc = tpu->bak;
@@ -404,45 +443,32 @@ tpu_exec(struct tpu *tpu, struct tpu_inst *inst)
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();
+ case INST_JMP:
+ tpu_jmp_label(tpu, inst->ops[0].label);
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 {
+ if (tpu->acc == 0)
+ tpu_jmp_label(tpu, inst->ops[0].label);
+ 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 {
+ if (tpu->acc != 0)
+ tpu_jmp_label(tpu, inst->ops[0].label);
+ 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 {
+ if (tpu->acc > 0)
+ tpu_jmp_label(tpu, inst->ops[0].label);
+ 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 {
+ if (tpu->acc < 0)
+ tpu_jmp_label(tpu, inst->ops[0].label);
+ else
tpu->pc += 1;
- }
return STATUS_RUN;
case INST_JRO:
tpu->pc += inst->ops[0].lit;
@@ -457,7 +483,7 @@ void
tpu_step(struct tpu *tpu)
{
if (tpu->pc >= tpu->inst_cnt) return;
- tpu_exec(tpu, &tpu->insts[tpu->pc]);
+ tpu->status = tpu_exec(tpu, &tpu->insts[tpu->pc]);
if (tpu->pc >= tpu->inst_cnt) tpu->pc = 0;
}