; ------------------------------------------------------------------------
; FILE     : LCD4DIEP.ASM - variation with 4 bite LCD communication      *
; (4-bites, high nibble, RB4..RB7 data, RA0..RA2 control)                *
; CONTENTS : Simple low-cost 7-digit digital scale using a PIC16F84      *
; COPYRIGHT: Peter Halicky  OM3CPH, OH6CJ                                *
; AUTHOR   : Peter Halicky  OM3CPH & Peter Halicky Jr., OM2PH ex OM2APH  *
; PCB      : Tibor Madarasz OM2ATM, modified Osmo OH6CJ August 2001      *
;--------------------------------------------------------------------------
; Osmo OH6CJ:  08.03.2001 Modifications for 4000 kHz crystal:
; Added: RF offset1 when RB1 = 0 and RF offset2 when RB1 = 1.
;        EEPROM routine to select either 1x16 or 2x20 LCD control.
;        EEPROM routine to select either 6 or 7 digits to be displayed.
;        When RB2=1, direct frequency is displayed (no add or sub RF).
;        RB3 to control decimal point location with additional 10 divider.
;        Added calibration parameters for time window (EEPROM 07h,08h).
; Osmo OH6CJ:  17.03.2001
;        Modified EEPROM TEXTS "ADDR" and "MODE" instead of A: 
;        Added sw version number 1.0 for EEPROM TEXT display 
; Osmo OH6CJ:  31.03.2001
;        Added Busy checkings after the clear display and cursor
;        positioning instructions because problems observed with certain
;        1x16 LCDs types (slow display updating, flashing or freezing).
;        Changed E Data R/W start pulse bit set and clear procedure. 
;        First digit on left (10 MHz) is not displayed if zero. 
; Osmo OH6CJ: 27.04.2001
;	 Added default values writing to EEPROM during first power-on.
;        Added EEPROM address 06h to select direct freq as a default.
;        Changed sw version to 1.2 in EEPROM MODE window.
; Osmo OH6CJ: 20.09.2001
;	 Added frequency lock function to control external HW-integrator
;        to keep VFO frequency stable. There is a subtruction between
;        the measured frequency and freq reference. Frequency actual is
;        set to reference after the out of lock situation when the new freq
;        has been stabilised (nobody touch for VFO knob). As a result
;        RB0 is controlled as digital output to indicate the need of
;        frequency correction upward and RB3 downward.
;        Function is enabled by EEPROM 0Bh.
;        Changed sw version to 1.3 in EEPROM MODE window. "EE-INIT" text
;        removed to save memory.
;        Modified Sub24bit and Add24bit routines as subcalls to save memory
;        New parameters are 0B..0Eh.
;        Unnecessary Clear STATUS,C command rows deleted.
; Osmo OH6CJ: 17.10.2001
;        Changed 0Dh and 0Eh default values and freq. check windows. 
; Osmo OH6CJ: 09.11.2001
;        Returned back swap function with sub display offset function, if 
;        subtracted value is bigger than subtracter.  
; Osmo OH6CJ: 03.12.2001
;        Added Freq Lock pass mode by RB2.Frequency samples must be within the
;        20 Hz window before they are accepted to freq reference.
;--------------------------------------------------------------------------
;       The EEPROM MODE is activated when RB0 = 1 and power is 
;       connected.
;--------------------------------------------------------------------------
; The used EEPROM addresses are:
; 00 = MFt1_HigB  = High Byte of RF offset1 (def. 0D) 9001.50 kHz = 0DBC36
; 01 = MFt1_MidB  = Mid Byte of RF offset1  (def. BC)
; 02 = MFt1_LowB  = Low Byte of RF offset1  (def. 36)
; 03 = MFt2_HigB  = High Byte of RF offset2 (def. 0D) 8998.50 khz = 0DBB0A
; 04 = MFt2_MidB  = Mid Byte of RF offset2  (def. BB)
; 05 = MFt2_LowB  = Low Byte of RF offset2  (def. 0A)
; 06 = Dir_freq   = Direct frequency without sub or add functions = 0 def.
; 07 = EE_Fine1  = Counter value for calibration 1 == 3*4/fx= 3us (def. 15)
; 08 = EE_Fine2  = Counter value for calibration 1 == 4*4/fx= 4us (def. 01)
; 09 = 1x16_Disp = LCD display type: 0 = 1x16 LCD, 1 = 2x20 LCD (def. 01)
; 0A = Digits    = Number of displayed digits: 0 = 7 digits, 1 = 6 (def. 1)
; 0B = Freq_lock = RB0 is output indicating need to increase and RB3 to
;                  decrease frequency compared to frequency reference.
;                  If freq is out of the window +/- 110 Hz 20 samples, both
;                  outputs are cleared. When the frequency is within
;                  +/- 20 Hz from reference, only short pulse is
;                  controlled. If over then long pulse is controlled.  
;                  00h = Function enabled with LCD indication "L" = locked
;                  01h...FFh = Function disabled 
; 0C = Ten_div   = Ten_divider function is activated in Freq_lock mode
;                  because of RB3 is not available now.
;                  00h = Enabled, 01..FFh = Disabled    (def. FF)
; 0D = Delay     = Delay time before frequency lock procedure starts after
;                  out of lock situation. 1 == 10 Hz (def. 028h == 4 s.)
; 0E = Sampling  = Sampling time with reference and lock checking.
;                  1h == 1 sample (def. = 0Ah)
; 0F = Defaults  = Default values are restored to EEPROM if set to >00h.
;--------------------------------------------------------------------------
; E-Mail: halicky@minv.sk     or om3cph@oe3xbs.aut.eu
;         peto-h@writeme.com  or om2ph@om0pbm.svk.eu
;         
; Bratislava, Slovakia, December 1998, revised & debugged February, 2000
;
; Further modified 2001 by oh6cj@sral.fi
;--------------------------------------------------------------------------
; This is 7-digit frequency meter counting up to 35 MHz. The decimal point
; is after MHz digit, but can be at any position.
;
; It adds or substracts RF according signal level at RA2:
;              +5V - adds RF       (offset2)
;     disconnected - substracts RF (offset1)
;
; Hardware is very simple:
;
; It contains     : PIC 16F84
;                   1 NPN low power HF Si transistor,
;                   16 character (2x8) in 1 Line LCD display,
;                   Xtal 1..10 MHz,
;                   some resistors, capacitors and 2 Si switching diodes...
;                   (see schematic)
; Note:
; LCD display is 16 character in 1 line LCD display PVC160101PTN which
; seems to be compatible with TWO LINES HITACHI LCD display, except
; that that one has only 8 characters in 1 line.
; See EEPROM parameter  09h to select either 1x16 or 2x20 
;
; The counter uses internal prescaler of PIC as low byte of counter,
; TMR0 as middle byte and some register as high byte of counter.
;
; Some ideas were taken from "Simple low-cost digital frequency meter
;                             using a PIC 16C54" (frqmeter.asm)
;                             written by James Hutchby, MadLab Ltd. 1996
;
; LCD interfacing was completly taken from Norm Cramer's LCD.ASM
; ------------------------------------------------------------------------
;
; This software is free for private usage. It was created for HAM radio
; community members. Commercial exploatation is allowed only with permission
; of authors.
;
; ------------------------------------------------------------------------
;
; The measuring period is 100 000 us.
; Procesor cycle is T = 4/Fx [us,MHz], Fx is Xtal frequency
;
; Number of processor cycles per measuring period:
;
;        N = 100 000/T processor cycles
;        N = Fx * 100 000/4 = 25 000 x Fx
;
;        The main steps of measuring period:
;
;        1. decode 3-byte value into 7 decimal numbers,
;        2. decode decimal value of digit to chars,
;        3. set decimal point if needed,
;        4. output to PORTB (LCD) either 6 or 7 digits,
;        5. start measurement,
;        6. test TMR0 overflow bite, if YES increase TimerH,
;        7. goto 5 until measuring period is done,
;        8. stop measurement,
;        9. shift out precounter content,
;       10. Add/substract RF according signal from optocoupler,
;       11. goto 1
;
; ------------------------------------------------------------------------
; Total timing formula: N = 25 000 * Fx = ((9*T1+4)*T2+4)*T3+5+9*T4+Z
;
; N = 25 000 * Fx [MHz]
;
; Example:  Fx = 4 MHz
;
; N = 25 000 * 4 = 100 000
; N = 25 000 * Fx = ((9*T1+4)*T2+4)*T3+5+9*T4+Z
; 
; SW Tuning
; -----------
; The calibration tuning is possible by EEPROM parameters 07h and
; 08h. The minimum chnage step for timing is 1 us based on the 4 MHz 
; crystal. Relation is 1 us/100000 us. == 0.00001. 
; A bit smaller value for T4 is used than calculated above to get area
; for final correction +/- by EE_fine1 and EE_fine2. The correct
; calibration value is found by means of the combination of EE_fine1&2 
; and comparing to reference source. 
; E.g. the freq. of calibrated reference source is 10.00000 MHz and 
; counter shows 9.99980 MHz. EE_fine1 = 14 and EE_fine2 = 1
; EE_fine1 & 2 delay = 14*3 us + 1*4 us = 46 us.
; The counting window is too short. Let's count the absolute influence
; value on 10 MHz: 0.00001 * 10 MHz = 100 Hz. Now we can calculate that
; we must increase value by 20 Hz == 2 us.
; The results are EE_fine1 =12 and EE_fine2 =3    (12*3us + 3*4us) = 48 us 
; The most accurate tuning is made by adjusting one of the capacitor in
; the crystal circuit.   
; ------------------------------------------------------------------------

      include <p16f84.inc>

