Files
LithosAnanake/docs/v4.0.0/DATASHEET-F18.md
T
rajamesandClaude Opus 5.5 32753f5479 feat(v4): the machine has a ROM -- read and run, never written
A read-only range of V4_ROM_WORDS words at V4_ROM_BASE, above RAM
(MACHINE.md 2, DATASHEET-F18.md 5).  Instruction fetch, @p and data fetches
read it; a running programme's store there is an address fault.  Attached by
the owner like every device; a reset detaches it.  The host node's range is
4096 words.  text.c places branches by the whole address space.  The
nucleus image is byte-identical.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 11:25:24 -04:00

16 KiB
Raw Blame History

StarForth v4 F18 Machine — Datasheet

The machine: registers, stacks, memory, instruction word, opcodes, control unit, faults and I/O. This sheet says what the machine is. Nothing above the machine is here: no FORTH, no interpreter, no compiler, no dictionary. Those are source, in the capsule's trailer (MACHINE.md).

Source of truth. The golden model in C: v4/src/ and v4/include/v4/, as built on 2026-10-15. The FPGA machine will match it instruction for instruction (JUSTIFICATION.md §10, step 3: Verilator co-simulation in lockstep). Every statement here is checked against the model's code, cited by file. Where the model is not yet what was intended, the sheet says so (§13).

Design history: DECOMPOSITION.md §1 (the ISA) and §3 (decisions D-1 to D-19). Where the two differ, the model and this sheet follow the later ruling.


1. Summary

Architecture Two-stack machine derived from Chuck Moore's F18 (GA144 node)
Instruction set 32 opcodes, 5 bits each
Instruction word 32 bits: six 5-bit slots + 2 spare bits, at every cell width
Cell (data) width 32 or 64 bits, a build parameter
Addressing Word-addressed. An address counts cells.
Registers P, A, B; T, S (top of the data stack); R (top of the return stack)
Data stack T, S + ring. 10 deep on a mesh node, 32 on the host node. Counted and guarded.
Return stack R + ring. 9 deep on a mesh node, 32 on the host node. Counted and guarded.
Memory RAM, a build parameter (1024 words mesh, 16384 host)
I/O Memory-mapped: ports, console, stack-depth registers, error register
Faults 6 kinds, vectored through a 6-word table
Side effects Per-opcode heat, per-call-target heat, anti-clock. No instruction cost.

2. Build parameters

Parameter Symbol Values Where
Cell width V4_CELL_BITS 32, 64 cell.h
RAM words V4_NODE_WORDS default 1024; host node 16384 node.h, v4/Makefile HOST_WORDS
Data ring V4_DATA_RING default 8 (depth 10); host 30 (depth 32) stack.h, v4/Makefile
Return ring V4_RET_RING default 8 (depth 9); host 31 (depth 32) stack.h, v4/Makefile
Ports V4_PORTS default 8 node.h

The host node is the same machine with more memory and deeper stacks (D-17). Nothing else differs.

3. Registers

Reg Width Holds Visible to code through
P cell Program counter: the address of the next instruction word, or of the next @p/!p cell @p, !p, ex, branches, ;
A cell Address register; also the low half of the +* product a, a!, @, @+, !, !+, +*
B cell Address register b!, @b, !b
T cell Top of the data stack every data opcode
S cell Second on the data stack over, +, and, xor, +*
R cell Top of the return stack ;, ex, call, next, unext, push, pop
I 32 bits The instruction word being executed, and the slot within it not visible. Held by the control unit (exec.c).

T and S are the top two places of the data stack, and R is the top place of the return stack. They are not copies of stack cells (node.h, stack.h). There is no stack pointer register and no flags register. Conditions are tested on T directly (if, -if).

4. Stacks

Data stack (v4_dstack, stack.h): T, S, then a ring of V4_DATA_RING cells.

  • A push moves S into the ring, T into S, and the new value into T.
  • A pop does the reverse.

