An important, and not always obvious, part of programming peripheral devices is dealing with interrupts. Table B-1 describes Open Boot 2.0 words for testing interrupts from the Forth Monitor. Note that these cannot be used in FCode programs because of their highly system-dependent nature.
Table B-1 Interrrupt-handling words
-----------------------------------------------------------------------------------------------------------------
Word Stack Diagram Descriptions -----------------------------------------------------------------------------------------------------------------
catch-interrupt ( level -- ) Establishes a handler for interrupt "level" (1-15). If an interrupt occurs on that level, the handler sets the value of the interrupt-occurred? variable to -1 and sets the value of the vector-used variable to the interrupt level. interrupt-occurred? ( -- adr ) Returns the address of a variable whose value will be set to "-1" when an interrupt occurs. vector-used ( -- adr ) Returns the address of a variable whose value will be set to the interrupt level when an interrupt occurs on a level guarded by catch- interrupt. pil@ ( -- level ) Gets (pil@) and sets (pil!) the current processor interrupt level. ( level -- ) The system will only respond to interrupts above the current setting pil! of the PIL. After the first interrupt is handled, the PIL is automatically raised to the level of the interrupt (thus disabling further interrupts at the same level). Re-lower the PIL if you wish to process additional interrupts. -----------------------------------------------------------------------------------------------------------------
Assume a device which interrupts on level 3. Here is a sample Forth program for testing the device's ability to interrupt.
------------------------------------------------------------------------------
: test-interrupt ( -- ) pil@ r \ Remember old priority level interrupt-occurred? off 3 catch-interrupt 2 pil! \ Allow level 3 interrupts <do whatever is necessary to make the device interrupt'> 1000 0 do loop \ Wait awhile; may not be necessary interrupt-occurred? @ if <do whatever is necessary to turn off the device's interrupt request'> ." Interrupt on level " vector-used @ . cr else ." No interrupt." cr then r pil! ; ------------------------------------------------------------------------------
Note - If you want to test interrupts on CPU levels 14, 10 or 8, you will also need to set the interrupt-enable register to the appropriate value as well. (SBus level 6 is equivalent to CPU level 8 on most current systems.) See comments at end for more details.
Caution - There is a bug in Open Boot PROMs 1.1 thru 2.1 in the interrupt- occurred? flag, causing it to return a 0 even after an interrupt has occurred.
For example:
------------------------------------------------------------------------------
interrupt-occurred? off \ Clear flag 6 catch-interrupt \ Establish handler 5 pil! \ Lower CPU priority to allow level 6 interrupts 89 interrupt-enable! \ Cause a level 6 "software interrupt" interrupt-occurred? ? \ Examine flag; it should be ffffffff but it's 0 (bug) ------------------------------------------------------------------------------
Here is a workaround patch for this bug.
---------------------------------
ok see catch-interrupt : catch-interrupt 10 + (ffeac10c) swap vector! ; ---------------------------------
Note the number shown in parentheses (ffeac10c in this example). In the following step, substitute that number in place of the example number ffeac10c.
---------------------------------------
ok ramforth ok 8000.0000 ffeac10c execute 4 + ! ok ---------------------------------------
A way to determine this magic value from a program would be (for any Open Boot 2.0-based system) as follows:
-------------------------------------------------------------
['] catch-interrupt (addr of catch-interrupt) h# 0a + w@ (offset pointer for ffxxxxxx routine) 4 * origin + (ffeac10c) -------------------------------------------------------------
An interrupt can be generated just by writing the proper value to the interrupt register. Here is the format of this register:
Table B-2 Interrupt Register Format
--------------------------------------------
Bit # Bit Name Function --------------------------------------------
7 A Enable level 14 interrupts. 6 B None (always 0). 5 C Enable level 10 interrupts. 4 D Enable level 8 interrupts. 3 E Software interrupt level 6. 2 F Software interrupt level 4. 1 G Software interrupt level 1. 0 H Enable all interrupts. --------------------------------------------
Writing a zero to bits A, C, or D only masks that interrupt, it does not clear the source.
Writing a one to a software interrupt bit requests an interrupt on that level; the bit must be cleared to clear the request.
Merely writing a one to register bit H will not enable interrupts on levels 14, 10 and 8, since these also have a separate mask.
To enable level 8, for example, You need to write a one to both bits D and H. After power-up or after any Forth traps, the Open Boot writes this interrupt register to "81".
Note - Writing a zero to bit H will clear the Asynchronous Memory (level 15) Interrupt, as well as masking all interrupts. Interrupts should be immediately re-enabled by writing a one to bit 0.
On reset, all bits are cleared and all interrupts are reset.
Finally, here is a complete test example. All known bugs are accounted for.
------------------------------------------------------------------------------
\ Interrupt-testing program
hex : patch-bugs ( -- ) ramforth 8000.0000 ['] catch-interrupt 0a + w@ 2* ( 8000.0000 token ) \ If "firmware-version" found and =2.0 (2.0000), then 2* again \ 2.0 boot PROMS use a 4* multiplier, to expand the available dictionary p" firmware-version" find ( acf n | pstr 0 ) if execute ( version ) 2.0000 = if 2* then else drop then ( 8000.0000 token' ) origin + ( 8000.0000 acf ) execute 4 + ! ; : catch-level ( level -- ) interrupt-occurred? off dup d# 8 = if 91 interrupt-enable! then dup d# 10 = if a1 interrupt-enable! then \ Or, just always do "b1 interrupt-enable!" to enable all masks... dup catch-interrupt ( level ) 1- pil! \ Set priority level to allow this interrupt ; : check-interrupt ( -- ) <do whatever is necessary to make the device interrupt'> 20 ms \ Wait awhile; may not be necessary interrupt-occurred? @ ( flag ) if <do whatever is necessary to turn off the device's interrupt request'> ." Interrupt on level " vector-used @ . cr else ." No interrupt." cr then ; 7 value my-level \ My device's interrupt level \ Alternatively... \ 5 value my-sbus-level \ : my-level ( -- int-level ) my-sbus-level sbus-intrcpu ; 0 value old-pil \ Holder for system interrupt level : test-interrupt ( -- ) patch-bugs \ Overkill, only needs to be called once per session pil@ is old-pil \ Save old interrupt level my-level catch-level \ Setup handler check-interrupt \ Do the test old-pil pil! \ Restore old interrupt level ; ------------------------------------------------------------------------------