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
+31
View File
@@ -92,6 +92,11 @@ typedef struct {
v4_cell dstack_reg; /* its word address, or -1 */
v4_cell rstack_reg; /* its word address, or -1 */
/* The block storage device (D-19). See v4_node_storage_attach. */
v4_cell storage_reg; /* word address of its four registers, or -1 */
unsigned char *storage; /* the blocks, 1024 bytes each */
unsigned storage_blocks; /* how many */
/* NODE-ERROR as a trap (D-18). See v4_node_error_attach. */
v4_cell error_reg; /* its word address, or -1 */
} v4_node;
@@ -238,6 +243,32 @@ void v4_node_fault(v4_node *n, unsigned kind, v4_cell addr);
* detaches both (-1); the addresses are the caller's choice (D-4). */
void v4_node_stack_regs_attach(v4_node *n, v4_cell d, v4_cell r);
/* THE BLOCK STORAGE DEVICE (DECOMPOSITION.md D-19, 5.11).
*
* Mass storage is a device service; until the mesh exists the model stands
* in for it on the host node with four memory-mapped registers at `reg`:
*
* reg + 0 BLOCK-NUMBER which block, 0 .. blocks - 1
* reg + 1 BLOCK-ADDRESS word address of 256 cells of node memory
* reg + 2 BLOCK-COMMAND a store of 1 reads the block into those cells,
* a store of 2 writes it from them
* reg + 3 BLOCK-STATUS 0 after a command that worked, -1 after one
* that did not: no such block, cells that are
* not all in memory, or an unknown command
*
* The first, second and fourth are memory words the device reads and writes;
* only a store to the third does anything. A block is 1024 bytes, and a
* byte address is four times a word address plus 0 .. 3 (D-1), so a block
* is 256 cells, four bytes to a cell, the first byte lowest, whatever the
* cell width: a read leaves the rest of each cell zero, and a write takes
* the low 32 bits.
*
* `bytes` is the caller's: `blocks` * 1024 bytes, which the device reads and
* writes and never frees. v4_node_reset detaches it (reg -1). */
#define V4_BLOCK_BYTES 1024u
#define V4_BLOCK_CELLS 256u
void v4_node_storage_attach(v4_node *n, v4_cell reg, unsigned char *bytes, unsigned blocks);
/* NODE-ERROR as a trap (DECOMPOSITION.md D-18, ruled 2026-10-04: every error
* is guarded, shown, and returns the node to its prompt).
*