Return stack (v4_rstack): R, then a ring of V4_RET_RING cells, the same way.

Counted and guarded (D-16). Each stack counts what it holds, from 0 to its depth. Before every opcode the control unit checks two things:

  • the stacks hold what the opcode takes, including a T or S it only reads;
  • the stacks have room for what it leaves.

If either check fails, the opcode does nothing and the machine faults (§9). The ring's wrap is never seen by a running program.

Not addressable. There is no address for any stack cell. A program sees only the two depth registers (§10.3).

5. Memory

  • Word-addressed (D-1). An address names a cell, so CELLS is the identity.

    • A byte address is 4 × word + 0..3. That is a software convention used by the block format and strings. The machine itself has no byte access.
  • RAM: addresses 0 to V4_NODE_WORDS − 1.

  • Out of range (D-14). Every address a running program uses is checked before it is used:

    • P, for an instruction fetch, an @p fetch or a !p store;
    • A, for @ @+ ! !+;
    • B, for @b !b.

    An address outside RAM is an address fault (§9). No access is made, and A and B are left unchanged.

  • I/O addresses (§10) sit inside the address space. A fetch from or store to one goes to the device, not to RAM. The exceptions are NODE-ERROR, which writes RAM and then traps, and the console receive addresses, where a store goes to RAM.

  • ROM. Designed, not built: MACHINE.md §2, and plan Task 1. It is a read-only range at V4_NODE_WORDS and above. It can be fetched and executed. A store there is an address fault. In the model it is attached by the owner, as every device is, and a reset detaches it.

    • Its routines are shared code, GA144-style (MACHINE.md 2.1). Payloads call them through the entry table at the ROM's base. Their state lives in a reserved RAM area. This sheet will list each entry's stack effect and the registers it uses once the ROM is built.

6. Instruction word

 31    27 26    22 21    17 16    12 11     7 6      2 1  0
+--------+--------+--------+--------+--------+--------+----+
| slot 0 | slot 1 | slot 2 | slot 3 | slot 4 | slot 5 | xx |
+--------+--------+--------+--------+--------+--------+----+
  • 32 bits at every cell width (D-9). On a 64-bit machine the instruction word is the low 32 bits of the cell, and the high half is ignored. Compiled code is identical at 32 and 64. Literals and data are a full cell.
  • Slots execute left to right.
  • A branch (jump call next if -if) takes its target from every bit to the right of its slot, the 2 spare bits included. The target replaces the same low bits of P, and the bits of P above them are kept. Branches are page-relative, as on the F18.
Branch in slot Address bits Reach
0 27 128M words
1 22 4M words
2 17 128K words
3 12 4K words
4, 5 7, 2 outside the spec. The model does not trap it; it applies the 7- or 2-bit target.
  • The spare bits are never executed. They are address bits for a branch in any slot.
  • The low bits of a non-branch slot are ignored by that opcode.

7. Opcodes

"Takes" and "leaves" are what the control unit checks before the opcode runs (§4). "d" is the data stack, "r" is the return stack.

