STM8 eForth Alias Words
Alias Words is a unique STM8 eForth feature for creating temporary or permanent secondary dictionary entries for compiled code.
This makes it possible to build a smaller binary distribution with a much more compact dictionary without loosing access to any of the less needed words. Alias Words are the mechanism by which dictionary entries for hidden machine code words are added at a later date as needed.
Thanks to an STM8 eForth core feature Alias Words directly return the aliased code address. Therefore removing Alias definitions won't break the code using them, and the can be used as "scaffolding" in RAM during the build process.
Alias Words serve the following use cases:
- add unlinked Forth words to a dictionary chain in Flash, EEPROM, or RAM
- keep words accessible after redefinition
- keep source code that uses static XTs (execution tokens) maintainable
- provide dictionary entries for code positions halfway into a colon definition (!)
Alias Words for unlinked STM8 eForth words are automatically generated in the STM8 eForth build process. In the binary release, alias definitions for headerless words can be found in the
After creating a symlink to
./target in the working directory, Alias words can be used easily with e4thcom and codeload.py.
Note: the Makefile in showcase STM8 eForth application projects (e.g. W1209 data logging thermostat) show how to set up a
target output directory.
For an example: the words
LOCKF were removed from the standard dictionary to conserve memory space (in many cases
RAM cover the use case). If, for instance, a simple Flash store word is needed, the hidden words can be exposed:
#require ULOCKF #require LOCKF : !F ( n a -- ) \ write n to a in Flash memory ULOCKF ! LOCKF ;
For programmers using a pure text terminal (e.g. on Windows) the following procedure using the example DIGIT will allow dictionary entries to be added for any words which have been left out of the dictionary.
\ create the dictionary entry in flash for use in a later session NVM : DIGIT [ $CC C, $88F1 , OVERT \ $88F1 is the address of the label DIGIT: from the assembly run of forth.asm \ this is contents of the file ./out/<BOARD>/target/DIGIT \ in ./out/<BOARD>/forth.rst search for DIGIT words \ we now see Digit in the dictionary DIGIT IRET SAVEC RESET RAM .... <snip> \ the dictionary entry for DIGIT was added to NVM \ - switch to RAM to make the entry permanent RAM \ do a cold restart or power cycle cold STM8eForth 2.2.22 ok \ we check if DIGIT is still there .. words DIGIT IRET SAVEC RESET RAM <snip> 9 digit . 57 ok \ and it works as expected
Hint: Which words are available?
Look in the target folder for the binary you are using, e.g. \out\W1209-FD\target for the W1209 board with full duplex. The words with no dictionary entry, but included in the binary as code, are listed here. Use the alias functionality to create dictionary entries as needed.
Alias Words in the EEPROM
When working with memory constraint STM8 devices with just 8K Flash it would be nice if the EEPROM could be used to make the code memory space larger.
Unfortunately, the STM8 core will execute code from Flash and from RAM but an attempt to execute code from an EEPROM address will cause a reset. Using the ALIAS feature it's still possible to have dictionary entries in the EEPROM, and Flash space can be preserved for executable code by combining "headerless code" in Flash with ALIAS entries in the EEPROM!
The library feature
EEALIAS does the following:
- find the root of the dictionary in Flash memory
- unlink (remove) any dictionary chain in the EEPROM
- create a new dictionary chain in the EEPROM and link it
- in EEPROM MODE accept ALIAS definitions
- abort if a normal compilation (non-ALIAS words) is attempted
- return to RAM MODE with
EEALIAS isn't a Forth word but rather a "macro" which loads Forth code to RAM, and removes itself when done. In e4thcom or codeload.py, it has to be loaded with
#include, not with
The usage of
EEALIAS can best be explained through an example session.
We start from a "clean slate" binary (here a
#include EEALIAS<enter> \ ... \ now #require some ALIAS files, finalize with EECLOSE \ ... Closing: ./lib/EEALIAS ok
The Forth console is now in EEPROM Mode. Now aliases for
DNEGATE are loaded from the
./target/ folder (which should be a symlink to the STM8 eForth binary's
#r SPACES Uploading: ./target/SPACES : SPACES [ $CC C, $897A , OVERT ok Closing: ./target/SPACES ok #r DIGIT Uploading: ./target/DIGIT : DIGIT [ $CC C, $87FA , OVERT ok Closing: ./target/DIGIT ok #r DNEGATE Uploading: ./target/DNEGATE : DNEGATE [ $CC C, $84CA , OVERT ok Closing: ./target/DNEGATE ok EECLOSE ok
EECLOSE concludes with
WIPE, which warm-starts the Forth console. The new aliases are now at the end of the dictionary:
words IRET SAVEC RESET RAM NVM LOCK ULOCK WORDS .S DUMP IMMEDIATE ALLOT VARIABLE CONSTANT CREATE DOES> ] : ; OVERT ." $" ABORT" AFT REPEAT WHILE AHEAD ELSE THEN IF AGAIN UNTIL BEGIN +LOOP LOOP DO NEXT FOR COMPILE LITERAL CALL, C, , [COMPILE] ' hi CR [ NAME> \ ( .( ? . U. TYPE U.R .R SPACE KEY DECIMAL HEX <# SIGN HOLD #S # #> ERASE FILL CMOVE HER E COUNT +! DEPTH PICK 0= ABS NEGATE NOT 1+ 1- 2+ 2- 2* 2/ EXG */ */MOD M* * UM* / MOD /MOD M/MOD UM/MOD WITHIN MIN MAX < U< = 2DUP ROT ?DUP BG TIM BL last '?KEY 'EMIT BASE - 0< OR AND XOR + UM+ I OVER SWAP DUP 2DROP DROP NIP >R R@ R> C! C@ ! @ B! 2C@ 2C! 2@ 2! EXIT EXECUTE LEAVE EMIT ?KEY TX! ?RX ADC@ ADC! COLD 'BOOT DNEGATE DIGIT SPACES ok
When the dictionary entries in the EEPROM are no longer needed, load
EEALIAS and run
WIPE instead of EECLOSE to unlink the dictionary in the Flash ROM. This step is necessary if the application needs to use the EEPROM.
Loading all the Hidden Words
aliaslist.fs in a board's target folder contains load instruction for Alias of all the board binary's headerless words (with the exception of some highly specific words, e.g.
With e4thcom loading these aliases to the EEPROM is equally easy as loading to Flash memory:
#include EEALIAS #include aliaslist.fs EECLOSE
This results in the following dictionary list:
words IRET SAVEC RESET RAM NVM LOCK ULOCK WORDS .S DUMP IMMEDIATE ALLOT VARIABLE CON STANT CREATE DOES> ] : ; OVERT ." $" ABORT" AFT REPEAT WHILE AHEAD ELSE THEN IF AGAIN UNTIL BEGIN +LOOP LOOP DO NEXT FOR COMPILE LITERAL CALL, C, , [COMPILE] ' hi CR [ NAME> \ ( .( ? . U. TYPE U.R .R SPACE KEY DECIMAL HEX <# SIGN HOLD #S # #> ERASE FILL CMOVE HERE COUNT +! DEPTH PICK 0= ABS NEGATE NOT 1+ 1- 2+ 2- 2* 2/ EXG */ */MOD M* * UM* / MOD /MOD M/MOD UM/MOD WITHIN MIN MAX < U< = 2DUP ROT ?D UP BG TIM BL last '?KEY 'EMIT BASE - 0< OR AND XOR + UM+ I OVER SWAP DUP 2DROP D ROP NIP >R R@ R> C! C@ ! @ B! 2C@ 2C! 2@ 2! EXIT EXECUTE LEAVE EMIT ?KEY TX! ?RX ADC@ ADC! COLD 'BOOT NUMBER? do$ dm+ QUIT $"| A@ $COMPILE CUPPER DOXCODE SPACES _TYPE PRESET AFLAGS (+loop) QUERY ?STACK DNEGATE ?UNIQUE doVAR NAME? PAD Y@ str TAP DIGIT? EXTRACT YFLAGS WORD $," PACK$ .OK 1 0 EVAL find ?branch COMPILE? PAR SE >CHAR .ID ABORT TOKEN DIGIT ^H SAME? -1 $INTERPRET kTAP ACCEPT ."| $,n ok
Note that loading
aliaslist.fs to Flash memory would add the words
IRET instead of before
Alias Words Technical Background
The feature was proposed by @RigTig in issue #26 and #27:
- create minimal Forth core binaries by leaving out all non-essential parts of the dictionary
- extract addresses from a the
forth.rstlist file in the out folder
- create alias words from extracted addresses when, and compile them into RAM as needed
- compile new words using an alias word
Aliases are simply dictionary entries followed by a
stm8eForth v2.2 : .. [ OVERT $CC C, ' . , ok 4 .. 4 ok
In this example an alias
.. is created for
. "print number" (in the standard use case the address comes from the STM8EF build script). The idiom
: name [ OVERT creates an empty dictionary entry, and then leaves compile mode. The sequence
$CC C, writes a
JP (jump) STM8 opcode to code field of the new word, and
' . , completes the
JP with the address of the word
4 .. demonstrates that the word
.. works as expected.
When the alias
.. is used, the TG9541/STM8EF implementation of
NAME> detects that the code field of
.. starts with a the opcode
JP ($CC), not any other instruction.
NAME> then returns the value of the jump target not the address of the jump.
Running the code directly, of course, would do the same. The advantage is that aliases can be removed from the dictionary (e.g. defined in RAM), and compiled code in the Flash memory will continue to use the "aliased code".
The following code demonstrates that the alias feature is completely transparent to the application:
: ..test .. ; ok : .test . ; ok ' ..test 10 dump 117 CD 8A D2 81 1 10 5 2E 74 65 73 74 CD 8A D2 81 M_R____.testM_R_ ok ' .test 10 dump 123 CD 8A D2 81 0 1 4 64 75 6D 70 74 74 0 0 0 M_R____dumptt___ ok
The code of the words
..test (with the alias
.test (with the original
.) is identical (
CD 8A D2 81,
CALL 8AD2 RET).
Defining Alias Words
A definer for Alias Words may look like this:
: ALIAS ( "name" xt -- ) \ M.Mahlow's solution : $CC C, , [compile] [ OVERT ;
ALIAS creating alias words is akin to defining a constant:
\ create ' . ALIAS .. ok 56 .. 56 ok : a 123 [ here ] . ; ok ALIAS b ok a 123 ok 23 b 23 ok
Other Use Cases for Alias Words
Another use case for Alias Words is safeguarding a word before overwriting it in the dictionary.
Consider this example (using e4thcom):
stm8eForth v2.2 #r ALIAS Uploading: ./lib/ALIAS \ STM8eForth : ALIAS MM-170929 \ ------------------------------------------------------------------------------ \ ... nvm hex ok : a 1 ; ok ' a . 9336 ok ' a ALIAS old-a ok : a 2 ; reDef a ok a old-a . . 1 2 ok ram cold stm8eForth v2.2 hex ' old-a . 9336 ok