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>
This commit is contained in:
rajames
2026-10-15 11:25:24 -04:00
co-authored by Claude Opus 5.5
parent 42f8a13914
commit 32753f5479
7 changed files with 165 additions and 11 deletions
+13 -5
View File
@@ -43,6 +43,14 @@ static int faulted(v4_node *n, v4_cell addr)
return 1;
}
/* The same for a fetch, which may also read the ROM (node.h). */
static int faulted_read(v4_node *n, v4_cell addr)
{
if (v4_node_addr_readable(n, addr)) return 0;
v4_node_fault(n, V4_FAULT_ADDRESS, addr);
return 1;
}
/* D-16: what each opcode takes from the stacks and leaves on them. If the
* data stack holds fewer than `dneed`, or the return stack fewer than
* `rneed`, or a stack the opcode leaves one more on is full, it is a fault
@@ -142,7 +150,7 @@ void v4_exec_op(v4_node *n, v4_exec_state *es, v4_heat *h,
break;
case V4_OP_FETCH_P:
if (faulted(n, n->p)) return;
if (faulted_read(n, n->p)) return;
if (v4_node_port_index(n, n->p) >= 0) {
/* P is a port: the literal is the next word its neighbour
* writes, and P stays where it is */
@@ -154,18 +162,18 @@ void v4_exec_op(v4_node *n, v4_exec_state *es, v4_heat *h,
n->p = add_wrap(n->p, 1);
break;
case V4_OP_FETCH_INC:
if (faulted(n, n->a)) return;
if (faulted_read(n, n->a)) return;
if (!v4_node_read_ready(n, n->a)) return; /* a port with nothing written to it yet */
v4_dstack_push(ds, v4_node_fetch(n, n->a));
n->a = add_wrap(n->a, 1);
break;
case V4_OP_FETCH_B:
if (faulted(n, n->b)) return;
if (faulted_read(n, n->b)) return;
if (!v4_node_read_ready(n, n->b)) return; /* a port with nothing written to it yet */
v4_dstack_push(ds, v4_node_fetch(n, n->b));
break;
case V4_OP_FETCH_A:
if (faulted(n, n->a)) return;
if (faulted_read(n, n->a)) return;
if (!v4_node_read_ready(n, n->a)) return; /* a port with nothing written to it yet */
v4_dstack_push(ds, v4_node_fetch(n, n->a));
break;
@@ -269,7 +277,7 @@ unsigned v4_exec_step_word(v4_node *n, v4_exec_state *es, v4_heat *h)
slot = es->resume_slot;
} else {
/* P itself may be what is outside memory */
if (faulted(n, n->p)) return 0;
if (faulted_read(n, n->p)) return 0;
if (v4_node_port_index(n, n->p) >= 0) {
/* P is a port: the node executes what its neighbour writes
* there, and P stays where it is */