Files
LithosAnanke/v4/src/node.c
T
rajamesandClaude Opus 5.5 32753f5479 feat(v4): the machine has a ROM -- read and run, never written
A read-only range of V4_ROM_WORDS words at V4_ROM_BASE, above RAM
(MACHINE.md 2, DATASHEET-F18.md 5).  Instruction fetch, @p and data fetches
read it; a running programme's store there is an address fault.  Attached by
the owner like every device; a reset detaches it.  The host node's range is
4096 words.  text.c places branches by the whole address space.  The
nucleus image is byte-identical.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 11:25:24 -04:00

286 lines
8.8 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->rom = NULL; /* attached again by the owner, as every device is */
n->rom_words = 0;
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);
}
int v4_node_addr_readable(const v4_node *n, v4_cell addr)
{
return v4_node_addr_ok(addr)
|| ((v4_ucell)addr - (v4_ucell)V4_ROM_BASE) < (v4_ucell)n->rom_words;
}
void v4_node_rom_attach(v4_node *n, const v4_cell *rom, unsigned words)
{
if (!rom || words > V4_ROM_WORDS) { n->rom = NULL; n->rom_words = 0; return; }
n->rom = rom;
n->rom_words = words;
}
v4_cell v4_node_load(const v4_node *n, v4_cell addr)
{
if (v4_node_addr_ok(addr)) return n->mem[(unsigned)addr];
if (v4_node_addr_readable(n, addr)) return n->rom[(v4_ucell)addr - (v4_ucell)V4_ROM_BASE];
return 0;
}
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 == (int)V4_PORT_WIRE) { n->wire = (value >= 0 && value < (v4_cell)V4_PORTS) ? (unsigned)value : V4_PORTS; return; }
if (port == (int)V4_PORT_A) { n->wire_a = value; return; }
if (port == (int)V4_PORT_B) { n->wire_b = value; return; }
if (port == (int)V4_PORT_DO) { n->do_op = value; n->doing = 1; n->spoke = 1; return; }
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", "which port", How and Have 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) return (v4_cell)n->writers;
if (port == (int)V4_PORTS + 3) return (v4_cell)n->readers;
if (port == (int)V4_PORT_WIRE) return (v4_cell)n->wire;
if (port == (int)V4_PORT_A) return n->wire_a;
if (port == (int)V4_PORT_B) return n->wire_b;
if (port == (int)V4_PORT_DO) return n->do_op;
if (port == (int)V4_PORT_HOW) return n->how;
if (port == (int)V4_PORT_HAVE) { n->have_fetched = 1; return (v4_cell)n->have; }
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->wire = 0;
n->wire_a = 0;
n->wire_b = 0;
n->doing = 0;
n->spoke = 0;
n->do_op = 0;
n->how = 0;
n->have = 0;
n->have_fetched = 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_PORT_HAVE) 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;
}
int v4_node_doing(const v4_node *n)
{
return n->doing;
}
void v4_node_done(v4_node *n, v4_cell how)
{
n->how = how;
n->doing = 0;
}
void v4_node_have(v4_node *n, unsigned mask)
{
n->have = mask;
}
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;
}