B OpenBoot Interrupt Testing





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
;

------------------------------------------------------------------------------