waforth/src/waforth.wat
2018-06-01 22:26:45 +02:00

1770 lines
62 KiB
Text

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Assembler Macros
;;
;; This is not part of the WebAssembly spec, but uses some custom assembler
;; infrastructure.
;;
;; Although you can go crazy wild with macro programming, I tried to keep this
;; as simple as possible.
;;
;; Variables and functions in the WebAssembly module definition starting with
;; ! are processed by the assembler, and defined in this section.
;; The assembler also fixes the order of "table" in the module (which needs to come
;; before "elem"s, but due to our assembly macros building up the table need to come
;; last in our definition.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(require "tools/assembler.rkt")
(define (char-index cs char pos)
(cond ((null? cs) #f)
((char=? char (car cs)) pos)
(else (char-index (cdr cs) char (add1 pos)))))
(define !baseBase #x100)
(define !stateBase #x104)
(define !wordBase #x200)
;; Compiled modules are limited to 4096 bytes until Chrome refuses to load
;; them synchronously
(define !moduleHeaderBase #x1000)
(define !preludeDataBase #x2000)
(define !returnStackBase #x4000)
(define !stackBase #x10000)
(define !dictionaryBase #x20000)
(define !memorySize (* 100 1024 1024))
(define !moduleHeader
(string-append
"\u0000\u0061\u0073\u006D" ;; Header
"\u0001\u0000\u0000\u0000" ;; Version
"\u0001" "\u0011" ;; Type section
"\u0004" ;; #Entries
"\u0060\u0000\u0000" ;; (func)
"\u0060\u0001\u007F\u0000" ;; (func (param i32))
"\u0060\u0000\u0001\u007F" ;; (func (result i32))
"\u0060\u0001\u007f\u0001\u007F" ;; (func (param i32) (result i32))
"\u0002" "\u0039" ;; Import section
"\u0004" ;; #Entries
"\u0003\u0065\u006E\u0076" "\u0005\u0074\u0061\u0062\u006C\u0065" ;; 'env' . 'table'
"\u0001" "\u0070" "\u0000" "\u0004" ;; table, anyfunc, flags, initial size
"\u0003\u0065\u006E\u0076" "\u0009\u0074\u0061\u0062\u006C\u0065\u0042\u0061\u0073\u0065" ;; 'env' . 'tableBase
"\u0003" "\u007F" "\u0000" ;; global, i32, immutable
"\u0003\u0065\u006E\u0076" "\u0006\u006d\u0065\u006d\u006f\u0072\u0079" ;; 'env' . 'memory'
"\u0002" "\u0000" "\u0001" ;; memory
"\u0003\u0065\u006E\u0076" "\u0003\u0074\u006f\u0073" ;; 'env' . 'tos'
"\u0003" "\u007F" "\u0000" ;; global, i32, immutable
"\u0003" "\u0002" ;; Function section
"\u0001" ;; #Entries
"\u0001" ;; Type 0
"\u0009" "\u0007" ;; Element section
"\u0001" ;; #Entries
"\u0000" ;; Table 0
"\u0023\u0000\u000B" ;; get_global 0, end
"\u0001" ;; #elements
"\u0000" ;; function 0
"\u000A" "\u00FF\u0000\u0000\u0000" ;; Code section (padded length)
"\u0001" ;; #Bodies
"\u00FE\u0000\u0000\u0000" ;; Body size (padded)
"\u0001" ;; #locals
"\u00FD\u0000\u0000\u0000\u007F")) ;; # #i32 locals (padded)
(define !moduleHeaderSize (string-length !moduleHeader))
(define !moduleHeaderCodeSizeOffset (char-index (string->list !moduleHeader) #\u00FF 0))
(define !moduleHeaderBodySizeOffset (char-index (string->list !moduleHeader) #\u00FE 0))
(define !moduleHeaderLocalCountOffset (char-index (string->list !moduleHeader) #\u00FD 0))
(define !moduleBodyBase (+ !moduleHeaderBase !moduleHeaderSize))
(define !moduleHeaderCodeSizeBase (+ !moduleHeaderBase !moduleHeaderCodeSizeOffset))
(define !moduleHeaderBodySizeBase (+ !moduleHeaderBase !moduleHeaderBodySizeOffset))
(define !moduleHeaderLocalCountBase (+ !moduleHeaderBase !moduleHeaderLocalCountOffset))
(define !fNone #x0)
(define !fImmediate #x80)
(define !fHidden #x20)
(define !lengthMask #x1F)
;; Predefined table indices
(define !pushIndex 1)
(define !popIndex 2)
(define !typeIndex 3)
(define !pushDataAddressIndex 4)
(define !pushDataValueIndex 5)
(define !tableStartIndex 6)
(define !dictionaryLatest 0)
(define !dictionaryTop !dictionaryBase)
(define (!def_word name f (flags 0) (idx !tableStartIndex))
(let* ((base !dictionaryTop)
(previous !dictionaryLatest)
(name-entry-length (* (ceiling (/ (+ (string-length name) 1) 4)) 4))
(size (+ 8 name-entry-length)))
(cond ((= idx !tableStartIndex)
(set! !tableStartIndex (+ !tableStartIndex 1))
(set! !dictionaryLatest !dictionaryTop)))
(set! !dictionaryTop (+ !dictionaryTop size))
`((elem (i32.const ,(eval idx)) ,(string->symbol f))
(data
(i32.const ,(eval base))
,(integer->integer-bytes previous 4 #f #f)
,(integer->integer-bytes (bitwise-ior (string-length name) flags) 1 #f #f)
,(eval name)
,(make-bytes (- name-entry-length (string-length name) 1) 0)
,(integer->integer-bytes idx 4 #f #f)))))
(define (!+ x y) (list (+ x y)))
(define (!/ x y) (list (ceiling (/ x y))))
(define !preludeData "")
(define (!prelude c)
(set! !preludeData
(regexp-replace* #px"[ ]?\n[ ]?"
(regexp-replace* #px"[ ]+"
(regexp-replace* #px"[\n]+" (string-append !preludeData "\n" c) "\n")
" ")
"\n"))
(list))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; WebAssembly module definition
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(module
(import "shell" "emit" (func $shell_emit (param i32)))
(import "shell" "key" (func $shell_key (result i32)))
(import "shell" "load" (func $shell_load (param i32 i32 i32)))
(import "shell" "debug" (func $shell_debug (param i32)))
(memory (export "memory") (!/ !memorySize 65536))
(type $word (func (param i32)))
(global $tos (mut i32) (i32.const !stackBase))
(global $tors (mut i32) (i32.const !returnStackBase))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Built-in words
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 6.1.0010 !
(func $! (param i32)
(local $bbtos i32)
(i32.store (i32.load (i32.sub (get_global $tos) (i32.const 4)))
(i32.load (tee_local $bbtos (i32.sub (get_global $tos) (i32.const 8)))))
(set_global $tos (get_local $bbtos)))
(!def_word "!" "$!")
;; 6.1.0070
(func $tick (param i32)
(call $word (i32.const -1))
(if (i32.eqz (i32.load (i32.const !wordBase))) (then (unreachable)))
(call $find (i32.const -1))
(drop (call $pop)))
(!def_word "'" "$tick")
;; 6.1.0090
(func $star (param i32)
(local $btos i32)
(local $bbtos i32)
(i32.store (tee_local $bbtos (i32.sub (get_global $tos) (i32.const 8)))
(i32.mul (i32.load (tee_local $btos (i32.sub (get_global $tos) (i32.const 4))))
(i32.load (get_local $bbtos))))
(set_global $tos (get_local $btos)))
(!def_word "*" "$star")
;; 6.1.0120
(func $plus (param i32)
(local $btos i32)
(local $bbtos i32)
(i32.store (tee_local $bbtos (i32.sub (get_global $tos) (i32.const 8)))
(i32.add (i32.load (tee_local $btos (i32.sub (get_global $tos) (i32.const 4))))
(i32.load (get_local $bbtos))))
(set_global $tos (get_local $btos)))
(!def_word "+" "$plus")
;; 6.1.0130
(func $+! (param i32)
(local $addr i32)
(local $bbtos i32)
(i32.store (tee_local $addr (i32.load (i32.sub (get_global $tos) (i32.const 4))))
(i32.add (i32.load (get_local $addr))
(i32.load (tee_local $bbtos (i32.sub (get_global $tos) (i32.const 8))))))
(set_global $tos (get_local $bbtos)))
(!def_word "+!" "$+!")
;; 6.1.0140
(func $plus-loop (param i32)
(call $ensureCompiling)
(call $compilePlusLoop))
(!def_word "+LOOP" "$plus-loop" !fImmediate)
;; 6.1.0150
(func $comma (param i32)
(i32.store
(get_global $here)
(i32.load (i32.sub (get_global $tos) (i32.const 4))))
(set_global $here (i32.add (get_global $here) (i32.const 4)))
(set_global $tos (i32.sub (get_global $tos) (i32.const 4))))
(!def_word "," "$comma")
;; 6.1.0160
(func $minus (param i32)
(local $btos i32)
(local $bbtos i32)
(i32.store (tee_local $bbtos (i32.sub (get_global $tos) (i32.const 8)))
(i32.sub (i32.load (get_local $bbtos))
(i32.load (tee_local $btos (i32.sub (get_global $tos) (i32.const 4))))))
(set_global $tos (get_local $btos)))
(!def_word "-" "$minus")
;; 6.1.0180
(func $.q (param i32)
(call $Sq (i32.const -1))
(call $emitICall (i32.const 0) (i32.const !typeIndex)))
(!def_word ".\"" "$.q" !fImmediate)
;; 6.1.0230
(func $/ (param i32)
(local $btos i32)
(local $bbtos i32)
(i32.store (tee_local $bbtos (i32.sub (get_global $tos) (i32.const 8)))
(i32.div_s (i32.load (get_local $bbtos))
(i32.load (tee_local $btos (i32.sub (get_global $tos) (i32.const 4))))))
(set_global $tos (get_local $btos)))
(!def_word "/" "$/")
;; 6.1.0240
(func $/MOD (param i32)
(local $btos i32)
(local $bbtos i32)
(local $n1 i32)
(local $n2 i32)
(i32.store (tee_local $bbtos (i32.sub (get_global $tos) (i32.const 8)))
(i32.rem_s (tee_local $n1 (i32.load (get_local $bbtos)))
(tee_local $n2 (i32.load (tee_local $btos (i32.sub (get_global $tos)
(i32.const 4)))))))
(i32.store (get_local $btos) (i32.div_s (get_local $n1) (get_local $n2))))
(!def_word "/MOD" "$/MOD")
;; 6.1.0250
(func $0< (param i32)
(local $btos i32)
(if (i32.lt_s (i32.load (tee_local $btos (i32.sub (get_global $tos)
(i32.const 4))))
(i32.const 0))
(then (i32.store (get_local $btos) (i32.const -1)))
(else (i32.store (get_local $btos) (i32.const 0)))))
(!def_word "0<" "$0<")
;; 6.1.0270
(func $zero-equals (param i32)
(local $btos i32)
(if (i32.eqz (i32.load (tee_local $btos (i32.sub (get_global $tos)
(i32.const 4)))))
(then (i32.store (get_local $btos) (i32.const -1)))
(else (i32.store (get_local $btos) (i32.const 0)))))
(!def_word "0=" "$zero-equals")
;; 6.1.0290
(func $one-plus (param i32)
(local $btos i32)
(i32.store (tee_local $btos (i32.sub (get_global $tos) (i32.const 4)))
(i32.add (i32.load (get_local $btos)) (i32.const 1))))
(!def_word "1+" "$one-plus")
;; 6.1.0300
(func $one-minus (param i32)
(local $btos i32)
(i32.store (tee_local $btos (i32.sub (get_global $tos) (i32.const 4)))
(i32.sub (i32.load (get_local $btos)) (i32.const 1))))
(!def_word "1-" "$one-minus")
;; 6.1.0320
(func $2* (param i32)
(local $btos i32)
(i32.store (tee_local $btos (i32.sub (get_global $tos) (i32.const 4)))
(i32.shl (i32.load (get_local $btos)) (i32.const 1))))
(!def_word "2*" "$2*")
;; 6.1.0330
(func $2/ (param i32)
(local $btos i32)
(i32.store (tee_local $btos (i32.sub (get_global $tos) (i32.const 4)))
(i32.shr_s (i32.load (get_local $btos)) (i32.const 1))))
(!def_word "2/" "$2/")
;; 6.1.0370
(func $two-drop (param i32)
(set_global $tos (i32.sub (get_global $tos) (i32.const 8))))
(!def_word "2DROP" "$two-drop")
;; 6.1.0380
(func $two-dupe (param i32)
(i32.store (get_global $tos)
(i32.load (i32.sub (get_global $tos) (i32.const 8))))
(i32.store (i32.add (get_global $tos) (i32.const 4))
(i32.load (i32.sub (get_global $tos) (i32.const 4))))
(set_global $tos (i32.add (get_global $tos) (i32.const 8))))
(!def_word "2DUP" "$two-dupe")
;; 6.1.0400
(func $2OVER (param i32)
(i32.store (get_global $tos)
(i32.load (i32.sub (get_global $tos) (i32.const 16))))
(i32.store (i32.add (get_global $tos) (i32.const 4))
(i32.load (i32.sub (get_global $tos) (i32.const 12))))
(set_global $tos (i32.add (get_global $tos) (i32.const 8))))
(!def_word "2OVER" "$2OVER")
;; 6.1.0430
(func $2SWAP (param i32)
(local $x1 i32)
(local $x2 i32)
(set_local $x1 (i32.load (i32.sub (get_global $tos) (i32.const 16))))
(set_local $x2 (i32.load (i32.sub (get_global $tos) (i32.const 12))))
(i32.store (i32.sub (get_global $tos) (i32.const 16))
(i32.load (i32.sub (get_global $tos) (i32.const 8))))
(i32.store (i32.sub (get_global $tos) (i32.const 12))
(i32.load (i32.sub (get_global $tos) (i32.const 4))))
(i32.store (i32.sub (get_global $tos) (i32.const 8))
(get_local $x1))
(i32.store (i32.sub (get_global $tos) (i32.const 4))
(get_local $x2)))
(!def_word "2SWAP" "$2SWAP")
;; 6.1.0450
(func $colon (param i32)
(call $create (i32.const -1))
(call $hidden)
(set_global $cp (i32.const !moduleBodyBase))
(set_global $currentLocal (i32.const 0))
(set_global $localsCount (i32.const 0))
(call $right-bracket (i32.const -1))
)
(!def_word ":" "$colon")
;; 6.1.0460
(func $semicolon (param i32)
(local $bodySize i32)
(local $nameLength i32)
(call $emitEnd)
;; Update code size
(set_local $bodySize (i32.sub (get_global $cp) (i32.const !moduleHeaderBase)))
(i32.store
(i32.const !moduleHeaderCodeSizeBase)
(call $leb128-4p
(i32.sub (get_local $bodySize)
(i32.const (!+ !moduleHeaderCodeSizeOffset 4)))))
;; Update body size
(i32.store
(i32.const !moduleHeaderBodySizeBase)
(call $leb128-4p
(i32.sub (get_local $bodySize)
(i32.const (!+ !moduleHeaderBodySizeOffset 4)))))
;; Update #locals
(i32.store
(i32.const !moduleHeaderLocalCountBase)
(call $leb128-4p (get_global $localsCount)))
;; Write a name section
(set_local $nameLength (i32.and (i32.load8_u (i32.add (get_global $latest) (i32.const 4)))
(i32.const !lengthMask)))
(i32.store8 (get_global $cp) (i32.const 0))
(i32.store8 (i32.add (get_global $cp) (i32.const 1))
(i32.add (i32.const 13) (i32.mul (i32.const 2) (get_local $nameLength))))
(i32.store8 (i32.add (get_global $cp) (i32.const 2)) (i32.const 0x04))
(i32.store8 (i32.add (get_global $cp) (i32.const 3)) (i32.const 0x6e))
(i32.store8 (i32.add (get_global $cp) (i32.const 4)) (i32.const 0x61))
(i32.store8 (i32.add (get_global $cp) (i32.const 5)) (i32.const 0x6d))
(i32.store8 (i32.add (get_global $cp) (i32.const 6)) (i32.const 0x65))
(set_global $cp (i32.add (get_global $cp) (i32.const 7)))
(i32.store8 (get_global $cp) (i32.const 0x00))
(i32.store8 (i32.add (get_global $cp) (i32.const 1))
(i32.add (i32.const 1) (get_local $nameLength)))
(i32.store8 (i32.add (get_global $cp) (i32.const 2)) (get_local $nameLength))
(set_global $cp (i32.add (get_global $cp) (i32.const 3)))
(call $memmove (get_global $cp)
(i32.add (get_global $latest) (i32.const 5))
(get_local $nameLength))
(set_global $cp (i32.add (get_global $cp) (get_local $nameLength)))
(i32.store8 (get_global $cp) (i32.const 0x01))
(i32.store8 (i32.add (get_global $cp) (i32.const 1))
(i32.add (i32.const 3) (get_local $nameLength)))
(i32.store8 (i32.add (get_global $cp) (i32.const 2)) (i32.const 0x01))
(i32.store8 (i32.add (get_global $cp) (i32.const 3)) (i32.const 0x00))
(i32.store8 (i32.add (get_global $cp) (i32.const 4)) (get_local $nameLength))
(set_global $cp (i32.add (get_global $cp) (i32.const 5)))
(call $memmove (get_global $cp)
(i32.add (get_global $latest) (i32.const 5))
(get_local $nameLength))
(set_global $cp (i32.add (get_global $cp) (get_local $nameLength)))
;; Load the code and store the index
(call $shell_load (i32.const !moduleHeaderBase)
(i32.sub (get_global $cp) (i32.const !moduleHeaderBase))
(get_global $nextTableIndex))
(i32.store (call $body (get_global $latest)) (get_global $nextTableIndex))
(set_global $nextTableIndex (i32.add (get_global $nextTableIndex) (i32.const 1)))
(call $hidden)
(call $left-bracket (i32.const -1)))
(!def_word ";" "$semicolon" !fImmediate)
;; 6.1.0480
(func $less-than (param i32)
(local $btos i32)
(local $bbtos i32)
(if (i32.lt_s (i32.load (tee_local $bbtos (i32.sub (get_global $tos) (i32.const 8))))
(i32.load (tee_local $btos (i32.sub (get_global $tos) (i32.const 4)))))
(then (i32.store (get_local $bbtos) (i32.const -1)))
(else (i32.store (get_local $bbtos) (i32.const 0))))
(set_global $tos (get_local $btos)))
(!def_word "<" "$less-than")
;; 6.1.0530
(func $= (param i32)
(local $btos i32)
(local $bbtos i32)
(if (i32.eq (i32.load (tee_local $bbtos (i32.sub (get_global $tos) (i32.const 8))))
(i32.load (tee_local $btos (i32.sub (get_global $tos) (i32.const 4)))))
(then (i32.store (get_local $bbtos) (i32.const -1)))
(else (i32.store (get_local $bbtos) (i32.const 0))))
(set_global $tos (get_local $btos)))
(!def_word "=" "$=")
;; 6.1.0540
(func $greater-than (param i32)
(local $btos i32)
(local $bbtos i32)
(if (i32.gt_s (i32.load (tee_local $bbtos (i32.sub (get_global $tos) (i32.const 8))))
(i32.load (tee_local $btos (i32.sub (get_global $tos) (i32.const 4)))))
(then (i32.store (get_local $bbtos) (i32.const -1)))
(else (i32.store (get_local $bbtos) (i32.const 0))))
(set_global $tos (get_local $btos)))
(!def_word ">" "$greater-than")
;; 6.1.0550
(func $>BODY (param i32)
(local $btos i32)
(i32.store (tee_local $btos (i32.sub (get_global $tos) (i32.const 4)))
(i32.add (call $body (i32.load (get_local $btos)))
(i32.const 4))))
(!def_word ">BODY" "$>BODY")
;; 6.1.0580
(func $>R (param i32)
(set_global $tos (i32.sub (get_global $tos) (i32.const 4)))
(i32.store (get_global $tors) (i32.load (get_global $tos)))
(set_global $tors (i32.add (get_global $tors) (i32.const 4))))
(!def_word ">R" "$>R")
;; 6.1.0630
(func $?DUP (param i32)
(local $btos i32)
(if (i32.ne (i32.load (tee_local $btos (i32.sub (get_global $tos) (i32.const 4))))
(i32.const 0))
(then
(i32.store (get_global $tos)
(i32.load (get_local $btos)))
(set_global $tos (i32.add (get_global $tos) (i32.const 4))))))
(!def_word "?DUP" "$?DUP")
;; 6.1.0650
(func $@ (param i32)
(local $btos i32)
(i32.store (tee_local $btos (i32.sub (get_global $tos) (i32.const 4)))
(i32.load (i32.load (get_local $btos)))))
(!def_word "@" "$@")
;; 6.1.0690
(func $ABS (param i32)
(local $btos i32)
(local $v i32)
(local $y i32)
(i32.store (tee_local $btos (i32.sub (get_global $tos) (i32.const 4)))
(i32.sub (i32.xor (tee_local $v (i32.load (get_local $btos)))
(tee_local $y (i32.shr_s (get_local $v) (i32.const 31))))
(get_local $y))))
(!def_word "ABS" "$ABS")
;; 6.1.0710
(func $ALLOT (param i32)
(set_global $here (i32.add (get_global $here) (call $pop))))
(!def_word "ALLOT" "$ALLOT")
;; 6.1.0720
(func $AND (param i32)
(local $btos i32)
(local $bbtos i32)
(i32.store (tee_local $bbtos (i32.sub (get_global $tos) (i32.const 8)))
(i32.and (i32.load (tee_local $btos (i32.sub (get_global $tos) (i32.const 4))))
(i32.load (get_local $bbtos))))
(set_global $tos (get_local $btos)))
(!def_word "AND" "$AND")
;; 6.1.0705
(func $ALIGN (param i32)
(set_global $here (i32.and
(i32.add (get_global $here) (i32.const 3))
(i32.const -4 #| ~3 |#))))
(!def_word "ALIGN" "$ALIGN")
;; 6.1.0706
(func $ALIGNED (param i32)
(local $btos i32)
(i32.store (tee_local $btos (i32.sub (get_global $tos) (i32.const 4)))
(i32.and (i32.add (i32.load (get_local $btos)) (i32.const 3))
(i32.const -4 #| ~3 |#))))
(!def_word "ALIGNED" "$ALIGNED")
;; 6.1.0750
(func $BASE (param i32)
(i32.store (get_global $tos) (i32.const !baseBase))
(set_global $tos (i32.add (get_global $tos) (i32.const 4))))
(!def_word "BASE" "$BASE")
;; 6.1.0760
(func $begin (param i32)
(call $ensureCompiling)
(call $compileBegin))
(!def_word "BEGIN" "$begin" !fImmediate)
;; 6.1.0770
(func $bl (param i32) (call $push (i32.const 32)))
(!def_word "BL" "$bl")
;; 6.1.0850
(func $c-store (param i32)
(local $bbtos i32)
(i32.store8 (i32.load (i32.sub (get_global $tos) (i32.const 4)))
(i32.load (tee_local $bbtos (i32.sub (get_global $tos) (i32.const 8)))))
(set_global $tos (get_local $bbtos)))
(!def_word "C!" "$c-store")
;; 6.1.0860
(func $c-comma (param i32)
(i32.store8 (get_global $here)
(i32.load (i32.sub (get_global $tos) (i32.const 4))))
(set_global $here (i32.add (get_global $here) (i32.const 1)))
(set_global $tos (i32.sub (get_global $tos) (i32.const 4))))
(!def_word "C," "$c-comma")
;; 6.1.0870
(func $c-fetch (param i32)
(local $btos i32)
(i32.store (tee_local $btos (i32.sub (get_global $tos) (i32.const 4)))
(i32.load8_u (i32.load (get_local $btos)))))
(!def_word "C@" "$c-fetch")
;; 6.1.0895
(func $CHAR (param i32)
(call $word (i32.const -1))
(i32.store (i32.sub (get_global $tos) (i32.const 4))
(i32.load8_u (i32.const (!+ !wordBase 4)))))
(!def_word "CHAR" "$CHAR")
;; 6.1.0950
(func $CONSTANT (param i32)
(call $create (i32.const -1))
(i32.store (call $body (get_global $latest)) (i32.const !pushDataValueIndex))
(i32.store (get_global $here) (call $pop))
(set_global $here (i32.add (get_global $here) (i32.const 4))))
(!def_word "CONSTANT" "$CONSTANT")
;; 6.1.0980
(func $COUNT (param i32)
(local $btos i32)
(local $addr i32)
(i32.store (get_global $tos)
(i32.load (tee_local $addr (i32.load (tee_local $btos (i32.sub (get_global $tos)
(i32.const 4)))))))
(i32.store (get_local $btos) (i32.add (get_local $addr) (i32.const 4)))
(set_global $tos (i32.add (get_global $tos) (i32.const 4))))
(!def_word "COUNT" "$COUNT")
;; 6.1.1000
(func $create (param i32)
(local $length i32)
(i32.store (get_global $here) (get_global $latest))
(set_global $latest (get_global $here))
(set_global $here (i32.add (get_global $here) (i32.const 4)))
(call $word (i32.const -1))
(drop (call $pop))
(i32.store8 (get_global $here) (tee_local $length (i32.load (i32.const !wordBase))))
(set_global $here (i32.add (get_global $here) (i32.const 1)))
(call $memmove (get_global $here) (i32.const (!+ !wordBase 4)) (get_local $length))
(set_global $here (i32.add (get_global $here) (get_local $length)))
(call $ALIGN (i32.const -1))
;; Leave space for the code pointer
(i32.store (get_global $here) (i32.const 0))
(set_global $here (i32.add (get_global $here) (i32.const 4))))
(!def_word "CREATE" "$create")
;; 6.1.1200
(func $DEPTH (param i32)
(i32.store (get_global $tos)
(i32.shr_u (i32.sub (get_global $tos) (i32.const !stackBase)) (i32.const 2)))
(set_global $tos (i32.add (get_global $tos) (i32.const 4))))
(!def_word "DEPTH" "$DEPTH")
;; 6.1.1240
(func $do (param i32)
(call $ensureCompiling)
(call $compileDo))
(!def_word "DO" "$do" !fImmediate)
;; 6.1.1250
; (func $DOES> (param i32))
; (!def_word "DOES>" "$DOES>")
;; 6.1.1260
(func $drop (param i32)
(set_global $tos (i32.sub (get_global $tos) (i32.const 4))))
(!def_word "DROP" "$drop")
;; 6.1.1290
(func $dupe (param i32)
(i32.store
(get_global $tos)
(i32.load (i32.sub (get_global $tos) (i32.const 4))))
(set_global $tos (i32.add (get_global $tos) (i32.const 4))))
(!def_word "DUP" "$dupe")
;; 6.1.1310
(func $else (param i32)
(call $ensureCompiling)
(call $compileElse))
(!def_word "ELSE" "$else" !fImmediate)
;; 6.1.1320
(func $emit (param i32)
(call $shell_emit (i32.load (i32.sub (get_global $tos) (i32.const 4))))
(set_global $tos (i32.sub (get_global $tos) (i32.const 4))))
(!def_word "EMIT" "$emit")
;; 6.1.1370
(func $EXECUTE (param i32)
(local $body i32)
(set_local $body (call $body (call $pop)))
(call_indirect (type $word) (i32.add (get_local $body) (i32.const 4))
(i32.load (get_local $body))))
(!def_word "EXECUTE" "$EXECUTE")
;; 6.1.1380
(func $EXIT (param i32)
(call $emitReturn))
(!def_word "EXIT" "$EXIT" !fImmediate)
;; 6.1.1540
(func $FILL (param i32)
(local $bbbtos i32)
(call $memset (i32.load (tee_local $bbbtos (i32.sub (get_global $tos) (i32.const 12))))
(i32.load (i32.sub (get_global $tos) (i32.const 4)))
(i32.load (i32.sub (get_global $tos) (i32.const 8))))
(set_global $tos (get_local $bbbtos)))
(!def_word "FILL" "$FILL")
;; 6.1.1550
(func $find (export "FIND") (param i32)
(local $entryP i32)
(local $entryNameP i32)
(local $entryLF i32)
(local $wordP i32)
(local $wordStart i32)
(local $wordLength i32)
(local $wordEnd i32)
(set_local $wordLength
(i32.load (tee_local $wordStart (i32.load (i32.sub (get_global $tos)
(i32.const 4))))))
(set_local $wordStart (i32.add (get_local $wordStart) (i32.const 4)))
(set_local $wordEnd (i32.add (get_local $wordStart) (get_local $wordLength)))
(set_local $entryP (get_global $latest))
(block $endLoop
(loop $loop
(set_local $entryLF (i32.load (i32.add (get_local $entryP) (i32.const 4))))
(block $endCompare
(if (i32.and
(i32.eq (i32.and (get_local $entryLF) (i32.const !fHidden)) (i32.const 0))
(i32.eq (i32.and (get_local $entryLF) (i32.const !lengthMask))
(get_local $wordLength)))
(then
(set_local $wordP (get_local $wordStart))
(set_local $entryNameP (i32.add (get_local $entryP) (i32.const 5)))
(block $endCompareLoop
(loop $compareLoop
(br_if $endCompare (i32.ne (i32.load8_s (get_local $entryNameP))
(i32.load8_s (get_local $wordP))))
(set_local $entryNameP (i32.add (get_local $entryNameP) (i32.const 1)))
(set_local $wordP (i32.add (get_local $wordP) (i32.const 1)))
(br_if $endCompareLoop (i32.eq (get_local $wordP)
(get_local $wordEnd)))
(br $compareLoop)))
(i32.store (i32.sub (get_global $tos) (i32.const 4))
(get_local $entryP))
(if (i32.eq (i32.and (get_local $entryLF) (i32.const !fImmediate)) (i32.const 0))
(then
(call $push (i32.const -1)))
(else
(call $push (i32.const 1))))
(return))))
(set_local $entryP (i32.load (get_local $entryP)))
(br_if $endLoop (i32.eqz (get_local $entryP)))
(br $loop)))
(call $push (i32.const 0)))
(!def_word "FIND" "$find")
;; 6.1.1650
(func $here (param i32)
(i32.store (get_global $tos) (get_global $here))
(set_global $tos (i32.add (get_global $tos) (i32.const 4))))
(!def_word "HERE" "$here")
;; 6.1.1680
(func $i (param i32)
(call $ensureCompiling)
(call $compilePushLocal (i32.sub (get_global $currentLocal) (i32.const 1))))
(!def_word "I" "$i" !fImmediate)
;; 6.1.1700
(func $if (param i32)
(call $ensureCompiling)
(call $compileIf))
(!def_word "IF" "$if" !fImmediate)
;; 6.1.1710
(func $immediate (param i32)
(i32.store
(i32.add (get_global $latest) (i32.const 4))
(i32.or
(i32.load (i32.add (get_global $latest) (i32.const 4)))
(i32.const !fImmediate))))
(!def_word "IMMEDIATE" "$immediate")
;; 6.1.1720
(func $INVERT (param i32)
(local $btos i32)
(i32.store (tee_local $btos (i32.sub (get_global $tos) (i32.const 4)))
(i32.xor (i32.load (get_local $btos)) (i32.const -1))))
(!def_word "INVERT" "$INVERT")
;; 6.1.1730
(func $j (param i32)
(call $ensureCompiling)
(call $compilePushLocal (i32.sub (get_global $currentLocal) (i32.const 3))))
(!def_word "J" "$j" !fImmediate)
;; 6.1.1750
(func $key (param i32)
(i32.store (get_global $tos) (call $readChar))
(set_global $tos (i32.add (get_global $tos) (i32.const 4))))
(!def_word "KEY" "$key")
;; 6.1.1760
(func $LEAVE (param i32)
(call $ensureCompiling)
(call $compileLeave))
(!def_word "LEAVE" "$LEAVE" !fImmediate)
;; 6.1.1780
(func $literal (param i32)
(call $compilePushConst (call $pop)))
(!def_word "LITERAL" "$literal" !fImmediate)
;; 6.1.1800
(func $loop (param i32)
(call $ensureCompiling)
(call $compileLoop))
(!def_word "LOOP" "$loop" !fImmediate)
;; 6.1.1805
(func $LSHIFT (param i32)
(local $btos i32)
(local $bbtos i32)
(i32.store (tee_local $bbtos (i32.sub (get_global $tos) (i32.const 8)))
(i32.shl (i32.load (get_local $bbtos))
(i32.load (tee_local $btos (i32.sub (get_global $tos) (i32.const 4))))))
(set_global $tos (get_local $btos)))
(!def_word "LSHIFT" "$LSHIFT")
;; 6.1.1870
(func $MAX (param i32)
(local $btos i32)
(local $bbtos i32)
(local $v i32)
(if (i32.lt_s (i32.load (tee_local $bbtos (i32.sub (get_global $tos) (i32.const 8))))
(tee_local $v (i32.load (tee_local $btos (i32.sub (get_global $tos)
(i32.const 4))))))
(then
(i32.store (get_local $bbtos) (get_local $v))))
(set_global $tos (get_local $btos)))
(!def_word "MAX" "$MAX")
;; 6.1.1880
(func $MIN (param i32)
(local $btos i32)
(local $bbtos i32)
(local $v i32)
(if (i32.gt_s (i32.load (tee_local $bbtos (i32.sub (get_global $tos) (i32.const 8))))
(tee_local $v (i32.load (tee_local $btos (i32.sub (get_global $tos)
(i32.const 4))))))
(then
(i32.store (get_local $bbtos) (get_local $v))))
(set_global $tos (get_local $btos)))
(!def_word "MIN" "$MIN")
;; 6.1.1880
(func $MOD (param i32)
(local $btos i32)
(local $bbtos i32)
(i32.store (tee_local $bbtos (i32.sub (get_global $tos) (i32.const 8)))
(i32.rem_s (i32.load (get_local $bbtos))
(i32.load (tee_local $btos (i32.sub (get_global $tos) (i32.const 4))))))
(set_global $tos (get_local $btos)))
(!def_word "MOD" "$MOD")
;; 6.1.1900
(func $MOVE (param i32)
(local $bbbtos i32)
(call $memmove (i32.load (i32.sub (get_global $tos) (i32.const 8)))
(i32.load (tee_local $bbbtos (i32.sub (get_global $tos) (i32.const 12))))
(i32.load (i32.sub (get_global $tos) (i32.const 4))))
(set_global $tos (get_local $bbbtos)))
(!def_word "MOVE" "$MOVE")
;; 6.1.1910
(func $negate (param i32)
(local $btos i32)
(i32.store (tee_local $btos (i32.sub (get_global $tos) (i32.const 4)))
(i32.sub (i32.const 0) (i32.load (get_local $btos)))))
(!def_word "NEGATE" "$negate")
;; 6.1.1980
(func $OR (param i32)
(local $btos i32)
(local $bbtos i32)
(i32.store (tee_local $bbtos (i32.sub (get_global $tos) (i32.const 8)))
(i32.or (i32.load (tee_local $btos (i32.sub (get_global $tos) (i32.const 4))))
(i32.load (get_local $bbtos))))
(set_global $tos (get_local $btos)))
(!def_word "OR" "$OR")
;; 6.1.1990
(func $over (param i32)
(i32.store (get_global $tos)
(i32.load (i32.sub (get_global $tos) (i32.const 8))))
(set_global $tos (i32.add (get_global $tos) (i32.const 4))))
(!def_word "OVER" "$over")
;; 6.1.2060
(func $R> (param i32)
(set_global $tors (i32.sub (get_global $tors) (i32.const 4)))
(i32.store (get_global $tos) (i32.load (get_global $tors)))
(set_global $tos (i32.add (get_global $tos) (i32.const 4))))
(!def_word "R>" "$R>")
;; 6.1.2070
(func $R@ (param i32)
(i32.store (get_global $tos) (i32.load (i32.sub (get_global $tors) (i32.const 4))))
(set_global $tos (i32.add (get_global $tos) (i32.const 4))))
(!def_word "R@" "$R@")
;; 6.1.2120
(func $RECURSE (param i32)
(call $compileRecurse))
(!def_word "RECURSE" "$RECURSE" !fImmediate)
;; 6.1.2140
(func $repeat (param i32)
(call $ensureCompiling)
(call $compileRepeat))
(!def_word "REPEAT" "$repeat" !fImmediate)
;; 6.1.2160 ROT
(func $ROT (param i32)
(local $tmp i32)
(local $btos i32)
(local $bbtos i32)
(local $bbbtos i32)
(set_local $tmp (i32.load (tee_local $btos (i32.sub (get_global $tos) (i32.const 4)))))
(i32.store (get_local $btos)
(i32.load (tee_local $bbbtos (i32.sub (get_global $tos) (i32.const 12)))))
(i32.store (get_local $bbbtos)
(i32.load (tee_local $bbtos (i32.sub (get_global $tos) (i32.const 8)))))
(i32.store (get_local $bbtos)
(get_local $tmp)))
(!def_word "ROT" "$ROT")
;; 6.1.2162
(func $RSHIFT (param i32)
(local $btos i32)
(local $bbtos i32)
(i32.store (tee_local $bbtos (i32.sub (get_global $tos) (i32.const 8)))
(i32.shr_u (i32.load (get_local $bbtos))
(i32.load (tee_local $btos (i32.sub (get_global $tos) (i32.const 4))))))
(set_global $tos (get_local $btos)))
(!def_word "RSHIFT" "$RSHIFT")
;; 6.1.2165
(func $Sq (param i32)
(local $c i32)
(local $start i32)
(set_local $start (get_global $here))
(block $endLoop
(loop $loop
(if (i32.eqz (tee_local $c (call $readChar)))
(then
(unreachable)))
(br_if $endLoop (i32.eq (get_local $c) (i32.const 0x22)))
(i32.store8 (get_global $here) (get_local $c))
(set_global $here (i32.add (get_global $here) (i32.const 1)))
(br $loop)))
(call $compilePushConst (get_local $start))
(call $compilePushConst (i32.sub (get_global $here) (get_local $start)))
(call $ALIGN (i32.const -1)))
(!def_word "S\"" "$Sq" !fImmediate)
;; 6.1.2220
(func $space (param i32) (call $bl (i32.const -1)) (call $emit (i32.const -1)))
(!def_word "SPACE" "$space")
;; 6.1.2250
(func $STATE (param i32)
(i32.store (get_global $tos) (i32.const !stateBase))
(set_global $tos (i32.add (get_global $tos) (i32.const 4))))
(!def_word "STATE" "$STATE")
;; 6.1.2260
(func $swap (param i32)
(local $btos i32)
(local $bbtos i32)
(local $tmp i32)
(set_local $tmp (i32.load (tee_local $bbtos (i32.sub (get_global $tos) (i32.const 8)))))
(i32.store (get_local $bbtos)
(i32.load (tee_local $btos (i32.sub (get_global $tos) (i32.const 4)))))
(i32.store (get_local $btos) (get_local $tmp)))
(!def_word "SWAP" "$swap")
;; 6.1.2270
(func $then (param i32)
(call $ensureCompiling)
(call $compileThen))
(!def_word "THEN" "$then" !fImmediate)
;; 6.2.2295
(func $TO (param i32)
(call $word (i32.const -1))
(if (i32.eqz (i32.load (i32.const !wordBase))) (then (unreachable)))
(call $find (i32.const -1))
(if (i32.eqz (call $pop)) (unreachable))
(i32.store (i32.add (call $body (call $pop)) (i32.const 4)) (call $pop)))
(!def_word "TO" "$TO")
;; 6.1.2340
(func $U< (param i32)
(local $btos i32)
(local $bbtos i32)
(if (i32.lt_u (i32.load (tee_local $bbtos (i32.sub (get_global $tos) (i32.const 8))))
(i32.load (tee_local $btos (i32.sub (get_global $tos) (i32.const 4)))))
(then (i32.store (get_local $bbtos) (i32.const -1)))
(else (i32.store (get_local $bbtos) (i32.const 0))))
(set_global $tos (get_local $btos)))
(!def_word "U<" "$U<")
;; 6.1.2380
(func $UNLOOP (param i32))
(!def_word "UNLOOP" "$UNLOOP" !fImmediate)
;; 6.1.2390
(func $UNTIL (param i32)
(call $ensureCompiling)
(call $compileUntil))
(!def_word "UNTIL" "$UNTIL" !fImmediate)
;; 6.2.2405
(!def_word "VALUE" "$CONSTANT")
;; 6.1.2410
(func $VARIABLE (param i32)
(call $create (i32.const -1))
(i32.store (call $body (get_global $latest)) (i32.const !pushDataAddressIndex))
(i32.store (get_global $here) (i32.const 0))
(set_global $here (i32.add (get_global $here) (i32.const 4))))
(!def_word "VARIABLE" "$VARIABLE")
;; 6.1.2430
(func $while (param i32)
(call $ensureCompiling)
(call $compileWhile))
(!def_word "WHILE" "$while" !fImmediate)
(func $word (export "WORD") (param i32)
(local $char i32)
(local $stringPtr i32)
;; Search for first non-blank character
(block $endSkipBlanks
(loop $skipBlanks
(set_local $char (call $readChar))
;; Skip comments (if necessary)
(if (i32.eq (get_local $char) (i32.const 0x5C #| '\' |#))
(then
(loop $skipComments
(set_local $char (call $readChar))
(br_if $skipBlanks (i32.eq (get_local $char) (i32.const 0x0a #| '\n' |#)))
(br_if $endSkipBlanks (i32.eq (get_local $char) (i32.const -1)))
(br $skipComments))))
(br_if $skipBlanks (i32.eq (get_local $char) (i32.const 0x20 #| ' ' |#)))
(br_if $skipBlanks (i32.eq (get_local $char) (i32.const 0x0a #| ' ' |#)))
(br $endSkipBlanks)))
(if (i32.ne (get_local $char) (i32.const -1))
(then
;; Search for first blank character
(i32.store8 (i32.const (!+ !wordBase 4)) (get_local $char))
(set_local $stringPtr (i32.const (!+ !wordBase 5)))
(block $endReadChars
(loop $readChars
(set_local $char (call $readChar))
(br_if $endReadChars (i32.eq (get_local $char) (i32.const 0x20 #| ' ' |#)))
(br_if $endReadChars (i32.eq (get_local $char) (i32.const 0x0a #| ' ' |#)))
(br_if $endReadChars (i32.eq (get_local $char) (i32.const -1)))
(i32.store8 (get_local $stringPtr) (get_local $char))
(set_local $stringPtr (i32.add (get_local $stringPtr) (i32.const 0x1)))
(br $readChars))))
(else
;; Reached end of input
(set_local $stringPtr (i32.const (!+ !wordBase 4)))))
;; Write word length
(i32.store (i32.const !wordBase)
(i32.sub (get_local $stringPtr) (i32.const (!+ !wordBase 4))))
(call $push (i32.const !wordBase)))
;; 6.1.2490
(func $XOR (param i32)
(local $btos i32)
(local $bbtos i32)
(i32.store (tee_local $bbtos (i32.sub (get_global $tos) (i32.const 8)))
(i32.xor (i32.load (tee_local $btos (i32.sub (get_global $tos) (i32.const 4))))
(i32.load (get_local $bbtos))))
(set_global $tos (get_local $btos)))
(!def_word "XOR" "$XOR")
;; 6.1.2500
(func $left-bracket (param i32)
(i32.store (i32.const !stateBase) (i32.const 0)))
(!def_word "[" "$left-bracket" !fImmediate)
;; 6.1.2540
(func $right-bracket (param i32)
(i32.store (i32.const !stateBase) (i32.const 1)))
(!def_word "]" "$right-bracket")
;; 6.2.0280
(func $zero-greater (param i32)
(local $btos i32)
(if (i32.gt_s (i32.load (tee_local $btos (i32.sub (get_global $tos)
(i32.const 4))))
(i32.const 0))
(then (i32.store (get_local $btos) (i32.const -1)))
(else (i32.store (get_local $btos) (i32.const 0)))))
(!def_word "0>" "$zero-greater")
;; 6.2.1350
(func $erase (param i32)
(local $bbtos i32)
(call $memset (i32.load (tee_local $bbtos (i32.sub (get_global $tos) (i32.const 8))))
(i32.const 0)
(i32.load (i32.sub (get_global $tos) (i32.const 4))))
(set_global $tos (get_local $bbtos)))
(!def_word "ERASE" "$erase")
(func $dspFetch (param i32)
(i32.store
(get_global $tos)
(get_global $tos))
(set_global $tos (i32.add (get_global $tos) (i32.const 4))))
(!def_word "DSP@" "$dspFetch")
(func $S0 (param i32)
(call $push (i32.const !stackBase)))
(!def_word "S0" "$S0")
(func $latest (param i32)
(i32.store (get_global $tos) (get_global $latest))
(set_global $tos (i32.add (get_global $tos) (i32.const 4))))
(!def_word "LATEST" "$latest")
;; High-level words
(!prelude #<<EOF
\ 6.1.1170
: DECIMAL 10 BASE ! ;
\ 6.1.0897
: CHAR+ 1+ ;
\ 6.1.0898
: CHARS ;
\ 6.1.0880
: CELL+ 4 + ;
\ 6.1.0890
: CELLS 4 * ;
\ 6.1.0350
: 2@ DUP CELL+ @ SWAP @ ;
\ 6.1.0310
: 2! SWAP OVER ! CELL+ ! ;
: UWIDTH BASE @ / ?DUP IF RECURSE 1+ ELSE 1 THEN ;
: '\n' 10 ;
: 'A' [ CHAR A ] LITERAL ;
: '0' [ CHAR 0 ] LITERAL ;
: '(' [ CHAR ( ] LITERAL ;
: ')' [ CHAR ) ] LITERAL ;
: (
1
BEGIN
KEY
DUP '(' = IF
DROP
1+
ELSE
')' = IF 1- THEN
THEN
DUP 0= UNTIL
DROP
; IMMEDIATE
\ 6.1.0990
: CR '\n' EMIT ;
\ 6.1.2230
: SPACES BEGIN DUP 0> WHILE SPACE 1- REPEAT DROP ;
\ 6.1.2320
: U.
BASE @ /MOD
?DUP IF RECURSE THEN
DUP 10 < IF 48 ELSE 10 - 65 THEN
+
EMIT
;
\ 15.6.1.0220
: .S
DSP@ S0
BEGIN
2DUP >
WHILE
DUP @ U.
SPACE
4 +
REPEAT
2DROP
;
\ 6.2.0210
: .R
SWAP
DUP 0< IF NEGATE 1 SWAP ROT 1- ELSE 0 SWAP ROT THEN
SWAP DUP UWIDTH ROT SWAP -
SPACES SWAP
IF 45 EMIT THEN
U.
;
\ 6.1.0180
: . 0 .R SPACE ;
EOF
)
;; Reads a number from the word buffer, and puts it on the stack.
;; Returns -1 if an error occurred.
;; TODO: Support other bases
(func $number (result i32)
(local $sign i32)
(local $length i32)
(local $char i32)
(local $value i32)
(local $base i32)
(local $p i32)
(local $end i32)
(if (i32.eqz (tee_local $length (i32.load (i32.const !wordBase))))
(return (i32.const -1)))
(set_local $p (i32.const (!+ !wordBase 4)))
(set_local $end (i32.add (i32.const (!+ !wordBase 4)) (get_local $length)))
(set_local $base (i32.load (i32.const !baseBase)))
;; Read first character
(if (i32.eq (tee_local $char (i32.load8_u (i32.const (!+ !wordBase 4))))
(i32.const 0x2d #| '-' |#))
(then
(set_local $sign (i32.const -1))
(set_local $char (i32.const 48)))
(else (set_local $sign (i32.const 1))))
;; Read all characters
(set_local $value (i32.const 0))
(block $endLoop
(loop $loop
(if (i32.or (i32.lt_s (get_local $char) (i32.const 48 #| '0' |# ))
(i32.gt_s (get_local $char) (i32.const 57 #| '9' |# )))
(then (return (i32.const -1))))
(set_local $value (i32.add (i32.mul (get_local $value) (get_local $base))
(i32.sub (get_local $char)
(i32.const 48))))
(set_local $p (i32.add (get_local $p) (i32.const 1)))
(br_if $endLoop (i32.eq (get_local $p) (get_local $end)))
(set_local $char (i32.load8_s (get_local $p)))
(br $loop)))
(call $push (i32.mul (get_local $sign) (get_local $value)))
(return (i32.const 0)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Interpreter
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Interprets the string in the input, until the end of string is reached.
;; Returns 0 if processed, 1 if still compiling, -1 if a word was not found.
(func $interpret (result i32)
(local $findResult i32)
(local $findToken i32)
(local $body i32)
(block $endLoop
(loop $loop
(call $word (i32.const -1))
(br_if $endLoop (i32.eqz (i32.load (i32.const !wordBase))))
(call $find (i32.const -1))
(set_local $findResult (call $pop))
(set_local $findToken (call $pop))
(if (i32.eqz (get_local $findResult))
(then ;; Not in the dictionary. Is it a number?
(if (i32.eqz (call $number))
(then ;; It's a number. Are we compiling?
(if (i32.ne (i32.load (i32.const !stateBase)) (i32.const 0))
(then
;; We're compiling. Pop it off the stack and
;; add it to the compiled list
(call $compilePushConst (call $pop)))))
;; We're not compiling. Leave the number on the stack.
(else ;; It's not a number.
(drop (call $pop))
;; TODO: Give error
(return (i32.const -1)))))
(else ;; Found the word.
(set_local $body (call $body (get_local $findToken)))
;; Are we compiling?
(if (i32.eqz (i32.load (i32.const !stateBase)))
(then
;; We're not compiling. Execute the word.
(call_indirect (type $word)
(i32.add (get_local $body) (i32.const 4))
(i32.load (get_local $body))))
(else
;; We're compiling. Is it immediate?
(if (i32.eq (get_local $findResult) (i32.const 1))
(then ;; Immediate. Execute the word.
(call_indirect (type $word)
(i32.add (get_local $body) (i32.const 4))
(i32.load (get_local $body))))
(else ;; Not Immediate. Compile the word call.
(call $emitConst (i32.add (get_local $body) (i32.const 4)))
(call $emitICall
(i32.const 1)
(i32.load (get_local $body)))))))))
(br $loop)))
;; 'WORD' left the address on the stack
(drop (call $pop))
(return (i32.load (i32.const !stateBase))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Compiler functions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(func $compilePushConst (param $n i32)
(call $emitConst (get_local $n))
(call $emitICall (i32.const 1) (i32.const !pushIndex)))
(func $compilePushLocal (param $n i32)
(call $emitGetLocal (get_local $n))
(call $emitICall (i32.const 1) (i32.const !pushIndex)))
(func $compileIf
(call $compilePop)
(call $emitConst (i32.const 0))
;; ne
(i32.store8 (get_global $cp) (i32.const 0x47))
(set_global $cp (i32.add (get_global $cp) (i32.const 1)))
;; if (empty block)
(i32.store8 (get_global $cp) (i32.const 0x04))
(set_global $cp (i32.add (get_global $cp) (i32.const 1)))
(i32.store8 (get_global $cp) (i32.const 0x40))
(set_global $cp (i32.add (get_global $cp) (i32.const 1))))
(func $compileElse
(i32.store8 (get_global $cp) (i32.const 0x05))
(set_global $cp (i32.add (get_global $cp) (i32.const 1))))
(func $compileThen (call $emitEnd))
(func $compileDo
(set_global $currentLocal (i32.add (get_global $currentLocal) (i32.const 2)))
(if (i32.gt_s (get_global $currentLocal) (get_global $localsCount))
(then
(set_global $localsCount (get_global $currentLocal))))
(call $compilePop)
(call $emitSetLocal (i32.sub (get_global $currentLocal) (i32.const 1)))
(call $compilePop)
(call $emitSetLocal (get_global $currentLocal))
(call $emitBlock)
(call $emitLoop))
(func $compileLoop
(call $emitConst (i32.const 1))
(call $compileLoopEnd))
(func $compilePlusLoop
(call $compilePop)
(call $compileLoopEnd))
;; Assumes increment is on the operand stack
(func $compileLoopEnd
(call $emitGetLocal (i32.sub (get_global $currentLocal) (i32.const 1)))
(call $emitAdd)
(call $emitSetLocal (i32.sub (get_global $currentLocal) (i32.const 1)))
(call $emitGetLocal (i32.sub (get_global $currentLocal) (i32.const 1)))
(call $emitGetLocal (get_global $currentLocal))
(call $emitGreaterEqualSigned)
(call $emitBrIf (i32.const 1))
(call $emitBr (i32.const 0))
(call $emitEnd)
(call $emitEnd)
(set_global $currentLocal (i32.sub (get_global $currentLocal) (i32.const 2))))
(func $compileLeave
(call $emitBr (i32.const 1)))
(func $compileBegin
(call $emitBlock)
(call $emitLoop))
(func $compileWhile
(call $compilePop)
(call $emitEqualsZero)
(call $emitBrIf (i32.const 1)))
(func $compileRepeat
(call $emitBr (i32.const 0))
(call $emitEnd)
(call $emitEnd))
(func $compileUntil
(call $compilePop)
(call $emitEqualsZero)
(call $emitBrIf (i32.const 0))
(call $emitBr (i32.const 1))
(call $emitEnd)
(call $emitEnd))
(func $compileRecurse
;; get_local 0
(i32.store8 (get_global $cp) (i32.const 0x20))
(set_global $cp (i32.add (get_global $cp) (i32.const 1)))
(i32.store8 (get_global $cp) (i32.const 0x00))
(set_global $cp (i32.add (get_global $cp) (i32.const 1)))
;; call 0
(i32.store8 (get_global $cp) (i32.const 0x10))
(set_global $cp (i32.add (get_global $cp) (i32.const 1)))
(i32.store8 (get_global $cp) (i32.const 0x00))
(set_global $cp (i32.add (get_global $cp) (i32.const 1))))
(func $compilePop
(call $emitICall (i32.const 2) (i32.const !popIndex)))
(func $emitICall (param $type i32) (param $n i32)
(call $emitConst (get_local $n))
(i32.store8 (get_global $cp) (i32.const 0x11))
(set_global $cp (i32.add (get_global $cp) (i32.const 1)))
(i32.store8 (get_global $cp) (get_local $type))
(set_global $cp (i32.add (get_global $cp) (i32.const 1)))
(i32.store8 (get_global $cp) (i32.const 0x00))
(set_global $cp (i32.add (get_global $cp) (i32.const 1))))
(func $emitBlock
(i32.store8 (get_global $cp) (i32.const 0x02))
(set_global $cp (i32.add (get_global $cp) (i32.const 1)))
(i32.store8 (get_global $cp) (i32.const 0x40))
(set_global $cp (i32.add (get_global $cp) (i32.const 1))))
(func $emitLoop
(i32.store8 (get_global $cp) (i32.const 0x03))
(set_global $cp (i32.add (get_global $cp) (i32.const 1)))
(i32.store8 (get_global $cp) (i32.const 0x40))
(set_global $cp (i32.add (get_global $cp) (i32.const 1))))
(func $emitConst (param $n i32)
(i32.store8 (get_global $cp) (i32.const 0x41))
(set_global $cp (i32.add (get_global $cp) (i32.const 1)))
(set_global $cp (call $leb128 (get_global $cp) (get_local $n))))
(func $emitEnd
(i32.store8 (get_global $cp) (i32.const 0x0b))
(set_global $cp (i32.add (get_global $cp) (i32.const 1))))
(func $emitBr (param $n i32)
(i32.store8 (get_global $cp) (i32.const 0x0c))
(set_global $cp (i32.add (get_global $cp) (i32.const 1)))
(i32.store8 (get_global $cp) (get_local $n))
(set_global $cp (i32.add (get_global $cp) (i32.const 1))))
(func $emitBrIf (param $n i32)
(i32.store8 (get_global $cp) (i32.const 0x0d))
(set_global $cp (i32.add (get_global $cp) (i32.const 1)))
(i32.store8 (get_global $cp) (get_local $n))
(set_global $cp (i32.add (get_global $cp) (i32.const 1))))
(func $emitSetLocal (param $n i32)
(i32.store8 (get_global $cp) (i32.const 0x21))
(set_global $cp (i32.add (get_global $cp) (i32.const 1)))
(set_global $cp (call $leb128 (get_global $cp) (get_local $n))))
(func $emitGetLocal (param $n i32)
(i32.store8 (get_global $cp) (i32.const 0x20))
(set_global $cp (i32.add (get_global $cp) (i32.const 1)))
(set_global $cp (call $leb128 (get_global $cp) (get_local $n))))
(func $emitAdd
(i32.store8 (get_global $cp) (i32.const 0x6a))
(set_global $cp (i32.add (get_global $cp) (i32.const 1))))
(func $emitEqualsZero
(i32.store8 (get_global $cp) (i32.const 0x45))
(set_global $cp (i32.add (get_global $cp) (i32.const 1))))
(func $emitGreaterEqualSigned
(i32.store8 (get_global $cp) (i32.const 0x4e))
(set_global $cp (i32.add (get_global $cp) (i32.const 1))))
(func $emitReturn
(i32.store8 (get_global $cp) (i32.const 0x0f))
(set_global $cp (i32.add (get_global $cp) (i32.const 1))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Word helper function
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(func $push (export "push") (param $v i32)
(i32.store (get_global $tos) (get_local $v))
(set_global $tos (i32.add (get_global $tos) (i32.const 4))))
(elem (i32.const !pushIndex) $push)
(func $pop (export "pop") (result i32)
(set_global $tos (i32.sub (get_global $tos) (i32.const 4)))
(i32.load (get_global $tos)))
(elem (i32.const !popIndex) $pop)
(func $TYPE
(local $p i32)
(local $end i32)
(set_local $end (i32.add (call $pop) (tee_local $p (call $pop))))
(block $endLoop
(loop $loop
(br_if $endLoop (i32.eq (get_local $p) (get_local $end)))
(call $shell_emit (i32.load8_u (get_local $p)))
(set_local $p (i32.add (get_local $p) (i32.const 1)))
(br $loop))))
(!def_word "TYPE" "$TYPE" !fNone !typeIndex)
(func $pushDataAddress (param $d i32)
(call $push (get_local $d)))
(elem (i32.const !pushDataAddressIndex) $pushDataAddress)
(func $pushDataValue (param $d i32)
(call $push (i32.load (get_local $d))))
(elem (i32.const !pushDataValueIndex) $pushDataValue)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Helper functions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(func $ensureCompiling
(if (i32.eqz (i32.load (i32.const !stateBase)))
(then
(unreachable))))
;; Toggle the hidden flag
(func $hidden
(i32.store
(i32.add (get_global $latest) (i32.const 4))
(i32.xor
(i32.load (i32.add (get_global $latest) (i32.const 4)))
(i32.const !fHidden))))
(func $memmove (param $dst i32) (param $src i32) (param $n i32)
(local $end i32)
(if (i32.gt_u (get_local $dst) (get_local $src))
(then
(set_local $end (get_local $src))
(set_local $src (i32.sub (i32.add (get_local $src) (get_local $n)) (i32.const 1)))
(set_local $dst (i32.sub (i32.add (get_local $dst) (get_local $n)) (i32.const 1)))
(block $endLoop
(loop $loop
(br_if $endLoop (i32.lt_u (get_local $src) (get_local $end)))
(i32.store8 (get_local $dst) (i32.load8_u (get_local $src)))
(set_local $src (i32.sub (get_local $src) (i32.const 1)))
(set_local $dst (i32.sub (get_local $dst) (i32.const 1)))
(br $loop))))
(else
(set_local $end (i32.add (get_local $src) (get_local $n)))
(block $endLoop
(loop $loop
(br_if $endLoop (i32.eq (get_local $src) (get_local $end)))
(i32.store8 (get_local $dst) (i32.load8_u (get_local $src)))
(set_local $src (i32.add (get_local $src) (i32.const 1)))
(set_local $dst (i32.add (get_local $dst) (i32.const 1)))
(br $loop))))))
(func $memset (param $dst i32) (param $c i32) (param $n i32)
(local $end i32)
(set_local $end (i32.add (get_local $dst) (get_local $n)))
(block $endLoop
(loop $loop
(br_if $endLoop (i32.eq (get_local $dst) (get_local $end)))
(i32.store8 (get_local $dst) (get_local $c))
(set_local $dst (i32.add (get_local $dst) (i32.const 1)))
(br $loop))))
;; LEB128 with fixed 4 bytes (with padding bytes)
;; This means we can only represent 28 bits, which should be plenty.
(func $leb128-4p (export "leb128_4p") (param $n i32) (result i32)
(i32.or
(i32.or
(i32.or
(i32.or
(i32.and (get_local $n) (i32.const 0x7F))
(i32.shl
(i32.and
(get_local $n)
(i32.const 0x3F80))
(i32.const 1)))
(i32.shl
(i32.and
(get_local $n)
(i32.const 0x1FC000))
(i32.const 2)))
(i32.shl
(i32.and
(get_local $n)
(i32.const 0xFE00000))
(i32.const 3)))
(i32.const 0x808080)))
;; Encodes `value` as leb128 to `p`, and returns the address pointing after the data
(func $leb128 (export "leb128") (param $p i32) (param $value i32) (result i32)
(local $more i32)
(local $byte i32)
(set_local $more (i32.const 1))
(block $endLoop
(loop $loop
(set_local $byte (i32.and (i32.const 0x7F) (get_local $value)))
(set_local $value (i32.shr_s (get_local $value) (i32.const 7)))
(if (i32.or (i32.and (i32.eqz (get_local $value))
(i32.eq (i32.and (get_local $byte) (i32.const 0x40))
(i32.const 0)))
(i32.and (i32.eq (get_local $value) (i32.const -1))
(i32.eq (i32.and (get_local $byte) (i32.const 0x40))
(i32.const 0x40))))
(then
(set_local $more (i32.const 0)))
(else
(set_local $byte (i32.or (get_local $byte) (i32.const 0x80)))))
(i32.store8 (get_local $p) (get_local $byte))
(set_local $p (i32.add (get_local $p) (i32.const 1)))
(br_if $loop (get_local $more))
(br $endLoop)))
(get_local $p))
(func $body (param $xt i32) (result i32)
(i32.and
(i32.add
(i32.add
(get_local $xt)
(i32.and
(i32.load8_u (i32.add (get_local $xt) (i32.const 4)))
(i32.const !lengthMask)))
(i32.const 8 #| 4 + 1 + 3 |#))
(i32.const -4)))
(func $readChar (result i32)
(local $n i32)
(if (i32.eq (get_global $preludeDataP) (get_global $preludeDataEnd))
(then
(return (call $shell_key)))
(else
(set_local $n (i32.load8_s (get_global $preludeDataP)))
(set_global $preludeDataP (i32.add (get_global $preludeDataP) (i32.const 1)))
(return (get_local $n))))
(unreachable))
(func $loadPrelude (export "loadPrelude")
(set_global $preludeDataP (i32.const !preludeDataBase))
(if (i32.ne (call $interpret) (i32.const 0))
(unreachable)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; A sieve with direct calls. Only here for benchmarking
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(func $sieve_prime (param i32)
(call $here (i32.const 131600)) (call $plus (i32.const 131600))
(call $c-fetch (i32.const 131600)) (call $zero-equals (i32.const 131600)))
(func $sieve_composite (param i32)
(call $here (i32.const 131600))
(call $plus (i32.const 131600))
(i32.store (get_global $tos) (i32.const 1))
(set_global $tos (i32.add (get_global $tos) (i32.const 4)))
(call $swap (i32.const 131600))
(call $c-store (i32.const 131600)))
;
(func $sieve (param i32)
(local $i i32)
(local $end i32)
(call $here (i32.const 131600))
(call $over (i32.const 131600))
(call $erase (i32.const 131600))
(call $push (i32.const 2))
(block $endLoop1
(loop $loop1
(call $two-dupe (i32.const 131600))
(call $dupe (i32.const 131600))
(call $star (i32.const 131600))
(call $greater-than (i32.const 131600))
(br_if $endLoop1 (i32.eqz (call $pop)))
(call $dupe (i32.const 131600))
(call $sieve_prime (i32.const 131600))
(if (i32.ne (call $pop) (i32.const 0))
(block
(call $two-dupe (i32.const 131600))
(call $dupe (i32.const 131600))
(call $star (i32.const 131600))
(set_local $i (call $pop))
(set_local $end (call $pop))
(block $endLoop2
(loop $loop2
(call $push (get_local $i))
(call $sieve_composite (i32.const 131600))
(call $dupe (i32.const 131600))
(set_local $i (i32.add (call $pop) (get_local $i)))
(br_if $endLoop2 (i32.ge_s (get_local $i) (get_local $end)))
(br $loop2)))))
(call $one-plus (i32.const 131600))
(br $loop1)))
(call $drop (i32.const 131600))
(call $push (i32.const 1))
(call $swap (i32.const 131600))
(call $push (i32.const 2))
(set_local $i (call $pop))
(set_local $end (call $pop))
(block $endLoop3
(loop $loop3
(call $push (get_local $i))
(call $sieve_prime (i32.const 131600))
(if (i32.ne (call $pop) (i32.const 0))
(block
(call $drop (i32.const -1))
(call $push (get_local $i))))
(set_local $i (i32.add (i32.const 1) (get_local $i)))
(br_if $endLoop3 (i32.ge_s (get_local $i) (get_local $end)))
(br $loop3))))
(!def_word "sieve_direct" "$sieve")
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Data
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(data (i32.const !baseBase) "\u000A\u0000\u0000\u0000")
(data (i32.const !stateBase) "\u0000\u0000\u0000\u0000")
(data (i32.const !moduleHeaderBase) !moduleHeader)
(data (i32.const !preludeDataBase) !preludeData)
(global $preludeDataEnd i32 (i32.const (!+ !preludeDataBase (string-length !preludeData))))
(global $preludeDataP (mut i32) (i32.const (!+ !preludeDataBase (string-length !preludeData))))
(func (export "tos") (result i32)
(get_global $tos))
(func (export "interpret") (result i32)
(local $result i32)
(if (i32.ge_s (tee_local $result (call $interpret)) (i32.const 0))
(then
;; Write ok
(call $shell_emit (i32.const 111))
(call $shell_emit (i32.const 107)))
(else
;; Write error
(call $shell_emit (i32.const 101))
(call $shell_emit (i32.const 114))
(call $shell_emit (i32.const 114))
(call $shell_emit (i32.const 111))
(call $shell_emit (i32.const 114))))
(call $shell_emit (i32.const 10))
(get_local $result))
(table (export "table") !tableStartIndex anyfunc)
(global $latest (mut i32) (i32.const !dictionaryLatest))
(global $here (mut i32) (i32.const !dictionaryTop))
(global $nextTableIndex (mut i32) (i32.const !tableStartIndex))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Compilation state
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(global $currentLocal (mut i32) (i32.const 0))
(global $localsCount (mut i32) (i32.const 0))
;; Compilation pointer
(global $cp (mut i32) (i32.const !moduleBodyBase)))