feat(v4.0.0): . U. D. .R U.R D.R and SPACES
The number-printing words, on pictured output and TYPE, executed on the golden model at both cell widths against a C reference (ten bases, eleven field widths) and six recorded transcripts of the v3 binary. Each plain word is its .R word with a width of 0; all six share one tail. The field width waits in a variable, (W), so the picture runs no deeper than it does on its own: each word leaves its caller 4 data cells and 2 return entries. As v3, the .R words print a trailing space. Unlike v3, D. takes ( lo hi ), prints the whole double, and printing honours BASE. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
40178098e9
commit
13335dcc9c
@@ -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
|
||||
|
||||
@@ -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 '<script>\n' | build/amd64/standard/starforth -s
|
||||
* as the text between "System initialization complete." and "Goodbye!".
|
||||
*/
|
||||
#include "v4/asm.h"
|
||||
#include "v4/testcode.h"
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
static int failures = 0, checks = 0;
|
||||
#define CHECK(c,...) do{checks++; if(!(c)){failures++; printf("FAIL %s:%d: ",__FILE__,__LINE__); printf(__VA_ARGS__); printf("\n");}}while(0)
|
||||
|
||||
#define CANARY ((v4_cell)0x0C0FFEE5)
|
||||
#define MAXU ((v4_ucell)~(v4_ucell)0)
|
||||
|
||||
/* The memory map is open (D-4); the test puts the variables at the top of
|
||||
* the node, where test_pictured.c and test_terminal.c put them. */
|
||||
#define NODE_ERROR ((v4_cell)(V4_NODE_WORDS - 2u))
|
||||
#define CONSOLE_TX ((v4_cell)(V4_NODE_WORDS - 4u))
|
||||
#define HBUF ((v4_cell)(V4_NODE_WORDS - 32u)) /* 16 cells = 64 bytes */
|
||||
#define HEND ((v4_cell)(HBUF * 4 + 64)) /* byte address past the buffer */
|
||||
#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 HCAP 63
|
||||
|
||||
static v4_node n;
|
||||
static v4_exec_state es;
|
||||
static v4_heat h;
|
||||
static v4_asm as;
|
||||
|
||||
static v4_cell w_swap, w_or, w_ummod, w_lshift, w_rshift, w_cstore, w_cfetch,
|
||||
w_dnegate, w_base, w_begin, w_hold, w_sign, w_hash, w_hashs, w_end,
|
||||
w_emit, w_space, w_type,
|
||||
w_spaces, w_dot, w_dotr, w_udot, w_udotr, w_ddot, w_ddotr,
|
||||
t_v3a, t_v3b, t_v3c, t_v3d, t_v3e, t_v3f;
|
||||
|
||||
#define O(name) v4_asm_op(&as, V4_OP_##name)
|
||||
#define LIT(v) v4_asm_lit(&as, (v4_cell)(v))
|
||||
#define CALL(w) v4_asm_branch(&as, V4_OP_CALL, (w))
|
||||
#define JUMP(w) v4_asm_branch(&as, V4_OP_JUMP, (w))
|
||||
#define FWD(op) v4_asm_branch_fwd(&as, V4_OP_##op)
|
||||
#define HERE_(r) v4_asm_resolve(&as, (r), v4_asm_label(&as))
|
||||
#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 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 these rest on, as in test_foundation.c, test_pictured.c and
|
||||
* test_terminal.c, where each is tested on its own. */
|
||||
static void build_dependencies(void)
|
||||
{
|
||||
/* : SWAP over push push drop pop pop ; */
|
||||
w_swap = v4_asm_label(&as);
|
||||
O(OVER); O(PUSH); O(PUSH); O(DROP); O(RPOP); O(RPOP); O(SEMI);
|
||||
|
||||
/* : OR over inv and xor ; */
|
||||
w_or = v4_asm_label(&as);
|
||||
O(OVER); O(INV); O(AND); O(XOR); O(SEMI);
|
||||
|
||||
/* : UM/MOD ( ulo uhi ud -- urem uquot ) section 4, call-free */
|
||||
{
|
||||
v4_cell loop, l_sub, l_setbit, l_nosub;
|
||||
v4_asm_ref r0, r1, r2, r3, r4, r5, r6, to_sub1, to_sub2, to_nosub1,
|
||||
to_nosub2, to_setbit;
|
||||
|
||||
w_ummod = v4_asm_label(&as);
|
||||
O(BANG_A); LIT(V4_CELL_BITS - 1); O(PUSH);
|
||||
loop = v4_asm_label(&as);
|
||||
r0 = FWD(MINUS_IF);
|
||||
O(TWO_STAR); O(OVER);
|
||||
r1 = FWD(MINUS_IF);
|
||||
O(DROP); LIT(1); O(ADD);
|
||||
r2 = FWD(JUMP);
|
||||
HERE_(r1);
|
||||
O(DROP);
|
||||
HERE_(r2);
|
||||
O(PUSH); O(TWO_STAR); O(RPOP);
|
||||
to_sub1 = FWD(JUMP);
|
||||
HERE_(r0);
|
||||
O(TWO_STAR); O(OVER);
|
||||
r3 = FWD(MINUS_IF);
|
||||
O(DROP); LIT(1); O(ADD);
|
||||
r4 = FWD(JUMP);
|
||||
HERE_(r3);
|
||||
O(DROP);
|
||||
HERE_(r4);
|
||||
O(PUSH); O(TWO_STAR); O(RPOP);
|
||||
O(DUP); O(PUSH_A); O(XOR);
|
||||
r5 = FWD(MINUS_IF);
|
||||
O(DROP);
|
||||
to_nosub1 = FWD(MINUS_IF);
|
||||
to_sub2 = FWD(JUMP);
|
||||
HERE_(r5);
|
||||
O(DROP); O(DUP); O(INV); O(PUSH_A); O(ADD); O(INV);
|
||||
r6 = FWD(MINUS_IF);
|
||||
O(DROP);
|
||||
to_nosub2 = FWD(JUMP);
|
||||
HERE_(r6);
|
||||
O(PUSH); O(DROP); O(RPOP);
|
||||
to_setbit = FWD(JUMP);
|
||||
l_sub = v4_asm_label(&as);
|
||||
O(INV); O(PUSH_A); O(ADD); O(INV);
|
||||
l_setbit = v4_asm_label(&as);
|
||||
O(PUSH); LIT(1); O(ADD); O(RPOP);
|
||||
l_nosub = v4_asm_label(&as);
|
||||
v4_asm_branch(&as, V4_OP_NEXT, loop);
|
||||
O(OVER); O(PUSH); O(PUSH); O(DROP); O(RPOP); O(RPOP); O(SEMI);
|
||||
|
||||
v4_asm_resolve(&as, to_sub1, l_sub);
|
||||
v4_asm_resolve(&as, to_sub2, l_sub);
|
||||
v4_asm_resolve(&as, to_setbit, l_setbit);
|
||||
v4_asm_resolve(&as, to_nosub1, l_nosub);
|
||||
v4_asm_resolve(&as, to_nosub2, l_nosub);
|
||||
}
|
||||
|
||||
/* : LSHIFT BEGIN dup WHILE 1- SWAP 2* SWAP REPEAT drop ;
|
||||
* : RSHIFT BEGIN dup WHILE 1- SWAP 2/ MSB inv and SWAP REPEAT drop ; */
|
||||
{
|
||||
v4_cell l0;
|
||||
v4_asm_ref l1;
|
||||
w_lshift = v4_asm_label(&as);
|
||||
l0 = v4_asm_label(&as);
|
||||
O(DUP); l1 = FWD(IF);
|
||||
O(DROP); LIT(-1); O(ADD); CALL(w_swap); O(TWO_STAR); CALL(w_swap);
|
||||
v4_asm_branch(&as, V4_OP_JUMP, l0);
|
||||
HERE_(l1);
|
||||
O(DROP); O(DROP); O(SEMI);
|
||||
|
||||
w_rshift = v4_asm_label(&as);
|
||||
l0 = v4_asm_label(&as);
|
||||
O(DUP); l1 = FWD(IF);
|
||||
O(DROP); LIT(-1); O(ADD); CALL(w_swap);
|
||||
O(TWO_SLASH); LIT((v4_cell)V4_MSB); O(INV); O(AND); CALL(w_swap);
|
||||
v4_asm_branch(&as, V4_OP_JUMP, l0);
|
||||
HERE_(l1);
|
||||
O(DROP); O(DROP); O(SEMI);
|
||||
}
|
||||
|
||||
/* C!, call-free, as in test_foundation.c.
|
||||
* : C! ( c baddr -- ) call-free
|
||||
* dup 2/ 2/ a! 3 and push 255 and pop c' k A: word address
|
||||
* if K0 -1 + if K1 -1 + if K2
|
||||
* drop 23 FOR 2* UNEXT @ 4278190080 inv and + ! ; byte 3
|
||||
* K2: drop 15 FOR 2* UNEXT @ -16711681 and + ! ; byte 2
|
||||
* K1: drop 7 FOR 2* UNEXT @ -65281 and + ! ; byte 1
|
||||
* K0: drop @ -256 and + ! ; byte 0
|
||||
* One case per byte position: the byte is shifted up, that byte of the
|
||||
* cell cleared with a constant mask, and the two added. */
|
||||
{
|
||||
v4_asm_ref k0, k1, k2;
|
||||
w_cstore = v4_asm_label(&as);
|
||||
O(DUP); O(TWO_SLASH); O(TWO_SLASH); O(BANG_A);
|
||||
LIT(3); O(AND); O(PUSH); LIT(255); O(AND); O(RPOP);
|
||||
k0 = v4_asm_branch_fwd(&as, V4_OP_IF);
|
||||
LIT(-1); O(ADD); k1 = v4_asm_branch_fwd(&as, V4_OP_IF);
|
||||
LIT(-1); O(ADD); k2 = v4_asm_branch_fwd(&as, V4_OP_IF);
|
||||
O(DROP); LIT(23); O(PUSH);
|
||||
(void)v4_asm_label(&as);
|
||||
O(TWO_STAR); O(UNEXT);
|
||||
O(FETCH_A); LIT((v4_cell)(v4_ucell)0xFF000000u); O(INV); O(AND); O(ADD); O(STORE_A); O(SEMI);
|
||||
v4_asm_resolve(&as, k2, v4_asm_label(&as));
|
||||
O(DROP); LIT(15); O(PUSH);
|
||||
(void)v4_asm_label(&as);
|
||||
O(TWO_STAR); O(UNEXT);
|
||||
O(FETCH_A); LIT(-16711681); O(AND); O(ADD); O(STORE_A); O(SEMI);
|
||||
v4_asm_resolve(&as, k1, v4_asm_label(&as));
|
||||
O(DROP); LIT(7); O(PUSH);
|
||||
(void)v4_asm_label(&as);
|
||||
O(TWO_STAR); O(UNEXT);
|
||||
O(FETCH_A); LIT(-65281); O(AND); O(ADD); O(STORE_A); O(SEMI);
|
||||
v4_asm_resolve(&as, k0, v4_asm_label(&as));
|
||||
O(DROP); O(FETCH_A); LIT(-256); O(AND); O(ADD); O(STORE_A); O(SEMI);
|
||||
}
|
||||
}
|
||||
|
||||
/* Pictured output, 5.8. */
|
||||
static void build_pictured(void)
|
||||
{
|
||||
v4_asm_ref a, b, c;
|
||||
v4_cell l;
|
||||
|
||||
/* : (BASE) ( -- b ) BASE, or 10 when it is outside 2..36
|
||||
* BASE b! @b dup -2 + -if L1 drop drop 10 ;
|
||||
* L1: drop dup -37 + -if L2 drop ;
|
||||
* L2: drop drop 10 ; */
|
||||
w_base = v4_asm_label(&as);
|
||||
VGET(BASE); O(DUP); LIT(-2); O(ADD); a = FWD(MINUS_IF);
|
||||
O(DROP); O(DROP); LIT(10); O(SEMI);
|
||||
HERE_(a);
|
||||
O(DROP); O(DUP); LIT(-37); O(ADD); b = FWD(MINUS_IF);
|
||||
O(DROP); O(SEMI);
|
||||
HERE_(b);
|
||||
O(DROP); O(DROP); LIT(10); O(SEMI);
|
||||
|
||||
/* : <# ( -- ) HEND HLD b! !b ; */
|
||||
w_begin = v4_asm_label(&as);
|
||||
LIT(HEND); VSET(HLD); O(SEMI);
|
||||
|
||||
/* : HOLD ( c -- ) D-13
|
||||
* dup -256 and if OKC drop jump ERR c outside 0..255
|
||||
* OKC: drop HLD b! @b -(HEND-62) + -if ROOM drop 63 held already
|
||||
* ERR: drop NODE-ERROR b! -1 !b ;
|
||||
* ROOM: drop HLD b! @b -1 + dup !b jump C!
|
||||
* The last step is a jump, not a call: C! returns to HOLD's caller. */
|
||||
w_hold = v4_asm_label(&as);
|
||||
O(DUP); LIT(-256); O(AND); a = FWD(IF);
|
||||
O(DROP); b = FWD(JUMP);
|
||||
HERE_(a);
|
||||
O(DROP); VGET(HLD); LIT(-(HEND - 62)); O(ADD); c = FWD(MINUS_IF);
|
||||
O(DROP);
|
||||
HERE_(b);
|
||||
O(DROP); LIT(NODE_ERROR); O(BANG_B); LIT(-1); O(STORE_B); O(SEMI);
|
||||
HERE_(c);
|
||||
O(DROP); VGET(HLD); LIT(-1); O(ADD); O(DUP); O(STORE_B);
|
||||
v4_asm_branch(&as, V4_OP_JUMP, w_cstore);
|
||||
|
||||
/* : SIGN ( n -- ) -if L1 drop 45 jump HOLD L1: drop ; */
|
||||
w_sign = v4_asm_label(&as);
|
||||
a = FWD(MINUS_IF);
|
||||
O(DROP); LIT(45); v4_asm_branch(&as, V4_OP_JUMP, w_hold);
|
||||
HERE_(a);
|
||||
O(DROP); O(SEMI);
|
||||
|
||||
/* : # ( 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 -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); 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);
|
||||
O(DROP); LIT(55); O(ADD);
|
||||
HERE_(b);
|
||||
v4_asm_branch(&as, V4_OP_JUMP, w_hold);
|
||||
|
||||
/* : #S ( ud -- 0 0 ) L: # over over OR if L1 drop jump L L1: drop ; */
|
||||
w_hashs = v4_asm_label(&as);
|
||||
l = v4_asm_label(&as);
|
||||
CALL(w_hash); O(OVER); O(OVER); CALL(w_or); a = FWD(IF);
|
||||
O(DROP); v4_asm_branch(&as, V4_OP_JUMP, l);
|
||||
HERE_(a);
|
||||
O(DROP); O(SEMI);
|
||||
|
||||
/* : #> ( ud -- baddr u ) drop drop HLD b! @b HEND over push inv pop + inv ; */
|
||||
w_end = v4_asm_label(&as);
|
||||
O(DROP); O(DROP); VGET(HLD); LIT(HEND); O(OVER); SUB_INLINE(); O(SEMI);
|
||||
|
||||
}
|
||||
|
||||
/* Number output, 5.8 and 5.10. */
|
||||
static void build_output(void)
|
||||
{
|
||||
v4_asm_ref a, b;
|
||||
v4_cell l, l_tail;
|
||||
|
||||
/* : C@ ( baddr -- c ) as written in 5.3
|
||||
* dup 3 and 3 LSHIFT SWAP 2 RSHIFT a! @ SWAP RSHIFT 255 and ; */
|
||||
w_cfetch = v4_asm_label(&as);
|
||||
O(DUP); LIT(3); O(AND); LIT(3); CALL(w_lshift);
|
||||
CALL(w_swap); LIT(2); CALL(w_rshift); O(BANG_A); O(FETCH_A);
|
||||
CALL(w_swap); CALL(w_rshift); LIT(255); O(AND); O(SEMI);
|
||||
|
||||
/* : DNEGATE ( d -- -d ) inv over if L1 drop push inv 1 + pop ; L1: drop 1 + ; */
|
||||
w_dnegate = v4_asm_label(&as);
|
||||
O(INV); O(OVER); a = FWD(IF);
|
||||
O(DROP); O(PUSH); O(INV); LIT(1); O(ADD); O(RPOP); O(SEMI);
|
||||
HERE_(a);
|
||||
O(DROP); LIT(1); O(ADD); O(SEMI);
|
||||
|
||||
/* : EMIT ( c -- ) CONSOLE-TX b! !b ; : SPACE ( -- ) 32 jump EMIT */
|
||||
w_emit = v4_asm_label(&as);
|
||||
LIT(CONSOLE_TX); O(BANG_B); O(STORE_B); O(SEMI);
|
||||
w_space = v4_asm_label(&as);
|
||||
LIT(32); JUMP(w_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 ; */
|
||||
w_type = v4_asm_label(&as);
|
||||
a = FWD(MINUS_IF);
|
||||
O(DROP); O(DROP); LIT(NODE_ERROR); O(BANG_B); LIT(-1); O(STORE_B); O(SEMI);
|
||||
HERE_(a);
|
||||
l = v4_asm_label(&as);
|
||||
b = FWD(IF);
|
||||
O(OVER); CALL(w_cfetch); CALL(w_emit);
|
||||
O(PUSH); LIT(1); O(ADD); O(RPOP); LIT(-1); O(ADD);
|
||||
JUMP(l);
|
||||
HERE_(b);
|
||||
O(DROP); O(DROP); O(SEMI);
|
||||
|
||||
/* ---- the words under test ---- */
|
||||
|
||||
/* : SPACES ( n -- )
|
||||
* -if L drop ; n < 0
|
||||
* L: if DONE SPACE -1 + jump L
|
||||
* DONE: drop ; */
|
||||
w_spaces = v4_asm_label(&as);
|
||||
a = FWD(MINUS_IF);
|
||||
O(DROP); O(SEMI);
|
||||
HERE_(a);
|
||||
l = v4_asm_label(&as);
|
||||
b = FWD(IF);
|
||||
CALL(w_space); LIT(-1); O(ADD); JUMP(l);
|
||||
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
|
||||
* TAIL: (W) b! @b -if POS drop jump OUT width < 0
|
||||
* POS: over - SPACES
|
||||
* OUT: TYPE jump SPACE
|
||||
* 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. */
|
||||
w_dotr = 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);
|
||||
l_tail = v4_asm_label(&as);
|
||||
VGET(FIELD); a = FWD(MINUS_IF);
|
||||
O(DROP); b = FWD(JUMP);
|
||||
HERE_(a);
|
||||
O(OVER); SUB_INLINE(); CALL(w_spaces);
|
||||
HERE_(b);
|
||||
CALL(w_type); JUMP(w_space);
|
||||
|
||||
/* : . ( n -- ) 0 jump .R */
|
||||
w_dot = v4_asm_label(&as);
|
||||
LIT(0); JUMP(w_dotr);
|
||||
|
||||
/* : U.R ( u width -- ) (W) b! !b 0 <# #S #> jump TAIL */
|
||||
w_udotr = 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 */
|
||||
w_udot = v4_asm_label(&as);
|
||||
LIT(0); JUMP(w_udotr);
|
||||
|
||||
/* : D.R ( d width -- )
|
||||
* (W) b! !b dup push -if A DNEGATE A: <# #S pop SIGN #> jump TAIL */
|
||||
w_ddotr = 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 */
|
||||
w_ddot = v4_asm_label(&as);
|
||||
LIT(0); JUMP(w_ddotr);
|
||||
|
||||
/* ---- the v3 transcripts ---- */
|
||||
#define DOT(v) do { LIT(v); CALL(w_dot); } while (0)
|
||||
#define UDOT(v) do { LIT(v); CALL(w_udot); } while (0)
|
||||
#define DOTR(v,w) do { LIT(v); LIT(w); CALL(w_dotr); } while (0)
|
||||
#define EM(c) do { LIT(c); CALL(w_emit); } while (0)
|
||||
|
||||
/* 5 . -5 . 0 . 123456789 . 91 EMIT 42 U. 0 U. 93 EMIT */
|
||||
t_v3a = v4_asm_label(&as);
|
||||
DOT(5); DOT(-5); DOT(0); DOT(123456789); EM(91); UDOT(42); UDOT(0); EM(93); O(SEMI);
|
||||
|
||||
/* 91 EMIT 42 5 .R 93 EMIT -42 5 .R 93 EMIT 123456 3 .R 93 EMIT 7 0 .R 93 EMIT
|
||||
* 7 -4 .R 93 EMIT 42 6 U.R 93 EMIT */
|
||||
t_v3b = v4_asm_label(&as);
|
||||
EM(91); DOTR(42, 5); EM(93); DOTR(-42, 5); EM(93); DOTR(123456, 3); EM(93);
|
||||
DOTR(7, 0); EM(93); DOTR(7, -4); EM(93);
|
||||
LIT(42); LIT(6); CALL(w_udotr); EM(93); O(SEMI);
|
||||
|
||||
/* HEX FF . -FF . BEEF U. OCTAL 100 . -17 . DECIMAL 91 EMIT */
|
||||
t_v3c = v4_asm_label(&as);
|
||||
LIT(16); VSET(BASE); DOT(255); DOT(-255); UDOT(48879);
|
||||
LIT(8); VSET(BASE); DOT(64); DOT(-15);
|
||||
LIT(10); VSET(BASE); EM(91); O(SEMI);
|
||||
|
||||
/* v3, low cell on top:
|
||||
* 91 EMIT 0 5 D. -1 -5 D. 0 0 D. 0 77 6 D.R 93 EMIT -1 -77 6 D.R 93 EMIT
|
||||
* the same doubles here, high cell on top. */
|
||||
t_v3d = v4_asm_label(&as);
|
||||
EM(91); LIT(5); LIT(0); CALL(w_ddot); LIT(-5); LIT(-1); CALL(w_ddot);
|
||||
LIT(0); LIT(0); CALL(w_ddot);
|
||||
LIT(77); LIT(0); LIT(6); CALL(w_ddotr); EM(93);
|
||||
LIT(-77); LIT(-1); LIT(6); CALL(w_ddotr); EM(93); O(SEMI);
|
||||
|
||||
/* 91 EMIT 3 SPACES 93 EMIT 0 SPACES 93 EMIT -2 SPACES 93 EMIT 1 SPACES 93 EMIT */
|
||||
t_v3e = v4_asm_label(&as);
|
||||
EM(91); LIT(3); CALL(w_spaces); EM(93); LIT(0); CALL(w_spaces); EM(93);
|
||||
LIT(-2); CALL(w_spaces); EM(93); LIT(1); CALL(w_spaces); EM(93); O(SEMI);
|
||||
|
||||
/* -1 U. -9223372036854775808 . 9223372036854775807 . 91 EMIT
|
||||
* v3's cell is 64 bits, so this one is compared at 64-bit cells only. */
|
||||
t_v3f = v4_asm_label(&as);
|
||||
UDOT(-1); DOT((v4_cell)V4_MSB); DOT((v4_cell)(V4_MSB - 1u)); EM(91); O(SEMI);
|
||||
}
|
||||
|
||||
/* ---- running ---------------------------------------------------------- */
|
||||
|
||||
static void prepare(v4_cell base)
|
||||
{
|
||||
v4_dstack_reset(&n.ds);
|
||||
v4_rstack_reset(&n.rs);
|
||||
v4_exec_reset(&es);
|
||||
v4_heat_reset(&h);
|
||||
v4_node_console_attach(&n, CONSOLE_TX);
|
||||
v4_node_store(&n, NODE_ERROR, 0);
|
||||
v4_node_store(&n, BASE, base);
|
||||
}
|
||||
|
||||
/* Runs `word` on argc arguments; true when it returns with the canary on
|
||||
* top, that is, having consumed exactly its arguments. */
|
||||
static int call(v4_cell word, v4_cell base, unsigned argc, v4_cell a, v4_cell b, v4_cell c)
|
||||
{
|
||||
prepare(base);
|
||||
v4_dstack_push(&n.ds, CANARY);
|
||||
if (argc > 0) v4_dstack_push(&n.ds, a);
|
||||
if (argc > 1) v4_dstack_push(&n.ds, b);
|
||||
if (argc > 2) v4_dstack_push(&n.ds, c);
|
||||
return v4_test_call(&n, &es, &h, word, 4000000) > 0 && n.ds.t == CANARY;
|
||||
}
|
||||
|
||||
static int printed(const char *want)
|
||||
{
|
||||
size_t len = strlen(want);
|
||||
return n.console_len == len && n.console_dropped == 0
|
||||
&& (len == 0 || memcmp(n.console, want, len) == 0);
|
||||
}
|
||||
|
||||
/* Stack headroom, as in test_foundation.c. */
|
||||
static int fits(v4_cell word, unsigned argc, v4_cell a, v4_cell b, v4_cell c,
|
||||
const char *want, unsigned dfill, unsigned rfill)
|
||||
{
|
||||
unsigned i;
|
||||
prepare(10);
|
||||
for (i = 0; i < dfill; i++) v4_dstack_push(&n.ds, (v4_cell)(0x5A000000 + i));
|
||||
v4_dstack_push(&n.ds, CANARY);
|
||||
if (argc > 0) v4_dstack_push(&n.ds, a);
|
||||
if (argc > 1) v4_dstack_push(&n.ds, b);
|
||||
if (argc > 2) v4_dstack_push(&n.ds, c);
|
||||
for (i = 0; i < rfill; i++) v4_rstack_push(&n.rs, (v4_cell)(0x6B000000 + i));
|
||||
if (v4_test_call(&n, &es, &h, word, 4000000) <= 0) return 0;
|
||||
if (!printed(want)) return 0;
|
||||
if (v4_dstack_pop(&n.ds) != CANARY) return 0;
|
||||
for (i = dfill; i-- > 0; ) if (v4_dstack_pop(&n.ds) != (v4_cell)(0x5A000000 + i)) return 0;
|
||||
for (i = rfill; i-- > 0; ) if (v4_rstack_pop(&n.rs) != (v4_cell)(0x6B000000 + i)) return 0;
|
||||
return 1;
|
||||
}
|
||||
static void headroom(const char *name, v4_cell word, unsigned argc, v4_cell a, v4_cell b, v4_cell c,
|
||||
const char *want, int *dh, int *rh)
|
||||
{
|
||||
int d, r;
|
||||
for (d = 0; d < V4_DATA_DEPTH; d++) if (!fits(word, argc, a, b, c, want, (unsigned)d + 1u, 0)) break;
|
||||
for (r = 0; r < V4_RET_DEPTH; r++) if (!fits(word, argc, a, b, c, want, 0, (unsigned)r + 1u)) break;
|
||||
*dh = d; *rh = r;
|
||||
printf(" %s headroom: data %d below canary, return %d below its return address\n", name, d, r);
|
||||
}
|
||||
|
||||
/* ---- the C reference ---------------------------------------------------- */
|
||||
|
||||
#define LIMBS (2 * V4_CELL_BITS / 16)
|
||||
#define BUFSZ (4 * V4_CELL_BITS + 128)
|
||||
|
||||
/* The digits of the unsigned double hi:lo in `base` (2..36), most
|
||||
* significant first, "0" for zero, by short division on 16-bit limbs. */
|
||||
static void ref_digits(v4_ucell lo, v4_ucell hi, unsigned base, char *out)
|
||||
{
|
||||
uint32_t l[LIMBS];
|
||||
char tmp[2 * V4_CELL_BITS + 1];
|
||||
unsigned i, len = 0;
|
||||
for (i = 0; i < LIMBS / 2; i++) {
|
||||
l[i] = (uint32_t)((lo >> (16 * i)) & 0xFFFFu);
|
||||
l[i + LIMBS / 2] = (uint32_t)((hi >> (16 * i)) & 0xFFFFu);
|
||||
}
|
||||
for (;;) {
|
||||
uint32_t rem = 0;
|
||||
int nonzero = 0;
|
||||
for (i = LIMBS; i-- > 0; ) {
|
||||
uint32_t v = (rem << 16) | l[i];
|
||||
l[i] = v / base;
|
||||
rem = v % base;
|
||||
if (l[i]) nonzero = 1;
|
||||
}
|
||||
tmp[len++] = (char)(rem < 10 ? '0' + rem : 'A' + (rem - 10));
|
||||
if (!nonzero) break;
|
||||
}
|
||||
for (i = 0; i < len; i++) out[i] = tmp[len - 1 - i];
|
||||
out[len] = 0;
|
||||
}
|
||||
|
||||
/* What is printed for the number text `num` in a field of `width`: the last
|
||||
* HCAP characters of it (D-13), padded on the left, then one space. Returns
|
||||
* whether the hold buffer overflowed. */
|
||||
static int ref_line(const char *num, v4_cell width, char *out)
|
||||
{
|
||||
size_t len = strlen(num), pad, i;
|
||||
int over = len > HCAP;
|
||||
if (over) { num += len - HCAP; len = HCAP; }
|
||||
pad = (width > 0 && (v4_ucell)width > len) ? (size_t)width - len : 0;
|
||||
for (i = 0; i < pad; i++) out[i] = ' ';
|
||||
memcpy(out + pad, num, len);
|
||||
out[pad + len] = ' ';
|
||||
out[pad + len + 1] = 0;
|
||||
return over;
|
||||
}
|
||||
|
||||
/* The text of a signed double, and of a signed or unsigned single. */
|
||||
static void ref_signed(v4_ucell lo, v4_ucell hi, unsigned base, char *out)
|
||||
{
|
||||
if (hi & V4_MSB) {
|
||||
lo = (v4_ucell)0 - lo;
|
||||
hi = ~hi + (lo == 0 ? 1u : 0u);
|
||||
*out++ = '-';
|
||||
}
|
||||
ref_digits(lo, hi, base, out);
|
||||
}
|
||||
|
||||
static unsigned eff_base(v4_cell b) { return (b < 2 || b > 36) ? 10u : (unsigned)b; }
|
||||
|
||||
static const v4_cell vec[] = {
|
||||
0, 1, 2, 9, 10, 35, 36, 255, 12345, -1, -2, -12345,
|
||||
(v4_cell)(V4_MSB - 1u), (v4_cell)V4_MSB, (v4_cell)(MAXU / 3u)
|
||||
};
|
||||
#define NVEC (sizeof vec / sizeof vec[0])
|
||||
|
||||
static const v4_cell bases[] = { 10, 16, 2, 8, 36, 3, 0, 1, 37, -5 };
|
||||
#define NBASE (sizeof bases / sizeof bases[0])
|
||||
|
||||
static const v4_cell widths[] = { 0, 1, 2, 5, 12, 40, 70, 100, -1, -100, (v4_cell)V4_MSB };
|
||||
#define NWIDTH (sizeof widths / sizeof widths[0])
|
||||
|
||||
int main(void)
|
||||
{
|
||||
char num[BUFSZ], want[BUFSZ];
|
||||
unsigned bi, i, j, wi;
|
||||
|
||||
printf("v4 number-output tests: V4_CELL_BITS=%d\n", V4_CELL_BITS);
|
||||
|
||||
v4_node_reset(&n);
|
||||
v4_asm_begin(&as, &n, 16);
|
||||
build_dependencies();
|
||||
build_pictured();
|
||||
build_output();
|
||||
CHECK(v4_asm_ok(&as), "number-output words assemble");
|
||||
CHECK(v4_asm_label(&as) < FIELD, "code stays below the variables");
|
||||
printf(" code: %ld of %u words\n", (long)v4_asm_label(&as), (unsigned)V4_NODE_WORDS);
|
||||
|
||||
/* The v3 transcripts. */
|
||||
CHECK(call(t_v3a, 10, 0, 0, 0, 0) && printed("5 -5 0 123456789 [42 0 ]"), "v3: . and U.");
|
||||
CHECK(call(t_v3b, 10, 0, 0, 0, 0) && printed("[ 42 ] -42 ]123456 ]7 ]7 ] 42 ]"),
|
||||
"v3: .R and U.R");
|
||||
CHECK(call(t_v3c, 10, 0, 0, 0, 0) && printed("FF -FF BEEF 100 -17 ["), "v3: HEX and OCTAL");
|
||||
CHECK(call(t_v3d, 10, 0, 0, 0, 0) && printed("[5 -5 0 77 ] -77 ]"), "v3: D. and D.R");
|
||||
CHECK(call(t_v3e, 10, 0, 0, 0, 0) && printed("[ ]]] ]"), "v3: SPACES");
|
||||
#if V4_CELL_BITS == 64
|
||||
CHECK(call(t_v3f, 10, 0, 0, 0, 0)
|
||||
&& printed("18446744073709551615 -9223372036854775808 9223372036854775807 ["),
|
||||
"v3: the extreme cells");
|
||||
#else
|
||||
CHECK(call(t_v3f, 10, 0, 0, 0, 0) && printed("4294967295 -2147483648 2147483647 ["),
|
||||
"the extreme cells at 32 bits");
|
||||
#endif
|
||||
CHECK(v4_node_load(&n, NODE_ERROR) == 0, "no error from the transcripts");
|
||||
|
||||
for (bi = 0; bi < NBASE; bi++) {
|
||||
unsigned base = eff_base(bases[bi]);
|
||||
|
||||
for (i = 0; i < NVEC; i++) {
|
||||
v4_cell v = vec[i];
|
||||
int over;
|
||||
|
||||
/* . and .R */
|
||||
ref_signed((v4_ucell)v, v < 0 ? MAXU : 0, base, num);
|
||||
over = ref_line(num, 0, want);
|
||||
CHECK(call(w_dot, bases[bi], 1, v, 0, 0) && printed(want),
|
||||
". base %ld [%u]: want \"%s\"", (long)bases[bi], i, want);
|
||||
CHECK(v4_node_load(&n, NODE_ERROR) == (over ? -1 : 0), ". NODE-ERROR base %ld [%u]", (long)bases[bi], i);
|
||||
for (wi = 0; wi < NWIDTH; wi++) {
|
||||
over = ref_line(num, widths[wi], want);
|
||||
CHECK(call(w_dotr, bases[bi], 2, v, widths[wi], 0) && printed(want),
|
||||
".R base %ld [%u] width %ld: want \"%s\"", (long)bases[bi], i, (long)widths[wi], want);
|
||||
CHECK(v4_node_load(&n, NODE_ERROR) == (over ? -1 : 0), ".R NODE-ERROR");
|
||||
}
|
||||
|
||||
/* U. and U.R */
|
||||
ref_digits((v4_ucell)v, 0, base, num);
|
||||
over = ref_line(num, 0, want);
|
||||
CHECK(call(w_udot, bases[bi], 1, v, 0, 0) && printed(want),
|
||||
"U. base %ld [%u]: want \"%s\"", (long)bases[bi], i, want);
|
||||
CHECK(v4_node_load(&n, NODE_ERROR) == (over ? -1 : 0), "U. NODE-ERROR base %ld [%u]", (long)bases[bi], i);
|
||||
for (wi = 0; wi < NWIDTH; wi++) {
|
||||
over = ref_line(num, widths[wi], want);
|
||||
CHECK(call(w_udotr, bases[bi], 2, v, widths[wi], 0) && printed(want),
|
||||
"U.R base %ld [%u] width %ld: want \"%s\"", (long)bases[bi], i, (long)widths[wi], want);
|
||||
CHECK(v4_node_load(&n, NODE_ERROR) == (over ? -1 : 0), "U.R NODE-ERROR");
|
||||
}
|
||||
|
||||
/* D. and D.R */
|
||||
for (j = 0; j < NVEC; j++) {
|
||||
ref_signed((v4_ucell)vec[i], (v4_ucell)vec[j], base, num);
|
||||
over = ref_line(num, 0, want);
|
||||
CHECK(call(w_ddot, bases[bi], 2, vec[i], vec[j], 0) && printed(want),
|
||||
"D. base %ld [%u,%u]: want \"%s\"", (long)bases[bi], i, j, want);
|
||||
CHECK(v4_node_load(&n, NODE_ERROR) == (over ? -1 : 0), "D. NODE-ERROR");
|
||||
for (wi = 0; wi < NWIDTH; wi += 5) {
|
||||
over = ref_line(num, widths[wi], want);
|
||||
CHECK(call(w_ddotr, bases[bi], 3, vec[i], vec[j], widths[wi]) && printed(want),
|
||||
"D.R base %ld [%u,%u] width %ld: want \"%s\"",
|
||||
(long)bases[bi], i, j, (long)widths[wi], want);
|
||||
CHECK(v4_node_load(&n, NODE_ERROR) == (over ? -1 : 0), "D.R NODE-ERROR");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* SPACES */
|
||||
{
|
||||
static const v4_cell sp[] = { -1, -2, -1000, (v4_cell)V4_MSB, 0, 1, 2, 3, 17, 64, 200 };
|
||||
for (i = 0; i < sizeof sp / sizeof sp[0]; i++) {
|
||||
size_t k, cnt = sp[i] > 0 ? (size_t)sp[i] : 0;
|
||||
for (k = 0; k < cnt; k++) want[k] = ' ';
|
||||
want[cnt] = 0;
|
||||
CHECK(call(w_spaces, 10, 1, sp[i], 0, 0) && printed(want), "SPACES %ld", (long)sp[i]);
|
||||
CHECK(v4_node_load(&n, NODE_ERROR) == 0, "SPACES sets no error");
|
||||
}
|
||||
}
|
||||
|
||||
/* Printing changes neither BASE nor anything outside the hold buffer. */
|
||||
{
|
||||
v4_node_store(&n, HBUF - 1, (v4_cell)0x5EED5EED);
|
||||
CHECK(call(w_ddot, 2, 2, -1, -1, 0), "D. of -1 in base 2");
|
||||
CHECK(v4_node_load(&n, BASE) == 2, "BASE untouched");
|
||||
CHECK(v4_node_load(&n, HBUF - 1) == (v4_cell)0x5EED5EED, "cell below the buffer untouched");
|
||||
CHECK(n.mem[CONSOLE_TX] == 0, "the register's memory word untouched");
|
||||
}
|
||||
|
||||
/* What they leave their caller (D-2). */
|
||||
{
|
||||
int dh, rh;
|
||||
headroom("SPACES", w_spaces, 1, 5, 0, 0, " ", &dh, &rh);
|
||||
CHECK(dh >= 6 && rh >= 6, "SPACES leaves room");
|
||||
headroom(".", w_dot, 1, -12345, 0, 0, "-12345 ", &dh, &rh);
|
||||
CHECK(dh >= 2 && rh >= 2, ". leaves room");
|
||||
headroom(".R", w_dotr, 2, -12345, 9, 0, " -12345 ", &dh, &rh);
|
||||
CHECK(dh >= 2 && rh >= 2, ".R leaves room");
|
||||
headroom("U.", w_udot, 1, 12345, 0, 0, "12345 ", &dh, &rh);
|
||||
CHECK(dh >= 2 && rh >= 2, "U. leaves room");
|
||||
headroom("U.R", w_udotr, 2, 12345, 9, 0, " 12345 ", &dh, &rh);
|
||||
headroom("D.", w_ddot, 2, -12345, -1, 0, "-12345 ", &dh, &rh);
|
||||
CHECK(dh >= 2 && rh >= 2, "D. leaves room");
|
||||
headroom("D.R", w_ddotr, 3, -12345, -1, 9, " -12345 ", &dh, &rh);
|
||||
CHECK(dh >= 2 && rh >= 2, "D.R leaves room");
|
||||
}
|
||||
|
||||
CHECK(v4_node_guards_intact(&n), "guards intact");
|
||||
|
||||
printf(" %d checks, %d failures\n", checks, failures);
|
||||
return failures ? 1 : 0;
|
||||
}
|
||||
Reference in New Issue
Block a user