; ------------------------------------------------------------------------

Index       equ        0Ch         ; dummy register
Count       equ        0Dh         ; inkremental register
Help        equ        0Eh         ; dummy register

LED0        equ        0Fh
LED1        equ       010h
LED2        equ       011h
LED3        equ       012h
LED4        equ       013h
LED5        equ       014h
LED6        equ       015h

CHAR        equ       016h         ; LCD subroutines internal use

TimerH      equ       017h         ; the highest byte of SW counter

LowB        equ       018h         ; low byte of resulted frequency
MidB        equ       019h         ; middle byte of resulted frequency
HigB        equ       01Ah         ; high byte of resulted frequency

TEMP        equ       01Bh         ; temporary register
HIndex      equ       01Ch         ; index register
LEDIndex    equ       01Dh         ; LED pointer

R1          equ       01Eh         ; Timing counters
R2          equ       01Fh
R3          equ       020h

USER_EEADR  equ       021h         ; EEPROM address   
USER_EEDATA equ       022h         ; EEPROM data
EE_ALR_READ equ       023h         ; EEPROM address already read
EE_Fine1    equ       024h         ; EEPROM calibration parameter 1
EE_Fine2    equ       025h         ; EEPROM calibration parameter 2 
Help2       equ       026h         ; temporary register
LowB_ref    equ       027h         ; LowB value for frequency reference
MidB_ref    equ       028h         ; MidB value for frequency reference
HigB_ref    equ       029h         ; HighB value for frequency reference
Status_word equ       02Ah         ; Frequency lock status word
Delay_counter equ     02Bh         ; Frequency lock delay counter 
Sample_counter equ    02Ch         ; Sample counter for frequency lock
; ------------------------------------------------------------------------
; LCD variables
; ------------------------------------------------------------------------

;Xtal       equ       4            ; MHz
DELAY15     equ       .21          ; 1+15000*Xtal/4/770
DELAY4100   equ       .7          ; 1+4100*Xtal/4/770
DELAY100    equ       1            ; 1+100*Xtal/4/770

LINE0       equ       0
LINE1       equ       040h

E           equ       2            ; LCD Enable control line          RA2
R_W         equ       1            ; LCD Read/Write control line      RA1
RS          equ       0            ; LCD Register-Select control line RA0
                                   ; PORTB bits
                                   ; LCD Data are sent through  RB4 - RB7
; ------------------------------------------------------------------------
                                   ; timing loop values
                                   ; must be from 1 to 255!!!
T1          equ       .199         ; first timing loop
T2          equ       .11          ; second timing loop
T3          equ       .5           ; third timing loop
T4          equ       .130         ; last timing loop 

; ------------------------------------------------------------------------
            org        0

Start       clrf       STATUS      ; Do initialization, Select bank 0
            clrf       INTCON      ; Clear int-flags, Disable interrupts
            clrf       PCLATH      ; Keep in lower 2KByte
            clrf       PORTA       ; ALL PORT output should output Low.
            clrf       PORTB

            clrf       Index
            clrf       LEDIndex

            clrf       LED0
            clrf       LED1
            clrf       LED2
            clrf       LED3
            clrf       LED4
            clrf       LED5
            clrf       LED6

            clrf       LowB
            clrf       MidB
            clrf       HigB

            clrf       Delay_counter  ; For Frequency lock function
            clrf       Sample_counter ; For Frequency lock function

            clrf       Status_word    ; For Frequency lock function
            
            bsf        STATUS,RP0

            movlw      b'00010000' ; RA0..RA3 outputs
            movwf      TRISA       ; RA4 input

            movlw      0xFF        ; RB0..RB7 inputs
            movwf      TRISB

            bsf        OPTION_REG,NOT_RBPU ; Disable PORTB pull-ups

            clrwdt
            movlw      b'10100111' ; Prescaler -> TMR0,
            movwf      OPTION_REG  ; 1:256, rising edge
            bcf        STATUS,RP0  ;
                                   ; Initilize LC-Display Module
                                   ; Busy-flag is not yet valid
            clrf       PORTA       ; ALL PORT output should output Low.
                                   ; Initilize the LCD Display Module
            clrf       PORTB       ; ALL PORT output should output Low

   	    bcf        PORTA,E     ; Clear all controll lines
	    bcf        PORTA,RS
	    bcf        PORTA,R_W

            movlw      DELAY15     ; Wait for 15ms for LCD to get powered up
            movwf      R1
            clrf       R2

LCycle      decfsz     R2,F
            goto       LCycle      ; 3*256
            decfsz     R1,F        ; 3*256+1
            goto       LCycle      ;(3*256+2)*R1=770*R1 in procesor cycles

;******************************************************************************
; Initialization of LCD display
;******************************************************************************

	    movlw      0x0F
	    andwf      PORTB,F    ; Clear the upper nibble

	    movlw      0x030      ; Command for 4-bit interface high nibble
            iorwf      PORTB      ; Send data to LCD

	    bsf        STATUS,RP0 ; Select Register page 1
	    movlw      0x0F
	    andwf      TRISB,W
	    movwf      TRISB      ; Set Port for output
	    bcf        STATUS,RP0 ; Select Register page 0

	    bsf        PORTA,E    ; Clock the initalize command to LCD module
	    bcf        PORTA,E
	    movlw      DELAY4100  ; Delay for at least 4.1ms before continuing
            movwf      R1
            clrf       R2

LCycle2     decfsz     R2,F
            goto       LCycle2    ; 3*256
            decfsz     R1,F       ; 3*256+1
            goto       LCycle2    ;(3*256+2)*R1=770*R1 in procesor cycles

	    bsf        PORTA,E    ; Clock the initalize command to LCD module
	    bcf        PORTA,E

            movlw      DELAY100   ; Wait for 100 us
            movwf      R1
            clrf       R2

LCycle3     decfsz     R2,F
            goto       LCycle3     ; 3*256
            decfsz     R1,F        ; 3*256+1
            goto       LCycle3     ;(3*256+2)*R1=770*R1 in procesor cycles

	    movlw      0x0F
	    andwf      PORTB,F    ; Clear the upper nibble

            movlw      020h       ; Command for 4-bit interface high nibble
	    iorwf      PORTB      ; Send data to LCD

	    bsf        PORTA,E    ; Clock the initalize command to LCD module
	    bcf        PORTA,E
	    movlw      0x028      ; 4 bits, 2 lines, 5x7 Font
	    call       PutCMD

	    movlw      B'00001000'; disp.off, curs.off, no-blink
	    call       PutCMD

            movlw      1          ; LCD clear
            call       PutCMD

            movlw      B'00001100'; disp.on, curs.off
            call       PutCMD

            movlw      B'00000110'; auto-inc (shift-cursor)
            call       PutCMD
;------------------------------------------------------------------------
	    movlw      7          ; Read calibration values from EEPROM
            movwf      USER_EEADR 
            call       EE_read
	    movwf      EE_Fine1

	    movlw      8          ; Read calibration values from EEPROM
            movwf      USER_EEADR 
            call       EE_read
	    movwf      EE_Fine2
;------------------------------------------------------------------------
	    movlw      0Bh          ; Read Freq lock function status
            movwf      USER_EEADR  
            call       EE_read
	    btfsc      STATUS,Z     ; If 0 then enabled -> set bit 2 SW
            bsf        Status_word,2                              
;------------------------------------------------------------------------
; This checks the first power-on. If true then save initial values to
; EEPROM memory. Finger print of this procedure is in EE address 00Fh
;------------------------------------------------------------------------
EEPROM_init_check
	    movlw      00Fh        ; EE address 00Fh 
            movwf      USER_EEADR  
            call       EE_read   ; Read EEPROM address 0Fh
            btfsc      STATUS,Z  ; then skip to Mode_test
            goto       Mode_test   

;	    movlw      'E'
;            call       PutCHAR	   ; Display character
;	    movlw      'E'
;            call       PutCHAR	   ; Display character
;	    movlw      '-'
;            call       PutCHAR	   ; Display character
;	    movlw      'I'
;            call       PutCHAR	   ; Display character
;	    movlw      'N'
;            call       PutCHAR	   ; Display character
;	    movlw      'I'
;            call       PutCHAR	   ; Display character
;	    movlw      'T'
;            call       PutCHAR	   ; Display character
;	    movlw      ':'
;            call       PutCHAR	   ; Display character
            call       Delay_200ms

	    movlw      0           ; Start saving of default data
            movwf      USER_EEADR  ; from  EE address 0h

