feat(v4.0.0): the DO loop runtimes
(DO) (?DO) (LOOP) (+LOOP) (LEAVE) I J UNLOOP and (0BRANCH), laid down in line by hand as the compiler will, executed on the golden model at both cell widths against C on every pair of 14 loop ends and 11 steps, and against sequences recorded from the v3 binary. (LOOP) goes round again while index < limit, signed, as v3 does; the document's equality test differed whenever start >= limit. (+LOOP) is now specified. J keeps the outer index in A and needs no extra return entry. LEAVE discards the limit and index. v3 left them on its return stack, which made a LEAVE in an inner loop stop the outer one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
481d484e93
commit
03d8d991ef
@@ -147,7 +147,7 @@ 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! D2/ M* 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`:
|
||||
document that clobber `A`: `@ ! +! -! 2@ 2! C@ C! D2/ M* 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 +LOOP J 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.
|
||||
@@ -721,28 +721,46 @@ The storage service belongs to the Artemis role, now a device node.
|
||||
| `IF` `ELSE` `THEN` `BEGIN` `UNTIL` `AGAIN` `WHILE` `REPEAT` `DO` `?DO` `LOOP` `+LOOP` `LEAVE` `CASE` `OF` `ENDOF` `ENDCASE` | CC | Compile-time structure words. Expansions per §2 and below. |
|
||||
| `EXIT` | OP | `;` |
|
||||
| `(BRANCH)` | OP | `jump` |
|
||||
| `(0BRANCH)` | IN | `if L … drop` (§2) |
|
||||
| `(DO)` | IN | `SWAP push push` (R: limit index) |
|
||||
| `(?DO)` | IN | `2DUP = IF 2DROP jump past-loop THEN SWAP push push` |
|
||||
| `(LOOP)` | IN | See below. |
|
||||
| `(+LOOP)` | IN | Sign-aware boundary-crossing test; specified in the compiler capsule. |
|
||||
| `(LEAVE)` | IN | `pop drop pop dup push push jump loop-test` (sets index to limit) |
|
||||
| `I` | IN | `pop dup push` |
|
||||
| `J` | IN | `pop pop pop dup push SWAP push SWAP push` |
|
||||
| `UNLOOP` | IN | `pop pop drop drop` |
|
||||
| `(0BRANCH)` | IN | `if L … drop` (§2) — executed on the golden model (2026-10-03) as `IF 1 ELSE 2 THEN`, including results recorded from the v3 binary. |
|
||||
| `(DO)` | IN | `over push push drop` (R: limit index) — `SWAP push push` without the `SWAP`. The body always runs once, as v3. Executed on the golden model (2026-10-03). |
|
||||
| `(?DO)` | IN | `over over xor if SKIP drop (DO) … body and loop end … jump PAST SKIP: drop drop drop PAST:` — skips the loop when index = limit, as v3. Executed on the golden model (2026-10-03). |
|
||||
| `(LOOP)` | IN | See below. Adds 1 and goes round again while index < limit, signed, as v3. Executed on the golden model (2026-10-03) against C on every pair of 14 loop ends, and sequences recorded from the v3 binary. |
|
||||
| `(+LOOP)` | IN | See below. `( n -- )`: adds `n` and goes round again while index < limit for `n >= 0`, while index >= limit for `n < 0`, as v3. Executed on the golden model (2026-10-03) against C on every pair of 14 loop ends with 11 steps, and sequences recorded from the v3 binary. Clobbers `A`. |
|
||||
| `(LEAVE)` | IN | `pop drop pop drop jump PAST` — discards the limit and index and leaves the loop at once. In a single loop this is what v3 does. v3 left the two on its return stack (index set to limit) and jumped past the loop, so a `LEAVE` in an inner loop made the outer `LOOP` count the inner loop's leftovers and stop: `: T 3 0 DO 10 0 DO I . I 1 = IF LEAVE THEN LOOP I . LOOP ;` prints `0 1 10` in v3 and `0 1 0 0 1 1 0 1 2` here. Executed on the golden model (2026-10-03) in `LOOP` and `+LOOP`, including sequences recorded from the v3 binary. |
|
||||
| `I` | IN | `pop dup push` — executed on the golden model (2026-10-03). |
|
||||
| `J` | IN | `pop pop pop dup push a! push push a` — the outer index waits in `A` while the inner pair goes back, so `J` needs no return entry beyond the two loops' own (with two in-line `SWAP`s it needed one more). Executed on the golden model (2026-10-03), including a sequence recorded from the v3 binary. Clobbers `A`. |
|
||||
| `UNLOOP` | IN | `pop drop pop drop` — executed on the golden model (2026-10-03) as `UNLOOP EXIT`, including a sequence recorded from the v3 binary. |
|
||||
|
||||
```
|
||||
<less>, used by both loop ends: ( i' lim -- i' lim s ), the top bit of s set
|
||||
exactly when i' < lim, signed -- i' itself when the two differ in sign, else
|
||||
i' - lim (the subtraction in line):
|
||||
over over xor -if SAME
|
||||
drop over jump T
|
||||
SAME: drop over over -
|
||||
T:
|
||||
|
||||
(LOOP) expansion:
|
||||
pop 1 + pop \ index' limit
|
||||
2DUP xor \ index' limit flag (0 when equal)
|
||||
if Lexit
|
||||
<less> \ index' limit s
|
||||
-if Lexit \ not less: done
|
||||
drop push push \ R: limit index'
|
||||
jump Lbody
|
||||
Lexit: drop drop drop
|
||||
|
||||
(+LOOP) expansion: ( n -- )
|
||||
dup a! pop + pop \ index' limit A: n
|
||||
<less> a xor \ top bit set: go round again
|
||||
-if Lexit
|
||||
drop push push
|
||||
jump Lbody
|
||||
Lexit: drop drop drop
|
||||
```
|
||||
|
||||
`(LOOP)` terminates when the index reaches the limit, which matches v3 for every loop where
|
||||
`start < limit`.
|
||||
`(LOOP)` goes round again while `index' < limit`, signed, which is v3's rule. So `0 5 DO … LOOP` runs
|
||||
once, as in v3; a test for `index' = limit`, which this document had first, would run it round the whole
|
||||
number circle. `(+LOOP)` goes round again exactly when `s` and `n` differ in sign: `index' < limit` for
|
||||
`n >= 0` and the opposite for `n < 0`. A loop costs its word two return-stack entries while it runs.
|
||||
|
||||
**New in v4:** `FOR`, `NEXT`, and `UNEXT` are native (§2). Counted loops that don't need an ascending
|
||||
index should use them; they cost one instruction per iteration.
|
||||
|
||||
Reference in New Issue
Block a user