===========
Jump Tables
===========

:Author: Erich Wälde

Summary: store precomputed values in a flash table / constructing a jump table

I wanted to store precomputed values in a permanent table. In one case
the values were precomputed by evaluating a special declaration syntax.
In the other case a list of :command:`:noname ... ;` Definitions  were 
going to be accessed through a (jump) table.

For the second problem I wrote down something like

.. warning:: does not work, please continue to read for the solution!

.. code-block:: forth

  create Table
    :noname  1 VarA +! ;  ,
    :noname  2 VarA +! ;  ,
    ... 

This is not only naive, but also wrong: I'm mixing the compiled code with
the XTs to be stored. So the table did contain the XTs in the wrong
places mixed with the corresponding code. Once I understood this, I came
up with the idea to generate the code and store the XTs in a RAM table
first, and then copy the table from RAM to FLASH. Additionally, the table
was initialised with :command:`noop` as default value, since several entries
remained *empty*, i.e. had nothing to do. I could have pushed the XTs on
the stack, but with >50 entries this did not seem correct to me. Here we
go:

First we define the length of the final table in *entries* and allocate
the corresponding RAM space and proved a function to fill it with default
values (:command:`fill` would not help here, because :command:`fill` copies 
bytes rather than cells):

.. code-block:: forth

   variable SomeVar  \ variables needed by the code snippets should
   variable OtherVar \ go BEFORE variable T.ram

   #10 constant T.len
   variable T.ram T.len cells allot
   : T.init
     ['] noop
     T.len 0 do dup T.ram i cells +  ! loop
     drop 
   ;

The RAM space can be *released* after producing the flash table, even
though :command:`T.ram` continues to exist as a word.

When compiling the :command:`:noname` code snippets (think *anonymous functions*),
we want to store the freshly generated XT at a given location in the ram
table. A separate word makes the source code look nice (provisions
against index overflow were added):

.. code-block:: forth

   : >T.ram ( xt idx -- )
     dup 0 T.len within if
       cells T.ram + !
     else
       drop \ or throw
     then
   ;

To copy the content of :command:`T.ram` to a new table in flash, the following
function will do the work. In expects the number of items and the source
address on the stack and consumes the next token of the source code as
name for the new table.

.. code-block:: forth

   : >ftable ( srcaddr len -- ) ( ccc.name )
     create  ( consumes ccc.name )
     ( len ) 0 do
       ( srcaddr ) dup  i cells +  @  ,
     loop
     ( srcaddr ) drop
     does>  \ fixme: needed???
   ; 

Now we are equipped to compile the anonymous functions and store the XTs
in :command:`T.ram`:

.. code-block:: forth

   T.init

   :noname  #1 SomeVar +! ;        #3 >T.ram  \ function for field #3
   :noname  #8 SomeVar +! 
            #1 OtherVar ! ;        #4 >T.ram  \ function for field #4 

Note that the *anonymous functions* can be of arbitrary length. The
order, in which the fields in :command:`T.ram` is filled, is irrelevant. 
It is not neccessary to fill all fields, since they all were initialized 
with the XT of :command:`noop`.

After the table is prepared to our liking, we copy it to flash:

.. code-block:: forth

   T.ram T.len >ftable T.flash

The new, permanent table is called :command:`T.flash` in this example. We can now
release T.ram with

.. code-block:: forth  

   T.ram to here

provided we did *not* define any other variables in the meantime. The XTs
in :command:`T.flash` can be called like this:

.. code-block:: forth

   : T.run ( index -- )
     dup 0 T.len within if
       ( index ) T.flash +  @i  execute
     else
       drop \ or throw
     then
   ;