;; lib/forth/runtime.sx — Forth primitives on SX
;;
;; Provides Forth-idiomatic wrappers over SX built-ins.
;; Primitives used:
;;   bitwise-and/or/xor/not/arithmetic-shift/bit-count (Phase 7)
;;   make-bytevector/bytevector-u8-ref/u8-set!/...     (Phase 20)
;;   quotient/remainder/modulo                          (Phase 15 / builtin)
;;
;; Naming: SX identifiers can't include @ or !-alone, so Forth words are:
;;   C@ → forth-cfetch    C! → forth-cstore
;;   @  → forth-fetch     !  → forth-store

;; ---------------------------------------------------------------------------
;; 1. Bitwise operations — Forth core words
;;    Forth TRUE = -1 (all bits set), FALSE = 0.
;;    All ops coerce to integer via truncate.
;; ---------------------------------------------------------------------------

(define (forth-and a b) (bitwise-and (truncate a) (truncate b)))
(define (forth-or a b) (bitwise-or (truncate a) (truncate b)))
(define (forth-xor a b) (bitwise-xor (truncate a) (truncate b)))

;; INVERT — bitwise NOT (Forth NOT is logical; INVERT is bitwise)
(define (forth-invert a) (bitwise-not (truncate a)))

;; LSHIFT RSHIFT — n bit — shift a by n positions
(define (forth-lshift a n) (arithmetic-shift (truncate a) (truncate n)))
(define
  (forth-rshift a n)
  (arithmetic-shift (truncate a) (- 0 (truncate n))))

;; 2* 2/ — multiply/divide by 2 via bit shift
(define (forth-2* a) (arithmetic-shift (truncate a) 1))
(define (forth-2/ a) (arithmetic-shift (truncate a) -1))

;; BIT-COUNT — number of set bits (Kernighan popcount)
(define (forth-bit-count a) (bit-count (truncate a)))

;; INTEGER-LENGTH — index of highest set bit (0 for zero)
(define (forth-integer-length a) (integer-length (truncate a)))

;; WITHIN — ( u ul uh -- flag ) true if ul <= u < uh
(define (forth-within u ul uh) (and (>= u ul) (< u uh)))

;; Arithmetic complements commonly used alongside bitwise ops
(define (forth-negate a) (- 0 (truncate a)))
(define (forth-abs a) (abs (truncate a)))
(define (forth-min a b) (if (< a b) a b))
(define (forth-max a b) (if (> a b) a b))
(define (forth-mod a b) (modulo (truncate a) (truncate b)))

;; /MOD — ( n1 n2 -- rem quot ) returns list (remainder quotient)
(define
  (forth-divmod a b)
  (list
    (remainder (truncate a) (truncate b))
    (quotient (truncate a) (truncate b))))

;; ---------------------------------------------------------------------------
;; 2. String buffer — word-definition / string accumulation
;;    EMIT appends one char; TYPE appends a string.
;;    Value is retrieved with forth-sb-value.
;; ---------------------------------------------------------------------------

(define
  (forth-sb-new)
  (let
    ((sb (dict)))
    (dict-set! sb "_forth_sb" true)
    (dict-set! sb "_chars" (list))
    sb))

(define (forth-sb? v) (and (dict? v) (dict-has? v "_forth_sb")))

;; EMIT — append one character
(define
  (forth-sb-emit! sb c)
  (dict-set! sb "_chars" (append (get sb "_chars") (list c)))
  sb)

;; TYPE — append a string
(define
  (forth-sb-type! sb s)
  (dict-set! sb "_chars" (append (get sb "_chars") (string->list s)))
  sb)

(define (forth-sb-value sb) (list->string (get sb "_chars")))

(define (forth-sb-length sb) (len (get sb "_chars")))

(define (forth-sb-clear! sb) (dict-set! sb "_chars" (list)) sb)

;; Emit integer as decimal digits
(define (forth-sb-emit-int! sb n) (forth-sb-type! sb (str (truncate n))))

;; ---------------------------------------------------------------------------
;; 3. Memory / Bytevectors — Forth raw memory model
;;    ALLOT allocates a bytevector. Byte and cell (32-bit LE) access.
;; ---------------------------------------------------------------------------

;; ALLOT — allocate n bytes zero-initialised
(define (forth-mem-new n) (make-bytevector (truncate n) 0))

(define (forth-mem? v) (bytevector? v))

(define (forth-mem-size v) (bytevector-length v))

;; C@ C! — byte fetch/store
(define (forth-cfetch mem addr) (bytevector-u8-ref mem (truncate addr)))

(define
  (forth-cstore mem addr val)
  (bytevector-u8-set!
    mem
    (truncate addr)
    (modulo (truncate val) 256))
  mem)

;; @ ! — 32-bit little-endian cell fetch/store
(define
  (forth-fetch mem addr)
  (let
    ((a (truncate addr)))
    (+
      (bytevector-u8-ref mem a)
      (* 256 (bytevector-u8-ref mem (+ a 1)))
      (* 65536 (bytevector-u8-ref mem (+ a 2)))
      (* 16777216 (bytevector-u8-ref mem (+ a 3))))))

(define
  (forth-store mem addr val)
  (let
    ((a (truncate addr)) (v (truncate val)))
    (bytevector-u8-set! mem a (modulo v 256))
    (bytevector-u8-set!
      mem
      (+ a 1)
      (modulo (quotient v 256) 256))
    (bytevector-u8-set!
      mem
      (+ a 2)
      (modulo (quotient v 65536) 256))
    (bytevector-u8-set!
      mem
      (+ a 3)
      (modulo (quotient v 16777216) 256)))
  mem)

;; MOVE — copy count bytes from src[src-addr] to dst[dst-addr]
(define
  (forth-move! src src-addr dst dst-addr count)
  (letrec
    ((go (fn (i) (when (< i (truncate count)) (bytevector-u8-set! dst (+ (truncate dst-addr) i) (bytevector-u8-ref src (+ (truncate src-addr) i))) (go (+ i 1))))))
    (go 0))
  dst)

;; FILL — fill count bytes at addr with byte value
(define
  (forth-fill! mem addr count byte)
  (letrec
    ((go (fn (i) (when (< i (truncate count)) (bytevector-u8-set! mem (+ (truncate addr) i) (modulo (truncate byte) 256)) (go (+ i 1))))))
    (go 0))
  mem)

;; ERASE — fill with zeros (Forth: ERASE)
(define
  (forth-erase! mem addr count)
  (forth-fill! mem addr count 0))

;; Dump memory region as list of byte values
(define
  (forth-mem->list mem addr count)
  (letrec
    ((go (fn (i acc) (if (= i 0) acc (go (- i 1) (cons (bytevector-u8-ref mem (+ (truncate addr) (- i 1))) acc))))))
    (go (truncate count) (list))))