Op Name Effect Takes Leaves room for
00 ; P ← R, pop R r 1
01 ex swap P and R r 1
02 jump a P ← a
03 call a push P to R; P ← a; +1 heat of a r 1
04 unext if R ≠ 0: R ← R−1, and the same word runs again from slot 0, with no refetch and P unchanged. Else pop R and go on to the next slot. r 1
05 next a if R ≠ 0: R ← R−1, P ← a. Else pop R. r 1
06 if a if T = 0: P ← a. T is not popped. d 1
07 -if a if T ≥ 0 (sign bit clear): P ← a. T is not popped. d 1
08 @p push the cell at P; P ← P+1 d 1
09 @+ push the cell at A; A ← A+1 d 1
0A @b push the cell at B d 1
0B @ push the cell at A d 1
0C !p cell at P ← T, pop; P ← P+1 d 1
0D !+ cell at A ← T, pop; A ← A+1 d 1
0E !b cell at B ← T, pop d 1
0F ! cell at A ← T, pop d 1
10 +* if A bit 0: T ← T+S (the carry out is not kept, D-3). Then T:A shift right one bit: T keeps its sign bit, T's bit 0 goes into A's top bit. S is not popped. d 2
11 2* T ← T << 1 d 1
12 2/ T ← T >> 1, arithmetic d 1
13 inv T ← ~T (F18 -) d 1
14 + T ← S + T, pop. Wraps. d 2
15 and T ← S & T, pop d 2
16 xor T ← S ^ T, pop (F18 or) d 2
17 drop pop T d 1
18 dup push T d 1 d 1
19 pop pop R, push it onto the data stack r 1 d 1
1A over push S d 2 d 1
1B a push A d 1
1C nop nothing
1D push pop T, push it onto R d 1 r 1
1E b! B ← T, pop d 1
1F a! A ← T, pop d 1

Arithmetic. All arithmetic wraps, two's complement, at the cell width. There is no carry flag, no overflow flag and no trap.

Names. Two names differ from Moore's: inv is F18 -, and xor is F18 or. No opcode name is a FORTH-79 word name (opcode.h).

8. Control unit

One step (v4_exec_step_word, exec.c):

  1. Stopped or blocked? If the machine has stopped (§9) or is blocked at a port or at Do (§10.4), nothing happens.
  2. Resume. If the machine was blocked part-way through a word and has since been served, it continues from the saved word and slot. A fault while it was blocked cancels the resume.
  3. Fetch.
    • P is checked against memory (§5).
    • If P is a port, the word is whatever the neighbour writes there, and P does not advance. This is how a newborn node runs code sent to it.
    • Otherwise the word is the cell at P, and P ← P+1.
  4. Slots, from 0 (or the resumed slot) to 5. For each:
    • Check the stacks (§4). Then run the opcode. Then count +1 heat for the opcode and +1 on the anti-clock (§11). An opcode that faults does not retire. A raised error retires (§9).
    • If the opcode was a fetch from a port with nothing written yet, the machine blocks before the opcode, and it will resume at this slot.
    • If it faulted, the rest of the word is abandoned, and P is the handler.
    • If it stored to a port or to Do, the machine blocks after the opcode, and it will resume at the next slot.
    • If it was unext with R ≠ 0, the same word runs again from slot 0.
    • If it was a branch, ; or ex, the word ends. Nothing to the right of a branch executes, taken or not.
  5. The step returns the number of opcodes retired.

@p and !p in an unext loop walk the cells after the loop word, because P is not moved by the restart. This is the F18's behaviour.

9. Faults

Kind Code Cause
Address 0 An address outside memory (§5). Also a store to a read-only port address (§10.4).
Data overflow 1 A push onto a full data stack
Data underflow 2 The data stack holds less than the opcode takes
Return overflow 3 A push onto a full return stack
Return underflow 4 The return stack holds less than the opcode takes
Raised 5 A non-zero store to NODE-ERROR (§10.5)

On a fault (v4_node_fault, node.c):

  • The kind and, for an address fault, the address are recorded, and the fault count goes up.
  • The return stack is emptied. The data stack is emptied too, except for a raised error, which keeps it.
  • P ← fault table + kind.

The fault table is 6 words of RAM, one per kind, each normally a jump to that kind's handler. A handler does not return to the faulting program.

With no fault table the machine stops: no further step does anything until it is reset or a table is attached.

10. Memory-mapped I/O

Each device is placed at an address by whoever owns the machine. In the model this is done by an attach call (node.h). Fixed addresses are D-4, deferred to the mesh and the RTL (§13). v4_node_reset detaches every device.

10.1 Console transmit — CONSOLE-TX

A store sends the low 8 bits as one character. RAM is not written. A fetch reads RAM.

