Hi,
I've written some code targeting the 18F1320, I thought I had a couple of those in stock and I just set out to build up a test circuit to verify the code but it was the 18F1330 I had and as it turns out it's a completely different beast (lacking a TMR2 which the code relies on for example).

I guess I could change the code to suit a device that I do have but then that might not be what you intend to run it on anyway and we'd still need some back and forth so I'll just post the code as is and ask you to kindly remember that it is untested. Hopefully though it serves its purpose and show you the overall idea of one way that this can be done without external RTC chips or interrupts.

There is a bit of code but I tried to make it as complete as I could for a first pass, I'm sure there are things I didn't think of though.

Feel free to pick it apart, ask questions why this or that.

Code:
'****************************************************************
'*  Name    : Speedometer.pbp                                   *
'*  Author  : Henrik Olsson                                     *
'*  Notice  : Copyright (c) 2017 [select VIEW...EDITOR OPTIONS] *
'*          : All Rights Reserved                               *
'*  Date    : 2017-09-13                                        *
'*  Version : 1.0                                               *
'*  Notes   : Uses TMR1 configured as a counter to measure      *
'*            distance traveled and TMR2 as a 2ms timebase to   *
'*            measure real time.                                *
'*            An active low start/stop button is used to start  *
'*            and stop the measurement.                         *
'*                                                              *
'*          : Target is 18F1320 but running on internal 8MHz    *
'*            oscillator but should work on any device having   *
'*            a TMR1 and TMR2 with or without minor changes.    *
'*          :                                                   *
'****************************************************************

'-------------------- Device configuration ----------------------
#CONFIG
    CONFIG  OSC = INTIO2	            ; Internal RC oscillator, port function on RA6 and port function on RA7
    CONFIG  FSCM = ON                   ; Fail-Safe Clock Monitor enabled
    CONFIG  IESO = ON                   ; Internal External Switchover mode enabled
    CONFIG  PWRT = OFF                  ; PWRT disabled
    CONFIG  BOR = ON                    ; Brown-out Reset enabled
    CONFIG  BORV = 27                   ; VBOR set to 2.7V
    CONFIG  WDT = ON                    ; WDT enabled
    CONFIG  WDTPS = 512                 ; 1:512
    CONFIG  MCLRE = ON                  ; MCLR pin enabled, RA5 input pin disabled
    CONFIG  STVR = ON                   ; Stack full/underflow will cause Reset
    CONFIG  LVP = OFF                   ; Low-Voltage ICSP disabled
    CONFIG  DEBUG = OFF                 ; Background debugger disabled, RB6 and RB7 configured as general purpose I/O pins
    CONFIG  CP0 = OFF                   ; Block 0 (00200-000FFFh) not code-protected
    CONFIG  CP1 = OFF                   ; Block 1 (001000-001FFFh) not code-protected
    CONFIG  CPB = OFF                   ; Boot Block (000000-0001FFh) not code-protected
    CONFIG  CPD = OFF                   ; Data EEPROM not code-protected
    CONFIG  WRT0 = OFF                  ; Block 0 (00200-000FFFh) not write-protected
    CONFIG  WRT1 = OFF                  ; Block 1 (001000-001FFFh) not write-protected
    CONFIG  WRTC = OFF                  ; Configuration registers (300000-3000FFh) not write-protected
    CONFIG  WRTB = OFF                  ; Boot Block (000000-0001FFh) not write-protected
    CONFIG  WRTD = OFF                  ; Data EEPROM not write-protected
    CONFIG  EBTR0 = OFF                 ; Block 0 (00200-000FFFh) not protected from table reads executed in other blocks
    CONFIG  EBTR1 = OFF                 ; Block 1 (001000-001FFFh) not protected from table reads executed in other blocks
    CONFIG  EBTRB = OFF                 ; Boot Block (000000-0001FFh) not protected from table reads executed in other blocks
#ENDCONFIG

