Visualising the Forth Stack

Visualize - form a mental picture of something that is invisible or abstract.

                          -.-.-,~ .
                          )     (
                          |_    |
                          /(_)---`\
                         (_      -'
The Forth Stack is doing  ]      |
my head in man ...        |    _,')
                          [_,-'_-'(
                         (_).-'    \
                         / /        \

Note

the Ascii code below is inserted into the Fourth source and NOT commented out for this article. The code won’t run as it is with the Ascii text uncommented. I wrote this page a few years ago and this method definitely helped me visualise the stack but these days I use a very simple method I call “pr”

Forth looks like a Stealth Aircraft

Forth is a very unique language, it’s a bit like a stealth aircraft because when viewed from different angles it looks like something else entirely.

One Forth statement may look a lot like Asembly Language :-

01  24 lshift $48000800 bis!

Yet, another line of Forth code, where the syntax is now that of the problem it is solving might look totally different, so what’s going on ?

: blink-forever begin blink key? until ;                \ blink until a keyboard key is pressed

Disruptively Simple

Whilst Forth may look very confusing to the uninitiated, it is actually disruptively simple under the covers. Once the ‘aha factor’ kicks in; and the light bulb over your head turns on, you will wonder why you ever had any trouble understanding Forth in the first place. You may even do cartwheels of joy at discovering a awesome problem solving programming language that’s been around since the 70’s.

See also

“Starting Forth”, an old classic for beginners : http://home.iae.nl/users/mhx/sf.html

The Stack

The next area of confusion, especially for me, (coming from a hobbyist C and Assembly Language background) was the Forth Stack, otherwise known as the Data, or Parameter Stack.

Non Stack Code

A Forth program which has reached the point where its syntax is that of the problem it has solved can be very easy to read. I bet you get the general idea of what this code is doing ?

For those that don’t, “26 temperature” will light a Magenta colored RGB led.

\ composite color table
: white        red-on      green-on        blue-on     ;
: magenta      red-on      green-off       blue-on     ;
: red          red-on      green-off       blue-off    ;
: yellow       red-on      green-on        blue-off    ;
: cyan         red-off     green-on        blue-on     ;
: green        red-off     green-on        blue-off    ;
: blue         red-off     green-off       blue-on     ;
: off          red-off     green-off       blue-off    ;

\ temp selection 10 C to 27 C as we only have 7 colors

: temperature ( temp -- color )
   dup 0  = if     off     else
   dup 10 < if     blue    else
   dup 13 < if     green   else
   dup 16 < if     cyan    else
   dup 19 < if     yellow  else
   dup 22 < if     red     else
   dup 25 < if     magenta else
   dup 28 < if     white   else
   ." ---- error, temp is above 28 C " cr
   THEN THEN THEN THEN THEN THEN THEN THEN
;

Stack Code

A more complex program involving multi Stack depths is another issue entirely.

The following Forth program (Minmax) takes data from an array containing X analogue to digital converter samples and produces the minimum, maximum, noise and average values like so:

Minmax Output

------------------------------------------
mimnax code output for input: "800 minmax"
------------------------------------------

data, minv and maxv = 1215 loop:799
data = 1215 loop:798
data = 1218 (new maxv) loop:797
... lots of lines of data
data = 1215 loop:1
data = 1217 loop:0
data = 1216 loop:-1

Total: 800 data points in mV
Noise = 16 mV
Max     = 1225     (Max - 1/2 Noise) = 1217
Min     = 1209     (Min + 1/2 Noise) = 1217
Average = 1220

ok.

Minmax Source Code

This is Minmax in all its Forthy glory, can you understand what the Stack is doing, or is that a headache you feel coming on ?

------------------
Minmax Source Code
------------------



: index? ( index? -- element ) raw-adc-data SWAP cells + @ ;                           \ fetch array elements

0 variable maxv
0 variable minv
0 variable counter
0 variable average

: minmax ( array elements -- min max average ) 1 - dup >r counter ! cr cr
   counter @ index? dup dup dup minv ! maxv ! average ! ." data, minv and maxv = " .   \ save last array element into maxv and minv
       begin
           counter @ dup dup  ." loop:" . cr 0  >=  while                              \ index 0 = loop -1
           index? dup dup dup dup dup average @ + average ! ." data = " .
               minv @ < if
               minv ! ." (new minv) "
                   else        drop                                                    \ data > minv so it's ignored
                   maxv @ > if
                   maxv ! ." (new maxv) "
                   else drop                                                           \ data < maxv so it's ignored
                   then
               then
               counter @ 1 - counter !
       repeat
   drop
   cr
   ."  Total: " r@ 1 + . ." data points in mV" cr
   ."  Noise = " maxv @ minv @ - dup . 2 / dup dup ." mV" cr
   maxv @ dup ."  Max     = " . ."     (Max - 1/2 Noise) = " swap - . cr
   minv @ dup ."  Min     = " . ."     (Min + 1/2 Noise) = " swap + . cr
   ."  Average = "  average @ r> / . cr cr
;

Now lets insert some Stack Diagrams into the code to help visualise where the data goes as it is ‘popped’ off the Stack. This way you can account for every DUP and every DROP quite easily, compared to holding them in your head as you debug the program.

Minmax with Stack Diagrams

Is this code any clearer, do you get some idea where data pushed onto the stack is going ?

------------------------------------------------------
Minmax source code with embedded Asciio Stack Diagrams
------------------------------------------------------

: minmax ( array elements -- min max average ) 1 - dup >r counter ! cr cr
    .------------.
    |   Stack    |
    |------------|
    | counter -1 |-----> >r
    | dup        |-----> counter !
    '------------'
   counter @ index? dup dup dup minv ! maxv ! average ! ." data, minv and maxv = " .     \ save last array element into maxv and minv
    .--------------.
    |  Data Stack  |
    |--------------|
    | counter valu |-----> minv !
    | dup          |-----> maxv !
    | dup          |-----> average !
    | dup          |-----> ." data, minv and maxv = " .
    '--------------'
   begin
       counter @ dup dup  ." loop:" . cr 0  >=  while                                      \ smallest index 0 = loop -1
    .------------------.
    |  data stack      |
    |------------------|
    | new counter valu |-----> ." loop:" .
    | dup              |-----> 0 >=  while------.
    | dup              |                        'index? or DROP after repeat
    '------------------'
       index? dup dup dup dup dup average @ + average ! ." data = " .
    .--------------.
    |  Data Stack  |
    |--------------|
    | next element |-----> average @ + average !
    | dup          |-----> ." data = " .
    | dup          |-----> minv @ < if--.
    | dup          |                    '------> minv ! ." (new minv) " or DROP after ELSE
    | dup          |-----> maxv @ > if---.
    | dup          |                     '---->  maxv ! ." (new maxv) " or DROP after ELSE
    '--------------'
       minv @ < if
       minv ! ." (new minv) "
       else    drop                                                                        \ data > minv so it's ignored
           maxv @ > if
           maxv ! ." (new maxv) "
           else drop                                                                       \ data < maxv so it's ignored
           then
       then
       counter @ 1 - counter !                                                             \ Decrement counter
   repeat
   drop
   cr
   ."  Total: " r@ 1 + . ." data points in mV" cr
   ."  Noise = " maxv @ minv @ - dup . 2 / dup dup ." mV" cr
   maxv @ dup ."  Max     = " . ."     (Max - 1/2 Noise) = " swap - . cr
   minv @ dup ."  Min     = " . ."     (Min + 1/2 Noise) = " swap + . cr
             ."  Average = "  average @ r> / . cr cr
;

This code includes Stack Diagram text boxes made using Asciio. It has made a big difference helping me visualise the Forth Stack, perhaps it may do the same for you ? (note: the text boxes are not commented-out for clarity).

I don’t leave the text stack diagrams in my code, they’re only used during design and debugging.

         ,       ,
         \\_    /|
         /- _`-/ '
        (/\/ \  /\
        O O   ) / |
        `-^--'`<  '
       (_)   _  )/
        `.___/` /
          `--' /
<---.   __ / __ \
<---|==(fl)=) \ /===
<---'   `-' `._,'\
           \     /
            ( ( / \__
         ,---_' |    \
         `-(____)    V

Asciio on FreeBSD RULES!