; #define DEBUG
;****************************************************************************
; * PECntr                                                                  *
; *   Version 2.7  1/18/2007                                               *
; *   OM3CPH, KL7R, AA0ZZ                                                   *
; *                                                                         *
;****************************************************************************
; Description:
;   This is a frequency counter that works on the PIC-EL board. 
;
;   This contains the LCD routines from the AA0ZZ PICELgen program and some of 
;   the code from the 4-line LCD counter from Peter Halicky, OM3CPH.
;   See PICELgen by AA0ZZ for the history of the LCD code.
;   See LCD4CNTR.ASM (by OM3CPH) for the history of the Counter code.
;
; ****************************************************************************
; Modification History
;    1/4/2006  v1.0 - Initial port of OM3CPH code to PIC-EL platform (KL7R)
;                     Assembly with working LCD code on PIC-EL (KL7R)
;   1/16/2006  v2.0 - Ported to 16F628 (AA0ZZ)
;                   - Modified to prevent hang if no signal input (AA0ZZ)
;                   - Added multiply-by-10 routine (AA0ZZ)
;   1/19/2006  v2.1 - Tweaked for proper operation (AA0ZZ)
;   1/29/2006  v2.2 - Fix bug in MultBy10 routine (AA0ZZ)
;   11/10/2006 v2.3 - Minor tweaks for frequency counter adapter (AA0ZZ)
;   12/04/2006 v2.4 - Rearrange code in timing loop (AA0ZZ)
;                   - Move timing constants to EEPROM (AA0ZZ)
;                   - Configure for 2x16 LCD only (AA0ZZ)
;                   - Add Calibration code (AA0ZZ)
;   12/30/2006 v2.5 - More debug code (AA0ZZ)
;                   - Clean up (AA0ZZ)
;    1/05/2007 v2.6 - Add fine-tune timing mechanism (AA0ZZ)
;    1/18/2007 v2.7 - Add one-second interval option (AA0ZZ)
;
;*****************************************************************************
;                                                                              
; Target Controller -      PIC16F628A in PIC-EL board                            
;                          __________                                          
;     PB -------------RA2 |1       18| RA1---------ENCODER A                     
;     "False pulses"--RA3 |2       17| RA0---------ENCODER B
;     Counter input---RA4 |3       16| OSC1--------XTAL   (4MHz)             
;     +5V ----------!MCLR |4       15| OSC2--------XTAL                        
;     Ground----------Vss |5       14| VDD---------+5 V                        
;     LCD11-----------RB0 |6       13| RB7---------NC                          
;     LCD12-----------RB1 |7       12| RB6---------LCD_rs  (LCD Pin 4)         
;     LCD13-----------RB2 |8       11| RB5---------LCD_rw  (LCD Pin 5)           
;     LCD14-----------RB3 |9       10| RB4---------LCD_e   (LCD Pin 6)         
;                          ----------                                          
;                                                                              
; ****************************************************************************
;INSTRUCTIONS FOR USE
;  - Starting default is for 1/10 second timing. 
;  - Change between 1/10 second timing and 10-second timing during operation by
;    pressing pushbutton PB-3.  Hold until you see the symbol change on line 1, 
;    position 16 of the LCD. This symbol will be "-" if 1/10 second timing is 
;    active and "+" is 1-second timing is active. The change is saved in EEPROM.
;  - Calibrate by connecting to known RF source.  Enter Calibrate mode by 
;    holding PB-3 down when powering up or when releasing the reset button. 
;    (To keep from inadvertently changing the interval in this process, press
;    and hold the reset button first, then press PB-3, then release the Reset 
;    button.  Then you change the timing loop constants for the currently active
;    timing interval.  (There are separate timing constants for the two intervals
;    so you need to calibrate them individually.)  You do the calibration by 
;    turning the encoder while holding down PB-3 and observing the values on 
;    line 2 of the LCD.  Increase the timing constants if the displayed frequency
;    was was too low and decrease the timing constants of the displayed frequency
;    was too high. Release the PB-3 and observe again, etc. Note that the frequency
;    displayed on line 1 of the LCD during calibration is not used.
;  - Note that the least significant timing variable is not used for 1/10 second
;    counting. It always stays at zero when doing 1/10 second calibrating.
;  - Note that the various timing constants have different ranges. (Z ranges from
;    1 to 3, FT ranges from 0 to 2, and FFT ranges from 0 to 9).  This is due 
;    to the nature of how the constants are used in the timing algorithm. 
;  - Maximum measured frequency for the PIC-EL counter is about 15 MHz.
;  
; 
;PIC-EL II board configuration notes:                                                                             
;                                                                              
;  RF is brought into the PIC-EL via RF Jack J1.  Remember to configure the jumper
;  on HDR3 properly - with the shorting jumper between pins 3 and 4,  Also, make
;  sure you have a shorting jumper installed on HDR3. 
;  
;  For this frequency counter to work, a "short" must also be established between 
;  RA3 and RA4.  This can be done on the PIC-EL board by inserting a plug in the 
;  CW paddle jack (J3) with the two terminals of the plug connected internally.
;
;  For better performance at RF frequencies, I suggest you change PIC-EL II 
;  capacitor C9 from .1u to .001u. This makes the PIC-EL counter work up to
;  about 15 MHz.
;
;Original PIC-EL board configuration notes:  
;
;  Everything above applies and, in addition to changing C9 from .1uF to .001uF 
;  you need to change resistor R13 from 470 ohms (was 3.3k).  This change is
;  standard on the PIC-EL II board.  
;
;
;ADDITIONAL AA0ZZ COMMENTS:
;
;  Remember, this is a DEMONSTRATION COUNTER ONLY!  It is meant to be a 
;  demonstration of the general principles of a frequency counter.  
;
;  The calibration code HELPS, but is not perfect. Calibration is important 
;  because the PIC clock will almost NEVER be exactly at the stated nominal 
;  value.  Since the counter interval depends on executing a known number of
;  instructions, the nominal interval will not be perfect if the PIC clock is
;  not perfect.  Remember, even this calibration code cannot get make the counter
;  interval exactly correct, even though the interval can be adjusted within 1 
;  instruction. Since 1 instruction is 1 uS with 4 MHz PIC crystal, when 
;  counting a 10 MHz RF signal, 1 instruction corresponds to 10 missed or extra
;  cycles. Then, when using the 1/10 second counting interval, the number of 
;  cycles counted within the 1/10 sec counting interval is multiplied by 10 
;  before displaying the frequency in Hz, so the error is also multiplied by 10.
;  This means the smallest adjustment increment is 100 Hz when 1/10 second
;  counting is done. When the 1-second timing interval is selected, no 
;  multiplication is necessary and the amount of error at 10 MHz is down to 
;  10 Hz. 
;
;  In a stand-alone counter, the 4 MHz PIC crystal could be replaced by a 
;  20 MHz crystal.  This would reduce the error by a factor of 5 so the error 
;  would be within 2 Hz for a 10 MHz input signal and a one-second timing 
;  interval.  That's pretty good! The timing loop counts would all have to be 
;  adjusted to make this code work with a 20 MHz PIC clock, of course.) 
;
; ****************************************************************************
; * Device type and options.                                                 *
; ****************************************************************************
;
        processor 16F628A
        radix     dec

; ****************************************************************************
; * Configuration fuse information for 16F628:                               *
; ****************************************************************************

        include   <P16F628A.INC>
; (Listed here for reference only)
;_BODEN_ON                    EQU     H'3FFF'
;_BODEN_OFF                   EQU     H'3FBF'
;_CP_ALL                      EQU     H'03FF'
;_CP_75                       EQU     H'17FF'
;_CP_50                       EQU     H'2BFF'
;_CP_OFF                      EQU     H'3FFF'
;_PWRTE_OFF                   EQU     H'3FFF'
;_PWRTE_ON                    EQU     H'3FF7'
;_WDT_ON                      EQU     H'3FFF'
;_WDT_OFF                     EQU     H'3FFB'
;_LVP_ON                      EQU     H'3FFF'
;_LVP_OFF                     EQU     H'3F7F'
;_MCLRE_ON                    EQU     H'3FFF'
;_MCLRE_OFF                   EQU     H'3FDF'
;_ER_OSC_CLKOUT               EQU     H'3FFF'
;_ER_OSC_NOCLKOUT             EQU     H'3FFE'
;_INTRC_OSC_CLKOUT            EQU     H'3FFD'
;_INTRC_OSC_NOCLKOUT          EQU     H'3FFC'
;_EXTCLK_OSC                  EQU     H'3FEF'
;_LP_OSC                      EQU     H'3FEC'
;_XT_OSC                      EQU     H'3FED'
;_HS_OSC                      EQU     H'3FEE'

 __config _CP_OFF&_LVP_OFF&_BODEN_OFF&_MCLRE_ON&_PWRTE_ON&_WDT_OFF&_XT_OSC  

;
; *************************************************************************** 
; Info for power-up display                                                   
; *************************************************************************** 
;
MCODE_REV_0  equ  '2'    ; Current code version is 2.7  
MCODE_REV_1  equ  '.'    ;                                                   
MCODE_REV_2  equ  '7'    ;                                                   
MCODE_REV_3  equ  ' '    ;
MCODE_REV_4  equ  ' '    ;
MCODE_REV_5  equ  ' '    ;
MCODE_REV_6  equ  ' '    ;
MCODE_REV_7  equ  ' '    ;
;
; These initial values are stored in EEPROM for a 4 MHz clock
; The default values are adjusted via the Calibration mechanism.
; (Working values are in variables in CBLOCK)
;
LASTINTERVAL_DEFAULT equ 0   ; 0 = 1/10 second counting,  1=1-second counting
;
TENTHSECEEPROMSTART equ 1       ; 1/10 second parameters start at EEPROM byte 1
;
TT1_DEFAULT   equ     11    ;
TT2_DEFAULT   equ     5     ;
TT3_DEFAULT   equ     190   ; 190 = 0xBE
TT4_DEFAULT   equ     148   ; 150 = 0x96
TZZ_DEFAULT   equ     1     ;   
TFT_DEFAULT   equ     0     ;   
TFFT_DEFAULT  equ     0     ;   << Not used....
;
ONESECEEPROMSTART equ 8      ; One Second parameters start at EEPROM byte 8
;
OT1_DEFAULT  equ     11   ;
OT2_DEFAULT  equ     5    ;
OT3_DEFAULT  equ     190  ; 190 = 0XBE
OT4_DEFAULT  equ     150  ; 150 = 0x96
OZZ_DEFAULT  equ     2    ;    
OFT_DEFAULT  equ     1    ;    
OFFT_DEFAULT equ     7    ;    
;
; ****************************************************************************
; * Setup the initial constant, based on the frequency of the reference      *
; * oscillator.  This can be tweaked with the calibrate function.            *
; ****************************************************************************
; 
        ORG     0x2100
;                                
        DATA    LASTINTERVAL_DEFAULT

        DATA    TT1_DEFAULT
        DATA    TT2_DEFAULT
        DATA    TT3_DEFAULT
        DATA    TT4_DEFAULT
        DATA    TZZ_DEFAULT
        DATA    TFT_DEFAULT
        DATA    TFFT_DEFAULT
;
        DATA    OT1_DEFAULT
        DATA    OT2_DEFAULT
        DATA    OT3_DEFAULT
        DATA    OT4_DEFAULT
        DATA    OZZ_DEFAULT
        DATA    OFT_DEFAULT
        DATA    OFFT_DEFAULT
;
;       Clear unused EEPROM bytes (128 bytes for 16F628)
;
        DATA    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
        DATA    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0           
        DATA    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
        DATA    0,0,0,0,0,0,0,0,0,0,0,0,0,0

; ****************************************************************************
; * RAM page independent file registers:                                     *
; * (Listed here for reference only)                                         *
; ****************************************************************************
;
;INDF    EQU     0x00
;TMR0    EQU     0x01
;PCL     EQU     0x02
;STATUS  EQU     0x03
;FSR     EQU     0x04
;PCLATH  EQU     0x0A
;INTCON  EQU     0x0B
;T0IF    EQU     2                 ; INTCON Bit - Flag for TMR0 overflow
;GIE     EQU     7                 ; INTCON Bit - Enable/Disable interrupts
;OPTION_REG EQU  0x81
;CMCON   equ     0x1F

; ****************************************************************************
; * Bank 0 file registers:                                                   *
; * (Listed here for reference only)                                         *
; ****************************************************************************

;PORTA   EQU     0x05
;PORTB   EQU     0x06

; ****************************************************************************
; * Bank 1 file registers:                                                   *
; * (Listed here for reference only)                                         *
; ****************************************************************************

