2022-08-21
I've been studying Forth recently, spcifically looking at a project called Planckforth by Koichi Nakamura. It's an amazing project and it's a great representation of why I like Forth so much. I'm going to write my thoughts down as I analyze Planckforth in case it's useful for anybody else.
In general Forths are either direct-threaded or indirect-threaded. Planckforth uses indirect threading. Here's a good explainer for the differences: https://muforth.nimblemachines.com/threaded-code/
Forth is just a simple, clever, lazy VM. In order to make use of the underlying processor's primitive instructions like ADD, MULT, LOAD, OUT etc, Forth wraps each of those instructions in a thin structure. So if your goal is to ADD a million numbers together, Forth is always going to be slower than assembly language or a traditional compiled language like C. But that minimal structure that we added allows us to now connect these assembly instructions together at runtime into more complex subroutines written in Forth. And because of the nature of that thin wrapper structure, we can subsequently connect Forth words, assembly primitives and any data we want into even more Forth words, like clicking Lego together.
Forth has a pair of very useful words called CREATE and DOES>.
Action Input buffer Stack after action
--------------------------------------------------------------------------------------------------------------------- --------------------------- ----------------------
**Phase 0: (does>) and does> are defined:**
input the text : (does>) latest >cfa 3 cells + ! ; : (does>) latest >cfa 3 cells + ! ;
execute ":"
enter compile mode ( 1 2 3)
build header for new word (does>) foobar
compile docol
compile "latest"
compile ">cfa"
compile "3"
compile cells
compile +
compile !
execute ;
back to interpreter
type the command : does> align 0 [compile] literal here cell- compile (does>) [compile] ; :noname swap ! ; immediate
execute :
enter compile mode
build header for new word `does>`
compile docol
compile align
compile 0
at this point 0 is a word that pushes the number 0 to the stack at runtime
execute [compile] since it's immediate
execute '
fetch word from stdin (pushes xt of word `literal` to stack)
execute ,
store xt of `literal`
execute exit
compile here
compile cell-
execute compile
execute '
fetch word from stdin (pushes xt of (does>) to stack)
execute (compile)
execute [compile]
execute '
fetch word from stdin (pushes xt of ; to stack)
execute literal
store L:lit and ;
litera
[ ' , ] literal , \ compile ,
1) indexed-array is defined:
type the command : indexed-array create cells allot does> swap cells + ;
execute word ":"
enter compile mode
build header for new word indexed-array
compile "docol"
compile "create" cells allot does> swap cells + ;
compile "cells" allot does> swap cells + ;
compile "allot" does> swap cells + ;
execute does> since it's immediate swap cells + ;
execute align
execute 0 ( 0 )
execute literal
stores L:lit and 0 ( )
execute here ( addr-of-Lit0+1 )
execute cell- ( addr-of-Lit0 )
execute L:lit
pushes (does>) ( addr-of-Lit0 (does>) )
execute ,
stores (does>)
execute ;
execute :noname ( addr-of-Lit0 addr-of-noname )
execute swap ( addr-of-noname addr-of-Lit0 )
execute !
writes addr-of-noname to Lit0's argument
e:exit
compile word "swap" cells + ;
compile word "cells" + ;
compile word "+" ;
execute ; since it's immediate
stores "exit"
exit compile mode
2) indexed-array is executed, fooooo is defined
type the command 20 indexed-array fooooo
execute 20 ( 20 ) indexed-array fooooo
execute indexed-array
execute create ( 20 ) fooooo
store new dict header
fetch word from stdin, store it
store docol
store L:lit
store here + 3cells
store nop
store exit
execute cells ( 20*cell )
execute allot ( )
here += 20*cell
execute L:lit
pushes (ptr to anon func) ( ptr-to-anon-func )
execute (does>)
execute latest ( ptr-to-anon-func dict-entry-of-fooooo )
execute >cfa ( ptr-to-anon-func cfa-of-fooooo )
execute 3 cells + ( ptr-to-anon-func foo-nop-ptr )
execute !
replaces fooooo's final nop with a call to the anon func
execute exit from end of (does>)
execute exit from end of indexed-array
back at interpreter
3) fooooo is executed
type the command 5 fooooo
execute 5 ( 5 )
execute fooooo
execute L:lit
push pointer to fooooo's storage space ( 5 fooooo-storage )
execute anonymous-func
execute swap ( fooooo-storage 5 )
execute cells ( fooooo-storage 5*cells )
execute + ( fooooo-storage+5*cells )
execute exit from anonymous-func
execute exit from fooooo
back at interpreter
Some good pages for learning about Forth: - http://yosefk.com/blog/my-history-with-forth-stack-machines.html - https://www.forth.com/starting-forth/9-forth-execution/ - https://www.bradrodriguez.com/papers/moving1.htm - https://www.bradrodriguez.com/papers/moving2.htm - https://github.com/TG9541/stm8ef
Washing machine application from "Forth Programmer's Handbook":
\ Port addresses
HEX
7000 CONSTANT MOTOR 7002 CONSTANT VALVE
7004 CONSTANT FAUCETS 7006 CONSTANT DETERGENT
7008 CONSTANT TIMER 700A CONSTANT CLUTCH
7010 CONSTANT LEVEL
DECIMAL
\ Basic commands
: ON ( port -- ) -1 SWAP OUTPUT ;
: OFF ( port -- ) 0 SWAP OUTPUT ;
: SECONDS ( n -- ) 1000 * MS ;
: MINUTES ( n -- ) 60 * SECONDS ;
\ Machine functions
: ADD ( port -- ) DUP ON 10 SECONDS OFF ;
: ?FULL ( -- n ) LEVEL INPUT ; \ factored out from TILL-FULL in "Starting FORTH"
: TILL-FULL ( -- ) BEGIN ?FULL UNTIL ;
: DRAIN ( -- ) VALVE ON 3 MINUTES VALVE OFF ;
: AGITATE ( -- ) MOTOR ON 10 MINUTES MOTOR OFF ;
: SPIN ( -- ) CLUTCH ON MOTOR ON 5 MINUTES
MOTOR OFF CLUTCH OFF ;
: FILL ( -- ) FAUCETS ON TILL-FULL FAUCETS OFF ;
\ Sequencing
: WASH ( -- ) FILL DETERGENT ADD AGITATE DRAIN ;
: RINSE ( -- ) FILL AGITATE DRAIN ;
: WASHER ( -- ) WASH SPIN RINSE SPIN ;
https://github.com/bfox9900/CAMEL99-ITC/blob/master/CosmicConquest/On%20Forth%20Coding%20Style.md
from Forth Dimensions May/June 1992 http://www.forth.org/fd/FD-V14N1.pdf
: attribute create 0 ,
does> create dup @ 1 rot +! ,
does> @ ;
attribute color
attribute shape
color red color blue color green
shape round shape square shape oval
Elizabeth Rather, Forth Dimensions March/April 1992:
: BRANCH ( -- ) R> @ >R ;
: 0BRANCH ( n -- ) 0= R @ CELL - R - AND R> + CELL + >R ;
https://gist.github.com/lbruder/10007431
: CONSTANTS ( n -- ) 0 DO I CONSTANT LOOP ;
10 CONSTANTS black brown red orange yellow green blue violet gray white
https://github.com/Forth-Standard/forth200x/blob/master/quotations.txt https://github.com/rufig/spf4-utf8/blob/master/devel/%7Epinka/lib/BigMath.f http://www.murphywong.net/hello/lf.htm