Next_Data_to_EE
            movf       USER_EEADR,W
            movwf      EEADR       ; Save address for EE writing routine

            call       EE_Table    ; W contains address of EE_table
            movwf      EEDATA      ; Save data to EE writing routine
            call       EE_write    ; Writing to EEPROM subroutine

            movlw      LINE1       ; continue at right half of the display
            iorlw      080h        ; Function set
            call       PutCMD      ; set cursor leftmost at the first line
            call       Busy        ; Wait until positioned (takes 40 us)

            movf       USER_EEADR,W    
            call       CharTable       ; Pick up char to be displayed.
            call       PutCHAR	       ; Display EEADR as ASCII character
            call       Delay_200ms     ; Delay is needed to get PIC ready
                                       ; for next EEPROM save function!
                                       ; Otherwise this loop doesn't work!
            movf       USER_EEADR,W
            sublw      00Fh            ; Loop test for addresses 00h...0Fh
            btfss      STATUS,C        ; if C = 0 --> negative result
            goto       Mode_test       ; Address 0F completed -> exit this
            incf       USER_EEADR,F    ; increment USER_EEADR
            goto       Next_Data_to_EE ;        

;------------------------------------------------------------------------
; This checks the normal counter mode or EEPROM SETTINGS mode acc. RB0
;------------------------------------------------------------------------
Mode_test
            movlw      1          ; LCD clear
            call       PutCMD

	    btfss      PORTB,0	
            goto       Entry       ; to line 509
            goto       EE_Routines ; if RB0 = 1 then to EE settings

;------------------------------------------------------------------------
; Tables for 3 byte constants
;------------------------------------------------------------------------
; Table of decades
;------------------------------------------------------------------------

DecTable    addwf      PCL,F      ; W + PCL -> PCL
            retlw      0          ; 10
            retlw      0          ;
            retlw      0Ah        ;

            retlw      0          ; 100
            retlw      0          ;
            retlw      064h       ;

            retlw      0          ; 1 000
            retlw      03h        ;
            retlw      0E8h       ;

            retlw      0          ; 10 000
            retlw      027h       ;
            retlw      010h       ;

            retlw      01h        ; 100 000
            retlw      086h       ;
            retlw      0A0h       ;

            retlw      0Fh        ; 1 000 000
            retlw      042h       ;
            retlw      040h       ;

CharTable   addwf      PCL,F      ; W + PCL -> PCL
            retlw      '0'        ; 
            retlw      '1'        ;
            retlw      '2'        ;
            retlw      '3'        ; 
            retlw      '4'        ;
            retlw      '5'        ;
            retlw      '6'        ; 
            retlw      '7'        ;
            retlw      '8'        ;
            retlw      '9'        ; 
            retlw      'A'        ;
            retlw      'B'        ;
            retlw      'C'        ; 
            retlw      'D'        ;
            retlw      'E'        ;
            retlw      'F'        ; 

;--------------------------------------------------------------------------
; The used EEPROM addresses and default values:
; 00 = MFt1_HigB  = High Byte of RF offset1 (def. 0D) 9001.50 kHz = 0DBC36
; 01 = MFt1_MidB  = Mid Byte of RF offset1  (def. BC)
; 02 = MFt1_LowB  = Low Byte of RF offset1  (def. 36)
; 03 = MFt2_HigB  = High Byte of RF offset2 (def. 0D) 8998.50 khz = 0DBB0A
; 04 = MFt2_MidB  = Mid Byte of RF offset2  (def. BB)
; 05 = MFt2_LowB  = Low Byte of RF offset2  (def. 0A)
; 06 = Dir_freq   = Direct frequency without sub or add functions = 0 def.
; 07 = EE_Fine1  = Counter value for calibration 1 == 3*4/fx= 3us (def. 15)
; 08 = EE_Fine2  = Counter value for calibration 1 == 4*4/fx= 4us (def. 01)
; 09 = 1x16_Disp = LCD display type: 0 = 1x16 LCD, 1 = 2x20 LCD (def. 00)
; 0A = Digits    = Number of displayed digits: 0 = 7 digits, 1 = 6 (def. 0)
; 0B = Freq_lock = Frequency lock func. indicating freq difference.
;                  00h = Enabled, 01...FF = Disabled, (def. FF) 
; 0C = Ten_div   = Ten_divider activated in Freq-lock mode
;                  00h = Enabled, 01..FFh = Disabled    (def. FF)
; 0D = Delay     = Delay time before frequency lock procedure starts after
;                  out of lock situation. 1 == 10 Hz (def. 032h == 5 s.)
; 0E = Sampling  = Number of samples with reference and lock check
;                  1h == 1 sample (def. = 05h)
; 0F = Defaults  = Default values are restored to EEPROM if set to >00h.
;--------------------------------------------------------------------------
; Default data table for EEPROM addresses 00h...0Fh
;--------------------------------------------------------------------------
EE_Table    addwf      PCL,F       ; W + PCL -> PCL
            retlw      00Dh        ; 00h  
            retlw      0BCh        ; 01h
            retlw      036h        ; 02h
            retlw      00Dh        ; 03h
            retlw      0BBh        ; 04h
            retlw      00Ah        ; 05h
            retlw      000h        ; 06h
            retlw      014h        ; 07h
            retlw      001h        ; 08h
            retlw      000h        ; 09h 
            retlw      000h        ; 0A   
            retlw      0FFh        ; 0B
            retlw      0FFh        ; 0C
            retlw      032h        ; 0D
            retlw      005h        ; 0E
            retlw      000h        ; 0F ; This is used as finger print
				        ; to do this subroutine only once.
                                                                                
;*************************************************************************
; LCD Module Subroutines
;========================================================================
; Busy: Returns when LCD busy-flag is inactive
;       PORTA returns as RA0..RA2 output, RA3,RA4 input
;************************************************************************

Busy	    bsf        STATUS,RP0 ; Select Register page 1

	    movlw      0xF0       ; Set port to input
	    iorwf      TRISB,W    ; Only set upper half of port
	    movwf      TRISB

            movlw      b'00011000' ; PORTA should be set RA0..RA2 output
            movwf      TRISA       ; RA3,RA4 input

	    bcf        STATUS,RP0 ; Select Register page 0

	    bcf        PORTA,RS   ; Set LCD for Command mode
	    bsf        PORTA,R_W  ; Setup to read busy flag
	    bsf        PORTA,E    ; Set E high
	
	    movf       PORTB,W    ; Read upper nibble busy flag, DDRam address
	    andlw      0xF0       ; Mask out lower nibble
	    movwf      TEMP

	    bcf        PORTA,E    ; Set E low
            nop
	    nop
	    bsf        PORTA,E    ; Toggle E to get lower nibble
	    bcf        PORTA,E
	    swapf      PORTB,W    ; Read lower nibble busy flag, DDRam address
	    andlw      0x0F       ; Mask out upper nibble
	    iorwf      TEMP,W     ; Combine nibbles
	    btfsc      TEMP,7     ; Check busy flag, high = busy
	    goto       Busy       ; If busy, check again

	    bcf        PORTA,R_W
	    bsf        STATUS,RP0 ; Select Register page 1
	    movlw      0x0F
	    andwf      TRISB,W
	    movwf      TRISB      ; Set Port for output
	    bcf        STATUS,RP0 ; Select Register page 0
	    return

;========================================================================
; PUTCHAR Sends character to LCD, Required character must be in W
;========================================================================

PutCHAR     movwf      CHAR       ; Character to be sent is from W saved
            call       Busy       ; Wait for LCD to be ready
                                  ; Busy routine sets PORTB adequately
	    movlw      0x0F
	    andwf      PORTB,F    ; Clear the upper nibble
	    movf       CHAR,W
	    andlw      0xF0       ; Get upper nibble
	    iorwf      PORTB,F    ; Send data to LCD
	    bcf        PORTA,R_W  ; Set LCD to write
	    bsf        PORTA,RS   ; Set LCD to data mode
	    bsf        PORTA,E    ; toggle E for LCD
	    bcf        PORTA,E
	    movlw      0x0F
	    andwf      PORTB,F    ; Clear the upper nibble
	    swapf      CHAR,W
	    andlw      0xF0       ; Get lower nibble
	    iorwf      PORTB,F    ; Send data to LCD
	    bsf        PORTA,E    ; toggle E for LCD
	    bcf        PORTA,E
            return

;========================================================================
; PutCMD Sends command to LCD, Required command must be in W
;========================================================================

PutCMD      movwf      CHAR       ; Command to be sent is from W saved
            call       Busy       ; Wait for LCD to be ready
	    movlw      0x0F
	    andwf      PORTB,F    ; Clear the upper nibble
	    movf       CHAR,W
	    andlw      0xF0       ; Get upper nibble
	    iorwf      PORTB,F    ; Send data to LCD
	    bcf        PORTA,R_W  ; Set LCD to write
	    bcf        PORTA,RS   ; Set LCD to command mode
	    bsf        PORTA,E    ; toggle E for LCD
	    bcf        PORTA,E
	    movlw      0x0F
	    andwf      PORTB,F    ; Clear the upper nibble
	    swapf      CHAR,W
	    andlw      0xF0       ; Get lower nibble
	    iorwf      PORTB,F    ; Send data to LCD
	    bsf        PORTA,E    ; toggle E for LCD
	    bcf        PORTA,E
	    return

