Files
LithosAnanake/v4/src/node.c
T
rajamesandClaude Opus 5.5 6015c87157 revert(v4.0.0): step 6d, the wait, is backed out -- two reviews found it unsound
Ruled 2026-10-08.  The engine, the nucleus and their tests are as they
were at dfabfa46, before step 6d; the code of the wait and its tests are
in history at 075385ab..2cf37aab.  The limit of MESH.md 7b.7 stands: a
node that is stuck can hold up its neighbours until Hera kills it.

MESH.md 7c is kept as the record of the design as approved, and 7c.6 says
what was built, what the two reviews found, the cause the findings share
-- a node's message machinery runs on the stacks its text is using, and a
fault abandons whatever was in progress -- and what a second attempt
must settle before anything is built.

Kept from it, in hosted-check: a line that leaves 29 values on the stack,
and WORDS with 26 values on it.  Both hung the products at some commit of
the wait.

make -C v4 test and sanitize at both widths, hosted-check, lint; three
bare-metal boots typing both.  POST 538 of 538, word_count=317,
dict_hash=0xc0769523a47b7dc3 on all six: the hash of step 6c's code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 07:31:40 -04:00

242 lines
7.3 KiB
C

/* node.c -- the v4 node. See node.h. */
#include "v4/node.h"
#include <stddef.h>
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;
}