diff --git a/docs/v4.0.0/DECOMPOSITION.md b/docs/v4.0.0/DECOMPOSITION.md index dfc9974d..cdda7453 100644 --- a/docs/v4.0.0/DECOMPOSITION.md +++ b/docs/v4.0.0/DECOMPOSITION.md @@ -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! UM* * UM/MOD Q.FROM-INT Q.TO-INT Q.* Q./ Q.EXP Q.SQRT Q.LOG Q.SIN Q.COS HOLD SIGN # #S TYPE SEND RECV`. Words that clobber `B`: -`Q./ Q.EXP Q.SQRT Q.LOG Q.SIN Q.COS <# HOLD SIGN # #S #> EMIT CR SPACE TYPE SEND RECV`. +document that clobber `A`: `@ ! +! -! 2@ 2! C@ C! 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 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 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. @@ -454,7 +454,8 @@ All output reaches the console through `EMIT` (DEV). | --- | --- | --- | | `<#` `#` `#S` `HOLD` `SIGN` `#>` | CAP | Pictured output over `UM/MOD` and a 64-byte hold buffer, filled backwards from its end `HEND` through the pointer `HLD`. Standard stack effects (v3 took its double low cell on top, and its tolerant `#>` popped `ud` only if present; neither is kept). Otherwise v3's behaviour: digits `0`–`9` then `A`–`Z`; `BASE` outside 2–36 reads as 10; 63 characters; `HOLD` of a value outside 0–255 or into a full buffer stores nothing and sets `NODE-ERROR` (D-13). Definitions below. Executed on the golden model (2026-10-03) against a C reference in bases 2, 3, 8, 10, 16, 36 and four invalid ones. `<# #S #>` leaves its caller 4 data cells and 3 return entries; the signed picture `dup push ABS 0 <# #S pop SIGN #>` leaves 2 return entries. `#`, `#S`, `HOLD` and `SIGN` clobber `A` and `B`. | | `HLD` | CAP | Variable: byte address of the first held character. | -| `.` `.R` `U.` `U.R` `D.` `D.R` | CAP | Built on pictured output and `TYPE`. | +| `.` `.R` `U.` `U.R` `D.` `D.R` | CAP | Built on pictured output and `TYPE`; definitions below. As v3: the number in the current base, then one space; the `.R` words right-justify it in `width` columns first, print a wider number whole, and pad nothing for `width <= 0`. (The space after the `.R` words is not FORTH-79; it is what v3 prints.) Where v4 parts from v3: a double is `( lo hi )`, where v3's `D.` took the low cell on top; `D.` prints the whole double, where v3 printed `DOUBLE-OVERFLOW` unless it fitted one signed cell (they agree whenever it does); printing honours the `BASE` variable, where v3 printed in a host copy that only `DECIMAL`, `HEX` and `OCTAL` set, so `n BASE !` changed v3's input base but not its output; and the number is built in the 63-character hold buffer, so a longer one — a 64-bit cell in base 2, a large double in a small base — loses its leading characters and sets `NODE-ERROR` (D-13), where v3 printed from a private 80-character buffer. Executed on the golden model (2026-10-03) against a C reference in the ten bases of the pictured-output test and eleven field widths, and against five transcripts of the v3 binary (a sixth, the extreme cells, at 64-bit cells). Each leaves its caller 4 data cells and 2 return entries. All clobber `A` and `B`. | +| `(W)` | CAP | Variable: the field width of the `.R` words. Like `BASE` and `HLD`, it lives in node memory, so the width is on neither stack while the picture runs. | | `.S` | RET | No visible stack pointer (D-2). | | `?` | CAP | `@ .` | | `DUMP` | CAP | Loop over `@`/`C@` with pictured output. | @@ -485,6 +486,25 @@ next word rather than a call, so `C!` returns straight to their caller and two r are saved. `#` keeps the high quotient under the second division on the data stack, not on the return stack. `-ROT` is in line (`SWAP push SWAP pop`). +```forth +\ number output. ABS is in line: -if A inv 1 + A: +: .R ( n width -- ) + (W) b! !b dup push ABS 0 <# #S pop SIGN #> \ baddr u + TAIL: (W) b! @b -if POS drop jump OUT \ width < 0 + POS: over - SPACES \ width - u spaces + OUT: TYPE jump SPACE +: . ( n -- ) 0 jump .R +: U.R ( u width -- ) (W) b! !b 0 <# #S #> jump TAIL +: U. ( u -- ) 0 jump U.R +: D.R ( d width -- ) (W) b! !b dup push -if A DNEGATE A: <# #S pop SIGN #> jump TAIL +: D. ( d -- ) 0 jump D.R +``` + +There is one picture for signed singles, one for unsigned and one for doubles; each plain word is +its `.R` word with a width of 0, entered by a jump, and all six share one tail, which ends in a jump +to `SPACE`. The width is tested for sign before `width - u`, which could wrap for a very negative +width and print a flood of spaces. + ### 5.9 Strings, parsing, and input | Word | Fate | Notes | @@ -523,7 +543,7 @@ return stack. `-ROT` is in line (`SWAP push SWAP pop`). | `TYPE` | CAP | `( baddr u -- )`. Loop of `C@ EMIT`, or one string message to the console node: `-if OK drop drop NODE-ERROR b! -1 !b ; OK: if DONE over C@ EMIT push 1 + pop -1 + jump OK DONE: drop drop ;` — nothing for `u = 0`; for `u < 0` nothing is printed and `NODE-ERROR` is set, where v3 printed nothing and raised its error flag. The address range is not checked (out-of-range addressing is still open, `node.h`). Executed on the golden model (2026-10-03). Leaves its caller 4 data cells and 3 return entries, the depth being `C@`'s as written. Clobbers `A` and `B`. | | `CR` | CAP | `10 EMIT`, as `10 jump EMIT` — executed on the golden model (2026-10-03). Character 10, as v3; the console turns it into a new line. | | `SPACE` | CAP | `BL EMIT`, as `32 jump EMIT` — executed on the golden model (2026-10-03). | -| `SPACES` | CAP | `BEGIN dup 0> WHILE SPACE 1- REPEAT drop` | +| `SPACES` | CAP | `( n -- )`: `-if L drop ; L: if DONE SPACE -1 + jump L DONE: drop ;` — `n` spaces, none for `n <= 0`, as v3. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 7 data cells and 7 return entries. Clobbers `B`. | | `."` `(do-string)` | CC | Compiler words. | ### 5.11 Blocks and mass storage diff --git a/v4/tests/test_numout.c b/v4/tests/test_numout.c new file mode 100644 index 00000000..9d66477b --- /dev/null +++ b/v4/tests/test_numout.c @@ -0,0 +1,692 @@ +/* test_numout.c -- `.` `U.` `D.` `.R` `U.R` `D.R` and SPACES, executed. + * + * DECOMPOSITION.md 5.8 and 5.10. The number-printing words are pictured + * output (test_pictured.c) handed to TYPE (test_terminal.c), so this file + * assembles both sets again, opcode for opcode, on a node of its own with + * the console attached, and reads back what was printed. + * + * v3 (v3/src/word_source/format_words.c, io_words.c): + * . U. the number in the current base, then one space + * .R U.R ( n width -- ) the number right-justified in `width` + * columns, then one space; a number wider than the field is + * printed whole; width <= 0 pads nothing + * D. D.R the same for a double + * SPACES ( n -- ) n spaces, none for n <= 0 + * v3's trailing space after the .R words is not FORTH-79 but is what v3 + * prints, so it is kept. + * + * Where v4 parts from v3, as 5.8 already rules for pictured output: + * - a double is ( lo hi ), high cell on top; v3's D. took the low cell on + * top; + * - D. prints the whole double; v3 printed "DOUBLE-OVERFLOW" unless the + * double fitted one signed cell, and agrees with v4 whenever it does; + * - the number is built in the 63-character hold buffer (D-13), so a + * number longer than that loses its leading characters and sets + * NODE-ERROR. v3 printed from a private 80-character buffer. + * + * Six transcripts of the real v3 binary are recorded below as expected + * output. They were taken on 2026-10-03 from + * printf '