;************************************************************************
; End of LCD Module Subroutines
;************************************************************************
;************************************************************************
; Delay Routines
;************************************************************************
Delay_200ms
            movlw      0FFh     ; Wait for 200ms for LCD to get powered up
            movwf      R1
            clrf       R2

LCycle4     decfsz     R2,F
            goto       LCycle4    ; 3*256
            decfsz     R1,F       ; 3*256+1
            goto       LCycle4    ;(3*256+2)*R1=770*R1 in processor cycles
            return

;************************************************************************
; EEPROM READ ROUTINE
;
; Input:    read address in USER_EEADR also in w
; Output:   data in USER_EEDATA also in w
;************************************************************************
EE_read
            bcf        STATUS,RP0  ; Bank 0
            movf       USER_EEADR,w; EE address 
            movwf      EEADR       ; Address to read
            bsf        STATUS,RP0  ; Bank 1
            bsf        EECON1,RD   ; EE Read
            bcf        STATUS,RP0  ; Bank 0
            movf       EEDATA,w    ; w = EEDATA 
            movwf      USER_EEDATA ; Data
	    return	

;************************************************************************
; EEPROM WRITE ROUTINE
;
; Input:    write address in USER_EEADR also in w
;           data in USER_EEDATA also in w
; Output:   -
;************************************************************************
EE_write
	bsf	STATUS,RP0	; Bank 1
	bcf	INTCON,GIE	; INTs disabled 
	bsf	EECON1,WREN	; Enable write
;--------------------------------------------------------
; Magic macro with EEPROM save (see 16C84 manual) 	
;--------------------------------------------------------
	movlw	55h
	movwf	EECON2
	movlw	0AAh
	movwf	EECON2
	bsf	EECON1,WR
ee_wr1
	btfsc	EECON1,WR       ; is the write completed?
	goto	ee_wr1          ; loop until completed
	bsf	INTCON,GIE	; Enable INTs		
	return

;************************************************************************
; 24 bit subtract routine 010809 -pn-
;
; 3 byte substraction of the frequency reference. Carry is set if
; result is negative. Temporary nonused registers LED0..LED6 are used.
;************************************************************************
; Inputs:   LED4 - LED0  (used for LowB)
;           LED5 - LED1  (used for MidB)
;           LED6 - LED2  (used for HigB) 
;------------------------------------------------------------------------
; Outputs:  The result of 24 bit Subtraction is saved to
;           LED4 (LowB)
;           LED5 (MidB)
;           LED6 (HigB)
;           Register W contain the result for STATUS checking. 
;************************************************************************
Sub24bit    bcf       Status_word,4  ; Clear carry status bit
            movf      LED0,W         ; sub LowB
            subwf     LED4,F         ; from LED4
            btfsc     STATUS,C       ; underflow?
            goto      sb0            ; no underflow
            decf      LED5,F         ; check 00 -> ff
            incf      LED5,F         ; ff?, Z then 1, LED5+1
            btfss     STATUS,Z       ; ... but if wasn't ff
            goto      notFF
            decf      LED6,F         ; dec also LED6
 
notFF       decf      LED5,F         ; correct LED5+1 -> MidB 
sb0         movf      LED1,W         ; middle byte
            subwf     LED5,F         ; subtract
            btfsc     STATUS,C       ; if underflow to MSbyte
            goto      sb1            ; no underflow
            decf      LED6,F         ; check 00 -> ff
sb1
            movf      LED2,W         ; MSByte
            subwf     LED6,F         ; sub it
            btfsc     STATUS,C
            bsf       Status_word,4  ; Set if Carry = 1 
            return

;************************************************************************
; 24 bit Addition routine 
;
; 3 byte addition. Carry is set if result is negative.
; Temporary nonused registers LED0..LED6 are used.
;************************************************************************
; Inputs:   LED4 + LED0  (used for LowB)
;           LED5 + LED1  (used for MidB)
;           LED6 + LED2  (used for HigB) 
;------------------------------------------------------------------------
; Outputs:  The result of 24 bit Addition is saved to
;           LED4 (LowB)
;           LED5 (MidB)
;           LED6 (HigB)
;           Register W contain the result for STATUS checking. 
;************************************************************************
Add24bit    bcf        Status_word,4  ; Clear carry status bit
            movf       LED0,W
            addwf      LED4,F
            btfss      STATUS,C
            goto       Addsb2
            incfsz     LED5,F
            goto       Addsb2
            movlw      1
            addwf      LED6,F
            btfsc      STATUS,C
            bsf        Status_word,4  ; Set if Carry = 1 
Addsb2
            movf       LED1,W
            addwf      LED5,F
            btfss      STATUS,C
            goto       Addsb3
            incf       LED6,F
Addsb3
            movf       LED2,W 
            addwf      LED6,F
            btfsc      STATUS,C
            bsf        Status_word,4  ; Set if Carry = 1 
            return
;***********************************************************************
; Preparation subcall for subtraction Freq_act - Freq_ref 24 bit 
;***********************************************************************
Preparation
            movf       LowB_ref,W      ; Preparation for sub call 
            movwf      LED0
            movf       MidB_ref,W
            movwf      LED1
            movf       HigB_ref,W
            movwf      LED2
            return
;***********************************************************************
; Zero result check of subtraction  
;***********************************************************************
Zero_check
            bcf        Status_word,6
            movlw      1
            subwf      LED4,W        ; = LowB of subtraction result 
            btfsc      STATUS,C      ; if negative -> LED4 is zero    
            goto       Not_zero1
            movlw      1
            subwf      LED5,W        ; = MidB of subtraction result 
            btfsc      STATUS,C      ; if negative -> LED5 is zero    
            goto       Not_zero1
            bsf        Status_word,6 ; zero result  
Not_zero1
            return

;***********************************************************************
; Display Offset Checking either Display Offset 1 or 2. Load offsets.  
;***********************************************************************
Offset_check
            movlw      3
	    btfss      PORTB,1     ; True? Then Mft2
            movlw      0           ; Mft1 selected
;-----------------------------------------------------------------------
; Display Offset read from EEPROM to registers LED4...6  
;-----------------------------------------------------------------------
            movwf      USER_EEADR 
            call       EE_read
            movwf      LED6        ; Offset1 HigB --> LED6
            incf       USER_EEADR,F
            call       EE_read
            movwf      LED5        ; Offset1 MidB --> LED5
            incf       USER_EEADR,F
            call       EE_read
            movwf      LED4        ; Offset1 LowB --> LED4
            return    

EE_Routines
;***********************************************************************
; EEPROM MODE TEXTS
;***********************************************************************
        movlw      LINE0
        iorlw      080h        ; set cursor leftmost at the first line
        call       PutCMD
        call       Busy        ; Wait until positioned (takes 40 us)
 
        movlw      'E'
        call       PutCHAR	   ; Display character
        movlw      'E'
        call       PutCHAR	   ; Display character
;        movlw      'P'
;        call       PutCHAR	   ; Display character
;        movlw      'R'
;        call       PutCHAR	   ; Display character
;        movlw      'O'
;        call       PutCHAR	   ; Display character
;        movlw      'M'
;        call       PutCHAR	   ; Display character
        movlw      ' '
        call       PutCHAR	   ; Display character

;        movlw      LINE1       ; continue at right half of the display
;        iorlw      080h        ; Function set
;        call       PutCMD      ; set cursor leftmost at the first line
;        call       Busy        ; Wait until positioned (takes 40 us)

        movlw      'M'
        call       PutCHAR	   ; Display character
        movlw      'O'
        call       PutCHAR	   ; Display character
        movlw      'D'
        call       PutCHAR	   ; Display character
        movlw      'E'
        call       PutCHAR	   ; Display character
        movlw      '-'
        call       PutCHAR	   ; Display character
        movlw      '1'
        call       PutCHAR	   ; Display character
        movlw      '.'
        call       PutCHAR	   ; Display character
        movlw      '4'
        call       PutCHAR	   ; Display character

        call    Delay_200ms	   ; Show the text
        call    Delay_200ms        ; at least 3x200ms...
        call    Delay_200ms        ; 

	movfw	PORTB               ; ...until RB0...RB3 are zero  
	andlw	00Fh                ;  
        btfss	STATUS,Z            ; RB0...RB3 false?
	goto	EE_Routines         ; No

        movlw   001h             ; Clear display
        call    PutCMD
        call    Busy             ; Wait until cleared (takes 1.6 ms)

 	clrf	USER_EEADR
        clrf	USER_EEDATA
	clrf	EE_ALR_READ

;***********************************************************************
; Switch routines
;***********************************************************************
Io_test

