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!