/* node.c -- the v4 node. See node.h. */ #include "v4/node.h" #include void v4_node_reset(v4_node *n) { n->p = 0; n->a = 0; n->b = 0; for (unsigned i = 0; i < V4_NODE_WORDS; i++) n->mem[i] = 0; v4_guard_fill(n->mem_guard_head, V4_MEM_BOUND, V4_GUARD_PATTERN_HEAD); v4_guard_fill(n->mem_guard_tail, V4_MEM_BOUND, V4_GUARD_PATTERN_TAIL); v4_dstack_reset(&n->ds); v4_rstack_reset(&n->rs); v4_node_console_attach(n, -1); v4_node_console_input_attach(n, -1, -1); v4_node_fault_attach(n, -1); v4_node_stack_regs_attach(n, -1, -1); v4_node_error_attach(n, -1); v4_node_port_attach(n, -1); } void v4_node_error_attach(v4_node *n, v4_cell addr) { n->error_reg = addr; } int v4_node_addr_ok(v4_cell addr) { /* One unsigned compare covers both ends: a negative address is a very * large unsigned one. */ return (v4_ucell)addr < (v4_ucell)V4_NODE_WORDS; } void v4_node_stack_regs_attach(v4_node *n, v4_cell d, v4_cell r) { n->dstack_reg = d; n->rstack_reg = r; } void v4_node_fault_attach(v4_node *n, v4_cell table) { n->fault_vector = (v4_node_addr_ok(table) && v4_node_addr_ok(table + (v4_cell)(V4_FAULT_KINDS - 1u))) ? table : (v4_cell)-1; n->fault_addr = 0; n->fault_kind = 0; n->faults = 0; n->stopped = 0; } void v4_node_fault(v4_node *n, unsigned kind, v4_cell addr) { n->fault_kind = kind; n->fault_addr = addr; n->faults++; v4_rstack_clear(&n->rs); if (kind != V4_FAULT_RAISED) v4_dstack_clear(&n->ds); if (n->fault_vector >= 0) n->p = n->fault_vector + (v4_cell)kind; else n->stopped = 1; } int v4_node_guards_intact(const v4_node *n) { return v4_guard_intact(n->mem_guard_head, V4_MEM_BOUND, V4_GUARD_PATTERN_HEAD) && v4_guard_intact(n->mem_guard_tail, V4_MEM_BOUND, V4_GUARD_PATTERN_TAIL) && v4_dstack_guards_intact(&n->ds) && v4_rstack_guards_intact(&n->rs); } v4_cell v4_node_load(const v4_node *n, v4_cell addr) { if (!v4_node_addr_ok(addr)) return 0; return n->mem[(unsigned)addr]; } void v4_node_store(v4_node *n, v4_cell addr, v4_cell value) { /* CONSOLE-TX: capture the character, leave memory alone. console_tx is * -1 when no console is attached, which no valid address equals. */ if (addr == n->console_tx) { if (n->console_len < V4_CONSOLE_CAP) n->console[n->console_len++] = (unsigned char)((v4_ucell)value & 0xFFu); else n->console_dropped++; return; } /* A port: the node keeps what it wrote and is blocked until its * neighbour has taken it. */ { int port = v4_node_port_index(n, addr); if (port >= 0 && port < (int)V4_PORTS) { n->request = value; n->ask_port = (unsigned)port; n->asking = 1; return; } if (port >= 0) { v4_node_fault(n, V4_FAULT_ADDRESS, addr); return; } /* "any" and "which port" are not written to */ } /* The stack registers: a store empties the stack. -1 when not attached. */ if (addr == n->dstack_reg) { v4_dstack_clear(&n->ds); return; } if (addr == n->rstack_reg) { v4_rstack_clear(&n->rs); return; } if (!v4_node_addr_ok(addr)) return; n->mem[(unsigned)addr] = value; /* NODE-ERROR: a non-zero code raises an error. -1 when not attached. */ if (addr == n->error_reg && value != 0) v4_node_fault(n, V4_FAULT_RAISED, value); } v4_cell v4_node_fetch(v4_node *n, v4_cell addr) { /* console_rx and console_status are -1 when no input is attached, which * no valid address equals. */ if (addr == n->console_status) return n->input_pos < n->input_len ? (v4_cell)-1 : (v4_cell)0; if (addr == n->console_rx) { v4_cell c; if (n->input_pos >= n->input_len) return (v4_cell)-1; c = (v4_cell)n->input[n->input_pos++]; if (n->input_pos == n->input_len) n->input_pos = n->input_len = 0; return c; } { int port = v4_node_port_index(n, addr); if (port == (int)V4_PORTS + 1) return (v4_cell)n->last_from; if (port == (int)V4_PORTS + 2) { n->since_look = 0; return (v4_cell)n->writers; } if (port == (int)V4_PORTS + 3) { n->since_look = 0; return (v4_cell)n->readers; } if (port >= 0) { /* the word its neighbour wrote; v4_node_read_ready has said it is there */ if (!n->given) return 0; n->given = 0; if (port == (int)V4_PORT_ANY) n->last_from = n->given_port; return n->given_value; } } if (addr == n->dstack_reg) return (v4_cell)n->ds.depth; if (addr == n->rstack_reg) return (v4_cell)n->rs.depth; return v4_node_load(n, addr); } void v4_node_console_input_attach(v4_node *n, v4_cell rx, v4_cell status) { n->console_rx = rx; n->console_status = status; n->input_len = 0; n->input_pos = 0; } unsigned v4_node_console_feed(v4_node *n, const void *chars, unsigned len) { const unsigned char *c = (const unsigned char *)chars; unsigned i, pending = n->input_len - n->input_pos, took = 0; /* Slide what is pending to the front so the whole buffer is usable. */ if (n->input_pos > 0) { for (i = 0; i < pending; i++) n->input[i] = n->input[n->input_pos + i]; n->input_pos = 0; n->input_len = pending; } while (took < len && n->input_len < V4_CONSOLE_CAP) n->input[n->input_len++] = c[took++]; return took; } void v4_node_console_attach(v4_node *n, v4_cell addr) { n->console_tx = addr; n->console_len = 0; n->console_dropped = 0; } void v4_node_port_attach(v4_node *n, v4_cell base) { n->port = base; n->asking = 0; n->request = 0; n->ask_port = 0; n->reading = 0; n->read_port = 0; n->given = 0; n->given_value = 0; n->given_port = 0; n->last_from = 0; n->writers = 0; n->readers = 0; n->since_look = 0; } void v4_node_port_status(v4_node *n, unsigned writers, unsigned readers) { n->writers = writers; n->readers = readers; } int v4_node_port_index(const v4_node *n, v4_cell addr) { if (n->port < 0 || addr < n->port || addr > n->port + (v4_cell)V4_PORTS + 3) return -1; return (int)(addr - n->port); } int v4_node_read_ready(v4_node *n, v4_cell addr) { int port = v4_node_port_index(n, addr); if (port < 0 || port > (int)V4_PORT_ANY) return 1; if (n->given && (port == (int)V4_PORT_ANY || (unsigned)port == n->given_port)) return 1; n->reading = 1; n->read_port = (unsigned)port; return 0; } void v4_node_port_give(v4_node *n, unsigned port, v4_cell value) { n->given = 1; n->given_value = value; n->given_port = port; n->reading = 0; } void v4_node_born(v4_node *n, v4_cell base) { v4_node_reset(n); v4_node_port_attach(n, base); n->p = base + (v4_cell)V4_PORT_ANY; } void v4_node_port_served(v4_node *n) { n->asking = 0; n->since_look++; } unsigned v4_node_words_since_look(const v4_node *n) { return n->since_look; } int v4_node_port_gone(v4_node *n, v4_cell code) { if (code == 0 || n->error_reg < 0 || n->fault_vector < 0 || n->stopped) return 0; if (n->asking) n->asking = 0; else if (n->reading && !n->given && n->read_port != V4_PORT_ANY) n->reading = 0; else return 0; v4_node_store(n, n->error_reg, code); /* raises it: P is the node's handler now */ return 1; }