Check_RB0
	btfss	PORTB,0
        goto    Check_RB1
        incf    USER_EEADR,1	; USER_EEADR = USER_EEADR + 1
	movf	USER_EEADR,0	; w = USER_EEADR 
	sublw	00Fh		; EE Address range 0...F
	btfss	STATUS,C	; if C = 0 --> negative result
	clrf	USER_EEADR
        call    Delay_200ms
	bcf	EE_ALR_READ,0	; reset if address is changed
Check_RB1
	btfss	PORTB,1
        goto    Check_RB2 
        incf    USER_EEDATA,1
        call    Delay_200ms
Check_RB2
	btfss	PORTB,2
        goto    Check_RB3 
        decf    USER_EEDATA,1
        call    Delay_200ms

Check_RB3
	btfss	PORTB,3
        goto    Continue 
        call    Delay_200ms
        movf	USER_EEADR,0
	movwf	EEADR
	movf	USER_EEDATA,0
	movwf	EEDATA

	call    EE_write
;--------------------------------------------------------
        movlw   001h             ; Clear display
        call    PutCMD
        call    Busy        ; Wait until cleared (takes 1.6 ms)

        movlw      '*'
        call       PutCHAR	   ; Display character
        movlw      'S'
        call       PutCHAR	   ; Display character
        movlw      'A'
        call       PutCHAR	   ; Display character
        movlw      'V'
        call       PutCHAR	   ; Display character
        movlw      'E'
        call       PutCHAR	   ; Display character
        movlw      'D'
        call       PutCHAR	   ; Display character
        movlw      '*'
        call       PutCHAR	   ; Display character

        call    Delay_200ms
        call    Delay_200ms
        call    Delay_200ms
        call    Delay_200ms
        movlw   001h               ; Clear display
        call    PutCMD             ; It takes about 1.6 ms
        call    Busy               ; Wait until cleared     
	clrf	EE_ALR_READ
;--------------------------------------------------------
Continue
        movlw      'A'
        call       PutCHAR	   ; Display character
        movlw      'D'
        call       PutCHAR	   ; Display character
        movlw      'D'
        call       PutCHAR	   ; Display character
        movlw      'R'
        call       PutCHAR	   ; Display character
        movlw      ':'
        call       PutCHAR	   ; Display character

	btfsc 	   EE_ALR_READ,0   ; Data read, value not changed
	goto	   Display_EE
        
        call       EE_read
        bsf	   EE_ALR_READ,0   ; Same data is read only once
;------------------------------------------------------------------------
Display_EE
        movf       USER_EEADR,0    ; Display address left digit first
	andlw	   0F0h
        movwf      Help
	swapf      Help,0
        call       CharTable       ; Pick up char to be displayed
        call       PutCHAR         ; Display character	
               
	movf       USER_EEADR,0
	andlw	   00Fh
        call       CharTable       ; Pick up char to be displayed
        call       PutCHAR         ; Display character		
;------------------------------------------------------------------------
        movlw      LINE1       ; continue at right half of the display
        iorlw      080h        ; Function set
        call       PutCMD      ; set cursor leftmost at the first line
        call       Busy        ; Wait until positioned (takes 40 us)

        movlw      'D'
        call       PutCHAR	   ; Display character
        movlw      'A'
        call       PutCHAR	   ; Display character
        movlw      'T'
        call       PutCHAR	   ; Display character
        movlw      'A'
        call       PutCHAR	   ; Display character
        movlw      ':'
        call       PutCHAR	   ; Display character
;------------------------------------------------------------------------
        movf       USER_EEDATA,0    ; Display data left digit first
	andlw	   0F0h
        movwf      Help
	swapf      Help,0
        call       CharTable       ; Pick up char to be displayed
        call       PutCHAR         ; Display character	
               
	movf       USER_EEDATA,0
	andlw	   00Fh
        call       CharTable       ; Pick up char to be displayed
        call       PutCHAR         ; Display character		
;------------------------------------------------------------------------
        movlw      LINE0        
	iorlw      080h        ; Function set
        call       PutCMD
        call       Busy        ; Wait until positioned (takes 40 us)

	goto	Io_test
;************************************************************************
; End of EEPROM routines

;************************************************************************
; Numeric routines
;------------------------------------------------------------------------
; 3 byte substraction of the constant from the table which sets carry if
; result is negative
;------------------------------------------------------------------------

Subc24      clrf       TEMP       ; it will TEMPorary save C
            movf       Index,W    ; pointer to low byte of constant
            movwf      HIndex     ; W -> HIndex
            call       DecTable   ; W returned with low byte of constant
            subwf      LowB,F     ; LowB - W -> LowB
                                  ; if underflow -> C=0
            btfsc      STATUS,C
            goto       Step1
            movlw      1
            subwf      MidB,F     ; decrement MidB
                                  ; if underflow -> C=0
            btfsc      STATUS,C
            goto       Step1

            movlw      1
            subwf      HigB,F     ; decrement HigB
            btfsc      STATUS,C   ; if underflow -> C=0
            goto       Step1
            bsf        TEMP,C     ; set C

Step1       decf       HIndex,F
            movf       HIndex,W   ; pointer to middle byte of const
            call       DecTable
            subwf      MidB,F     ; MidB - W -> MidB
            btfsc      STATUS,C   ; if underflow -> C=0
            goto       Step2
            movlw      1
            subwf      HigB,1     ; decrement HigB
            btfsc      STATUS,C   ; if underflow -> C=0
            goto       Step2
            bsf        TEMP,C     ; set C

Step2       decf       HIndex,F
            movf       HIndex,W   ; pointer to middle byte of constatnt
            call       DecTable
            subwf      HigB,F     ; HigB - W -> HigB
            btfsc      STATUS,C   ; if underflow -> C=0
            goto       ClearCF
            bsf        STATUS,C
            goto       SubEnd
ClearCF     rrf        TEMP,C     ; C -> STATUS
SubEnd      retlw      0

; ------------------------------------------------------------------------
; 3 byte addition of the constant from the table which sets carry if
; result overflows
; ------------------------------------------------------------------------

Addc24      clrf       TEMP       ; register for TEMPorary storage of C
            movf       Index,W    ; pointer to lower byte of const into W
            movwf      HIndex     ; save it into HIndex
            call       DecTable   ; W contains low byte of const
            addwf      LowB,1     ; W + LowB -> LowB
            btfss      STATUS,C   ; test overflow
            goto       Add2
            movlw      1
            addwf      MidB,F     ; increment MidB
            btfss      STATUS,C
            goto       Add2
            movlw      1
            addwf      HigB,F     ; increment HigB
            btfss      STATUS,C   ; test overflow
            goto       Add2
            bsf        TEMP,C     ; store C
Add2        decf       HIndex,F   ; pointer to middle byte into W
            movf       HIndex,W
            call       DecTable
            addwf      MidB,1     ; W + MidB -> MidB
            btfss      STATUS,C
            goto       Add3
            movlw      1
            addwf      HigB,1     ; increment HigB
            btfss      STATUS,C
            goto       Add3
            bsf        TEMP,C
Add3        decf       HIndex,F   ; pointer to higher byte into W
            movf       HIndex,W
            call       DecTable
            addwf      HigB,F     ; W + HigB -> HigB,
            btfss      STATUS,C
            goto       ClarCF
            bsf        STATUS,C
            goto       AddEnd
ClarCF      rrf        TEMP,C     ; C -> STATUS
AddEnd      retlw      0

;************************************************************************
; Entry point for main cycle
;------------------------------------------------------------------------
; Routine for the conversion of 3 byte number into 7 decimal numbers
;************************************************************************

Entry       movlw      6*3-1      ; pointer to dec. table
            movwf      Index      ; 6*3-1 -> Index

            movlw      9          ; maximum of substractions
            movwf      Count      ; 9 -> Count

            clrf       Help

            movlw      6
            movwf      LEDIndex

Divide      call       Subc24     ; substract untill result is negative,
            btfsc      STATUS,C   ; add last substracted number
            goto       Add24      ; next digit
            incf       Help,F
            decf       Count,F
            btfss      STATUS,Z
            goto       Divide
            movlw      3
            subwf      Index,F
            goto       Next

Add24       call       Addc24
            movlw      03h
            subwf      Index,F

Next        movlw      9
            movwf      Count
            movlw      LED1       ; LED1 -> W
            addwf      LEDIndex,W ; LED1 + LEDIndex -> W
            movwf      TEMP
            decf       TEMP,F     ; LEDIndex+LED1-1 -> TEMP
            movf       TEMP,W

            movwf      FSR        ; W -> FSR
            movf       Help,W     ; Help -> W
            clrf       Help       ; save result at LEDx
            movwf      INDF       ; W -> LED(6..1)
            decf       LEDIndex,F

            movlw      1
            addwf      Index,W
            btfss      STATUS,Z
            goto       Divide

            movf       LowB,W
            movwf      LED0       ; the rest -> LED0
;-------------------------------------------------------------------------
            btfss      Status_word,5     ; Frequency is within +/- 20 Hz
            goto       Ready_to_display
            bcf        PORTB,0           ; Clear outputs to get short pulse
            bcf        PORTB,3           ; for fine frequency correction.
