Turnkey applications

Turnkey application automatically execute a word upon startup.

: myinit ( -- )
	\ some code 
;

\ save the xt of myinit into turnkey vector (an eeprom variable)
' myinit is turnkey
\ whenever quit starts myinit gets executed just
\ before the accept/interpret loop starts

\ disable turnkey, note that this disables the
\ the serial terminal, you loose the prompt..
' noop is turnkey

Using create/does>

A subtle error will be made with the following code

: const create , does> @ ;

This code does not work as expected. The value compiled with , is compiled into the dictionary, which is read using the i@ word. The correct code is

: const create , does> i@ ;

Multitasking

amforth' multitasker is a loadable module. The core system provides the word pause which does nothing per default. It is called by rx0? and tx0? internally however.

To change the action of pause you need to define a word and apply it with the command is


\ define a clever multitasker
: mypause ( -- )
	\ do something useful, e.g. switch task
;

\ turn it on
' mypause is pause

\ and turn it off
' noop is pause

Amforth' multitasker makes use of the user area as the task control structure. The long term credit to the multitasker's design seem to go back to Bill Muench, as stated at http://www.taygeta.com/hforth.html. The current code is based on a usenet article from Brad Eckert from Fri, 17 Aug 2007.

\ initialize the task system
    onlytask   ( -- )     \ the system prompt is the only task
    20 20 task ( -- tid ) \ allocate stacks and a task control block
dup task-sleep ( -- tid ) \ let it sleep
dup alsotask   ( -- tid ) \ insert it into the task list chain

tlist                     \ show the task list

\ create a sample task, it only increments a counter and pauses
variable counter 0 counter !
: taskdemo ( tid -- )
    \ prepare task change, still within the current task context
    activate \ prepare the task control block to take over
    \ everything until ; is executed in the task context!
    begin
	1 counter +!
	pause
    again
;
taskdemo 
\ the counter loop is still not run!

\ fire up the multitasker, _now_ the counter loops starts
multi

\ show the task list, the multitasker is running
tlist

\ stop multitasking
single

Interrupts

Interrupts are processed in two stages. First stage is a simple lowlevel processing routine.

  1. The low-level interrupt routine stores the index of the interrupt in a RAM cell (not directly accessible from amforth)
  2. sets the T-flag in the status register to signal the inner interpreter that an interrupt needs attention.

The inner interpreter checks every time it is entered the T-flag. If it is set (1) the interrupt processing routine is activated. It reads the number of the interrupt and calculates the index into the RAM based interrupt vector table. This table is identical to the atmega interrupt table in the flash except that it holds the XT of the forth words that will be started for the interrupt.

The interrupt forth word is simply a colon word. It is executed within the context of the current user variable and stack frame. It must not have any stack effect outside the word. If throw is used, make sure it is catched within the context of the interrupt word!

\ set a noop to all interrupts. Useful in 'turnkey
: initInts
	#int 0 do
		['] noop i int!
	loop
;

\ effectivly disable interrupt (usually INT0)
' noop 1 int!

variable int1counter
0 int1counter !

: doint1
	1 int1counter +!
;

' doint1 1 int!
\ do not forget to enable the lowlevel avr interrupt too!
\  hex  5 MCUCR c! c0 GICR c! \ both int0 and int1 on every change

\ turn on all interrupts
+int

\ turn off all interrupts (inkl. terminal io!)
-int

Currently all but the the 2 interrupts for the usart 0 are available for forth interrupts. Please note, that the generic interrupt service routine does not clear any hardware interrupt conditions, if you want to deal with hardware interrupts (e.g. from TWI), you cannot use the forth interrupts. For these interrupts a little assembly routine is needed (like the usart ISRs).