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:
co-authored by
Claude Opus 5.5
parent
0fbe1432b7
commit
c8dc14897c
@@ -195,6 +195,69 @@ static void test_out_of_range_word_address_touches_nothing(void)
|
||||
free(n);
|
||||
}
|
||||
|
||||
/* D-19: the block storage device. Four registers; a store to the third
|
||||
* reads a block into 256 cells of memory or writes it from them. */
|
||||
static void test_storage_device(void)
|
||||
{
|
||||
static unsigned char blocks[4 * V4_BLOCK_BYTES];
|
||||
const v4_cell REG = 40, BUF = 100;
|
||||
v4_node *n = malloc(sizeof *n);
|
||||
unsigned i;
|
||||
int ok;
|
||||
if (!n) { CHECK(0, "malloc"); return; }
|
||||
v4_node_reset(n);
|
||||
CHECK(n->storage_reg == -1, "a fresh node has no storage");
|
||||
for (i = 0; i < sizeof blocks; i++) blocks[i] = (unsigned char)(i * 7u + (i >> 10));
|
||||
v4_node_storage_attach(n, REG, blocks, 4);
|
||||
|
||||
/* read block 2 */
|
||||
n->mem[BUF - 1] = 111; n->mem[BUF + 256] = 222;
|
||||
v4_node_store(n, REG, 2); v4_node_store(n, REG + 1, BUF);
|
||||
n->mem[REG + 3] = 55;
|
||||
v4_node_store(n, REG + 2, 1);
|
||||
CHECK(n->mem[REG + 3] == 0, "a read that works leaves status 0");
|
||||
for (ok = 1, i = 0; i < V4_BLOCK_CELLS; i++) {
|
||||
const unsigned char *b = blocks + 2 * V4_BLOCK_BYTES + 4u * i;
|
||||
v4_ucell want = (v4_ucell)b[0] | ((v4_ucell)b[1] << 8) | ((v4_ucell)b[2] << 16) | ((v4_ucell)b[3] << 24);
|
||||
if ((v4_ucell)n->mem[BUF + (v4_cell)i] != want) ok = 0;
|
||||
}
|
||||
CHECK(ok, "the block is in memory, four bytes to a cell, the first lowest, the rest of the cell zero");
|
||||
CHECK(n->mem[BUF - 1] == 111 && n->mem[BUF + 256] == 222, "and only 256 cells were written");
|
||||
CHECK(n->mem[REG] == 2 && n->mem[REG + 1] == BUF && n->mem[REG + 2] == 1, "the registers are memory words");
|
||||
|
||||
/* write it to block 0, changed */
|
||||
n->mem[BUF] = (v4_cell)0x44332211; n->mem[BUF + 255] = (v4_cell)(v4_ucell)0xAABBCCDDu;
|
||||
v4_node_store(n, REG, 0);
|
||||
v4_node_store(n, REG + 2, 2);
|
||||
CHECK(n->mem[REG + 3] == 0 && blocks[0] == 0x11 && blocks[1] == 0x22 && blocks[2] == 0x33 && blocks[3] == 0x44
|
||||
&& blocks[1020] == 0xDD && blocks[1023] == 0xAA, "a write puts the low four bytes of each cell on the device");
|
||||
CHECK(memcmp(blocks + 4, blocks + 2 * V4_BLOCK_BYTES + 4, 1016) == 0, "the rest of the block as it was read");
|
||||
CHECK(blocks[V4_BLOCK_BYTES] == (unsigned char)(1024u * 7u + 1u), "and the next block untouched");
|
||||
|
||||
/* what does not work: status -1, nothing moved */
|
||||
v4_node_store(n, REG, 4); v4_node_store(n, REG + 2, 1);
|
||||
CHECK(n->mem[REG + 3] == -1, "a block past the last");
|
||||
v4_node_store(n, REG, -1); v4_node_store(n, REG + 2, 1);
|
||||
CHECK(n->mem[REG + 3] == -1, "a negative block");
|
||||
v4_node_store(n, REG, 1); v4_node_store(n, REG + 1, (v4_cell)V4_NODE_WORDS - 255); v4_node_store(n, REG + 2, 1);
|
||||
CHECK(n->mem[REG + 3] == -1, "cells that run past the end of memory");
|
||||
v4_node_store(n, REG + 1, -4); v4_node_store(n, REG + 2, 2);
|
||||
CHECK(n->mem[REG + 3] == -1, "a negative address");
|
||||
v4_node_store(n, REG + 1, BUF); v4_node_store(n, REG + 2, 3);
|
||||
CHECK(n->mem[REG + 3] == -1, "an unknown command");
|
||||
v4_node_store(n, REG + 1, (v4_cell)V4_NODE_WORDS - 256); v4_node_store(n, REG + 2, 1);
|
||||
CHECK(n->mem[REG + 3] == 0 && v4_node_guards_intact(n), "the last 256 cells of memory are usable");
|
||||
|
||||
/* detached: plain memory */
|
||||
v4_node_storage_attach(n, -1, 0, 0);
|
||||
n->mem[REG + 3] = 9; v4_node_store(n, REG + 2, 1);
|
||||
CHECK(n->mem[REG + 3] == 9 && n->mem[REG + 2] == 1, "detached, the registers are memory");
|
||||
v4_node_storage_attach(n, REG, blocks, 4);
|
||||
v4_node_reset(n);
|
||||
CHECK(n->storage_reg == -1, "reset detaches the device");
|
||||
free(n);
|
||||
}
|
||||
|
||||
static void test_ends_are_distinguishable(void)
|
||||
{
|
||||
/* The two boundaries carry different patterns precisely so that a failure
|
||||
@@ -254,6 +317,7 @@ int main(void)
|
||||
test_linear_overrun_low_is_caught();
|
||||
test_linear_overrun_high_is_caught();
|
||||
test_out_of_range_word_address_touches_nothing();
|
||||
test_storage_device();
|
||||
test_ends_are_distinguishable();
|
||||
test_workload_never_false_alarms();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user