;TRISA   equ     0x85
;TRISB   equ     0x86
;EEDATA  equ     0x9A              ; Changed for 16F628
;EEADR   equ     0x9B              ; Changed for 16F628
;EECON1  equ     0x9C              ; New for 16F628
;EECON2  equ     0x9D              ; New for 16F628
;
;WREN    equ     0x02              ; EECON1 bits
;WR      equ     0x01              ; EECON1 bits
;RD      equ     0x00              ; EECON1 bits
; 
;
; *****************************************************************************
; * Bit numbers for the STATUS file register:                                 *
; * (Listed here for reference only)                                         *
; *****************************************************************************
;
;RP0   EQU     5   ; Use banksel instead!
;Z     EQU     2
;DC    EQU     1
;C     EQU     0
;
; ****************************************************************************
; * Assign names to IO pins.                                                 *
; ****************************************************************************
;
;   B register bits:
;
LCD11   equ     0x00              ; LCD Data lines
LCD12   equ     0x01              ;  "   "    "
LCD13   equ     0x02              ;  "   "    "
LCD14   equ     0x03              ;  "   "    "
LCD_busy equ    0x03              ; LCD busy bit 
LCD_e   equ     0x04              ; 0=disable, 1=enable
LCD_rw  equ     0x05              ; 0=write, 1=read
LCD_rs  equ     0x06              ; 0=instruction, 1=data
;
;   A register bits:
;
;ENCODER A equ  0x00              ; Encoder MUST BE HERE! is accessed by 
;ENCODER B equ  0x01              ;   direct read of PORTA
PB        equ   0x02              ; RA2
FalseP    equ   0x03              ; RA3 - "False Pulse"
;
; ****************************************************************************
; * Assign names to Flag bits                                                *
; ****************************************************************************
; IntervalFlag bits
OneSec  equ  0x00                 ; Flag bit for interval size. 1=1-sec, 0=1/10 
;
; ****************************************************************************
; *           Allocate variables in general purpose register space           *
; ****************************************************************************
;
        CBLOCK  0x20              ; Start Data Block  (16F628)
;
        IntervalFlag              ; Interval Flag
        ;  OneSec  equ  0         ; Bit 0=1 if 1-sec interval, 0 if 1/10 sec
        TenthSecCount             ; Counter of 1/10 second intervals 
        T1                        ; \
        T2                        ;  \
        T3                        ;   \
        T4                        ;    Timing Loop Constants
        ZZ                        ;   /
        FT                        ;  /
        FFT                       ; /
        TT1                       ; \
        TT2                       ;  \
        TT3                       ;   \
        TT4                       ;    Timing Loop Constants - Tenth second
        TZZ                       ;   /
        TFT                       ;  /
        TFFT                      ; /
        OT1                       ; \
        OT2                       ;  \
        OT3                       ;   \
        OT4                       ;    Timing Loop Constants - One second
        OZZ                       ;   /
        OFT                       ;  /
        OFFT                      ; /
        R1                        ; \                 
        R2                        ;  \
        R3                        ;   \
        R4                        ;     Temp Timing loop counters
        R5                        ;   /
        R6                        ;  /
        R7                        ; /
        Display_temp              ; Temp 
        freq_0                    ; Display frequency (hex) 
          freq_1                  ;  (4 bytes) 
          freq_2                  ;
          freq_3                  ;
        BCD_0                     ; Display frequency (BCD) 
          BCD_1                   ;  (5 bytes)
          BCD_2                   ;
          BCD_3                   ;
          BCD_4                   ;
        BCD_count                 ; Used in bin2BCD routine
        BCD_temp                  ;   "
        byte2send                 ;
        LCD_char                  ; Character being sent to the LCD
        LCD_read                  ; Character read from the LCD
        timer1                    ; Used in delay routines
        timer2                    ;   "
        ren_new                   ; New value of encoder pins A and B
        ren_old                   ; Old value of encoder pins A and B
        ren_read                  ; Encoder pins A and B and switch pin
        last_dir                  ; Indicates last direction of encoder
        next_dir                  ; Indicates expected direction
        count                     ; loop counter  (gets reused)                   
        rs_value                  ; The LCD rs line flag value
        PB_wait_count             ; Wait for 2 seconds 
        loopcnt                   ;    
        TimerH                    ;
        LowB                      ;
        MidB                      ;
        HigB                      ;
        prescaler                 ; prescaler counter 
        TEMP1                     ; temporary register for display
        TEMP2                     ; temporary register for display
        TEMP3                     ; temporary register for display
        TEMP4                     ; temporary register for display
        TEMP5                     ; temporary register for display
        TEMP6                     ; temporary register for display
        TEMP7                     ; temporary register for display
        multfac                   ; temp for multiply by 10
        prod3                     ; temp  "     "      "  "
        prod2                     ; temp  "     "      "  "
        prod1                     ; temp  "     "      "  "
        prod0                     ; temp  "     "      "  "
        ENDC                      ; End of Data Block
; 
; ****************************************************************************
; * The 16F628 resets to 0x00.                                               * 
; * The Interrupt vector is at 0x04. (Unused)                                *
; ****************************************************************************             
;
        ORG     0x0000                
reset_entry
        goto    start             ; Go to start
;
; *****************************************************************************
; Timing Instruction Tables
TTable 
        addwf   PCL,F             ; (FTOverhead=5)(Computed GOTO - counts as 2!)
        goto    Cycle20           ; (FTOverhead=7) Fine tuning loop with 0 NOPs
        goto    Cycle21           ; (FTOverhead=7) Fine tuning loop with 1 NOP 
        goto    Cycle22           ; (FTOverhead=7) Fine tuning loop with 2 NOPs
;
; *****************************************************************************
; Final Fine Tuning Instruction Table                      
; After Excuting X instructions, RETURN TO CALLER of FFTTable!
; (Total FFTOverhead is 9 instructions, including return to caller.) 
FFTTable 
        addwf   PCL,F             ; FFTOverhead=5 (Computed GOTO - counts as 2!)
        goto    Wait0Inst         ; 7+0 +2 for return to caller
        goto    Wait1Inst         ; 7+1 +2 for return to caller
        goto    Wait2Inst         ; 7+2 +2 for return to caller
        goto    Wait3Inst         ; 7+3 +2 for return to caller
        goto    Wait4Inst         ; 7+4 +2 for return to caller
        goto    Wait5Inst         ; 7+5 +2 for return to caller
        goto    Wait6Inst         ; 7+6 +2 for return to caller
        goto    Wait7Inst         ; 7+7 +2 for return to caller
        goto    Wait8Inst         ; 7+8 +2 for return to caller
        goto    Wait9Inst         ; 7+9 +2 for return to caller
;
; *****************************************************************************
; * start                                                                     *
; *                                                                           *
; * Purpose:  This is the start of the program.                               *
; *                                                                           *
; *   Input:  Signal input at PIC-EL RF jack J7 (RF IN/OUT)                   *
; *                                                                           *
; *  Output:  Count displayed on LCD                                          *
; *                                                                           *
; *****************************************************************************
;
start
        clrf    INTCON            ; No interrupts for now
        movlw   0x07              ; Code to turn off the analog comparators
        movwf   CMCON             ; Turn off comparators  (16F628)
        clrwdt                    ; No watchdog timer
        call    wait_8ms          ; Wait for LCD to settle
        banksel TRISA             ; 
        movlw   b'00100111'       ; Prescaler -> TMR0, Weak pull-ups enabled
        movwf   OPTION_REG        ; 1:256, rising edge
        movlw   b'00010111'       ; Set R0,R1,R2,RA4 input, others LOW outputs
                                  ; This clamps RA4 low (TMR0 inactive)
        movwf   TRISA             ; (RA0..RA2 also outputs but not important)
        clrf    TRISB             ; All LCD pins to outputs     
        banksel PORTA             ; Switch back to bank 0
        call    init_LCD          ; Initialize the LCD
        call    display_version   ; Display title and version                  
;
;       Enter Calibrate Mode if push button is pressed while turning the 
;       power on.
;
        btfss   PORTA,PB          ; Is pushbutton pressed? (low active)
        call    calibrate         ; Yes, calibrate
;
;       Get the current constants from the EEPROM
;
        banksel TRISA             ; Switch to bank 1
        clrf    EEADR             ; Set EEPROM read location to ZERO
        call    read_EEPROM       ; Read EEPROM (all in bank 1)
        movf    EEDATA,w          ; Get the first osc byte
        banksel PORTA             ; Back to bank 0 for store
        movwf   IntervalFlag      ; Save flag, bit7=0 for 1/10 sec,  1 for 1-sec counting
;
        banksel TRISA             ; Switch to bank 1
        call    read_EEPROM       ; Read EEPROM (all in bank 1)
        movf    EEDATA,w          ; Get the first osc byte
        banksel PORTA             ; Back to bank 0 for store
        movwf   TT1               ; Save TenthT1

        banksel TRISA             ; Switch to bank 1
        call    read_EEPROM       ; Read EEPROM (all in bank 1)
        movf    EEDATA,w          ; Get the first osc byte
        banksel PORTA             ; Back to bank 0 for store
        movwf   TT2               ; Save TenthT2

        banksel TRISA             ; Switch to bank 1
        call    read_EEPROM       ; Read EEPROM (all in bank 1)
        movf    EEDATA,w          ; Get the first osc byte
        banksel PORTA             ; Back to bank 0 for store
        movwf   TT3               ; Save TenthT3

        banksel TRISA             ; Switch to bank 1
        call    read_EEPROM       ; Read EEPROM (all in bank 1)
        movf    EEDATA,w          ; Get the first osc byte
        banksel PORTA             ; Back to bank 0 for store
        movwf   TT4               ; Save TenthT4

        banksel TRISA             ; Switch to bank 1
        call    read_EEPROM       ; Read EEPROM (all in bank 1)
        movf    EEDATA,w          ; Get the first osc byte
        banksel PORTA             ; Back to bank 0 for store
        movwf   TZZ               ; Save TenthTZZ

        banksel TRISA             ; Switch to bank 1
        call    read_EEPROM       ; Read EEPROM (all in bank 1)
        movf    EEDATA,w          ; Get the first osc byte
        banksel PORTA             ; Back to bank 0 for store
        movwf   TFT               ; Save TenthFT

        banksel TRISA             ; Switch to bank 1
        call    read_EEPROM       ; Read EEPROM (all in bank 1)
        movf    EEDATA,w          ; Get the first osc byte
        banksel PORTA             ; Back to bank 0 for store
        movwf   TFFT              ; Save TenthFFT
;
        banksel TRISA             ; Switch to bank 1
        call    read_EEPROM       ; Read EEPROM (all in bank 1)
        movf    EEDATA,w          ; Get the first osc byte
        banksel PORTA             ; Back to bank 0 for store
        movwf   OT1               ; Save OneSecT1

        banksel TRISA             ; Switch to bank 1
        call    read_EEPROM       ; Read EEPROM (all in bank 1)
        movf    EEDATA,w          ; Get the first osc byte
        banksel PORTA             ; Back to bank 0 for store
        movwf   OT2               ; Save OneSecT2

        banksel TRISA             ; Switch to bank 1
        call    read_EEPROM       ; Read EEPROM (all in bank 1)
        movf    EEDATA,w          ; Get the first osc byte
        banksel PORTA             ; Back to bank 0 for store
        movwf   OT3               ; Save OneSecT3

        banksel TRISA             ; Switch to bank 1
        call    read_EEPROM       ; Read EEPROM (all in bank 1)
        movf    EEDATA,w          ; Get the first osc byte
        banksel PORTA             ; Back to bank 0 for store
        movwf   OT4               ; Save OneSecT4

        banksel TRISA             ; Switch to bank 1
        call    read_EEPROM       ; Read EEPROM (all in bank 1)
        movf    EEDATA,w          ; Get the first osc byte
        banksel PORTA             ; Back to bank 0 for store
        movwf   OZZ               ; Save OneSecZZ

        banksel TRISA             ; Switch to bank 1
        call    read_EEPROM       ; Read EEPROM (all in bank 1)
        movf    EEDATA,w          ; Get the first osc byte
        banksel PORTA             ; Back to bank 0 for store
        movwf   OFT               ; Save OneSecFT

        banksel TRISA             ; Switch to bank 1
        call    read_EEPROM       ; Read EEPROM (all in bank 1)
        movf    EEDATA,w          ; Get the first osc byte
        banksel PORTA             ; Back to bank 0 for store
        movwf   OFFT              ; Save OneSecFFT
;
        call    DisplayInterval   ; Display interval on LCD, line 1, position 16
;
; Fall into the Main Program Loop
;
; *****************************************************************************
; * main                                                                      *
; *                                                                           *
; * Purpose:  This is the Main Program Loop.                                  *
; *                                                                           *
; *           Measure the pulses for 1/10 second and multiply by 10 (giving   *
; *           a counter resolution is 10 Hz) or measure the pulses for one    *
; *           second, giving a counter resolution of 1 Hz.                    *
; *                                                                           *
; *           Final count is accumulated in 3 bytes (maximum 16,777,215).     *
; *           If 1/10 second interval is used, after multiplying by 10, the   *
; *           theoretical maximum frequency is 167 MHz. If the measurement    *
; *           interval is 1 second, the 3 registers limit the measured        *
; *           frequency to 16,777,215.  Note that there are other PIC-EL      *
; *           hardware limitations that limit this maximum frequency to about *
; *           15 MHz anyway.                                                  *
; *                                                                           *
; *           The general plan is as follows:                                 * 
; *           1) Open RA4 "gate", the Timer0 input                            *
; *           2) Leave RA4 "gate" open for .1 s (prescaler input)             *
; *           3) Close RA4 (Clamped low by RA3)                               *
; *           4) Empty prescaler; add value to total count                    *
; *           5) Multiply final count by 10                                   *
; *           6) Display the value on the LCD                                 *
; *           7) Loop back to the top (main) and repeat                       *
; *                                                                           *
; *   Input:  Signal input at PIC-EL RF input jack                            * 
; *                                                                           *
; *  Output:  Count displayed on LCD                                          *
; *                                                                           *
; *****************************************************************************
;
main
;
        btfsc   IntervalFlag,OneSec ; Is one-second interval enabled?
        goto    MainOne           ; Yes, go set up for one second
