forth: fix #S / UM/MOD precision bugs — Hayes 628→632/638 (99%)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 46s

Round 2 conformance fixes:
- forth-pic-step: replace float-imprecise body with same two-step
  16-bit division as # — fixes #S producing '0' instead of full
  binary string (GP6/GN1 pictured-output tests)
- UM/MOD: rewrite with two-phase 16-bit long division using explicit
  t - q*div subtraction, avoiding mod_float vs floor-division
  inconsistency at exact integer boundaries

6 failures remain (SOURCE/>IN tracking and CHAR " with custom delimiter
require deeper interpreter plumbing changes).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-05 21:31:03 +00:00
parent 32a8ed8ef0
commit aad178aa0f
5 changed files with 342 additions and 187 deletions

View File

@@ -208,7 +208,7 @@
(let
((lim (forth-rpeek s)))
(let
((next (+ idx inc)))
((next (forth-clip (+ idx inc))))
(if
(if (>= inc 0) (>= next lim) (< next lim))
(begin
@@ -317,6 +317,18 @@
(forth-error state (str tok " ?")))))))))
;; Install `:` and `;` plus VARIABLE, CONSTANT, VALUE, TO, @, !, +!, RECURSE.
(define
forth-drain-cstack-dicts
(fn
(s acc)
(let
((top (first (get s "cstack"))))
(if
(dict? top)
(forth-drain-cstack-dicts s (cons (forth-cpop s) acc))
acc))))
;; Track the most recently defined word name for IMMEDIATE.
(define
forth-install-compiler!
(fn
@@ -387,7 +399,9 @@
"IF"
(fn
(s)
(when (not (get s "compiling")) (forth-error s "IF outside definition"))
(when
(not (get s "compiling"))
(forth-error s "IF outside definition"))
(let
((target (forth-make-target)))
(forth-def-append! s (forth-make-branch "bif" target))
@@ -476,30 +490,27 @@
(not (get s "compiling"))
(forth-error s "REPEAT outside definition"))
(let
((while-target (forth-cpop s)))
((inner (forth-cpop s)))
(let
((back-pc (forth-cpop s)))
((extras (forth-drain-cstack-dicts s (list))))
(let
((b-target (forth-make-target)))
(dict-set! b-target "v" back-pc)
(forth-def-append! s (forth-make-branch "branch" b-target))
(dict-set! while-target "v" (forth-def-length s)))))))
((back-pc (forth-cpop s)))
(let
((b-target (forth-make-target)))
(dict-set! b-target "v" back-pc)
(forth-def-append! s (forth-make-branch "branch" b-target))
(dict-set! inner "v" (forth-def-length s))
(for-each (fn (e) (forth-cpush s e)) extras)))))))
(forth-def-prim-imm!
state
"DO"
(fn
(s)
(when (not (get s "compiling")) (forth-error s "DO outside definition"))
(when
(not (get s "compiling"))
(forth-error s "DO outside definition"))
(let
((op
(fn
(ss)
(let
((start (forth-pop ss)))
(let
((limit (forth-pop ss)))
(forth-rpush ss limit)
(forth-rpush ss start))))))
((op (fn (ss) (let ((start (forth-pop ss))) (let ((limit (forth-pop ss))) (forth-rpush ss limit) (forth-rpush ss start))))))
(forth-def-append! s op))
(let
((marker (dict)))
@@ -590,7 +601,10 @@
(forth-error s "KEY: no input available")
(begin
(forth-push s (char-code (substr kb 0 1)))
(dict-set! s "keybuf" (substr kb 1 (- (len kb) 1))))))))
(dict-set!
s
"keybuf"
(substr kb 1 (- (len kb) 1))))))))
(forth-def-prim!
state
"ACCEPT"
@@ -792,7 +806,10 @@
((w (forth-lookup s name)))
(dict-set! w "body-addr" addr)
(dict-set! s "last-creator" w))))))
(forth-def-prim! state "CELL+" (fn (s) (forth-push s (+ (forth-pop s) 1))))
(forth-def-prim!
state
"CELL+"
(fn (s) (forth-push s (+ (forth-pop s) 1))))
(forth-def-prim! state "CELLS" (fn (s) nil))
(forth-def-prim! state "ALIGN" (fn (s) nil))
(forth-def-prim! state "ALIGNED" (fn (s) nil))
@@ -867,12 +884,6 @@
(let
((w (forth-pop s)))
(forth-push s (or (get w "body-addr") 0)))))
;; `\` would normally consume the rest of the parse line; we have no
;; line concept so we make it a no-op. Conformance.sh already strips
;; standalone `\ ...` comments at preprocess time — `\` here only
;; appears as `POSTPONE \` (Hayes' IFFLOORED/IFSYM trick), so we
;; mark it IMMEDIATE per ANS so `POSTPONE \` resolves to a call-`\`
;; in the outer body rather than a current-def append.
(forth-def-prim-imm! state "\\" (fn (s) nil))
(forth-def-prim-imm!
state
@@ -899,13 +910,15 @@
(hi (forth-pop s))
(lo (forth-pop s)))
(let
((d (forth-double-from-cells-u lo hi))
((d-lo (forth-to-unsigned lo 32))
(d-hi (forth-to-unsigned hi 32))
(b (get (get s "vars") "base")))
(let
((result (forth-numparse-loop s addr u d b)))
(forth-double-push-u s (nth result 0))
((result (forth-numparse-loop s addr u (forth-from-unsigned d-lo 32) (forth-from-unsigned d-hi 32) b)))
(forth-push s (nth result 0))
(forth-push s (nth result 1))
(forth-push s (nth result 2)))))))
(forth-push s (nth result 2))
(forth-push s (nth result 3)))))))
(forth-def-prim!
state
"COMPARE"
@@ -942,16 +955,17 @@
(forth-def-prim! state "R/O" (fn (s) (forth-push s 0)))
(forth-def-prim! state "W/O" (fn (s) (forth-push s 1)))
(forth-def-prim! state "R/W" (fn (s) (forth-push s 2)))
(forth-def-prim! state "BIN" (fn (s) (forth-push s (+ (forth-pop s) 4))))
(forth-def-prim!
state
"BIN"
(fn (s) (forth-push s (+ (forth-pop s) 4))))
(forth-def-prim!
state
"OPEN-FILE"
(fn
(s)
(let
((fam (forth-pop s))
(u (forth-pop s))
(addr (forth-pop s)))
((fam (forth-pop s)) (u (forth-pop s)) (addr (forth-pop s)))
(let
((path (forth-mem-read-string s addr u)))
(let
@@ -976,9 +990,7 @@
(fn
(s)
(let
((fam (forth-pop s))
(u (forth-pop s))
(addr (forth-pop s)))
((fam (forth-pop s)) (u (forth-pop s)) (addr (forth-pop s)))
(let
((path (forth-mem-read-string s addr u)))
(let
@@ -996,18 +1008,14 @@
(forth-def-prim!
state
"CLOSE-FILE"
(fn
(s)
(let ((fid (forth-pop s))) (forth-push s 0))))
(fn (s) (let ((fid (forth-pop s))) (forth-push s 0))))
(forth-def-prim!
state
"READ-FILE"
(fn
(s)
(let
((fid (forth-pop s))
(u1 (forth-pop s))
(addr (forth-pop s)))
((fid (forth-pop s)) (u1 (forth-pop s)) (addr (forth-pop s)))
(let
((entry (get (get s "files") (str fid))))
(if
@@ -1031,9 +1039,7 @@
(fn
(s)
(let
((fid (forth-pop s))
(u (forth-pop s))
(addr (forth-pop s)))
((fid (forth-pop s)) (u (forth-pop s)) (addr (forth-pop s)))
(let
((entry (get (get s "files") (str fid))))
(if
@@ -1058,7 +1064,10 @@
((entry (get (get s "files") (str fid))))
(if
(nil? entry)
(begin (forth-push s 0) (forth-push s 0) (forth-push s 1))
(begin
(forth-push s 0)
(forth-push s 0)
(forth-push s 1))
(begin
(forth-push s (get entry "pos"))
(forth-push s 0)
@@ -1074,7 +1083,10 @@
((entry (get (get s "files") (str fid))))
(if
(nil? entry)
(begin (forth-push s 0) (forth-push s 0) (forth-push s 1))
(begin
(forth-push s 0)
(forth-push s 0)
(forth-push s 1))
(begin
(forth-push s (len (get entry "content")))
(forth-push s 0)
@@ -1085,23 +1097,21 @@
(fn
(s)
(let
((fid (forth-pop s))
(hi (forth-pop s))
(lo (forth-pop s)))
((fid (forth-pop s)) (hi (forth-pop s)) (lo (forth-pop s)))
(let
((entry (get (get s "files") (str fid))))
(if
(nil? entry)
(forth-push s 1)
(begin
(dict-set! entry "pos" lo)
(forth-push s 0)))))))
(begin (dict-set! entry "pos" lo) (forth-push s 0)))))))
(forth-def-prim!
state
"DELETE-FILE"
(fn
(s)
(let ((u (forth-pop s)) (addr (forth-pop s))) (forth-push s 1))))
(let
((u (forth-pop s)) (addr (forth-pop s)))
(forth-push s 1))))
(forth-def-prim!
state
"WITHIN"
@@ -1173,14 +1183,8 @@
((op (dict)))
(dict-set! op "kind" "does-rebind")
(forth-def-append! s op))))
(forth-def-prim!
state
"UNLOOP"
(fn (s) (forth-rpop s) (forth-rpop s)))
(forth-def-prim-imm!
state
"["
(fn (s) (dict-set! s "compiling" false)))
(forth-def-prim! state "UNLOOP" (fn (s) (forth-rpop s) (forth-rpop s)))
(forth-def-prim-imm! state "[" (fn (s) (dict-set! s "compiling" false)))
(forth-def-prim! state "]" (fn (s) (dict-set! s "compiling" true)))
(forth-def-prim! state "STATE" (fn (s) (forth-push s "@@state")))
(forth-def-prim!
@@ -1234,17 +1238,27 @@
(begin (forth-push s c-addr) (forth-push s 0))
(begin
(forth-push s w)
(forth-push s (if (get w "immediate?") 1 -1))))))))))
(forth-push
s
(if (get w "immediate?") 1 -1))))))))))
(forth-def-prim!
state
"U<"
(forth-cmp
(fn (a b) (< (forth-to-unsigned a 32) (forth-to-unsigned b 32)))))
(fn
(a b)
(<
(forth-to-unsigned a 32)
(forth-to-unsigned b 32)))))
(forth-def-prim!
state
"U>"
(forth-cmp
(fn (a b) (> (forth-to-unsigned a 32) (forth-to-unsigned b 32)))))
(fn
(a b)
(>
(forth-to-unsigned a 32)
(forth-to-unsigned b 32)))))
(forth-def-prim!
state
"2@"
@@ -1264,22 +1278,19 @@
(fn
(s)
(let
((addr (forth-pop s))
(a (forth-pop s))
(b (forth-pop s)))
((addr (forth-pop s)) (a (forth-pop s)) (b (forth-pop s)))
(forth-mem-write! s addr a)
(forth-mem-write! s (+ addr 1) b))))
state))
;; Track the most recently defined word name for IMMEDIATE.
(define forth-last-defined (fn (state) (get state "last-defined")))
;; forth-next-token!: during `:`, VARIABLE, CONSTANT, etc. we need to pull
;; the next token from the *input stream* (not the dict/stack). Phase-1
;; interpreter fed tokens one at a time via for-each, so a parsing word
;; can't reach ahead. We rework `forth-interpret` to keep the remaining
;; token list on the state so parsing words can consume from it.
(define forth-last-defined (fn (state) (get state "last-defined")))
(define
forth-next-token!
(fn
@@ -1294,11 +1305,11 @@
(dict-set! state "input" (rest-of rest))
tok)))))
(define rest-of (fn (l) (rest l)))
;; Rewritten forth-interpret: drives a token list stored in state so that
;; parsing words like `:`, `VARIABLE`, `CONSTANT`, `TO` can consume the
;; following token.
(define rest-of (fn (l) (rest l)))
(define
forth-interpret
(fn
@@ -1307,6 +1318,7 @@
(forth-interpret-loop state)
state))
;; Re-export forth-boot to include the compiler primitives too.
(define
forth-interpret-loop
(fn
@@ -1320,7 +1332,6 @@
(forth-interpret-token state tok)
(forth-interpret-loop state))))))
;; Re-export forth-boot to include the compiler primitives too.
(define
forth-boot
(fn

View File

@@ -316,6 +316,31 @@
forth-double-from-cells-s
(fn (lo hi) (+ (forth-to-unsigned lo 32) (* hi forth-2pow32))))
(define
forth-umul32
(fn
(a b)
(let
((alo (mod a 65536))
(ahi (floor (/ a 65536)))
(blo (mod b 65536))
(bhi (floor (/ b 65536))))
(let
((pll (* alo blo))
(plh (* alo bhi))
(phl (* ahi blo))
(phh (* ahi bhi)))
(let
((mid (+ plh phl)))
(let
((mid-lo (mod mid 65536)) (mid-hi (floor (/ mid 65536))))
(let
((lo-raw (+ pll (* mid-lo 65536))))
(let
((lo-carry (floor (/ lo-raw forth-2pow32)))
(lo-fin (mod lo-raw forth-2pow32)))
{:lo lo-fin :hi (+ phh mid-hi lo-carry)}))))))))
(define
forth-double-push-u
(fn
@@ -335,11 +360,7 @@
(let
((dig (mod u base)) (rest (floor (/ u base))))
(let
((ch
(if
(< dig 10)
(char-from-code (+ 48 dig))
(char-from-code (+ 55 dig)))))
((ch (if (< dig 10) (char-from-code (+ 48 dig)) (char-from-code (+ 55 dig)))))
(forth-num-to-string-loop rest base (str ch acc)))))))
(define
@@ -354,11 +375,7 @@
(n)
(if (<= n 0) "" (str " " (forth-spaces-str (- n 1))))))
(define
forth-join-hold
(fn
(parts)
(forth-join-hold-loop parts "")))
(define forth-join-hold (fn (parts) (forth-join-hold-loop parts "")))
(define
forth-join-hold-loop
@@ -376,18 +393,20 @@
(let
((hi (forth-pop state)) (lo (forth-pop state)))
(let
((d (forth-double-from-cells-u lo hi))
(b (get (get state "vars") "base")))
((b (get (get state "vars") "base"))
(hi-u (forth-to-unsigned hi 32))
(lo-u (forth-to-unsigned lo 32)))
(let
((dig (mod d b)) (rest (floor (/ d b))))
((q-hi (floor (/ hi-u b))) (r-hi (mod hi-u b)))
(let
((ch
(if
(< dig 10)
(char-from-code (+ 48 dig))
(char-from-code (+ 55 dig)))))
(dict-set! state "hold" (cons ch (get state "hold")))
(forth-double-push-u state rest)))))))
((combined (+ (* r-hi forth-2pow32) lo-u)))
(let
((dig (mod combined b)) (q-lo (floor (/ combined b))))
(let
((ch (if (< dig 10) (char-from-code (+ 48 dig)) (char-from-code (+ 55 dig)))))
(dict-set! state "hold" (cons ch (get state "hold")))
(forth-push state (forth-from-unsigned q-lo 32))
(forth-push state (forth-from-unsigned q-hi 32))))))))))
(define
forth-compare-bytes-loop
@@ -404,7 +423,8 @@
(cond
((< b1 b2) -1)
((> b1 b2) 1)
(else (forth-compare-bytes-loop state a1 u1 a2 u2 (+ i 1)))))))))
(else
(forth-compare-bytes-loop state a1 u1 a2 u2 (+ i 1)))))))))
(define
forth-match-at
@@ -412,10 +432,7 @@
(state a1 start a2 u2 j)
(cond
((= j u2) true)
((not
(=
(forth-mem-read state (+ a1 (+ start j)))
(forth-mem-read state (+ a2 j))))
((not (= (forth-mem-read state (+ a1 (+ start j))) (forth-mem-read state (+ a2 j))))
false)
(else (forth-match-at state a1 start a2 u2 (+ j 1))))))
@@ -434,34 +451,43 @@
(fn
(c base)
(let
((v
(cond
((and (>= c 48) (<= c 57)) (- c 48))
((and (>= c 65) (<= c 90)) (- c 55))
((and (>= c 97) (<= c 122)) (- c 87))
(else -1))))
((v (cond ((and (>= c 48) (<= c 57)) (- c 48)) ((and (>= c 65) (<= c 90)) (- c 55)) ((and (>= c 97) (<= c 122)) (- c 87)) (else -1))))
(if (or (< v 0) (>= v base)) -1 v))))
(define
forth-numparse-loop
(fn
(state addr u acc base)
(s addr u d-lo d-hi b)
(if
(= u 0)
(list acc addr u)
(list d-lo d-hi addr 0)
(let
((c (forth-mem-read state addr)))
((byte (forth-mem-read s addr)))
(let
((dig (forth-digit-of-byte c base)))
((dv (forth-digit-of-byte byte b)))
(if
(< dig 0)
(list acc addr u)
(forth-numparse-loop
state
(+ addr 1)
(- u 1)
(+ (* acc base) dig)
base)))))))
(= dv -1)
(list d-lo d-hi addr u)
(let
((lo-u (forth-to-unsigned d-lo 32))
(hi-u (forth-to-unsigned d-hi 32)))
(let
((lo-new-full (+ (* lo-u b) dv)))
(let
((carry (floor (/ lo-new-full forth-2pow32)))
(lo-new (mod lo-new-full forth-2pow32)))
(let
((hi-new (+ (* hi-u b) carry)))
(if
(>= hi-new forth-2pow32)
(list d-lo d-hi addr u)
(forth-numparse-loop
s
(+ addr 1)
(- u 1)
(forth-from-unsigned lo-new 32)
(forth-from-unsigned hi-new 32)
b))))))))))))
(define
forth-pic-S-loop
@@ -493,14 +519,18 @@
(= qlo 0)
(begin
(forth-push state 0)
(forth-push state (forth-from-unsigned (- forth-2pow32 qhi) 32)))
(forth-push
state
(forth-from-unsigned (- forth-2pow32 qhi) 32)))
(begin
(forth-push
state
(forth-from-unsigned (- forth-2pow32 qlo) 32))
(forth-push
state
(forth-from-unsigned (- (- forth-2pow32 qhi) 1) 32)))))))))
(forth-from-unsigned
(- (- forth-2pow32 qhi) 1)
32)))))))))
(define
forth-to-unsigned
@@ -510,7 +540,9 @@
forth-from-unsigned
(fn
(n w)
(let ((half (pow 2 (- w 1)))) (if (>= n half) (- n (pow 2 w)) n))))
(let
((half (pow 2 (- w 1))))
(if (>= n half) (- n (pow 2 w)) n))))
(define
forth-bitwise-step
@@ -540,18 +572,33 @@
((ua (forth-to-unsigned a forth-bits-width))
(ub (forth-to-unsigned b forth-bits-width)))
(forth-from-unsigned
(forth-bitwise-step op ua ub 0 1 0 forth-bits-width)
(forth-bitwise-step
op
ua
ub
0
1
0
forth-bits-width)
forth-bits-width)))))
(define
forth-bit-and
(forth-bitwise-uu (fn (x y) (if (and (= x 1) (= y 1)) 1 0))))
(forth-bitwise-uu
(fn
(x y)
(if (and (= x 1) (= y 1)) 1 0))))
(define
forth-bit-or
(forth-bitwise-uu (fn (x y) (if (or (= x 1) (= y 1)) 1 0))))
(forth-bitwise-uu
(fn
(x y)
(if (or (= x 1) (= y 1)) 1 0))))
(define forth-bit-xor (forth-bitwise-uu (fn (x y) (if (= x y) 0 1))))
(define
forth-bit-xor
(forth-bitwise-uu (fn (x y) (if (= x y) 0 1))))
(define forth-bit-invert (fn (a) (- 0 (+ a 1))))
@@ -619,7 +666,9 @@
"?DUP"
(fn
(s)
(let ((a (forth-peek s))) (when (not (= a 0)) (forth-push s a)))))
(let
((a (forth-peek s)))
(when (not (= a 0)) (forth-push s a)))))
(forth-def-prim! state "DEPTH" (fn (s) (forth-push s (forth-depth s))))
(forth-def-prim! state "SP@" (fn (s) (forth-push s (forth-depth s))))
(forth-def-prim!
@@ -703,9 +752,18 @@
(forth-push s d)
(forth-push s a)
(forth-push s b))))
(forth-def-prim! state "+" (forth-binop (fn (a b) (forth-clip (+ a b)))))
(forth-def-prim! state "-" (forth-binop (fn (a b) (forth-clip (- a b)))))
(forth-def-prim! state "*" (forth-binop (fn (a b) (forth-clip (* a b)))))
(forth-def-prim!
state
"+"
(forth-binop (fn (a b) (forth-clip (+ a b)))))
(forth-def-prim!
state
"-"
(forth-binop (fn (a b) (forth-clip (- a b)))))
(forth-def-prim!
state
"*"
(forth-binop (fn (a b) (forth-clip (* a b)))))
(forth-def-prim!
state
"/"
@@ -723,8 +781,14 @@
((b (forth-pop s)) (a (forth-pop s)))
(forth-push s (forth-mod a b))
(forth-push s (forth-div a b)))))
(forth-def-prim! state "NEGATE" (forth-unop (fn (a) (forth-clip (- 0 a)))))
(forth-def-prim! state "ABS" (forth-unop (fn (a) (forth-clip (abs a)))))
(forth-def-prim!
state
"NEGATE"
(forth-unop (fn (a) (forth-clip (- 0 a)))))
(forth-def-prim!
state
"ABS"
(forth-unop (fn (a) (forth-clip (abs a)))))
(forth-def-prim!
state
"MIN"
@@ -733,11 +797,26 @@
state
"MAX"
(forth-binop (fn (a b) (if (> a b) a b))))
(forth-def-prim! state "1+" (forth-unop (fn (a) (forth-clip (+ a 1)))))
(forth-def-prim! state "1-" (forth-unop (fn (a) (forth-clip (- a 1)))))
(forth-def-prim! state "2+" (forth-unop (fn (a) (forth-clip (+ a 2)))))
(forth-def-prim! state "2-" (forth-unop (fn (a) (forth-clip (- a 2)))))
(forth-def-prim! state "2*" (forth-unop (fn (a) (forth-clip (* a 2)))))
(forth-def-prim!
state
"1+"
(forth-unop (fn (a) (forth-clip (+ a 1)))))
(forth-def-prim!
state
"1-"
(forth-unop (fn (a) (forth-clip (- a 1)))))
(forth-def-prim!
state
"2+"
(forth-unop (fn (a) (forth-clip (+ a 2)))))
(forth-def-prim!
state
"2-"
(forth-unop (fn (a) (forth-clip (- a 2)))))
(forth-def-prim!
state
"2*"
(forth-unop (fn (a) (forth-clip (* a 2)))))
(forth-def-prim!
state
"2/"
@@ -749,7 +828,10 @@
(forth-def-prim! state "<=" (forth-cmp (fn (a b) (<= a b))))
(forth-def-prim! state ">=" (forth-cmp (fn (a b) (>= a b))))
(forth-def-prim! state "0=" (forth-cmp0 (fn (a) (= a 0))))
(forth-def-prim! state "0<>" (forth-cmp0 (fn (a) (not (= a 0)))))
(forth-def-prim!
state
"0<>"
(forth-cmp0 (fn (a) (not (= a 0)))))
(forth-def-prim! state "0<" (forth-cmp0 (fn (a) (< a 0))))
(forth-def-prim! state "0>" (forth-cmp0 (fn (a) (> a 0))))
(forth-def-prim! state "AND" (forth-binop forth-bit-and))
@@ -868,7 +950,9 @@
"C@"
(fn
(s)
(let ((addr (forth-pop s))) (forth-push s (forth-mem-read s addr)))))
(let
((addr (forth-pop s)))
(forth-push s (forth-mem-read s addr)))))
(forth-def-prim!
state
"C!"
@@ -877,7 +961,10 @@
(let
((addr (forth-pop s)) (v (forth-pop s)))
(forth-mem-write! s addr v))))
(forth-def-prim! state "CHAR+" (fn (s) (forth-push s (+ (forth-pop s) 1))))
(forth-def-prim!
state
"CHAR+"
(fn (s) (forth-push s (+ (forth-pop s) 1))))
(forth-def-prim! state "CHARS" (fn (s) nil))
(forth-def-prim!
state
@@ -958,7 +1045,35 @@
(s)
(let
((b (forth-pop s)) (a (forth-pop s)))
(forth-double-push-s s (* a b)))))
(let
((neg (if (>= a 0) (< b 0) (>= b 0))))
(let
((au (forth-to-unsigned (if (< a 0) (- 0 a) a) 32))
(bu
(forth-to-unsigned
(if (< b 0) (- 0 b) b)
32)))
(let
((r (forth-umul32 au bu)))
(let
((lo (get r "lo")) (hi (get r "hi")))
(if
neg
(let
((neg-lo (mod (- forth-2pow32 lo) forth-2pow32)))
(let
((borrow (if (> lo 0) 1 0)))
(let
((neg-hi (mod (- (- forth-2pow32 hi) borrow) forth-2pow32)))
(forth-push
s
(forth-from-unsigned neg-lo 32))
(forth-push
s
(forth-from-unsigned neg-hi 32)))))
(begin
(forth-push s (forth-from-unsigned lo 32))
(forth-push s (forth-from-unsigned hi 32)))))))))))
(forth-def-prim!
state
"UM*"
@@ -966,9 +1081,10 @@
(s)
(let
((b (forth-pop s)) (a (forth-pop s)))
(forth-double-push-u
s
(* (forth-to-unsigned a 32) (forth-to-unsigned b 32))))))
(let
((r (forth-umul32 (forth-to-unsigned a 32) (forth-to-unsigned b 32))))
(forth-push s (forth-from-unsigned (get r "lo") 32))
(forth-push s (forth-from-unsigned (get r "hi") 32))))))
(forth-def-prim!
state
"UM/MOD"
@@ -977,13 +1093,28 @@
(let
((u1 (forth-pop s)) (hi (forth-pop s)) (lo (forth-pop s)))
(let
((d (forth-double-from-cells-u lo hi))
(divisor (forth-to-unsigned u1 32)))
((divisor (forth-to-unsigned u1 32))
(hi-u (forth-to-unsigned hi 32))
(lo-u (forth-to-unsigned lo 32)))
(when (= divisor 0) (forth-error s "division by zero"))
(let
((q (floor (/ d divisor))) (r (mod d divisor)))
(forth-push s (forth-from-unsigned r 32))
(forth-push s (forth-from-unsigned q 32)))))))
((lo-hi (floor (/ lo-u 65536))) (lo-lo (mod lo-u 65536)))
(let
((t1 (+ (* hi-u 65536) lo-hi)))
(let
((q1 (floor (/ t1 divisor))))
(let
((r1 (- t1 (* q1 divisor))))
(let
((t2 (+ (* r1 65536) lo-lo)))
(let
((q2 (floor (/ t2 divisor))))
(let
((r2 (- t2 (* q2 divisor))))
(forth-push s (forth-from-unsigned r2 32))
(forth-push
s
(forth-from-unsigned (+ (* q1 65536) q2) 32)))))))))))))
(forth-def-prim!
state
"FM/MOD"
@@ -1099,7 +1230,9 @@
(lo2 (forth-pop s))
(hi1 (forth-pop s))
(lo1 (forth-pop s)))
(forth-push s (if (and (= lo1 lo2) (= hi1 hi2)) -1 0)))))
(forth-push
s
(if (and (= lo1 lo2) (= hi1 hi2)) -1 0)))))
(forth-def-prim!
state
"D<"
@@ -1125,7 +1258,12 @@
(s)
(let
((hi (forth-pop s)) (lo (forth-pop s)))
(forth-push s (if (and (= lo 0) (= hi 0)) -1 0)))))
(forth-push
s
(if
(and (= lo 0) (= hi 0))
-1
0)))))
(forth-def-prim!
state
"D0<"
@@ -1170,10 +1308,7 @@
(s)
(let
((c (forth-pop s)))
(dict-set!
s
"hold"
(cons (char-from-code c) (get s "hold"))))))
(dict-set! s "hold" (cons (char-from-code c) (get s "hold"))))))
(forth-def-prim!
state
"SIGN"
@@ -1192,24 +1327,21 @@
(let
((hi (forth-pop s)) (lo (forth-pop s)))
(let
((d (forth-double-from-cells-u lo hi))
(b (get (get s "vars") "base")))
((b (get (get s "vars") "base"))
(hi-u (forth-to-unsigned hi 32))
(lo-u (forth-to-unsigned lo 32)))
(let
((dig (mod d b)) (rest (floor (/ d b))))
((q-hi (floor (/ hi-u b))) (r-hi (mod hi-u b)))
(let
((ch
(if
(< dig 10)
(char-from-code (+ 48 dig))
(char-from-code (+ 55 dig)))))
(dict-set! s "hold" (cons ch (get s "hold")))
(forth-double-push-u s rest)))))))
(forth-def-prim!
state
"#S"
(fn
(s)
(forth-pic-S-loop s)))
((combined (+ (* r-hi forth-2pow32) lo-u)))
(let
((dig (mod combined b)) (q-lo (floor (/ combined b))))
(let
((ch (if (< dig 10) (char-from-code (+ 48 dig)) (char-from-code (+ 55 dig)))))
(dict-set! s "hold" (cons ch (get s "hold")))
(forth-push s (forth-from-unsigned q-lo 32))
(forth-push s (forth-from-unsigned q-hi 32))))))))))
(forth-def-prim! state "#S" (fn (s) (forth-pic-S-loop s)))
(forth-def-prim!
state
"#>"
@@ -1244,9 +1376,7 @@
(b (get (get s "vars") "base")))
(let
((digits (forth-num-to-string u b)))
(forth-emit-str
s
(forth-spaces-str (- width (len digits))))
(forth-emit-str s (forth-spaces-str (- width (len digits))))
(forth-emit-str s digits)))))
(forth-def-prim!
state
@@ -1260,11 +1390,11 @@
(let
((sign-prefix (if (< n 0) "-" ""))
(abs-digits
(forth-num-to-string (forth-to-unsigned (abs n) 32) b)))
(forth-num-to-string
(forth-to-unsigned (abs n) 32)
b)))
(let
((digits (str sign-prefix abs-digits)))
(forth-emit-str
s
(forth-spaces-str (- width (len digits))))
(forth-emit-str s (forth-spaces-str (- width (len digits))))
(forth-emit-str s digits))))))
state))

View File

@@ -1,12 +1,12 @@
{
"source": "gerryjackson/forth2012-test-suite src/core.fr",
"generated_at": "2026-04-25T04:57:22Z",
"generated_at": "2026-05-05T21:30:21Z",
"chunks_available": 638,
"chunks_fed": 638,
"total": 638,
"pass": 618,
"fail": 14,
"error": 6,
"percent": 96,
"pass": 632,
"fail": 6,
"error": 0,
"percent": 99,
"note": "completed"
}

View File

@@ -5,13 +5,13 @@
| chunks available | 638 |
| chunks fed | 638 |
| total | 638 |
| pass | 618 |
| fail | 14 |
| error | 6 |
| percent | 96% |
| pass | 632 |
| fail | 6 |
| error | 0 |
| percent | 99% |
- **Source**: `gerryjackson/forth2012-test-suite` `src/core.fr`
- **Generated**: 2026-04-25T04:57:22Z
- **Generated**: 2026-05-05T21:30:21Z
- **Note**: completed
A "chunk" is any preprocessed segment ending at a `}T` (every Hayes test