From 7a21f07ad5b393d8992a26b9f721968207522336 Mon Sep 17 00:00:00 2001 From: rajames Date: Sat, 3 Oct 2026 11:51:03 -0400 Subject: [PATCH] feat(v4.0.0): # keeps its high quotient on the data stack `#` held the high quotient on the return stack across its second UM/MOD, which was the deepest point of pictured output once C! was made call-free. It now rotates it under the division on the data stack (-ROT in line: SWAP push SWAP pop). Headroom, return entries: <# #S #> 2 -> 3, the signed picture 1 -> 2. Data room is unchanged. Same 4716 checks per width at 32- and 64-bit cells, optimised and ASan+UBSan (`make test`, `make sanitize`); the headroom checks now require the new figures. 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 | 8 +++++--- v4/tests/test_pictured.c | 15 +++++++++------ 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/docs/v4.0.0/DECOMPOSITION.md b/docs/v4.0.0/DECOMPOSITION.md index db348f1d..4d5446b3 100644 --- a/docs/v4.0.0/DECOMPOSITION.md +++ b/docs/v4.0.0/DECOMPOSITION.md @@ -452,7 +452,7 @@ All output reaches the console through `EMIT` (DEV). | Word | Fate | Notes | | --- | --- | --- | -| `<#` `#` `#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 3 data cells and 2 return entries; the signed picture `dup push ABS 0 <# #S pop SIGN #>` leaves 1 return entry. The deepest point is inside `#`, which holds the high quotient on the return stack across its second `UM/MOD`. `#`, `#S`, `HOLD` and `SIGN` clobber `A` and `B`. | +| `<#` `#` `#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`. | | `.S` | RET | No visible stack pointer (D-2). | @@ -472,7 +472,8 @@ All output reaches the console through `EMIT` (DEV). ROOM: drop HLD b! @b -1 + dup !b jump C! : SIGN ( n -- ) -if L1 drop 45 jump HOLD L1: drop ; : # ( ud1 -- ud2 ) - 0 (BASE) UM/MOD push (BASE) UM/MOD pop ROT \ qlo qhi rem + 0 (BASE) UM/MOD -ROT \ qhi lo rem1 + (BASE) UM/MOD -ROT \ qlo qhi rem dup -10 + -if L1 drop 48 + jump L2 L1: drop 55 + L2: jump HOLD : #S ( ud -- 0 0 ) L: # over over OR if L1 drop jump L L1: drop ; : #> ( ud -- baddr u ) drop drop HLD b! @b HEND over push inv pop + inv ; @@ -481,7 +482,8 @@ All output reaches the console through `EMIT` (DEV). `#` divides the double by the base in two `UM/MOD` steps, the high cell first and its remainder leading the low cell; the last remainder is the digit. `HOLD`, `SIGN` and `#` end with a jump to the next word rather than a call, so `C!` returns straight to their caller and two return-stack entries -are saved. `ROT` is in line. +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`). ### 5.9 Strings, parsing, and input diff --git a/v4/tests/test_pictured.c b/v4/tests/test_pictured.c index 0a6f1ad5..8662f9ac 100644 --- a/v4/tests/test_pictured.c +++ b/v4/tests/test_pictured.c @@ -57,7 +57,7 @@ static v4_cell w_swap, w_or, w_ummod, w_lshift, w_rshift, w_cstore, #define VGET(a) do { LIT(a); O(BANG_B); O(FETCH_B); } while (0) #define VSET(a) do { LIT(a); O(BANG_B); O(STORE_B); } while (0) #define SWAP_INLINE() do { O(OVER); O(PUSH); O(PUSH); O(DROP); O(RPOP); O(RPOP); } while (0) -#define ROT_INLINE() do { O(PUSH); SWAP_INLINE(); O(RPOP); SWAP_INLINE(); } while (0) +#define NROT_INLINE() do { SWAP_INLINE(); O(PUSH); SWAP_INLINE(); O(RPOP); } while (0) #define SUB_INLINE() do { O(PUSH); O(INV); O(RPOP); O(ADD); O(INV); } while (0) /* The words pictured output rests on, as in test_foundation.c. */ @@ -238,11 +238,14 @@ static void build_pictured(void) /* : # ( ud1 -- ud2 ) * ud1 / base in two UM/MOD steps, the high cell first and its remainder * leading the low cell; the last remainder is the digit. - * 0 (BASE) UM/MOD push (BASE) UM/MOD pop ROT qlo qhi rem + * 0 (BASE) UM/MOD -ROT qhi lo rem1 + * (BASE) UM/MOD -ROT qlo qhi rem + * The high quotient waits under the second division on the data stack, + * not on the return stack. -ROT in line is SWAP push SWAP pop. * dup -10 + -if L1 drop 48 + jump L2 L1: drop 55 + L2: jump HOLD */ w_hash = v4_asm_label(&as); - LIT(0); CALL(w_base); CALL(w_ummod); O(PUSH); - CALL(w_base); CALL(w_ummod); O(RPOP); ROT_INLINE(); + LIT(0); CALL(w_base); CALL(w_ummod); NROT_INLINE(); + CALL(w_base); CALL(w_ummod); NROT_INLINE(); O(DUP); LIT(-10); O(ADD); a = FWD(MINUS_IF); O(DROP); LIT(48); O(ADD); b = FWD(JUMP); HERE_(a); @@ -510,9 +513,9 @@ int main(void) int dh, rh; v4_node_store(&n, BASE, 10); headroom("<# #S #>", t_ud, 2, 12345, 0, "12345", &dh, &rh); - CHECK(dh >= 2 && rh >= 2, "<# #S #> leaves room"); + CHECK(dh >= 2 && rh >= 3, "<# #S #> leaves room"); headroom("signed picture", t_signed, 1, -12345, 0, "-12345", &dh, &rh); - CHECK(dh >= 2 && rh >= 1, "the signed picture leaves room"); + CHECK(dh >= 2 && rh >= 2, "the signed picture leaves room"); } CHECK(v4_node_guards_intact(&n), "guards intact");