ISROnChange

This module generates an on change event if the state of PORTB 4..7 changes. If you don't want a particular pin to fire the event handler, use a call to SetRBMask() to block the trigger. Here is some code to test the module with...

// device and osc...
device = 18F452
clock = 20

// import modules...
include "ISROnChange.bas"
include "Utils.bas"

// event handler...
event OnChange()
   toggle(PORTD.0)
end event

// program start...
SetAllDigital
SetRBMask($80)                      // only interested in PORTB.7 on change events
ISROnChange.Initialize(OnChange)    // initialize the OnChange module, pass OnChange event handler

// loop forever...
while true
wend

Here is the module code itself...

module ISROnChange

// set interrupt priority level...
#if IsOption(CHANGE_PRIORITY) and not (CHANGE_PRIORITY in (ipLow, ipHigh))
   #error CHANGE_PRIORITY, "Invalid option. Priority must be ipHigh or ipLow."
#endif
#option CHANGE_PRIORITY = ipLow
const PriorityLevel = CHANGE_PRIORITY

public type TEvent = event()

dim 
   FPullupsDisabled as INTCON2.Booleans(7),    // RBPU, pullups
   FOnChangeIF as INTCON.Booleans(0),          // RBIF, on port change interrupt flag
   FOnChangeEnable as INTCON.Booleans(3),      // RBIE, enable on change inetrrupts
   FIPHigh as INTCON2.Booleans(0)              // RBIP, interrupt priority

dim 
   FOnChangeEvent as TEvent,                   // address of the OnChange event handler
   FLastState as byte,                         // last state of PORTB
   FRBMask as byte,                            // the port bits we are interested in
   FRBChanged as byte                          // holds the changed state flags

public dim
   RB7 as FRBChanged.Booleans(7),              // RB7 has changed
   RB6 as FRBChanged.Booleans(6),              // RB6 has changed
   RB5 as FRBChanged.Booleans(5),              // RB5 has changed
   RB4 as FRBChanged.Booleans(4)               // RB4 has changed

{
****************************************************************************
* Name    : OnChange (PRIVATE)                                             *
* Purpose : On port change interrupt handler                               *
****************************************************************************
} 
interrupt OnChange(PriorityLevel)
   dim StateOfPort as byte

   // end mismact condition and clear interrupt flag...
   WREG = PORTB   
   FOnChangeIF = false  

   // has the state of the port changed - if yes, set
   // the port changed flags and execute the on change event handler...
   StateOfPort = PORTB and FRBMask     
   if StateOfPort <> FLastState then
      FRBChanged = StateOfPort xor FLastState  
      FLastState = StateOfPort
      FOnChangeEvent()
   endif   
end interrupt
{
****************************************************************************
* Name    : SetRBMask                                                      *
* Purpose : Sets the port pins we want to process. For example             *
*         : %1100 0000 ($C0) is RB7 and RB6                                *
*         : %1001 0000 ($90) is RB7 and RB4                                *
****************************************************************************
} 
public sub SetRBMask(pMask as byte)
   FRBMask = pMask and $F0
end sub
{
****************************************************************************
* Name    : Initialize                                                     *
* Purpose : Initialize the module                                          *
****************************************************************************
} 
public sub Initialize(pOnChangeEvent as TEvent)
   FLastState = PORTB and FRBMask      // get current state
   FPullupsDisabled = false            // enable PORTB pullups
   FOnChangeEvent = pOnChangeEvent     // assign the event handler
   #if CHANGE_PRIORITY = ipLow
   FIPHigh = false
   #endif
   FOnChangeEnable = true
   enable(OnChange)
end sub

// initialize the module
FRBMask = $F0