An implementation of FORTH-like virtual stack machine in Zig
(/opt/zig-0.14/zig build -Doptimize=ReleaseFast
to build)
zf> .dict
bye . cr .dict .dstk .rstk .text .data dup drop + 1+ - 1- * / mod = <> > < 0= 0<> 0> 0<
max min ! @ allot create : ; if else then do iter i loop next begin again until (
vm.code[0] = vm.dict.getWordNumber("prom").?;
vm.code[1] = vm.dict.getWordNumber("read").?;
vm.code[2] = vm.dict.getWordNumber("jifz").?;
vm.code[3] = 7;
vm.code[4] = vm.dict.getWordNumber("proc").?;
vm.code[5] = vm.dict.getWordNumber("jump").?;
vm.code[6] = 0;
vm.code[7] = vm.dict.getWordNumber("bye").?;
zf> .text
code[0] = 0xffffffffffffff01 // print 'zf> ' ('zf(c)> ' when "compiling") <--|
code[1] = 0xffffffffffffff02 // read a word |
code[2] = 0xffffffffffffff05 // if TOS == 0, jump to |
code[3] = 0x0000000000000007 // this location -->| |
code[4] = 0xffffffffffffff03 // process the word | |
code[5] = 0xffffffffffffff04 // unconditional jump | |
code[6] = 0x0000000000000000 // to the beginning | -->|
code[7] = 0xffffffffffffff0b // bye <--|
zf> : raise-to-the-power-of-two
zf(c)> dup * .
zf(c)> ;
zf> : τετραγωνίζω
zf(c)> dup * .
zf(c)> ;
zf> : в-квадрате
zf(c)> dup * .
zf(c)> ;
zf>
zf> 5 raise-to-the-power-of-two cr
25
zf> 8 τετραγωνίζω cr
64
zf> 9 в-квадрате cr
81
zf> : foreach
zf(c)> iter i . next
zf(c)> ;
zf>
zf> 5 1 foreach cr
1 2 3 4
zf>
NOTE: iter
and next
are just synonyms for standard words do
and loop
, respectively.
zf> 3 allot
zf> .data
data[0] = 0x0000000000000000
data[1] = 0x0000000000000000
data[2] = 0x0000000000000000
zf> -3 allot
zf> .data
zf> create var1 1 allot
zf> create var2 1 allot
zf> .data
data[0] = 0x0000000000000000
data[1] = 0x0000000000000000
zf> 7 var1 !
zf> 8 var2 !
zf> .data
data[0] = 0x0000000000000007
data[1] = 0x0000000000000008
zf> var1 @ var2 @ * . cr
56
zf> : var create 1 allot ;
zf> : km 1000 * ;
zf> : hour 3600 * ;
zf> var distance
zf> var time
zf> var speed
zf> 100 km distance !
zf> 1 hour time !
zf> distance @ time @ / speed !
zf> .data
data[0] = 0x00000000000186a0 ('distance')
data[1] = 0x0000000000000e10 ('time')
data[2] = 0x000000000000001b ('speed')
zf> : take-and-print @ . cr ;
zf> speed take-and-print
27 \ in m/s
$ cat lib.zf app.zf | zig-out/bin/zf
zf> zf> zf> zf>
7
zf>
Bye, see you later!
$ cat lib.zf
: var create 1 allot ;
: ? @ . cr ;
$ cat app.zf
var x
7 x !
cr x ?
- Starting FORTH
- Forth standard
- Open FirmWare/Forth Lessons
- Implementing a FORTH virtual machine
- MuP21 instruction set
- do-loop
- do-leave-loop
- create-does
- Multiple 'DOES>'
- immediate -> ndcs
zf> .text
text[0000] = 0000000000000001 `prom`
text[0001] = 0000000000000002 `read`
text[0002] = 0000000000000005 `jifz`
text[0003] = 0000000000000007 (to 7)
text[0004] = 0000000000000003 `proc`
text[0005] = 0000000000000004 `jump`
text[0006] = 0000000000000000 (to 0)
text[0007] = 000000000000000b `bye`
now add some word
: odd? 1 and 0<> if 1 else 0 then . cr ;
zf> .text
...
text[0008] = 0000000000000007 `lit`
text[0009] = 0000000000000001 (1)
text[000a] = 0000000000000016 `and`
text[000b] = 0000000000000026 `0<>`
text[000c] = 0000000000000005 `jifz`
text[000d] = 0000000000000012 (to 12)
text[000e] = 0000000000000007 `lit`
text[000f] = 0000000000000001 (1)
text[0010] = 0000000000000004 `jump`
text[0011] = 0000000000000014 (to 14)
text[0012] = 0000000000000007 `lit`
text[0013] = 0000000000000000 (0)
text[0014] = 000000000000000c `.`
text[0015] = 000000000000000d `cr`
text[0016] = 0000000000000006 `return`
zf> : self-incrementing-var create 1 allot does> dup @ 1+ swap ! ;
zf>
zf> self-incrementing-var i1
zf> self-incrementing-var i2
zf> .data
...
data[2] = 0x0000000000000000 ('i1')
data[3] = 0x0000000000000000 ('i2')
zf> i1 i1 i1 i1 i1 i1
zf> i2
zf> .data
...
data[2] = 0x0000000000000006 ('i1')
data[3] = 0x0000000000000001 ('i2')
: array ( n -- )
create dup , allot
does>
over 1 < if
drop drop
else
2dup @ > if
drop drop
else
over + swap drop
then
then
;
text[0078] = 0000000000000027 'create'
text[0079] = 0000000000000006 'dup'
text[007a] = 0000000000000048 ','
text[007b] = 0000000000000026 'allot'
text[007c] = 0000000000000029 'does'
text[007d] = 0000000000000003 'ret'
text[007e] = 000000000000003d 'over'
text[007f] = 0000000000000005 'lit'
text[0080] = 0000000000000001 (1)
text[0081] = 0000000000000017 '<'
text[0082] = 0000000000000002 'jifz'
text[0083] = 0000000000000088 (-->88)
text[0084] = 0000000000000007 'drop'
text[0085] = 0000000000000007 'drop'
text[0086] = 0000000000000001 'jump'
text[0087] = 0000000000000095 (-->95)
text[0088] = 000000000000003f '2dup'
text[0089] = 0000000000000019 '@'
text[008a] = 0000000000000016 '>'
text[008b] = 0000000000000002 'jifz'
text[008c] = 0000000000000091 (-->91)
text[008d] = 0000000000000007 'drop'
text[008e] = 0000000000000007 'drop'
text[008f] = 0000000000000001 'jump'
text[0090] = 0000000000000095 (-->95)
text[0091] = 000000000000003d 'over'
text[0092] = 000000000000000f '+'
text[0093] = 0000000000000008 'swap'
text[0094] = 0000000000000007 'drop'
text[0095] = 0000000000000003 'ret'
zf> 3 array a
zf> 3 array b
zf>
zf> 7 1 b !
zf> 8 2 b !
zf> 9 3 b !
zf> .data
data[0] = 0x0000000000000003 ('a')
data[1] = 0x0000000000000000 ('-')
data[2] = 0x0000000000000000 ('-')
data[3] = 0x0000000000000000 ('-')
data[4] = 0x0000000000000003 ('b')
data[5] = 0x0000000000000007 ('-')
data[6] = 0x0000000000000008 ('-')
data[7] = 0x0000000000000009 ('-')
zf> 5 ' . exec cr
5
: endif postpone then ; immediate
: if-so-then postpone if ; immediate
zf> : not-zero? if-so-then 111 . cr else 222 . cr endif ;
zf> 5 not-zero?
111
zf> 0 not-zero?
222
zf> -5 not-zero?
111
: if postpone _if ; immediate
: then postpone _then ; immediate
: THEN postpone _if ; immediate
: ELSE postpone else ; immediate
: IF postpone _then ; immediate
zf> : odd(v1)? 1 and if 1 . cr else 0 . cr then ;
zf> : odd(v2)? 1 and THEN 1 . cr ELSE 0 . cr IF ;
zf> 3 odd(v1)?
1
zf> 4 odd(v1)?
0
zf> 3 odd(v2)?
1
zf> 4 odd(v2)?
0