<!DOCTYPE html>
<html>
<head>
 <meta charset="utf-8">
 <meta name="viewport" content="width=device-width, initial-scale=1">
 <title>Learning Forth with Planckforth</title>
 <link rel="stylesheet" type="text/css" href="/style.css">
</head>
<body>
 <header>
   <h1 class="title">Learning Forth with Planckforth</h1>
   <p class="date">2022-08-21</p>
 </header>
<p>I've been studying Forth recently, spcifically looking at a project called <a href="https://github.com/nineties/planckforth">Planckforth</a> 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.</p>
<p>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/</p>
<p>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.</p>
<p>Forth has a pair of very useful words called CREATE and DOES&gt;.</p>
<pre><code>Action                                                                                                                 Input buffer                          Stack after action
---------------------------------------------------------------------------------------------------------------------  ---------------------------           ----------------------
**Phase 0: (does&gt;) and does&gt; are defined:**
input the text : (does&gt;) latest &gt;cfa 3 cells + ! ;                                                                      : (does&gt;) latest &gt;cfa 3 cells + ! ;
   execute &quot;:&quot;
       enter compile mode                                                                                                                                   ( 1 2 3)
       build header for new word (does&gt;)                                                                                foobar
       compile docol
   compile &quot;latest&quot;
   compile &quot;&gt;cfa&quot;
   compile &quot;3&quot;
   compile cells
   compile +
   compile !
   execute ;
back to interpreter
type the command : does&gt; align 0 [compile] literal here cell- compile (does&gt;) [compile] ; :noname swap ! ; immediate
   execute :
       enter compile mode
       build header for new word `does&gt;`
       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&#39;s immediate
       execute &#39;
           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 &#39;
           fetch word from stdin (pushes xt of (does&gt;) to stack)
       execute (compile)
           execute [compile]
               execute &#39;
                   fetch word from stdin (pushes xt of ; to stack)
               execute literal
                   store L:lit and ;


           litera
   [ &#39; , ] literal ,   \ compile ,

1) indexed-array is defined:
type the command : indexed-array create cells allot does&gt; swap cells + ;
   execute word &quot;:&quot;
       enter compile mode
       build header for new word indexed-array
       compile &quot;docol&quot;
   compile &quot;create&quot;                                                            cells allot does&gt; swap cells + ;
   compile &quot;cells&quot;                                                             allot does&gt; swap cells + ;
   compile &quot;allot&quot;                                                             does&gt; swap cells + ;
   execute does&gt; since it&#39;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&gt;)                           ( addr-of-Lit0 (does&gt;) )
       execute ,
           stores (does&gt;)
       execute ;
       execute :noname                              ( addr-of-Lit0 addr-of-noname )
       execute swap                                 ( addr-of-noname addr-of-Lit0 )
       execute !
           writes addr-of-noname to Lit0&#39;s argument
       e:exit
   compile word &quot;swap&quot;                                                              cells + ;
   compile word &quot;cells&quot;                                                             + ;
   compile word &quot;+&quot;                                                                 ;
   execute ; since it&#39;s immediate
       stores &quot;exit&quot;
       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&gt;)
           execute latest                              ( ptr-to-anon-func dict-entry-of-fooooo )
           execute &gt;cfa                                ( ptr-to-anon-func cfa-of-fooooo )
           execute 3 cells +                           ( ptr-to-anon-func foo-nop-ptr )
           execute !
               replaces fooooo&#39;s final nop with a call to the anon func
           execute exit from end of (does&gt;)
       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&#39;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</code></pre>
<p>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</p>
<p>Washing machine application from "Forth Programmer's Handbook":</p>
<pre><code>\ 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 &quot;Starting FORTH&quot;
: 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 ;</code></pre>
<p>https://github.com/bfox9900/CAMEL99-ITC/blob/master/CosmicConquest/On%20Forth%20Coding%20Style.md</p>
<p>from Forth Dimensions May/June 1992 http://www.forth.org/fd/FD-V14N1.pdf</p>
<pre><code>: attribute create 0 ,
           does&gt;  create dup @ 1 rot +! ,
           does&gt;  @ ;

attribute color
attribute shape

color red     color blue    color green
shape round   shape square  shape oval</code></pre>
</body>
</html>