--- title: "Learning Forth with Planckforth" date: 2022-08-21 --- I've been studying Forth recently, spcifically looking at a project called [Planckforth](https://github.com/nineties/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 MOTOR3 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