10.2 Console receive — CONSOLE-RX, CONSOLE-STATUS

  • A fetch from STATUS gives −1 if a character is pending and 0 if none is. It changes nothing.
  • A fetch from RX takes the next character, 0–255, or gives −1 if there is none.
  • Neither address blocks. A program that must wait polls STATUS.
  • A store to either is an ordinary RAM store.
  • An instruction or literal fetch at these addresses always reads RAM.

10.3 Stack depth — DSTACK-DEPTH, RSTACK-DEPTH (D-16)

  • A fetch gives that stack's depth. For the data stack, the fetch's own push is not counted.
  • A store empties that stack. For the data stack, the value stored is taken off first.

10.4 Ports (DECOMPOSITION.md §6, MESH.md 4.1, 7d.3)

At the port base address:

Offset Name Fetch Store
0 … PORTS−1 port k Blocks until the neighbour on k writes, then gives that word Blocks until the neighbour on k has taken the word
PORTS any Blocks until a neighbour on any port writes address fault
PORTS+1 which The port the last "any" read came from address fault
PORTS+2 writers bit k: the neighbour on k is waiting to write here —
PORTS+3 readers bit k: the neighbour on k is waiting to read. Bit PORTS+k: port k has anything on it at all. —
PORTS+4 Wire what was stored the port the next operation is about
PORTS+5 A what was stored an operand
PORTS+6 B what was stored an operand
PORTS+7 Do the operation number Blocks until the operation is done (whole messages)
PORTS+8 How how the last operation went address fault
PORTS+9 Have bit k: a message is waiting on wire k address fault

A read port also works as an execution source: if P is a port, the machine executes what arrives (§8, step 3). A newborn node (v4_node_born) has P at "any" and blocks there, empty, until a neighbour sends it code.

10.5 Error register — NODE-ERROR (D-18)

  • A store of a non-zero value writes RAM, then raises a fault of kind 5. The value stored is recorded as the fault address.
  • A store of 0 is an ordinary store, which is how the flag is cleared.
  • Not attached, it is plain RAM.

11. Side effects: heat and the anti-clock

Not instructions. They cost nothing (DECOMPOSITION.md §1.4, heat.h):

  • Every retired opcode adds 1 to that opcode's heat counter, HEAT-OP[0..31], and 1 to the anti-clock.
  • Every call adds 1 to its target's heat counter, HEAT-CALL[target].
  • Counters are 64 bits at every cell width (JUSTIFICATION.md §5).

They are not yet memory-mapped: the §7 addresses wait on D-4.

12. Reset and birth

v4_node_reset:

  • P, A and B are set to 0.
  • Both stacks are emptied, and every RAM cell is set to 0.
  • Every device is detached, and the fault table is detached.

v4_node_born(base): a reset, then ports at base, then P = base + PORTS ("any"). The machine blocks there, empty, until it is sent code.

13. Not yet as intended

These are reported, not hidden:

  1. Per-call-target heat is the model's interim structure: heat.h says "in the meantime", and D-6 is deferred. It is a fixed 4096-entry table, and a call to a target at address 4096 or above is not counted. This is the stand-in recorded 2026-10-05. It is not the intended mechanism, and it is not to be extended.
  2. Device addresses are attached by the owner, not fixed. D-4 (the memory map) is deferred. The FPGA needs fixed addresses.
  3. The ROM range is designed (MACHINE.md) and not built.
  4. stack.h's opening comment still says there is no overflow detection "and there never will be". D-16 revised that, and the header carries a revision note above it. The code counts and guards (§4). The comment's first paragraph is out of date.
  5. Branches in slots 4 and 5 are outside the spec but not trapped (§6).

14. What is not in the machine

No FORTH, no interpreter, no compiler, no assembler-in-hardware, no dictionary, no names, no vocabularies, no boot policy. The ROM (MACHINE.md §2) is F18 code the machine runs. It is not part of this sheet's hardware. Everything above the opcodes arrives as source.