The following code can be used or modified to do what you want. Everything is done in the background.

The good thing about it, is that it can be used on any port - not just port B.
And it will watch a large number of tachs - not just 8 (of course, you have to expand the ASM section).
It does NOT need a debounce - as long as the sample interval is longer than the expected bounce time.

The sample interval is set by the TMR0 interrupt rate. The interrupt rate must be high enough to 'catch' every
HIGH and LOW of the input. So if the HIGH time of the input is 10 mSec and the LOW time of the input is 30 mSec,
then the interrupt must be faster than 10 mSec.

As written, the code will run for 1000 interrupt times, then quit. if you don't want it to stop, and only count trasitions, simply eliminate
the TACHCLOCK part.

(as written)

To start,

Clear the TachCounters
Clear TPE

In your main loop, check for TPE to be set. If it is,

Read the TachCounters
Clear the TachCounters
Clear TPE




Code:
       INCLUDE "DT_INTS-18.bas"         

  
ASM
INT_LIST  macro    
              INT_Handler    TMR0_INT,   ReadTachs,   ASM,  yes 

          endm
    INT_CREATE               
ENDASM
                         
Goto OverInt

'---[TMR0 interrupt handler]---------------------------------------------------
   
Asm                            
ReadTachs
         movff    PreloadH,TMR0H  ; Preload depends on clk speed
         movff    PreloadL,TMR0L
 
         btfsc   TPE,0
         bra     DoneForNow
        
       
         infsnz  TachClock        
         incf    TachClock + 1
            
         movlw   0x03 - 1          ; Real value is 1000 = 0x3E8, but must have one less
         cpfsgt  TachClock + 1      ; to compare with greater than
         bra     TachRoutine
         movlw   0xE8 - 1          ; Likewise, subtract one here, too.
         cpfsgt  TachClock
         bra     TachRoutine
         bsf     TPE,0
         
         clrf    TachClock
         clrf    TachClock + 1
         bra     DoneForNow
                
TachRoutine
              
         movf    PORTF,0,0
         movwf   Temp,0            ; Save VAR so can't change between compare and save
         xorwf   OldPortF,0,0
         movwf   changedF,0
         movff   Temp,OldPortF
  
         
Tach1       
         btfss   changedF,0
         bra     Tach2
         infsnz  Tach1Counter
         incf    Tach1Counter+1
       
Tach2        
         btfss   changedF,1
         bra     Tach3
         infsnz  Tach2Counter
         incf    Tach2Counter+1
                
Tach3        
         btfss   changedF,2
         bra     Tach4
         infsnz  Tach3Counter
         incf    Tach3Counter+1
Tach4        
         btfss   changedF,3
         bra     DoneForNow
         infsnz  Tach4Counter
         incf    Tach4Counter+1
       
           
DoneForNow
        
         bcf     INTCON,2   ; Clear the TIMER0 interrupt flag
        INT_RETURN	 	
ENDASM

   
  
OverInt:  

TachCounter1=0:TachCounter2=0:TachCounter3=0:TachCounter4=0

asm
         movff    PreloadH,TMR0H  ; Preload depends on clk speed
         movff    PreloadL,TMR0L
         bcf     INTCON,2 
endasm

   TPE = 0

INT_ENABLE TMR0_INT

(your code here)