;
        movf    TT1,w             ;\
        movwf   T1                ; \
        movf    TT2,w             ;  \
        movwf   T2                ;   \
        movf    TT3,w             ;    \
        movwf   T3                ;     \
        movf    TT4,w             ; Set up working regs from TenthSecond save
        movwf   T4                ;      /
        movf    TZZ,w             ;     /
        movwf   ZZ                ;    /
        movf    TFT,w             ;   /
        movwf   FT                ;  /
        movf    TFFT,w            ; /
        movwf   FFT               ;/
        goto    MainRegsOK        ; Done setting up working registers
;
MainOne    
        movf    OT1,w             ;\
        movwf   T1                ; \
        movf    OT2,w             ;  \
        movwf   T2                ;   \
        movf    OT3,w             ;    \
        movwf   T3                ;     \
        movf    OT4,w             ; Set up working regs - from OneSecond save
        movwf   T4                ;      /
        movf    OZZ,w             ;     /
        movwf   ZZ                ;    /
        movf    OFT,w             ;   /
        movwf   FT                ;  /
        movf    OFFT,w            ; /
        movwf   FFT               ;/
MainRegsOK
;
;-------------------------------------------------------------------------
; It is time to prepare new measuring cycle
;-------------------------------------------------------------------------
;
; See if pushbutton is being pressed to switch timing interval
;
        btfsc   PORTA,PB          ; Is pushbutton pressed? (low active)
        goto    MainOK            ; No, MainOK
        btfss   IntervalFlag,OneSec ; Is one-second interval currently selected?
        goto    SetOneSec         ; No, go set to 1 second
        bcf     IntervalFlag,OneSec ; Clear 1-second interval (select 1/10 sec)
        goto    IntervalOK        ; Interval now OK.  Go to display indicator on LCD.
SetOneSec   
        bsf     IntervalFlag,OneSec ; Set the 1-second interval
IntervalOK
        banksel TRISA             ; Switch to bank 1
        clrf    EEADR             ; Set EEPROM read location to ZERO
        banksel PORTA             ; Back to bank 0
        movf    IntervalFlag,w    ; Get IntervalFlag byte to record (bank 0)
        banksel TRISA             ; Switch to bank 1
        movwf   EEDATA            ; Put byte in EEPROM write location 
        call    write_EEPROM      ; (all in bank 1)
        banksel PORTA             ; Back to bank 0
        call    DisplayInterval   ; Display interval on LCD, line 1, position 16
;
        call       wait_16ms      ; Debounce switch (wait for it to settle)
WaitForRelease
        btfss      PORTA,PB   ; Pushbutton still being held down?
        goto       WaitForRelease ; Yes, loop until it's released