Ready_to_display
;-------------------------------------------------------------------------
; registers LED0..LED6 are filled with values - ready to be displayed
;-------------------------------------------------------------------------
 
            movlw      6
            movwf      LEDIndex

;--------------------------------------------------------------------------
; Next clears DDRAM position 07h if 10 divider and normal mode is switched.
; Othervise it causes incorrect frequency reading because of decimal point
; location change.  14.500.1 MHz --> 8th character must be cleared!
; With 10 divider:  145.001 MHz      (incorrectly 145.0011)
; Also address 0Bh must be cleared to prevent text MHzz
;-------------------------------------------------------------------------- 
            movf       USER_EEADR,0  ; Do it only if bit 3 state has been
            andwf      PORTB,0       ; changed compared to the previous   
            movwf      USER_EEDATA   ; value.
            btfsc      USER_EEDATA,3 ; Is RB3 state changed?
            goto       cursor_cntr   ; No, pass next!

            movlw      7           ; locate cursor to address 07h (8th ch)
 	    iorlw      080h        ; Function set
	    call       PutCMD      ; Position cursor leftmost on second row
            call       Busy        ; Wait until positioned (takes 40 us)
            movlw      ' '         ; Clear previous character 
            call       PutCHAR	   ; Display "empty" character

            movlw      00Bh        ; locate cursor to address 00Bh (12th ch)
 	    iorlw      080h        ; Function set
	    call       PutCMD      ; Position cursor leftmost on second row
            call       Busy        ; Wait until positioned (takes 40 us)
            movlw      ' '         ; Clear previous character
            call       PutCHAR	   ; Display "empty" character
;-------------------------------------------------------------------------- 
cursor_cntr
            movlw      LINE0
   	    iorlw      080h        ; Position cursor leftmost on first line
	    call       PutCMD
            call       Busy        ; Wait until positioned (takes 40 us)

	    movf       PORTB
            movwf      USER_EEADR  ; This flag is used as temporary variable
 	                           ; for checking the same address read only
                                   ; once, if no changes for address. 
LEDCycle    movlw      LED0        ; LED0 -> W
            addwf      LEDIndex,W  ; LED1 + LEDIndex -> W

            movwf      FSR         ; W -> FSR
            movf       INDF,W      ; LED(0..6) -> W

;---------------------------------------------------------------------------
; Following tests the 10 MHz digit value. If zero then not displayed.
; E.g.     0 3.6 9 4.0 0 MHz --> 3.6 9 4.0 0 MHz
; LEDIndex 6 5 4 3 2 1 0  value according to digit
;---------------------------------------------------------------------------
            btfsc      STATUS,Z
            goto       Clear_zero
            iorlw      030h
            call       PutCHAR	   ; Display character
            goto       Next_test

Clear_zero
	    movlw      6           ; Is the LEDIndex value 6?
	    subwf      LEDIndex,W
            btfss      STATUS,Z    ; If ZERO is set, it is 6
            goto       Zero_ok

            movlw      ' '         ; This is instead of '0'
            call       PutCHAR	   ; Display character
            goto       Next_test
Zero_ok
            movlw      '0'         ; 
            call       PutCHAR	   ; Display character '0'
	    	
Next_test
;--------------------------------------------------------------------
; If frequency lock function has been activated by 00Bh parameter,
; decimal point movement is controlled only by EE 00Ch, not with RB3.
;--------------------------------------------------------------------
            btfss      Status_word,2 ; Freq lock function enabled = 1
            goto       No_freq_lock
	    movlw      00Ch          ; Test the 10 divider 
            movwf      USER_EEADR  
            call       EE_read
	    btfss      STATUS,Z      ; If zero flag=1 then 10 divider 
            goto       Two_to_w 
            bsf        Status_word,7 ; Ten divider sel. by parameter 
            goto       Ten_divider
No_freq_lock
;--------------------------------------------------------------------
; This tests the additional 10 divider and therefore controls the
; position of decimal point according to RB3 selection.
; E.g. f=145.500.0 / 10 = 14.550.00 --> decimal point move 145.500.0
;--------------------------------------------------------------------
	    btfsc      PORTB,3     ; If 0 then no 10 divider
            goto       Ten_divider ; 
Two_to_w    bcf        Status_word,7 ; No ten divider sel. by parameter
            movlw      2           ; test for decimal point
            movwf      Help2 
            goto       Dec_point
Ten_divider
            movlw      1           ; test for decimal point
            movwf      Help2 	    
;--------------------------------------------------------------------
; Test for combination of 6 digits and 10 divider -> no need to
; display second decimal point --> skip to Second_dot_test.
; (e.g. 145.150. MHz -> 145.150 MHz )
;--------------------------------------------------------------------
;--------------------------------------------------------------------------
; This tests the 10Hz or 100 Hz to be displayed as a last digit
;--------------------------------------------------------------------------
	    movlw      00Ah        ; Test the number of digits (6 or 7)
            movwf      USER_EEADR  
            call       EE_read
	    btfss      STATUS,Z    ; If 1 then 7 digits 
            goto       Second_dot_test  ; Skip second decimal point print 
;--------------------------------------------------------------------------
Dec_point
 	    movf       Help2,0     ; Help2 --> w
            subwf      LEDIndex,W
            btfss      STATUS,Z
            goto       Second_dot_test
            movlw      '.'         ; this can be ' ' or ',' ......
            call       PutCHAR	   ; Display character

Second_dot_test
;--------------------------------------------------------------------
; **** 10 Divider test ****
; This tests first the additional 10 divider HW and therefore 
; controls the position of decimal point according to RB3 selection.
; E.g. f=145.500.0 / 10 = 14.550.00 --> decimal point move 145.500.0
;
;        f = 1 4 5.5 0 0.0 MHz
; LEDIndex = 6 5 4 3 2 1 0 value in digit
;--------------------------------------------------------------------
            btfss      Status_word,2 ; Freq lock function enabled = 1
            goto       RB3_test
            btfsc      Status_word,7 ;1=Ten divider sel. by parameter 
            goto       Ten_divider2
            goto       Five_to_w
RB3_test
	    btfsc      PORTB,3     ; If 0 then no 10 divider
            goto       Ten_divider2 ; 
Five_to_w
            movlw      5           ; test for decimal point
            goto       Dec_point2
Ten_divider2
            movlw      4           ; test for decimal point
;--------------------------------------------------------------------
Dec_point2
            subwf      LEDIndex,W
            btfss      STATUS,Z
            goto       sixth_mark
            movlw      '.'         ; this can be ' ' or ',' ......
            call       PutCHAR     ; Display character

sixth_mark
	    movlw      9           ; Test the display type 1 x 16 LCD
            movwf      USER_EEADR  
            call       EE_read
	    btfss      STATUS,Z    ; If 0 then 1x16 LCD
            goto       NoDot       ; other type than 1x16 LCD
	    
;-------------------------------------------------------------------------
; 1x16 LCD 9th character control. This is passed with other LCD types
;-------------------------------------------------------------------------
            movlw      1           ; 1x16 LCD  
            subwf      LEDIndex,W
            btfss      STATUS,Z
            goto       NoDot
; According to 1x16 LCD the 9th character address is 040h (LINE1 = 040h)	    	    	
            movlw      LINE1       ; continue at right half of display
 	    iorlw      080h        ; Function set
	    call       PutCMD      ; Position cursor leftmost on first line
            call       Busy        ; Wait until positioned (takes 40 us)
;--------------------------------------------------------------------------

NoDot
            decfsz     LEDIndex,F
            goto       LEDCycle    ; continue with next number

;--------------------------------------------------------------------------
; This tests the 10Hz or 100 Hz to be displayed as a last digit
;        f = 1 4 5.5 0 0.0 MHz
; LEDIndex = 6 5 4 3 2 1 0
;--------------------------------------------------------------------------
	    movlw      00Ah        ; Test the number of digits (6 or 7)
            movwf      USER_EEADR  ; Read it from EEPROM address 0Ah.
            call       EE_read
	    btfss      STATUS,Z    ; If 1 then 7 digits 
            goto       MHz_text    ; If 6 digits. LEDIndex 0 not displayed.
;--------------------------------------------------------------------------

            movlw      LED0        ; LED0 -> W
            addwf      LEDIndex,W  ; LED0 + LEDIndex -> W

            movwf      FSR         ; W -> FSR
            movf       INDF,W      ; [FSR] -> W

            iorlw      030h
            call       PutCHAR	   ; Display character
MHz_text
            movlw      ' '
            call       PutCHAR	   ; Display character
            movlw      'M'
            call       PutCHAR	   ; Display character
            movlw      'H'
            call       PutCHAR	   ; Display character
            movlw      'z'
            call       PutCHAR	   ; Display character

