From 40178098e9f372e6fc1f48be5be6f8c3a5ba27a4 Mon Sep 17 00:00:00 2001 From: rajames Date: Sat, 3 Oct 2026 12:13:25 -0400 Subject: [PATCH] feat(v4.0.0): EMIT, CR, SPACE and TYPE on the console register : EMIT ( c -- ) CONSOLE-TX b! !b ; : CR ( -- ) 10 jump EMIT : SPACE ( -- ) 32 jump EMIT : TYPE ( baddr u -- ) -if OK drop drop NODE-ERROR b! -1 !b ; OK: if DONE over C@ EMIT push 1 + pop -1 + jump OK DONE: drop drop ; They follow v3 (v3/src/word_source/io_words.c): EMIT prints the low byte of the cell, CR character 10, SPACE a blank; TYPE prints u bytes, nothing for u = 0, and for u < 0 nothing, with NODE-ERROR set where v3 raised its error flag. TYPE does not check the address range, which v3 does; out-of-range addressing is still an open question in node.h. New test v4/tests/test_terminal.c, on a node of its own with the console attached, 939 checks per width. Two transcripts of the real v3 binary are recorded as expected output: 65 EMIT 66 EMIT SPACE 67 EMIT CR 68 EMIT 321 EMIT -> "AB C\nDA" S" Hello, v3" TYPE 91 EMIT 0 TYPE 93 EMIT -> "Hello, v3[]" Also: EMIT of every byte and of wider values, CR, SPACE, TYPE from every start within a cell at lengths 0..40, bytes with the top bit set and zero bytes, negative counts, and that TYPE writes no memory. At 32- and 64-bit cells, optimised and ASan+UBSan (`make test`, `make sanitize`). Five mutations each fail. Headroom (data cells under args / return entries): EMIT 8/8, TYPE 4/3. TYPE's depth is C@'s, which is still as written (C@ -> RSHIFT -> SWAP). Test results on the amd64 host only. This is a development check, not acceptance (JUSTIFICATION.md section 16). Co-Authored-By: Claude Opus 5.5 --- docs/v4.0.0/DECOMPOSITION.md | 12 +- v4/tests/test_terminal.c | 282 +++++++++++++++++++++++++++++++++++ 2 files changed, 288 insertions(+), 6 deletions(-) create mode 100644 v4/tests/test_terminal.c diff --git a/docs/v4.0.0/DECOMPOSITION.md b/docs/v4.0.0/DECOMPOSITION.md index 930b4179..dfc9974d 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 SEND RECV`. Words that clobber `B`: -`Q./ Q.EXP Q.SQRT Q.LOG Q.SIN Q.COS <# HOLD SIGN # #S #> 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 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`. **Return-stack words** (`>R R> R@ 2>R 2R> 2R@ I J UNLOOP` and the loop runtimes) are always IN. @@ -517,12 +517,12 @@ return stack. `-ROT` is in line (`SWAP push SWAP pop`). | Word | Fate | Notes | | --- | --- | --- | -| `EMIT` | DEV | Console service: one-character message. | +| `EMIT` | DEV | Console service: one-character message. Until the mesh exists it is a store to the `CONSOLE-TX` register (§7): `CONSOLE-TX b! !b`. As in v3, the low byte of the cell is the character. Executed on the golden model (2026-10-03). Clobbers `B`. | | `KEY` | DEV | Console service: blocking receive. | | `?TERMINAL` | DEV | Console service: non-blocking status. | -| `TYPE` | CAP | Loop of `C@ EMIT`, or one string message to the console node. | -| `CR` | CAP | `10 EMIT` | -| `SPACE` | CAP | `BL EMIT` | +| `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` | | `."` `(do-string)` | CC | Compiler words. | diff --git a/v4/tests/test_terminal.c b/v4/tests/test_terminal.c new file mode 100644 index 00000000..359ab414 --- /dev/null +++ b/v4/tests/test_terminal.c @@ -0,0 +1,282 @@ +/* test_terminal.c -- EMIT, CR, SPACE and TYPE, executed. + * + * DECOMPOSITION.md 5.10. EMIT is a device service; on the single-node model + * it is a store to the CONSOLE-TX capture register (node.h), so what these + * words print can be read back and compared with v3. + * + * v3 (v3/src/word_source/io_words.c): EMIT prints the low byte of the cell; + * CR prints character 10; SPACE prints a blank; TYPE ( addr u -- ) prints u + * bytes, nothing for u = 0, and for u < 0 prints nothing and raises its error + * flag. v4 sets NODE-ERROR in that last case, as D-13 does for HOLD. + * + * Two transcripts of the real v3 binary are recorded below as expected + * output. They were taken on 2026-10-03 from + * printf '