'---------- Variables ------------
Seconds         VAR WORD                ' elapsed time, seconds
ms              VAR WORD                ' elapsed time, milliseconds
State           VAR BYTE                ' State machine variable, keeps track of what we're doing
Distance        VAR WORD                ' Distance traveled in metres, calculated based on pulsecount
AverageSpeed    VAR WORD                ' Final result of measurement, in units of 0.1km/h (123=12.3km/h)
Temp            var WORD                ' Used during calculation
DebounceCount   VAR BYTE                ' Used to debounce the start/stop button
TimeOverflow    VAR BIT                 ' Gets set if the seconds variable grows too big.

'---------- Constants -------------
Idle            CON 0
Starting        CON 1
Counting        CON 2
Stopping        CON 3
Done            CON 4

'------------ Aliases --------------
StartStopButton VAR PortB.0             ' Low when pressed
ReadyIndicator  VAR LATB.2              ' High when idle/ready
RunIndicator    VAR LATB.3              ' High when counting/timing
ErrorIndicator  VAR LATB.4              ' High on count or time overflow
TMR1ON          VAR T1CON.0             ' Run/stop bit for TMR1
TMR2ON          VAR T2CON.2             ' Run/stop bit for TMR2
TMR1IF          VAR PIR1.0              ' TMR1 interrupt flag
TMR2IF          VAR PIR1.1              ' TMR2 interrupt flag

'--------- Hardware setup -----------
ADCON1 = %11111111                      ' All pins as digital
LATB   = %00000000                      ' All pins low
TRISB  = %01000001                      ' RB0=Button, RB1=TX, RB2=LED, RB3=LED, RB4=LED, RB6=Counter input
OSCCON = %01110010                      ' Internal oscillator, 8MHz
T1CON  = %00000010                      ' External clock on RB6, timer OFF for now.
T2CON  = %00000010                      ' 1:16 prescaler, TMR2 off for now
PR2    = 249                            ' T2 interrupt flag set every 250*16 tick

RCSTA  = $90                            ' Enable serial port & continuous receive
TXSTA  = $20                            ' Enable transmit, BRGH = 0
SPBRG  = 12                             ' 38400 Baud @ 8MHz, 0,16%
SPBRGH = 0
BAUDCTL.3 = 1                           ' Enable 16 bit baudrate generator

PAUSE 500

Start:
    HSEROUT["Program start",13]

    For Temp = 0 to 9
        ReadyIndicator = 1
        Pause 250
        ReadyIndicator = 0
        Pause 250
    NEXT

    ReadyIndicator = 1


Main:
    Select CASE State
        
        Case Idle
            If StartStopButton = 0 THEN             ' If the button is pressed we
                Seconds = 0
                ms = 0
                TMR1H = 0                           ' reset the pulse count
                TMR1L = 0
                TMR2 = 0                            ' reset the timebase
                TMR1IF = 0                          ' clear the overflow flag
                TMR2IF = 0                          ' clear the timebase tick flag
                TMR1ON = 1                          ' enable the counter to count pulses from the driveshaft
                TMR2ON = 1                          ' enable the 2ms timer tick
                ReadyIndicator = 0                  ' update status LEDs
                RunIndicator = 1
                ErrorIndicator = 0
                TimeOverflow = 0                    ' clear overflow flag
                State = Starting                    ' and move to the next state.
            ENDIF
'---------------------------------------------------------------------------------------------------            
        CASE Starting
            IF TMR2IF THEN                          ' TMR2IF gets set every 2ms
                TMR2IF = 0                          ' reset flag
                GOSUB UpdateTime                    ' update our time registers
                
                ' Wait for the button to be released before transitioning to the next state.
                If StartStopButton = 1 THEN         ' Button released?
                    DebounceCount = DebounceCount + 1
                ELSE
                    DebounceCount = 0               ' Button must be released 10 ticks in a row.
                ENDIF
                
                IF DebounceCount = 9 THEN           ' Button has been released for 10 ticks
                    DebounceCount = 0
                    State = Counting                ' Transition to next state.
                ENDIF
            ENDIF
