tis100

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

tpu.c (16080B)


      1#include "tpu.h"
      2#include "util.h"
      3
      4#include <ctype.h>
      5#include <assert.h>
      6#include <stdio.h>
      7#include <stdbool.h>
      8#include <stddef.h>
      9#include <string.h>
     10
     11const char *dir_reprs[] = {
     12	"LEFT", "RIGHT", "UP", "DOWN"
     13};
     14
     15const char *mode_reprs[] = {
     16	"IDLE", "RUN", "READ", "WRITE"
     17};
     18
     19const char *inst_reprs[] = {
     20	"NOP", "MOV", "SWP", "SAV", "ADD", "SUB",
     21	"NEG", "JMP", "JEZ", "JNZ", "JGZ", "JLZ", "JRO"
     22};
     23
     24const char *op_reprs[] = {
     25	"ACC", "NIL", "LEFT", "RIGHT", "UP", "DOWN",
     26	"ANY", "LAST", "LIT", "<NAME>"
     27};
     28
     29static enum tpu_port_dir
     30op_to_dir(enum tpu_inst_op_type op)
     31{
     32	return op - OP_LEFT;
     33}
     34
     35static enum tpu_port_dir
     36opposite_dir(enum tpu_port_dir dir)
     37{
     38	switch (dir) {
     39	case DIR_UP:
     40		return DIR_DOWN;
     41	case DIR_DOWN:
     42		return DIR_UP;
     43	case DIR_LEFT:
     44		return DIR_RIGHT;
     45	case DIR_RIGHT:
     46		return DIR_LEFT;
     47	}
     48}
     49
     50static uint32_t
     51djb_hash(const char *str)
     52{
     53	uint32_t hash;
     54	const char *c;
     55
     56	hash = 5381;
     57	for (c = str; *c; c++)
     58		hash = 33 * hash + (uint8_t) tolower(*c);
     59
     60	return hash;
     61}
     62
     63void
     64label_map_init(struct label_map *map)
     65{
     66	memset(map, 0, sizeof(struct label_map));
     67}
     68
     69void
     70label_map_deinit(struct label_map *map)
     71{
     72	struct label_map_link *link, *next;
     73	size_t i;
     74
     75	for (i = 0; i < LABEL_MAP_BUCKETS; i++) {
     76		link = map->buckets[i];
     77		while (link) {
     78			next = link->next;
     79			free(link);
     80			link = next;
     81		}
     82	}
     83
     84	for (i = 0; i < TPU_MAX_INST_CNT; i++)
     85		free(map->labels[i].str);
     86}
     87
     88static struct label_map_link **
     89label_map_link_pos(struct label_map *map, const char *name)
     90{
     91	struct label_map_link **link;
     92
     93	link = &map->buckets[djb_hash(name) % LABEL_MAP_BUCKETS];
     94	while (*link && strcasecmp(map->labels[(*link)->pc].str, name))
     95		link = &(*link)->next;
     96
     97	return link;
     98}
     99
    100bool
    101label_map_add(struct label_map *map, const char *name, int pc, bool prefix)
    102{
    103	struct label_map_link **pos, *link;
    104
    105	pos = label_map_link_pos(map, name);
    106	if (*pos) return false;
    107
    108	if (map->labels[pc].str) return false;
    109	map->labels[pc].str = strdup(name);
    110	if (!map->labels[pc].str) die("strdup:");
    111	map->labels[pc].prefix = prefix;
    112
    113	*pos = link = malloc(sizeof(struct label_map_link));
    114	if (!link) die("malloc:");
    115	link->pc = pc;
    116	link->next = NULL;
    117
    118	return true;
    119}
    120
    121int
    122label_map_get(struct label_map *map, const char *name)
    123{
    124	struct label_map_link **link;
    125
    126	link = label_map_link_pos(map, name);
    127	if (!*link) return -1;
    128
    129	return (*link)->pc;
    130}
    131
    132void
    133tpu_port_init(struct tpu_port *port, struct tpu *tpu)
    134{
    135	port->tpu = tpu;
    136	port->attached = false;
    137	port->dst_port = NULL;
    138	port->type = PORT_BIDI;
    139	port->reset_in = false;
    140	port->writing = false;
    141	port->avail = false;
    142	port->in = -1;
    143	port->writing = false;
    144	port->out = -1;
    145	port->io = false;
    146}
    147
    148void
    149tpu_port_deinit(struct tpu_port *port)
    150{
    151	/* empty */
    152}
    153
    154void
    155tpu_port_update(struct tpu_port *port)
    156{
    157	struct tpu *tpu;
    158
    159	if (!port->attached) return;
    160
    161	if (port->writing && !port->dst_port->avail) {
    162		port->dst_port->in = port->out;
    163		port->dst_port->avail = true;
    164	}
    165
    166	if (port->reset_in && (!port->tpu || !port->tpu->idle)) {
    167		port->avail = false;
    168		port->dst_port->writing = false;
    169		if ((tpu = port->dst_port->tpu)) {
    170			/* hacky: switch mode to RUN after receive */
    171			assert(tpu->mode == MODE_WRITE);
    172			tpu->mode = MODE_RUN;
    173			tpu->idle = false;
    174			if (++tpu->pc >= tpu->inst_cnt) tpu->pc = 0;
    175		}
    176		port->reset_in = false;
    177	}
    178}
    179
    180void
    181tpu_io_port_init(struct tpu_io_port *io_port, char c, enum tpu_port_dir dir,
    182	enum tpu_port_type type, int x, int y)
    183{
    184	tpu_port_init(&io_port->port, NULL);
    185	io_port->port.io = io_port;
    186	io_port->io_step = 0;
    187	io_port->type = type;
    188	io_port->file = NULL;
    189	io_port->dir = dir;
    190	io_port->c = c;
    191	io_port->x = x;
    192	io_port->y = y;
    193}
    194
    195void
    196tpu_io_port_deinit(struct tpu_io_port *io_port)
    197{
    198	tpu_port_deinit(&io_port->port);
    199	if (io_port->file) fclose(io_port->file);
    200}
    201
    202void
    203tpu_init(struct tpu *tpu)
    204{
    205	size_t i;
    206
    207	tpu->disabled = false;
    208
    209	tpu->mode = MODE_IDLE;
    210	tpu->idle = false;
    211
    212	tpu->x = 0;
    213	tpu->y = 0;
    214
    215	tpu->steps = 0;
    216	tpu->idle_steps = 0;
    217
    218	tpu->pc = 0;
    219	tpu->acc = 0;
    220	tpu->bak = 0;
    221	tpu->inst_cnt = 0;
    222	tpu->rows = 0;
    223	label_map_init(&tpu->label_map);
    224
    225	tpu->last = -1;
    226	tpu->io_port = -1;
    227	for (i = 0; i < 4; i++)
    228		tpu_port_init(&tpu->ports[i], tpu);
    229}
    230
    231void
    232tpu_deinit(struct tpu *tpu)
    233{
    234	int i;
    235
    236	for (i = 0; i < tpu->inst_cnt; i++) {
    237		if (tpu->insts[i].opcnt >= 1
    238				&& tpu->insts[i].ops[0].type == OP_LABEL)
    239			free(tpu->insts[i].ops[0].val.label);
    240	}
    241	label_map_deinit(&tpu->label_map);
    242}
    243
    244struct tpu_inst *
    245tpu_current_inst(struct tpu *tpu)
    246{
    247	if (tpu->pc >= tpu->inst_cnt)
    248		return NULL;
    249	return &tpu->insts[tpu->pc];
    250}
    251
    252void
    253tpu_init_ports(struct tpu *tpu, struct tpu_map *map)
    254{
    255	struct tpu *neighbor;
    256	enum tpu_port_dir odir;
    257	int x, y, i;
    258
    259	for (i = 0; i < 4; i++) {
    260		if (tpu->ports[i].attached) continue;
    261		if (tpu->disabled) continue;
    262
    263		switch (i) {
    264		case DIR_LEFT:
    265			x = tpu->x - 1;
    266			y = tpu->y;
    267			break;
    268		case DIR_RIGHT:
    269			x = tpu->x + 1;
    270			y = tpu->y;
    271			break;
    272		case DIR_UP:
    273			x = tpu->x;
    274			y = tpu->y - 1;
    275			break;
    276		case DIR_DOWN:
    277			x = tpu->x;
    278			y = tpu->y + 1;
    279			break;
    280		}
    281
    282		neighbor = tpu_map_get(map, x, y);
    283		if (neighbor && !neighbor->disabled) {
    284			odir = opposite_dir((enum tpu_port_dir) i);
    285			tpu->ports[i].attached = true;
    286			tpu->ports[i].dst_port = &neighbor->ports[odir];
    287			neighbor->ports[odir].attached = true;
    288			neighbor->ports[odir].dst_port = &tpu->ports[i];
    289		}
    290	}
    291}
    292
    293void
    294tpu_update(struct tpu *tpu)
    295{
    296	struct tpu_port *port;
    297	int i;
    298
    299	for (i = 0; i < 4; i++) {
    300		port = &tpu->ports[i];
    301		tpu_port_update(port);
    302	}
    303}
    304
    305bool
    306tpu_set_inst(struct tpu *tpu, int pc, enum tpu_inst_type inst_type,
    307	unsigned opcnt, struct tpu_inst_op op1, struct tpu_inst_op op2)
    308{
    309	struct tpu_inst *inst;
    310
    311	inst = &tpu->insts[pc];
    312	inst->type = inst_type;
    313	inst->opcnt = opcnt;
    314	inst->ops[0] = op1;
    315	inst->ops[1] = op2;
    316
    317	switch (inst->type) {
    318	case INST_NOP: case INST_SAV:
    319	case INST_SWP: case INST_NEG:
    320		if (inst->opcnt != 0) return false;
    321		break;
    322	case INST_ADD: case INST_SUB:
    323		if (inst->opcnt != 1) return false;
    324		break;
    325	 case INST_JRO:
    326		if (inst->opcnt != 1) return false;
    327		if (inst->ops[0].type != OP_LIT) return false;
    328		break;
    329	case INST_JMP: case INST_JEZ: case INST_JNZ:
    330	case INST_JGZ: case INST_JLZ:
    331		if (inst->opcnt != 1) return false;
    332		if (inst->ops[0].type != OP_LABEL) return false;
    333		break;
    334	case INST_MOV:
    335		if (inst->opcnt != 2) return false;
    336		if (inst->ops[0].type == OP_LABEL) return false;
    337		if (inst->ops[1].type == OP_LABEL) return false;
    338		if (inst->ops[1].type == OP_LIT) return false;
    339		break;
    340	}
    341
    342	return true;
    343}
    344
    345struct tpu_inst *
    346tpu_add_inst(struct tpu *tpu, enum tpu_inst_type inst_type,
    347	unsigned opcnt, struct tpu_inst_op op1, struct tpu_inst_op op2)
    348{
    349	if (tpu->inst_cnt >= TPU_MAX_INST_CNT)
    350		die("tpu_add_inst: tpu X%i Y%i, >= max %i instructions",
    351			tpu->x, tpu->y, TPU_MAX_INST_CNT);
    352	if (!tpu_set_inst(tpu, (uint8_t) tpu->inst_cnt,
    353			inst_type, opcnt, op1, op2))
    354		return false;
    355	return &tpu->insts[tpu->inst_cnt++];
    356}
    357
    358/* tpu can only read values if there is a writer attached and offering
    359 * a value -- this value is cleared on successful read */
    360
    361static bool
    362tpu_port_read(struct tpu *tpu, enum tpu_port_dir dir, int *lit)
    363{
    364	struct tpu_port *port = &tpu->ports[dir];
    365
    366	if (!port->avail)
    367		return false;
    368
    369	*lit = port->in;
    370	port->reset_in = true;
    371
    372	return true;
    373}
    374
    375/* tpu can write value to ->out immediately but only finish the instruction
    376 * once the written value is read and cleared by the reader */
    377
    378static bool
    379tpu_port_write(struct tpu *tpu, enum tpu_port_dir dir, int lit)
    380{
    381	struct tpu_port *port = &tpu->ports[dir];
    382
    383	if (port->writing)
    384		return false;
    385
    386	port->writing = true;
    387	port->out = lit;
    388
    389	return true;
    390}
    391
    392static void
    393tpu_jmp_label(struct tpu *tpu, const char *label)
    394{
    395	tpu->pc = label_map_get(&tpu->label_map, label);
    396	if (tpu->pc < 0 || tpu->pc >= tpu->inst_cnt) abort();
    397}
    398
    399int
    400tpu_exec_get(struct tpu *tpu, struct tpu_inst_op *op, int *lit)
    401{
    402	int i;
    403
    404	switch (op->type) {
    405	case OP_ACC:
    406		*lit = tpu->acc;
    407		return true;
    408	case OP_NIL:
    409		*lit = 0;
    410		return true;
    411	case OP_LEFT: case OP_RIGHT: case OP_UP: case OP_DOWN:
    412		return tpu_port_read(tpu, op_to_dir(op->type), lit);
    413	case OP_ANY:
    414		for (i = 0; i < 4; i++) {
    415			if (tpu_port_read(tpu, (enum tpu_port_dir) i, lit)) {
    416				tpu->last = i;
    417				return true;
    418			}
    419		}
    420		return false;
    421	case OP_LAST:
    422		if (tpu->last < 0) return false;
    423		return tpu_port_read(tpu, (enum tpu_port_dir) tpu->last, lit);
    424	case OP_LIT:
    425		*lit = op->val.lit;
    426		return true;
    427	case OP_LABEL:
    428		abort();
    429	}
    430}
    431
    432bool
    433tpu_exec_put(struct tpu *tpu, struct tpu_inst_op *op, int lit)
    434{
    435	int i;
    436
    437	switch (op->type) {
    438	case OP_ACC:
    439		tpu->acc = lit;
    440		return true;
    441	case OP_NIL:
    442		return true;
    443	case OP_LEFT: case OP_RIGHT: case OP_UP: case OP_DOWN:
    444		return tpu_port_write(tpu, op_to_dir(op->type), lit);
    445	case OP_ANY:
    446		for (i = 0; i < 4; i++) {
    447			if (tpu_port_write(tpu, (enum tpu_port_dir) i, lit)) {
    448				tpu->last = i;
    449				return true;
    450			}
    451		}
    452		return false;
    453	case OP_LAST:
    454		if (tpu->last < 0) return false;
    455		return tpu_port_write(tpu, (enum tpu_port_dir) tpu->last, lit);
    456	case OP_LABEL:
    457	case OP_LIT:
    458		abort();
    459	}
    460}
    461
    462enum tpu_mode
    463tpu_exec(struct tpu *tpu, struct tpu_inst *inst)
    464{
    465	int lit;
    466	int val;
    467
    468	tpu->idle = false;
    469	switch (inst->type) {
    470	case INST_NOP:
    471		tpu->pc += 1;
    472		return MODE_RUN;
    473	case INST_MOV:
    474		if (tpu->mode == MODE_WRITE) {
    475			tpu->idle = true;
    476			return MODE_WRITE;
    477		}
    478		if (!tpu_exec_get(tpu, &inst->ops[0], &lit)) {
    479			tpu->idle = true;
    480			return MODE_READ;
    481		}
    482		if (!tpu_exec_put(tpu, &inst->ops[1], lit)) {
    483			tpu->idle = true;
    484			return MODE_WRITE;
    485		}
    486		/* if MOV to port, set WRITE tentatively and fix on reset_in */
    487		if (inst->ops[1].type != OP_ACC && inst->ops[1].type != OP_NIL)
    488			return MODE_WRITE;
    489		tpu->pc += 1;
    490		return MODE_RUN;
    491	case INST_SWP:
    492		lit = tpu->acc;
    493		tpu->acc = tpu->bak;
    494		tpu->bak = lit;
    495		tpu->pc += 1;
    496		return MODE_RUN;
    497	case INST_SAV:
    498		tpu->bak = tpu->acc;
    499		tpu->pc += 1;
    500		return MODE_RUN;
    501	case INST_ADD:
    502		if (!tpu_exec_get(tpu, &inst->ops[0], &val)) {
    503			tpu->idle = true;
    504			return MODE_READ;
    505		}
    506		tpu->acc = MIN(MAX(tpu->acc + val, -999), 999);
    507		tpu->pc += 1;
    508		return MODE_RUN;
    509	case INST_SUB:
    510		 if (!tpu_exec_get(tpu, &inst->ops[0], &val)) {
    511			 tpu->idle = true;
    512			 return MODE_READ;
    513		 }
    514		tpu->acc = MIN(MAX(tpu->acc - val, -999), 999);
    515		tpu->pc += 1;
    516		return MODE_RUN;
    517	case INST_NEG:
    518		tpu->acc = -tpu->acc;
    519		tpu->pc += 1;
    520		return MODE_RUN;
    521	case INST_JMP:
    522		tpu_jmp_label(tpu, inst->ops[0].val.label);
    523		return MODE_RUN;
    524	case INST_JEZ:
    525		if (tpu->acc == 0)
    526			tpu_jmp_label(tpu, inst->ops[0].val.label);
    527		else
    528			tpu->pc += 1;
    529		return MODE_RUN;
    530	case INST_JNZ:
    531		if (tpu->acc != 0)
    532			tpu_jmp_label(tpu, inst->ops[0].val.label);
    533		else
    534			tpu->pc += 1;
    535		return MODE_RUN;
    536	case INST_JGZ:
    537		if (tpu->acc > 0)
    538			tpu_jmp_label(tpu, inst->ops[0].val.label);
    539		else
    540			tpu->pc += 1;
    541		return MODE_RUN;
    542	case INST_JLZ:
    543		if (tpu->acc < 0)
    544			tpu_jmp_label(tpu, inst->ops[0].val.label);
    545		else
    546			tpu->pc += 1;
    547		return MODE_RUN;
    548	case INST_JRO:
    549		tpu->pc += inst->ops[0].val.lit;
    550		if (tpu->pc < 0) tpu->pc = 0;
    551		if (tpu->pc >= tpu->inst_cnt)
    552			tpu->pc = (int) tpu->inst_cnt - 1;
    553		return MODE_RUN;
    554	default:
    555		abort();
    556	}
    557}
    558
    559void
    560tpu_step(struct tpu *tpu)
    561{
    562	int prev_mode;
    563
    564	prev_mode = tpu->mode;
    565	if (tpu->pc < tpu->inst_cnt) {
    566		tpu->mode = tpu_exec(tpu, &tpu->insts[tpu->pc]);
    567		if (tpu->pc >= tpu->inst_cnt) tpu->pc = 0;
    568	} else {
    569		tpu->mode = MODE_IDLE;
    570		tpu->idle = true;
    571	}
    572}
    573
    574void
    575tpu_map_init(struct tpu_map *map)
    576{
    577	memset(map->buckets, 0, sizeof(void *) * TPU_MAP_BUCKETS);
    578}
    579
    580void
    581tpu_map_deinit(struct tpu_map *map)
    582{
    583	struct tpu_map_link *link, *next;
    584	size_t i;
    585
    586	for (i = 0; i < TPU_MAP_BUCKETS; i++) {
    587		link = map->buckets[i];
    588		while (link) {
    589			next = link->next;
    590			tpu_deinit(link->tpu);
    591			free(link->tpu);
    592			free(link);
    593			link = next;
    594		}
    595	}
    596}
    597
    598static struct tpu_map_link **
    599tpu_map_link_pos(struct tpu_map *map, int x, int y)
    600{
    601	struct tpu_map_link **link;
    602	size_t i;
    603
    604	i = (size_t) (x + y) % TPU_MAP_BUCKETS;
    605	link = &map->buckets[i];
    606	while (*link && !((*link)->x == x && (*link)->y == y))
    607		link = &(*link)->next;
    608
    609	return link;
    610}
    611
    612bool
    613tpu_map_add(struct tpu_map *map, struct tpu *tpu)
    614{
    615	struct tpu_map_link **pos, *link;
    616
    617	pos = tpu_map_link_pos(map, tpu->x, tpu->y);
    618	if (*pos) return false;
    619	*pos = link = malloc(sizeof(struct tpu_map_link));
    620	if (!link) die("malloc:");
    621	link->tpu = tpu;
    622	link->x = tpu->x;
    623	link->y = tpu->y;
    624	link->next = NULL;
    625	return true;
    626}
    627
    628struct tpu *
    629tpu_map_get(struct tpu_map *map, int x, int y)
    630{
    631	struct tpu_map_link **link;
    632
    633	if (x < 0 || y < 0) return NULL;
    634
    635	link = tpu_map_link_pos(map, x, y);
    636	if (!*link) return NULL;
    637
    638	return (*link)->tpu;
    639}
    640
    641void
    642tis_init(struct tis *tis)
    643{
    644	tpu_map_init(&tis->tpu_map);
    645	memset(&tis->in_ports, 0, TIS_MAX_IO_PORTS * sizeof(void *));
    646	memset(&tis->out_ports, 0, TIS_MAX_IO_PORTS * sizeof(void *));
    647	tis->steps = 0;
    648	tis->show_stats = false;
    649	tis->asm_filepath = NULL;
    650}
    651
    652void
    653tis_deinit(struct tis *tis)
    654{
    655	int i;
    656
    657	tpu_map_deinit(&tis->tpu_map);
    658	for (i = 0; i < TIS_MAX_IO_PORTS; i++) {
    659		if (tis->in_ports[i]) {
    660			tpu_io_port_deinit(tis->in_ports[i]);
    661			free(tis->in_ports[i]);
    662		}
    663		if (tis->out_ports[i]) {
    664			tpu_io_port_deinit(tis->out_ports[i]);
    665			free(tis->out_ports[i]);
    666		}
    667	}
    668	free(tis->asm_filepath);
    669}
    670
    671bool
    672tis_step(struct tis *tis)
    673{
    674	struct tpu_map_link *link;
    675	bool running;
    676	size_t i;
    677
    678	for (i = 0; i < TPU_MAP_BUCKETS; i++) {
    679		link = tis->tpu_map.buckets[i];
    680		for (; link; link = link->next)
    681			tpu_step(link->tpu);
    682	}
    683
    684	tis_communicate(tis);
    685
    686	for (i = 0; i < TPU_MAP_BUCKETS; i++) {
    687		link = tis->tpu_map.buckets[i];
    688		for (; link; link = link->next)
    689			tpu_update(link->tpu);
    690	}
    691
    692	for (i = 0; i < TIS_MAX_IO_PORTS; i++) {
    693		if (tis->in_ports[i])
    694			tpu_port_update(&tis->in_ports[i]->port);
    695		if (tis->out_ports[i])
    696			tpu_port_update(&tis->out_ports[i]->port);
    697	}
    698
    699	running = false;
    700	for (i = 0; i < TPU_MAP_BUCKETS; i++) {
    701		link = tis->tpu_map.buckets[i];
    702		for (; link; link = link->next) {
    703			running |= !link->tpu->idle;
    704			if (link->tpu->pc < link->tpu->inst_cnt) {
    705				link->tpu->steps += 1;
    706				if (link->tpu->idle)
    707					link->tpu->idle_steps += 1;
    708			}
    709		}
    710	}
    711
    712	tis->steps += 1;
    713
    714	return running;
    715}
    716
    717void
    718tis_communicate(struct tis *tis)
    719{
    720	struct tpu_io_port *io_port;
    721	char buf[16], *s;
    722	int val, i;
    723
    724	for (i = 0; i < TIS_MAX_IO_PORTS; i++) {
    725		io_port = tis->in_ports[i];
    726		if (!io_port) continue;
    727		if (!io_port->port.attached || io_port->port.writing)
    728			continue;
    729		if (!io_port->file) continue;
    730		while ((s = fgets(buf, sizeof(buf), io_port->file))) {
    731			if ((s = strchr(buf, '\n'))) *s = '\0';
    732			if (!*buf) continue;
    733			val = (int) strtol(buf, &s, 10);
    734			if (!s || *s != '\0')
    735				die("communicate: invalid input '%s'", buf);
    736			io_port->port.out = val;
    737			io_port->port.writing = true;
    738			io_port->io_step = tis->steps;
    739			break;
    740		}
    741	}
    742
    743	for (i = 0; i < TIS_MAX_IO_PORTS; i++) {
    744		io_port = tis->out_ports[i];
    745		if (!io_port) continue;
    746		if (!io_port->port.attached || !io_port->port.avail)
    747			continue;
    748		if (io_port->file)
    749			fprintf(io_port->file, "%i\n", io_port->port.in);
    750		io_port->port.reset_in = true;
    751		tpu_port_update(&io_port->port);
    752		io_port->io_step = tis->steps;
    753	}
    754}
    755
    756struct tis_stats
    757tis_gen_stats(struct tis *tis)
    758{
    759	struct tis_stats stats;
    760	struct tpu_map_link *link;
    761	size_t idle_steps;
    762	size_t all_steps;
    763	int i;
    764
    765	stats.steps = 0;
    766	stats.nodes = 0;
    767	stats.insts = 0;
    768	stats.idle = 0;
    769
    770	for (i = 0; i < TIS_MAX_IO_PORTS; i++) {
    771		if (tis->out_ports[i] && tis->out_ports[i]->io_step > 0) {
    772			stats.steps = MAX(stats.steps, tis->out_ports[i]->io_step + 1);
    773		}
    774	}
    775
    776	all_steps = 0;
    777	idle_steps = 0;
    778	for (i = 0; i < TPU_MAP_BUCKETS; i++) {
    779		link = tis->tpu_map.buckets[i];
    780		for (; link; link = link->next) {
    781			stats.insts += link->tpu->inst_cnt;
    782			idle_steps += link->tpu->idle_steps;
    783			all_steps += link->tpu->steps;
    784			stats.nodes += (link->tpu->inst_cnt > 0);
    785		}
    786	}
    787
    788	if (!stats.steps) stats.steps = all_steps;
    789
    790	if (all_steps == 0)
    791		stats.idle = 0;
    792	else
    793		stats.idle = (float) idle_steps / (float) all_steps;
    794
    795	return stats;
    796}