In part -1- of this series this table was presented to to show some Forth words.
The header part of the table is in bold.
Table 1
| TOS | n1 | n2 | n3 |
SWAP | TOS | n2 | n1 | n3 |
DROP | TOS - 1 |
| n2 | n3 |
+ | TOS - 1 |
| n1 + n2 | n3 |
This table is not (yet) a real Forth table and also can be simplified.
Calculations in this table are 'normal algebra' and not in Forth RPN calculations.
'Normal algebra' n1 + n2 becomes in RPN n1 n2 +
As already mentioned in my previous post the change of TOS depends on the amount of bits. The TOS should perhaps should be decremented by 2 or another value depending on the amount of bits in each stack item and the memory width. It is a system constant depending on the implementation.
In this article series i will use SWDTH (stack width). In the RPN table TOS - 1 should be written as TOS SWDTH -
Table 2
| TOS | n1 | n2 | n3 |
SWAP | TOS | n2 | n1 | n3 |
DROP | TOS SWDTH - |
| n2 | n3 |
+ | TOS SWDTH - |
| n1 n2 + | n3 |
Some things in this table are still confusing (however it looks already more Forth).
For example a 'strange historical convention' of n1 n2 n3 where
n1 is the item on top of the stack, n2 is the item just below it and n3 is two steps below. Another way is to see n1 n2 n3 as memory locations where values calculated are stored. n1 is TOS @ and n2 is TOS SWDTH - @
Table 3
| TOS | n1 | n2 | n3 |
| TOS | TOS @ | TOS SWDTH - @ | TOS SWDTH 2* - @ |
SWAP | TOS | n2 | n1 | n3 |
DROP | TOS SWDTH - |
| n2 | n3 |
+ | TOS SWDTH - |
| n1 n2 + | n3 |
In a real 'physical' implementation of the table it would be needed to include items above the TOS (Top Of the Stack) in case items are added or pushed to the stack. Also it would be strange to represent the first items above the stack using n0 . It would be more systematic if the item on top of stack was referenced as n0 and use n+1 and n-1 for the items just above or below. However in Forth for many years items on the stack where called n1 n2 n3 and breaking with this convention would cause a lot of confusion. Also we do not want tot use complicated difficult to read terms as TOS @ or TOS SWDTH - @ So i decided (temporary) to use p0 and not n1 see tabel below.
Table 4
| TOS | ( n0 ? ) | n1 | n2 | n3 |
| TOS | p1 | p0 | p-1 | p-2 |
| TOS | TOS SWDTH + @ | TOS @ | TOS SWDTH - @ | TOS SWDTH 2* - @ |
SWAP | TOS |
| n2 | n1 | n3 |
DROP | TOS SWDTH - |
|
| n2 | n3 |
+ | TOS SWDTH - |
|
| n1 n2 + | n3 |
In the next evolution of the table i added an example of a Forth word that pushes a value to the stack.
It is the Forth word 0 . It is a number implemented in this Forth table where the value 0 is pushed to the stack.
In the table header a new Forth word PA is introduced ( shorthand for ParameterStackAddress ).
The location is described using Forth word PA to avoid using n1 n2 n3 or p0 p1 p2 notations,
: PA SWDTH * TOS @ + ;
Table 5
| TOS | ( n0 ? ) | n1 | n2 | n3 |
|
| p1 | p0 | p-1 | p-2 |
Location | TOS | 1 PA | 0 PA | -1 PA | -2 PA |
| TOS | TOS SWDTH + @ | TOS @ | TOS SWDTH - @ | TOS SWDTH 2* - @ |
SWAP | TOS |
| n2 | n1 | n3 |
DROP | TOS SWDTH - |
|
| n2 | n3 |
+ | TOS SWDTH - |
|
| n1 n2 + | n3 |
0 | TOS SWDTH + | 0 | n1 | n2 | n3 |
We now have almost everything described in Forth and RPN
To evolve the table further we need to do something with the gaps (empty spaces).
In fact there are several options to handle these gaps.
A) Handle a gap as something that not needs to be changed ( "Ignore" or "don't change" )
Table 6a
| TOS | ( n0 ? ) | n1 | n2 | n3 |
|
| p1 | p0 | p-1 | p-2 |
Location | TOS | 1 PA | 0 PA | -1 PA | -2 PA |
| TOS | TOS SWDTH + @ | TOS @ | TOS SWDTH - @ | TOS SWDTH 2* - @ |
SWAP | TOS | Ignore | n2 | n1 | Ignore |
DROP | TOS SWDTH - | Ignore | Ignore | Ignore | Ignore |
+ | TOS SWDTH - | Ignore | Ignore | n1 n2 + | Ignore |
0 | TOS SWDTH + | 0 | Ignore | Ignore | Ignore |
B) Fill gap with a "Null" value
"Null" is a special marker and keyword indicating that something has no value (in SQL )
Using option 2 ( Null) the small table the table would look as below.
Table 6b
| TOS | ( n0 ? ) | n1 | n2 | n3 |
|
| p1 | p0 | p-1 | p-2 |
| TOS | 1 PA | 0 PA | -1 PA | -2 PA |
| TOS | TOS SWDTH + @ | TOS @ | TOS SWDTH - @ | TOS SWDTH 2* - @ |
SWAP | TOS | Null | n2 | n1 | n3 |
DROP | TOS SWDTH - | Null | Null | n2 | n3 |
+ | TOS SWDTH - | Null | Null | n1 n2 + | n3 |
0 | TOS SWDTH + | 0 | n1 | n2 | n3 |
Both representations have advantages and disadvantages.
I like option B) using Null as this removes garbage (data that is no longer needed) from the system. Keeping items in a system that are no longer needed can introduce errors and vulnerabilities,
C) A third option could be using Null and, when no changes are needed use some kind of Noop (no operation, do nothing) indicator.
Table 6c
| TOS | ( n0 ? ) | n1 | n2 | n3 |
|
| p1 | p0 | p-1 | p-2 |
| TOS | 1 PA | 0 PA | -1 PA | -2 PA |
| TOS | TOS SWDTH + @ | TOS @ | TOS SWDTH - @ | TOS SWDTH 2* - @ |
SWAP | Noop | Null | n2 | n1 | Noop |
DROP | TOS SWDTH - | Null | Null | Noop | Noop |
+ | TOS SWDTH - | Null | Null | n1 n2 + | Noop |
0 | TOS SWDTH + | 0 | Noop | Noop | Noop
|
If Null and Noop are Forth words a table can be created that describes Forth words in Forth code. The Noop has as disadvantage that just rewriting the old value perhaps is as fast or even faster then evaluating that something is a Noop and not changing a value.
Table 7
| TOS | ( n0 ? ) | n1 | n2 | n3 |
|
| p1 | p0 | p-1 | p-2 |
| TOS | 1 PA | 0 PA | -1 PA | -2 PA |
| TOS | TOS SWDTH + @ | TOS @ | TOS SWDTH - @ | TOS SWDTH 2* - @ |
SWAP | Noop | Null | -1 PA | 0 PA | Noop |
DROP | TOS SWDTH - | Null | Null | Noop | Noop |
+ | TOS SWDTH - | Null | Null | 0 PA -1 PA + | Noop |
0 | TOS SWDTH + | 0 | Noop | Noop | Noop
|
Extending the table with more Forth words gives the next table (Table 8)
Table 8
| TOS | ( n0 ? ) | n1 | n2 | n3 |
|
| p1 | p0 | p-1 | p-2 |
| TOS | 1 PA | 0 PA | -1 PA | -2 PA |
| TOS | TOS SWDTH + @ | TOS @ | TOS SWDTH - @ | TOS SWDTH 2* - @ |
SWAP | Noop | Null | -1 PA | 0 PA | Noop |
DROP | TOS SWDTH - | Null | Null | Noop | Noop |
+ | TOS SWDTH - | Null | Null | 0 PA -1 PA + | Noop |
0 | TOS SWDTH + | 0 | Noop | Noop | Noop
|
1 | TOS SWDTH + | 1 | Noop | Noop | Noop |
2 | TOS SWDTH + | 2 | Noop | Noop | Noop |
3 | TOS SWDTH + | 3 | Noop | Noop | Noop |
4 | TOS SWDTH + | 4 | Noop | Noop | Noop |
-1 | TOS SWDTH + | -1 | Noop | Noop | Noop |
-2 | TOS SWDTH + | -2 | Noop | Noop | Noop |
Noop | Noop | Noop | Noop | Noop | Noop |
- | TOS SWDTH - | Null | Null | 0 PA -1 PA - | Noop |
* | TOS SWDTH - | Null | Null | 0 PA -1 PA * | Noop |
/ | TOS SWDTH - | Null | Null | 0 PA -1 PA / | Noop |
= | TOS SWDTH - | Null | Null | 0 PA -1 PA = | Noop |
@ | Noop | Null | @ | Noop | Noop |
! | TOS SWDTH - | Null | Null | Null | Noop |
2* | Noop | Null | 2 * | Noop | Noop |
2* | Noop | Null | 2* | Noop | Noop |
TOS | TOS SWDTH + | TOS | Noop | Noop | Noop |
TOS | TOS SWDTH + | "Value" | Noop | Noop | Noop |
SWDTH | TOS SWDTH + | "Value" | Noop | Noop | Noop |
SWDTH+ | Noop | Null | Noop | Noop | Noop |
SWDTH- | Noop | Null | Noop | Noop | Noop |
In the last part of table 8 some issues can be seen that need (and can) be solved. Therefor i did write it in Italics and some words i described in multiple ways.
! Can't be full described in this table.The result of ! will be that 2 items are removed from the stack. An address and a value. The value will be stored at the address. However it is not easy to extend the lookup table so it will include all addresses in memory. ( @ can be described in the table. However there must be some kind of low level implementation!)
2* multiplies the value that is on top of stack by two. It can be described as just doing 2 *
Written in Forth code : 2* 2 * ;
However in some Forth systems this is implemented in low level code as it can be executed fast, This is just one example, however in general this creates a dilemma when trying to make a table like this. Many Forth words can easy be defined as itself, However that does not explain what these Forth words do. Also sometimes these words can be implemented in another way using other Forth words or should call low level code.
TOS ( Top Of Stack) is a variable. It it can be put in the table just calling some implementation of itself. However an easy implementation is pushing 'the value of the address' on the stack.
SWDTH is a (system) constant, as described in the beginning of this article. A constant can be described in this table as it self, Or just put the value on the stack comparable to TOS
Some sequences of operations appear very often in this table. Examples are
TOS SWDTH + and TOS SWDTH + and also my new Forth word PA
: PA SWDTH * TOS @ + ;
This indicates that speeding up these sequences can speed up the Forth system.
We have seen that we can put the descriptions of (several) Forth words in a table.
Can that help us in a new Forth implementation and what about a 'Non sequential computing environment'? Yes, if we can describe Forth words in a table we can describe Forth in a table. This not only makes Forth portable, it also makes it possible we can create Forth in a table environment, e.g. a relational database.