The console's receive side, standing in for the console node until the mesh exists, as CONSOLE-TX does for output. A data fetch (@, @+, @b) from CONSOLE-STATUS gives -1 when a character is pending and 0 when not; from CONSOLE-RX it gives the next character and takes it, or -1 with none pending. The characters come from a queue the test feeds. Instruction words and literals are still fetched with v4_node_load, so code at a register's address is never taken for the register. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
93 lines
3.0 KiB
C
93 lines
3.0 KiB
C
/* node.c -- the v4 node. See node.h. */
|
|
#include "v4/node.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);
|
|
}
|
|
|
|
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)
|
|
{
|
|
/* Precondition checked by the caller; see node.h. The unsigned compare is
|
|
* deliberate: `addr` is signed, and a negative address must not wrap into
|
|
* a large positive one and read the top of memory instead. */
|
|
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;
|
|
}
|
|
n->mem[(unsigned)addr] = 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;
|
|
}
|
|
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;
|
|
}
|