feat(v4.0.0): the byte-string words

FILL ERASE MOVE, COUNT CMOVE CMOVE> BLANK -TRAILING COMPARE SEARCH SCAN
SKIP, as loops over the call-free C@ and C!, executed on the golden
model at both cell widths against C and five recorded transcripts of
the v3 binary.

A negative count reads as 0 where v3 read it so, and elsewhere writes
nothing and sets NODE-ERROR.  v3's counted-string auto-detection is not
kept in any of them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rajames
2026-10-03 17:25:27 -04:00
co-authored by Claude Opus 5.5
parent 7c5be22799
commit cb66db1107
2 changed files with 783 additions and 16 deletions
+77 -16
View File
@@ -147,8 +147,8 @@ IF body1 ELSE body2 THEN → if L1 drop body1 jump L2
times, as on the F18. `FOR ... UNEXT` is the same but the body must fit in one instruction word.
**Register conventions.** `A` and `B` are caller-saved. A word that uses them says so. Words in this
document that clobber `A`: `@ ! +! -! 2@ 2! C@ C! DECIMAL HEX OCTAL UM* * UM/MOD Q.FROM-INT Q.TO-INT Q.* Q./ Q.EXP Q.SQRT Q.LOG Q.SIN Q.COS HOLD SIGN # #S . .R U. U.R D. D.R ? DUMP Q.PRINT TYPE SEND RECV`. Words that clobber `B`:
`Q./ Q.EXP Q.SQRT Q.LOG Q.SIN Q.COS <# HOLD SIGN # #S #> . .R U. U.R D. D.R ? DUMP Q.PRINT EMIT CR SPACE SPACES TYPE SEND RECV`.
document that clobber `A`: `@ ! +! -! 2@ 2! C@ C! FILL ERASE MOVE COUNT CMOVE CMOVE> BLANK -TRAILING COMPARE SEARCH SCAN SKIP DECIMAL HEX OCTAL UM* * UM/MOD Q.FROM-INT Q.TO-INT Q.* Q./ Q.EXP Q.SQRT Q.LOG Q.SIN Q.COS HOLD SIGN # #S . .R U. U.R D. D.R ? DUMP Q.PRINT TYPE SEND RECV`. Words that clobber `B`:
`COMPARE SEARCH Q./ Q.EXP Q.SQRT Q.LOG Q.SIN Q.COS <# HOLD SIGN # #S #> . .R U. U.R D. D.R ? DUMP Q.PRINT EMIT CR SPACE SPACES TYPE SEND RECV`.
**Return-stack words** (`>R R> R@ 2>R 2R> 2R@ I J UNLOOP` and the loop runtimes) are always IN.
@@ -333,9 +333,9 @@ Section numbers match the v3 primitive reference.
| `2!` | CAP | `a! SWAP !+ !` |
| `C@` | CAP | See below (D-1). Call-free. Executed on the golden model (2026-10-03). Leaves its caller 8 data cells and 7 return entries. |
| `C!` | CAP | See below (D-1). Call-free. Executed on the golden model (2026-10-03). |
| `FILL` | CAP | See below. |
| `MOVE` | CAP | `push 2DUP U< IF pop CMOVE> ELSE pop CMOVE THEN` |
| `ERASE` | CAP | `0 FILL` |
| `FILL` | CAP | `( baddr u c -- )`: the low byte of `c`. See below. Nothing for `u = 0`; for `u < 0` nothing is written and `NODE-ERROR` is set (v3 read a negative count as a huge unsigned one). Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 5 data cells and 5 return entries. Clobbers `A` and, on error, `B`. |
| `MOVE` | CAP | `( src dst u -- )`, bytes: `push over over - -if UP drop pop jump CMOVE> UP: drop pop jump CMOVE` — `CMOVE>` when `src < dst`, else `CMOVE`, so overlapping bytes are read before they are overwritten, as v3's `memmove`. The test is the sign of `src - dst`, in line. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 4 data cells and 5 return entries. |
| `ERASE` | CAP | `0 FILL`, as `0 jump FILL`. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. |
| `CELLS` | IN | Empty (word-addressed). Becomes `2* 2*` if D-1 chooses bytes. |
```forth
@@ -366,7 +366,10 @@ Section numbers match the v3 primitive reference.
K0: drop @ -256 and + ! ; \ byte 0
: FILL ( baddr u c -- )
SWAP BEGIN dup WHILE 1- push 2DUP SWAP C! SWAP 1+ SWAP pop REPEAT 2DROP drop ;
-ROT \ c baddr u
-if OK drop drop drop NODE-ERROR b! -1 !b ; \ u < 0
OK: if DONE push over over C! 1 + pop -1 + jump OK
DONE: drop drop drop ;
```
### 5.4 Arithmetic
@@ -554,14 +557,15 @@ width and print a flood of spaces.
| Word | Fate | Notes |
| --- | --- | --- |
| `COUNT` | CAP | `dup 1+ SWAP C@` |
| `CMOVE` | CAP | See below. |
| `CMOVE>` | CAP | See below. |
| `BLANK` | CAP | `32 FILL` |
| `-TRAILING` | CAP | Loop from the end while the character is a space. |
| `COMPARE` | CAP | Byte loop returning `-1`, `0`, or `1`. v3's counted-string auto-detection is not kept. |
| `SEARCH` | CAP | Nested loop over `COMPARE`. Same note. |
| `SCAN` `SKIP` | CAP | Byte loops. |
| `COUNT` | CAP | `( baddr -- baddr+1 c )`: `dup C@ push 1 + pop`. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 7 data cells and 6 return entries. Clobbers `A`. |
| `CMOVE` | CAP | `( src dst u -- )`, low byte first. See below. Nothing for `u = 0`; for `u < 0` nothing is written and `NODE-ERROR` is set, where v3 raised its error flag. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 5 data cells and 5 return entries. |
| `CMOVE>` | CAP | `( src dst u -- )`, high byte first. See below. Errors as `CMOVE`. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 4 data cells and 5 return entries. |
| `BLANK` | CAP | `32 FILL`, as `32 jump FILL`. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. v3's counted-string auto-detection is not kept. |
| `-TRAILING` | CAP | `( baddr u -- baddr u' )`. See below. A negative count reads as 0, as v3. v3's counted-string auto-detection is not kept. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 6 data cells and 6 return entries. |
| `COMPARE` | CAP | `( a1 u1 a2 u2 -- n )`: `-1`, `0` or `1` by the first differing byte, unsigned, else the shorter string is less. See below. Negative counts read as 0, as v3. v3's counted-string auto-detection is not kept. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 4 data cells and 5 return entries. Clobbers `A` and `B`. |
| `SEARCH` | CAP | `( a1 u1 a2 u2 -- a3 u3 flag )`: the rest of string 1 from the first place string 2 occurs and `-1`, or string 1 and `0`; an empty string 2 is found at the start. See below; the inner comparison is in line, not a call to `COMPARE`. Same notes. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 3 data cells and 5 return entries. Clobbers `A` and `B`. |
| `SCAN` `SKIP` | CAP | `( baddr u c -- baddr' u' )`: `SCAN` stops at the first byte equal to the low byte of `c` (or at the end, with `u' = 0`); `SKIP` stops at the first byte not equal to it. See below. A negative count reads as 0, as v3. v3's counted-string auto-detection is not kept. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Each leaves its caller 5 data cells and 5 return entries. Clobber `A`. |
| `(S)` | CAP | Variable, 4 cells: `COMPARE`'s two lengths; `SEARCH`'s string 2 and the whole of string 1. |
| `BL` | IN | `32` |
| `EXPECT` `QUERY` | CC | Built on `KEY` (DEV). |
| `SPAN` `TIB` `>IN` `SOURCE` | CC | Interpreter state on the host node. |
@@ -571,11 +575,68 @@ width and print a flood of spaces.
| `LITERAL` `[LITERAL]` (placeholders) | RET | The working `LITERAL` is in §5.17. |
```forth
\ SWAP and the subtractions ( - ) are in line.
: CMOVE ( src dst u -- )
BEGIN dup WHILE 1- push over C@ over C! 1+ SWAP 1+ SWAP pop REPEAT drop 2DROP ;
-if OK drop drop drop NODE-ERROR b! -1 !b ; \ u < 0
OK: if DONE push \ src dst R: u
over C@ over C! 1 + push 1 + pop pop -1 + jump OK
DONE: drop drop drop ;
: CMOVE> ( src dst u -- )
BEGIN dup WHILE 1- push over R@ + C@ over R@ + C! pop REPEAT drop 2DROP ;
-if OK drop drop drop NODE-ERROR b! -1 !b ; \ u < 0
OK: if DONE -1 + push \ src dst R: i = u - 1
over pop dup push + C@ \ src dst c
over pop dup push + C! \ src dst
pop jump OK
DONE: drop drop drop ;
: -TRAILING ( baddr u -- baddr u' )
-if L drop 0 ; \ u < 0
L: if DONE over over + -1 + C@ -32 + if SP drop ; \ not a space
SP: drop -1 + jump L
DONE: ;
: COMPARE ( a1 u1 a2 u2 -- n )
-if A drop 0 A: (S) 1 + b! !b \ a1 u1 a2
SWAP -if B drop 0 B: dup (S) b! !b \ a1 a2 u1
(S) 1 + b! @b \ a1 a2 u1 u2
over over - -if GE drop drop jump M \ the smaller
GE: drop push drop pop
M: \ a1 a2 m
L: if EQ push \ a1 a2 R: m
over C@ over C@ - if SAME \ c1 - c2
-if GT drop drop drop pop drop -1 ;
GT: drop drop drop pop drop 1 ;
SAME: drop 1 + push 1 + pop pop -1 + jump L
EQ: drop drop drop (S) b! @b (S) 1 + b! @b - \ u1 - u2
-if NN drop -1 ;
NN: if ZZ drop 1 ;
ZZ: ;
: SEARCH ( a1 u1 a2 u2 -- a3 u3 flag )
-if A drop 0 A: (S) 1 + b! !b (S) b! !b \ a1 u1 (S): a2 u2
-if B drop 0 B: dup (S) 3 + b! !b over (S) 2 + b! !b \ a u (S)+2: a1 u1
L: dup (S) 1 + b! @b - -if TRY \ u - u2
drop drop drop (S) 2 + b! @b (S) 3 + b! @b 0 ; \ not found
TRY: drop over (S) b! @b (S) 1 + b! @b \ a u p q k
I: if MATCH push \ a u p q R: k
over C@ over C@ xor if SAME
drop drop drop pop drop push 1 + pop -1 + jump L \ next place
SAME: drop 1 + push 1 + pop pop -1 + jump I
MATCH: drop drop drop -1 ;
: SCAN ( baddr u c -- baddr' u' )
255 and push -if L drop 0 \ baddr u R: c
L: if DONE over C@ pop dup push xor if FOUND
drop push 1 + pop -1 + jump L
FOUND: drop
DONE: pop drop ;
: SKIP ( baddr u c -- baddr' u' )
255 and push -if L drop 0 \ baddr u R: c
L: if DONE over C@ pop dup push xor if SAME drop jump DONE
SAME: drop push 1 + pop -1 + jump L
DONE: pop drop ;
```
### 5.10 Terminal I/O