summaryrefslogtreecommitdiffstats
path: root/tpu.c
diff options
context:
space:
mode:
authorLouis Burda <quent.burda@gmail.com>2023-12-25 01:46:55 +0100
committerLouis Burda <quent.burda@gmail.com>2023-12-25 01:46:55 +0100
commit5ff5bb25cc864e7fa06aa8fed0c4ec92e99f103a (patch)
tree646a3a56d5e9d831e81d64c48a2e39e594cf0685 /tpu.c
parent70d48f0db2ff618436df7c8c52ead5a0701fd7ab (diff)
downloadtis100-5ff5bb25cc864e7fa06aa8fed0c4ec92e99f103a.tar.gz
tis100-5ff5bb25cc864e7fa06aa8fed0c4ec92e99f103a.zip
Make mode/state behaviour compatible with game
Diffstat (limited to 'tpu.c')
-rw-r--r--tpu.c256
1 files changed, 158 insertions, 98 deletions
diff --git a/tpu.c b/tpu.c
index dca2a72..459db24 100644
--- a/tpu.c
+++ b/tpu.c
@@ -2,6 +2,7 @@
#include "util.h"
#include <ctype.h>
+#include <assert.h>
#include <stdio.h>
#include <stdbool.h>
#include <stddef.h>
@@ -11,7 +12,7 @@ const char *dir_reprs[] = {
"LEFT", "RIGHT", "UP", "DOWN"
};
-const char *status_reprs[] = {
+const char *mode_reprs[] = {
"IDLE", "RUN", "READ", "WRITE"
};
@@ -129,18 +130,18 @@ label_map_get(struct label_map *map, const char *name)
}
void
-tpu_port_init(struct tpu_port *port)
+tpu_port_init(struct tpu_port *port, struct tpu *tpu)
{
+ port->tpu = tpu;
port->attached = false;
port->dst_port = NULL;
- port->dst_tpu = NULL;
port->type = PORT_BIDI;
- port->clr_post_run = false;
+ port->reset_in = false;
port->reading = false;
port->writing = false;
- port->in_set = false;
+ port->avail = false;
port->in = -1;
- port->out_set = false;
+ port->writing = false;
port->out = -1;
}
@@ -151,10 +152,35 @@ tpu_port_deinit(struct tpu_port *port)
}
void
+tpu_port_update(struct tpu_port *port)
+{
+ struct tpu *tpu;
+
+ if (!port->attached) return;
+
+ if (port->writing && !port->dst_port->avail) {
+ port->dst_port->in = port->out;
+ port->dst_port->avail = true;
+ }
+
+ if (port->reset_in && (!port->tpu || !port->tpu->idle)) {
+ port->avail = false;
+ port->dst_port->writing = false;
+ port->reset_in = false;
+ if ((tpu = port->dst_port->tpu)) {
+ /* hacky: only allow mode to be RUN after receive */
+ tpu->mode = MODE_RUN;
+ tpu->idle = false;
+ if (++tpu->pc >= tpu->inst_cnt) tpu->pc = 0;
+ }
+ }
+}
+
+void
tpu_io_port_init(struct tpu_io_port *io_port, char c, enum tpu_port_dir dir,
enum tpu_port_type type, int x, int y)
{
- tpu_port_init(&io_port->port);
+ tpu_port_init(&io_port->port, NULL);
io_port->type = type;
io_port->file = NULL;
io_port->dir = dir;
@@ -175,7 +201,10 @@ tpu_init(struct tpu *tpu)
{
size_t i;
- tpu->status = STATUS_IDLE;
+ tpu->disabled = false;
+
+ tpu->mode = MODE_IDLE;
+ tpu->idle = true;
tpu->x = 0;
tpu->y = 0;
@@ -193,7 +222,7 @@ tpu_init(struct tpu *tpu)
tpu->last = -1;
tpu->io_port = -1;
for (i = 0; i < 4; i++)
- tpu_port_init(&tpu->ports[i]);
+ tpu_port_init(&tpu->ports[i], tpu);
}
void
@@ -226,6 +255,7 @@ tpu_init_ports(struct tpu *tpu, struct tpu_map *map)
for (i = 0; i < 4; i++) {
if (tpu->ports[i].attached) continue;
+ if (tpu->disabled) continue;
switch (i) {
case DIR_LEFT:
@@ -247,45 +277,25 @@ tpu_init_ports(struct tpu *tpu, struct tpu_map *map)
}
neighbor = tpu_map_get(map, x, y);
- if (neighbor) {
+ if (neighbor && !neighbor->disabled) {
odir = opposite_dir((enum tpu_port_dir) i);
tpu->ports[i].attached = true;
- tpu->ports[i].dst_tpu = neighbor;
tpu->ports[i].dst_port = &neighbor->ports[odir];
neighbor->ports[odir].attached = true;
- neighbor->ports[odir].dst_tpu = tpu;
neighbor->ports[odir].dst_port = &tpu->ports[i];
}
}
}
void
-tpu_update_ports(struct tpu *tpu)
+tpu_update(struct tpu *tpu)
{
struct tpu_port *port;
int i;
for (i = 0; i < 4; i++) {
port = &tpu->ports[i];
- if (!port->attached) continue;
- if (port->out_set && !port->dst_port->in_set) {
- port->dst_port->in = port->out;
- port->dst_port->in_set = true;
- port->dst_port->reading = false;
- port->out_set = false;
- port->writing = false;
- }
- if (port->dst_port->out_set && !port->in_set) {
- port->in = port->dst_port->out;
- port->in_set = true;
- port->reading = false;
- port->dst_port->out_set = false;
- port->dst_port->writing = false;
- }
- if (tpu->status == STATUS_RUN && port->clr_post_run) {
- port->in_set = false;
- port->clr_post_run = false;
- }
+ tpu_port_update(port);
}
}
@@ -342,38 +352,40 @@ tpu_add_inst(struct tpu *tpu, enum tpu_inst_type inst_type,
return &tpu->insts[tpu->inst_cnt++];
}
-/* tpu can always write to an empty port (->out), but only
- * read values 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 */
+/* tpu can only read values if there is a writer attached and offering a
+ * value (->dst_port->out) -- which is cleared on successful read */
static bool
tpu_port_read(struct tpu *tpu, enum tpu_port_dir dir, int *lit)
{
- struct tpu_port *port;
+ struct tpu_port *port = &tpu->ports[dir];
- port = &tpu->ports[dir];
- port->clr_post_run = true;
- if (!port->in_set) {
+ if (!port->avail) {
port->reading = true;
return false;
}
+
*lit = port->in;
+ port->reset_in = true;
+
return true;
}
+/* tpu can always write values (->out) but only finish writing once the
+ * written value is read and cleared by the reader (->dst_port->in) */
+
static bool
tpu_port_write(struct tpu *tpu, enum tpu_port_dir dir, int lit)
{
- struct tpu_port *port;
+ struct tpu_port *port = &tpu->ports[dir];
- port = &tpu->ports[dir];
- if (port->out_set) {
- port->writing = true;
+ if (port->writing) {
return false;
}
- port->out_set = true;
+
+ port->writing = true;
port->out = lit;
+
return true;
}
@@ -447,117 +459,106 @@ tpu_exec_put(struct tpu *tpu, struct tpu_inst_op *op, int lit)
}
}
-enum tpu_status
-tpu_exec_mov(struct tpu *tpu, struct tpu_inst *inst)
-{
- int lit;
-
- if (inst->type != INST_MOV) abort();
-
- if (!tpu_exec_get(tpu, &inst->ops[0], &lit))
- return STATUS_READ;
-
- if (!tpu_exec_put(tpu, &inst->ops[1], lit))
- return STATUS_WRITE;
-
- return STATUS_RUN;
-}
-
-enum tpu_status
+enum tpu_mode
tpu_exec(struct tpu *tpu, struct tpu_inst *inst)
{
- enum tpu_status status;
int lit;
int val;
+ tpu->idle = false;
switch (inst->type) {
case INST_NOP:
tpu->pc += 1;
- return STATUS_RUN;
+ return MODE_RUN;
case INST_MOV:
- status = tpu_exec_mov(tpu, inst);
- if (status == STATUS_RUN)
- tpu->pc += 1;
- return status;
+ if (tpu->mode != MODE_WRITE) {
+ if (!tpu_exec_get(tpu, &inst->ops[0], &lit)) {
+ tpu->idle = true;
+ return MODE_READ;
+ }
+ }
+ /* tpu->idle/mode fixed up via tpu->reset_in if value is read */
+ if (!tpu_exec_put(tpu, &inst->ops[1], lit))
+ tpu->idle = true;
+ return MODE_WRITE;
case INST_SWP:
lit = tpu->acc;
tpu->acc = tpu->bak;
tpu->bak = lit;
tpu->pc += 1;
- return STATUS_RUN;
+ return MODE_RUN;
case INST_SAV:
tpu->bak = tpu->acc;
tpu->pc += 1;
- return STATUS_RUN;
+ return MODE_RUN;
case INST_ADD:
- if (!tpu_exec_get(tpu, &inst->ops[0], &val))
- return STATUS_READ;
+ if (!tpu_exec_get(tpu, &inst->ops[0], &val)) {
+ tpu->idle = true;
+ return MODE_READ;
+ }
tpu->acc = MIN(MAX(tpu->acc + val, -999), 999);
tpu->pc += 1;
- return STATUS_RUN;
+ return MODE_RUN;
case INST_SUB:
if (!tpu_exec_get(tpu, &inst->ops[0], &val))
- return STATUS_READ;
+ return MODE_READ;
tpu->acc = MIN(MAX(tpu->acc - val, -999), 999);
tpu->pc += 1;
- return STATUS_RUN;
+ return MODE_RUN;
case INST_NEG:
tpu->acc = -tpu->acc;
tpu->pc += 1;
- return STATUS_RUN;
+ return MODE_RUN;
case INST_JMP:
tpu_jmp_label(tpu, inst->ops[0].val.label);
- return STATUS_RUN;
+ return MODE_RUN;
case INST_JEZ:
if (tpu->acc == 0)
tpu_jmp_label(tpu, inst->ops[0].val.label);
else
tpu->pc += 1;
- return STATUS_RUN;
+ return MODE_RUN;
case INST_JNZ:
if (tpu->acc != 0)
tpu_jmp_label(tpu, inst->ops[0].val.label);
else
tpu->pc += 1;
- return STATUS_RUN;
+ return MODE_RUN;
case INST_JGZ:
if (tpu->acc > 0)
tpu_jmp_label(tpu, inst->ops[0].val.label);
else
tpu->pc += 1;
- return STATUS_RUN;
+ return MODE_RUN;
case INST_JLZ:
if (tpu->acc < 0)
tpu_jmp_label(tpu, inst->ops[0].val.label);
else
tpu->pc += 1;
- return STATUS_RUN;
+ return MODE_RUN;
case INST_JRO:
tpu->pc += inst->ops[0].val.lit;
if (tpu->pc < 0) tpu->pc = 0;
if (tpu->pc >= tpu->inst_cnt)
tpu->pc = (int) tpu->inst_cnt - 1;
- return STATUS_RUN;
+ return MODE_RUN;
default:
abort();
}
}
-enum tpu_status
+void
tpu_step(struct tpu *tpu)
{
+ int prev_mode;
+
+ prev_mode = tpu->mode;
if (tpu->pc < tpu->inst_cnt) {
- tpu->status = tpu_exec(tpu, &tpu->insts[tpu->pc]);
+ tpu->mode = tpu_exec(tpu, &tpu->insts[tpu->pc]);
if (tpu->pc >= tpu->inst_cnt) tpu->pc = 0;
} else {
- tpu->status = STATUS_IDLE;
+ tpu->mode = MODE_IDLE;
}
-
- tpu->steps += 1;
- if (tpu->status != STATUS_RUN)
- tpu->idle_steps += 1;
-
- return tpu->status;
}
void
@@ -633,6 +634,9 @@ tis_init(struct tis *tis)
tpu_map_init(&tis->tpu_map);
memset(&tis->in_ports, 0, TIS_MAX_IO_PORTS * sizeof(void *));
memset(&tis->out_ports, 0, TIS_MAX_IO_PORTS * sizeof(void *));
+ tis->steps = 0;
+ tis->show_stats = false;
+ tis->asm_init = false;
}
void
@@ -660,19 +664,43 @@ tis_step(struct tis *tis)
bool running;
size_t i;
+ tis_communicate(tis);
+
running = false;
for (i = 0; i < TPU_MAP_BUCKETS; i++) {
link = tis->tpu_map.buckets[i];
for (; link; link = link->next)
- running |= (tpu_step(link->tpu) == STATUS_RUN);
+ tpu_step(link->tpu);
}
for (i = 0; i < TPU_MAP_BUCKETS; i++) {
link = tis->tpu_map.buckets[i];
- for (; link; link = link->next)
- tpu_update_ports(link->tpu);
+ for (; link; link = link->next) {
+ tpu_update(link->tpu);
+ running |= !link->tpu->idle;
+ }
+ }
+
+ for (i = 0; i < TPU_MAP_BUCKETS; i++) {
+ link = tis->tpu_map.buckets[i];
+ for (; link; link = link->next) {
+ if (link->tpu->pc < link->tpu->inst_cnt) {
+ link->tpu->steps += 1;
+ if (link->tpu->idle)
+ link->tpu->idle_steps += 1;
+ }
+ }
+ }
+
+ for (i = 0; i < TIS_MAX_IO_PORTS; i++) {
+ if (tis->in_ports[i])
+ tpu_port_update(&tis->in_ports[i]->port);
+ if (tis->out_ports[i])
+ tpu_port_update(&tis->out_ports[i]->port);
}
+ tis->steps += 1;
+
return running;
}
@@ -686,7 +714,7 @@ tis_communicate(struct tis *tis)
for (i = 0; i < TIS_MAX_IO_PORTS; i++) {
io_port = tis->in_ports[i];
if (!io_port) continue;
- if (!io_port->port.attached || io_port->port.out_set)
+ if (!io_port->port.attached || io_port->port.writing)
continue;
if (!io_port->file) continue;
while ((s = fgets(buf, sizeof(buf), io_port->file))) {
@@ -696,7 +724,7 @@ tis_communicate(struct tis *tis)
if (!s || *s != '\0')
die("communicate: invalid input '%s'", buf);
io_port->port.out = val;
- io_port->port.out_set = true;
+ io_port->port.writing = true;
break;
}
}
@@ -704,10 +732,42 @@ tis_communicate(struct tis *tis)
for (i = 0; i < TIS_MAX_IO_PORTS; i++) {
io_port = tis->out_ports[i];
if (!io_port) continue;
- if (!io_port->port.attached || !io_port->port.in_set)
+ if (!io_port->port.attached || !io_port->port.avail)
continue;
if (io_port->file)
fprintf(io_port->file, "%i\n", io_port->port.in);
- io_port->port.in_set = false;
+ io_port->port.reset_in = true;
+ tpu_port_update(&io_port->port);
+ }
+}
+
+struct tis_stats
+tis_gen_stats(struct tis *tis)
+{
+ struct tis_stats stats;
+ struct tpu_map_link *link;
+ size_t idle_steps;
+ size_t all_steps;
+ int i;
+
+ stats.steps = tis->steps;
+ stats.blocks = 0;
+ stats.insts = 0;
+ stats.idle = 0;
+
+ all_steps = 0;
+ idle_steps = 0;
+ for (i = 0; i < TPU_MAP_BUCKETS; i++) {
+ link = tis->tpu_map.buckets[i];
+ for (; link; link = link->next) {
+ stats.insts += link->tpu->inst_cnt;
+ idle_steps += link->tpu->idle_steps;
+ all_steps += link->tpu->steps;
+ stats.blocks += (link->tpu->inst_cnt > 0);
+ }
}
+
+ stats.idle = (float) idle_steps / (float) all_steps;
+
+ return stats;
}