Writing FCode Programs
  Search only this book
Download this book in PDF

OpenBoot Interrupt Testing

B

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
WordStack DiagramDescriptions
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@
pil!
( -- level )
( level -- )
Gets (pil@) and sets (pil!) the current processor interrupt level.
The system will only respond to interrupts above the current setting
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
Bit #Bit NameFunction
7AEnable level 14 interrupts.
6BNone (always 0).
5CEnable level 10 interrupts.
4DEnable level 8 interrupts.
3ESoftware interrupt level 6.
2FSoftware interrupt level 4.
1GSoftware interrupt level 1.
0HEnable 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  


  \ Interrupt-testing program  
  ;  
  
  : 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-intr>cpu  ;  
  
  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  
  ;