feat(v4.0.0): the block words are the kernel's -- v3's way in full
Ruled 2026-10-07. BLOCK, BUFFER, UPDATE, SAVE-BUFFERS and EMPTY-BUFFERS are each one kernel request on port 0, by block number only. The node has a window of four slots in its memory, as a v3 VM has; the kernel copies blocks into it, keeps the record of which block is in which slot, and decides when a block is written (v4/system/blocks.c). The node keeps no record and gives the kernel no address, so a request cannot overwrite the node's code. When a block is written stays FORTH-79's: UPDATE marks it. When the chain of devices changes the slots are let go, as in v3. The window is 512 cells more than the two buffers were; the dictionary space ends that much lower, at 13824. MESH.md 8.5 had said, from the review, that a v3 VM's BLOCK is the kernel's buffer. It is not, and the section now says what was reported, what v3 does, and what was ruled. make -C v4 test, sanitize and hosted-check pass; amd64, aarch64 and riscv64 boot, POST 538 of 538, the same hashes on all six: logs/20261007-092835, -093112, -093456. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
7f4d946d2e
commit
9bfd5071d2
+8
-9
@@ -59,7 +59,7 @@
|
||||
#define HEND ((HBUF_W + 16) * 4)
|
||||
#define XVARS (HBUF_W - 4) /* (X): words.v4, 4 cells */
|
||||
#define QBASE (XVARS - 40) /* the Q words' and DUMP's scratch cells, 39 in all */
|
||||
#define BVARS (QBASE - 16) /* blocks.v4: (B) 6 cells, then SCR BLK (SRC) (SRC-HOOK) */
|
||||
#define BVARS (QBASE - 16) /* blocks.v4: 6 cells not used now, then SCR BLK (SRC) (SRC-HOOK) */
|
||||
#define SCR (BVARS + 6)
|
||||
#define BLK (BVARS + 7)
|
||||
#define SRC (BVARS + 8)
|
||||
@@ -99,8 +99,7 @@
|
||||
#define WORD_FORGOTTEN (BUF0_W - 12) /* the xt of the word that is told when entries go, or 0 */
|
||||
#define LOG_LEVEL (BUF0_W - 5) /* the level at or below which a message is printed */
|
||||
#define BOOT_CELLS (BVARS + 14) /* (BOOT): DP and LATEST as the loader left them, 2 cells */
|
||||
#define BUF0_W (BVARS - 2 * 256) /* the two block buffers, 256 cells each */
|
||||
#define BUF1_W (BUF0_W + 256)
|
||||
#define BUF0_W (BVARS - (v4_cell)(V4_BLOCK_SLOTS * V4_BLOCK_CELLS)) /* the block window: the kernel's slots, 256 cells each (v4/blocks.h) */
|
||||
/* the ports' ten addresses and the two that follow them end below BUF0_W - 20 */
|
||||
typedef char host_map_ports_fit[(V4_PORTS <= 8u) ? 1 : -1];
|
||||
/* Nothing printed and no message waiting: a node's variables for them, as at switch-on. */
|
||||
@@ -116,7 +115,7 @@ typedef char host_map_ports_fit[(V4_PORTS <= 8u) ? 1 : -1];
|
||||
#define DICT_W ((v4_cell)8192)
|
||||
#endif
|
||||
#ifndef DICT_END_W
|
||||
#define DICT_END_W ((v4_cell)14336)
|
||||
#define DICT_END_W ((v4_cell)13824) /* 512 cells lower than it was: the block window is four slots now */
|
||||
#endif
|
||||
/* the messages waiting are above the dictionary */
|
||||
typedef char host_map_queue_fits[(MQ_W >= DICT_END_W) ? 1 : -1];
|
||||
@@ -169,7 +168,6 @@ static int host_load(v4_text *tx, v4_node *n, const char *const *files, unsigned
|
||||
v4_text_constant(tx, "(S)", SVARS);
|
||||
v4_text_constant(tx, "(W)", WVARS);
|
||||
v4_text_constant(tx, "(X)", XVARS);
|
||||
v4_text_constant(tx, "(B)", BVARS);
|
||||
v4_text_constant(tx, "(BOOT)", BOOT_CELLS);
|
||||
v4_text_constant(tx, "(LOG-LEVEL)", LOG_LEVEL);
|
||||
v4_text_constant(tx, "(ACL-HOOK)", ACL_HOOK);
|
||||
@@ -209,10 +207,11 @@ static int host_load(v4_text *tx, v4_node *n, const char *const *files, unsigned
|
||||
v4_text_constant(tx, "(OUT)", OUT_W);
|
||||
v4_text_constant(tx, "(WORD-DEFINED)", WORD_DEFINED);
|
||||
v4_text_constant(tx, "(WORD-FORGOTTEN)", WORD_FORGOTTEN);
|
||||
v4_text_constant(tx, "BLOCK-READ#", V4_REQ_BLOCK_READ); /* the kernel's two block requests (v4/blocks.h) */
|
||||
v4_text_constant(tx, "BLOCK-WRITE#", V4_REQ_BLOCK_WRITE);
|
||||
v4_text_constant(tx, "BUF0", BUF0_W);
|
||||
v4_text_constant(tx, "BUF1", BUF1_W);
|
||||
v4_text_constant(tx, "BLOCK#", V4_REQ_BLOCK); /* the kernel's block requests (v4/blocks.h) */
|
||||
v4_text_constant(tx, "BUFFER#", V4_REQ_BUFFER);
|
||||
v4_text_constant(tx, "UPDATE#", V4_REQ_UPDATE);
|
||||
v4_text_constant(tx, "SAVE-BUFFERS#", V4_REQ_SAVE_BUFFERS);
|
||||
v4_text_constant(tx, "EMPTY-BUFFERS#", V4_REQ_EMPTY_BUFFERS);
|
||||
v4_text_constant(tx, "(Q/)", QBASE); /* 5 cells */
|
||||
v4_text_constant(tx, "(QE)", QBASE + 5); /* 8 */
|
||||
v4_text_constant(tx, "(QR)", QBASE + 13); /* 5 */
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
/* test_blocks.c -- the kernel serves a node's block requests.
|
||||
* docs/v4.0.0/MESH.md sections 8.2 and 8.3.
|
||||
*
|
||||
* A node, stopped, with a number and an address on its data stack; the
|
||||
* blocks are v3's block subsystem (v3/src/block_subsystem.c): its fast RAM
|
||||
* and a device of 64 blocks.
|
||||
*/
|
||||
#include "v4/blocks.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "block_subsystem.h"
|
||||
|
||||
extern void sf_time_init(void);
|
||||
extern void log_set_level(int level);
|
||||
|
||||
static int failures = 0, checks = 0;
|
||||
#define CHECK(c,...) do{checks++; if(!(c)){failures++; printf("FAIL %s:%d: ",__FILE__,__LINE__); printf(__VA_ARGS__); printf("\n");}}while(0)
|
||||
|
||||
#define DEVICE_BLOCKS 64u
|
||||
#define BUF 100
|
||||
static unsigned char ram[(size_t)BLK_RAM_BLOCKS * BLK_FORTH_SIZE], dev[DEVICE_BLOCKS * BLK_FORTH_SIZE];
|
||||
static v4_node n;
|
||||
|
||||
static v4_cell ask(v4_cell request, v4_cell block, v4_cell addr)
|
||||
{
|
||||
v4_dstack_push(&n.ds, 7777);
|
||||
v4_dstack_push(&n.ds, block);
|
||||
v4_dstack_push(&n.ds, addr);
|
||||
if (!v4_blocks_serve(&n, request)) return -100;
|
||||
{
|
||||
v4_cell status = v4_dstack_pop(&n.ds);
|
||||
return v4_dstack_pop(&n.ds) == 7777 ? status : -200; /* what was under its arguments is still there */
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
unsigned i;
|
||||
v4_cell top = (v4_cell)V4_NODE_WORDS;
|
||||
|
||||
printf("v4 block request tests: V4_CELL_BITS=%d, V4_NODE_WORDS=%u\n", V4_CELL_BITS, (unsigned)V4_NODE_WORDS);
|
||||
sf_time_init();
|
||||
log_set_level(-1);
|
||||
CHECK(blk_subsys_init(ram, sizeof ram) == BLK_OK && blk_subsys_add_raw_device(dev, DEVICE_BLOCKS) == BLK_OK, "the kernel's chain: fast RAM and a device of 64 blocks");
|
||||
v4_node_reset(&n);
|
||||
|
||||
/* ---- a block is written and read ---- */
|
||||
for (i = 0; i < V4_BLOCK_CELLS; i++) n.mem[BUF + i] = (v4_cell)((v4_ucell)0x41424344u + i);
|
||||
#if V4_CELL_BITS > 32
|
||||
n.mem[BUF + 5] = (v4_cell)0x7766554433221100LL; /* only its low 32 bits are the block's */
|
||||
#endif
|
||||
CHECK(ask(V4_REQ_BLOCK_WRITE, 2050, BUF) == V4_BLOCK_OK, "a block is written");
|
||||
CHECK(dev[2 * 1024] == 0x44 && dev[2 * 1024 + 1] == 0x43 && dev[2 * 1024 + 2] == 0x42 && dev[2 * 1024 + 3] == 0x41 && dev[2 * 1024 + 4] == 0x45,
|
||||
"block 2050 is the device's third, four bytes to a cell, the first lowest");
|
||||
CHECK(dev[1 * 1024 + 1023] == 0 && dev[3 * 1024] == 0, "and no other block was touched");
|
||||
for (i = 0; i < V4_BLOCK_CELLS; i++) n.mem[400 + i] = -1;
|
||||
CHECK(ask(V4_REQ_BLOCK_READ, 2050, 400) == V4_BLOCK_OK, "it is read");
|
||||
for (i = 0; i < V4_BLOCK_CELLS && (i == 5 || n.mem[400 + i] == (v4_cell)((v4_ucell)0x41424344u + i)); i++) { }
|
||||
CHECK(i == V4_BLOCK_CELLS, "as it was written: cell %u differs", i);
|
||||
#if V4_CELL_BITS > 32
|
||||
CHECK(n.mem[400 + 5] == (v4_cell)0x33221100, "the rest of each cell is zero");
|
||||
#else
|
||||
CHECK(n.mem[400 + 5] == (v4_cell)((v4_ucell)0x41424344u + 5u), "cell 5 as the others");
|
||||
#endif
|
||||
CHECK(n.mem[399] == 0 && n.mem[400 + V4_BLOCK_CELLS] == 0, "256 cells and no more");
|
||||
n.mem[BUF] = 0x5A;
|
||||
CHECK(ask(V4_REQ_BLOCK_WRITE, 20, BUF) == V4_BLOCK_OK && ram[((size_t)20 + BLK_FORTH_SYS_RESERVED) * 1024] == 0x5A, "a block below 2048 is the chain's fast RAM");
|
||||
|
||||
/* ---- numbers that are no block ---- */
|
||||
CHECK(ask(V4_REQ_BLOCK_READ, 0, 400) == V4_BLOCK_RANGE, "0 is no block");
|
||||
CHECK(ask(V4_REQ_BLOCK_READ, -1, 400) == V4_BLOCK_RANGE && ask(V4_REQ_BLOCK_WRITE, -5, BUF) == V4_BLOCK_RANGE, "nor is a number below it");
|
||||
CHECK(ask(V4_REQ_BLOCK_READ, 2111, 400) == V4_BLOCK_OK && ask(V4_REQ_BLOCK_READ, 2112, 400) == V4_BLOCK_RANGE, "the device's last block is, and the one after it is not");
|
||||
CHECK(ask(V4_REQ_BLOCK_WRITE, 2112, BUF) == V4_BLOCK_RANGE, "nor can it be written");
|
||||
#if V4_CELL_BITS > 32
|
||||
CHECK(ask(V4_REQ_BLOCK_READ, (v4_cell)0x100000014LL, 400) == V4_BLOCK_RANGE, "a number 32 bits do not hold is no block, whatever its low bits are");
|
||||
#endif
|
||||
|
||||
/* ---- cells that are not in the node's memory ---- */
|
||||
n.mem[top - 1] = 123;
|
||||
CHECK(ask(V4_REQ_BLOCK_READ, 2050, top - 255) == V4_BLOCK_REFUSED && n.mem[top - 1] == 123, "256 cells that run past the end of memory are refused, and nothing is read");
|
||||
CHECK(ask(V4_REQ_BLOCK_READ, 2050, top - 256) == V4_BLOCK_OK, "the last 256 cells are not");
|
||||
CHECK(ask(V4_REQ_BLOCK_READ, 2050, -1) == V4_BLOCK_REFUSED && ask(V4_REQ_BLOCK_WRITE, 2050, top) == V4_BLOCK_REFUSED, "an address below 0, or at the end, is refused");
|
||||
CHECK(dev[2 * 1024] == 0x44, "and nothing was written");
|
||||
|
||||
/* ---- a request with too little on the stack ---- */
|
||||
{
|
||||
unsigned char before = dev[2 * 1024];
|
||||
v4_cell low = n.mem[0];
|
||||
v4_dstack_reset(&n.ds);
|
||||
CHECK(v4_blocks_serve(&n, V4_REQ_BLOCK_READ) && n.ds.depth == 1 && v4_dstack_pop(&n.ds) == V4_BLOCK_REFUSED, "a request with nothing on the stack is refused, and the status is all that is left");
|
||||
v4_dstack_push(&n.ds, 2050);
|
||||
CHECK(v4_blocks_serve(&n, V4_REQ_BLOCK_WRITE) && n.ds.depth == 1 && v4_dstack_pop(&n.ds) == V4_BLOCK_REFUSED, "and so is one with a single value");
|
||||
CHECK(dev[2 * 1024] == before && n.mem[0] == low, "nothing was read or written");
|
||||
}
|
||||
|
||||
/* ---- what v3 wrote ---- */
|
||||
{
|
||||
uint8_t *b = blk_get_buffer(2070, 1);
|
||||
CHECK(b != 0, "v3 gives a buffer for block 2070");
|
||||
if (b) { memset(b, 'V', BLK_FORTH_SIZE); CHECK(blk_update(2070) == BLK_OK && blk_flush(2070) == BLK_OK, "and v3 writes it"); }
|
||||
CHECK(ask(V4_REQ_BLOCK_READ, 2070, 400) == V4_BLOCK_OK && n.mem[400] == (v4_cell)0x56565656 && n.mem[400 + 255] == (v4_cell)0x56565656, "what v3's own calls wrote is what the node reads");
|
||||
}
|
||||
|
||||
/* ---- what is not a block request ---- */
|
||||
v4_dstack_push(&n.ds, 11); v4_dstack_push(&n.ds, 22);
|
||||
CHECK(!v4_blocks_serve(&n, 5) && !v4_blocks_serve(&n, 0) && !v4_blocks_serve(&n, -3) && v4_dstack_pop(&n.ds) == 22 && v4_dstack_pop(&n.ds) == 11,
|
||||
"any other request is not served here, and the stack is as it was");
|
||||
|
||||
printf(" %d checks, %d failures\n", checks, failures);
|
||||
return failures ? 1 : 0;
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
/* test_blocks.c -- the kernel serves a node's block requests.
|
||||
* docs/v4.0.0/MESH.md sections 8.2 and 8.3.
|
||||
*
|
||||
* A host node, stopped, with what a request takes on its data stack; the blocks
|
||||
* are v3's block subsystem (v3/src/block_subsystem.c): its fast RAM and a
|
||||
* device of 64 blocks. The window is four slots of 256 cells in the node's
|
||||
* memory, and the kernel keeps the record of what is in them.
|
||||
*/
|
||||
#include "v4/blocks.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "block_subsystem.h"
|
||||
|
||||
extern void sf_time_init(void);
|
||||
extern void log_set_level(int level);
|
||||
|
||||
static int failures = 0, checks = 0;
|
||||
#define CHECK(c,...) do{checks++; if(!(c)){failures++; printf("FAIL %s:%d: ",__FILE__,__LINE__); printf(__VA_ARGS__); printf("\n");}}while(0)
|
||||
|
||||
#define DEVICE_BLOCKS 64u
|
||||
#define WINDOW 200
|
||||
static unsigned char ram[(size_t)BLK_RAM_BLOCKS * BLK_FORTH_SIZE], dev[DEVICE_BLOCKS * BLK_FORTH_SIZE];
|
||||
static v4_node n;
|
||||
static v4_blocks w;
|
||||
static v4_cell got_addr;
|
||||
|
||||
/* a request that takes a block's number: its status, and the address in got_addr */
|
||||
static v4_cell ask(v4_cell request, v4_cell block)
|
||||
{
|
||||
v4_cell status;
|
||||
v4_dstack_reset(&n.ds);
|
||||
v4_dstack_push(&n.ds, 7777);
|
||||
v4_dstack_push(&n.ds, block);
|
||||
if (!v4_blocks_serve(&w, &n, request)) return -100;
|
||||
status = v4_dstack_pop(&n.ds);
|
||||
got_addr = v4_dstack_pop(&n.ds);
|
||||
return (v4_dstack_pop(&n.ds) == 7777 && n.ds.depth == 0) ? status : -200; /* what was under its argument is still there */
|
||||
}
|
||||
/* a request that takes nothing: its status */
|
||||
static v4_cell tell(v4_cell request)
|
||||
{
|
||||
v4_dstack_reset(&n.ds);
|
||||
v4_dstack_push(&n.ds, 7777);
|
||||
if (!v4_blocks_serve(&w, &n, request)) return -100;
|
||||
if (request == V4_REQ_EMPTY_BUFFERS) return (v4_dstack_pop(&n.ds) == 7777 && n.ds.depth == 0) ? 0 : -200;
|
||||
{
|
||||
v4_cell status = v4_dstack_pop(&n.ds);
|
||||
return (v4_dstack_pop(&n.ds) == 7777 && n.ds.depth == 0) ? status : -200;
|
||||
}
|
||||
}
|
||||
static int in_window(v4_cell a) { return a >= WINDOW && a < WINDOW + (v4_cell)(V4_BLOCK_SLOTS * V4_BLOCK_CELLS) && (a - WINDOW) % (v4_cell)V4_BLOCK_CELLS == 0; }
|
||||
static unsigned char *chain_block(unsigned num) { return num < 2048u ? ram + ((size_t)num + BLK_FORTH_SYS_RESERVED) * 1024u : dev + (size_t)(num - 2048u) * 1024u; }
|
||||
|
||||
int main(void)
|
||||
{
|
||||
unsigned i;
|
||||
v4_cell a, b;
|
||||
|
||||
printf("v4 block request tests: V4_CELL_BITS=%d, V4_NODE_WORDS=%u\n", V4_CELL_BITS, (unsigned)V4_NODE_WORDS);
|
||||
sf_time_init();
|
||||
log_set_level(-1);
|
||||
CHECK(blk_subsys_init(ram, sizeof ram) == BLK_OK && blk_subsys_add_raw_device(dev, DEVICE_BLOCKS) == BLK_OK, "the kernel's chain: fast RAM and a device of 64 blocks");
|
||||
v4_node_reset(&n);
|
||||
v4_blocks_init(&w, WINDOW);
|
||||
|
||||
/* ---- BLOCK: the block is copied into a slot of the window ---- */
|
||||
memset(chain_block(2050), 'A', 1024);
|
||||
chain_block(2050)[1] = 'B';
|
||||
n.mem[WINDOW - 1] = 111; n.mem[WINDOW + V4_BLOCK_SLOTS * V4_BLOCK_CELLS] = 222;
|
||||
CHECK(ask(V4_REQ_BLOCK, 2050) == V4_BLOCK_OK && in_window(got_addr), "BLOCK gives the address of a slot of the window");
|
||||
a = got_addr;
|
||||
CHECK(n.mem[a] == (v4_cell)0x41414241 && n.mem[a + 255] == (v4_cell)0x41414141, "holding the block, four bytes to a cell, the first lowest, the rest of each cell zero");
|
||||
chain_block(2050)[0] = 'Z';
|
||||
CHECK(ask(V4_REQ_BLOCK, 2050) == V4_BLOCK_OK && got_addr == a && n.mem[a] == (v4_cell)0x41414241, "a block already in a slot is given as it is there, and not read again");
|
||||
CHECK(ask(V4_REQ_BLOCK, 20) == V4_BLOCK_OK && in_window(got_addr) && got_addr != a, "another block is put in another slot");
|
||||
CHECK(n.mem[WINDOW - 1] == 111 && n.mem[WINDOW + V4_BLOCK_SLOTS * V4_BLOCK_CELLS] == 222, "nothing outside the window is written");
|
||||
|
||||
/* ---- UPDATE marks; nothing is written until SAVE-BUFFERS ---- */
|
||||
CHECK(ask(V4_REQ_BLOCK, 2051) == V4_BLOCK_OK, "block 2051 is asked for");
|
||||
b = got_addr;
|
||||
n.mem[b] = (v4_cell)0x64636261;
|
||||
#if V4_CELL_BITS > 32
|
||||
n.mem[b + 1] = (v4_cell)0x7766554433221100LL; /* only its low 32 bits are the block's */
|
||||
#endif
|
||||
CHECK(tell(V4_REQ_UPDATE) == V4_BLOCK_OK && chain_block(2051)[0] == 0, "UPDATE marks it, and it is not written yet");
|
||||
CHECK(tell(V4_REQ_SAVE_BUFFERS) == V4_BLOCK_OK && memcmp(chain_block(2051), "abcd", 4) == 0, "SAVE-BUFFERS writes it");
|
||||
#if V4_CELL_BITS > 32
|
||||
CHECK(chain_block(2051)[4] == 0x00 && chain_block(2051)[7] == 0x33 && chain_block(2051)[8] == 0, "the low 32 bits of each cell, and no more");
|
||||
#endif
|
||||
CHECK(chain_block(2050)[0] == 'Z' && chain_block(2050)[1] == 'B', "a block that was not UPDATEd is not written");
|
||||
n.mem[b] = (v4_cell)0x31313131;
|
||||
CHECK(tell(V4_REQ_SAVE_BUFFERS) == V4_BLOCK_OK && chain_block(2051)[0] == 'a', "nor is one changed again and not UPDATEd again");
|
||||
CHECK(tell(V4_REQ_UPDATE) == V4_BLOCK_OK, "UPDATE is of the block most recently asked for");
|
||||
CHECK(ask(V4_REQ_BLOCK, 2050) == V4_BLOCK_OK && tell(V4_REQ_UPDATE) == V4_BLOCK_OK && tell(V4_REQ_SAVE_BUFFERS) == V4_BLOCK_OK
|
||||
&& chain_block(2051)[0] == '1' && chain_block(2050)[0] == 'A', "two UPDATEd blocks are both written, each from its own slot");
|
||||
|
||||
/* ---- EMPTY-BUFFERS forgets, and writes nothing ---- */
|
||||
n.mem[a] = (v4_cell)0x58585858;
|
||||
CHECK(ask(V4_REQ_BLOCK, 2050) == V4_BLOCK_OK && tell(V4_REQ_UPDATE) == V4_BLOCK_OK && tell(V4_REQ_EMPTY_BUFFERS) == 0 && tell(V4_REQ_SAVE_BUFFERS) == V4_BLOCK_OK
|
||||
&& chain_block(2050)[0] == 'A', "EMPTY-BUFFERS forgets an UPDATEd block: it is not written");
|
||||
CHECK(tell(V4_REQ_UPDATE) == V4_BLOCK_OK && tell(V4_REQ_SAVE_BUFFERS) == V4_BLOCK_OK, "UPDATE with no block asked for does nothing");
|
||||
chain_block(2050)[0] = 'Q';
|
||||
CHECK(ask(V4_REQ_BLOCK, 2050) == V4_BLOCK_OK && (n.mem[got_addr] & 0xFF) == 'Q', "and the block is read again when it is next asked for");
|
||||
|
||||
/* ---- BUFFER: a slot for the block, not read ---- */
|
||||
(void)tell(V4_REQ_EMPTY_BUFFERS);
|
||||
CHECK(ask(V4_REQ_BUFFER, 2060) == V4_BLOCK_OK && in_window(got_addr) && n.mem[got_addr] == 0 && n.mem[got_addr + 255] == 0, "BUFFER gives a slot of zeros, as v3's does");
|
||||
n.mem[got_addr] = (v4_cell)0x62626262;
|
||||
CHECK(tell(V4_REQ_UPDATE) == V4_BLOCK_OK && tell(V4_REQ_SAVE_BUFFERS) == V4_BLOCK_OK && chain_block(2060)[0] == 'b' && chain_block(2060)[4] == 0, "and what is put in it is written");
|
||||
CHECK(ask(V4_REQ_BLOCK, 2060) == V4_BLOCK_OK && n.mem[got_addr] == (v4_cell)0x62626262, "BLOCK then finds it in its slot");
|
||||
|
||||
/* ---- more blocks than slots ---- */
|
||||
(void)tell(V4_REQ_EMPTY_BUFFERS);
|
||||
for (i = 0; i < V4_BLOCK_SLOTS; i++) {
|
||||
CHECK(ask(V4_REQ_BLOCK, 2100 + (v4_cell)i) == V4_BLOCK_OK, "block %u is asked for", 2100 + i);
|
||||
n.mem[got_addr] = (v4_cell)(0x30303030 + i);
|
||||
}
|
||||
CHECK(tell(V4_REQ_UPDATE) == V4_BLOCK_OK, "the last of them is UPDATEd");
|
||||
CHECK(ask(V4_REQ_BLOCK, 2110) == V4_BLOCK_OK && chain_block(2100 + V4_BLOCK_SLOTS - 1)[0] == 0, "one more takes a slot whose block was not UPDATEd, and nothing is written");
|
||||
for (i = 0; i < V4_BLOCK_SLOTS; i++) { (void)ask(V4_REQ_BLOCK, 2100 + (v4_cell)i); n.mem[got_addr] = (v4_cell)(0x30303030 + i); (void)tell(V4_REQ_UPDATE); }
|
||||
for (i = 0; i < V4_BLOCK_SLOTS && chain_block(2100 + i)[0] == 0; i++) { }
|
||||
CHECK(i == V4_BLOCK_SLOTS, "with every slot UPDATEd nothing is written until a slot is wanted");
|
||||
{
|
||||
unsigned written = 0;
|
||||
CHECK(ask(V4_REQ_BLOCK, 2111) == V4_BLOCK_OK, "one more block is asked for");
|
||||
for (i = 0; i < V4_BLOCK_SLOTS; i++) if (chain_block(2100 + i)[0] != 0) written++;
|
||||
CHECK(written >= 1, "and the block whose slot it took was written first: %u written", written);
|
||||
}
|
||||
(void)tell(V4_REQ_EMPTY_BUFFERS);
|
||||
|
||||
/* ---- numbers that are no block ---- */
|
||||
CHECK(ask(V4_REQ_BLOCK, 0) == V4_BLOCK_RANGE && got_addr == 0, "0 is no block");
|
||||
CHECK(ask(V4_REQ_BLOCK, -1) == V4_BLOCK_RANGE && ask(V4_REQ_BUFFER, -5) == V4_BLOCK_RANGE, "nor is a number below it");
|
||||
CHECK(ask(V4_REQ_BLOCK, 2111) == V4_BLOCK_OK && ask(V4_REQ_BLOCK, 2112) == V4_BLOCK_RANGE && ask(V4_REQ_BUFFER, 2112) == V4_BLOCK_RANGE, "the device's last block is, and the one after it is not");
|
||||
#if V4_CELL_BITS > 32
|
||||
CHECK(ask(V4_REQ_BLOCK, (v4_cell)0x100000014LL) == V4_BLOCK_RANGE, "a number 32 bits do not hold is no block, whatever its low bits are");
|
||||
#endif
|
||||
|
||||
/* ---- a request with too little on the stack ---- */
|
||||
v4_dstack_reset(&n.ds);
|
||||
CHECK(v4_blocks_serve(&w, &n, V4_REQ_BLOCK) && n.ds.depth == 2 && v4_dstack_pop(&n.ds) == V4_BLOCK_REFUSED && v4_dstack_pop(&n.ds) == 0,
|
||||
"BLOCK with nothing on the stack is refused: no address, and the status");
|
||||
|
||||
/* ---- a window that is not in the node's memory ---- */
|
||||
{
|
||||
v4_blocks bad;
|
||||
v4_cell top = (v4_cell)V4_NODE_WORDS, status;
|
||||
v4_blocks_init(&bad, top - (v4_cell)(V4_BLOCK_SLOTS * V4_BLOCK_CELLS) + 1);
|
||||
n.mem[top - 1] = 123;
|
||||
v4_dstack_reset(&n.ds); v4_dstack_push(&n.ds, 2050);
|
||||
CHECK(v4_blocks_serve(&bad, &n, V4_REQ_BLOCK), "a request with a window that runs past the end of memory is served");
|
||||
status = v4_dstack_pop(&n.ds);
|
||||
CHECK(status == V4_BLOCK_REFUSED && v4_dstack_pop(&n.ds) == 0 && n.mem[top - 1] == 123, "by being refused, with nothing written");
|
||||
}
|
||||
|
||||
/* ---- the chain changes: what is in the slots is let go, as v3 does ---- */
|
||||
(void)tell(V4_REQ_EMPTY_BUFFERS);
|
||||
CHECK(ask(V4_REQ_BLOCK, 2070) == V4_BLOCK_OK && (n.mem[got_addr] = (v4_cell)0x59595959) != 0 && tell(V4_REQ_UPDATE) == V4_BLOCK_OK, "a block is in a slot, UPDATEd");
|
||||
{
|
||||
static unsigned char more[4 * 1024];
|
||||
CHECK(blk_subsys_add_raw_device(more, 4) == BLK_OK, "a device joins the chain");
|
||||
memset(chain_block(2070), 'N', 1024);
|
||||
CHECK(tell(V4_REQ_SAVE_BUFFERS) == V4_BLOCK_OK && chain_block(2070)[0] == 'N', "what was in the slots is not written to the chain as it now is");
|
||||
CHECK(ask(V4_REQ_BLOCK, 2070) == V4_BLOCK_OK && (n.mem[got_addr] & 0xFF) == 'N', "and the block is read again");
|
||||
}
|
||||
|
||||
/* ---- what is not a block request ---- */
|
||||
v4_dstack_reset(&n.ds);
|
||||
v4_dstack_push(&n.ds, 11); v4_dstack_push(&n.ds, 22);
|
||||
CHECK(!v4_blocks_serve(&w, &n, 5) && !v4_blocks_serve(&w, &n, 0) && !v4_blocks_serve(&w, &n, -6) && v4_dstack_pop(&n.ds) == 22 && v4_dstack_pop(&n.ds) == 11,
|
||||
"any other request is not served here, and the stack is as it was");
|
||||
|
||||
printf(" %d checks, %d failures\n", checks, failures);
|
||||
return failures ? 1 : 0;
|
||||
}
|
||||
+23
-10
@@ -78,7 +78,7 @@ static char out[V4_CONSOLE_CAP + 1];
|
||||
#define REFUSING_FIRST "2112" /* its first block */
|
||||
static unsigned char ram[RAM_BYTES], common_dev[DISK_BLOCKS * V4_BLOCK_BYTES], refusing_dev[REFUSING_BLOCKS * V4_BLOCK_BYTES];
|
||||
#define disk (ram + (size_t)BLK_FORTH_SYS_RESERVED * BLK_FORTH_SIZE)
|
||||
static unsigned long block_requests; /* how many the node has made */
|
||||
static v4_blocks kblocks; /* what the kernel keeps of the node's block window */
|
||||
|
||||
extern void sf_time_init(void);
|
||||
extern void log_set_level(int level);
|
||||
@@ -122,7 +122,7 @@ static v4_cell last_request;
|
||||
static void kernel_serve(void)
|
||||
{
|
||||
last_request = n.request;
|
||||
if (v4_blocks_serve(&n, n.request)) { block_requests++; return; } /* a block, as for any node (blocks.h) */
|
||||
if (v4_blocks_serve(&kblocks, &n, n.request)) return; /* a block, as for any node (blocks.h) */
|
||||
if (n.request == 1) {
|
||||
v4_cell xt = v4_dstack_pop(&n.ds);
|
||||
if (known_n < 1024) known[known_n++] = xt;
|
||||
@@ -266,6 +266,7 @@ static void boot_with(unsigned depth)
|
||||
n.mem[SRC] = TIB;
|
||||
for (v4_cell k = BVARS; k < BVARS + 14; k++) if (k != SRC) n.mem[k] = 0; /* empty buffers, SCR and BLK 0, no hook */
|
||||
memset(ram, 0, sizeof ram); memset(common_dev, 0, sizeof common_dev);
|
||||
v4_blocks_init(&kblocks, BUF0_W);
|
||||
v4_node_console_input_attach(&n, CONSOLE_RX, CONSOLE_ST);
|
||||
v4_node_fault_attach(&n, w_fault);
|
||||
v4_node_error_attach(&n, NODE_ERROR);
|
||||
@@ -1175,9 +1176,10 @@ int main(void)
|
||||
boot();
|
||||
CHECK(is(say(REFUSING_FIRST " BLOCK DROP UPDATE SAVE-BUFFERS 65 EMIT\n"), "Storage refused\n ERROR\nok> "), "a write storage will not take says so, and the prompt returns");
|
||||
{
|
||||
unsigned long before = block_requests;
|
||||
CHECK(is(say(REFUSING_FIRST " BLOCK DROP\n"), " ok\nok> ") && block_requests == before + 1, "the buffer was emptied: the next BLOCK asks the kernel again");
|
||||
CHECK(is(say(REFUSING_FIRST " BLOCK DROP\n"), " ok\nok> ") && block_requests == before + 1, "and the one after finds it in the buffer");
|
||||
unsigned k, held = 0;
|
||||
for (k = 0; k < V4_BLOCK_SLOTS; k++) if (kblocks.block[k] == 2112u) held++;
|
||||
CHECK(held == 0, "the block storage would not take is let go: no slot holds it");
|
||||
CHECK(is(say(REFUSING_FIRST " BLOCK C@ .\n"), "0 ok\nok> "), "and the next BLOCK reads it from storage again");
|
||||
}
|
||||
|
||||
/* ---- blocks: text loaded from storage ---- */
|
||||
@@ -1225,11 +1227,22 @@ int main(void)
|
||||
"an UPDATEd block is not written yet");
|
||||
CHECK(is(say("FLUSH\n"), " ok\nok> ") && disk[3 * V4_BLOCK_BYTES] == 'H' && disk[3 * V4_BLOCK_BYTES + 1] == ' ' && disk[3 * V4_BLOCK_BYTES + 1023] == 'I'
|
||||
&& disk[2 * V4_BLOCK_BYTES + 1023] == 0 && disk[4 * V4_BLOCK_BYTES] == 0, "FLUSH writes it, all 1024 bytes and no others");
|
||||
CHECK(is(say("74 3 BLOCK C! UPDATE 4 BLOCK DROP\n"), " ok\nok> ") && disk[3 * V4_BLOCK_BYTES] == 'H', "with two buffers a second block does not disturb it");
|
||||
CHECK(is(say("5 BLOCK DROP\n"), " ok\nok> ") && disk[3 * V4_BLOCK_BYTES] == 'J', "a third takes its buffer, and it is written first");
|
||||
CHECK(is(say("EMPTY-BUFFERS 74 3 BLOCK C! UPDATE 4 BLOCK DROP 5 BLOCK DROP 6 BLOCK DROP\n"), " ok\nok> ") && disk[3 * V4_BLOCK_BYTES] == 'H',
|
||||
"with four slots three more blocks do not disturb it");
|
||||
CHECK(is(say("7 BLOCK DROP\n"), " ok\nok> ") && disk[3 * V4_BLOCK_BYTES] == 'H', "a fifth takes the slot of a block that was not UPDATEd, and nothing is written");
|
||||
CHECK(is(say("77 4 BLOCK C! UPDATE 77 5 BLOCK C! UPDATE 77 6 BLOCK C! UPDATE\n"), " ok\nok> ") && disk[3 * V4_BLOCK_BYTES] == 'H'
|
||||
&& disk[4 * V4_BLOCK_BYTES] != 'M' && disk[5 * V4_BLOCK_BYTES] != 'M' && disk[6 * V4_BLOCK_BYTES] != 'M', "four UPDATEd blocks fill the slots, and none is written yet");
|
||||
{
|
||||
unsigned written;
|
||||
CHECK(is(say("8 BLOCK DROP\n"), " ok\nok> "), "one more block is asked for");
|
||||
written = (disk[3 * V4_BLOCK_BYTES] == 'J') + (disk[4 * V4_BLOCK_BYTES] == 'M') + (disk[5 * V4_BLOCK_BYTES] == 'M') + (disk[6 * V4_BLOCK_BYTES] == 'M');
|
||||
CHECK(written == 1, "it takes the slot of one of them, and that one is written first: %u written", written);
|
||||
}
|
||||
CHECK(is(say("FLUSH\n"), " ok\nok> ") && disk[3 * V4_BLOCK_BYTES] == 'J' && disk[4 * V4_BLOCK_BYTES] == 'M' && disk[5 * V4_BLOCK_BYTES] == 'M' && disk[6 * V4_BLOCK_BYTES] == 'M',
|
||||
"FLUSH writes the rest");
|
||||
CHECK(is(say("3 BLOCK C@ . 75 3 BLOCK C! UPDATE EMPTY-BUFFERS FLUSH 3 BLOCK C@ .\n"), "74 74 ok\nok> ") && disk[3 * V4_BLOCK_BYTES] == 'J',
|
||||
"EMPTY-BUFFERS forgets a change without writing it");
|
||||
CHECK(is(say("76 3 BLOCK C! 5 BLOCK DROP 6 BLOCK DROP 3 BLOCK C@ .\n"), "74 ok\nok> "), "a change without UPDATE is lost when the buffer is taken");
|
||||
CHECK(is(say("76 3 BLOCK C! 5 BLOCK DROP 6 BLOCK DROP 7 BLOCK DROP 8 BLOCK DROP 3 BLOCK C@ .\n"), "74 ok\nok> "), "a change without UPDATE is lost when the slot is taken");
|
||||
CHECK(is(say("3 BLOCK 4 BLOCK 1024 CMOVE UPDATE SAVE-BUFFERS\n"), " ok\nok> ") && memcmp(disk + 3 * V4_BLOCK_BYTES, disk + 4 * V4_BLOCK_BYTES, V4_BLOCK_BYTES) == 0
|
||||
&& disk[4 * V4_BLOCK_BYTES] == 'J', "two blocks in memory at once: one copied to the other");
|
||||
put_block(40, "this is on the device");
|
||||
@@ -1238,7 +1251,7 @@ int main(void)
|
||||
{
|
||||
v4_cell k, wide = 0;
|
||||
(void)say("3 BLOCK DROP\n");
|
||||
for (k = 0; k < 2 * 256; k++) if ((v4_ucell)n.mem[BUF0_W + k] > 0xFFFFFFFFu) wide = 1;
|
||||
for (k = 0; k < (v4_cell)(V4_BLOCK_SLOTS * V4_BLOCK_CELLS); k++) if ((v4_ucell)n.mem[BUF0_W + k] > 0xFFFFFFFFu) wide = 1;
|
||||
CHECK(!wide, "a block's bytes are four to a cell at every cell width");
|
||||
}
|
||||
{
|
||||
@@ -1396,7 +1409,7 @@ int main(void)
|
||||
&& is(say("4 LOG-LEVEL!\n"), " ok\nok> "), "a vocabulary, a base, a block, a screen, a log level");
|
||||
CHECK(is(say("1 2 COLD 65 EMIT\n"), "FORTH-79 Cold Start\nSystem initialized.\n ok\nok> "), "COLD says so");
|
||||
CHECK(n.mem[DP] == dp && n.mem[LATEST] == capsule_latest && n.mem[CONTEXT] == LATEST && n.mem[CURRENT] == LATEST && n.mem[VOC_LINK] == 0
|
||||
&& n.mem[BASE] == 10 && n.mem[SCR] == 0 && n.mem[BVARS] == 0 && n.mem[FENCE] == dp / 4 && n.mem[LOG_LEVEL] == 2, "and everything is as the loader left it");
|
||||
&& n.mem[BASE] == 10 && n.mem[SCR] == 0 && kblocks.block[0] == 0 && kblocks.block[1] == 0 && kblocks.block[2] == 0 && kblocks.block[3] == 0 && n.mem[FENCE] == dp / 4 && n.mem[LOG_LEVEL] == 2, "and everything is as the loader left it");
|
||||
CHECK(is(say("KEEPME\n"), "UNKNOWN WORD: 'KEEPME'\n ERROR\nok> ") && is(say("VV\n"), "UNKNOWN WORD: 'VV'\n ERROR\nok> ")
|
||||
&& is(say(".S : NEW 16 . ; NEW\n"), "<0> \n16 ok\nok> "), "what was defined is gone, and the system works");
|
||||
}
|
||||
|
||||
@@ -113,9 +113,11 @@ static uint64_t parity_hash[8];
|
||||
/* THE KERNEL, for a node that is not Hera: on its port 0 it serves the
|
||||
* node's blocks, and no other request (MESH.md 8.3). */
|
||||
static unsigned refused;
|
||||
static v4_blocks node_blocks[PLACES]; /* what the kernel keeps of each node's block window, by where the node is in the pool */
|
||||
static int node_kernel_take(void *self, v4_cell value)
|
||||
{
|
||||
if (!v4_blocks_serve((v4_node *)self, value)) refused++;
|
||||
v4_fabric_node *x = (v4_fabric_node *)self;
|
||||
if (!v4_blocks_serve(&node_blocks[x - pool], &x->n, value)) refused++;
|
||||
return 1;
|
||||
}
|
||||
static v4_device node_kernel[PLACES];
|
||||
@@ -135,8 +137,9 @@ static int host_born(void *self)
|
||||
v4_node_stack_regs_attach(&pool[k].n, DSTACK_REG, RSTACK_REG);
|
||||
v4_node_error_attach(&pool[k].n, NODE_ERROR);
|
||||
v4_node_fault_attach(&pool[k].n, w_fault);
|
||||
v4_blocks_init(&node_blocks[k], BUF0_W);
|
||||
if (k != 0) { /* Hera's kernel is wired where she is made */
|
||||
node_kernel[k].take = node_kernel_take; node_kernel[k].give = 0; node_kernel[k].self = &pool[k].n;
|
||||
node_kernel[k].take = node_kernel_take; node_kernel[k].give = 0; node_kernel[k].self = &pool[k];
|
||||
if (!v4_fabric_wire_device(&f, (unsigned)place, 0, &node_kernel[k])) return -1;
|
||||
}
|
||||
return place;
|
||||
@@ -187,7 +190,7 @@ static int kernel_give(void *self, v4_cell *value) /* the nucleus, to a nod
|
||||
static int kernel_take(void *self, v4_cell value) /* a request */
|
||||
{
|
||||
(void)self;
|
||||
if (v4_blocks_serve(&v4_fabric_node_at(&f, hera_place)->n, value)) return 1; /* a block, as for any node */
|
||||
if (v4_blocks_serve(&node_blocks[0], &pool[0].n, value)) return 1; /* a block, as for any node; Hera is the first in the pool */
|
||||
if (value < FIRST_REQUEST || !v4_manage_serve(&manage, hera_place, (unsigned)(value - FIRST_REQUEST))) refused++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user