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.
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 >
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
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.