feat(v4.0.0): block storage, LOAD and LIST (D-19)

- The golden model's host node gets a block storage device: four
  memory-mapped registers (number, address, command, status), 1024-byte
  blocks as 256 cells.  tests/test_node.c.
- capsule/blocks.v4: BLOCK BUFFER UPDATE SAVE-BUFFERS EMPTY-BUFFERS LIST
  LOAD SCR BLK (FORTH-79) and v3's FLUSH THRU and -->, over two buffers.
- The text being interpreted is at the address in (SRC), which QUERY makes
  the terminal's buffer and LOAD a block's; LOAD saves and restores it, so
  blocks nest and the rest of LOAD's line runs afterwards.  WORD makes
  sure a loading block is still in a buffer before it reads.
- In a block, \ skips to the next 64-character line.
- A block number that does not exist, and --> at the terminal, are errors
  with messages (D-18).
- tests/test_host_quit.c: ten transcripts of the v3 binary; nesting three
  deep on two buffers; what reaches the device and when.

v3's LOAD drops the rest of its line, and v3 has no BLK.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rajames
2026-10-05 08:32:04 -04:00
co-authored by Claude Opus 5.5
parent 0fbe1432b7
commit c8dc14897c
10 changed files with 462 additions and 16 deletions
+40
View File
@@ -1,5 +1,6 @@
/* node.c -- the v4 node. See node.h. */
#include "v4/node.h"
#include <stddef.h>
void v4_node_reset(v4_node *n)
{
@@ -16,6 +17,43 @@ void v4_node_reset(v4_node *n)
v4_node_fault_attach(n, -1);
v4_node_stack_regs_attach(n, -1, -1);
v4_node_error_attach(n, -1);
v4_node_storage_attach(n, -1, 0, 0);
}
void v4_node_storage_attach(v4_node *n, v4_cell reg, unsigned char *bytes, unsigned blocks)
{
n->storage_reg = reg;
n->storage = bytes;
n->storage_blocks = blocks;
}
/* BLOCK-COMMAND: do it, and leave the status in BLOCK-STATUS. */
static void storage_command(v4_node *n, v4_cell command)
{
v4_cell num = n->mem[n->storage_reg], addr = n->mem[n->storage_reg + 1];
unsigned char *b;
unsigned i;
n->mem[n->storage_reg + 3] = (v4_cell)-1;
if (!n->storage || num < 0 || (v4_ucell)num >= (v4_ucell)n->storage_blocks) return;
if (!v4_node_addr_ok(addr) || !v4_node_addr_ok(addr + (v4_cell)(V4_BLOCK_CELLS - 1u))) return;
b = n->storage + (size_t)num * V4_BLOCK_BYTES;
if (command == 1) {
for (i = 0; i < V4_BLOCK_CELLS; i++)
n->mem[addr + (v4_cell)i] = (v4_cell)((v4_ucell)b[4u * i] | ((v4_ucell)b[4u * i + 1u] << 8)
| ((v4_ucell)b[4u * i + 2u] << 16) | ((v4_ucell)b[4u * i + 3u] << 24));
} else if (command == 2) {
for (i = 0; i < V4_BLOCK_CELLS; i++) {
v4_ucell w = (v4_ucell)n->mem[addr + (v4_cell)i];
b[4u * i] = (unsigned char)(w & 0xFFu);
b[4u * i + 1u] = (unsigned char)((w >> 8) & 0xFFu);
b[4u * i + 2u] = (unsigned char)((w >> 16) & 0xFFu);
b[4u * i + 3u] = (unsigned char)((w >> 24) & 0xFFu);
}
} else {
return;
}
n->mem[n->storage_reg + 3] = 0;
}
void v4_node_error_attach(v4_node *n, v4_cell addr)
@@ -86,6 +124,8 @@ void v4_node_store(v4_node *n, v4_cell addr, v4_cell value)
if (addr == n->rstack_reg) { v4_rstack_clear(&n->rs); return; }
if (!v4_node_addr_ok(addr)) return;
n->mem[(unsigned)addr] = value;
/* BLOCK-COMMAND. storage_reg is -1 when no device is attached. */
if (n->storage_reg >= 0 && addr == n->storage_reg + 2) { storage_command(n, value); return; }
/* 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);
}