Coding Interrupt Handlers in Forth

A project log for eForth for cheap STM8S gadgets

Turn cheap stuff from AliExpress into interactive development kits!

ThomasThomas 01/26/2017 at 21:290 Comments

@Elliot Williams convinced me that writing interrupt handlers in Forth instead of assembly (or C) is a Cool Thing. I had pondered a lot about how to do that with the least amount of overhead but it took a lengthy discussion with Elliot to get it right!

There was a problem to solve: the STM8 register X is the Data Stack pointer, and it's also needed for implementing certain core words. The assumption that "X is TOS" isn't always justified. There is no way around this, except by blocking interrupts, or by rewriting code so that X always represents a valid stack pointer (with undesired effects like increased code size or longer runtime).

My "the most simple thing that works" solution assigns a small clean data stack to user-defined interrupts. The data stack is just 8 cells deep, but that should be more than sufficient (please read below why).

Interrupt handlers in Forth code are now based on the following words:

Writing an interrupt handler is easy - here is an AWU (Auto Wake Up) handler as an example:

: IVEC! ( a n - -   ) 2* 2* $800A + ! ;
: HALT ( -- ) [ $8E C, ] ;
: awuint SAVEC awu_csr1 c@ IRET ;
: initawu 38 awu_apr c! 1 awu_tbr c! 16 awu_csr1 c! ;
' awuint 1 IVEC!

The interrupt handler awuint first does a Forth VM context switch with SAVEC. Reading awu_csr1 clears the AWU interrupt flag, and IRET restores the Forth VM context and returns to the interrupted code with IRET. It's not necessary to leave the stack balanced (otherwise DROP would be required). The word IVEC! stores the address of our new hander to interrupt vector 1 (the AWU interrupt in Flash memory). Since IVEC! is only needed at "compile time" it can be compiled to RAM and doesn't need to be part of the Forth image.

The word HALT encodes the STM8 HALT instruction that shuts down the CPU clock until an interrupt occurs (I first added a word HALT to the Forth core but this user code implementation is just as good). When I run HALT with this code, it returns because of the Auto Wake Up interrupt. Note that I didn't find the time to make sense of the AWU configuration, and I simply took the AWU timing values from this page.

When writing interrupt handlers in Forth, I would like to recommend the following practice:

Working code with SAVEC and IRET is in the develop branch on GitHub. I'm still working on some details but it will be part of the next release.