;-------------------------------------------------------------------------
; Frequency lock control indicates:
;           "L" = Locked if Status_word bit 0 is set
;           " " = none for out of lock, typically if VFO reference changed 
; Function is activated by EEPROM 0Bh = 0 and disabled when >0
;-------------------------------------------------------------------------
            btfss      Status_word,2  ; If bit set -> freq lock enabled
            goto       Result_end     ; No +/- for LCD
            movlw      ' '            ; One space after MHz text
            call       PutCHAR	      ; Display character

            btfss      Status_word,0  ; Is the Locked bit set?
            goto       Display_unlocked ;   
            btfsc      PORTB,2        ; Is RB2 freezing activated?
            goto       Display_F
Display_Lock
            movlw      'L'            ; Load letter 'L'
            call       PutCHAR	      ; Display character
            goto       Result_end
Display_F  
            movlw      'F'            ; Load letter 'L'
            call       PutCHAR	      ; Display character
            goto       Result_end
            
Display_unlocked                      ; Sampling or Unlocked state
            movlw      ' '            ; None indicates unlocked situation
            call       PutCHAR	      ; Display character
Result_end
            movlw      LINE0
	    iorlw      080h           ; Function set
	    call       PutCMD

;-------------------------------------------------------------------------
; It is time to prepare new measuring cycle
;-------------------------------------------------------------------------

            clrf       TimerH
            clrf       TMR0
            nop                    ; it is SUGGESTED...
            nop

            clrf       LEDIndex

            movlw      T1          ; set initial counter values
            movwf      R1
            movlw      T2
            movwf      R2
            movlw      T3
            movwf      R3

            clrf       INTCON      ; global INT disable, TMR0 INT disable
                                   ; clear TMR0 overflow bite

; ------------------------------------------------------------------------
; Start measurement:  RA3 + RA4 set input
; ------------------------------------------------------------------------

            movlw      b'00010000' ; all ports set L, RA4 set H
            movwf      PORTA

            bsf        STATUS,RP0
            movlw      b'00011111' ; RA0..RA4 input
            movwf      TRISA
            bcf        STATUS,RP0

; -------------------------------------------------------------------------
; It is opened now...
; -------------------------------------------------------------------------

Cycle       btfss      INTCON,2   ; 1   Test for TMR0 overflow
            goto       Nothing    ; 3
            incf       TimerH,F   ; 3
            bcf        INTCON,2   ; 4
            goto       Nxt        ; 6

Nothing     nop                   ; 4
            nop                   ; 5
            nop                   ; 6

Nxt         decfsz     R1,F       ; 7
            goto       Cycle      ; 9
            movlw      T1         ; 9*T1

            movwf      R1         ; 9*T1+1
            decfsz     R2,F       ; 9*T1+2
            goto       Cycle      ; 9*T1+4
            movlw      T2         ;(9*T1+4)*T2

            movwf      R2         ;(9*T1+4)*T2+1
            decfsz     R3,F       ;(9*T1+4)*T2+2
            goto       Cycle      ;(9*T1+4)*T2+4

          bcf        PORTB,0          ; Clear outputs to get long pulse
          bcf        PORTB,3          ; for frequency correction.

; ------------------------------------------------------------------------
; Final test for TMR0 overflow
; ------------------------------------------------------------------------

            movlw      T4         ;((9*T1+4)*T2+4)*T3
            movwf      Help       ;((9*T1+4)*T2+4)*T3+1

Cycle2      btfss      INTCON,2   ; 1
            goto       Not2Do     ; 3
            incf       TimerH,F   ; 3
            bcf        INTCON,2   ; 4
            goto       Nx         ; 6

Not2Do      nop                   ; 4
            nop                   ; 5
            nop                   ; 6

Nx          decfsz     Help,F     ; 7
            goto       Cycle2     ; 9
;           nop                   ; ((9*T1+4)*T2+4)*T3+1+9*T4+Z
;           nop                   ; Z times fine tuning nops
;-------------------------------------------------------------------------
; Fine tuning loops set by EEPROM parameters 
;-------------------------------------------------------------------------
            movf       EE_Fine1,0
            movwf      Help      

LCycleFine1 decfsz     Help,F      ; 1 Fine tuning loop 1 set by EEPROM
            goto       LCycleFine1 ; 3 

            movf       EE_Fine2,0
            movwf      Help      
LCycleFine2
            nop                    ; 1  
            decfsz     Help,F      ; 2 Fine tuning loop 2 set by EEPROM
            goto       LCycleFine2 ; 4
            
; ------------------------------------------------------------------------
; Stop the measurement
; ------------------------------------------------------------------------
            clrw                   ; 1
            movwf      PORTB       ; 2
            movlw      b'00010000' ; 3   RA0..RA3 = 0
            movwf      PORTA       ; 4   W -> PORTA
                                   ; ((9*T1+4)*T2+4)*T3+1+9*T4+Z+4
            bsf        STATUS,RP0  ;
            movlw      b'00010111' ;     RA3 output Low
            movwf      TRISA       ;     RA0..RA2,RA4 input
            bcf        STATUS,RP0  ;

            btfsc      INTCON,2    ; really final check
            incf       TimerH,F
            bcf        INTCON,2

; ------------------------------------------------------------------------
; Analyse precounter and store counted value in registers
; ------------------------------------------------------------------------

            movf       TMR0,W
            movwf      MidB        ; TMR0 -> MidB

            movf       TimerH,W
            movwf      HigB        ; TimerH -> HigB

            clrf       TEMP

CountIt     incf       TEMP,F
            bsf        PORTA,3     ; _| false impulz
            bcf        PORTA,3     ;    |_

            bcf        INTCON,2
            movf       TMR0,W      ; actual TMR0 -> W
            subwf      MidB,W
            btfsc      STATUS,Z
            goto       CountIt
            incf       TEMP,F
            comf       TEMP,F
            incf       TEMP,F
            incf       TEMP,W
            movwf      LowB
; ------------------------------------------------------------------------
; Frequency shift according value on RA2 pin or direct frequency display
; Both routines are simplified Subc24 and Addc24 routines
; ------------------------------------------------------------------------
            movlw      b'00010000'
            movwf      PORTA

            bsf        STATUS,RP0
            movlw      b'00000100' ; set RA2 as input
            movwf      TRISA
            bcf        STATUS,RP0

; ************************************************************************
; Frequency lock procedure stars here.
; ************************************************************************
            btfss      Status_word,2  ; If bit set -> freq lock enabled
            goto       end_of_lock    ; otherwise function is skipped 
; ------------------------------------------------------------------------
; Measured frequency is saved for temporary registers for Sub24bit. 
; ------------------------------------------------------------------------
            btfsc      PORTB,2        ; Is bit 2 true? Means passing
            goto       end_of_lock    ; Skip the freq lock if RB2 true
            movf       LowB,W
            movwf      LED4
            movf       MidB,W
            movwf      LED5
            movf       HigB,W
            movwf      LED6
;-------------------------------------------------------------------------
; RB0 and RB3 are set to outputs only in the Freq_lock mode 
;-------------------------------------------------------------------------
            bsf        STATUS,RP0
	    movlw      b'11110110' ; Set port RB0 and RB3 to output
	    andwf      TRISB,W     ; Other bits as before
	    movwf      TRISB
            bcf        STATUS,RP0
;-------------------------------------------------------------------------
Lock_bit_check
            btfsc      Status_word,0 ; Is Locked bit set?
            goto       Lock_function ; Yes goto lock procedure

            movlw      00Dh          ; Read user's set delay time from
            movwf      USER_EEADR    ; EEPROM
            call       EE_read

            subwf      Delay_counter,W
            btfss      STATUS,Z      ; Is delay counter zero?
            goto       Inc_delay_counter      
            btfsc      Status_word,1 ;  Is Sample bit set?
            goto       cont_sample
            goto       Save_sample
Inc_delay_counter
            incf       Delay_counter,F
            goto       end_of_lock
; ------------------------------------------------------------------------
; Save freq act for freq ref
; ------------------------------------------------------------------------
Save_sample
            movf       LowB,W
            movwf      LowB_ref      ; Frequency actual to reference 
            movf       MidB,W
            movwf      MidB_ref      ; Frequency actual to reference 
            movf       HigB,W
            movwf      HigB_ref      ; Frequency actual to reference 
            bsf        Status_word,1 ; Set sample bit
cont_sample        
            call       Preparation
            call       Sub24bit      ; Sub Sample - Freq_ref (24 bit)
            call       Zero_check 
            btfss      Status_word,6 ; Is result zero?    
            goto       Underflow_check 
Inc_sample_counter
            movlw      00Eh          ; Read user's set sampling time from
            movwf      USER_EEADR    ; EEPROM
            call       EE_read
            subwf      Sample_counter,W
            btfss      STATUS,Z  
            goto       Sample1
            bsf        Status_word,0  ; Set Lock bit
            bcf        Status_word,1  ; Clear sample bit
            clrf       Sample_counter ; Clear sample counter for Lock_func 
            goto       end_of_lock                               
Underflow_check
            movlw      2         ; Preparation for 24 bit add or sub
            movwf      LED0      ; window checking of freq ref sample
            clrf       LED1      ; LED4...6 includes still last sub result 
            clrf       LED2      ; 2 == 20Hz

            btfss      Status_word,4 ; Carry status after subtraction 
            goto       Neg_val_win            
