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
@@ -180,7 +180,7 @@ definition below depends on one, it says so.
|
||||
| **D-16** | Stack overflow and underflow; `DEPTH`, `PICK`, `ROLL` (ruled 2026-10-04; revises D-2). | **Guarded: a stack fault.** Each stack counts what it holds. Before every opcode the node checks that the stacks hold what the opcode takes — including a `T` or `S` it only reads — and have room for what it leaves. If not, the opcode does nothing and the node faults exactly as for a bad address (D-14), to that kind's handler: `Stack overflow`, `Stack underflow`, `Return stack overflow`, `Return stack underflow`, then ` ERROR` and the prompt. **Every fault, D-14's included, empties both stacks**: the handler does not return, and what a word stopped part-way has left on the data stack is of no use to its caller. (v3 keeps what the failing word had not taken, and names the word: `DROP: Stack underflow`.) The fault handler is a table of five words, one per kind, each a jump (`(FAULTS)` in `v4/capsule/quit.v4`). Two registers (§7) are all a programme sees of the stacks: `DSTACK-DEPTH` and `RSTACK-DEPTH` read as the depth, and a store to one empties that stack. There is still no stack pointer and no address for a stack cell, so `SP@` and `SP!` stay retired; `.S` waits for number output to reach the capsule. The sizes were unchanged by this ruling, 10 and 9 (D-17 then deepens the host node's): one more value, or one more level of call, is now an error message where it used to be silent corruption. Executed on the golden model (2026-10-04): `tests/test_exec.c` for every opcode at every depth of both stacks, `tests/test_host_quit.c` from the prompt. |
|
||||
| **D-17** | Stack sizes on the host node (2026-10-04, following D-16). | **32 values and 32 return entries on the host node; a mesh node keeps the F18's 10 and 9.** Once the stacks are counted (D-16) their size is a parameter of the node, like its memory, and the host node is the one that runs the interpreter and the compiler underneath the user's programme: at 10 and 9 the prompt left a programme about six values, and `/` could be used only four words deep. The mechanism is the same at both sizes — top registers over a ring — and so is every word's definition. v3's stacks are deeper still. In the golden model the sizes are `V4_DATA_RING` and `V4_RET_RING` (`stack.h`), set for the host-node tests in `v4/Makefile`. |
|
||||
| **D-18** | Every other error (ruled 2026-10-04: "guard all errors"). | **A word that finds an error raises it, and the line ends there.** The word takes its own arguments off the stack and stores an error code in `NODE-ERROR` (§7). On a node with a prompt that store is a trap, a sixth kind of fault beside D-14's and D-16's: nothing after it executes, the return stack is emptied, and the prompt prints the code's message, ends any definition that was open, prints ` ERROR` and waits for the next line. Unlike the other faults it leaves the data stack as the word left it. The codes: 1 `Negative count` (`CMOVE`, `TYPE`), 2 `Not a number` (`NUMBER`), 3 `Number too long` (`HOLD` into a full buffer), 4 `Not a character` (`HOLD`), 5 `Dictionary full` (`,` `C,` `ALLOT`, any defining word), 6 `Name missing` (a defining word with nothing after it), 7 `Control structure mismatch`, 8 `Control structures too deep`, 9 `Shift count out of range`, 10 `Protected word`, 11 `Division by zero` (`Q./`), 12 `Argument out of range`, 13 `Block out of range`, 14 `No block is being loaded`, 15 `Deferred word not set`, 16 `Not a word`; −1 means the word printed its own message (`UNKNOWN WORD: 'xxx'`, `xxx: compile-only`). Before this ruling these set the flag and the line ran on to its end. v3 stops at once too; its messages name the word (`LOOP: missing DO`) where v4's name the fault. With the trap not attached `NODE-ERROR` is plain memory and the word returns, which is how the words are tested below the prompt. D-11, D-12 and D-13 said "set `NODE-ERROR`"; on a node with a prompt that now means this. Executed on the golden model (2026-10-04): `tests/test_exec.c`, `tests/test_host_quit.c`. |
|
||||
| **D-19** | Block storage on the golden model (2026-10-05). **Replaced 2026-10-07:** the four registers are gone; a node asks its kernel for a block by a request on port 0 (`MESH.md` section 8, `v4/include/v4/blocks.h`). What follows is the decision as it was. | **Four memory-mapped registers on the host node, until the mesh carries the storage service.** `BLOCK-NUMBER`, `BLOCK-ADDRESS` (the word address of 256 cells), `BLOCK-COMMAND` (a store of 1 reads the block into those cells, 2 writes it from them) and `BLOCK-STATUS` (0 when the command worked, −1 for a block the device has not got or cells not all in memory). A block is 1024 bytes: 256 cells, four bytes to a cell, the first byte lowest, at either cell width. This is the console's arrangement (§7) applied to storage; like it, it is the model's stand-in and not the mesh protocol, which §5.11 still leaves to the device node. |
|
||||
| **D-19** | Block storage on the golden model (2026-10-05). **Replaced 2026-10-07:** the four registers are gone; the block words are kernel requests on port 0, by block number, and the kernel keeps the node's window of four slots (`MESH.md` section 8, `v4/include/v4/blocks.h`). What follows is the decision as it was. | **Four memory-mapped registers on the host node, until the mesh carries the storage service.** `BLOCK-NUMBER`, `BLOCK-ADDRESS` (the word address of 256 cells), `BLOCK-COMMAND` (a store of 1 reads the block into those cells, 2 writes it from them) and `BLOCK-STATUS` (0 when the command worked, −1 for a block the device has not got or cells not all in memory). A block is 1024 bytes: 256 cells, four bytes to a cell, the first byte lowest, at either cell width. This is the console's arrangement (§7) applied to storage; like it, it is the model's stand-in and not the mesh protocol, which §5.11 still leaves to the device node. |
|
||||
|
||||
**Consequences of D-2 that every definition must respect.** The data stack holds 10 items and the
|
||||
return stack 9, and every `call`, `FOR`, `DO` loop frame and `push` uses return-stack slots. Nesting
|
||||
|
||||
+76
-60
@@ -315,34 +315,57 @@ in 8.6, so that it is not proposed again.
|
||||
|
||||
### 8.2 What a node does *(step 6)*
|
||||
|
||||
`BLOCK`, `BUFFER`, `UPDATE` and `SAVE-BUFFERS` keep their two buffers and
|
||||
their FORTH-79 behaviour. The one word beneath them in
|
||||
`v4/capsule/blocks.v4` that read or wrote a block by storing to four
|
||||
registers makes a kernel request instead: it puts the block's number and
|
||||
the word address of the buffer's 256 cells on the data stack and writes the
|
||||
request's number to port 0, where its kernel is. When the write has been
|
||||
served the status is on the stack in their place: 0 it worked, 2 it was
|
||||
refused, 3 there is no such block.
|
||||
**Ruled 2026-10-07: v3's way in full.** The block words are the kernel's.
|
||||
`BLOCK`, `BUFFER`, `UPDATE`, `SAVE-BUFFERS` and `EMPTY-BUFFERS` in
|
||||
`v4/capsule/blocks.v4` each write one request to port 0, where the node's
|
||||
kernel is, and do nothing else: `BLOCK` and `BUFFER` with the block's
|
||||
number on the stack, the others with nothing. The node gives the kernel no
|
||||
address and keeps no record of what is in its buffers.
|
||||
|
||||
There are two requests, read a block and write a block. Their numbers are
|
||||
the same for every node and are below zero; the requests a host names for
|
||||
its own words (`KERNEL-WORD`) count up from 1.
|
||||
The node has a **window** in its memory, four slots of 256 cells, as a v3
|
||||
VM has four slots of 1 KiB at the top of its memory. The kernel copies a
|
||||
block into a slot and gives the node the slot's address.
|
||||
|
||||
The five requests' numbers are the same for every node and are below zero,
|
||||
-1 to -5; the requests a host names for its own words (`KERNEL-WORD`)
|
||||
count up from 1. The status a request leaves: 0 it worked, 2 it was
|
||||
refused (error 17, "Storage refused"), 3 there is no such block (error 13).
|
||||
|
||||
The four storage registers and `v4_node_storage_attach` go from the engine.
|
||||
|
||||
### 8.3 What the kernel does *(step 6)*
|
||||
|
||||
Every node has its kernel on port 0, whatever else is there for it: Hera's
|
||||
requests for nodes (section 9) are honoured only from Hera, and the two
|
||||
block requests from every node.
|
||||
requests for nodes (section 9) are honoured only from Hera, and the block
|
||||
requests from every node.
|
||||
|
||||
`v4/system/blocks.c`, which the hosted and the bare-metal builds and the
|
||||
tests share, serves them: it takes the number and the address from the
|
||||
node's data stack, checks that the 256 cells are in the node's memory, and
|
||||
reads or writes the block through v3's block subsystem
|
||||
(`v3/src/block_subsystem.c`), four characters to a cell, the first lowest.
|
||||
The engine (`v4/src`) knows nothing of it.
|
||||
tests share, serves them, from v3's block subsystem
|
||||
(`v3/src/block_subsystem.c`). For each node it keeps what v3 keeps for each
|
||||
VM (`v3/src/word_source/block_words.c`): which block is in which slot of
|
||||
the window, which have been UPDATEd, and the chain as it was when they
|
||||
were filled. The engine (`v4/src`) knows nothing of it.
|
||||
|
||||
- It writes into the node's window and nowhere else in the node. That is
|
||||
how a request cannot overwrite the node's code (ruled 2026-10-07: the
|
||||
kernel refuses it): there is no address for a node to give.
|
||||
- `BLOCK` gives the slot a block is in, and reads it from storage only if
|
||||
it is in none. `BUFFER` gives a slot of zeros without reading.
|
||||
- **When a block is written is FORTH-79's:** `UPDATE` marks it, and it is
|
||||
written by `SAVE-BUFFERS` or when its slot is wanted. v3's `UPDATE`
|
||||
copies the slot to the kernel at once; the standard is followed
|
||||
(standing ruling). A slot whose block is not marked is taken before one
|
||||
whose block is.
|
||||
- A block storage will not take is let go of, and its slot is empty.
|
||||
- When the chain of devices changes, everything in the slots is let go and
|
||||
none of it is written, as v3 does.
|
||||
- Four characters to a cell, the first lowest; a read leaves the rest of
|
||||
each cell zero and a write takes the low 32 bits.
|
||||
- **Each node has its own copy of a block it holds, as each v3 VM has.**
|
||||
If node A has block *n* in a slot and node B writes it, A goes on reading
|
||||
what it had, and A's next `UPDATE` and `SAVE-BUFFERS` writes all of the
|
||||
block over B's. v3 is the same. Closing that would be a departure from
|
||||
v3 and is not ruled.
|
||||
- v3's code does what it does: header, allocation map, block cards,
|
||||
relocation, three blocks and their cards to a 4 KiB device block.
|
||||
- There is one chain, as in v3: fast RAM at blocks 0 to 2047, then the
|
||||
@@ -376,37 +399,20 @@ block number not to exist, and on the real chain it does.
|
||||
step 6a.
|
||||
- **Cloud stores and real USB drives** wait for their drivers.
|
||||
|
||||
### 8.5 Open, for ruling
|
||||
### 8.5 Ruled 2026-10-07, after the review of step 6
|
||||
|
||||
Found by review of step 6 on 2026-10-07; nothing is built for either.
|
||||
The review found that a block request could be made by hand with an
|
||||
address in the node's own code, and said that a node's own copy of a
|
||||
block was a departure from v3. The second was wrong: a v3 VM has a window
|
||||
of four slots in its own memory and `BLOCK` copies the kernel's block into
|
||||
one (`v3/include/vm.h`, `BLK_VM_SLOTS`; `block_words.c`, `blk_vm_load`).
|
||||
It was reported to Captain Bob as a departure before it was checked
|
||||
against v3, and he ruled on it as one; it was then checked and taken back.
|
||||
|
||||
- **A node keeps its own copy of a block.** `BLOCK` gives the address of
|
||||
one of the node's two buffers, and the kernel is asked only when a block
|
||||
is not in one. If node A has block *n* in a buffer and node B writes it,
|
||||
A goes on reading what it had; and if A then does `UPDATE` and
|
||||
`SAVE-BUFFERS`, all of B's block is overwritten.
|
||||
**Corrected 2026-10-07.** This entry first said that in v3 a VM's `BLOCK`
|
||||
gives the kernel's buffer, so that every VM sees one copy. That was
|
||||
wrong, and was not checked against v3 before it was written. In v3 each
|
||||
VM has a window of four 1 KiB slots at the top of its own memory
|
||||
(`v3/include/vm.h`, `BLK_VM_SLOTS`; `v3/src/word_source/block_words.c`,
|
||||
`blk_vm_load`): `BLOCK` copies the kernel's block into a slot, unless
|
||||
the block is already in one, and gives the slot's address. So a v3 VM
|
||||
keeps its own copy too, and two VMs can each hold a stale one, exactly
|
||||
as here. Where v4 differs from v3:
|
||||
- two buffers, where v3 has four slots;
|
||||
- the node keeps the record of what is in them and does the asking,
|
||||
where in v3 the block words are the kernel's and the kernel keeps the
|
||||
record and does the copying, only ever into the window;
|
||||
- `UPDATE` marks the buffer and it is written when the buffer is wanted
|
||||
or at `SAVE-BUFFERS`, as FORTH-79 has it, where v3's `UPDATE` copies
|
||||
the slot to the kernel at once;
|
||||
- v3 lets go of every slot when the device chain changes; v4 does that
|
||||
in one place only, after POST on bare metal.
|
||||
- **A block read over the node's own code.** The kernel checks that the
|
||||
256 cells are in the node's memory and nothing more, so a request made
|
||||
by hand with an address in the nucleus overwrites it and the node stops.
|
||||
`!` can do the same; whether the kernel should refuse it is not ruled.
|
||||
His rulings, on what v3 does: **the kernel refuses a request that would
|
||||
overwrite the node's code**, and **v3's way in full** — the block words
|
||||
are the kernel's, by number only, with the kernel keeping the record of
|
||||
the window and doing the copying. Both are built: 8.2 and 8.3.
|
||||
|
||||
### 8.6 Withdrawn 2026-10-07
|
||||
|
||||
@@ -628,17 +634,23 @@ Each is tested, committed and pushed before the next.
|
||||
6. **Storage (section 8): every node asks the kernel for its blocks.**
|
||||
**Done 2026-10-07.** It was first built another way and brought back;
|
||||
8.6 says what was withdrawn and why.
|
||||
- *The requests* (`v4/include/v4/blocks.h`, `v4/system/blocks.c`). -1
|
||||
reads a block and -2 writes one, `( n waddr -- status )`, for any
|
||||
node, from the kernel's block subsystem. `v4/tests/test_blocks.c`: 22
|
||||
checks at 64 bits, 21 at 32 — a block written and read, the fast RAM,
|
||||
numbers that are no block, cells that are not in the node's memory,
|
||||
and that what v3's own calls wrote is what the node reads.
|
||||
- *The node* (`v4/capsule/blocks.v4`). `(DEVICE)` makes the request on
|
||||
port 0. The four storage registers and `v4_node_storage_attach` are
|
||||
gone from the engine. Error 17 is "Storage refused".
|
||||
`v4/tests/test_host_quit.c`: its block cases unchanged, and a disk
|
||||
that will not be written says so and empties the buffer.
|
||||
- *The requests* (`v4/include/v4/blocks.h`, `v4/system/blocks.c`). Five,
|
||||
-1 to -5: `BLOCK`, `BUFFER`, `UPDATE`, `SAVE-BUFFERS`,
|
||||
`EMPTY-BUFFERS`, for any node, by block number only. The kernel keeps
|
||||
the node's window of four slots. `v4/tests/test_host_blocks.c`: 41
|
||||
checks at 64 bits, 39 at 32 — a block copied into a slot and found
|
||||
there again, `UPDATE` and when a block is written, `EMPTY-BUFFERS`,
|
||||
`BUFFER`, more blocks than slots, numbers that are no block, too
|
||||
little on the stack, a window not in the node's memory, and the chain
|
||||
changing under what the slots hold.
|
||||
- *The node* (`v4/capsule/blocks.v4`). Each block word is one request
|
||||
on port 0; the node's own buffers and its record of them are gone, and
|
||||
so are the four storage registers and `v4_node_storage_attach`. Error
|
||||
17 is "Storage refused". The window takes 512 cells more than the two
|
||||
buffers did, and the dictionary space ends 512 cells lower, at 13824.
|
||||
`v4/tests/test_host_quit.c`: its block cases, those that counted on
|
||||
two buffers rewritten for four slots, and a disk that will not be
|
||||
written says so and is let go of.
|
||||
- *v3* (`v3/src/block_subsystem.c`). `blk_subsys_init` takes no `VM`.
|
||||
Nothing else of it is changed. Accepted on the v3 configuration, three
|
||||
ISAs, `PARITY:M7.1a` hash `0x08873e0f44b7cb2a` as on 2026-10-03:
|
||||
@@ -658,7 +670,9 @@ Each is tested, committed and pushed before the next.
|
||||
a block that is not there. `logs/20261007-081603` (amd64), `-081839`
|
||||
(aarch64), `-082226` (riscv64); and again after the review's changes
|
||||
below, with blocks 1 and 2047 read clean at the prompt:
|
||||
`logs/20261007-085017`, `-085254`, `-085636`. The parity hashes are the same on the
|
||||
`logs/20261007-085017`, `-085254`, `-085636`; and as it stands, with the
|
||||
block words the kernel's: `logs/20261007-092835`, `-093112`,
|
||||
`-093456`. The parity hashes are the same on the
|
||||
three hosted and the three bare-metal systems.
|
||||
- *Review, 2026-10-07.* One reading of the whole step by a fresh
|
||||
reviewer; no critical defect. Changed after it: a block request with
|
||||
@@ -669,7 +683,9 @@ Each is tested, committed and pushed before the next.
|
||||
`blocks.c` is built under each test's own warnings and sanitizers;
|
||||
the hosted link no longer takes whatever objects lie in its
|
||||
directory (it had linked the withdrawn `store_v3.o`). Two findings
|
||||
are for ruling, section 8.5.
|
||||
went to Captain Bob, and his rulings on them are section 8.5; they
|
||||
made the block words the kernel's, which replaced the request first
|
||||
built here, `( n waddr -- status )`.
|
||||
- **Not as intended yet.**
|
||||
- Who may have which block is not checked (8.4).
|
||||
- On bare metal the virtio disk is read and not written. v3's
|
||||
|
||||
@@ -188,9 +188,10 @@ array behind its four block registers (D-19), which skips the address
|
||||
space, the metadata, the owner, the ACL and the Stadium.
|
||||
|
||||
**As of 2026-10-07** (`MESH.md` step 6): the registers and the RAM array
|
||||
are gone. A node's block words make a kernel request and
|
||||
`v4/system/blocks.c` serves it through v3's block subsystem, so the address
|
||||
space and the metadata are v3's. Still skipped: the owner and first-touch
|
||||
are gone. A node's block words are kernel requests by block number, and
|
||||
`v4/system/blocks.c` serves them through v3's block subsystem into the
|
||||
node's window of four slots, as v3's block words do for a VM; so the
|
||||
address space and the metadata are v3's. Still skipped: the owner and first-touch
|
||||
claim, the ACL, and the Stadium touch, which need the node's identity. A
|
||||
first build of that step had block requests passed from node to node and
|
||||
nodes with no storage; Captain Bob withdrew it as a divergence from this
|
||||
|
||||
Reference in New Issue
Block a user