'---------------------------------------------------------------------------------------------------             
        
        Case Counting
            IF TMR2IF THEN                          ' 2ms tick?
                TMR2IF = 0                          ' clear flag and
                GOSUB UpdateTime                    ' update our time registers
                
                If StartStopButton = 0 THEN         ' Button pressed?
                    RunIndicator = 0
                    TMR1ON = 0                      ' Disable pulse counter
                    TMR2ON = 0                      ' Disable timer
                    State = Stopping
                ENDIF
             ENDIF
             
             IF TMR1IF THEN                         ' Counter overflow
                ErrorIndicator = 1             
             ENDIF
             
             ' Here we can do anything else that needs doing while the
             ' system is counting but it's very important that it does
             ' not take longer than 2ms or we will lose time.                    
'---------------------------------------------------------------------------------------------------              
        CASE Stopping
            ' Wait for the button to released before transitioning to the next state,
            If StartStopButton = 1 THEN             ' Is button released?
                DebounceCount = DebounceCount + 1
            ELSE
                DebounceCount = 0
            ENDIF
            
            IF DebounceCount = 10 THEN              ' Button released for duration of debounde time?
                DebounceCount = 0
                State = Done                        ' Transition to next state.
            ENDIF
            
            ' The timer is no longer running so we pause here for the same duration.
            PAUSE 2            
'---------------------------------------------------------------------------------------------------  
        
        CASE Done
            ' This is where we perform the calculations and present the result.
            
            If TMR1IF THEN                      ' Interrupt flag is set if counter has rolled over
                HSEROUT["ERROR: Pulse counter overflow, can't compute...", 13]
                Goto Abort
            ENDIF
            
            IF TimeOverflow THEN                ' Flag set if seconds variable has rolled over
                HSEROUT["ERROR: Seconds counter overflow, can't compute...", 13]
                Goto Abort
            ENDIF
            
            ' Each count from the driveshaft equals 0.6m of car movement.
            ' Lets say the pulse count is 12345 and the seconds count is 503.
            ' We've traveled 12345*0.6=7407 metres in 503 seconds, 7407/503*3.6=53.0km/h
            ' Using the ** operator allows us to effectively multiply by fractions of 65536 so:
            '
            ' 12345 ** 39322 = 7407
            ' 7407 * 36 = 266652
            ' 266652 / 503 = 530 which we interpret as 53.0km/h
            ' 
            ' 6045 pulses in 312 seconds,
            ' 6045*0.6/312*3.6=41.85km/h:
            ' 6045 ** 39322 = 3627
            ' 3627 * 36 = 130572
            ' 130572 / 312 = 418 which we interpret at 41.8km/h
            '
            ' 53203 pulses in 1412 seconds,
            ' 53203*0.6/1412*3.6=81.39km/h:
            ' 53202 ** 39322 = 31922
            ' 31922 * 36 = 1149192
            ' 1149192 / 1412 = 813 which we interpret as 81.3km/h
            
            Distance.HIGHBYTE = TMR1H
            Distance.lowbyte = TMR1L
            Distance = Distance ** 39322        ' Multiply by 0.6, Distance is now in metres
            
            Temp = Distance * 36
            AverageSpeed = DIV32 Seconds        ' 215 for 21.5km/h
            
            HSEROUT[DEC (TMR1H * 256 + TMR1L), " pulses",13]
            HSEROUT[DEC Distance, "m", 13]
            HSEROUT[DEC Seconds, ".", DEC3 ms, "s",13]
            HSEROUT[DEC AverageSpeed/10,".", DEC AverageSpeed//10, "km/h",13]
Abort:
            ReadyIndicator = 1
            ErrorIndicator = 0
            State = Idle      
        
    END SELECT
    
Goto Main

'-------------------------------------- Subroutines ------------------------------------------------
UpdateTime:
    ' Our timebase is 500Hz.
    ms = ms + 2
    If ms = 1000 then
        ms = 0
        seconds = seconds + 1
        
        ' We're using DIV32 to calculate the average speed. It does not allow the divisor
        ' to be larger than 32767 (which is 9 hours and some minutes so it should be fine)
        ' but since we're good programmers we're going to check for it anyway.
        If Seconds > 32767 THEN
            TimeOverflow = 1
            ErrorIndicator = 1
        ENDIF

    ENDIF
RETURN