Skip to content

STM8 eForth Alias Words

Thomas edited this page Jul 2, 2019 · 12 revisions

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 out/<BOARD>/targetfolder.

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 ULOCKF and LOCKF were removed from the standard dictionary to conserve memory space (in many cases NVM and 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 EECLOSE

Note that 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 #require.

The usage of EEALIAS can best be explained through an example session.

We start from a "clean slate" binary (here a STM8S001J3):

#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 SPACES, DIGIT and DNEGATE are loaded from the ./target/ folder (which should be a symlink to the STM8 eForth binary's out/<BOARD>/target/ folder):

#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

Note that 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 EERESET and 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

The file 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. dodoes).

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 $,n to NUMBER? after IRET instead of before 'BOOT.

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.rst list 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 JMP instruction:

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 .. Typing 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 ..), and .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 
;

Using 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