Persistent Jump Table

Date:2018-11-11

Intro

The cookbook section holds an explanation, how persistent Jump tables can be constructed.

The table used in the inner workings of the DCF clock has the following features:

  • there are #60 entries, some unused
  • the functions called will consume one item from the stack
  • the default action should then be drop rather than noop
  • the stored functions are accessed exclusively through the table, their name is not needed. So the will be defined using noname:

Creating the RAM table

First we create the temporary RAM table by simply alloting 60 cells:

\ create temporary RAM table
variable dcf.tmp.table #60 cells allot

Then we add the XT of drop as default:

' drop dcf.tmp.table #60 ramtable.init

At this point we have a valid RAM table, allthough it will not lead to much useful action as is.

To simplify the code generating the table, we add a short hand to store a given XT at position idx in the RAM table just defined.

: >rt ( xt idx -- )  dcf.tmp.table >ramtable ;

Creating noname: functions, storing their XTs in the RAM table

Just for the sake of the argument we define a word, which is lighting up an LED, if the value on the stack is non-zero.

:noname ( x -- )
  if    ( not zero )  led.0 on
  else  ( zero )      led.0 off
  then
;  #31 >rt

This function is then added at index #31 into the RAM table.

Whatever the behaviour of the newly defined function, all we know is that :noname will leave the XT on the stack, and >rt will pick that (and the index) up and place the XT in the RAM table at index #31.

Please Note: There should be no allocation of RAM space during this stage. All needed variables must be defined before alloting the temporary table. Beware of defining words! If we can stick to this rule, we can sort of release the RAM afterwards.

In the code this section is much longer of course. We will see more details in section Reading a dcf77 receiver.

Preserve the precious new content

After defining all the needed words and filling the RAM table, we want to preserve the precious new content.

dcf.tmp.table #60 >flashtable cmd_map

This will define a new word cmd_map, which will leave the address of its parameter section on the stack, just like an ordinary variable. The parameter section will hold the saved content.

Release the space of the temporary RAM table

If we got this far, and if we did not allocate any RAM space after the temporary RAM table, the we can release it by boldly resetting the here pointer

dcf.tmp.table to here

If funny, bad, or inexplicable things happen while running your code, disable this step and see, if it changes the behaviour. Just in bloody case.

Using the preserved table at last

The particular jump table in this case is used to call the correct function after a second has passed. It will get the value of the received DCF Bit and deal with it according to its position in the telegram.

Using the above table is a matter of fetch and execute:

: pos.cmd ( index -- )
  dup 0 #60 within if
    ( position ) cmd_map +  @i execute
  else
    drop
  then
;

Somewhere in your code you will add the value to be consumed onto the stack, add the index into the jump table on top and call pos.cmd.

Putting it all together

The code lines give above are bracketing the lengthy section of code, which defines all the functions needed to handle a DCF77 telegram.

\ create temporary RAM table
variable dcf.tmp.table #60 cells allot \  only temporary really!
\ fill RAM table with ' drop  \ noop -> drop: remove argument!
' drop dcf.tmp.table #60 ramtable.init
\ dcf.tmp.table #60 ramtable.dump \ DEBUG
: >rt ( xt idx -- )  dcf.tmp.table >ramtable ;

\ code snippet XTs to ram table
\ structure of functions
\ :noname  ( 0|1 -- )
\     if   ( bit:1  ) ...
\     else ( bit:0  ) ...
\     then ( always ) ...
\ ;                                     #idx >rt
\ :noname
\     drop ( always ) ...
\ ;                                     #idx >rt

\ #0   always 0
:noname
  \ ...
;                                         #0 >rt

\ ... more definitions here ...

:noname
  \ ...
;                                        #58 >rt

\ dcf.tmp.table #60 ramtable.dump \ DEBUG
\ copy RAM table to FLASH
dcf.tmp.table #60 >flashtable pos_cmd_map
\ release RAM
\ WARNING: there might be dragons around!
\          Iff so, disable the next line
Dcf.tmp.table to here

: pos.cmd ( index -- )
  dup 0 #60 within if
    ( position ) pos_cmd_map +  @i execute
  else
    drop
  then
;