Values

The standard VALUE gives access to memory content like a variable does. The difference between these two is that a value gives a actual data whereas a variable leaves the address of the data on the stack. The place, where a value stores the data is usually not known. There is only one way to change it: use of TO.

> 42 Evalue answer
 ok
> answer .
 42 ok
> 4711 to answer
  ok
> answer .
  4711
>

This resembles the intended usage pattern for EEPROM: Write seldom, read often.

The forth standard defines a few value types: 2VALUE for double cell data, FVALUE for floating point numbers and the single cell sized VALUE itself. They all use the same TO command to change their content. This requires a non-trivial implementation to achieve it. Amforth uses a simple data structure for each value in the dictionary (flash), almost identical to the one used by defer. The first element contains the address of the actual data. This first field is followed by 2 execution tokens (XT) for the read and write operations. This makes the runtime operations fairly easy. The read operation (the 2nd element in the data structure) is called with the address of the 1st element. It is expected that the read operation leaves the data on the data stack. Similiar the write operation. The TO command simply executes the write execution token (the 3rd element). The similarities between values and defers goes as far as to and is are in fact identical and can be used with both.

This generic approach allows not only single cell data in EEPROM but any data everwhere. The following examples illustrate this with an implementation of a value that stores a single byte in RAM and a cached version of the standard EEPROM value. They have in common that calling their names give the data and applying TO to them stores new data.

cvalue

Cvalues store a single byte in RAM. The first element in the value data structure in the dictionary is the address of the RAM byte. The defining word allocates it. Like any other RAM based data its content is not preserved over resets and restarts.

\ two helper functions, not called directly
: c@v @i c@ ;
: c!v @i c! ;

: cvalue ( n "name" -- )
  (value)           \ create a new wordlist entry
  here ,            \ the address of the RAM memory
  ['] c@v ,         \ method for the read operation
  ['] c!v ,         \ method for the write (TO) operation
  here c!           \ initialize the RAM content
  1 allot           \ formally allocate the RAM byte
;

Using this new value is straight forward:

> 42 cvalue answer
ok
> answer .
42 ok
> 17 to answer
ok
> answer .
17 ok
>

After its definition the new size restricted value is used like any other value. To read it, simply call its name. To write to it, use the TO command. As a bonus, all operations are save against overflows:

> $dead to answer
ok
> hex answer .
AD ok
>

cached Value

A cached value is a value that stores the data in EEPROM but tolerates heavy write access by using a RAM cell as a cache. This RAM cell gets all write operations. The eeprom is not written until an explicit flush is performed. At startup the cache needs to be warmed, this is not done automatically.

 \ 2 is a magic number
 : @cache 2 + @i @ ;
 : !cache 2 + @i ! ;

 \ cache related words
 : flush-cache 1+ dup 2 + @i @ swap @i !e ;
 : warm-cache  1+ dup @i @e swap 2 + @i ! ;

 : cache-value
  (value)                           \ create the vocabulary entry
  dup ehere dup , dup cell+ to ehere !e \ allocate an EEPROM cell.
  ['] @cache ,                      \ XT for the read method
  ['] !cache ,                      \ XT for the write methon
  here 2 ( 1 cell ) allot dup , !   \ allocate a RAM cell and initialize it
;

The following example session creates a cached value and demonstrates the content of the two memory’s during normal execution.

> ehere \ keep the eeprom address for later direct access
 ok
> 42 cache-value c-dp
 ok
> 17 to c-dp
 ok
> c-dp . dup @e .
\ RAM and EEPROM contents are different!
 17 42 ok
> ' c-dp flush-cache
 ok
> c-dp . dup @e .
 17 17 ok
>

Note that there is a difference in programming style between the load/store and the addiional warm/flush operations. The latter use a code sequence like

' value method

instead of the standard TO schema

method value

Its fairly simple to achieve the TO schema for the other commands as well, but since this requires a parsing word (which is state smart too) the forth gurus consider this suboptimal. A second argument against may be the growing acceptance of the OO notation object method with object beeing kind of an address.

 : flush
  ' state @ if
    postpone literal postpone flush-cache
  else
    flush-cache
  then
; immediate

Double Cell RAM Value

A very compact implementation (a single short word) makes use of Quotations:

\ a value in RAM with 2 cells data storage
\ requires quotations and 2@/2! from double wordset

: 2rvalue ( d -- )
  (value)
  here ,
  [: @i 2@ ;] ,
  [: @i 2! ;] ,
  here 2! 4 allot
;

This value stores a double cell information in RAM. The read and write methods are embedded as quotations.