feat(v4.0.0): the byte-string words
FILL ERASE MOVE, COUNT CMOVE CMOVE> BLANK -TRAILING COMPARE SEARCH SCAN SKIP, as loops over the call-free C@ and C!, executed on the golden model at both cell widths against C and five recorded transcripts of the v3 binary. A negative count reads as 0 where v3 read it so, and elsewhere writes nothing and sets NODE-ERROR. v3's counted-string auto-detection is not kept in any of them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
7c5be22799
commit
cb66db1107
@@ -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! DECIMAL HEX OCTAL 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 ? DUMP Q.PRINT 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 ? DUMP Q.PRINT EMIT CR SPACE SPACES TYPE SEND RECV`.
|
||||
document that clobber `A`: `@ ! +! -! 2@ 2! C@ C! FILL ERASE MOVE COUNT CMOVE CMOVE> BLANK -TRAILING COMPARE SEARCH SCAN SKIP DECIMAL HEX OCTAL 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 ? DUMP Q.PRINT TYPE SEND RECV`. Words that clobber `B`:
|
||||
`COMPARE SEARCH Q./ Q.EXP Q.SQRT Q.LOG Q.SIN Q.COS <# HOLD SIGN # #S #> . .R U. U.R D. D.R ? DUMP Q.PRINT 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.
|
||||
|
||||
@@ -333,9 +333,9 @@ Section numbers match the v3 primitive reference.
|
||||
| `2!` | CAP | `a! SWAP !+ !` |
|
||||
| `C@` | CAP | See below (D-1). Call-free. Executed on the golden model (2026-10-03). Leaves its caller 8 data cells and 7 return entries. |
|
||||
| `C!` | CAP | See below (D-1). Call-free. Executed on the golden model (2026-10-03). |
|
||||
| `FILL` | CAP | See below. |
|
||||
| `MOVE` | CAP | `push 2DUP U< IF pop CMOVE> ELSE pop CMOVE THEN` |
|
||||
| `ERASE` | CAP | `0 FILL` |
|
||||
| `FILL` | CAP | `( baddr u c -- )`: the low byte of `c`. See below. Nothing for `u = 0`; for `u < 0` nothing is written and `NODE-ERROR` is set (v3 read a negative count as a huge unsigned one). Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 5 data cells and 5 return entries. Clobbers `A` and, on error, `B`. |
|
||||
| `MOVE` | CAP | `( src dst u -- )`, bytes: `push over over - -if UP drop pop jump CMOVE> UP: drop pop jump CMOVE` — `CMOVE>` when `src < dst`, else `CMOVE`, so overlapping bytes are read before they are overwritten, as v3's `memmove`. The test is the sign of `src - dst`, in line. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 4 data cells and 5 return entries. |
|
||||
| `ERASE` | CAP | `0 FILL`, as `0 jump FILL`. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. |
|
||||
| `CELLS` | IN | Empty (word-addressed). Becomes `2* 2*` if D-1 chooses bytes. |
|
||||
|
||||
```forth
|
||||
@@ -366,7 +366,10 @@ Section numbers match the v3 primitive reference.
|
||||
K0: drop @ -256 and + ! ; \ byte 0
|
||||
|
||||
: FILL ( baddr u c -- )
|
||||
SWAP BEGIN dup WHILE 1- push 2DUP SWAP C! SWAP 1+ SWAP pop REPEAT 2DROP drop ;
|
||||
-ROT \ c baddr u
|
||||
-if OK drop drop drop NODE-ERROR b! -1 !b ; \ u < 0
|
||||
OK: if DONE push over over C! 1 + pop -1 + jump OK
|
||||
DONE: drop drop drop ;
|
||||
```
|
||||
|
||||
### 5.4 Arithmetic
|
||||
@@ -554,14 +557,15 @@ width and print a flood of spaces.
|
||||
|
||||
| Word | Fate | Notes |
|
||||
| --- | --- | --- |
|
||||
| `COUNT` | CAP | `dup 1+ SWAP C@` |
|
||||
| `CMOVE` | CAP | See below. |
|
||||
| `CMOVE>` | CAP | See below. |
|
||||
| `BLANK` | CAP | `32 FILL` |
|
||||
| `-TRAILING` | CAP | Loop from the end while the character is a space. |
|
||||
| `COMPARE` | CAP | Byte loop returning `-1`, `0`, or `1`. v3's counted-string auto-detection is not kept. |
|
||||
| `SEARCH` | CAP | Nested loop over `COMPARE`. Same note. |
|
||||
| `SCAN` `SKIP` | CAP | Byte loops. |
|
||||
| `COUNT` | CAP | `( baddr -- baddr+1 c )`: `dup C@ push 1 + pop`. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 7 data cells and 6 return entries. Clobbers `A`. |
|
||||
| `CMOVE` | CAP | `( src dst u -- )`, low byte first. See below. Nothing for `u = 0`; for `u < 0` nothing is written and `NODE-ERROR` is set, where v3 raised its error flag. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 5 data cells and 5 return entries. |
|
||||
| `CMOVE>` | CAP | `( src dst u -- )`, high byte first. See below. Errors as `CMOVE`. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 4 data cells and 5 return entries. |
|
||||
| `BLANK` | CAP | `32 FILL`, as `32 jump FILL`. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. v3's counted-string auto-detection is not kept. |
|
||||
| `-TRAILING` | CAP | `( baddr u -- baddr u' )`. See below. A negative count reads as 0, as v3. v3's counted-string auto-detection is not kept. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 6 data cells and 6 return entries. |
|
||||
| `COMPARE` | CAP | `( a1 u1 a2 u2 -- n )`: `-1`, `0` or `1` by the first differing byte, unsigned, else the shorter string is less. See below. Negative counts read as 0, as v3. v3's counted-string auto-detection is not kept. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 4 data cells and 5 return entries. Clobbers `A` and `B`. |
|
||||
| `SEARCH` | CAP | `( a1 u1 a2 u2 -- a3 u3 flag )`: the rest of string 1 from the first place string 2 occurs and `-1`, or string 1 and `0`; an empty string 2 is found at the start. See below; the inner comparison is in line, not a call to `COMPARE`. Same notes. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 3 data cells and 5 return entries. Clobbers `A` and `B`. |
|
||||
| `SCAN` `SKIP` | CAP | `( baddr u c -- baddr' u' )`: `SCAN` stops at the first byte equal to the low byte of `c` (or at the end, with `u' = 0`); `SKIP` stops at the first byte not equal to it. See below. A negative count reads as 0, as v3. v3's counted-string auto-detection is not kept. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Each leaves its caller 5 data cells and 5 return entries. Clobber `A`. |
|
||||
| `(S)` | CAP | Variable, 4 cells: `COMPARE`'s two lengths; `SEARCH`'s string 2 and the whole of string 1. |
|
||||
| `BL` | IN | `32` |
|
||||
| `EXPECT` `QUERY` | CC | Built on `KEY` (DEV). |
|
||||
| `SPAN` `TIB` `>IN` `SOURCE` | CC | Interpreter state on the host node. |
|
||||
@@ -571,11 +575,68 @@ width and print a flood of spaces.
|
||||
| `LITERAL` `[LITERAL]` (placeholders) | RET | The working `LITERAL` is in §5.17. |
|
||||
|
||||
```forth
|
||||
\ SWAP and the subtractions ( - ) are in line.
|
||||
: CMOVE ( src dst u -- )
|
||||
BEGIN dup WHILE 1- push over C@ over C! 1+ SWAP 1+ SWAP pop REPEAT drop 2DROP ;
|
||||
-if OK drop drop drop NODE-ERROR b! -1 !b ; \ u < 0
|
||||
OK: if DONE push \ src dst R: u
|
||||
over C@ over C! 1 + push 1 + pop pop -1 + jump OK
|
||||
DONE: drop drop drop ;
|
||||
|
||||
: CMOVE> ( src dst u -- )
|
||||
BEGIN dup WHILE 1- push over R@ + C@ over R@ + C! pop REPEAT drop 2DROP ;
|
||||
-if OK drop drop drop NODE-ERROR b! -1 !b ; \ u < 0
|
||||
OK: if DONE -1 + push \ src dst R: i = u - 1
|
||||
over pop dup push + C@ \ src dst c
|
||||
over pop dup push + C! \ src dst
|
||||
pop jump OK
|
||||
DONE: drop drop drop ;
|
||||
|
||||
: -TRAILING ( baddr u -- baddr u' )
|
||||
-if L drop 0 ; \ u < 0
|
||||
L: if DONE over over + -1 + C@ -32 + if SP drop ; \ not a space
|
||||
SP: drop -1 + jump L
|
||||
DONE: ;
|
||||
|
||||
: COMPARE ( a1 u1 a2 u2 -- n )
|
||||
-if A drop 0 A: (S) 1 + b! !b \ a1 u1 a2
|
||||
SWAP -if B drop 0 B: dup (S) b! !b \ a1 a2 u1
|
||||
(S) 1 + b! @b \ a1 a2 u1 u2
|
||||
over over - -if GE drop drop jump M \ the smaller
|
||||
GE: drop push drop pop
|
||||
M: \ a1 a2 m
|
||||
L: if EQ push \ a1 a2 R: m
|
||||
over C@ over C@ - if SAME \ c1 - c2
|
||||
-if GT drop drop drop pop drop -1 ;
|
||||
GT: drop drop drop pop drop 1 ;
|
||||
SAME: drop 1 + push 1 + pop pop -1 + jump L
|
||||
EQ: drop drop drop (S) b! @b (S) 1 + b! @b - \ u1 - u2
|
||||
-if NN drop -1 ;
|
||||
NN: if ZZ drop 1 ;
|
||||
ZZ: ;
|
||||
|
||||
: SEARCH ( a1 u1 a2 u2 -- a3 u3 flag )
|
||||
-if A drop 0 A: (S) 1 + b! !b (S) b! !b \ a1 u1 (S): a2 u2
|
||||
-if B drop 0 B: dup (S) 3 + b! !b over (S) 2 + b! !b \ a u (S)+2: a1 u1
|
||||
L: dup (S) 1 + b! @b - -if TRY \ u - u2
|
||||
drop drop drop (S) 2 + b! @b (S) 3 + b! @b 0 ; \ not found
|
||||
TRY: drop over (S) b! @b (S) 1 + b! @b \ a u p q k
|
||||
I: if MATCH push \ a u p q R: k
|
||||
over C@ over C@ xor if SAME
|
||||
drop drop drop pop drop push 1 + pop -1 + jump L \ next place
|
||||
SAME: drop 1 + push 1 + pop pop -1 + jump I
|
||||
MATCH: drop drop drop -1 ;
|
||||
|
||||
: SCAN ( baddr u c -- baddr' u' )
|
||||
255 and push -if L drop 0 \ baddr u R: c
|
||||
L: if DONE over C@ pop dup push xor if FOUND
|
||||
drop push 1 + pop -1 + jump L
|
||||
FOUND: drop
|
||||
DONE: pop drop ;
|
||||
|
||||
: SKIP ( baddr u c -- baddr' u' )
|
||||
255 and push -if L drop 0 \ baddr u R: c
|
||||
L: if DONE over C@ pop dup push xor if SAME drop jump DONE
|
||||
SAME: drop push 1 + pop -1 + jump L
|
||||
DONE: pop drop ;
|
||||
```
|
||||
|
||||
### 5.10 Terminal I/O
|
||||
|
||||
@@ -0,0 +1,706 @@
|
||||
/* test_strings.c -- the byte-string words, executed.
|
||||
*
|
||||
* DECOMPOSITION.md 5.3 and 5.9: FILL ERASE MOVE, COUNT CMOVE CMOVE> BLANK
|
||||
* -TRAILING COMPARE SEARCH SCAN SKIP. All are loops over C@ and C!, which
|
||||
* are assembled again here as they are in test_foundation.c, on a node of
|
||||
* its own.
|
||||
*
|
||||
* Behaviour follows v3 (v3/src/word_source/string_words.c, memory_words.c):
|
||||
* COUNT ( baddr -- baddr+1 c )
|
||||
* CMOVE ( src dst u -- ) low byte first
|
||||
* CMOVE> ( src dst u -- ) high byte first
|
||||
* MOVE ( src dst u -- ) whichever of the two does not overwrite
|
||||
* bytes it has yet to read
|
||||
* FILL ( baddr u c -- ) the low byte of c
|
||||
* BLANK ERASE ( baddr u -- ) FILL with 32, with 0
|
||||
* -TRAILING ( baddr u -- baddr u' ) without its trailing spaces
|
||||
* COMPARE ( a1 u1 a2 u2 -- n ) -1, 0 or 1: the first differing
|
||||
* byte (unsigned), else the shorter is less
|
||||
* SEARCH ( a1 u1 a2 u2 -- a3 u3 f ) the first place string 2 occurs in
|
||||
* string 1: the rest of string 1 from there
|
||||
* and -1, or string 1 and 0; an empty
|
||||
* string 2 is found at the start
|
||||
* SCAN ( baddr u c -- baddr' u' ) from the first byte equal to c
|
||||
* SKIP ( baddr u c -- baddr' u' ) past the leading bytes equal to c
|
||||
* A negative count reads as 0 in -TRAILING, COMPARE, SEARCH, SCAN and SKIP,
|
||||
* as in v3. In CMOVE, CMOVE>, MOVE, FILL, BLANK and ERASE it does nothing
|
||||
* and sets NODE-ERROR, where v3 raised its error flag (or, in FILL, read it
|
||||
* as a huge unsigned count).
|
||||
*
|
||||
* Where v4 parts from v3: v3's -TRAILING, COMPARE, SEARCH, SCAN, SKIP and
|
||||
* BLANK took a string whose first byte equalled its length to be a counted
|
||||
* string and stepped over that byte. 5.9 does not keep that for COMPARE and
|
||||
* SEARCH; it is not kept for the others either. Addresses are not
|
||||
* range-checked (open, node.h).
|
||||
*
|
||||
* Five transcripts of the real v3 binary are recorded below as expected
|
||||
* results, taken on 2026-10-03 as in test_numout.c.
|
||||
*/
|
||||
#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)
|
||||
|
||||
/* The memory map is open (D-4); the test chooses the addresses. */
|
||||
#define NODE_ERROR ((v4_cell)(V4_NODE_WORDS - 2u))
|
||||
#define SV ((v4_cell)(V4_NODE_WORDS - 12u)) /* (S): COMPARE's and SEARCH's lengths and addresses, 4 cells */
|
||||
#define BUFW ((v4_cell)(V4_NODE_WORDS - 96u)) /* 64 cells = 256 bytes of text */
|
||||
#define BUF ((v4_cell)(BUFW * 4))
|
||||
#define BUFLEN 256
|
||||
|
||||
static v4_node n;
|
||||
static v4_exec_state es;
|
||||
static v4_heat h;
|
||||
static v4_asm as;
|
||||
|
||||
static v4_cell w_cfetch, w_cstore, w_count, w_cmove, w_cmoveup, w_move, w_fill,
|
||||
w_blank, w_erase, w_trailing, w_compare, w_search, w_scan, w_skip;
|
||||
|
||||
#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)
|
||||
#define ERROR_EXIT() do { LIT(NODE_ERROR); O(BANG_B); LIT(-1); O(STORE_B); O(SEMI); } while (0)
|
||||
|
||||
static void build(void)
|
||||
{
|
||||
v4_asm_ref a, b, c;
|
||||
v4_cell l, l2;
|
||||
|
||||
/* C@ and C!, call-free, as in test_foundation.c. */
|
||||
w_cfetch = v4_asm_label(&as);
|
||||
{
|
||||
v4_asm_ref f0, f1, f2;
|
||||
O(DUP); O(TWO_SLASH); O(TWO_SLASH); O(BANG_A); LIT(3); O(AND);
|
||||
f0 = v4_asm_branch_fwd(&as, V4_OP_IF);
|
||||
LIT(-1); O(ADD); f1 = v4_asm_branch_fwd(&as, V4_OP_IF);
|
||||
LIT(-1); O(ADD); f2 = v4_asm_branch_fwd(&as, V4_OP_IF);
|
||||
O(DROP); O(FETCH_A); LIT(23); O(PUSH);
|
||||
(void)v4_asm_label(&as);
|
||||
O(TWO_SLASH); O(UNEXT);
|
||||
LIT(255); O(AND); O(SEMI);
|
||||
v4_asm_resolve(&as, f2, v4_asm_label(&as));
|
||||
O(DROP); O(FETCH_A); LIT(15); O(PUSH);
|
||||
(void)v4_asm_label(&as);
|
||||
O(TWO_SLASH); O(UNEXT);
|
||||
LIT(255); O(AND); O(SEMI);
|
||||
v4_asm_resolve(&as, f1, v4_asm_label(&as));
|
||||
O(DROP); O(FETCH_A); LIT(7); O(PUSH);
|
||||
(void)v4_asm_label(&as);
|
||||
O(TWO_SLASH); O(UNEXT);
|
||||
LIT(255); O(AND); O(SEMI);
|
||||
v4_asm_resolve(&as, f0, v4_asm_label(&as));
|
||||
O(DROP); O(FETCH_A); LIT(255); O(AND); O(SEMI);
|
||||
}
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
/* : COUNT ( baddr -- baddr+1 c ) dup C@ push 1 + pop ; */
|
||||
w_count = v4_asm_label(&as);
|
||||
O(DUP); CALL(w_cfetch); O(PUSH); LIT(1); O(ADD); O(RPOP); O(SEMI);
|
||||
|
||||
/* : CMOVE ( src dst u -- )
|
||||
* -if OK drop drop drop NODE-ERROR b! -1 !b ; u < 0
|
||||
* OK: if DONE push src dst R: u
|
||||
* over C@ over C! 1 + push 1 + pop pop -1 + jump OK
|
||||
* DONE: drop drop drop ; */
|
||||
w_cmove = v4_asm_label(&as);
|
||||
a = FWD(MINUS_IF);
|
||||
O(DROP); O(DROP); O(DROP); ERROR_EXIT();
|
||||
HERE_(a);
|
||||
l = v4_asm_label(&as);
|
||||
b = FWD(IF);
|
||||
O(PUSH); O(OVER); CALL(w_cfetch); O(OVER); CALL(w_cstore);
|
||||
LIT(1); O(ADD); O(PUSH); LIT(1); O(ADD); O(RPOP); O(RPOP); LIT(-1); O(ADD);
|
||||
JUMP(l);
|
||||
HERE_(b);
|
||||
O(DROP); O(DROP); O(DROP); O(SEMI);
|
||||
|
||||
/* : CMOVE> ( src dst u -- )
|
||||
* -if OK drop drop drop NODE-ERROR b! -1 !b ; u < 0
|
||||
* OK: if DONE -1 + push src dst R: i = u - 1
|
||||
* over pop dup push + C@ src dst c
|
||||
* over pop dup push + C! src dst
|
||||
* pop jump OK
|
||||
* DONE: drop drop drop ; */
|
||||
w_cmoveup = v4_asm_label(&as);
|
||||
a = FWD(MINUS_IF);
|
||||
O(DROP); O(DROP); O(DROP); ERROR_EXIT();
|
||||
HERE_(a);
|
||||
l = v4_asm_label(&as);
|
||||
b = FWD(IF);
|
||||
LIT(-1); O(ADD); O(PUSH);
|
||||
O(OVER); O(RPOP); O(DUP); O(PUSH); O(ADD); CALL(w_cfetch);
|
||||
O(OVER); O(RPOP); O(DUP); O(PUSH); O(ADD); CALL(w_cstore);
|
||||
O(RPOP);
|
||||
JUMP(l);
|
||||
HERE_(b);
|
||||
O(DROP); O(DROP); O(DROP); O(SEMI);
|
||||
|
||||
/* : MOVE ( src dst u -- )
|
||||
* push over over - -if UP drop pop jump CMOVE> src < dst
|
||||
* UP: drop pop jump CMOVE
|
||||
* The sign of src - dst, the subtraction in line. */
|
||||
w_move = v4_asm_label(&as);
|
||||
O(PUSH); O(OVER); O(OVER); SUB_INLINE(); a = FWD(MINUS_IF);
|
||||
O(DROP); O(RPOP); JUMP(w_cmoveup);
|
||||
HERE_(a);
|
||||
O(DROP); O(RPOP); JUMP(w_cmove);
|
||||
|
||||
/* : FILL ( baddr u c -- )
|
||||
* -ROT c baddr u
|
||||
* -if OK drop drop drop NODE-ERROR b! -1 !b ; u < 0
|
||||
* OK: if DONE push over over C! 1 + pop -1 + jump OK
|
||||
* DONE: drop drop drop ; */
|
||||
w_fill = v4_asm_label(&as);
|
||||
NROT_INLINE();
|
||||
a = FWD(MINUS_IF);
|
||||
O(DROP); O(DROP); O(DROP); ERROR_EXIT();
|
||||
HERE_(a);
|
||||
l = v4_asm_label(&as);
|
||||
b = FWD(IF);
|
||||
O(PUSH); O(OVER); O(OVER); CALL(w_cstore); LIT(1); O(ADD); O(RPOP); LIT(-1); O(ADD);
|
||||
JUMP(l);
|
||||
HERE_(b);
|
||||
O(DROP); O(DROP); O(DROP); O(SEMI);
|
||||
|
||||
/* : BLANK ( baddr u -- ) 32 jump FILL : ERASE ( baddr u -- ) 0 jump FILL */
|
||||
w_blank = v4_asm_label(&as);
|
||||
LIT(32); JUMP(w_fill);
|
||||
w_erase = v4_asm_label(&as);
|
||||
LIT(0); JUMP(w_fill);
|
||||
|
||||
/* : -TRAILING ( baddr u -- baddr u' )
|
||||
* -if L drop 0 ; u < 0
|
||||
* L: if DONE over over + -1 + C@ -32 + if SP drop ; not a space
|
||||
* SP: drop -1 + jump L
|
||||
* DONE: ; */
|
||||
w_trailing = v4_asm_label(&as);
|
||||
a = FWD(MINUS_IF);
|
||||
O(DROP); LIT(0); O(SEMI);
|
||||
HERE_(a);
|
||||
l = v4_asm_label(&as);
|
||||
b = FWD(IF);
|
||||
O(OVER); O(OVER); O(ADD); LIT(-1); O(ADD); CALL(w_cfetch); LIT(-32); O(ADD);
|
||||
c = FWD(IF);
|
||||
O(DROP); O(SEMI);
|
||||
HERE_(c);
|
||||
O(DROP); LIT(-1); O(ADD); JUMP(l);
|
||||
HERE_(b);
|
||||
O(SEMI);
|
||||
|
||||
/* : COMPARE ( a1 u1 a2 u2 -- n )
|
||||
* -if A drop 0 A: (S) 1 + b! !b a1 u1 a2
|
||||
* SWAP -if B drop 0 B: dup (S) b! !b a1 a2 u1
|
||||
* (S) 1 + b! @b a1 a2 u1 u2
|
||||
* over over - -if GE drop drop jump M the smaller
|
||||
* GE: drop push drop pop
|
||||
* M: a1 a2 m
|
||||
* L: if EQ push a1 a2 R: m
|
||||
* over C@ over C@ - if SAME c1 - c2
|
||||
* -if GT drop drop drop pop drop -1 ;
|
||||
* GT: drop drop drop pop drop 1 ;
|
||||
* SAME: drop 1 + push 1 + pop pop -1 + jump L
|
||||
* EQ: drop drop drop (S) b! @b (S) 1 + b! @b - u1 - u2
|
||||
* -if NN drop -1 ;
|
||||
* NN: if ZZ drop 1 ;
|
||||
* ZZ: ;
|
||||
* SWAP and the subtractions are in line. The two clamped lengths wait
|
||||
* in the variable (S). */
|
||||
w_compare = v4_asm_label(&as);
|
||||
a = FWD(MINUS_IF); O(DROP); LIT(0); HERE_(a);
|
||||
VSET(SV + 1);
|
||||
SWAP_INLINE();
|
||||
a = FWD(MINUS_IF); O(DROP); LIT(0); HERE_(a);
|
||||
O(DUP); VSET(SV);
|
||||
VGET(SV + 1);
|
||||
O(OVER); O(OVER); SUB_INLINE(); a = FWD(MINUS_IF);
|
||||
O(DROP); O(DROP); b = FWD(JUMP);
|
||||
HERE_(a);
|
||||
O(DROP); O(PUSH); O(DROP); O(RPOP);
|
||||
HERE_(b);
|
||||
l = v4_asm_label(&as);
|
||||
a = FWD(IF);
|
||||
O(PUSH); O(OVER); CALL(w_cfetch); O(OVER); CALL(w_cfetch); SUB_INLINE();
|
||||
b = FWD(IF);
|
||||
c = FWD(MINUS_IF);
|
||||
O(DROP); O(DROP); O(DROP); O(RPOP); O(DROP); LIT(-1); O(SEMI);
|
||||
HERE_(c);
|
||||
O(DROP); O(DROP); O(DROP); O(RPOP); O(DROP); LIT(1); O(SEMI);
|
||||
HERE_(b);
|
||||
O(DROP); LIT(1); O(ADD); O(PUSH); LIT(1); O(ADD); O(RPOP); O(RPOP); LIT(-1); O(ADD);
|
||||
JUMP(l);
|
||||
HERE_(a);
|
||||
O(DROP); O(DROP); O(DROP); VGET(SV); VGET(SV + 1); SUB_INLINE();
|
||||
a = FWD(MINUS_IF);
|
||||
O(DROP); LIT(-1); O(SEMI);
|
||||
HERE_(a);
|
||||
b = FWD(IF);
|
||||
O(DROP); LIT(1); O(SEMI);
|
||||
HERE_(b);
|
||||
O(SEMI);
|
||||
|
||||
/* : SEARCH ( a1 u1 a2 u2 -- a3 u3 flag )
|
||||
* -if A drop 0 A: (S) 1 + b! !b (S) b! !b a1 u1 (S): a2 u2
|
||||
* -if B drop 0 B: dup (S) 3 + b! !b over (S) 2 + b! !b a u (S)+2: a1 u1
|
||||
* L: dup (S) 1 + b! @b - -if TRY u - u2
|
||||
* drop drop drop (S) 2 + b! @b (S) 3 + b! @b 0 ; not found
|
||||
* TRY: drop over (S) b! @b (S) 1 + b! @b a u p q k
|
||||
* I: if MATCH push a u p q R: k
|
||||
* over C@ over C@ xor if SAME
|
||||
* drop drop drop pop drop push 1 + pop -1 + jump L next place
|
||||
* SAME: drop 1 + push 1 + pop pop -1 + jump I
|
||||
* MATCH: drop drop drop -1 ; */
|
||||
w_search = v4_asm_label(&as);
|
||||
a = FWD(MINUS_IF); O(DROP); LIT(0); HERE_(a);
|
||||
VSET(SV + 1); VSET(SV);
|
||||
a = FWD(MINUS_IF); O(DROP); LIT(0); HERE_(a);
|
||||
O(DUP); VSET(SV + 3); O(OVER); VSET(SV + 2);
|
||||
l = v4_asm_label(&as);
|
||||
O(DUP); VGET(SV + 1); SUB_INLINE(); a = FWD(MINUS_IF);
|
||||
O(DROP); O(DROP); O(DROP); VGET(SV + 2); VGET(SV + 3); LIT(0); O(SEMI);
|
||||
HERE_(a);
|
||||
O(DROP); O(OVER); VGET(SV); VGET(SV + 1);
|
||||
l2 = v4_asm_label(&as);
|
||||
a = FWD(IF);
|
||||
O(PUSH); O(OVER); CALL(w_cfetch); O(OVER); CALL(w_cfetch); O(XOR);
|
||||
b = FWD(IF);
|
||||
O(DROP); O(DROP); O(DROP); O(RPOP); O(DROP);
|
||||
O(PUSH); LIT(1); O(ADD); O(RPOP); LIT(-1); O(ADD);
|
||||
JUMP(l);
|
||||
HERE_(b);
|
||||
O(DROP); LIT(1); O(ADD); O(PUSH); LIT(1); O(ADD); O(RPOP); O(RPOP); LIT(-1); O(ADD);
|
||||
JUMP(l2);
|
||||
HERE_(a);
|
||||
O(DROP); O(DROP); O(DROP); LIT(-1); O(SEMI);
|
||||
|
||||
/* : SCAN ( baddr u c -- baddr' u' )
|
||||
* 255 and push -if L drop 0 baddr u R: c
|
||||
* L: if DONE over C@ pop dup push xor if FOUND
|
||||
* drop push 1 + pop -1 + jump L
|
||||
* FOUND: drop
|
||||
* DONE: pop drop ; */
|
||||
w_scan = v4_asm_label(&as);
|
||||
LIT(255); O(AND); O(PUSH);
|
||||
a = FWD(MINUS_IF); O(DROP); LIT(0); HERE_(a);
|
||||
l = v4_asm_label(&as);
|
||||
a = FWD(IF);
|
||||
O(OVER); CALL(w_cfetch); O(RPOP); O(DUP); O(PUSH); O(XOR); b = FWD(IF);
|
||||
O(DROP); O(PUSH); LIT(1); O(ADD); O(RPOP); LIT(-1); O(ADD);
|
||||
JUMP(l);
|
||||
HERE_(b);
|
||||
O(DROP);
|
||||
HERE_(a);
|
||||
O(RPOP); O(DROP); O(SEMI);
|
||||
|
||||
/* : SKIP ( baddr u c -- baddr' u' )
|
||||
* 255 and push -if L drop 0 baddr u R: c
|
||||
* L: if DONE over C@ pop dup push xor if SAME drop jump DONE
|
||||
* SAME: drop push 1 + pop -1 + jump L
|
||||
* DONE: pop drop ; */
|
||||
w_skip = v4_asm_label(&as);
|
||||
LIT(255); O(AND); O(PUSH);
|
||||
a = FWD(MINUS_IF); O(DROP); LIT(0); HERE_(a);
|
||||
l = v4_asm_label(&as);
|
||||
a = FWD(IF);
|
||||
O(OVER); CALL(w_cfetch); O(RPOP); O(DUP); O(PUSH); O(XOR); b = FWD(IF);
|
||||
O(DROP); c = FWD(JUMP);
|
||||
HERE_(b);
|
||||
O(DROP); O(PUSH); LIT(1); O(ADD); O(RPOP); LIT(-1); O(ADD);
|
||||
JUMP(l);
|
||||
HERE_(a); HERE_(c);
|
||||
O(RPOP); O(DROP); O(SEMI);
|
||||
}
|
||||
|
||||
/* ---- running ---------------------------------------------------------- */
|
||||
|
||||
/* The test's own copy of the 256 text bytes; the node's are compared with
|
||||
* it after every call. */
|
||||
static unsigned char img[BUFLEN];
|
||||
|
||||
static unsigned char node_byte(v4_cell baddr)
|
||||
{
|
||||
return (unsigned char)(((v4_ucell)n.mem[baddr >> 2] >> (8u * (unsigned)(baddr & 3))) & 0xFFu);
|
||||
}
|
||||
static void load_img(void)
|
||||
{
|
||||
unsigned i;
|
||||
for (i = 0; i < BUFLEN / 4; i++) {
|
||||
v4_ucell w = (v4_ucell)img[4 * i] | ((v4_ucell)img[4 * i + 1] << 8)
|
||||
| ((v4_ucell)img[4 * i + 2] << 16) | ((v4_ucell)img[4 * i + 3] << 24);
|
||||
n.mem[BUFW + (v4_cell)i] = (v4_cell)w;
|
||||
}
|
||||
}
|
||||
static int img_matches(void)
|
||||
{
|
||||
unsigned i;
|
||||
for (i = 0; i < BUFLEN; i++) if (node_byte(BUF + (v4_cell)i) != img[i]) return 0;
|
||||
for (i = 0; i < BUFLEN / 4; i++) /* nothing above byte 3 of a cell either */
|
||||
if (((v4_ucell)n.mem[BUFW + (v4_cell)i] >> 16 >> 16) != 0) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Runs `word` on argc arguments over the current img; true when it returns.
|
||||
* The results are then popped with res(). */
|
||||
static int call(v4_cell word, unsigned argc, v4_cell a, v4_cell b, v4_cell c, v4_cell d)
|
||||
{
|
||||
v4_cell args[4];
|
||||
unsigned i;
|
||||
args[0] = a; args[1] = b; args[2] = c; args[3] = d;
|
||||
load_img();
|
||||
v4_dstack_reset(&n.ds);
|
||||
v4_rstack_reset(&n.rs);
|
||||
v4_exec_reset(&es);
|
||||
v4_heat_reset(&h);
|
||||
v4_node_store(&n, NODE_ERROR, 0);
|
||||
v4_dstack_push(&n.ds, CANARY);
|
||||
for (i = 0; i < argc; i++) v4_dstack_push(&n.ds, args[i]);
|
||||
return v4_test_call(&n, &es, &h, word, 4000000) > 0;
|
||||
}
|
||||
static v4_cell res(void) { return v4_dstack_pop(&n.ds); }
|
||||
static int clean(void) { return v4_dstack_pop(&n.ds) == CANARY; }
|
||||
static int err(void) { return v4_node_load(&n, NODE_ERROR) != 0; }
|
||||
|
||||
/* Stack headroom, as in test_foundation.c: the results and the bytes must
|
||||
* be what a run with empty stacks gave. */
|
||||
static int fits(v4_cell word, unsigned argc, const v4_cell *args, unsigned nres, const v4_cell *want,
|
||||
const unsigned char *img0, const unsigned char *img1, unsigned dfill, unsigned rfill)
|
||||
{
|
||||
unsigned i;
|
||||
memcpy(img, img0, BUFLEN);
|
||||
load_img();
|
||||
v4_dstack_reset(&n.ds);
|
||||
v4_rstack_reset(&n.rs);
|
||||
v4_exec_reset(&es);
|
||||
for (i = 0; i < dfill; i++) v4_dstack_push(&n.ds, (v4_cell)(0x5A000000 + i));
|
||||
v4_dstack_push(&n.ds, CANARY);
|
||||
for (i = 0; i < argc; i++) v4_dstack_push(&n.ds, args[i]);
|
||||
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;
|
||||
for (i = nres; i-- > 0; ) if (v4_dstack_pop(&n.ds) != want[i]) 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;
|
||||
memcpy(img, img1, BUFLEN);
|
||||
return img_matches();
|
||||
}
|
||||
static void headroom(const char *name, v4_cell word, unsigned argc, v4_cell a, v4_cell b, v4_cell c, v4_cell d,
|
||||
unsigned nres, int min_d, int min_r)
|
||||
{
|
||||
unsigned char img0[BUFLEN], img1[BUFLEN];
|
||||
v4_cell args[4], want[4];
|
||||
unsigned i;
|
||||
int dh, rh;
|
||||
args[0] = a; args[1] = b; args[2] = c; args[3] = d;
|
||||
memcpy(img0, img, BUFLEN);
|
||||
CHECK(call(word, argc, a, b, c, d), "%s runs", name);
|
||||
for (i = nres; i-- > 0; ) want[i] = res();
|
||||
for (i = 0; i < BUFLEN; i++) img1[i] = node_byte(BUF + (v4_cell)i);
|
||||
for (dh = 0; dh < V4_DATA_DEPTH; dh++) if (!fits(word, argc, args, nres, want, img0, img1, (unsigned)dh + 1u, 0)) break;
|
||||
for (rh = 0; rh < V4_RET_DEPTH; rh++) if (!fits(word, argc, args, nres, want, img0, img1, 0, (unsigned)rh + 1u)) break;
|
||||
printf(" %s headroom: data %d below canary, return %d below its return address\n", name, dh, rh);
|
||||
CHECK(dh >= min_d && rh >= min_r, "%s leaves room", name);
|
||||
memcpy(img, img0, BUFLEN);
|
||||
}
|
||||
|
||||
/* ---- the C reference ---------------------------------------------------- */
|
||||
|
||||
static uint32_t rng = 0x9E3779B9u;
|
||||
static uint32_t rnd(void) { rng ^= rng << 13; rng ^= rng >> 17; rng ^= rng << 5; return rng; }
|
||||
|
||||
static void random_img(unsigned alphabet)
|
||||
{
|
||||
unsigned i;
|
||||
for (i = 0; i < BUFLEN; i++)
|
||||
img[i] = (unsigned char)(alphabet ? 'a' + rnd() % alphabet : rnd() & 0xFFu);
|
||||
}
|
||||
static void text_at(unsigned off, const char *s) { memcpy(img + off, s, strlen(s)); }
|
||||
|
||||
static int ref_compare(unsigned o1, long u1, unsigned o2, long u2)
|
||||
{
|
||||
long m, i;
|
||||
if (u1 < 0) u1 = 0;
|
||||
if (u2 < 0) u2 = 0;
|
||||
m = u1 < u2 ? u1 : u2;
|
||||
for (i = 0; i < m; i++)
|
||||
if (img[o1 + i] != img[o2 + i]) return img[o1 + i] < img[o2 + i] ? -1 : 1;
|
||||
return u1 < u2 ? -1 : (u1 > u2 ? 1 : 0);
|
||||
}
|
||||
/* Returns the offset of the match in string 1, or -1. */
|
||||
static long ref_search(unsigned o1, long u1, unsigned o2, long u2)
|
||||
{
|
||||
long i;
|
||||
if (u1 < 0) u1 = 0;
|
||||
if (u2 < 0) u2 = 0;
|
||||
for (i = 0; i + u2 <= u1; i++)
|
||||
if (u2 == 0 || memcmp(img + o1 + i, img + o2, (size_t)u2) == 0) return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
unsigned char before[BUFLEN];
|
||||
unsigned i, t;
|
||||
|
||||
printf("v4 string tests: V4_CELL_BITS=%d\n", V4_CELL_BITS);
|
||||
|
||||
v4_node_reset(&n);
|
||||
v4_asm_begin(&as, &n, 16);
|
||||
build();
|
||||
CHECK(v4_asm_ok(&as), "string words assemble");
|
||||
CHECK(v4_asm_label(&as) < BUFW, "code stays below the text");
|
||||
printf(" code: %ld of %u words\n", (long)v4_asm_label(&as) - 16, (unsigned)V4_NODE_WORDS);
|
||||
|
||||
/* ---- the v3 transcripts ---- */
|
||||
|
||||
/* S" abc" S" abd" COMPARE . abc abc abd abc ab abc abc ab -> -1 0 1 -1 1 */
|
||||
memset(img, 0, BUFLEN);
|
||||
text_at(0, "abc"); text_at(8, "abd"); text_at(16, "abc"); text_at(24, "ab");
|
||||
CHECK(call(w_compare, 4, BUF, 3, BUF + 8, 3) && res() == -1 && clean(), "v3: abc abd COMPARE");
|
||||
CHECK(call(w_compare, 4, BUF, 3, BUF + 16, 3) && res() == 0 && clean(), "v3: abc abc COMPARE");
|
||||
CHECK(call(w_compare, 4, BUF + 8, 3, BUF, 3) && res() == 1 && clean(), "v3: abd abc COMPARE");
|
||||
CHECK(call(w_compare, 4, BUF + 24, 2, BUF, 3) && res() == -1 && clean(), "v3: ab abc COMPARE");
|
||||
CHECK(call(w_compare, 4, BUF, 3, BUF + 24, 2) && res() == 1 && clean(), "v3: abc ab COMPARE");
|
||||
|
||||
/* S" hello world" S" o w" SEARCH . . DROP -> -1 7
|
||||
* S" hello world" S" xyz" SEARCH . . DROP -> 0 11
|
||||
* S" hello" S" " SEARCH . . DROP -> -1 5 */
|
||||
memset(img, 0, BUFLEN);
|
||||
text_at(1, "hello world"); text_at(32, "o w"); text_at(40, "xyz");
|
||||
CHECK(call(w_search, 4, BUF + 1, 11, BUF + 32, 3) && res() == -1 && res() == 7 && res() == BUF + 5 && clean(),
|
||||
"v3: hello world / o w SEARCH");
|
||||
CHECK(call(w_search, 4, BUF + 1, 11, BUF + 40, 3) && res() == 0 && res() == 11 && res() == BUF + 1 && clean(),
|
||||
"v3: hello world / xyz SEARCH");
|
||||
CHECK(call(w_search, 4, BUF + 1, 5, BUF + 40, 0) && res() == -1 && res() == 5 && res() == BUF + 1 && clean(),
|
||||
"v3: hello / empty SEARCH");
|
||||
|
||||
/* S" hi " -TRAILING . DROP S" aaab" 97 SKIP . DROP S" hello" 108 SCAN . DROP
|
||||
* S" hello" 122 SCAN . DROP S" " -TRAILING . DROP -> 2 1 3 0 0 */
|
||||
memset(img, 0, BUFLEN);
|
||||
text_at(2, "hi "); text_at(16, "aaab"); text_at(33, "hello"); text_at(48, " ");
|
||||
CHECK(call(w_trailing, 2, BUF + 2, 5, 0, 0) && res() == 2 && res() == BUF + 2 && clean(), "v3: -TRAILING");
|
||||
CHECK(call(w_skip, 3, BUF + 16, 4, 97, 0) && res() == 1 && res() == BUF + 19 && clean(), "v3: SKIP");
|
||||
CHECK(call(w_scan, 3, BUF + 33, 5, 108, 0) && res() == 3 && res() == BUF + 35 && clean(), "v3: SCAN");
|
||||
CHECK(call(w_scan, 3, BUF + 33, 5, 122, 0) && res() == 0 && res() == BUF + 38 && clean(), "v3: SCAN, absent");
|
||||
CHECK(call(w_trailing, 2, BUF + 48, 4, 0, 0) && res() == 0 && res() == BUF + 48 && clean(), "v3: -TRAILING, all spaces");
|
||||
|
||||
/* CREATE B 16 ALLOT B 16 46 FILL B 4 BLANK B 8 + 3 ERASE B 16 DUMP
|
||||
* 20 20 20 20 2E 2E 2E 2E 00 00 00 2E 2E 2E 2E 2E
|
||||
* "ABCDEF" B 6 CMOVE B B 2 + 6 CMOVE> B B 1 + 6 CMOVE B 16 DUMP
|
||||
* 41 41 41 41 41 41 41 46 00 00 00 2E 2E 2E 2E 2E
|
||||
* B 3 + B 5 MOVE B 16 DUMP
|
||||
* 41 41 41 41 46 41 41 46 00 00 00 2E 2E 2E 2E 2E */
|
||||
{
|
||||
static const unsigned char d1[16] = { 0x20,0x20,0x20,0x20,0x2E,0x2E,0x2E,0x2E,0,0,0,0x2E,0x2E,0x2E,0x2E,0x2E };
|
||||
static const unsigned char d2[16] = { 0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x46,0,0,0,0x2E,0x2E,0x2E,0x2E,0x2E };
|
||||
static const unsigned char d3[16] = { 0x41,0x41,0x41,0x41,0x46,0x41,0x41,0x46,0,0,0,0x2E,0x2E,0x2E,0x2E,0x2E };
|
||||
const v4_cell B = BUF + 21;
|
||||
#define STEP(word, argc, a, b, c) do { \
|
||||
CHECK(call(word, argc, a, b, c, 0) && clean() && !err(), "v3 block-copy step returns"); \
|
||||
for (i = 0; i < BUFLEN; i++) img[i] = node_byte(BUF + (v4_cell)i); } while (0)
|
||||
memset(img, 0x77, BUFLEN);
|
||||
text_at(100, "ABCDEF");
|
||||
STEP(w_fill, 3, B, 16, 46);
|
||||
STEP(w_blank, 2, B, 4, 0);
|
||||
STEP(w_erase, 2, B + 8, 3, 0);
|
||||
CHECK(memcmp(img + 21, d1, 16) == 0, "v3: FILL, BLANK, ERASE");
|
||||
STEP(w_cmove, 3, BUF + 100, B, 6);
|
||||
STEP(w_cmoveup, 3, B, B + 2, 6);
|
||||
STEP(w_cmove, 3, B, B + 1, 6);
|
||||
CHECK(memcmp(img + 21, d2, 16) == 0, "v3: CMOVE, CMOVE>, CMOVE");
|
||||
STEP(w_move, 3, B + 3, B, 5);
|
||||
CHECK(memcmp(img + 21, d3, 16) == 0, "v3: MOVE");
|
||||
for (i = 0; i < BUFLEN; i++)
|
||||
if ((i < 21 || i >= 37) && (i < 100 || i >= 106) && img[i] != 0x77) break;
|
||||
CHECK(i == BUFLEN, "nothing outside B was written");
|
||||
}
|
||||
|
||||
/* 3 B C! 65 66 67 ... B COUNT . B - . -> 3 1 */
|
||||
memset(img, 0, BUFLEN);
|
||||
img[9] = 3; text_at(10, "ABC");
|
||||
CHECK(call(w_count, 1, BUF + 9, 0, 0, 0) && res() == 3 && res() == BUF + 10 && clean(), "v3: COUNT");
|
||||
|
||||
/* ---- against C ---- */
|
||||
|
||||
/* COUNT: every byte value, every position in a cell */
|
||||
random_img(0);
|
||||
for (i = 0; i < BUFLEN; i++)
|
||||
CHECK(call(w_count, 1, BUF + (v4_cell)i, 0, 0, 0) && res() == img[i] && res() == BUF + (v4_cell)i + 1
|
||||
&& clean() && img_matches(), "COUNT [%u]", i);
|
||||
|
||||
/* FILL, BLANK, ERASE */
|
||||
for (t = 0; t < 400; t++) {
|
||||
unsigned off = rnd() % 200, len = rnd() % 40;
|
||||
v4_cell ch = (v4_cell)(rnd() % 3 == 0 ? rnd() : rnd() & 0xFFu);
|
||||
unsigned which = t % 3;
|
||||
random_img(0);
|
||||
memcpy(before, img, BUFLEN);
|
||||
if (which == 0) { CHECK(call(w_fill, 3, BUF + (v4_cell)off, (v4_cell)len, ch, 0) && clean(), "FILL returns"); }
|
||||
else if (which == 1) { CHECK(call(w_blank, 2, BUF + (v4_cell)off, (v4_cell)len, 0, 0) && clean(), "BLANK returns"); ch = 32; }
|
||||
else { CHECK(call(w_erase, 2, BUF + (v4_cell)off, (v4_cell)len, 0, 0) && clean(), "ERASE returns"); ch = 0; }
|
||||
memset(img + off, (int)((v4_ucell)ch & 0xFFu), len);
|
||||
CHECK(img_matches() && !err(), "FILL/BLANK/ERASE [%u] off %u len %u", t, off, len);
|
||||
}
|
||||
|
||||
/* CMOVE, CMOVE>, MOVE: apart and overlapping both ways */
|
||||
for (t = 0; t < 900; t++) {
|
||||
unsigned len = rnd() % 48, src = rnd() % (BUFLEN - 48), dst;
|
||||
unsigned which = t % 3, k;
|
||||
if (t % 2) dst = rnd() % (BUFLEN - 48);
|
||||
else { int d = (int)(rnd() % 17) - 8; dst = (unsigned)((int)src + d < 0 ? 0 : (int)src + d); if (dst > BUFLEN - 48) dst = BUFLEN - 48; }
|
||||
random_img(0);
|
||||
CHECK(call(which == 0 ? w_cmove : which == 1 ? w_cmoveup : w_move, 3,
|
||||
BUF + (v4_cell)src, BUF + (v4_cell)dst, (v4_cell)len, 0) && clean(), "move returns [%u]", t);
|
||||
if (which == 0) for (k = 0; k < len; k++) img[dst + k] = img[src + k];
|
||||
else if (which == 1) for (k = len; k-- > 0; ) img[dst + k] = img[src + k];
|
||||
else memmove(img + dst, img + src, len);
|
||||
CHECK(img_matches() && !err(), "%s [%u] src %u dst %u len %u",
|
||||
which == 0 ? "CMOVE" : which == 1 ? "CMOVE>" : "MOVE", t, src, dst, len);
|
||||
}
|
||||
|
||||
/* a negative count: nothing written, NODE-ERROR set */
|
||||
{
|
||||
static const v4_cell neg[] = { -1, -7, (v4_cell)V4_MSB };
|
||||
random_img(0);
|
||||
for (i = 0; i < 3; i++) {
|
||||
CHECK(call(w_cmove, 3, BUF, BUF + 50, neg[i], 0) && clean() && img_matches() && err(), "CMOVE negative count [%u]", i);
|
||||
CHECK(call(w_cmoveup, 3, BUF, BUF + 50, neg[i], 0) && clean() && img_matches() && err(), "CMOVE> negative count [%u]", i);
|
||||
CHECK(call(w_move, 3, BUF, BUF + 50, neg[i], 0) && clean() && img_matches() && err(), "MOVE negative count, up [%u]", i);
|
||||
CHECK(call(w_move, 3, BUF + 50, BUF, neg[i], 0) && clean() && img_matches() && err(), "MOVE negative count, down [%u]", i);
|
||||
CHECK(call(w_fill, 3, BUF, neg[i], 65, 0) && clean() && img_matches() && err(), "FILL negative count [%u]", i);
|
||||
CHECK(call(w_blank, 2, BUF, neg[i], 0, 0) && clean() && img_matches() && err(), "BLANK negative count [%u]", i);
|
||||
CHECK(call(w_erase, 2, BUF, neg[i], 0, 0) && clean() && img_matches() && err(), "ERASE negative count [%u]", i);
|
||||
}
|
||||
}
|
||||
|
||||
/* -TRAILING */
|
||||
for (t = 0; t < 600; t++) {
|
||||
unsigned off = rnd() % 200, len = rnd() % 40, sp = rnd() % 41, k, want;
|
||||
random_img(t % 4 == 0 ? 0 : 3);
|
||||
for (k = 0; k < sp && k < len; k++) img[off + len - 1 - k] = 32;
|
||||
if (t % 5 == 0 && len) img[off] = (unsigned char)len; /* v3 would have taken this for a counted string */
|
||||
for (want = len; want > 0 && img[off + want - 1] == 32; want--) { }
|
||||
CHECK(call(w_trailing, 2, BUF + (v4_cell)off, (v4_cell)len, 0, 0) && res() == (v4_cell)want
|
||||
&& res() == BUF + (v4_cell)off && clean() && img_matches() && !err(), "-TRAILING [%u] len %u", t, len);
|
||||
}
|
||||
CHECK(call(w_trailing, 2, BUF + 5, -3, 0, 0) && res() == 0 && res() == BUF + 5 && clean() && !err(),
|
||||
"-TRAILING of a negative count is 0");
|
||||
|
||||
/* SCAN and SKIP */
|
||||
for (t = 0; t < 1200; t++) {
|
||||
unsigned off = rnd() % 200, len = rnd() % 40, k;
|
||||
v4_cell ch;
|
||||
random_img(t % 6 == 0 ? 0 : 2 + t % 3);
|
||||
ch = (v4_cell)(len && t % 3 ? img[off + rnd() % len] : (unsigned char)rnd());
|
||||
if (t % 7 == 0) ch += 256 * (v4_cell)(1 + rnd() % 100); /* only the low byte counts */
|
||||
if (t % 2 == 0) {
|
||||
for (k = 0; k < len && img[off + k] != ((v4_ucell)ch & 0xFFu); k++) { }
|
||||
CHECK(call(w_scan, 3, BUF + (v4_cell)off, (v4_cell)len, ch, 0) && res() == (v4_cell)(len - k)
|
||||
&& res() == BUF + (v4_cell)(off + k) && clean() && img_matches() && !err(), "SCAN [%u] len %u", t, len);
|
||||
} else {
|
||||
for (k = 0; k < len && img[off + k] == ((v4_ucell)ch & 0xFFu); k++) { }
|
||||
CHECK(call(w_skip, 3, BUF + (v4_cell)off, (v4_cell)len, ch, 0) && res() == (v4_cell)(len - k)
|
||||
&& res() == BUF + (v4_cell)(off + k) && clean() && img_matches() && !err(), "SKIP [%u] len %u", t, len);
|
||||
}
|
||||
}
|
||||
CHECK(call(w_scan, 3, BUF + 5, -3, 65, 0) && res() == 0 && res() == BUF + 5 && clean() && !err(), "SCAN of a negative count");
|
||||
CHECK(call(w_skip, 3, BUF + 5, -3, 65, 0) && res() == 0 && res() == BUF + 5 && clean() && !err(), "SKIP of a negative count");
|
||||
|
||||
/* COMPARE */
|
||||
for (t = 0; t < 3000; t++) {
|
||||
unsigned o1 = rnd() % 100, o2 = 128 + rnd() % 100;
|
||||
long u1 = (long)(rnd() % 24), u2 = (long)(rnd() % 24);
|
||||
int want;
|
||||
random_img(t % 10 == 0 ? 0 : 2);
|
||||
if (t % 3 == 0) { unsigned same = rnd() % 24; memcpy(img + o2, img + o1, same); }
|
||||
if (t % 11 == 0) img[o1 + rnd() % 4] = (unsigned char)(0x80u + rnd() % 128); /* bytes compare unsigned */
|
||||
if (t % 13 == 0 && u1) img[o1] = (unsigned char)u1; /* not a counted string */
|
||||
if (t % 50 == 0) u1 = -(long)(1 + rnd() % 5);
|
||||
if (t % 70 == 0) u2 = -(long)(1 + rnd() % 5);
|
||||
want = ref_compare(o1, u1, o2, u2);
|
||||
CHECK(call(w_compare, 4, BUF + (v4_cell)o1, (v4_cell)u1, BUF + (v4_cell)o2, (v4_cell)u2)
|
||||
&& res() == (v4_cell)want && clean() && img_matches() && !err(),
|
||||
"COMPARE [%u] u1 %ld u2 %ld want %d", t, u1, u2, want);
|
||||
}
|
||||
CHECK(call(w_compare, 4, BUF + 9, 12, BUF + 9, 12) && res() == 0 && clean(), "COMPARE of a string with itself");
|
||||
|
||||
/* SEARCH */
|
||||
for (t = 0; t < 3000; t++) {
|
||||
unsigned o1 = rnd() % 80, o2 = 160 + rnd() % 60;
|
||||
long u1 = (long)(rnd() % 40), u2 = (long)(rnd() % 6), at, c1, c2;
|
||||
v4_cell f, u3, a3;
|
||||
random_img(t % 10 == 0 ? 0 : 2);
|
||||
if (t % 3 == 0 && u1 >= u2) memcpy(img + o2, img + o1 + rnd() % (unsigned)(u1 - u2 + 1), (size_t)u2);
|
||||
if (t % 4 == 0) u2 = (long)(rnd() % 45);
|
||||
if (t % 50 == 0) u1 = -(long)(1 + rnd() % 5);
|
||||
if (t % 70 == 0) u2 = -(long)(1 + rnd() % 5);
|
||||
at = ref_search(o1, u1, o2, u2);
|
||||
c1 = u1 < 0 ? 0 : u1; c2 = u2;
|
||||
(void)c2;
|
||||
CHECK(call(w_search, 4, BUF + (v4_cell)o1, (v4_cell)u1, BUF + (v4_cell)o2, (v4_cell)u2), "SEARCH returns [%u]", t);
|
||||
f = res(); u3 = res(); a3 = res();
|
||||
if (at >= 0)
|
||||
CHECK(f == -1 && u3 == (v4_cell)(c1 - at) && a3 == BUF + (v4_cell)o1 + (v4_cell)at && clean(),
|
||||
"SEARCH found [%u] u1 %ld u2 %ld at %ld", t, u1, u2, at);
|
||||
else
|
||||
CHECK(f == 0 && u3 == (v4_cell)c1 && a3 == BUF + (v4_cell)o1 && clean(),
|
||||
"SEARCH not found [%u] u1 %ld u2 %ld", t, u1, u2);
|
||||
CHECK(img_matches() && !err(), "SEARCH writes nothing [%u]", t);
|
||||
}
|
||||
|
||||
/* What they leave their caller (D-2). */
|
||||
random_img(2);
|
||||
text_at(40, "needle"); text_at(200, "needle"); text_at(64, "tail ");
|
||||
headroom("COUNT", w_count, 1, BUF + 3, 0, 0, 0, 2, 6, 6);
|
||||
headroom("CMOVE", w_cmove, 3, BUF + 3, BUF + 90, 9, 0, 0, 3, 5);
|
||||
headroom("CMOVE>", w_cmoveup, 3, BUF + 3, BUF + 90, 9, 0, 0, 3, 5);
|
||||
headroom("MOVE", w_move, 3, BUF + 3, BUF + 6, 9, 0, 0, 3, 5);
|
||||
headroom("FILL", w_fill, 3, BUF + 3, 9, 65, 0, 0, 3, 5);
|
||||
headroom("BLANK", w_blank, 2, BUF + 3, 9, 0, 0, 0, 3, 5);
|
||||
headroom("-TRAILING", w_trailing, 2, BUF + 64, 8, 0, 0, 2, 4, 6);
|
||||
headroom("COMPARE", w_compare, 4, BUF + 40, 6, BUF + 200, 6, 1, 3, 5);
|
||||
headroom("SEARCH", w_search, 4, BUF + 20, 40, BUF + 200, 6, 3, 2, 5);
|
||||
headroom("SCAN", w_scan, 3, BUF + 64, 8, 32, 0, 2, 4, 5);
|
||||
headroom("SKIP", w_skip, 3, BUF + 68, 4, 32, 0, 2, 4, 5);
|
||||
|
||||
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