;
MainOK
        clrf    TimerH            ;
        clrf    TMR0              ;
        movf    T1,W              ; Get initial counter values and
        movwf   R1                ;   move into working registers
        movf    T2,W              ;
        movwf   R2                ;
        movf    T3,W              ;
        movwf   R3                ;
        movf    T4,W              ;
        movwf   R4                ;
        movf    ZZ,W              ;
        movwf   R5                ;
        movf    FT,W              ;
        movwf   R6                ;
        movf    FFT,W             ;
        movwf   R7                ;
        movlw   10                ; Set up number of 1/10 second intervals  (default) 
                                  ; (Count gets decremented before being used and loop
                                  ;  does not execute when count reaches zero)
        movwf   TenthSecCount     ; (Won't be used if 1-sec interval is not selected)
        clrf    INTCON            ; global INT disable, TMR0 INT disable
                                  ; clear TMR0 overflow flag
; ------------------------------------------------------------------------
; Start measurement:  RA4 set to input
; ------------------------------------------------------------------------

        banksel TRISA             ; Switch to bank 1
        movlw   b'00011111'       ; RA4,RA3,R2,R1,R0 = input
        movwf   TRISA             ; TMR0 (RA4) is now open for business
;
; -------------------------------------------------------------------------
; "Gate" is open now   
; -------------------------------------------------------------------------
        banksel PORTA             ; 2 Back to bank 0 

Cycle1      
        btfss   INTCON,T0IF       ; 1 Test for TMR0 overflow
        goto    Cycle1NoOv        ; 3 No overflow so keep going
        incf    TimerH,F          ; 3 Overflow occurred so inc high byte
        bcf     INTCON,T0IF       ; 4 Clear TMR0 overflow flag
        goto    Cycle1Nxt         ; 6
Cycle1NoOv  
        nop                       ; 4 (waste same number of instructions)
        nop                       ; 5
        nop                       ; 6
Cycle1Nxt   
        decfsz  R1,F              ; 7
        goto    Cycle1            ; 9
        movf    T1,w              ;   2+(9*T1)+0
        movwf   R1                ;   2+(9*T1)+1
               
        decfsz  R2,F              ;   2+(9*T1)+2
        goto    Cycle1            ;   2+(9*T1)+4
        movf    T2,w              ;   2+(((9*T1)+4)*T2)+0
        movwf   R2                ;   2+(((9*T1)+4)*T2)+1

        decfsz  R3,F              ;   2+(((9*T1)+4)*T2)+2
        goto    Cycle1            ;   2+(((9*T1)+4)*T2)+4
        nop                       ;   2+(((((9*T1)+4)*T2)+4)*T3)+0
;
; THE "ZZ" LOOP
;
; Yes, this tweak loop is only in multiples of 3 so we cannot use ZZ alone to get a total
; loop count to be exactly 100,000 instructions.  That's where the FT mechanism comes
; in later.  This timing mechanism was changed so that calibration can be performed to 
; adjust the loop counts. Calibration is very important since the PIC crystal is almost
; never right on frequency so the instructions are not executed at exactly the calculated
; speed. The default loops will execute very near 100,000 instructions (1 uSec per 
; instruction, so 1/10 sec). However, calibration will adjust the loop counts so that the
; loops contain the correct number of instructions to keep the gate open for 1/10 sec so 
; the counter will be accurate with the existing PIC clock.  This calibration is more 
; important than coming up with exactly 100,000 instrctions for the loop.
;
; NOTE: The "tweak" value, ZZ, MUST BE KEPT SMALL since there's no check for TMR0 overflow 
; within the ZZ loop.  It should be 1, 2 or 3 since there are 3 instructions in each ZZ-loop
; and T4 can then be adjusted to give increments of 9.  ZZ cannot be zero or it wraps around
; and looks like 256. 
;
tweakloop   
                                  ;   2+(((((9*T1)+4)*T2)+4)*T3)
        decfsz  R5,F              ; 1
        goto    tweakloop         ; 3
        nop                       ; 3 waste one - now equal in all cases  
                                  ; Loop adds (3*ZZ) instructions
;
; THE "FINAL LOOP" -  one of three loops executed, depending on fine-tuning parameter
; Loop will add (Fine-tuning overhead) + FT + (9*T4) instructions to the timing interval
;
                                  ;    2+(((((9*T1)+4)*T2)+4)*T3)+(3*ZZ)
        movf    R6,w              ; FTOverhead=1 Select which final fine-tune to execute
        goto    TTable            ; FToverhead=3 Go to table for fine-tune routine selection 
; 
; TTable will jump to one of these variable-sized loops below, depending on FT (in R6)
; (FineTuneOverhead is 6 at this point.)
;
; ------- Execute this version of T4 loop only if FT=0 ----------
Cycle20     
        btfss   INTCON,T0IF       ; 1  Check TMR0 overflow flag
        goto    Cycle20NoOv       ; 3  No overflow so keep going
        incf    TimerH,F          ; 3  Overflow occurred so inc high byte
        bcf     INTCON,T0IF       ; 4  Clear TMR0 overflow flag
        goto    Cycle20Nxt        ; 6

Cycle20NoOv 
        nop                       ; 4 (waste same number of instructions)
        nop                       ; 5
        nop                       ; 6

Cycle20Nxt  
            decfsz     R4,F       ; 7  
            goto       Cycle20    ; 9 
            nop                   ; 9   
; End of T4 loop
; Now add fine-tuning NOP 
                                  ;    2+((9*T1+4)*T2+4)*T3+(3*ZZ)+(9*T4)
                                  ; (7 for FineTuningOverhead) plus no NOPs for FT=0
        goto    CheckOneSec       ; (9 for FineTuningOverhead) plus no NOPs for FT=0 
                                  ;      2+((9*T1+4)*T2+4)*T3+(3*ZZ)+(9*T4)+(9+FT)
;
; ------- Execute this version of T4 loop only if FT=1 ----------
Cycle21     
        btfss   INTCON,T0IF       ; 1  Check TMR0 overflow flag
        goto    Cycle21NoOv       ; 3  No overflow so keep going
        incf    TimerH,F          ; 3  Overflow occurred so inc high byte
        bcf     INTCON,T0IF       ; 4  Clear TMR0 overflow flag
        goto    Cycle21Nxt        ; 6

Cycle21NoOv 
        nop                       ; 4 (waste same number of instructions)
        nop                       ; 5
        nop                       ; 6

Cycle21Nxt  
        decfsz  R4,F              ; 7  
        goto    Cycle21           ; 9 
        nop                       ; 9   2+((9*T1+4)*T2+4)*T3+(3*ZZ)+(9*T4)+0
; End of T4 loop
; Now add fine-tuning NOP 
        nop                       ; (7 for FineTuningOverhead) plus one NOP for FT=1
        goto    CheckOneSec       ; (9 for FineTuningOverhead) plus one NOP for FT=1 
                                  ;     2+((9*T1+4)*T2+4)*T3+(3*ZZ)+(9*T4)+(9+FT)
;
; ------- Execute this version of T4 loop only if FT=2 ----------
Cycle22     
        btfss   INTCON,T0IF       ; 1  Check TMR0 overflow flag
        goto    Cycle22NoOv       ; 3  No overflow so keep going
        incf    TimerH,F          ; 3  Overflow occurred so inc high byte
        bcf     INTCON,T0IF       ; 4  Clear TMR0 overflow flag
        goto    Cycle22Nxt        ; 6

Cycle22NoOv 
        nop                       ; 4 (waste same number of instructions)
        nop                       ; 5
        nop                       ; 6

Cycle22Nxt  
        decfsz  R4,F              ; 7  
        goto    Cycle22           ; 9 
        nop                       ; 9   2+((9*T1+4)*T2+4)*T3+(3*ZZ)+(9*T4)+0
; End of T4 loop
; Now add fine-tuning NOPs
        nop                       ; (7 for FineTuningOverhead) plus
        nop                       ;   two NOPs for FT=2
        goto    CheckOneSec       ; (9 for FineTuningOverhead) plus two NOPs for FT=2 
                                  ;      2+((9*T1+4)*T2+4)*T3+(3*ZZ)+(9*T4)+(9+FT)
;
; ------------------------------------------------------------------------
; See if measurement interval is 1 second    
; ------------------------------------------------------------------------
CheckOneSec
        btfss   IntervalFlag,OneSec ; 1 Is one-second interval requested?
        goto    TenthSecTimingCleanup; 3  No, 1/10 second interval so clean up
        decfsz  TenthSecCount,f   ; 3 Yes, are any more 1/10 second loops needed?
        goto    TenthSecLoopAgain ; 5 Yes, set up for new loop and go start it
        nop                       ; 5 (For clarity.  Wait for equivalent time if skip)
        goto    MultipleLoopsDone ; 7 Done with the 1/10 sec loops, ready for tweak
;
; ========= 1/10 SECOND TIMING INTERVAL =========
;  
TenthSecTimingCleanup
; 1/10 second interval is all that's requested.  Just clean up and end.
        call    Wait10Inst        ; 17 (adds 10 + 4 overhead)
        call    Wait9Inst         ; 30 (adds 9 + 4 overhead)
        goto    CloseGate         ; 32  
;
; ========= ONE SECOND TIMING INTERVAL =========
;  
TenthSecLoopAgain
                                  ;     2+((9*T1+4)*T2+4)*T3+(3*ZZ)+(9*T4)+(9+FT)+5
        movf    T1,W              ; 6  Get initial counter values and
        movwf   R1                ; 7    move into working registers
        movf    T2,W              ; 8 \ 
        movwf   R2                ; 9  \
        movf    T3,W              ; 10   \
        movwf   R3                ; 11    \
        movf    T4,W              ; 12     \
        movwf   R4                ; 13   Set up for new pass through 1/10 sec loop
        movf    ZZ,W              ; 14      /
        movwf   R5                ; 15     /
        movf    FT,W              ; 16    /
        movwf   R6                ; 17   /
        movf    FFT,W             ; 18  /
        movwf   R7                ; 19 /
        goto    Cycle1            ; 21  Back to the top of 1/10 second loop
;
MultipleLoopsDone
; Ten 1/10 second loops now done.  Ready to do final tweak and close gate 
; (14 instructions - fall through)
        call    Wait10Inst        ; 14 (10 + 4 overhead)
                                  ;   2+((((9*T1+4)*T2+4)*T3+(3*ZZ)+(9*T4)+(9+FT)+21)*10)
OneSecondTweak
; Need to allow 0-9 instructions for 1-second fine-tuning calibration.  One instruction 
; difference in the 10 1/10 second loops is equivalent to 10 instructions here. 
;
        movf    FFT,w             ; Total FinalFineTuneOverhead 
        call    FFTTable          ;    is 9 instructions 
                                  ;  2+((((9*T1+4)*T2+4)*T3+(3*ZZ)+(9*T4)+(9+FT)+(19+6))*10)+(FFT+9)
;
; ------------------------------------------------------------------------
; Stop the measurement
; ------------------------------------------------------------------------
CloseGate
; One final check for overflow
;  2+((9*T1+4)*T2+4)*T3+(3*ZZ)+(9*T4)+(9+FT)+32                  (if 1/10 sec)
;  2+((((9*T1+4)*T2+4)*T3+(3*ZZ)+(9*T4)+(9+FT)+25)*10)+(FFT+9)   (if 1 sec)
        btfss   INTCON,T0IF       ; 1 Test for TMR0 overflow
        goto    LastCheck         ; 3 No overflow so waste a few and done. 
        incf    TimerH,F          ; 3 Overflow occurred so inc high byte
        bcf     INTCON,T0IF       ; 4 Clear TMR0 overflow flag
        goto    LastCheckDone     ; 6
LastCheck   
        nop                       ; 4 (waste same number of instructions)
        nop                       ; 5
        nop                       ; 6
LastCheckDone
        movlw   b'00010000'       ; 7 RA0..RA3 = 0 (lock TMR0 OFF)
        movwf   PORTA             ; 8 W -> PORTA
        banksel TRISA             ; 10 Switch to bank 1
        movlw   b'00010111'       ; 11 Turn RA3 to (low) output, RA4,R2-R0 inputs
        movwf   TRISA             ; 12 (No RF into RA4 since RA3 holds it low)

;
; "Gate" is now closed
; Counter is no longer taking input from the external source since RA3 is now a 
; low output - except when RA3 is generating the "false pulses" below.
;
;  2+((9*T1+4)*T2+4)*T3+(3*ZZ)+(9*T4)+(9+FT)+32+12                 (if 1/10 sec)
;  2+((((9*T1+4)*T2+4)*T3+(3*ZZ)+(9*T4)+(9+FT)+25)*10)+(FFT+9)+12  (if 1 sec)
;
        banksel PORTA             ; Back to bank 0

; ------------------------------------------------------------------------
; Analyze precounter and store counted value in registers
;
; Note: This routine is required because the timer is using a prescaler of 256. 
;       This routine generates "false pulse" signals into the timer to see 
;       when it overflows. The number of "false pulses" required to cause an 
;       overflow is used to calculate the number of "real pulses" that were 
;       already in the timer register (TMR0) at the end of the timing cycle.
;       These "real pulses" are used as the low-order frequency byte.  If this
;       "false pulse" mechanism was not used, the lowest order byte count will
;       always be 255 so, when multiplied by 10, the displayed frequency could
;       be off as much as 2550 Hz.
;  
;       This routine requires the frequency counter hardware to have a "short"
;       between PIC pin 2 (RA3) and Pin 3 (RA4). On the PIC-EL board this can
;       be done by simply inserting a shorted plug into the CW paddle stereo 
;       jack (J3). 
; ------------------------------------------------------------------------

        movf    TMR0,W
        movwf   MidB              ; TMR0 -> MidB
        movf    TimerH,W
        movwf   HigB              ; TimerH -> HigB
        clrf    prescaler         ; Initialize
CountIt     
        incf    prescaler,F       ; Add one to "false pulse" count
        bsf     PORTA,FalseP      ; _| raise "False Pulse" for prescaler empty
        nop
        nop
        bcf     PORTA,FalseP      ;   |_ drop "False Pulse"          
        bcf     INTCON,T0IF       ; Clear TMR0 overflow flag
        movf    TMR0,W            ; Current TMR0 -> W
        bcf     STATUS,Z          ; Clear Z flag
        subwf   MidB,W            ; See if TMR0 has changed since save
        btfss   STATUS,Z          ; Has TMR0 changed?  (Z is set if identical - not changed)
        goto    DoneFT            ; Yes, it changed so we have the count and we are done
        movf    prescaler,F       ; TMR0 not changed. See if false pulses count overflowed
                                  ;   If there is no "short" from RA3 to RA4 (TMR0), false 
                                  ;   pulses will never increment TMR0 and it would loop forever.
        btfss   STATUS,Z          ; Has false pulse count wrapped back to zero? (256 pulses)
        goto    CountIt           ; Not yet, so we are OK to keep counting.  Loop back
DoneFT
        comf    prescaler,F       ; Complement to see how many were there BEFORE the false pulses
        incf    prescaler,W       ; Move actual prescaler count (real pulses) to W
        movwf   LowB              ; Save as least significant byte (for debugging)
        movwf   freq_0            ;\ 
        movf    MidB,W            ; \
        movwf   freq_1            ;  Update freq bytes for display
        movf    HigB,W            ;  /
        movwf   freq_2            ; /
        clrf    freq_3            ;/
; Start temp debug code  ================================
#ifdef DEBUG
        movf    freq_2,W          ; Most significant byte
        movwf   TEMP1             ;   in first position
        movf    freq_1,W          ; Next most significant
        movwf   TEMP2             ;   in next position                      
        movf    freq_0,W          ; Low byte (prescaler value)
        movwf   TEMP3             ;   in next position
        clrf    TEMP4             ;\         
        clrf    TEMP5             ;  \         
        clrf    TEMP6             ;  Clear remaining 4 bytes
        clrf    TEMP7             ; /            
        call    DisplayHEXData    ; Display 5 bytes (HEX) on second line of LCD  
#endif
; End temp debug code ===================================

; The measurement period is either 1/10 second (100,000 PIC instructions with 4 MHz clock.)
; or one second.  If it's 1/10 second, to display the true frequency (pulses per second),
; the .1s count needs to be multiplied by 10 before displaying it.  If one second, display
; it as is.
;
        btfss   IntervalFlag,OneSec ; Is one-second interval enabled?
        call    MultBy10          ; If not, multiply freq bytes by 10
        call    bin2BCD           ; Convert freq_bytes to BCD
        call    show_freq         ; Display it
        call    wait_256ms        ; Give time to see the display
        call    wait_256ms        ; Give time to see the display
        goto    main              ; Continue main loop                         
;
; ******************************************************************************
; * CheckTimer                                                                 *
; *                                                                          *
; * Purpose:  Check For TMR0 Overflow                                          *
; *                                                                            *
; *   Input: INTCON                                                            *
; *                                                                            *
; *  Output: TimerH updated and INTCON cleared if overflow occurred            *
; *                                                                            *
; ******************************************************************************
;
CheckTimer
        btfss   INTCON,T0IF       ; 3  Check TMR0 overflow flag 
                                  ; (including 2 instructions to get here)
        goto    CheckTimerNoOv    ; 5  No overflow so keep going
        incf    TimerH,F          ; 5  Overflow occurred so inc high byte
        bcf     INTCON,T0IF       ; 6  Clear TMR0 overflow flag
        goto    CheckTimerExit    ; 8
CheckTimerNoOv
        nop                       ; 6 (waste same number of instructions)
        nop                       ; 7
        nop                       ; 8
CheckTimerExit  
        return                    ; 10
;
; *****************************************************************************
; * init_LCD                                                                  *
; *                                                                           *
; * Purpose:  Power on initialization of Liquid Crystal Display.  The LCD     *
; *           controller chip must be equivalent to an Hitachi 44780.  The    *
; *           LCD is assumed to be a 16x2 display.                            * 
; *                                                                           *
; *   Input:  None                                                            *
; *                                                                           *
; *  Output:  None                                                            *
; *                                                                           *
; *****************************************************************************
;
init_LCD
        call    wait_64ms         ; Wait for LCD to power up
;        Put 4-bit command in RB3..RB0                                         
;        PIC's RB3..RB0 lines connect to LCD's DB7..DB4 (pins 14-11)            
        movlw   0x03              ; LCD init instruction (First)               
        movwf   PORTB             ; Send to LCD via RB3..RB0                   
        bsf     PORTB,LCD_e       ; Set the LCD E line high,
        call    wait_64ms         ;   wait a "long" time,
        bcf     PORTB,LCD_e       ;   and then Clear E 
        movlw   0x03              ; LCD init instruction (Second)              
        movwf   PORTB             ; Send to LCD via RB3..RB0                   
        bsf     PORTB,LCD_e       ; Set E high,
        call    wait_32ms         ;   wait a while,
        bcf     PORTB,LCD_e       ;   and then Clear E 
        movlw   0x03              ; LCD init instruction (Third)               
        movwf   PORTB             ; Send to LCD via RB3..RB0                   
        bsf     PORTB,LCD_e       ; Set E high,
        call    wait_32ms         ;   wait a while,
        bcf     PORTB,LCD_e       ;   and then Clear E
        movlw   0x02              ; 4-bit mode instruction                     
        movwf   PORTB             ; Send to LCD via RB3..RB0                   
        bsf     PORTB,LCD_e       ; Set E high,
        call    wait_16ms         ;   wait a while,
        bcf     PORTB,LCD_e       ;   and then Clear E
        movlw   0x28              ; 1/16 duty cycle, 5x8 matrix
        call    cmnd2LCD          ; Send command in w to LCD
        movlw   0x08              ; Display off, cursor and blink off 
        call    cmnd2LCD          ; Send command to LCD
        movlw   0x01              ; Clear and reset cursor
        call    cmnd2LCD          ; Send command in w to LCD
        movlw   0x06              ; Set cursor to move right, no shift
        call    cmnd2LCD          ; Send command in w to LCD
        movlw   0x0C              ; Display on, cursor and blink off
        call    cmnd2LCD          ; Send command in w to LCD
        return                    ; 
;
; *****************************************************************************
; * display_version                                                           *
; *                                                                           *
; * Purpose:  Display version and other info on LCD for 2 seconds             *
; *           upon power-up                                                   *
; *                                                                           *
; *   Input:  MCODE_REV_0 through MCODE_REV_7                                 *
; *                                                                           *
; *  Output:  LCD displays debug info                                         *
; *                                                                           *
; *****************************************************************************
;
display_version
        movlw   0x80              ; Point LCD at digit 1                
        call    cmnd2LCD          ;
        movlw   'P'               ; digit 1              
        call    data2LCD          ;
        movlw   'I'               ; digit 2                  
        call    data2LCD          ;
        movlw   'C'               ; digit 3                
        call    data2LCD          ;
        movlw   'E'               ; digit 4               
        call    data2LCD          ;
        movlw   'L'               ; digit 5              
        call    data2LCD          ;        
        movlw   'c'               ; digit 6              
        call    data2LCD          ;
        movlw   'n'               ; digit 7               
        call    data2LCD          ;
        movlw   't'               ; digit 8               
        call    data2LCD          ;
        movlw   0x88              ; Point LCD at digit 9
        movwf   LCD_char          ; 
        call    cmnd2LCD          ; Send command to LCD
        movlw   MCODE_REV_0       ; Get mcode rev byte
        call    data2LCD          ;   and display it (digit 9)
        movlw   MCODE_REV_1       ; Get mcode rev byte
        call    data2LCD          ;   and display it (digit 10)
        movlw   MCODE_REV_2       ; Get mcode rev byte
        call    data2LCD          ;   and display it (digit 11)
        movlw   MCODE_REV_3       ; Get mcode rev byte
        call    data2LCD          ;   and display it (digit 12)
        movlw   MCODE_REV_4       ; Get mcode rev byte
        call    data2LCD          ;   and display it (digit 13)
        movlw   MCODE_REV_5       ; Get mcode rev byte
        call    data2LCD          ;   and display it (digit 14)
        movlw   MCODE_REV_6       ; Get mcode rev byte
        call    data2LCD          ;   and display it (digit 15)
        movlw   MCODE_REV_7       ; Get mcode rev byte
        call    data2LCD          ;   and display it (digit 16)
        call    wait_a_sec        ; Wait one second
;
        return

; *****************************************************************************
; * DisplayInterval                                                           *
; *                                                                           *
; * Purpose:  Display timing interval indicator on LCD                        *
; *                                                                           *
; *   Input:  IntervalFlag in EEPROM                                          *
; *                                                                           *
; *  Output:  LCD displays interval indicator on LCD                          *
; *                                                                           *
; *****************************************************************************
;
DisplayInterval        
;
;       Get the current constants from the EEPROM
;
        banksel TRISA             ; Switch to bank 1
        clrf    EEADR             ; Set EEPROM read location to ZERO
        call    read_EEPROM       ; Read EEPROM (all in bank 1)
        movf    EEDATA,w          ; Get the first osc byte
        banksel PORTA             ; Back to bank 0 for store
        movwf   IntervalFlag      ; Save flag, bit7=0 for 1/10 sec,  1 for 1-sec counting
; Now display it
        movlw   0x8F              ; Point LCD to line 1, position 16               
        call    cmnd2LCD          ;
        movlw   '+'               ; One-second interval indicator (default)
        btfss   IntervalFlag,OneSec ; Is one-second interval set?
        movlw   '-'               ; No, it's 1/10 sec so get that indicator
        call    data2LCD          ; Display indicator
        return
;
; *****************************************************************************
; * bin2BCD;                                                                  *
; *                                                                           *
; * Purpose:  This subroutine converts a 32 bit binary number to a 10 digit   *
; *           BCD number.  The input value taken from freq(0 to 3) is         *
; *           preserved.  The output is in BCD(0 to 4), each byte holds =>    *
; *           (hi_digit,lo_digit), most significant digits are in BCD_4.      *
; *           This routine is a modified version of one described in          *
; *           MicroChip application note AN526.                               *
; *                                                                           *
; *   Input:  The value in freq_0 ... freq_3                                  *
; *                                                                           *
; *  Output:  The BCD number in BCD_0 ... BCD_4                               *
; *                                                                           *
; *****************************************************************************
;
bin2BCD
        movlw   0x20              ; Set loop counter
        movwf   BCD_count         ;   to 32
        clrf    BCD_0             ; Clear output
        clrf    BCD_1             ;   "     "
        clrf    BCD_2             ;   "     "
        clrf    BCD_3             ;   "     "
        clrf    BCD_4             ;   "     "
bin_loop
        bcf     STATUS,C          ; Clear carry bit in STATUS
;
; Rotate bits in freq bytes.  Move from LS byte (freq_0) to next byte (freq_1).
; Likewise, move from freq_1 to freq_2 and from freq_2 to freq_3.
;
        rlf     freq_0,f          ; Rotate left, 0 -> LS bit, MS bit -> Carry
        rlf     freq_1,f          ; Rotate left, Carry->LS bit, MS bit->Carry
        rlf     freq_2,f          ; Rotate left, Carry->LS bit, MS bit->Carry
        rlf     freq_3,f          ; Rotate left, Carry->LS bit, MS bit->Carry
        btfsc   STATUS,C          ; Is Carry clear? If so, skip next instruction
        bsf     freq_0,0          ; Carry is set so wrap and set bit 0 in freq_0
;
; Build BCD bytes. Move into LS bit of BCD bytes (LS of BCD_0) from MS bit of
; freq_3 via the Carry bit.  
;
        rlf     BCD_0,f           ; Rotate left, Carry->LS bit, MS bit->Carry
        rlf     BCD_1,f           ; Rotate left, Carry->LS bit, MS bit->Carry
        rlf     BCD_2,f           ; Rotate left, Carry->LS bit, MS bit->Carry
        rlf     BCD_3,f           ; Rotate left, Carry->LS bit, MS bit->Carry
        rlf     BCD_4,f           ; Rotate left, Carry->LS bit, MS bit->Carry
        decf    BCD_count,f       ; Decrement loop count
        btfss   STATUS,Z          ; Is loop count now zero?
        goto    adjust            ; No, go to adjust
        return                    ; Yes, EXIT 
; ============================================================================
adjust  ; Internal subroutine, called by bin2BCD main loop only
; 
; As BCD bytes are being built, make sure the nibbles do not grow larger than 9. 
; If a nibble gets larger than 9, increment to next higher nibble.  
; (If the LS nibble of a byte overflows, increment the MS nibble of that byte.)
; (If the MS nibble of a byte overflows, increment the LS nibble of next byte.)
;
        movlw   BCD_0             ; Get pointer to BCD_0
        movwf   FSR               ; Put pointer in FSR for indirect addressing
        call    adj_BCD           ; 
        incf    FSR,f             ; Move indirect addressing pointer to BCD_1
        call    adj_BCD           ; 
        incf    FSR,f             ; Move indirect addressing pointer to BCD_2
        call    adj_BCD           ; 
        incf    FSR,f             ; Move indirect addressing pointer to BCD_3
        call    adj_BCD           ; 
        incf    FSR,f             ; Move indirect addressing pointer to BCD_4
        call    adj_BCD           ; 
        goto    bin_loop          ; Back to main loop of bin2BCD
; ============================================================================
adj_BCD  ; Internal subroutine, called by adjust only
        movlw   3                 ; Add 3
        addwf   INDF,w            ;   to LS digit
        movwf   BCD_temp          ; Save in temp
        btfsc   BCD_temp,3        ; Is LS digit + 3 > 7  (Bit 3 set)
        movwf   INDF              ; Yes, save incremented value as LS digit
        movlw   0x30              ; Add 3
        addwf   INDF,w            ;   to MS digit
        movwf   BCD_temp          ; Save as temp
        btfsc   BCD_temp,7        ; Is MS digit + 3 > 7  (Bit 7 set)
        movwf   INDF              ; Yes, save incremented value as MS digit
        return                    ; Return to adjust subroutine
;
; *****************************************************************************
; * show_freq                                                                 *
; *                                                                           *
; * Purpose:  Display the frequency setting on the LCD.                       *
; *           Display freq in kHz - e.g  14,025.000 kHz                       *
; *                                                                           *
; *   Input:  The values in BCD_4 ... BCD_0                                   *
; *                                                                           *
; *  Output:  The number displayed on the 16x2 LCD                            *
; *    Line 1 addresses are  0x00 to 0x0F   (0x80 to 0x8F)                    *
; *    Line 2 addresses are  0x40 to 0x4F   (0xC0 to 0xCF)                    *
; *                                                                           *
; *****************************************************************************
;
show_freq
        movlw   0x80              ; Point the LCD to first LCD digit location  
        call    cmnd2LCD          ; Send starting digit location to LCD
        movlw   ' '               ; Send a space
        call    data2LCD          ;   to position 1 of LCD
;
; Running 4-bit mode, so need to send Most Significant Nibble first.
;
; Extract and send "XXXX" from byte containing "XXXXYYYY"
;  - Swap halves to get YYYYXXXX
;  - Mask with 0x0F to get 0000XXXX
;  - Add ASCII bias (0030XXXX)
;
        swapf   BCD_3,w           ; Swap 10MHz BCD digit into lower nibble of W
        andlw   0x0F              ; Mask for lower nibble only       (0000XXXX)
        addlw   0x30              ; Add offset for ASCII char set    (0030XXXX)
        call    data2LCD          ; Send byte in W to LCD
;
; Extract and send "YYYY" from byte containing "XXXXYYYY"
;   - Mask with 0x0F to get 0000YYYY
;   - Add offset for ASCII character set in LCD  (0030YYYY)
;
        movf    BCD_3,w           ; Put 1MHz BCD digit into lower nibble of W
        andlw   0x0F              ; Mask for lower nibble only       (0000YYYY)
        addlw   0x30              ; Add offset for ASCII char set    (0030YYYY)
        call    data2LCD          ; Send byte in W to LCD
; 
        movlw   ','               ; Get a comma
        call    data2LCD          ; Send byte in W to LCD
;
        swapf   BCD_2,w           ; Swap 100KHz BCD digit into lower nibble of W
        andlw   0x0F              ; Mask for lower nibble only       (0000XXXX)
        addlw   0x30              ; Add offset for ASCII char set    (0030XXXX)
        call    data2LCD          ; Send byte in W to LCD
;
        movf    BCD_2,w           ; Put 10KHz BCD digit into lower nibble of W
        andlw   0x0F              ; Mask for lower nibble only       (0000YYYY)
        addlw   0x30              ; Add offset for ASCII char set    (0030YYYY)
        call    data2LCD          ; Send byte in W to LCD
;
        swapf   BCD_1,w           ; Swap 1KHz BCD digit into lower nibble of W
        andlw   0x0F              ; Mask for lower nibble only       (0000XXXX)
        addlw   0x30              ; Add offset for ASCII char set    (0030XXXX)
        call    data2LCD          ; Send byte in W to LCD
;
        movlw   '.'               ; Get a period
        call    data2LCD          ; Send byte in W to LCD
        movlw   0x88              ; Point to LCD digit number nine
        call    cmnd2LCD          ; Send command byte in W to LCD
;
        movf    BCD_1,w           ; Put 100 Hz BCD digit into lower nibble of W
        andlw   0x0F              ; Mask for lower nibble only       (0000YYYY)
        addlw   0x30              ; Add offset for ASCII char set    (0030YYYY)
        call    data2LCD          ; Send data byte in W to LCD
;
        swapf   BCD_0,w           ; Swap 10 Hz BCD digit into lower nibble of W
        andlw   0x0F              ; Mask for lower nibble only       (0000XXXX)
        addlw   0x30              ; Add offset for ASCII char set    (0030XXXX)
        call    data2LCD          ; Send data byte in W to LCD
;
        movf    BCD_0,w           ; Put 1 Hz BCD digit into lower nibble of W
        andlw   0x0F              ; Mask for lower nibble only       (0000YYYY)
        addlw   0x30              ; Add offset for ASCII char set    (0030YYYY)
        call    data2LCD          ; Send byte in W to LCD
;
        movlw   ' '               ; Send a space
        call    data2LCD          ;   to position 12 of LCD
;
        movlw   'k'               ; Send a 'k'
        call    data2LCD          ;   to position 13 of LCD
;
        movlw   'H'               ; Send an "H"
        call    data2LCD          ;   to position 14 of LCD
;
        movlw   'z'               ; Send a 'z'
        call    data2LCD          ;   to position 15 of LCD
;
; Don't disturb Line 1 Position 16 (Interval)
;
#ifndef DEBUG
        movlw   0xC0              ; Point the LCD to first LCD digit location  
        call    cmnd2LCD          ; Send starting digit location to LCD
        movlw   16                ; 16 positions to clear
        movwf   TEMP1             ; Store in temp location
ClearLine2Loop
        movlw   ' '               ; Send a space
        call    data2LCD          ;   to LCD line 2
        decfsz  TEMP1,f           ; More characters to clear? 
        goto    ClearLine2Loop    ; Yes, loop back
#endif 
        return                    ; 
;
; *****************************************************************************
; * DisplayHEXData                                                            *
; *                                                                           *
; * Purpose:  Display data bytes on second line of LCD                        *
; *                                                                           *
; *   Input:  TEMP1..TEMP7                                                    *
; *                                                                           *
; *  Output:  7 bytes displayed displayed in HEX on second line of LCD        *
; *           (Only the least-significant half of digits 1 and 5-7 are        *
; *            displayed.)                                                    *
; *                                                                           *
; *****************************************************************************
;
DisplayHEXData   
        movlw   0xC0              ; Point LCD at second line, second digit
        movwf   LCD_char          ; 
        call    cmnd2LCD          ; 

; For the first byte, only display the LEAST SIGNIFICANT DIGIT!
; (We only have 16 characters available and need spaces between them.)


        movf    TEMP1,w           ; Get loop count to display
        andlw   0x0F              ; Mask for lower nibble only       (0000YYYY)
        addlw   0x30              ; Add offset for ASCII char set    (0030YYYY)
        movwf   Display_temp      ; Save it in temp
        movlw   0x3A              ; See if this digit is A - F
        subwf   Display_temp,w    ; Subtract 10 from digit, (result stays in W) 
        btfss   STATUS,C          ; Look at Carry bit (set if positive or zero)
        goto    DisplayOK2        ; C is set so OK (it's a number, not a letter)
        movf    Display_temp,w    ; Set up w again
        addlw   0x07              ; Skip over symbols between numbers and letters
        movwf   Display_temp      ; Save it
DisplayOK2
        movf    Display_temp,w    ; Set up W with character to be displayed 
        call    data2LCD          ; Send byte in W to LCD

        movlw   ' '               ; Send a space
        movwf   LCD_char          ; 
        call    data2LCD          ; 
;
        swapf   TEMP2,w           ; Swap digits to display first digit first
        andlw   0x0F              ; Mask for lower nibble only       (0000XXXX)
        addlw   0x30              ; Add offset for ASCII char set    (0030XXXX)
        movwf   Display_temp      ; Save it in temp
        movlw   0x3A              ; See if this digit is A - F
        subwf   Display_temp,w    ; Subtract 10 from digit, (result stays in W) 
        btfss   STATUS,C          ; Look at Carry bit (set if positive or zero)
        goto    DisplayOK3        ; C is set so OK (it's a number, not a letter)
        movf    Display_temp,w    ; Set up w again
        addlw   0x07              ; Skip over symbols between numbers and letters
        movwf   Display_temp      ; Save it
DisplayOK3
        movf    Display_temp,w    ; Set up W with character to be displayed 
        call    data2LCD          ; Send byte in W to LCD

        movf    TEMP2,w           ; Get loop count to display
        andlw   0x0F              ; Mask for lower nibble only       (0000YYYY)
        addlw   0x30              ; Add offset for ASCII char set    (0030YYYY)
        movwf   Display_temp      ; Save it in temp
        movlw   0x3A              ; See if this digit is A - F
        subwf   Display_temp,w    ; Subtract 10 from digit, (result stays in W) 
        btfss   STATUS,C          ; Look at Carry bit (set if positive or zero)
        goto    DisplayOK4        ; C is set so OK (it's a number, not a letter)
        movf    Display_temp,w    ; Set up w again
        addlw   0x07              ; Skip over symbols between numbers and letters
        movwf   Display_temp      ; Save it
DisplayOK4
        movf    Display_temp,w    ; Set up W with character to be displayed 
        call    data2LCD          ; Send byte in W to LCD

        movlw   ' '               ; Send a space
        movwf   LCD_char          ; 
        call    data2LCD          ; 

        swapf   TEMP3,w           ; Swap digits to display first digit first
        andlw   0x0F              ; Mask for lower nibble only       (0000XXXX)
        addlw   0x30              ; Add offset for ASCII char set    (0030XXXX)
        movwf   Display_temp      ; Save it in temp
        movlw   0x3A              ; See if this digit is A - F
        subwf   Display_temp,w    ; Subtract 10 from digit, (result stays in W) 
        btfss   STATUS,C          ; Look at Carry bit (set if positive or zero)
        goto    DisplayOK5        ; C is set so OK (it's a number, not a letter)
        movf    Display_temp,w    ; Set up w again
        addlw   0x07              ; Skip over symbols between numbers and letters
        movwf   Display_temp      ; Save it
DisplayOK5
        movf    Display_temp,w    ; Set up W with character to be displayed 
        call    data2LCD          ; Send byte in W to LCD

        movf    TEMP3,w           ; Get loop count to display
        andlw   0x0F              ; Mask for lower nibble only       (0000YYYY)
        addlw   0x30              ; Add offset for ASCII char set    (0030YYYY)
        movwf   Display_temp      ; Save it in temp
        movlw   0x3A              ; See if this digit is A - F
        subwf   Display_temp,w    ; Subtract 10 from digit, (result stays in W) 
        btfss   STATUS,C          ; Look at Carry bit (set if positive or zero)
        goto    DisplayOK6        ; C is set so OK (it's a number, not a letter)
        movf    Display_temp,w    ; Set up w again
        addlw   0x07              ; Skip over symbols between numbers and letters
        movwf   Display_temp      ; Save it
DisplayOK6
        movf    Display_temp,w    ; Set up W with character to be displayed 
        call    data2LCD          ; Send byte in W to LCD

        movlw   ' '               ; Send a space
        movwf   LCD_char          ; 
        call    data2LCD          ; 

        swapf   TEMP4,w           ; Swap digits to display first digit first
        andlw   0x0F              ; Mask for lower nibble only       (0000XXXX)
        addlw   0x30              ; Add offset for ASCII char set    (0030XXXX)
        movwf   Display_temp      ; Save it in temp
        movlw   0x3A              ; See if this digit is A - F
        subwf   Display_temp,w    ; Subtract 10 from digit, (result stays in W) 
        btfss   STATUS,C          ; Look at Carry bit (set if positive or zero)
        goto    DisplayOK7        ; C is set so OK (it's a number, not a letter)
        movf    Display_temp,w    ; Set up w again
        addlw   0x07              ; Skip over symbols between numbers and letters
        movwf   Display_temp      ; Save it
DisplayOK7
        movf    Display_temp,w    ; Set up W with character to be displayed 
        call    data2LCD          ; Send byte in W to LCD

        movf    TEMP4,w           ; Get loop count to display
        andlw   0x0F              ; Mask for lower nibble only       (0000YYYY)
        addlw   0x30              ; Add offset for ASCII char set    (0030YYYY)
        movwf   Display_temp      ; Save it in temp
        movlw   0x3A              ; See if this digit is A - F
        subwf   Display_temp,w    ; Subtract 10 from digit, (result stays in W) 
        btfss   STATUS,C          ; Look at Carry bit (set if positive or zero)
        goto    DisplayOK8        ; C is set so OK (it's a number, not a letter)
        movf    Display_temp,w    ; Set up w again
        addlw   0x07              ; Skip over symbols between numbers and letters
        movwf   Display_temp      ; Save it
DisplayOK8
        movf    Display_temp,w    ; Set up W with character to be displayed 
        call    data2LCD          ; Send byte in W to LCD

        movlw   ' '               ; Send a space
        movwf   LCD_char          ; 
        call    data2LCD          ; 

; For the next three parameters, only display the LEAST SIGNIFICANT DIGIT!

        movf    TEMP5,w           ; Get loop count to display
        andlw   0x0F              ; Mask for lower nibble only       (0000YYYY)
        addlw   0x30              ; Add offset for ASCII char set    (0030YYYY)
        movwf   Display_temp      ; Save it in temp
        movlw   0x3A              ; See if this digit is A - F
        subwf   Display_temp,w    ; Subtract 10 from digit, (result stays in W) 
        btfss   STATUS,C          ; Look at Carry bit (set if positive or zero)
        goto    DisplayOK9       ; C is set so OK (it's a number, not a letter)
        movf    Display_temp,w    ; Set up w again
        addlw   0x07              ; Skip over symbols between numbers and letters
        movwf   Display_temp      ; Save it
DisplayOK9
        movf    Display_temp,w    ; Set up W with character to be displayed 
        call    data2LCD          ; Send byte in W to LCD

        movlw   ' '               ; Send a space
        movwf   LCD_char          ; 
        call    data2LCD          ; 

        movf    TEMP6,w           ; Get loop count to display
        andlw   0x0F              ; Mask for lower nibble only       (0000YYYY)
        addlw   0x30              ; Add offset for ASCII char set    (0030YYYY)
        movwf   Display_temp      ; Save it in temp
        movlw   0x3A              ; See if this digit is A - F
        subwf   Display_temp,w    ; Subtract 10 from digit, (result stays in W) 
        btfss   STATUS,C          ; Look at Carry bit (set if positive or zero)
        goto    DisplayOK10       ; C is set so OK (it's a number, not a letter)
        movf    Display_temp,w    ; Set up w again
        addlw   0x07              ; Skip over symbols between numbers and letters
        movwf   Display_temp      ; Save it
DisplayOK10
        movf    Display_temp,w    ; Set up W with character to be displayed 
        call    data2LCD          ; Send byte in W to LCD

        movlw   ' '               ; Send a space
        movwf   LCD_char          ; 
        call    data2LCD          ; 

        movf    TEMP7,w           ; Get loop count to display
        andlw   0x0F              ; Mask for lower nibble only       (0000YYYY)
        addlw   0x30              ; Add offset for ASCII char set    (0030YYYY)
        movwf   Display_temp      ; Save it in temp
        movlw   0x3A              ; See if this digit is A - F
        subwf   Display_temp,w    ; Subtract 10 from digit, (result stays in W) 
        btfss   STATUS,C          ; Look at Carry bit (set if positive or zero)
        goto    DisplayOK11       ; C is set so OK (it's a number, not a letter)
        movf    Display_temp,w    ; Set up w again
        addlw   0x07              ; Skip over symbols between numbers and letters
        movwf   Display_temp      ; Save it
DisplayOK11
        movf    Display_temp,w    ; Set up W with character to be displayed 
        call    data2LCD          ; Send byte in W to LCD

        return
;
; *****************************************************************************
; * busy_check                                                                *
; *                                                                           *
; * Purpose:  Check if LCD is done with the last operation.                   *
; *           This subroutine polls the LCD busy flag to determine if         *
; *           previous operations are completed.                              *
; *                                                                           *
; *   Input:  None                                                            *
; *                                                                           *
; * On exit:  PORTB set as:  RB3          input                               *
; *                          all others   outputs                             *
; *****************************************************************************
; 
busy_check
        clrf    PORTB             ; Clear all outputs on PortB
        banksel TRISB             ; Switch to bank 1 for Tristate operation    
        movlw   b'00001000'       ; Set RB3 input, others outputs              
        movwf   TRISB             ;   via Tristate
        banksel PORTB             ; Switch back to bank 0
        bcf     PORTB,LCD_rs      ; Set up LCD for Read Busy Flag (RS = 0) 
        bsf     PORTB,LCD_rw      ; Set up LCD for Read (RW = 1)  
        movlw   0xFF              ; Set up constant 255
        movwf   timer1            ;   for timer loop counter
LCD_is_busy
        bsf     PORTB,LCD_e       ; Set E high
        movf    PORTB,w           ; Read PortB into W
        movwf   LCD_read          ; Save W for later testing
        bcf     PORTB,LCD_e       ; Drop E again
        nop                       ; Wait a
        nop                       ;   while
        bsf     PORTB,LCD_e       ; Pulse E high (dummy read of lower nibble),
        nop                       ;   wait,
        bcf     PORTB,LCD_e       ;   and drop E again
        decf    timer1,f          ; Decrement loop counter
        btfsc   STATUS,Z          ; Is loop counter down to zero?
        goto    not_busy          ; If yes, return regardless
        btfsc   LCD_read,LCD_busy ; Busy Flag (RB3) in save byte clear?        
        goto    LCD_is_busy       ; If not, it is busy so jump back
not_busy
        return                    ; 
;
; *****************************************************************************
; * cmnd2LCD                                                                  *
; *                                                                           *
; * Purpose:  Send Command or Data byte to the LCD                            *
; *           Entry point cmnd2LCD:  Send a Command to the LCD                *
; *           Entry Point data2LCD:  Send a Data byte to the LCD              *
; *                                                                           *
; *   Input:  W has the command or data byte to be sent to the LCD.           *
; *                                                                           *
; *  Output:  None                                                            *
; *****************************************************************************
;
cmnd2LCD   ; ****** Entry point ******
        movwf   LCD_char          ; Save byte to write to LCD
        clrf    rs_value          ; Remember to clear RS  (clear rs_value)   
        bcf     ,LCD_rs      ; Set RS for Command to LCD
        goto    write2LCD         ; Go to common code
data2LCD   ; ****** Entry point ********
        movwf   LCD_char          ; Save byte to write to LCD
        bsf     rs_value,0        ; Remember to set RS (set bit 0 of rs_value)
        bsf     PORTB,LCD_rs      ; Set RS for Data to LCD
write2LCD
        call    busy_check        ; Check to see if LCD is ready for new data
        clrf    PORTB             ; Clear all of Port B (inputs and outputs)
        banksel TRISB             ; Switch to bank 1 for Tristate operation
        movlw   0x00              ; Set up to enable PORTB data pins
        movwf   TRISB             ; All pins (RB7..RB0) are back to outputs
        banksel PORTB             ; Switch back to bank 0
        bcf     PORTB,LCD_rw      ; Set LCD back to Write mode  (RW = 0)
        bcf     PORTB,LCD_rs      ; Guess RS should be clear              
        btfsc   rs_value,0        ; Should RS be clear?  (is bit 0 == 0?) 
        bsf     PORTB,LCD_rs      ; No, set RS                            
;
; Transfer Most Significant nibble  (XXXX portion of XXXXYYYY)
;

        movlw   0xF0              ; Set up mask                                        
        andwf   PORTB,f           ; Keep RB7..RB4 but clear old RB3..RB0          
        swapf   LCD_char,w        ; Put byte into W (reverse nibbles)          
        andlw   0x0F              ; Mask to give 0000XXXX in W                 
        iorwf   PORTB,f           ; To RB3..RB0 with RB7..RB4 unchanged        
        bsf     PORTB,LCD_e       ; Pulse the E line high,
        nop                       ;   wait, 
        bcf     PORTB,LCD_e       ;   and drop it again
;
; Transfer Least Significant nibble  (YYYY portion of XXXXYYYY)
;
        movlw   0xF0              ; Set up mask                                        
        andwf   PORTB,f           ; Clear old RB3..RB0                         
        movf    LCD_char,w        ; Move LS nibble of into W                   
        andlw   0x0F              ; Mask to give 0000YYYY in W                 
        iorwf   PORTB,f           ; To RB3..RB0 with RB7..RB4 unchanged        
        bsf     PORTB,LCD_e       ; Pulse the E line high,
        nop                       ;   wait, 
        bcf     PORTB,LCD_e       ;   and drop it again
        return


; *****************************************************************************
; * MultBy10                                                                  *
; *                                                                           *
; * Purpose:  Multiply frequency by 10 (decimal)                              *
; *                                                                           *
; *   Entry:  24-bit value in freq_2 (MSB) to freq_0 (LSB)                    *
; *           (Assumes freq_3 is zero)                                        *
; *                                                                           *
; *    Exit:  freq_2 to freq_0 updated                                        *
; *           (freq_3 is zero)                                                *
; *****************************************************************************

MultBy10
        movlw   0x0A              ; Multiplication factor
        movwf   multfac           ; Temp
        clrf    prod0             ; Clear the product
        clrf    prod1
        clrf    prod2
        clrf    prod3
        clrf    freq_3            ; Clear the top byte of the multiplicand
multloop:
        movf    multfac,W         ; Check to see if multfac is empty
        btfsc   STATUS,Z          ; Done if zero
        goto    mult_exit         ;
        bcf     STATUS,C          ; clear carry
        rrf     multfac,F         ; Get the next bit
        btfss   STATUS,C          ; Add if carry is set
        goto    multshift         ; shift only if 0
        movf    freq_0,W          ; Add the 32 bits of multiplicand to product
        addwf   prod0,F           ; each addwf sets the carry
        btfsc   STATUS,C          ; Don't increment if no carry
        call    carryprop1        ; Propigate carry to next byte(s)
        movf    freq_1,W          ;
        addwf   prod1,F           ;
        btfsc   STATUS,C          ; Don't increment if no carry
        call    carryprop2        ; Propigate carry to next byte(s)
        movf    freq_2,W          ;
        addwf   prod2,F           ;
        btfsc   STATUS,C          ; Don't increment if no carry
        incf    prod3,F           ; Add one to next byte if carry occured
        movf    freq_3,W          ;
        addwf   prod3,F           ;
multshift:
        bcf     STATUS,C          ; clear carry
        rlf     freq_0,F          ; Shift the 24 bits one bit to the left
        rlf     freq_1,F
        rlf     freq_2,F
        rlf     freq_3,F    
        goto    multloop
mult_exit
        movf    prod0,W           ; Get Least Significant Byte of product
        movwf   freq_0            ; Store in LSB of freq
        movf    prod1,W           ; Get next byte of product
        movwf   freq_1            ; Store 
        movf    prod2,W           ; Get next byte of product
        movwf   freq_2            ; Store
        clrf    freq_3            ; Clear MSB of freq
        return

; **** Subroutines used by MultBy10 ****
carryprop1:
        incfsz  prod1,F
        return
carryprop2:
        incfsz  prod2,F
        return
carryprop3:
        incf    prod3,F
        return
;
; *****************************************************************************
; * poll_encoder                                                              *
; *                                                                           *
; * Purpose:  This routine reads the encoder bits until a change is detected. *
; *           Then it determines the direction the knob was moved.            *
; *           (NO QUANTITY IN THIS VERSION!)                                  *
; *                                                                           *
; *   Input:  Knob input read from port A                                     *
; *           ren_old -> the last encoder bits read                           *
; *           last_dir -> the last direction moved                            *
; *                                                                           *
; *  Output:  ren_new -> the current encoder bits                             *
; *           last_dir -> the last direction (0 = down, 2 = up)               *
; *                                                                           *
; *****************************************************************************
;
poll_encoder
        movf    PORTA,w           ; Get the current encoder value
        movwf   ren_read          ; Save it
        btfsc   ren_read,PB       ; Is the pushbutton still pressed?
        goto    poll_exit         ; No, exit now
        movlw   0x03              ; Get encoder mask (to isolate RA0 and RA1)  
        andwf   ren_read,w        ; Isolated encoder bits into W               
        movwf   ren_new           ; Save new value
        xorwf   ren_old,w         ; Has it changed?
        btfsc   STATUS,Z          ; Check zero-flag (zero if no change)        
        goto    poll_encoder      ; No change, keep looking until it changes   
;                                 ; Zero-flag is not set, so continue on        
; It changed. Now determine which direction the encoder turned.                
;============================================================================= 
;   Encoder bits are on RA0 and RA1 - the two low order bits of ren_new          
;   A and B are "gray code" - 90 degrees out of phase (quadrature)             
;         ___     ___                                                          
;        |   |   |   |                                                         
; A  ____|   |___|   |___                                                      
;           ___     ___                                                        
;          |   |   |   |                                                       
; B     ___|   |___|   |___                                                    
;       ^ ^ ^ ^ ^ ^ ^ ^                                                        
;       a b c d a b c d                                                        
;                                                                              
;              A B                                                             
; At point a:  0 0                                                             
; At point b:  1 0                                                             
; At point c:  1 1                                                             
; At point d:  0 1                                                             
;                                                                              
; Going UP, the sequence is a,b,c,d,a,b,c,d, etc. so the sequence is:          
;     00, 10, 11, 01, 00, 10, 11, 01, etc.                                     
;                                                                              
; Going DOWN, the sequence is d,c,b,a,d,c,b,a, etc. so the sequence is:        
;     01, 11, 10, 00, 01, 11, 10, 00, etc.                                     
;                                                                              
; To determine if the sequence is UP or DOWN:                                  
;   1) Take the "Right-Bit" of any pair.                                       
;   2) XOR it with the "Left-Bit" of the next pair in the sequence.            
;   3) If the result is 1 it is UP                                             
;      If the result is 0 it is DOWN                                           
;                                                                              
; The direction flag is 0 (DOWN) or 2 (UP) because of bit positioning          
;============================================================================= 
        bcf     STATUS,C          ; Clear the carry bit to prepare for rotate  
        rlf     ren_old,f         ; Rotate old bits left to align "Right-Bit"  
        movf    ren_new,w         ; Set up new bits in W                       
        xorwf   ren_old,f         ; XOR old (left shifted) with new bits       
        movf    ren_old,w         ; Put XOR results into W also                
        andlw   0x02              ; Mask to look at only "Left-Bit" of pair    
        movwf   next_dir          ; Save result (in W) as direction (bit=UP)   
        xorwf   last_dir,w        ; See if direction is same as before         
;
;       Prevent encoder slip from giving a false change in direction.
;
        btfsc   STATUS,Z          ; Zero flag set? (i.e, is direction same?)   
        goto    pe_continue       ; Yes, same direction so no slip; keep going 
        movf    next_dir,w        ; No Zero-flag, so direction changed         
        movwf   last_dir          ; Update the direction indicator             
        movf    ren_new,w         ; Save the current encoder bits (now in W)   
        movwf   ren_old           ;   for next time                            
        goto    poll_encoder      ; Try again
pe_continue
        clrf    last_dir          ; Clear last_dir (default is DN)             
        btfss   ren_old,1         ; Are we going UP?                           
        goto    exit3             ; No, exit3                                  
up2 
        movlw   0x02              ; Get UP value                               
        movwf   last_dir          ;   and set in last_dir                      
exit3 
        movf    ren_new,w         ; Get the current encoder bits
        movwf   ren_old           ; Save them in ren_old for the next time
poll_exit 
        return                    ; Return to the caller
;
; *****************************************************************************
; * calibrate                                                                 * 
; *                                                                           *
; * Purpose:  This routine is entered at start up if the push button (PB3) is *
; *           pressed. "000,010.000 CAL" (a relative number - for reference   *
; *           only) is displayed on the LCD.  The EEPROM values for the timing*
; *           loop will be adjusted by turning the encoder. By performing this*
; *           calibration with a know frequency source, it allows the loop    *
; *           counter values to be adjusted for the given PIC crystal so that * 
; *           the timing interval takes exactly 1/10 second.  Once the button *
; *           is released the new loop count constants are stored in the      *
; *           EEPROM and normal operation begins.                             *
; *                                                                           *
; *   Input:  The original osc constant in EEPROM                             *
; *                                                                           *
; *  Output:  The corrected osc constant in EEPROM                            *
; *                                                                           *
; *****************************************************************************
;
calibrate
;
        movlw   0x8C              ; Point LCD at 13th digit of first line
        movwf   LCD_char          ; 
        call    cmnd2LCD          ; 
        movlw   'C'               ; Send a C
        movwf   LCD_char          ; 
        call    data2LCD          ; 
        movlw   'A'               ; Send an A
        movwf   LCD_char          ; 
        call    data2LCD          ; 
        movlw   'L'               ; Send an L
        movwf   LCD_char          ; 
        call    data2LCD          ; 
;
; Don't disturb Line 1 position 16 (Interval)
;
; Read the starting parameters from EEPROM.
;
        banksel TRISA             ; Switch to bank 1
        clrf    EEADR             ; Set EEPROM read location to ZERO
        call    read_EEPROM       ; Read EEPROM (all in bank 1)
        movf    EEDATA,w          ; Get the first osc byte
        banksel PORTA             ; Back to bank 0 for store
        movwf   IntervalFlag      ; Save flag, bit7=0 for 1/10 sec,  1 for 1-sec counting
;
        movlw   ONESECEEPROMSTART ; Read 1-second parameters from EEPROM (default)
        btfss   IntervalFlag,OneSec ; Is one-second interval enabled?
        movlw   TENTHSECEEPROMSTART ; Set up to read 1/10 sec parameters from EEPROM
        banksel TRISA             ; Switch to bank 1
        movwf   EEADR             ; Set EEPROM read location
        call    read_EEPROM       ; Read EEPROM (all in bank 1)
        movf    EEDATA,w          ; Get the first osc byte
        banksel PORTA             ; Back to bank 0 for store
        movwf   T1                ; Save T1

        banksel TRISA             ; Switch to bank 1
        call    read_EEPROM       ; Read EEPROM (all in bank 1)
        movf    EEDATA,w          ; Get the first osc byte
        banksel PORTA             ; Back to bank 0 for store
        movwf   T2                ; Save T2

        banksel TRISA             ; Switch to bank 1
        call    read_EEPROM       ; Read EEPROM (all in bank 1)
        movf    EEDATA,w          ; Get the first osc byte
        banksel PORTA             ; Back to bank 0 for store
        movwf   T3                ; Save T3

        banksel TRISA             ; Switch to bank 1
        call    read_EEPROM       ; Read EEPROM (all in bank 1)
        movf    EEDATA,w          ; Get the first osc byte
        banksel PORTA             ; Back to bank 0 for store
        movwf   T4                ; Save T4

        banksel TRISA             ; Switch to bank 1
        call    read_EEPROM       ; Read EEPROM (all in bank 1)
        movf    EEDATA,w          ; Get the first osc byte
        banksel PORTA             ; Back to bank 0 for store
        movwf   ZZ                ; Save ZZ  

        banksel TRISA             ; Switch to bank 1
        call    read_EEPROM       ; Read EEPROM (all in bank 1)
        movf    EEDATA,w          ; Get the first osc byte
        banksel PORTA             ; Back to bank 0 for store
        movwf   FT                ; Save FT 

        banksel TRISA             ; Switch to bank 1
        call    read_EEPROM       ; Read EEPROM (all in bank 1)
        movf    EEDATA,w          ; Get the first osc byte
        banksel PORTA             ; Back to bank 0 for store
        movwf   FFT               ; Save FFT 
;
; Display loop values on LCD second line and adjust via encoder
; Loop until pushbutton is released.
; Then write final values back to EEPROM and exit.
;
cal_loop
        movf    T1,w              ; Set up 
        movwf   TEMP1             ;   registers
        movf    T2,w              ;     for
        movwf   TEMP2             ;       display
        movf    T3,w              ;
        movwf   TEMP3             ; 
        movf    T4,w              ;
        movwf   TEMP4             ; 
        movf    ZZ,w              ;
        movwf   TEMP5             ; 
        movf    FT,w              ;
        movwf   TEMP6             ; 
        movf    FFT,w             ;
        movwf   TEMP7             ; 
        call    DisplayHEXData    ; Display loop values on second line of LCD
        call    poll_encoder      ; Wait until the encoder has moved or PB released
        btfsc   ren_read,PB       ; Is the pushbutton still pressed?
        goto    IncDecDone        ; No, done with changes   
update_osc
        btfsc   last_dir,1        ; Are we moving down?
        goto    IncLoopCounts     ; No, go increase the loop counts
                                  ; Yes, decrease loop counts
; Decrement Loop Counts
        btfss   IntervalFlag,OneSec; Are we calibrating 1-second variables?
        goto    DecFT             ; No, go start with FT instead
;
        movf    FFT,F             ; Look at FFT (to set up status register)
        btfsc   STATUS,Z          ; Are we already at zero?  (FFT range is 0-9)
        goto    DecFFTisZero      ; Yes, so go handle zero
        decf    FFT,F             ; No, decrement FFT and
        goto    IncDecDisplay     ;   go display  
DecFFTisZero        
        movlw   9                 ; Yes, FFT was already down to zero so 
        movwf   FFT               ;   restore FFT=9 and decrement higher parameter (FT)
DecFT
        movf    FT,F              ; Look at FT (to set up status register)
        btfsc   STATUS,Z          ; Are we already at zero?  (FT range is 0-2)
        goto    DecFTisZero       ; Yes, so go handle zero
        decf    FT,F              ; No, decrement FT and
        goto    IncDecDisplay     ;   go display  
DecFTisZero        
        movlw   2                 ; Yes, FT was already down to zero so 
        movwf   FT                ;   restore FT=2 and decrement higher parameter (ZZ)
        decfsz  ZZ,f              ; Decrement ZZ  (ZZ range is 1 - 3)
        goto    IncDecDisplay     ; Not down to zero after decrement so OK
        movlw   3                 ; ZZ down to zero after decrement so out of range 
        movwf   ZZ                ; Restore ZZ=3 and decrement higher parameter (T4)
        decfsz  T4,f              ; Decrement T4
        goto    IncDecDisplay     ; Not down to zero after decrement so OK
        movlw   1                 ; T4 down to zero. We can't decrease any more so
        movwf   T4                ;   restore T4=1
        goto    IncDecDisplay     ;   and go display and look again
;
IncLoopCounts
        btfss   IntervalFlag,OneSec; Are we calibrating 1-second variables?
        goto    IncFT             ; No, go start with FT instead
;
        incf    FFT,f             ; Increment FFT  (FFT range is 0-9)
        movlw   10                ; Set up to see if FFT = 10 now (too big)
        subwf   FFT,w             ; Subtract 10 from FFT, (result stays in W) 
        btfss   STATUS,C          ; Look at Carry bit (set if positive or zero)
        goto    IncDecDisplay     ; Carry not set so FFT hasn't reached 10 yet so OK
        clrf    FFT               ; Set FFT back to zero
IncFT
        incf    FT,f              ; Increment FT  (FT range is 0-2)
        movlw   3                 ; Set up to see if FT = 3 now (too big)
        subwf   FT,w              ; Subtract 3 from FT, (result stays in W) 
        btfss   STATUS,C          ; Look at Carry bit (set if positive or zero)
        goto    IncDecDisplay     ; Carry not set so FT hasn't reached 3 yet so OK
        clrf    FT                ; FT = 3 so set it back to 0 and increment higher
                                  ;   parameter (ZZ) 
;
        incf    ZZ,f              ; Increment ZZ (ZZ range is 1-3)
        movlw   4                 ; See if ZZ = 4 now (too big)
        subwf   ZZ,w               ; Subtract 4 from ZZ, (result stays in W) 
        btfss   STATUS,C         ; Look at Carry bit (set if positive or zero)
        goto    IncDecDisplay     ; Carry not set so ZZ hasn't reached 3 yet so OK
        movlw   1                 ; ZZ = 4 so set it back to 1 and increment higher
        movwf   ZZ                ;   parameter (T4)
;
        incf    T4,f              ; Increment T4
        btfss   STATUS,Z          ; Are we at zero now (wrap)?
        goto    IncDecDisplay     ; No, OK. Display and go back to look again
        movlw   0xFF              ; Set T4 to maximum. We can't increase any more
        movwf   T4                ;   
IncDecDisplay
        goto    cal_loop          ; Yes, go back to top, display, and look again
IncDecDone
;
; Write final value to EPROM
;
        banksel TRISA             ; Switch to bank 1
        clrf    EEADR             ; Set EEPROM read location to ZERO
        banksel PORTA             ; Back to bank 0
        movf    IntervalFlag,w    ; Get IntervalFlag byte to record (bank 0)
        banksel TRISA             ; Switch to bank 1
        movwf   EEDATA            ; Put byte in EEPROM write location 
        call    write_EEPROM      ; (all in bank 1)
        banksel PORTA             ; Back to bank 0
;
        movlw   ONESECEEPROMSTART ; Set up to write 1-second parameters in EEPROM (default)
        btfss   IntervalFlag,OneSec ; Is one-second interval enabled?
        movlw   TENTHSECEEPROMSTART ; Set up to write 1/10 sec parameters from EEPROM
        banksel TRISA             ; Switch to bank 1
        movwf   EEADR             ; Set EEPROM read location
        banksel PORTA             ; Back to bank 0
;
        movf    T1,w              ; Get the first byte to record (bank 0)
        banksel TRISA             ; Switch to bank 1
        movwf   EEDATA            ; Put byte in EEPROM write location 
        call    write_EEPROM      ; (all in bank 1)
        banksel PORTA             ; Back to bank 0
;
        movf    T2,w              ; Get the second byte to record (bank 0)
        banksel TRISA             ; Switch to bank 1
        movwf   EEDATA            ; Put byte in EEPROM write location 
        call    write_EEPROM      ; (all in bank 1)
        banksel PORTA             ; Back to bank 0
;
        movf    T3,w              ; Get the third byte to record (bank 0)
        banksel TRISA             ; Switch to bank 1
        movwf   EEDATA            ; Put byte in EEPROM write location 
        call    write_EEPROM      ; (all in bank 1)
        banksel PORTA             ; Back to bank 0
;
        movf    T4,w              ; Get the fourth byte to record (bank 0)
        banksel TRISA             ; Switch to bank 1
        movwf   EEDATA            ; Put byte in EEPROM write location 
        call    write_EEPROM      ; (all in bank 1)
        banksel PORTA             ; Back to bank 0
;
        movf    ZZ,w               ; Get the fifth byte to record (bank 0) 
        banksel TRISA             ; Switch to bank 1
        movwf   EEDATA            ; Put byte in EEPROM write location 
        call    write_EEPROM      ; (all in bank 1)
        banksel PORTA             ; Back to bank 0
;
        movf    FT,w              ; Get the sixth byte to record (bank 0) 
        banksel TRISA             ; Switch to bank 1
        movwf   EEDATA            ; Put byte in EEPROM write location 
        call    write_EEPROM      ; (all in bank 1)
        banksel PORTA             ; Back to bank 0
;
        movf    FFT,w             ; Get the seventh byte to record (bank 0) 
        banksel TRISA             ; Switch to bank 1
        movwf   EEDATA            ; Put byte in EEPROM write location 
        call    write_EEPROM      ; (all in bank 1)
        banksel PORTA             ; Back to bank 0
;
; Update parameter save registers
;
        btfsc   IntervalFlag,OneSec ; Is one-second interval enabled?
        goto    CalUpdateOneSec   ; Yes, go update one-second registers
        movf    T1,w
        movwf   TT1               ; Update T1-save for TenthSecond
        movf    T2,w
        movwf   TT2               ; Update T2-save for TenthSecond
        movf    T3,w
        movwf   TT3               ; Update T3-save for TenthSecond
        movf    T4,w
        movwf   TT4               ; Update T4-save for TenthSecond
        movf    ZZ,w
        movwf   TZZ               ; Update ZZ-save for TenthSecond
        movf    FT,w
        movwf   TFT               ; Update FT-save for TenthSecond
        movf    FFT,w
        movwf   TFFT              ; Update FFT-save for TenthSecond
        goto    CalUpdateDone     ; Go to exit
;
CalUpdateOneSec
        movf    T1,w
        movwf   OT1               ; Update T1-save for OneSecond
        movf    T2,w
        movwf   OT2               ; Update T2-save for OneSecond
        movf    T3,w
        movwf   OT3               ; Update T3-save for OneSecond
        movf    T4,w
        movwf   OT4               ; Update T4-save for OneSecond
        movf    ZZ,w
        movwf   OZZ               ; Update ZZ-save for OneSecond
        movf    FT,w
        movwf   OFT               ; Update FT-save for OneSecond
        movf    FFT,w
        movwf   OFFT              ; Update FFT-save for OneSecond
;
CalUpdateDone
        return                    ; Return to the caller

; *****************************************************************************
; * write_EEPROM                                                              *
; *                                                                           *
; * Purpose:  Write the byte of data at EEDATA to the EEPROM at address       *
; *           EEADR.                                                          *
; *                                                                           *
; *   Input:  The values at EEDATA and EEADR.                                 *
; *                                                                           *
; *  Output:  The EEPROM value is updated.                                    *
; *                                                                           *
; *  NOTE:  ALL IN BANK 1                                                     *
; *****************************************************************************
;
write_EEPROM
        bsf     EECON1,WREN       ; Set the EEPROM write enable bit
        ; Start required sequence
        bcf     INTCON,GIE        ; Disable interrupts
        movlw   0x55              ; Write 0x55 and 0xAA to EECON2
        movwf   EECON2            ;   control register, as required
        movlw   0xAA              ;   
        movwf   EECON2            ;
        bsf     EECON1,WR         ; Set WR bit to begin write
        ; End required sequence
bit_check
        btfsc   EECON1,WR         ; Has the write completed?
        goto    bit_check         ; No, keep checking
        bsf     EECON1,GIE        ; Enable  interrupts
        bcf     EECON1,WREN       ; Clear the EEPROM write enable bit
        incf    EEADR,f           ; Increment the EE write address
        return                    ; Return to the caller
;
; *****************************************************************************
; * read_EEPROM                                                               *
; *                                                                           *
; * Purpose:  Read a byte of EEPROM data at address EEADR into EEDATA.        *
; *                                                                           *
; *   Input:  The address EEADR.                                              *
; *                                                                           *
; *  Output:  The value in EEDATA.                                            *
; *                                                                           *
; *  NOTE:  All in BANK 1                                                     *
; *****************************************************************************
;
read_EEPROM
;        bsf     EEDATA,RD         ; Request the read  (0ld way)
        bsf     EECON1,RD         ; Request the read
        movf    EEDATA,W          ; Get the data
        incf    EEADR,f           ; Increment the read address
        return                    ; Return to the caller
;
; *****************************************************************************
; *  Fine Tune Instruction Wait routines                                      *
; *****************************************************************************
Wait0Inst
        return                    ; 2
Wait1Inst
        nop                       ; 1
        return                    ; 3       
Wait2Inst
        nop                       ; 2
        nop                       ; 3
        return                    ; 4
Wait3Inst
        nop                       ; 1
        nop                       ; 2
        nop                       ; 3
        return                    ; 5
Wait4Inst
        call     Wait0Inst        ; 4
        return                    ; 6
Wait5Inst
        call     Wait1Inst        ; 5
        return                    ; 7
Wait6Inst
        call     Wait2Inst        ; 6
        return                    ; 8
Wait7Inst
        call     Wait3Inst        ; 7
        return                    ; 9
Wait8Inst
        call     Wait4Inst        ; 8
        return                    ; 10
Wait9Inst
        call     Wait5Inst        ; 9
        return                    ; 11
Wait10Inst
        call     CheckTimer       ; 10 
        return                    ; 12
;
; *****************************************************************************
; *                                                                           *
; * Purpose:  Wait for a specified number of milliseconds.                    *
; *           (This assumes 4 MHz clock)                                      *
; *                                                                           *
; *           Entry point wait_a_sec:  Wait for 1 second                      *
; *           Entry point wait_256ms:  Wait for 256 msec                      *
; *           Entry point wait_128ms:  Wait for 128 msec                      *
; *           Entry point wait_64ms :  Wait for 64 msec                       *
; *           Entry point wait_32ms :  Wait for 32 msec                       *
; *           Entry point wait_16ms :  Wait for 16 msec                       *
; *           Entry point wait_8ms  :  Wait for 8 msec                        *
; *                                                                           *
; *   Input:  None                                                            *
; *                                                                           *
; *  Output:  None                                                            *
; *                                                                           *
; *****************************************************************************
;
wait_a_sec  ; ****** Entry point ******    
        call    wait_256ms        ;       
        call    wait_256ms        ;       
        call    wait_256ms        ;       
        call    wait_256ms        ;       
        return
wait_256ms  ; ****** Entry point ******    
        call    wait_128ms        ;
        call    wait_128ms        ;
        return
wait_128ms  ; ****** Entry point ******    
        movlw   0xFF              ; Set up outer loop 
        movwf   timer1            ;   counter to 255
        goto    outer_loop        ; Go to wait loops
wait_64ms  ; ****** Entry point ******     
        movlw   0x80              ; Set up outer loop
        movwf   timer1            ;   counter to 128
        goto    outer_loop        ; Go to wait loops
wait_32ms   ; ****** Entry point ******    
        movlw   0x40              ; Set up outer loop
        movwf   timer1            ;   counter to 64
        goto    outer_loop        ; Go to wait loops
wait_16ms   ; ****** Entry point ******    
        movlw   0x20              ; Set up outer loop
        movwf   timer1            ;   counter to 32  
        goto    outer_loop        ; Go to wait loops
wait_8ms   ; ****** Entry point ******     
        movlw   0x10              ; Set up outer loop
        movwf   timer1            ;   counter to 16
        goto    outer_loop        ; Go to wait loops
wait_.5ms   ; ****** Entry point ******     
        movlw   0x1               ; Set up outer loop
        movwf   timer1            ;   counter to 1
                                  ; Fall through into wait loops
;
; Wait loops used by other wait routines
;  - 1 microsecond per instruction (with a 4 MHz microprocessor crystal)
;  - 510 instructions per inner loop
;  - (Timer1 * 514) instructions (.514 msec) per outer loop
;  - Round off to .5 ms per outer loop
;
outer_loop                        
        movlw   0xFF              ; Set up inner loop counter
        movwf   timer2            ;   to 255
inner_loop
        decfsz  timer2,f          ; Decrement inner loop counter
        goto    inner_loop        ; If inner loop counter not down to zero, 
                                  ;   then go back to inner loop again
        decfsz  timer1,f          ; Yes, Decrement outer loop counter
        goto    outer_loop        ; If outer loop counter not down to zero,
                                  ;   then go back to outer loop again
        return                    ; Yes, return to caller
;       
; *****************************************************************************
;
        END