Pos_val_win
            movlw      0FDh      ; FFFFFDh
            movwf      LED0
            movlw      0FFh
            movwf      LED1
            movlw      0FFh
            movwf      LED2
            call       Add24bit
            btfsc      Status_word,4    ; If overflow -> out of window
            goto       Clear_delay_time_counter           
            goto       Inc_sample_counter
Neg_val_win
            call       Add24bit
            btfsc      Status_word,4  ; If overflow -> within the window
            goto       Inc_sample_counter

Clear_delay_time_counter              ; Frequency changed!
            clrf       Delay_counter  ; Clear delay counter 
            bcf        Status_word,1  ; Clear sample bit
            goto       end_of_lock
; ------------------------------------------------------------------------
; Freq Lock function routine part starts here. Sampling of reference
; is now over and Freq_ref set. Integrator control is already released
; now. This routine controls the RB0 output:
;                              1 == Charging integrator capacitor
;                              0 == Discharging  - " - 
; ------------------------------------------------------------------------
; Status_word  bit 0 = Locked       Frequency lock, "L" is displayed
;              bit 1 = Sampling     Reference is being sampled
;              bit 2 = Freq_lock_en Frequency lock function enabled (0Bh)
;              bit 3 = -
;              bit 4 = Carry Status Carry status after the Sub or Add
;              bit 5 = Freq +/-20Hz Frequency actual is within +-20 Hz
;              bit 6 = Zero result  Result of 24 bit Subtraction is zero   
;              bit 7 = Ten Divider  Ten divider function enabled
; ------------------------------------------------------------------------
Lock_function
            bcf        Status_word,5  ; Clear bit Freq_within +/-20 Hz.
            call       Preparation    ; Transfer freq act to LED0...LED2
            call       Sub24bit       ; Freq_sample - Freq_ref (24 bit)
            call       Zero_check     ; Zero result checking.
            btfsc      Status_word,6  ; Zero result?      
            goto       Lock_state
Not_zero
            movf       LED4,W
            movwf      TEMP
            btfsc      Status_word,4    ; C=0 if underflow
            goto       Clear_RB0   ; Bit4 clear --> Positive result         
Set_RB0                           ; Negative subtraction result: act < ref 
            bsf        PORTB,0   ; Set RB0
            bcf        PORTB,3   ; Clear RB3
; ------------------------------------------------------------------------
            incf       TEMP,F    ; TEMP = LowB + 1 of sub result
            btfsc      STATUS,Z  ; Is zero? Means 10 Hz below reference.
            goto       Set_SW_bit7_RB0
            incf       TEMP,F    ; TEMP = LowB + 2 of sub result
            btfsc      STATUS,Z  ; Is zero? Means 20 Hz over reference.
Set_SW_bit7_RB0
            bsf        Status_word,5   ; Set bit Freq_within +/- 20 Hz.
; ------------------------------------------------------------------------
            clrf       LED2      ; Window checking of freq act 
            clrf       LED1      ; LED4...6 incl. still latest sub result
            movlw      00Ah      ; 0Ah == 100 Hz for window width
            movwf      LED0      ; Preparation for 24 bit add or sub.
; ------------------------------------------------------------------------
            call       Add24bit  ; Add 00000Ah for (Freq_act - Freq_ref)        
            btfsc      Status_word,4  ; Overflow = 1  
            goto       Lock_state ; C = 1 -> overflow,within the window
; ------------------------------------------------------------------------
; Sample counter checking
; ------------------------------------------------------------------------
Sample_counter_checking                 ; C = 1 -> Outside of window
            movlw      00Eh          ; Read user's set sampling time from
            movwf      USER_EEADR    ; EEPROM
            call       EE_read
            subwf      Sample_counter,W
            btfsc      STATUS,Z
            goto       Set_out_of_lock
            incf       Sample_counter,F ;  
            goto       end_of_lock
; ------------------------------------------------------------------------
; Frequency actual is out of lock situation
; ------------------------------------------------------------------------
Set_out_of_lock
            bcf        Status_word,0  ; Clear Lock bit
            clrf       Sample_counter
            clrf       Delay_counter
            goto       end_of_lock
Clear_RB0                        ; Positive subtraction result act > ref
            bcf        PORTB,0   ; Clear RB0
            bsf        PORTB,3   ; Set RB3
; ------------------------------------------------------------------------
            decf       TEMP,F    ; TEMP = LowB - 1 of sub result
            btfsc      STATUS,Z  ; Is zero? Means 10 Hz over reference.
            goto       Set_SW_bit7_RB3
            decf       TEMP,F    ; TEMP = LowB - 2 of sub result
            btfsc      STATUS,Z  ; Is zero? Means 20 Hz over reference.
Set_SW_bit7_RB3
            bsf        Status_word,5   ; Set bit Freq_within +/- 20 Hz.
; ------------------------------------------------------------------------
            movlw      0F6h      ; FFFFF6h
            movwf      LED0
            movlw      0FFh
            movwf      LED1
            movlw      0FFh
            movwf      LED2
; ------------------------------------------------------------------------
            call       Add24bit  ; Add FFFFF6h for (Freq_act - Freq_ref)        
            btfsc      Status_word,4  ; If overflow then out of window
            goto       Sample_counter_checking
; ------------------------------------------------------------------------
;  Frequency is still in locked state!! Great!!!
; ------------------------------------------------------------------------
Lock_state                       
            clrf       Sample_counter
            goto       end_of_lock   
Sample1
            incf       Sample_counter,F
end_of_lock
; ------------------------------------------------------------------------
            call       Offset_check ; Load selected Display offset values
; ------------------------------------------------------------------------
Test_counter_mode
            movlw      6           ; Test the direct freq EE-parameter
            movwf      USER_EEADR  ; It passes RA2, RB1, RB2 functions
            call       EE_read
	    btfsc      STATUS,Z    ; If 0 then direct frequency mode
            goto       MFEnd       ; EE address 06h data value is zero

            btfsc      Status_word,2 ; If Freq_lock enabled, skip direct
                                   ; or Display offset alternative func.
            goto       Preparation_1 ; RB2 works now only with freezing
                                     ; function when freq lock enabled. 
            btfsc      PORTB,E     ; If RB2 = 1 then direct frequency
            goto       MFEnd
; ------------------------------------------------------------------------
;  First preparation of registers LED2...0 for MFAdd or MFSub
; ------------------------------------------------------------------------
Preparation_1
            movf       HigB,W      ; Freq act to LED2, LED1 and LED0
            movwf      LED2
            movf       MidB,W
            movwf      LED1
            movf       LowB,W
            movwf      LED0
; ------------------------------------------------------------------------
            btfsc      PORTA,E     ; If RA2 = 1 then adds the RF
                                   ; If RA2 = 0 then subs the RF
            goto       MFAdd
SubMF
; ------------------------------------------------------------------------
;  24 bit subtraction according to Display offset selection
; ------------------------------------------------------------------------
            call       Sub24bit        ; Do the subtraction! 
; ------------------------------------------------------------------------
            btfsc      Status_word,4   ; Is the carry set? (underflow)
; ------------------------------------------------------------------------
; If subtracted > subtractor --> result = negative. Therefore they must be
; swapped before subtraction. LED6 <-> LED2, LED5 <-> LED1, LED4 <-> LED0.
; Example: IF = 2050 KHz, Received freq = 3500 kHz, Osc. freq = 5550 kHz
; 2050 kHz - 5550 kHz = - 3500, therefore terms must be swapped, because
; 5550 kHz - 2050 kHz = 3500 kHz
; ------------------------------------------------------------------------
            goto       Save_results

            call       Offset_check ; Restore original values after first
                                    ; subtraction 
            movf       LED6,W       ; Swap the HighB Freq act and Offset
            movwf      TEMP         ; TEMP = LED6
            movf       LED2,W
            movwf      LED6
            movf       TEMP,W       
            movwf      LED2         ; Move TEMP to LED2

            movf       LED5,W       ; Swap the MidB Freq act and Offset
            movwf      TEMP         ; TEMP = LED5
            movf       LED1,W
            movwf      LED5
            movf       TEMP,W
            movwf      LED1

            movf       LED4,W       ; Swap the LowB Freq act and Offset
            movwf      TEMP         ; TEMP = LED4
            movf       LED0,W
            movwf      LED4
            movf       TEMP,W
            movwf      LED0
; ------------------------------------------------------------------------
            call       Sub24bit     ; Subtract again after swapping!!
; ------------------------------------------------------------------------
            goto       Save_results
MFAdd
; ------------------------------------------------------------------------
;  24 bit addition function according to Display offset selection
; ------------------------------------------------------------------------
            call       Add24bit    ; Do the addition! 
; ------------------------------------------------------------------------
Save_results
            movf       LED6,W      ; Save results back to registers
            movwf      HigB
            movf       LED5,W
            movwf      MidB
            movf       LED4,W
            movwf      LowB
; ------------------------------------------------------------------------
MFEnd
            goto       Entry       ;  start new cycle
; ------------------------------------------------------------------------
            end
