fix(v4.0.0): .R U.R and D.R print no trailing space

FORTH-79's reference .R right-justifies the number in its field and
prints nothing after it; v3 printed a space.  . U. and D. still print
their one space.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rajames
2026-10-04 08:47:29 -04:00
co-authored by Claude Opus 5.5
parent b137678691
commit 01b5bbb23f
3 changed files with 96 additions and 65 deletions
+31 -17
View File
@@ -56,6 +56,7 @@ static int failures = 0, checks = 0;
#define HLD ((v4_cell)(V4_NODE_WORDS - 35u))
#define BASE ((v4_cell)(V4_NODE_WORDS - 34u))
#define FIELD ((v4_cell)(V4_NODE_WORDS - 36u)) /* (W): the field width of the .R words */
#define TRAIL ((v4_cell)(V4_NODE_WORDS - 46u)) /* (W)+1 in the document: whether a blank follows the number */
#define HCAP 63
#define QP ((v4_cell)(V4_NODE_WORDS - 40u)) /* (QP): Q.PRINT's BASE, |q| and sign */
#define DP ((v4_cell)(V4_NODE_WORDS - 44u)) /* (DP): DUMP's BASE, address, count, column */
@@ -301,7 +302,7 @@ static void build_pictured(void)
static void build_output(void)
{
v4_asm_ref a, b, c, d, done1, done2;
v4_cell l, l_tail, l_line;
v4_cell l, l_tail, l_rbody, l_ubody, l_dbody, l_line;
/* : C@ ( baddr -- c ) call-free, 5.3
* dup 2/ 2/ a! 3 and k A: word address
@@ -380,16 +381,21 @@ static void build_output(void)
HERE_(b);
O(DROP); O(SEMI);
/* : .R ( n width -- )
* (W) b! !b dup push -if A inv 1 + A: 0 <# #S pop SIGN #> baddr u
/* : .R ( n width -- ) FORTH-79: no blank after it
* 0 (W) 1 + b! !b
* BODY: (W) b! !b dup push -if A inv 1 + A: 0 <# #S pop SIGN #> baddr u
* TAIL: (W) b! @b -if POS drop jump OUT width < 0
* POS: over - SPACES
* OUT: TYPE jump SPACE
* OUT: TYPE (W) 1 + b! @b if DONE drop jump SPACE
* DONE: drop ;
* The width waits in the variable (W), on neither stack, so the picture
* runs as deep as it does on its own. A negative width is dropped before
* the subtraction, which could otherwise wrap. U.R and D.R jump to TAIL,
* and SPACE returns to the caller. */
* the subtraction, which could otherwise wrap. (W)+1 says whether a blank
* follows the number: `.` sets it and enters at BODY with a width of 0;
* `.R` clears it. U.R and D.R jump to TAIL. */
w_dotr = v4_asm_label(&as);
LIT(0); VSET(TRAIL);
l_rbody = v4_asm_label(&as);
VSET(FIELD); O(DUP); O(PUSH);
a = FWD(MINUS_IF); O(INV); LIT(1); O(ADD); HERE_(a);
LIT(0); CALL(w_begin); CALL(w_hashs); O(RPOP); CALL(w_sign); CALL(w_end);
@@ -400,30 +406,37 @@ static void build_output(void)
O(OVER); SUB_INLINE(); CALL(w_spaces);
HERE_(b);
l_out = v4_asm_label(&as);
CALL(w_type); JUMP(w_space);
CALL(w_type); VGET(TRAIL); a = FWD(IF);
O(DROP); JUMP(w_space);
HERE_(a);
O(DROP); O(SEMI);
/* : . ( n -- ) 0 jump .R */
/* : . ( n -- ) -1 (W) 1 + b! !b 0 jump BODY a blank after it */
w_dot = v4_asm_label(&as);
LIT(0); JUMP(w_dotr);
LIT(-1); VSET(TRAIL); LIT(0); JUMP(l_rbody);
/* : U.R ( u width -- ) (W) b! !b 0 <# #S #> jump TAIL */
/* : U.R ( u width -- ) 0 (W) 1 + b! !b UBODY: (W) b! !b 0 <# #S #> jump TAIL */
w_udotr = v4_asm_label(&as);
LIT(0); VSET(TRAIL);
l_ubody = v4_asm_label(&as);
VSET(FIELD); LIT(0); CALL(w_begin); CALL(w_hashs); CALL(w_end); JUMP(l_tail);
/* : U. ( u -- ) 0 jump U.R */
/* : U. ( u -- ) -1 (W) 1 + b! !b 0 jump UBODY */
w_udot = v4_asm_label(&as);
LIT(0); JUMP(w_udotr);
LIT(-1); VSET(TRAIL); LIT(0); JUMP(l_ubody);
/* : D.R ( d width -- )
* (W) b! !b dup push -if A DNEGATE A: <# #S pop SIGN #> jump TAIL */
* 0 (W) 1 + b! !b DBODY: (W) b! !b dup push -if A DNEGATE A: <# #S pop SIGN #> jump TAIL */
w_ddotr = v4_asm_label(&as);
LIT(0); VSET(TRAIL);
l_dbody = v4_asm_label(&as);
VSET(FIELD); O(DUP); O(PUSH);
a = FWD(MINUS_IF); CALL(w_dnegate); HERE_(a);
CALL(w_begin); CALL(w_hashs); O(RPOP); CALL(w_sign); CALL(w_end); JUMP(l_tail);
/* : D. ( d -- ) 0 jump D.R */
/* : D. ( d -- ) -1 (W) 1 + b! !b 0 jump DBODY */
w_ddot = v4_asm_label(&as);
LIT(0); JUMP(w_ddotr);
LIT(-1); VSET(TRAIL); LIT(0); JUMP(l_dbody);
/* ---- the words under test ---- */
/* : CR ( -- ) 10 jump EMIT */
@@ -445,7 +458,7 @@ static void build_output(void)
* pop 15 FOR 2/ UNEXT HIMASK and high cell of |q| >> 16
* #S (QP) 3 + b! @b SIGN #>
* (QP) b! @b BASE b! !b
* jump OUT TYPE jump SPACE, in .R
* -1 (W) 1 + b! !b jump OUT TYPE and a blank, in .R
* frac * 100000 / 65536 is frac * 3125 / 2048, which stays inside a
* 32-bit cell; times 3125 is times 5 five times. HIMASK clears the top
* 16 bits, making the 2/ shift a logical one. */
@@ -476,6 +489,7 @@ static void build_output(void)
LIT((v4_cell)(MAXU >> 16)); O(AND);
CALL(w_hashs); VGET(QP + 3); CALL(w_sign); CALL(w_end);
VGET(QP); VSET(BASE);
LIT(-1); VSET(TRAIL);
JUMP(l_out);
/* : (DB) ( -- c ) (DP) 1 + b! @b (DP) 3 + b! @b + jump C@
@@ -793,7 +807,7 @@ int main(void)
build_pictured();
build_output();
CHECK(v4_asm_ok(&as), "printing words assemble");
CHECK(v4_asm_label(&as) < XVAR, "code stays below the variables");
CHECK(v4_asm_label(&as) < TRAIL, "code stays below the variables");
printf(" code: %ld of %u words\n", (long)v4_asm_label(&as) - CODE0, (unsigned)V4_NODE_WORDS);
/* 128 bytes to dump: every kind of byte, with v3's twenty at 0xD0. */