;*****************************************************************************************
;
;       PROGRAM:                SYNTH.ASM & INCLUDE FILE, SETTINGS.DAT
;
;       ASSEMBLER:              MPASMWIN v02.30
;
;       AUTHOR:                 S. JONES         EMAIL stevejones@picknowl.com.au
;
;
;       REVISION HISTORY:
;
;       VERSION:        DATE            COMMENTS
;       1.00            05-02-2000      FIRST RELEASE.
;
;		1.00_628		23.Juli-2005    Ein Versuch
;
;
;		Umgebaut von Tomtom DL1MFK
;		Der PIC 16F628 wird nur mit dem internen RC-Oszillator betrieben.
;		das PLL.DAT-File und das PLL628.ASM-File ist so angepasst, daß eine
;		Schrittweite von 125kHz entsprechend dem SP5055 (mit 4MHz Quarz) eingehalten wird
;		es ist (noch) nicht möglich mit dieser Programm-Version den Faktor auf 4
;		zu setzen und damit 10 GHz anzuzeigen.


;
;       DESCRIPTION:
;
;       Programmable synthesizer using a PIC 16F84, TSA5511 PLL and a 2x16 lcd.
;       RC clock for pic (22pF & 5.6K = approx 3.6MHz, = 900KHz instruction cycle time)
;
;       PLL frequency range of 50 to 1400MHz for TX and RX.
;       Frequency Steps of 50Khz with a crystal reference of 3.2MHz.
;       RX freq display range. 0 to 1900MHz.
;       TX freq display range. 0 to 1900MHz.
;       RX Display to VCO offset +/-500MHz.
;       TX Display to VCO offset +/-500MHz.
;       Display multiplier. (Multiplies all frequency displays on the lcd by N)
;
;       User adjustable settings. ( Via setup screens. Accessible if shift
;                                   button is pressed during power up )
;       1)      Display multiplier.     1 to 20
;       2)      Minimum RX display.     0 to 1900MHz
;       3)      Maximum RX display.     0 to 1900MHz
;       4)      RX VCO offset.       -500 to  500MHz
;       5)      Minimum TX display.     0 to 1900MHz
;       6)      Maximum TX display.     0 to 1900MHz
;       7)      TX VCO offset.       -500 to  500MHz
;
;       GENERAL COMMENTS
;
;       A SINGLE PRESS OF THE FREQ + OR FREQ - BUTTONS WILL CHANGE THE PLL BY ONE STEP OF
;       50KHz.
;       HOLDING A BUTTON DOWN FOR A FEW SECONDS WILL CHANGE THE PLL BY MULTIPLE STEPS
;       OF 100KHz.
;
;       IF THE SHIFT BUTTON IS HELD, A SINGLE PRESS OF THE FREQ + OR FREQ - BUTTONS WILL
;       CHANGE THE PLL BY ONE STEP OF 1MHz.
;       IF THE SHIFT BUTTON IS HELD, HOLDING A BUTTON DOWN FOR A FEW SECONDS WILL CHANGE
;       THE PLL BY MULTIPLE STEPS OF 10MHz.
;
;       IF THE PTT LEAD CHANGES STATE WHILE MODIFYING THE FREQUENCY, THE FREQ + AND
;       FREQ - BUTTONS MUST BE RELEASED FOR ONE SECOND BEFORE THE FREQUENCY CAN BE
;       MODIFIED AGAIN. (IE, PREVENTS CHANGING TX FREQ INADVERTANTLY IF THE PTT LEAD IS
;       OPPERATED WHILE SETTING THE RX FREQUENCY)
;
;       ANY CHANGES TO RX OR TX FREQUENCY ARE UPDATED IN EEPROM 2 SECONDS AFTER RELEASE OF
;       THE FREQ + AND FREQ - BUTTONS. ONLY BYTES THAT HAVE CHANGED ARE REPROGRAMMED.
;
;       TO GAIN ACCESS TO THE SETUP SCREENS, HOLD THE SHIFT BUTTON DOWN DURING POWER-UP,
;       THEN A DOUBLE CLICK OF THE SHIFT BUTTON WILL TAKE YOU TO THE SETUP SCREENS.
;
;       WHILE IN THE SETUP SCREENS, IF THERE ARE NO BUTTONS PRESSED FOR 10 SECONDS, YOU
;       WILL BE RETURNED TO THE MAIN DISPLAY.
;
;       CHANGES MADE TO THE SETUP, ARE UPDATED IN EEPROM UPON RETURN TO THE MAIN DISPLAY.
;
;       TO MIMIMISE DISTERBANCE TO THE PLL, THE DIVIDER INFO THAT SETS THE PLL, IS SENT
;       BY THE PIC TO THE PLL ONLY IF THE FREQUENCY NEEDS TO BE CHANGED.
;       (IE OPPERATION OF THE PTT LEAD OR DURING MANUAL FREQUENCY CHANGES)
;
;       WHEN THE PLL FREQUENCY IS CHANGED, IT IS ASSUMED THAT THE PLL IS UNLOCKED. THE PIC
;       CHECKS THE PLL STATUS CONTINUOUSLY UNTIL THE PLL LOCKS. ONCE LOCKED, STATUS CHECKS
;       ARE MADE EVERY SECOND. IF THE PLL GOES OUT OF LOCK, CONTINUOUS CHECKS ARE STARTED
;       AGAIN. (TO DISABLE THE CHECKS EVERY SECOND, CHANGE THE 'LOCK_TM' EQUATE TO 255)
;
;       IF AN ATTEMPT IS MADE TO SET THE PLL BELOW 50MHz, 'UNLOCKED' WILL BE DISPLAYED
;       ON THE LCD. AND THE PLL WILL BE SET TO 50MHz.
;       (IE, IF RX DISPLAY = 400MHz AND THE RX VCO OFFSET IS -500MHz)
;       IF AN ATTEMPT IS MADE TO SET THE PLL ABOVE 1400MHz, 'UNLOCKED' WILL BE DISPLAYED
;       ON THE LCD. AND THE PLL WILL BE SET TO 1400MHz.
;       (IE, IF TX DISPLAY = 1000MHz AND THE TX VCO OFFSET IS 500MHz)
;
;       THE LCD USED WITH THIS PROGRAM SHOULD 2 LINE x 16 CHARACTER.
;
;       TWO METHODS OF MAKING SURE THE LCD IS NOT BUSY ARE AVALIABLE.
;       1) READING THE BUSY FLAG FROM THE LCD.
;       2) WAITING LONGER THAN THE MAXIMUM LCD EXECUTION TIME.
;       IF THE LCD DOES NOT HAVE A R/W PIN, SET THE "LCD_USES_RW" EQUATE TO FALSE. THEN
;       THERE WILL BE NO BUSY CHECKS MADE ON THE LCD. (SLIGHTLY SLOWER)
;       A LCD DISPLAY THAT HAS THE R/W PIN CAN USE EITHER METHOD. SETTING THE
;       "LCD_USES_RW" EQUATE TO TRUE IS SLIGHTLY FASTER BUT USES MORE CODE SPACE.
;       THE SLOWER OPPERATION IS NOT NOTICABLE IN THIS APPLICATION.
;
;*****************************************************************************************

;*****************************************************************************************
;       REGISTER DEFINITIONS
;*****************************************************************************************

W               EQU             0
F               EQU             1

INDF            SET             0X00
TMR0            SET             0X01
PCL             SET             0X02
STATUS          SET             0X03
FSR             SET             0X04
PORTA           SET             0X05
PORTB           SET             0X06
INTCON          SET             0X0B
OPTION_REG      SET             0X01
CMCON           EQU             H'001F'
EEDATA          EQU     		H'001A'
EEADR           EQU     		H'001B'
EECON1          EQU     		H'001C'
EECON2          EQU     		H'001D'
PIR1            EQU     		H'000C'
GIE             EQU     		H'0007'
#DEFINE         CARRY           STATUS,0
#DEFINE         ZERO            STATUS,2
#DEFINE         RP0             STATUS,5
#DEFINE         T0IF            INTCON,2
#DEFINE         RD              EECON1,0
#DEFINE         WR              EECON1,1
#DEFINE         WREN            EECON1,2
#DEFINE         WRERR           EECON1,3
#DEFINE         EEIF            PIR1,7


;       CONFIGURATION BITS
_WDT_OFF                     EQU     H'3FFB'
_LVP_OFF                     EQU     H'3F7F'
_INTRC_OSC_NOCLKOUT          EQU     H'3FFC'



        PROCESSOR       16F628                   ;SELECT PROCCESOR, AND ASSEMBLER OPTIONS.
        LIST            F=INHX8M,N=0
        RADIX           DEC
        __CONFIG        _INTRC_OSC_NOCLKOUT & _WDT_OFF & _LVP_OFF
						;INT OSCILLATOR (4MHz), NO WATCHDOG, NO LOW VOLTAGE PROGRAMMING

;*****************************************************************************************
;               GENERAL DEFINITIONS
;*****************************************************************************************

#DEFINE         SCL             PORTA,0         ;I2C CLOCK O/P LINE.
#DEFINE         SDA             PORTA,1         ;I2C DATA O/P LINE.
#DEFINE         PTT	        	PORTA,2         ;PTT INPUT PIN.  0 = TX FREQ, 1 = RX FREQ
#DEFINE         TX_EN           PORTA,3         ;TX ENABLE OUTPUT PIN. 0 = ENABLED.

#DEFINE         RW	        	PORTA,7         ;Bei Verwendung von RW-Pin am LCD

#DEFINE         BUTTON_P        PORTA,4         ;FREQ + BUTTON.
#DEFINE         BUTTON_N        PORTB,0         ;FREQ - BUTTON.
#DEFINE         BUTTON_S        PORTB,1         ;SHIFT BUTTON.

#DEFINE			E    	        PORTB,2			;LCD Enable		0=aus	1=an
#DEFINE 		RS      		PORTB,3			;LCD Register Select	0=CMD	1=Daten

#DEFINE         LEADING_FLAG    FLAGS,0         ;LEADING ZERO INDICATOR.
#DEFINE         NEGATIVE        FLAGS,1         ;NUMBER TO DISPLAY IS NEGATIVE.
#DEFINE         PLL_VALID       FLAGS,2         ;PLL DIVIDER NUMBER IS VALID.
#DEFINE         TIMEOUT         FLAGS,3         ;IF DISP TIMEOUT SET, GO TO MAIN DISPLAY.
#DEFINE         EEPROM_UPDATE   FLAGS,4         ;IF SET, UPDATE THE EEPROM.
#DEFINE         UNLOCKED        FLAGS,5         ;IF SET ENABLE ACCESS TO SETUP DISPLAYS.
#DEFINE         PTT_COPY        FLAGS,6         ;COPY OF CURRENT PTT STATE.
#DEFINE         PTT_CHANGED     FLAGS,7         ;SET IF PTT HAS CHANGED.
#DEFINE         MAX_MIN_MOD     FLAG2,1         ;SET IF MAXIMUM OR MINIMUM CHANGED ARG.
#DEFINE         PRESSED         RESULT,0        ;RESULT FLAG FROM BUTTON PRESSED CALL.

;*****************************************************************************************
;               GENERAL EQUATES
;*****************************************************************************************

RESETVECTOR     EQU             H'00'           ;PIC16F84 RESET VECTOR.
INTVECTOR       EQU             H'04'           ;PIC16F84 INTERUPT VECTOR.
TRUE            EQU             H'FF'
FALSE           EQU             H'00'

LCD_USES_RW     EQU             FALSE           ;TRUE   IF USING LCD WITH R/W LEAD.
                                                ;FALSE  IF USING LCD WITH NO R/W LEAD.

DP              EQU             H'A5'           ;LCD DECIMAL POINT.
MIN_ON          EQU             5               ;MIN VALID BUTTON PRESSED TIME. (5 x 9mS)
MAX_ON          EQU             80              ;MAX VALID BUTTON PRESSED TIME. (60 x 9mS)
REPEAT          EQU             30              ;REPEAT PERIOD. (30 x 9mS)
MAX_ON_S        EQU             40              ;MAX VALID SHIFT PRESSED TIME. (30 x 9mS)
DOUBLE_TIME     EQU             36              ;DOUBLE SHIFT WITHIN THIS TIME.(25 x 9mS)
TIMEOUT_NUM     EQU             1000            ;DISPLAY TIMEOUT. (1100 x 9mS)
LOCK_TM         EQU             110             ;TIME BETWEEN STATUS CHECKS. (110 x 9mS)
                                                ;255 = NO CHECKS ONCE IN LOCK.
PLL_ADDRESS     EQU             B'11000010'     ;PLL CHIP ADDRESS.
;PLL_ADDRESS     EQU             B'01000000'     ;BUS EXPANDER CHIP ADDRESS.

STEP_SIZE       EQU               125           ;PLL FREQUENCY STEPS = 50KHz
MAX_OFFSET      EQU            500000           ;PLL MAXIMUM TX OR RX OFFSET = 500MHz
PLL_MIN_FREQ    EQU            500000           ;PLL MINIMUM FREQUENCY = 500MHz
PLL_MAX_FREQ    EQU           3000000           ;PLL MAXIMUM FREQUENCY = 3000MHz

TIMEOUT_NUM_X   EQU     0 - TIMEOUT_NUM
TIMEOUT_NUM_H   EQU     HIGH ( H'FFFF' & TIMEOUT_NUM_X )
TIMEOUT_NUM_L   EQU     LOW ( H'FFFF' & TIMEOUT_NUM_X )

PLL_SPAN        EQU     ( PLL_MAX_FREQ - PLL_MIN_FREQ )/ STEP_SIZE
PLL_SPAN_H      EQU     HIGH PLL_SPAN
PLL_SPAN_L      EQU     LOW PLL_SPAN

OFFSET_MAX      EQU     MAX_OFFSET / STEP_SIZE          ;MAXIMUM TX OR RX OFFSET = 500MHz
OFFSET_MAX_H    EQU     HIGH OFFSET_MAX
OFFSET_MAX_L    EQU     LOW OFFSET_MAX

OFFSET_MIN      EQU     0 - (MAX_OFFSET / STEP_SIZE)    ;MINIMUM TX OR RX OFFSET = -500MHz
OFFSET_MIN_H    EQU     HIGH ( H'FFFF' & OFFSET_MIN)
OFFSET_MIN_L    EQU     LOW OFFSET_MIN

PLL_DIV_MAX     EQU     PLL_MAX_FREQ / STEP_SIZE        ;DIVIDER NUM IS INVALID IF GREATER
PLL_DIV_MAX_H   EQU     HIGH PLL_DIV_MAX                ;THAN 1400MHz.
PLL_DIV_MAX_L   EQU     LOW PLL_DIV_MAX

PLL_DIV_MIN     EQU     PLL_MIN_FREQ / STEP_SIZE        ;DIVIDER NUM IS INVALID IF LESS
PLL_DIV_MIN_H   EQU     HIGH PLL_DIV_MIN                ;THAN 50MHz.
PLL_DIV_MIN_L   EQU     LOW PLL_DIV_MIN

DISP_MAX        EQU     (PLL_MAX_FREQ / STEP_SIZE) + OFFSET_MAX
DISP_MAX_H      EQU     HIGH DISP_MAX
DISP_MAX_L      EQU     LOW DISP_MAX                    ;MAXIMUM TX OR RX DISP = 3000MHz.


;*****************************************************************************************
                ORG     H'2100'

                INCLUDE "pll_627.dat"          ;FILE TO SET THE INITIAL PLL & DISPLAY
;                                                FREQ RANGE INTO EEPROM.
;*****************************************************************************************



;*****************************************************************************************
;               RAM VARIABLES 68 BYTES MAX
;*****************************************************************************************

                CBLOCK  H'20'                   ;RAM STARTS AT H'0C'

                COUNT                           ;GENERAL COUNTER VARIABLES.
                COUNT1
                COUNT2
                SAVE_STATUS                     ;FOR INTERUPT SERVIVE ROUTINE.
                SAVE_W_REG                      ;             "
                BUT_P_CNT                       ;BUTTON PRESSED TIME COUNT. (x 9mS)
                BUT_N_CNT                       ;            "
                BUT_S_CNT                       ;            "
                BUT_P_OLD                       ;STATE OF OLD BUTTON COUNT.
                BUT_N_OLD                       ;            "
                BUT_S_OLD                       ;            "
                S_TIMER                         ;TIMER FOR DOUBLE PRESS OF SHIFT BUTTON.
                LOCK_TIMER                      ;TIMER FOR CHECKING PLL STATUS.
                RESULT                          ;RESULT OF A BUTTON CHECK CALL.
                TIMEOUT_H                       ;16 BIT DISPLAY TIMEOUT DOWN COUNTER,
                TIMEOUT_L                       ;RETURN TO MAIN DISPLAY WHEN = 0.
                EEPROM_WRITE                    ;EEPROM WRITE UPDATE DOWN COUNTER.
                FLAGS                           ;8 MISC FLAGS.
                FLAG2                           ;     "
                PLL_STATUS                      ;STATUS BYTE FROM PLL READ.
                TEMP                            ;TEMPORARY STORAGE.
                TEMP_H                          ;       "
                TEMP_L                          ;       "
                MULT_NUM                        ;COPY OF CURRENT SETUP DATA FROM EEPROM.
                RX_DISP_FREQ:2                  ;                  "
                TX_DISP_FREQ:2                  ;                  "
                MIN_RX_DISP_FR:2                ;                  "
                MAX_RX_DISP_FR:2                ;                  "
                RX_VCO_OFFSET:2                 ;                  "
                MIN_TX_DISP_FR:2                ;                  "
                MAX_TX_DISP_FR:2                ;                  "
                TX_VCO_OFFSET:2                 ;                  "
                PLL_OLD:2                       ;PREVIOUS PLL DIVIDER NUMBER.
                ARG                             ;8 BIT BINARY BUFFER (MATHS).
                ARG_H                           ;16 BIT BINARY BUFFER (MATHS).
                ARG_L                           ;          "
                ARG2_H                          ;16 BIT BINARY BUFFER (MATHS).
                ARG2_L                          ;          "
                ARG3_H                          ;16 BIT BINARY BUFFER (MATHS).
                ARG3_L                          ;          "
                RESULT_H                        ;24 BIT BINARY BUFFER (MATHS).
                RESULT_M                        ;          "
                RESULT_L                        ;          "
                NEG_POSITION                    ;LOCATION OF THE - SIGN IN THE DEC BUFFER.
                DEC_0                           ;8 DIGIT DECIMAL BUFFER. MSD
                DEC_1                           ;          "
                DEC_2                           ;          "
                DEC_3                           ;          "
                DEC_4                           ;          "
                DECIMAL_POINT                   ;          "             DECIMAL POINT.
                DEC_5                           ;          "
                DEC_6                           ;          "
                DEC_7                           ;          "             LSD

                ENDC


;*****************************************************************************************
;
;               INITIALISE THE HARDWARE.
;
;*****************************************************************************************

                ORG     RESETVECTOR

                CALL    LONG_DELAY              ;WAIT UNTIL LCD AND PLL HAVE INITIALISED.
                MOVLW   H'21'                   ;POINT TO START OF RAM. (JUST PAST COUNT)
                MOVWF   FSR
                GOTO    CONTINUE                ;JUMP OVER INTERUPT ROUTINES.


;*****************************************************************************************
;               INTERRUPT SERVICE ROUTINE
;
;               SERVICE THE TMR0 INTERUPT (EVERY 9mS)
;
;               INCREMENT THE BUTTON PRESSED COUNTERS IF THE CORROSPONDING BUTTON
;               IS PRESSED & SET THE DISPLAY TIMEOUT & EEPROM WRITE DOWN COUNTER.
;               ELSE SET THE BUTTON COUNTER TO 0.
;
;               DEC THE 8 BIT EEPROM WRITE, DOWN COUNTER. WHEN IT REACHES 0, SET THE
;               EEPROM WRITE FLAG. (INDICATES WHEN TO UPDATE THE EEPROM, IE 2 SECONDS
;               AFTER RELEASING THE FREQ + OR - BUTTON WRITE ANY CHANGES TO EEPROM)
;
;               DECREMENT THE 16 BIT DISPLAY DOWN COUNTER. WHEN IT REACHES 0, SET THE
;               TIMEOUT FLAG. (IF NO BUTTONS HAVE BEEN PRESSED FOR A WHILE. INDICATES
;               WHEN TO RETURN TO THE MAIN DISPLAY)
;
;               DEC THE 8 BIT SHIFT BUTTON DOUBLE PRESS, DOWN COUNTER. WONT DEC PAST 0.
;               (THE FIRST PRESS SETS THE COUNTER. A DOUBLE PRESS IS VALID IF THE SECOND
;               PRESS OCCURS WHILE THE COUNTER IS NON ZERO)
;
;               DEC THE 8 BIT LOCK TIMER, DOWN COUNTER. WONT DEC PAST 0.
;               WHEN EQUAL TO 0 ITS TIME TO CHECK IF THE PLL IS LOCKED.
;*****************************************************************************************

                ORG     INTVECTOR

                BTFSS   T0IF                    ;WAS THE INTERUPT CAUSED BY TMR0 ?
                RETFIE                          ;N. JUST RETURN & RE-ENABLE INTERUPTS.
                MOVWF   SAVE_W_REG              ;Y. SAVE WREG & STATUS REGS.
                SWAPF   STATUS,W
                MOVWF   SAVE_STATUS
                BCF     RP0                     ;MAKE SURE WE ARE ADDRESSING BANK 0.

;-----------------------------------------------------------------------------------------

CHK_BUT_P:      BTFSS   BUTTON_P                ;IS FREQ + BUTTON PRESSED?
                GOTO    INC_P_CNT
                CLRF    BUT_P_CNT               ;N. CLEAR THE COUNT.
                GOTO    CHK_BUT_N
INC_P_CNT:      INCFSZ  BUT_P_CNT,W             ;Y. INC THE COUNT.
                MOVWF   BUT_P_CNT               ;   BUT DONT INC PAST 255.
                CALL    SET_TIMEOUT             ;   SET THE DISPLAY TIMEOUT DOWN COUNTER.
                MOVLW   H'FF'                   ;   SET THE EEPROM WRITE DOWN COUNTER.
                MOVWF   EEPROM_WRITE

;-----------------------------------------------------------------------------------------

CHK_BUT_N:      BTFSS   BUTTON_N                ;IS FREQ - BUTTON PRESSED?
                GOTO    INC_N_CNT
                CLRF    BUT_N_CNT               ;N. CLEAR THE COUNT.
                GOTO    CHK_BUT_S
INC_N_CNT:      INCFSZ  BUT_N_CNT,W             ;Y. INC THE COUNT.
                MOVWF   BUT_N_CNT               ;   BUT DONT INC PAST 255.
                CALL    SET_TIMEOUT             ;   SET THE DISPLAY TIMEOUT DOWN COUNTER.
                MOVLW   H'FF'                   ;   SET THE EEPROM WRITE DOWN COUNTER.
                MOVWF   EEPROM_WRITE

;-----------------------------------------------------------------------------------------

CHK_BUT_S:      BTFSS   BUTTON_S                ;IS SHIFT BUTTON PRESSED?
                GOTO    INC_S_CNT
                CLRF    BUT_S_CNT               ;N. CLEAR THE COUNT.
                GOTO    CHK_TIMEOUT
INC_S_CNT:      INCFSZ  BUT_S_CNT,W             ;Y. INC THE COUNT.
                MOVWF   BUT_S_CNT               ;   BUT DONT INC PAST 255.
                CALL    SET_TIMEOUT             ;   SET THE DISPLAY TIMEOUT DOWN COUNTER.

;-----------------------------------------------------------------------------------------

CHK_TIMEOUT:    MOVF    TIMEOUT_H,W             ;GET THE DISPLAY TIMEOUT.
                IORWF   TIMEOUT_L,W
                BTFSC   ZERO                    ;IS IT = 0?
                GOTO    T_DONE                  ;Y. DONT DO ANYTHING.
                INCFSZ  TIMEOUT_L,F             ;INC LOW BYTE. IS IT = 0 ?
                GOTO    T_DONE                  ;N. ALL DONE.
                INCFSZ  TIMEOUT_H,F             ;Y. INC THE HIGH BYTE. IS IT ALSO = 0 ?
                GOTO    T_DONE                  ;   N. ALL DONE.
                BSF     TIMEOUT                 ;   Y. SET THE TIMEOUT FLAG.
T_DONE:

;-----------------------------------------------------------------------------------------

                MOVF    S_TIMER,W               ;GET THE DOUBLE PRESSED SHIFT COUNTER.
                BTFSS   ZERO                    ;IS IT = 0?
                DECF    S_TIMER,F               ;N. DEC THE COUNTER.

;-----------------------------------------------------------------------------------------

                INCF    LOCK_TIMER,W            ;GET THE LOCK TIME COUNTER.
                BTFSC   ZERO                    ;IS IT = 255?
                GOTO    NO_LOCK_TM_DEC
                MOVF    LOCK_TIMER,W            ;N. GET THE LOCK TIME COUNTER.
                BTFSS   ZERO                    ;   IS IT = 0?
                DECF    LOCK_TIMER,F            ;   N. DEC THE COUNTER.
NO_LOCK_TM_DEC:                                 ;Y. DONT DEC THE COUNTER.

;-----------------------------------------------------------------------------------------

                MOVF    EEPROM_WRITE,W          ;GET THE EEPROM WRITE TIMEOUT COUNTER.
                BTFSC   ZERO                    ;IS IT = 0?
                GOTO    EEPROM_WR_DONE          ;Y. DONT DO ANYTHING.
                DECFSZ  EEPROM_WRITE,F          ;N. DEC THE COUNTER. COUNT = 0?
                GOTO    EEPROM_WR_DONE          ;   N. ALL DONE.
                BSF     EEPROM_UPDATE           ;   Y. SET THE FLAG. (UPDATE THE EEPROM)
EEPROM_WR_DONE:

;-----------------------------------------------------------------------------------------

RS_INT_STATUS:  SWAPF   SAVE_STATUS,W
                MOVWF   STATUS                  ;RESTORE STATUS REG.
                SWAPF   SAVE_W_REG,F            ;SAVE WREG.
                SWAPF   SAVE_W_REG,W            ;RESTORE WREG.
                BCF     T0IF                    ;CLEAR TMR0 INTERUPT FLAG.
                RETFIE

;-----------------------------------------------------------------------------------------

SET_TIMEOUT:    MOVLW   TIMEOUT_NUM_H           ;SET THE TIMEOUT COUNTER.
                MOVWF   TIMEOUT_H
                MOVLW   TIMEOUT_NUM_L
                MOVWF   TIMEOUT_L
                RETURN

;-----------------------------------------------------------------------------------------

;*****************************************************************************************
;               TEXT TABLE      (RETLW 'X')
;
;               PLACED IN PAGE 0, SO WE DONT HAVE TO BOTHER WITH PAGE BITS.
;*****************************************************************************************

TABLE:          ADDWF   PCL,F                   ;JUMP TO CHAR POINTED TO IN W REG.
ST:                                             ;LABEL THE START OF TEXT STRINGS.

TX:             EQU $ - ST
                        DT      "TX",0
RX:             EQU $ - ST
                        DT      "RX",0
UN:             EQU $ - ST
                        DT      "UN",0
LOCKED:         EQU $ - ST
                        DT      "LOCKED",0
TWO_SPACES:     EQU $ - ST
                        DT      "  ",0
DISP:           EQU $ - ST
                        DT      " DISPLAY ",0
MULT:           EQU $ - ST
                        DT      "MULT",0
MAX:            EQU $ - ST
                        DT      "MAX",0
MIN:            EQU $ - ST
                        DT      "MIN ",0
VCO:            EQU $ - ST
                        DT      " VCO OFFSET ",0
MHZ:            EQU $ - ST
                        DT      " MHz",0
TABLE_END:

;*****************************************************************************************
;
;               INITIALISE THE HARDWARE. (CONTINUED)
;
;*****************************************************************************************

CONTINUE:       MOVLW   67                      ;67 BYTES OF RAM TO CLEAR.
                MOVWF   COUNT
CLEAR_LOOP:     CLRF    INDF                    ;CLEAR THE RAM BYTE.
                INCF    FSR,F                   ;INC THE RAM ADDRESS POINTER.
                DECFSZ  COUNT,F                 ;DEC THE COUNT.
                GOTO    CLEAR_LOOP              ;LOOP UNTIL ALL BYTES DONE.

		MOVLW	7
                MOVWF	CMCON
		BSF     RP0                     ;SELECT PAGE 1 FOR TRIS REG ACCESS.
                MOVLW   B'00010100'
                MOVWF   PORTA                   ;RA0,RA1,RA3 = OUTPUTS, RA2,RA4 INPUT.
                MOVLW   B'00000011'
                MOVWF   PORTB                   ;RB1-RB7 = OUTPUT, RB0 = INPUT.
                MOVLW   B'00000100'             ;SET PRESCALER TO TMR0.
                MOVWF   OPTION_REG              ;TMR0 RATE = 1/32 OF INSTRUCTION CLOCK.
                BCF     RP0                     ;RETURN TO PAGE 0 FOR PORT ACCESS.

                BSF     TX_EN                   ;SET TX_EN PIN HIGH, PLL NOT LOCKED YET.

                CALL    INIT_LCD                ;SETUP THE LCD DISPLAY.

                BTFSS   BUTTON_S                ;IS THE SHIFT BUTTON PRESSED?
                BSF     UNLOCKED                ;Y. ENABLE ACCESS TO THE SETUP DISPLAYS.

                CALL    GET_SETUP               ;GET SETUP INFO FROM EEPROM.
                CLRF    TMR0                    ;SETS 1ST INTERUPT TO 9mS.
                MOVLW   B'10100000'             ;ENABLE TMR0 OVERFLOW INTERUPT.
                MOVWF   INTCON

;*****************************************************************************************
;               MAIN DISPLAY
;*****************************************************************************************

MAIN_DISP:      CALL    SET_TIMEOUT             ;SET THE DISPLAY TIMEOUT DOWN COUNTER.
                BCF     TIMEOUT                 ;CLEAR THE DISPLAY TIMEOUT FLAG.
                CALL    LCD_L1_P2               ;POSITION THE CURSOR, LINE 1 POS 2.

                MOVLW   'X'
                CALL    LCD_CHR                 ;DISPLAY 'X'.

                CALL    LCD_3_SPACES
                CALL    LCD_3_SPACES            ;DISPLAY '      '

                MOVLW   H'8A'
                CALL    LCD_CMD                 ;POSITION THE CURSOR, LINE 1 POS 11.

                MOVLW   LOCKED                  ;DISPLAY 'LOCKED'.
                CALL    LCD_TEXT

                MOVLW   H'C0'
                CALL    LCD_CMD                 ;POSITION THE CURSOR, LINE 2 POS 1.
                CALL    LCD_SPACE

                CALL    DISP_MHZ                ;DISPLAY ' MHz  ' ON LINE 2 POS 11.


MAIN_LOOP:      BTFSC   TIMEOUT                 ;HAS THE DISPLAY TIMEOUT BEEN REACHED?
                GOTO    MAIN_DISP               ;Y. REDRAW THE MAIN DISPLAY.

                CALL    UPDATE_EEPROM           ;SEE IF EEPROM UPDATE IS REQUIRED.

                MOVLW   H'80'
                CALL    LCD_CMD                 ;GOTO THE START OF LINE 1.

                BTFSS   PTT                     ;CHECK PTT LEAD. 0 = TX, 1 = RX.
                GOTO    PTT_IS_TX
                BTFSS   PTT_COPY                ;HAS PTT LEAD GONE HIGH SINCE LAST PASS.
                BSF     PTT_CHANGED             ;Y. SET THE PTT CHANGED FLAG.
                BSF     PTT_COPY                ;KEEP A COPY OF CURRENT PTT HIGH STATE.
                MOVLW   'R'                     ;IF PTT IS HIGH, DISPLAY 'RX '.
                GOTO    PTT_DONE
PTT_IS_TX:      BTFSC   PTT_COPY                ;HAS PTT LEAD GONE LOW SINCE LAST PASS.
                BSF     PTT_CHANGED             ;Y. SET THE PTT CHANGED FLAG.
                BCF     PTT_COPY                ;KEEP A COPY OF CURRENT PTT LOW STATE.
                MOVLW   'T'                     ;PREPARE TO DISPLAY 'TX '.
PTT_DONE:       CALL    LCD_CHR                 ;DISPLAY THE 'T' OR 'R'.;
                MOVLW   H'88'
                CALL    LCD_CMD                 ;POSITION THE CURSOR, LINE 1 POS 7.

                MOVLW   UN                      ;PREPARE TO DISPLAY 'UN'.
                BTFSS   PLL_VALID               ;IS THE PLL DIVIDER SETTING VALID 
                GOTO    PLL_NOT_LOCKED
                BTFSS   PLL_STATUS,6            ;AND, IS THE PLL LOCKED?
                GOTO    PLL_NOT_LOCKED
                MOVLW   TWO_SPACES              ;Y. DISPLAY '  '.
PLL_NOT_LOCKED: CALL    LCD_TEXT                ;N. DISPLAY 'UN'.

                BTFSS   PTT_CHANGED             ;HAS THE PTT CHANGED?
                GOTO    NO_PTT_CHANGE
                BTFSC   EEPROM_WRITE,7          ;Y. HAS FREQ +/- BEEN RELEASED FOR A SEC?
                GOTO    SKIP_BUTTONS            ;   N. DONT INC/DEC FREQUENCY.
                BCF     PTT_CHANGED             ;   Y. CLEAR THE PTT CHANGED FLAG.
                                                ;N. ITS OK TO INC/DEC THE FREQUENCY.
NO_PTT_CHANGE:  BTFSS   PTT_COPY                ;CHECK THE OLD PTT LEAD. 0 = TX, 1 = RX.
                GOTO    OLD_PTT_IS_TX

                MOVLW   RX_DISP_FREQ            ;OLD PTT IS HIGH SO,
                CALL    MOVE_TO_ARG             ;PREPARE TO INC/DEC RX FREQ.

                CALL    UP_DOWN                 ;CHECK UP DOWN BUTTONS FOR INC/DEC.

                MOVLW   MIN_RX_DISP_FR          ;MAKE SURE WE DONT GO BELOW THE MINIMUM
                CALL    MOVE_TO_ARG2            ;SET BY MIN_RX_DISP_FR.

                CALL    MAXIMUM

                MOVLW   MAX_RX_DISP_FR          ;MAKE SURE WE DONT GO ABOVE THE MAXIMUM
                CALL    MOVE_TO_ARG2            ;SET BY MAX_RX_DISP_FR.

                CALL    MINIMUM

                MOVLW   RX_DISP_FREQ            ;SAVE THE RX FREQ.
                GOTO    OLD_PTT_DONE

OLD_PTT_IS_TX:  MOVLW   TX_DISP_FREQ            ;OLD PTT IS LOW SO,
                CALL    MOVE_TO_ARG             ;PREPARE TO INC/DEC TX FREQ.

                CALL    UP_DOWN                 ;CHECK UP DOWN BUTTONS FOR INC/DEC.

                MOVLW   MIN_TX_DISP_FR          ;MAKE SURE WE DONT GO BELOW THE MINIMUM
                CALL    MOVE_TO_ARG2            ;SET BY MIN_TX_DISP_FR.

                CALL    MAXIMUM

                MOVLW   MAX_TX_DISP_FR          ;MAKE SURE WE DONT GO ABOVE THE MAXIMUM
                CALL    MOVE_TO_ARG2            ;SET BY MAX_TX_DISP_FR.

                CALL    MINIMUM

                MOVLW   TX_DISP_FREQ            ;SAVE THE TX FREQ.
OLD_PTT_DONE:   CALL    COPY_FROM_ARG

SKIP_BUTTONS:   CALL    UPDATE_PLL              ;UPDATE PLL AND RETURN THE DISPLAY FREQ.

                CALL    DISPLAY_FREQ            ;DISPLAY THE VARIABLE.

                BTFSS   UNLOCKED                ;IS ACCESS TO SETUP DISPLAYS ALLOWED?
                GOTO    MAIN_LOOP               ;N. CONTINUE UPDATING THE DISPLAY.

                CALL    CHK_S_DOUBLE
                BTFSS   PRESSED                 ;SHIFT BUTTON PRESSED BRIEFLY TWICE?
                GOTO    MAIN_LOOP               ;N. CONTINUE UPDATING THE DISPLAY.
                                                ;Y. GOTO NEXT MENU.

;*****************************************************************************************
;               MULTIPLIER MENU
;*****************************************************************************************

                CALL    LCD_CLEAR               ;CLEAR THE DISPLAY.
                MOVLW   DISP
                CALL    LCD_TEXT                ;DISPLAY ' DISPLAY '.
                MOVLW   MULT
                CALL    LCD_TEXT                ;DISPLAY 'MULT'.

MULT_DISP:      BTFSC   TIMEOUT                 ;HAS THE DISPLAY TIMEOUT BEEN REACHED?
                GOTO    MAIN_DISP               ;Y. RETURN TO THE MAIN DISPLAY.

                CALL    UPDATE_PLL              ;UPDATE PLL AND TX_EN LEAD.

                CALL    CHK_P_MULT              ;IF THE FREQ + BUTTON IS PRESSED.
                BTFSC   PRESSED
                INCF    MULT_NUM,F              ;INC THE MULTIPLIER BY 1.
                CALL    CHK_N_MULT              ;IF THE FREQ - BUTTON IS PRESSED.
                BTFSC   PRESSED
                DECF    MULT_NUM,F              ;DEC THE MULTIPLIER BY 1.

                MOVLW   1
                SUBWF   MULT_NUM,W
                BTFSC   CARRY                   ;WAS NUMBER LESS THAN 1.
                GOTO    MULT_0                  ;N. IT WAS 1 OR ABOVE.
                MOVLW   1
                MOVWF   MULT_NUM                ;Y. SET IT BACK TO 1.

MULT_0:         MOVLW   21
                SUBWF   MULT_NUM,W
                BTFSS   CARRY                   ;WAS NUMBER GREATER THAN 20.
                GOTO    BIN_DEC                 ;N. IT WAS 20 OR BELOW.
                MOVLW   20
                MOVWF   MULT_NUM                ;Y. SET IT BACK TO 20.

BIN_DEC:        CLRF    DEC_1                   ;CLEAR THE MSD.
                MOVF    MULT_NUM,W
                MOVWF   DEC_2                   ;PUT NUM TO CONVERT IN LSD.
TENTH           MOVLW   10
                SUBWF   DEC_2,W                 ;SUBTRACT 10 FROM NUM.
                BTFSS   CARRY                   ;IS THE RESULT NEGATIVE?
                GOTO    DISP_NUMBER             ;Y. ALL DONE.
                MOVWF   DEC_2
                INCF    DEC_1,F                 ;N. INC THE 10'S
                GOTO    TENTH                   ;   LOOP UNTIL DONE.

DISP_NUMBER:    MOVLW   H'8E'                   ;POSITION THE CURSOR, LINE 1 POS 15.
                CALL    LCD_CMD

                MOVF    DEC_1,W                 ;DISPLAY THE NUMBER IN ASCII FORM.
                ADDLW   H'30'
                CALL    LCD_CHR
                MOVF    DEC_2,W
                ADDLW   H'30'
                CALL    LCD_CHR

                CALL    CHK_S_BRIEF
                BTFSS   PRESSED                 ;SHIFT PRESSED BRIEFLY?
                GOTO    MULT_DISP               ;N. CONTINUE UPDATING THE DISPLAY.
                                                ;Y. GOTO NEXT MENU.

;*****************************************************************************************
;               MINIMUM RX DISPLAY FREQUENCY
;*****************************************************************************************

                CALL    LCD_L1_P2               ;POSITION THE CURSOR, LINE 1 POS 2.
                MOVLW   MIN
                CALL    LCD_TEXT                ;DISPLAY 'MIN '.
                MOVLW   RX
                CALL    LCD_TEXT                ;DISPLAY 'RX'.
                MOVLW   DISP
                CALL    LCD_TEXT                ;DISPLAY ' DISPLAY '.
                CALL    DISP_MHZ                ;DISPLAY ' MHz  ' ON LINE 2 POS 11.

MIN_RX_DISP:    BTFSC   TIMEOUT                 ;HAS THE DISPLAY TIMEOUT BEEN REACHED?
                GOTO    MAIN_DISP               ;Y. RETURN TO THE MAIN DISPLAY.

                CALL    UPDATE_PLL              ;UPDATE PLL AND TX_EN LEAD.

                MOVLW   MIN_RX_DISP_FR          ;MOVE THE VARIABLE TO INC/DEC TO ARG.

                CALL    UP_DOWN_MIN             ;CHECK +/- BUTTONS, & UPPER/LOWWER LIMIT.

                MOVLW   MIN_RX_DISP_FR          ;SAVE THE VARIABLE THAT WAS INC/DEC.
                CALL    COPY_FROM_ARG

                CALL    DISPLAY_FREQ            ;DISPLAY THE VARIABLE.

                CALL    CHK_S_BRIEF
                BTFSS   PRESSED                 ;SHIFT PRESSED BRIEFLY?
                GOTO    MIN_RX_DISP             ;N. CONTINUE UPDATING THE DISPLAY.
                                                ;Y. GOTO NEXT MENU.

;*****************************************************************************************
;               MAXIMUM RX DISPLAY FREQUENCY
;*****************************************************************************************

                CALL    LCD_L1_P2               ;POSITION THE CURSOR, LINE 1 POS 2.
                MOVLW   MAX
                CALL    LCD_TEXT                ;DISPLAY 'MAX'.

MAX_RX_DISP:    BTFSC   TIMEOUT                 ;HAS THE DISPLAY TIMEOUT BEEN REACHED?
                GOTO    MAIN_DISP               ;Y. RETURN TO THE MAIN DISPLAY.

                CALL    UPDATE_PLL              ;UPDATE PLL AND TX_EN LEAD.

                MOVLW   MAX_RX_DISP_FR          ;MOVE THE VARIABLE TO INC/DEC TO ARG.
                CALL    MOVE_TO_ARG

                CALL    UP_DOWN                 ;CHECK UP DOWN BUTTONS FOR INC/DEC.

                MOVLW   MIN_RX_DISP_FR          ;GET THE MIN RX DISPLAY FREQ.

                CALL    MAX_CHECK               ;MAKE SURE WE DONT GO BELOW THAT MINIMUM.

                MOVLW   MAX_RX_DISP_FR          ;SAVE THE MODIFIED INC/DEC VARIABLE.
                CALL    COPY_FROM_ARG

                CALL    DISPLAY_FREQ            ;DISPLAY THE VARIABLE.

                CALL    CHK_S_BRIEF
                BTFSS   PRESSED                 ;SHIFT PRESSED BRIEFLY?
                GOTO    MAX_RX_DISP             ;N. CONTINUE UPDATING THE DISPLAY.
                                                ;Y. GOTO NEXT MENU.

;*****************************************************************************************
;               RX VCO OFFSET FREQUENCY
;*****************************************************************************************

                CALL    LCD_L1_P2               ;POSITION THE CURSOR, LINE 1 POS 2.
                MOVLW   RX                      ;DISPLAY 'RX'.
                CALL    LCD_TEXT
                MOVLW   VCO
                CALL    LCD_TEXT                ;DISPLAY ' VCO OFFSET '.

RX_VCO_OFF:     BTFSC   TIMEOUT                 ;HAS THE DISPLAY TIMEOUT BEEN REACHED?
                GOTO    MAIN_DISP               ;Y. RETURN TO THE MAIN DISPLAY.

                CALL    UPDATE_PLL              ;UPDATE PLL AND TX_EN LEAD.

                MOVLW   RX_VCO_OFFSET           ;MOVE THE VARIABLE TO INC/DEC TO ARG.

                CALL    UP_DOWN_OFFSET          ;INC/DEC THE OFFSET WITHIN LIMITS.

                MOVLW   RX_VCO_OFFSET           ;SAVE THE MODIFIED INC/DEC VARIABLE.
                CALL    COPY_FROM_ARG

                CALL    DISPLAY_FREQ            ;DISPLAY THE VARIABLE.

                CALL    CHK_S_BRIEF
                BTFSS   PRESSED                 ;SHIFT PRESSED BRIEFLY?
                GOTO    RX_VCO_OFF              ;N. CONTINUE UPDATING THE DISPLAY.
                                                ;Y. GOTO NEXT MENU.

;*****************************************************************************************
;               MINIMUM TX DISPLAY FREQUENCY
;*****************************************************************************************

                CALL    LCD_L1_P2               ;POSITION THE CURSOR, LINE 1 POS 2.
                MOVLW   MIN
                CALL    LCD_TEXT                ;DISPLAY 'MIN '.
                MOVLW   TX
                CALL    LCD_TEXT                ;DISPLAY 'TX'.
                MOVLW   DISP
                CALL    LCD_TEXT                ;DISPLAY ' DISPLAY '.

MIN_TX_DISP:    BTFSC   TIMEOUT                 ;HAS THE DISPLAY TIMEOUT BEEN REACHED?
                GOTO    MAIN_DISP               ;Y. RETURN TO THE MAIN DISPLAY.

                CALL    UPDATE_PLL              ;UPDATE PLL AND TX_EN LEAD.

                MOVLW   MIN_TX_DISP_FR          ;MOVE THE VARIABLE TO INC/DEC TO ARG.

                CALL    UP_DOWN_MIN             ;CHECK +/- BUTTONS, & UPPER/LOWWER LIMIT.

                MOVLW   MIN_TX_DISP_FR          ;SAVE THE MODIFIED INC/DEC VARIABLE.
                CALL    COPY_FROM_ARG

                CALL    DISPLAY_FREQ            ;DISPLAY THE VARIABLE.

                CALL    CHK_S_BRIEF
                BTFSS   PRESSED                 ;SHIFT PRESSED BRIEFLY?
                GOTO    MIN_TX_DISP             ;N. CONTINUE UPDATING THE DISPLAY.
                                                ;Y. GOTO NEXT MENU.

;*****************************************************************************************
;               MAXIMUM TX DISPLAY FREQUENCY
;*****************************************************************************************

                CALL    LCD_L1_P2               ;POSITION THE CURSOR, LINE 1 POS 2.
                MOVLW   MAX
                CALL    LCD_TEXT                ;DISPLAY 'MAX'.

MAX_TX_DISP:    BTFSC   TIMEOUT                 ;HAS THE DISPLAY TIMEOUT BEEN REACHED?
                GOTO    MAIN_DISP               ;Y. RETURN TO THE MAIN DISPLAY.

                CALL    UPDATE_PLL              ;UPDATE PLL AND TX_EN LEAD.

                MOVLW   MAX_TX_DISP_FR          ;MOVE THE VARIABLE TO INC/DEC TO ARG.
                CALL    MOVE_TO_ARG

                CALL    UP_DOWN                 ;CHECK UP DOWN BUTTONS FOR INC/DEC.

                MOVLW   MIN_TX_DISP_FR          ;GET THE MIN TX DISPLAY FREQ.

                CALL    MAX_CHECK               ;MAKE SURE WE DONT GO BELOW THAT MINIMUM.

                MOVLW   MAX_TX_DISP_FR          ;SAVE THE MODIFIED INC/DEC VARIABLE.
                CALL    COPY_FROM_ARG

                CALL    DISPLAY_FREQ            ;DISPLAY THE VARIABLE.

                CALL    CHK_S_BRIEF
                BTFSS   PRESSED                 ;SHIFT PRESSED BRIEFLY?
                GOTO    MAX_TX_DISP             ;N. CONTINUE UPDATING THE DISPLAY.
                                                ;Y. GOTO NEXT MENU.

;*****************************************************************************************
;               TX VCO OFFSET FREQUENCY
;*****************************************************************************************

                CALL    LCD_L1_P2               ;POSITION THE CURSOR, LINE 1 POS 2.
                MOVLW   TX                      ;DISPLAY 'TX'.
                CALL    LCD_TEXT
                MOVLW   VCO
                CALL    LCD_TEXT                ;DISPLAY ' VCO OFFSET '.

TX_VCO_OFF:     BTFSC   TIMEOUT                 ;HAS THE DISPLAY TIMEOUT BEEN REACHED?
                GOTO    MAIN_DISP               ;Y. RETURN TO THE MAIN DISPLAY.

                CALL    UPDATE_PLL              ;UPDATE PLL AND TX_EN LEAD.

                MOVLW   TX_VCO_OFFSET           ;MOVE THE VARIABLE TO INC/DEC TO ARG.

                CALL    UP_DOWN_OFFSET          ;INC/DEC THE OFFSET WITHIN LIMITS.

                MOVLW   TX_VCO_OFFSET           ;SAVE THE MODIFIED INC/DEC VARIABLE.
                CALL    COPY_FROM_ARG

                CALL    DISPLAY_FREQ            ;DISPLAY THE VARIABLE.

                CALL    CHK_S_BRIEF
                BTFSS   PRESSED                 ;SHIFT PRESSED BRIEFLY?
                GOTO    TX_VCO_OFF              ;N. CONTINUE UPDATING THE DISPLAY.
                GOTO    MAIN_DISP               ;Y. GO BACK TO THE MAIN DISPLAY.

;*****************************************************************************************
;  MISC ROUTINES USED BY THE DISPLAY ROUTINES.
;
;                       DISP_MHZ                DISPLAY ' MHz  ' ON LINE 2 POS 11.
;.
;                       DISPLAY_FREQ            DISPLAY A NUMBER ON LINE 2 OF LCD.
;                                               NUM x 5 x DISP_MULT.
;
;                       UP_DOWN_OFFSET          INC/DEC THE RX/TX VCO OFFSET,
;                                               AND MAKES SURE THE OFFSET IS NOT TO BIG.
;
;                       UP_DOWN_MIN             USED BY MIN TX & RX MENU DISPLAYS.
;
;                       MAX_CHECK               USED BY MAX TX & RX MENU DISPLAYS.
;
;                       UPDATE_PLL              CHECK THE STATE OF PTT, SELECT RX/TX FREQ,
;                                               ADD ANY OFFSET & SET THE PLL DIVIDER.
;                                               ALSO CHECKS PLL STATUS.
;*****************************************************************************************

DISP_MHZ:       MOVLW   H'CA'
                CALL    LCD_CMD                 ;POSITION THE CURSOR, LINE 2 POS 11.
                MOVLW   MHZ
                CALL    LCD_TEXT               ;DISPLAY ' MHz'.
				CALL	LCD_2_SPACES
                RETURN

;-----------------------------------------------------------------------------------------

MAX_CHECK:      CALL    MOVE_TO_ARG2            ;GET MIN_RX_DISP_FR OR MIN_TX_DISP_FR.
                GOTO    U_D_MIN                 ;MAKE SURE THE RESULT IS NOT TO LARGE.

;-----------------------------------------------------------------------------------------

UP_DOWN_MIN:    CALL    MOVE_TO_ARG
                CALL    UP_DOWN                 ;CHECK UP DOWN BUTTONS FOR INC/DEC.
                CLRF    ARG2_H                  ;MAKE SURE THE RESULT IS NOT TO SMALL.
                CLRF    ARG2_L
U_D_MIN:        CALL    MAXIMUM
                MOVLW   DISP_MAX_H              ;MAKE SURE THE RESULT IS NOT TO LARGE.
                MOVWF   ARG2_H
                MOVLW   DISP_MAX_L
                MOVWF   ARG2_L
                CALL    MINIMUM
                RETURN

;-----------------------------------------------------------------------------------------

DISPLAY_FREQ:   BCF     NEGATIVE                ;CLEAR THE NEGATIVE FLAG.
                COMF    ARG_H,W                 ;NUM BETWEEN 49151 AND 65535(C000 - FFFF)
                ANDLW   B'11000000'             ;WE CLASS AS A NEGATIVE NUMBER.
                BTFSS   ZERO                    ;IS IT A NEGATIVE NUMBER?
                GOTO    DISP_FREQ               ;N. JUST CONTINUE.
                BSF     NEGATIVE                ;Y. SET THE NEGATIVE FLAG.
                COMF    ARG_H,F                 ;   CONVERT IT TO A + NUMBER.
                COMF    ARG_L,F                 ;   COMPLIMENT ARG, THEN ADD 1.
                MOVLW   1
                ADDWF   ARG_L,F
                BTFSC   CARRY
                INCF    ARG_H,F
DISP_FREQ:      MOVF    MULT_NUM,W              ;GET THE DISPLAY MULTIPLIER NUMBER.
                MOVWF   ARG
                BCF     CARRY                   ;MULTIPLY THE MULTIPLIER BY 125 kHz

                RLF     ARG,F                   ;Multiplikator für 2,5 GHz =1 !!!
                RLF     ARG,F                    
                ADDWF   ARG,F
                MOVF    ARG,W
		MOVWF   ARG
                BCF     CARRY                   
                RLF     ARG,F                   
                RLF     ARG,F                   
                ADDWF   ARG,F
                MOVF    ARG,W
		MOVWF   ARG
                BCF     CARRY                   
                RLF     ARG,F                   
                RLF     ARG,F                   
                ADDWF   ARG,F

                CALL    MULTIPLY                ;ARG x  MULT_NUM.
		CALL    BIN_TO_DEC              ;CONVERT THE NUMBER TO ASCII DECIMAL.
                MOVLW   H'C1'                   ;POSITION THE CURSOR, LINE 2 POS 2.
                CALL    LCD_CMD
                MOVLW   DEC_0                   ;SET THE POINTER TO THE START
                MOVWF   FSR                     ;OF THE DECIMAL BUFFER.
                MOVLW   9                       ;8 DIGIT + DECIMAL POINT TO DISPLAY.
                MOVWF   COUNT
DISP_LOOP:      MOVF    INDF,W                  ;GET CHR POINTED TO BY FSR.
                CALL    LCD_CHR                 ;DISPLAY THE CHR.
                INCF    FSR,F                   ;INC THE POINTER.
                DECFSZ  COUNT,F                 ;ALL DONE?
                GOTO    DISP_LOOP               ;N. CONTINUE.
                RETURN

;-----------------------------------------------------------------------------------------

UP_DOWN_OFFSET: 
                CALL    MOVE_TO_ARG
                MOVLW   B'10000000'             ;ADD H'8000' TO ARG. MAKES IT EASY TO
                XORWF   ARG_H,F                 ;DEAL WITH NEGATIVE NUMBERS.

                CALL    UP_DOWN                 ;CHECK UP DOWN BUTTONS FOR INC/DEC.

                MOVLW   OFFSET_MAX_H ^ H'80'
                MOVWF   ARG2_H                  ;MAKE SURE THE RESULT IS NOT TO LARGE.
                MOVLW   OFFSET_MAX_L            ;ARG2 = +500MHz. (+ H'8000')
                MOVWF   ARG2_L
                CALL    MINIMUM                 ;RETURN THE MINIMUM OF ARG & ARG2.

                MOVLW   OFFSET_MIN_H ^ H'80'
                MOVWF   ARG2_H                  ;MAKE SURE THE RESULT IS NOT TO SMALL.
                MOVLW   OFFSET_MIN_L            ;ARG2 = -500MHz. (+ H'8000')
                MOVWF   ARG2_L
                CALL    MAXIMUM                 ;RETURN THE MAXIMUM OF ARG & ARG2.

                MOVLW   B'10000000'             ;REMOVE THE H'8000' OFFSET.
                XORWF   ARG_H,F
                RETURN

;-----------------------------------------------------------------------------------------

UPDATE_PLL:     CALL    GET_PLL_STATUS          ;UPDATE THE STATUS BYTE. (PLL LOCKED ?)

                BTFSS   PTT                     ;CHECK PTT LEAD. 0 = TX, 1 = RX.
                GOTO    PTT_TX

                MOVLW   RX_DISP_FREQ            ;RX. MOVE RX DISPLAY FREQ TO ARG.
                CALL    MOVE_TO_ARG

                MOVLW   TEMP_H                  ;RX. SAVE RX DISP FREQ FOR LATER ON.
                CALL    COPY_FROM_ARG

                MOVLW   RX_VCO_OFFSET           ;RX. GET THE RX VCO OFFSET.
                GOTO    DONE_PTT

PTT_TX:         MOVLW   TX_DISP_FREQ            ;TX. MOVE TX DISPLAY FREQ TO ARG.
                CALL    MOVE_TO_ARG

                MOVLW   TEMP_H                  ;TX. SAVE TX DISP FREQ FOR LATER ON.
                CALL    COPY_FROM_ARG

                MOVLW   TX_VCO_OFFSET           ;TX. GET THE RX VCO OFFSET.
DONE_PTT:       CALL    MOVE_TO_ARG2

                CALL    ADD                     ;ADD THE VCO OFFSET.

                CALL    SET_PLL                 ;SEND THE DIVIDER NUMBER TO THE PLL.

                MOVLW   TEMP_H                  ;RECOVER THE TX/RX DISP FREQ.
                CALL    MOVE_TO_ARG
                RETURN

;*****************************************************************************************
;  LCD ROUTINES.        LCD_CHR                 SEND A CHARACTER TO THE LCD DISPLAY.
;                       LCD_CMD                 SEND A COMMAND INSTRUCTION TO THE LCD.
;                       LCD_TEXT                SEND A TEXT STRING TO THE LCD.
;                       LCD_L1_P2               POSITION CURSOR LINE 1, POS 2.
;                       LCD_CLEAR               CLEAR THE LCD, GOTO THE START OF LINE 1.
;                       LCD_SPACE               DISPLAY A SPACE ON THE LCD.
;                       LCD_2_SPACES            DISPLAY TWO SPACES ON THE LCD.
;                       LCD_3_SPACES            DISPLAY THREE SPACES ON THE LCD.
;*****************************************************************************************

LCD_3_SPACES    CALL    LCD_SPACE
LCD_2_SPACES    CALL    LCD_SPACE
LCD_SPACE:      MOVLW   ' '

        IF LCD_USES_RW                          ;ASSEMBLE IF USING STANDARD LCD.

LCD_CHR:        CALL    LCD_BUSY_?              ;WAIT UNTIL THE LCD IS NOT BUSY.
                BSF     RS                      ;SELECT DISPLAY DATA REGISTER.
                GOTO    LCD_BYTE
        ELSE

LCD_CHR:        MOVWF   TEMP_H                  ;SAVE THE BYTE TO SEND TO THE LCD.
                MOVWF   TEMP_L
                BSF     RS                      ;SELECT DISPLAY DATA REGISTER.
                GOTO    LCD_BYTE

        ENDIF

;-----------------------------------------------------------------------------------------

        IF LCD_USES_RW                          ;ASSEMBLE IF USING STANDARD LCD.

LCD_CLEAR:      MOVLW   H'01'                   ;CLEAR THE DISPLAY.
                GOTO    LCD_CMD
LCD_L1_P2:      MOVLW   H'81'                   ;POSITION THE CURSOR, LINE 1 POS 2.
LCD_CMD:        CALL    LCD_BUSY_?              ;WAIT UNTIL THE LCD IS NOT BUSY.
                BCF     RS                      ;SELECT INSTRUCTION REGISTER.
                CALL    LCD_BYTE                ;SEND THE BYTE TO THE LCD.
                BSF     RS                      ;RESTORE THE RS (SCL)TO ITS NORMAL STATE.
                RETURN

        ELSE                                    ;ASSEMBLE IF USING LCD WITH NO R/W LEAD.

LCD_CLEAR:      MOVLW   H'01'                   ;CLEAR THE DISPLAY.
                CALL    LCD_CMD
                MOVLW   24                      ;WAIT 20mS. LCD EXECUTION TIME = 5mS.
                CALL    DELAY2                  ;(845uS x 24)
                RETURN

LCD_L1_P2:      MOVLW   H'81'                   ;POSITION THE CURSOR, LINE 1 POS 2.
LCD_CMD:        MOVWF   TEMP_H                  ;SAVE THE BYTE TO SEND TO THE LCD.
                MOVWF   TEMP_L
                BCF     RS                      ;SELECT INSTRUCTION REGISTER.
                CALL    LCD_BYTE                ;SEND THE BYTE TO THE LCD.
                BSF     RS                      ;RESTORE THE RS (SCL)TO ITS NORMAL STATE.
                RETURN

        ENDIF

;-----------------------------------------------------------------------------------------

LCD_TEXT:       MOVWF   TEMP                    ;TEMP HOLDS TEXT START ADDRESS.
                CALL    TABLE
                ANDLW   H'FF'
                BTFSC   ZERO                    ;AT END OF MESSAGE? (0 RETURNED AT END)
                RETURN                          ;Y. ALL DONE.
                CALL    LCD_CHR                 ;N. DISPLAY CHARACTER.
                MOVF    TEMP,W                  ;   POINT TO NEXT CHARACTER.
                ADDLW   1
                GOTO    LCD_TEXT

;-----------------------------------------------------------------------------------------

LCD_BYTE:		bcf		INTCON,GIE
				BCF     RW                      ;SET LCD FOR WRITE FROM PIC.
                BSF     RP0                     ;SELECT PAGE 1 FOR TRIS REG ACCESS.
                MOVLW   B'00000011'
                MOVWF   PORTB                   ;SET UPPER 7 BITS AS OUTPUTS.
                BCF     RP0                     ;RETURN TO PAGE 0 FOR PORT ACCESS.
                BSF     E                       ;SET THE LCD CLOCK.
                MOVLW   B'11110000'
                ANDWF   TEMP_H,F                ;STRIP LOW NIBBLE FROM THE BYTE TO SEND.
                MOVF    PORTB,W                 ;GET CURRENT PORT SETINGS.
                ANDLW   B'00001100'             ;KEEP THE LOW NIBBLE UNCHANGED.
                IORWF   TEMP_H,W                ;COMBINE THE TWO NIBBLES.
                MOVWF   PORTB                   ;SEND NIBBLE TO LCD.
                BCF     E                       ;CLOCK THE NIBLE INTO THE LCD.
                BSF     E                       ;SET THE LCD CLOCK.
                SWAPF   TEMP_L,F                ;SWAP THE UPPER AND LOWER NIBBLES.
                MOVLW   B'11110000'
                ANDWF   TEMP_L,F                ;STRIP LOW NIBBLE FROM THE BYTE TO SEND.
                MOVF    PORTB,W                 ;GET CURRENT PORT SETINGS.
                ANDLW   B'00001100'             ;KEEP THE LOW NIBBLE UNCHANGED.
                IORWF   TEMP_L,W                ;COMBINE THE TWO NIBBLES.
                MOVWF   PORTB                   ;SEND NIBBLE TO LCD.
                BCF     E                       ;CLOCK THE NIBLE INTO THE LCD.

        IF LCD_USES_RW == FALSE                 ;ASSEMBLE IF USING LCD WITH NO R/W LEAD.

                MOVLW   75                      ;WAIT 250uS. LCD EXECUTION TIME = 120uS.
                CALL    DELAY                   ;(3.3uS x 75)
        ENDIF
				bsf		INTCON,GIE
                RETURN

;-----------------------------------------------------------------------------------------

        IF      LCD_USES_RW                     ;ASSEMBLE IF USING STANDARD LCD.

LCD_BUSY_?      MOVWF   TEMP_H                  ;SAVE THE BYTE TO SEND TO THE LCD.
                MOVWF   TEMP_L
                BCF     RS                      ;SELECT INSTRUCTION REGISTER.
                BSF     RP0                     ;SELECT PAGE 1 FOR TRIS REG ACCESS.
                MOVLW   B'11110011'             ;SET HI 4 BITS AS INPUT.
                MOVWF   PORTB
                BCF     RP0                     ;RETURN TO PAGE 0 FOR PORT ACCESS.
                BSF     RW                      ;SET LCD FOR READ FROM PIC.
BUSY_LOOP:      BSF     E                       ;CLOCK THE LCD.
                MOVF    PORTB,W                 ;READ THE HIGH NIBBLE.
                ANDLW   B'10000000'             ;ISOLATE THE BUSY BIT.
                BCF     E                       ;CLOCK THE LCD.
                BSF     E                       ;CLOCK THE LCD.
                                                ;IGNORE THE LOWER NIBBLE.
                BCF     E                       ;CLOCK THE LCD.
                BTFSS   ZERO
                GOTO    BUSY_LOOP               ;LOOP UNTIL BUSY BIT IS CLEAR.
                RETURN

        ENDIF
;-----------------------------------------------------------------------------------------
;               INITIALISE THE LCD IN 4 BIT MODE.
;-----------------------------------------------------------------------------------------

INIT_LCD:       CLRF    PORTB                   ;SET RS,RW,E & SDA LOW.

                CALL    LCD_RESET               ;RESET THE LCD.
                CALL    LCD_RESET               ;RESET THE LCD.
                CALL    LCD_RESET               ;RESET THE LCD.

                MOVLW   B'00101000'             ;LCD 4 BIT MODE COMMAND.
                MOVWF   PORTB                   ;ALSO E HIGH.
                BCF     E
                CALL    LONG_DELAY
                MOVLW   B'00101000'             ;SET LCD TO 2 LINE, 5x7 DOT.
                CALL    LCD_CMD
                MOVLW   B'00001000'             ;TURN DISPLAY OFF.
                CALL    LCD_CMD
                CALL    LCD_CLEAR               ;CLEAR DISPLAY.
                MOVLW   B'00000110'             ;CURSOR INCREMENTS, NO DISPLAY SHIFT.
                CALL    LCD_CMD
                MOVLW   B'00001100'             ;TURN LCD ON.
                GOTO    LCD_CMD                 ;RETURN VIA LCD_CMD.

;-----------------------------------------------------------------------------------------

LCD_RESET:      CALL    LONG_DELAY
                MOVLW   B'00111000'             ;LCD RESET COMMAND.
                MOVWF   PORTB                   ;ALSO E HIGH.
                BCF     E
                RETURN

;*****************************************************************************************
;  I2C ROUTINES.        SET_PLL                 GIVEN THE DIVIDER NUMBER IN ARG, CHECKS
;                                               THAT THE NUMBER IS VALID THEN SENDS IT
;                                               TO THE PLL.
;                       GET_PLL_STATUS          GET THE STATUS BYTE FROM THE PLL,
;                                               AND SETS THE TX_EN LEAD.
;                       I2C_START               SEND AN I2C START SIGNAL ON THE BUS.
;                       I2C_TX_BYTE             SEND THE BYTE IN W TO THE BUS.
;                       I2C_STOP                SEND AN I2C STOP SIGNAL ON THE BUS.
;*****************************************************************************************

SET_PLL:        BCF     MAX_MIN_MOD             ;CLEAR THE ARG CHANGED FLAG.
                BSF     PLL_VALID               ;SET THE VALID FLAG.
                CALL    TRIM_NEG                ;IF DIVIDER NUMBER IS NEGATIVE, MAKE IT 0.

                MOVLW   PLL_DIV_MIN_H
                MOVWF   ARG2_H                  ;MAKE SURE THE DIVIDER NUMBER IS NOT
                MOVLW   PLL_DIV_MIN_L           ;BELOW 50MHz.
                MOVWF   ARG2_L
                CALL    MAXIMUM

                MOVLW   PLL_DIV_MAX_H
                MOVWF   ARG2_H                  ;MAKE SURE THE DIVIDER NUMBER IS NOT
                MOVLW   PLL_DIV_MAX_L           ;ABOVE 1400MHz.
                MOVWF   ARG2_L
                CALL    MINIMUM

                MOVLW   PLL_OLD                 ;GET THE OLD PLL DIVIDER NUMBER.
                CALL    MOVE_TO_ARG2
                CALL    CMP                     ;SAME AS THE OLD DIVIDER NUMBER?
                BTFSC   ZERO
                GOTO    DONT_SEND               ;Y. NO NEED TO RE-SEND THE NUMBER.

                MOVLW   PLL_OLD                 ;N. SAVE THE NEW DIVIDER NUMBER.
                CALL    COPY_FROM_ARG           ;   TO PLL_OLD.

                CALL    I2C_START
                MOVLW   PLL_ADDRESS             ;   SEND THE PLL ADDRESS.
                CALL    I2C_TX_BYTE
                MOVF    ARG_H,W                 ;   SEND MSD BYTE OF THE PLL DIVIDER.
                CALL    I2C_TX_BYTE
                MOVF    ARG_L,W                 ;   SEND LSB BYTE OF THE PLL DIVIDER.
                CALL    I2C_TX_BYTE
                MOVLW   B'10001110'             ;   SET CHARGE PUMP CURRENT TO 50uA,
                CALL    I2C_TX_BYTE             ;   (10101110 P6=REF, P7=F/DIV.)
                MOVLW   B'11111111'             ;   OUTPUT PINS ACTIVE.
                CALL    I2C_TX_BYTE
                CALL    I2C_STOP                ;   SEND THE STOP COMMAND.

                BCF     PLL_STATUS,6            ;   THE PLL IS NOT LOCKED NOW.

DONT_SEND:      BTFSC   MAX_MIN_MOD             ;DID MIN OR MAX CHANGE THE DIVIDER NUM?
                BCF     PLL_VALID               ;Y. CLEAR THE VALID FLAG. (INVALID)
                RETURN                          ;N. THE DIVIDER NUMBER WAS VALID.

;-----------------------------------------------------------------------------------------

GET_PLL_STATUS: BTFSS   PLL_STATUS,6            ;WAS PLL LOCKED WHEN LAST CHECKED?
                GOTO    GET_STATUS              ;N. GET THE CURRENT STATUS.
                MOVF    LOCK_TIMER,W            ;GET THE CURRENT PLL LOCKED TIMER.
                BTFSS   ZERO                    ;HAS THE COUNTER REACHED 0 ?
                GOTO    UPDATE_TX_EN            ;N. JUST UPDATE THE TX ENABLE PIN.
                CALL    RS_LOCKED_TMR           ;Y. RESET THE LOCKED TIMER.


GET_STATUS:     CALL    I2C_START               ;SEND AN I2C START SIGNAL.
                MOVLW   PLL_ADDRESS + 1         ;I2C ADDRESS & READ BIT.
                CALL    I2C_TX_BYTE             ;SEND THE ADDRESS BYTE TO THE PLL.

                MOVLW   8
                MOVWF   COUNT                   ;SET THE NUMBER OF BITS TO RECEIVE.
RX_BIT_LOOP:    CALL    I2C_DELAY
                BSF     SCL
                CALL    I2C_DELAY
                RRF     PORTA,W                 ;MOVE THE SDA (BIT 0) INTO THE CARRY BIT.
                RLF     PLL_STATUS,F            ;SAVE THE REVEIVED BIT.
                BCF     SCL
                DECFSZ  COUNT,F
                GOTO    RX_BIT_LOOP             ;LOOP UNTIL 8 BITS RECEIVED.
                CALL    LET_SDA_HIGH            ;SEND AN I2C NEVATIVE ACKNOWLEDGE.
                CALL    I2C_CLOCK               ;(SDA HIGH)
                CALL    I2C_STOP                ;SEND AN I2C STOP SIGNAL.

                BTFSC   PLL_STATUS,6            ;IS PLL LOCKED NOW?
                CALL    RS_LOCKED_TMR           ;Y. RESET THE LOCKED TIMER.

UPDATE_TX_EN:   BTFSS   PTT                     ;IF PTT IS HIGH, DONT ENABLE TX_EN LEAD.
                BTFSS   PLL_STATUS,6            ;IS THE PLL LOCKED?
                GOTO    NO_TX_EN
                BTFSS   PLL_VALID               ;Y. IS THE PLL DIVIDER SETING VALID?
                GOTO    NO_TX_EN
                BCF     TX_EN                   ;   Y. ENABLE THE TX_EN LEAD.
                RETURN
NO_TX_EN:       BSF     TX_EN                   ;N. DONT ENABLE THE TX_EN LEAD.
                RETURN

RS_LOCKED_TMR:  MOVLW   LOCK_TM                 ;RESET THE PLL LOCKED TIMER.
                MOVWF   LOCK_TIMER
                RETURN

;-----------------------------------------------------------------------------------------

I2C_TX_BYTE:    MOVWF   TEMP                    ;SAVE THE BYTE TO TX IN TEMP.
                MOVLW   8                       ;SET THE NUMBER OF BITS TO TRANSMIT.
                MOVWF   COUNT
TX_BIT_LOOP:    RLF     TEMP,F                  ;MOVE THE MSB INTO THE CARRY.
                BTFSC   CARRY
                CALL    LET_SDA_HIGH            ;IF CARRY IS SET, SET THE SDA.
                BTFSS   CARRY
                CALL    PULL_SDA_LOW            ;IF CARRY IS CLEAR, CLEAR THE SDA.
                CALL    I2C_CLOCK               ;LATCH THE DATA INTO THE PLL.
                DECFSZ  COUNT,F
                GOTO    TX_BIT_LOOP             ;LOOP UNTIL ALL BITS ARE SENT.
                CALL    LET_SDA_HIGH            ;LET THE SDA FLOAT HIGH.
                CALL    I2C_CLOCK               ;SEND AN I2C ACKNOWLEGE SIGNAL.
                RETURN

;-----------------------------------------------------------------------------------------

I2C_START:      BSF     SCL                     ;SEND AN I2C START SIGNAL.
                CALL    LET_SDA_HIGH 
                CALL    I2C_DELAY
                CALL    PULL_SDA_LOW
                GOTO    I2C_CLOCK2

;-----------------------------------------------------------------------------------------

I2C_STOP:       CALL    PULL_SDA_LOW            ;SEND AN I2C STOP SIGNAL.
                CALL    I2C_DELAY
                BSF     SCL
                CALL    I2C_DELAY
                CALL    LET_SDA_HIGH
                CALL    I2C_DELAY
                RETURN

;-----------------------------------------------------------------------------------------

I2C_CLOCK:      CALL    I2C_DELAY
                BSF     SCL                     ;PULSE THE SCL LINE
I2C_CLOCK2:     CALL    I2C_DELAY
                BCF     SCL                     ;         "
                RETURN

;-----------------------------------------------------------------------------------------

PULL_SDA_LOW:   BCF     SDA                     ;SDA WILL BE LOW WHEN SET AS O/P.
                BSF     RP0                     ;SELECT PAGE 1 FOR TRIS REG ACCESS.
                BCF     SDA                     ;SET SDA AS AN OUTPUT.
                BCF     RP0                     ;RETURN TO PAGE 0 FOR PORT ACCESS.
                RETURN

;-----------------------------------------------------------------------------------------

LET_SDA_HIGH:   BSF     RP0                     ;SELECT PAGE 1 FOR TRIS REG ACCESS.
                BSF     SDA                     ;SET SDA AS AN INPUT.
                BCF     RP0                     ;RETURN TO PAGE 0 FOR PORT ACCESS.
                RETURN

;-----------------------------------------------------------------------------------------

I2C_DELAY       MOVLW   15
DELAY:          MOVWF   COUNT1                  ; 15 LOOPS (3CLOCKS) = 50 uS.
I2C_LOOP:       DECFSZ  COUNT1,F
                GOTO    I2C_LOOP
                RETURN

;*****************************************************************************************
;  BIN_TO_DEC      CONVERT A 24 BIT BINARY NUMBER TO A 8 DIGIT ASCII DECIMAL NUMBER.
;
;       FIRST THE 24 BIT BINARY NUMBER IS CONVERTED TO 8 DIGIT BCD, TWO DIGITS PER BYTE IN
;       EACH OF DEC_1, DEC_3, DEC_5 AND DEC_7.
;       THE BCD IS THEN UNPACKED TO ONE DIGIT PER BYTE IN DEC_0 TO DEC_7.
;       IT IS THEN CONVERTED TO ASCII, INCLUDING NEGATIVE SIGN, DECIMAL POINT AND LEADING
;       ZERO BLANKING, READY FOR DISPLAY ON THE LCD.
;
;       THE 24 BIT BINARY NUMBER IS INPUT IN LOCATIONS RESULT_H, RESULT_M AND
;       RESULT_L WITH THE HIGH BYTE IN RESULT_H.
;       THE 8 DIGIT DECIMAL NUMBER IS RETURNED IN DEC_0 - DEC_7 WITH DEC_0
;       CONTAINING THE MSD.
;*****************************************************************************************

BIN_TO_DEC:     BCF     CARRY                   ;CLEAR THE CARRY BIT.
                MOVLW   24
                MOVWF   COUNT                   ;24 BITS TO PROCESS.
                CLRF    DEC_1                   ;BCD MSD DIGITS.
                CLRF    DEC_3
                CLRF    DEC_5
                CLRF    DEC_7                   ;BCD LSD DIGITS.
LOOP24:         RLF     RESULT_L, F             ;SHIFT 24 BIT NUMBER LEFT BY ONE BIT
                RLF     RESULT_M, F             ;INTO BCD.
                RLF     RESULT_H, F
                RLF     DEC_7, F
                RLF     DEC_5, F
                RLF     DEC_3, F
                RLF     DEC_1, F
				DECFSZ  COUNT, F
                GOTO    ADJ_DEC                 ;ADJUST BCD DIGITS AND LOOP TO LOOP24.

;-----------------------------------------------------------------------------------------

                SWAPF   DEC_7,W                 ;BINARY TO BCD CONVERSION DONE.
                MOVWF   DEC_6                   ;UNPACK THE BCD TO ONE CHR PER BYTE.
                SWAPF   DEC_5,W
                MOVWF   DEC_4
                SWAPF   DEC_3,W
                MOVWF   DEC_2
                SWAPF   DEC_1,W
                MOVWF   DEC_0

                BCF     LEADING_FLAG            ;BLANK LEADING ZERO,S UNTIL THE FLAG IS
                MOVLW   DEC_0                   ;SET BY A NON ZERO NUMBER.
                CALL    ADJ_ASCII
                MOVLW   DEC_1
                CALL    ADJ_ASCII
                MOVLW   DEC_2
                CALL    ADJ_ASCII
                MOVLW   DEC_3
                CALL    ADJ_ASCII
                MOVLW   DEC_4
                CALL    ADJ_ASCII
                BSF     LEADING_FLAG            ;ALWAYS DISPLAY CHR BEFORE DECIMAL POINT.
                MOVLW   DEC_5
                CALL    ADJ_ASCII
                MOVLW   DP
                MOVWF   DECIMAL_POINT           ;INSERT A DECIMAL POINT.
                MOVLW   DEC_6
                CALL    ADJ_ASCII
                MOVLW   DEC_7
                CALL    ADJ_ASCII

                BTFSS   NEGATIVE                ;WAS THIS A NEGATIVE NUMBER?
                RETURN                          ;N. JUST RETURN.
                MOVF    NEG_POSITION,W          ;Y. GET POSITION OF LAST BLANK.
                MOVWF   FSR
                MOVLW   '-'                     ;   PLACE A - SIGN IN FRONT OF THE NUMBER.
                MOVWF   INDF
                RETURN

;-----------------------------------------------------------------------------------------

ADJ_DEC:        MOVLW   DEC_7                   ;CALL ADJ_BCD TO ADJUST EACH OF
                CALL    ADJ_BCD                 ;THE 4 BCD DIGITS.
                MOVLW   DEC_5
                CALL    ADJ_BCD
                MOVLW   DEC_3
                CALL    ADJ_BCD
                MOVLW   DEC_1
                CALL    ADJ_BCD
                GOTO    LOOP24

;-----------------------------------------------------------------------------------------

ADJ_BCD:        MOVWF   FSR                     ;MAKE SURE THE BCD DIGITS ARE VALID.
                MOVLW   3                       ;CHECK LOW NIBBLE (LSD).
                ADDWF   INDF,W                  ;IF LSD + 3 > 7 THEN LSD = LSD + 3.
                MOVWF   TEMP                    ;ADD 3 TO LSD.
                BTFSC   TEMP,3                  ;IS RESULT > 7 ?
                MOVWF   INDF                    ;Y. LSD = LSD + 3.
                MOVLW   H'30'                   ;CHECK HIGH NIBBLE (MSD).
                ADDWF   INDF,W                  ;IF MSD + 3 > 7 THEN MSD = MSD + 3.
                MOVWF   TEMP                    ;ADD 3 TO MSD.
                BTFSC   TEMP,7                  ;IS RESULT > 7 ?
                MOVWF   INDF                    ;Y. MSD = MSD + 3.
                RETURN

;-----------------------------------------------------------------------------------------

ADJ_ASCII:      MOVWF   FSR
                MOVLW   B'00001111'             ;CLEAR THE HIGH NIBBLE.
                ANDWF   INDF,W
                BTFSC   ZERO
                GOTO    BLANK
NO_BLANK        ADDLW   H'30'                   ;CONVERT TO ASCII.
                MOVWF   INDF
                BSF     LEADING_FLAG
                RETURN

BLANK:          BTFSC   LEADING_FLAG            ;IF FLAG IS SET, DONT BLANK ZERO'S.
                GOTO    NO_BLANK
                MOVLW   ' '                     ;IF FLAG IS NOT SET, BLANK ZERO'S.
                MOVWF   INDF
                MOVF    FSR,W                   ;SAVE THE LOCATION OF THE LAST BLANK
                MOVWF   NEG_POSITION            ;SO THAT A NEGATIVE SIGN CAN BE INSERTED.
                RETURN

;*****************************************************************************************
;
;  MULTIPLY             16x8 BIT UNSIGNED FIXED POINT MULTIPLY 16x8 -> 24
;
;               Input:  16 BIT UNSIGNED FIXED POINT MULTIPLICAND IN ARG_H, ARG_L
;                        8 BIT UNSIGNED FIXED POINT MULTIPLIER IN ARG
;
;               Output: 24 BIT UNSIGNED FIXED POINT PRODUCT IN RESULT_H-M-L
;
;*****************************************************************************************

MULTIPLY:       CLRF    RESULT_L                ; CLEAR PARTIAL PRODUCT.
                MOVF    ARG_H,W
                MOVWF   RESULT_H
                MOVF    ARG_L,W
                MOVWF   RESULT_M
                MOVLW   8
                MOVWF   COUNT                   ;8 BITS TO PROCESS.

MULT_LOOP1:     RRF     ARG, F                  ;SHIFT THE 8 BIT ARG RIGHT. LSD TO CARRY.
                BTFSC   CARRY                   ;IS THE CARRY BIT = 1?
                GOTO    MULT_1
                DECFSZ  COUNT, F
                GOTO    MULT_LOOP1
                CLRF    RESULT_H
                CLRF    RESULT_M
                RETURN

MULT_1:         BCF     CARRY
                GOTO    MULT_2
MULT_LOOP2:     RRF     ARG, F
                BTFSS   CARRY
                GOTO    MULT_2

                MOVF    ARG_L,W                 ;Y. ADD MULTIPLICAND TO HIGH ORDER
                ADDWF   RESULT_M, F             ;   PARTIAL PRODUCT.
                MOVF    ARG_H,W
                BTFSC   CARRY
                INCFSZ  ARG_H,W
                ADDWF   RESULT_H, F

MULT_2:         RRF     RESULT_H, F             ;RIGHT SHIFT PARTIAL PRODUCT.
                RRF     RESULT_M, F
                RRF     RESULT_L, F
                DECFSZ  COUNT, F
                GOTO    MULT_LOOP2
                RETURN

;*****************************************************************************************
;
;  ADD                  16 BIT ADDITION.  ARG + ARG2 = ARG     (ARG 2 NOT CHANGED)
;
;               INPUT:  16 BIT ARGMENT 1 IN ARG_H, ARG_L.
;                       16 BIT ARGMENT 2 IN ARG2_H, ARG2_L.
;
;               OUTPUT: 16 BIT RESULT IN ARG_H, ARG_L.
;                       CARRY FLAG SET IF RESULT OVERFLOWED.
;
;*****************************************************************************************

ADD:            MOVF    ARG2_L,W
                ADDWF   ARG_L,F                 ;ADD THE LOW BYTES.
                MOVF    ARG2_H,W
                BTFSC   CARRY                   ;WAS THERE WAS A CARRY FROM THE LOW BYTE?
                INCFSZ  ARG2_H,W                ;Y. INC THE HIGH BYTE.
                ADDWF   ARG_H,F                 ;ADD THE HIGH BYTES.
                RETURN

;*****************************************************************************************
;
;  SUB                  16 BIT SUBTRACTION.  ARG - ARG2 = ARG     (ARG 2 NOT CHANGED)
;
;               INPUT:  16 BIT ARGMENT 1 IN ARG_H, ARG_L.
;                       16 BIT ARGMENT 2 IN ARG2_H, ARG2_L.
;
;               OUTPUT: 16 BIT RESULT IN ARG_H, ARG_L.
;                       (CARRY FLAG CLEAR (BORROW) IF RESULT WAS NEGATIVE.)
;
;*****************************************************************************************

SUB:            MOVF    ARG2_L,W
                SUBWF   ARG_L,F                 ;SUB THE LOW BYTES.
                MOVF    ARG2_H,W
                BTFSS   CARRY                   ;WAS THERE WAS A BORROW FROM THE LOW BYTE?
                INCFSZ  ARG2_H,W                ;Y. INC THE HIGH BYTE OF ARG 2.
                SUBWF   ARG_H,F                 ;SUB THE HIGH BYTES.
                RETURN

;*****************************************************************************************
;
;  CMP                  16 BIT COMPARE.  ARG - ARG2      (ARG & ARG 2 NOT CHANGED)
;
;               INPUT:  16 BIT ARGMENT 1 IN ARG_H, ARG_L.
;                       16 BIT ARGMENT 2 IN ARG2_H, ARG2_L.
;
;               OUTPUT: CARRY FLAG CLEAR (BORROW) IF RESULT WAS NEGATIVE.
;                       ZERO FLAG SET IF RESULT IS ZERO.
;
;*****************************************************************************************

CMP:            MOVF    ARG2_L,W
                SUBWF   ARG_L,W                 ;SUB THE LOW BYTES.
                MOVWF   TEMP                    ;SAVE THE LOW BYTE FOR ZERO CHECK.
                MOVF    ARG2_H,W
                BTFSS   CARRY                   ;WAS THERE WAS A BORROW FROM THE LOW BYTE?
                INCF    ARG2_H,W                ;Y. INC THE HIGH BYTE OF ARG 2.
                SUBWF   ARG_H,W                 ;SUB THE HIGH BYTES.
                IORWF   TEMP,W                  ;CHECK FOR ZERO RESULT.
                RETURN

;*****************************************************************************************
;
;  MINIMUM              16 BIT UNSIGNED MIN. RETURN THE SMALLEST OF TWO ARGMENTS.
;
;               INPUT:  16 BIT ARGMENT 1 IN ARG_H, ARG_L.
;                       16 BIT ARGMENT 2 IN ARG2_H, ARG2_L.
;
;               OUTPUT: THE SMALLEST ARGMENT IS RETURNED IN ARG_H, ARG_L.
;
;*****************************************************************************************

MINIMUM:        MOVF    ARG2_H,W                ;SAVE ARG 2.
                MOVWF   ARG3_H
                MOVF    ARG2_L,W
                MOVWF   ARG3_L

                MOVLW   1                       ;INC THE ARG2 BY 1.
                ADDWF   ARG2_L,F
                BTFSC   CARRY
                INCF    ARG2_H,F

                CALL    CMP
                BTFSS   CARRY                   ;IS ARG 1 > ARG 2 ?
                RETURN                          ;N. JUST RETURN.

                MOVLW   ARG3_H                  ;Y. SET ARG 1 THE SAME AS ARG 2.
                GOTO    RETURN_ARG              ;   INDICATE THAT ARG HAS BEEN CHANGED.

;*****************************************************************************************
;
;  MAXIMUM              16 BIT UNSIGNED MAX. RETURN THE LARGEST OF TWO ARGMENTS.
;
;               INPUT:  16 BIT ARGMENT 1 IN ARG_H, ARG_L.
;                       16 BIT ARGMENT 2 IN ARG2_H, ARG2_L.
;
;               OUTPUT: THE LARGEST ARGMENT IS RETURNED IN ARG_H, ARG_L.
;
;*****************************************************************************************

MAXIMUM:        CALL    CMP
                BTFSC   CARRY                   ;IS ARG 1 > ARG 2 ?
                RETURN                          ;Y. NO CHANGE REQUIRED.
                MOVLW   ARG2_H                  ;N. SET ARG 1 THE SAME AS ARG 2.
RETURN_ARG:     CALL    MOVE_TO_ARG
                BSF     MAX_MIN_MOD             ;   INDICATE THAT ARG HAS BEEN CHANGED.
                RETURN

;*****************************************************************************************
;  MISC ROUTINES.       MOVE_TO_ARG             MOVE THE DOUBLE BYTE POINTED TO BY INDF
;                                               REGISTER TO THE DOUBLE BYTE ARG VARIABLE.
;                       MOVE_TO_ARG2            MOVE THE DOUBLE BYTE POINTED TO BY INDF
;                                               REGISTER TO THE DOUBLE BYTE ARG2 VARIABLE.
;                       COPY_FROM_ARG           MOVE THE DOUBLE BYTE ARG VARIABLE TO THE
;                                               ADDRESS POINTED TO BY INDF REGISTER.
;                       LONG_DELAY              WAIT 100mS.
;*****************************************************************************************

MOVE_TO_ARG:    MOVWF   FSR
                MOVF    INDF,W
                MOVWF   ARG_H
                INCF    FSR,F
                MOVF    INDF,W
                MOVWF   ARG_L
                RETURN

;-----------------------------------------------------------------------------------------

COPY_FROM_ARG:  MOVWF   FSR
                MOVF    ARG_H,W
                MOVWF   INDF
                INCF    FSR,F
                MOVF    ARG_L,W
                MOVWF   INDF
                RETURN

;-----------------------------------------------------------------------------------------

MOVE_TO_ARG2:   MOVWF   FSR
                MOVF    INDF,W
                MOVWF   ARG2_H
                INCF    FSR,F
                MOVF    INDF,W
                MOVWF   ARG2_L
                RETURN

;-----------------------------------------------------------------------------------------

LONG_DELAY:     MOVLW   118
DELAY2:         MOVWF   COUNT1                  ; 118 LOOPS OF 845 uS = 100 mS.
                CLRF    COUNT2                  ; 256 LOOPS (3CLOCKS) x 1.1uS = 845 uS.
LD_LOOP:        DECFSZ  COUNT2,F
                GOTO    LD_LOOP
                DECFSZ  COUNT1,F
                GOTO    LD_LOOP
                RETURN

;*****************************************************************************************
;  UP_DOWN      INC OR DEC THE VARIABLE IN ARG. (ARG_H, ARG_L)
;
;               BY 1 WITH A QUICK PRESS OF FREQ + OR -. (SHIFT NOT PRESSED)
;               BY MULTIPLE 2'S WITH A LONG PRESS OF FREQ + OR -. (SHIFT NOT PRESSED)
;               BY 20 WITH A QUICK PRESS OF FREQ + OR -. (SHIFT PRESSED)
;               BY MULTIPLE 200'S WITH A LONG PRESS OF FREQ + OR -. (SHIFT PRESSED)
;*****************************************************************************************

UP_DOWN:        CLRF    ARG2_H
                MOVLW   1                       ;PREPARE TO INC/DEC BY 1. (NO SHIFT)
                BTFSS   BUTTON_S                ;IS THE SHIFT BUTTON PRESSED?
                MOVLW   20                      ;Y. PREPARE TO INC/DEC BY 20.
                MOVWF   ARG2_L                  ;  (SHIFT PRESSED)

                CALL    CHK_P_BRIEF
                BTFSC   PRESSED                 ;IF FREQ + PRESSED BRIEFLY
                CALL    ADD                     ;INC BY 1 OR 20.

                CALL    CHK_N_BRIEF
                BTFSC   PRESSED                 ;IF FREQ - PRESSED BRIEFLY
                CALL    SUB                     ;DEC BY 1 OR 20.

                MOVLW   2                       ;PREPARE TO INC/DEC BY 2. (NO SHIFT)
                BTFSS   BUTTON_S                ;IS THE SHIFT BUTTON PRESSED?
                MOVLW   200                     ;Y. PREPARE TO INC/DEC BY 200.
                MOVWF   ARG2_L                  ;  (SHIFT PRESSED)

                CALL    CHK_P_MULT
                BTFSC   PRESSED                 ;IF FREQ + PRESSED FOR A LONG TIME
                CALL    ADD                     ;MULTIPLE INC BY 2 OR 200.

                CALL    CHK_N_MULT
                BTFSC   PRESSED                 ;IF FREQ - PRESSED FOR A LONG TIME
                CALL    SUB                     ;MULTIPLE DEC BY 2 OR 200.

TRIM_NEG:       COMF    ARG_H,W                 ;ANY NUMBER BETWEEN 49151 AND 65535.
                ANDLW   B'11000000'             ;(C000 - FFFF)
                BTFSS   ZERO                    ;ARE INVALID SO SET ARG TO 0.
                RETURN
                CLRF    ARG_H
                CLRF    ARG_L
                RETURN

;*****************************************************************************************
;  EEPROM ROUTINES.     GET_SETUP               MOVE THE 17 BYTES OF SETUP INFO
;                                               FROM EEPROM TO RAM.
;                       UPDATE_EEPROM           IF THE EEPROM WRITE UPDATE FLAG IS SET,
;                                               PROGRAM ANY SETUP BYTES THAT HAVE CHANGED.
;                       READ_EEPROM             READ A BYTE FROM EEPROM. ASSUMES THE
;                                               EEPROM ADDRESS HAS BEEN PLACED IN EEADR.
;                       WRITE_EEPROM            WRITE THE BYTE IN W TO EEPROM. ASSUMES THE
;                                               EEPROM ADDRESS HAS BEEN PLACED IN EEADR.
;*****************************************************************************************

GET_SETUP:      bsf		RP0
				CLRF    EEADR                   ;POINT TO THE FIRST BYTE IN EEPROM.
				bcf		RP0
                MOVLW   MULT_NUM                ;GET BASE ADDRESS OF RAM BUFFER.
                MOVWF   FSR                     ;PLACE IT IN THE INDIRECT REGISTER.
                MOVLW   17
                MOVWF   COUNT                   ;17 BYTES TO READ.
R_EEPROM_LOOP:  CALL    READ_EEPROM             ;GET A BYTE FROM EEPROM.
                MOVWF   INDF                    ;PLACE IT IN RAM.
				bsf		RP0               
				INCF    EEADR,F                 ;PREPARE TO READ THE NEXT BYTE.
				bcf		RP0
                INCF    FSR,F
                DECFSZ  COUNT,F
                GOTO    R_EEPROM_LOOP           ;LOOP UNTIL ALL BYTES READ.
                RETURN

;-----------------------------------------------------------------------------------------

UPDATE_EEPROM:  BTFSS   EEPROM_UPDATE           ;IS THE UPDATE REQUIRED FLAG SET?
                RETURN                          ;N. ALL DONE.
                BCF     EEPROM_UPDATE           ;CLEAR THE FLAG.
				bsf		RP0
                CLRF    EEADR                   ;POINT TO THE FIRST BYTE IN EEPROM.
				bcf		RP0
                MOVLW   MULT_NUM                ;GET BASE ADDRESS OF RAM BUFFER.
                MOVWF   FSR                     ;PLACE IT IN THE INDIRECT REGISTER.
                MOVLW   17
                MOVWF   COUNT                   ;17 BYTES TO PROGRAM.
W_EEPROM_LOOP:	CALL    READ_EEPROM             ;GET A BYTE FROM EEPROM.
                SUBWF   INDF,W
                BTFSC   ZERO                    ;IS IT THE SAME AS IT IS IN RAM.
                GOTO    NO_CHANGE               ;Y. NO NEED TO UPDATE THIS BYTE.
                MOVF    INDF,W                  ;N. COPY THE BYTE FROM RAM TO EEPROM.
                CALL    WRITE_EEPROM
NO_CHANGE:      
				bsf		RP0
				INCF    EEADR,F                 ;PREPARE TO READ THE NEXT BYTE.
				bcf		RP0
                INCF    FSR,F
                DECFSZ  COUNT,F
                GOTO    W_EEPROM_LOOP           ;LOOP UNTIL ALL BYTES UPDATED.
				RETURN

;-----------------------------------------------------------------------------------------

READ_EEPROM:    BSF     RP0                     ;SELECT PAGE 1 FOR TRIS REG ACCESS.
                BSF     RD                      ;PERFORM AN EEPROM READ.
                MOVF    EEDATA,W                ;GET THE READ BYTE.
                BCF     RP0                     ;RETURN TO PAGE 0 FOR PORT ACCESS.
                RETURN

;-----------------------------------------------------------------------------------------

WRITE_EEPROM:   
				BCF     EEIF                    ;CLEAR THE EEPROM WRITE DONE FLAG.
				BSF		RP0
				MOVWF   EEDATA                  ;PUT DATA TO BE WRITEN IN THE BUFFER.
				BSF     WREN                    ;ENABLE EEPROM WRITE.
				bcf		INTCON,GIE
                MOVLW   H'55'                   ;PERFORM REQUIRED SAFETY STEPS.
                MOVWF   EECON2
                MOVLW   H'AA'
                MOVWF   EECON2
                BSF     WR                      ;BEGIN THE WRITE.
				bsf		INTCON,GIE
				bcf		RP0
WRITE_LOOP:     BTFSS   EEIF                    ;LOOP UNTIL THE WRITE IS COMPLETED.
                GOTO    WRITE_LOOP
                RETURN

;*****************************************************************************************
;  BUTTON ROUTINES.     CHK_S_BRIEF             RETURN A TRUE FLAG IN RESULT, IF THE
;                       CHK_P_BRIEF             BUTTON WAS PRESSED BRIEFLY.
;                       CHK_N_BRIEF             (SHIFT, + FREQ OR - FREQ)
;
;                       CHK_P_MULT              RETURN MULTIPLE TRUE FLAGS IN RESULT, IF
;                       CHK_N_MULT              THE BUTTON HAS BEEN PRESSED A WHILE.
;
;                       CHK_S_DOUBLE            RETURN A TRUE FLAG IN RESULT, IF THE
;                                               BUTTON HAS BEEN PRESSED TWICE BRIEFLY.
;*****************************************************************************************

CHK_S_BRIEF:    MOVLW   MAX_ON_S                ;GET THE UPPER TIME LIMIT FOR USE
                MOVWF   TEMP                    ;BY CHK_BRIEF.
                MOVF    BUT_S_OLD,W             ;GET THE OLD BUTTON STATE.
                MOVWF   RESULT                  ;SAVE THE OLD BUTTON STATE.
                SUBWF   BUT_S_CNT,W             ;COMPARE CURRENT AND SAVED BUTTON STATE.
                CALL    CHK_BRIEF               ;SEE IF BUTTON RELEASED BETWEEN MIN & MAX.
                MOVF    BUT_S_CNT,W
                MOVWF   BUT_S_OLD               ;SAVE THE CURRENT BUTTON STATE.
                RETURN

;-----------------------------------------------------------------------------------------

CHK_P_MULT:     MOVLW   MAX_ON + 10 + REPEAT    ;HAS THE BUTTON BEEN PRESSED A LONG WHILE?
                SUBWF   BUT_P_CNT,W
                BTFSS   CARRY
                GOTO    CHK_P_BRIEF
                MOVLW   MAX_ON + 10             ;Y. FOOL CHK_BRIEF INTO THINKING THAT
                MOVWF   BUT_P_CNT               ;   THE BUTTON HAS BEEN RELEASED.
                MOVLW   MAX_ON + 12 + REPEAT    ;   GET A LARGER UPPER TIME LIMIT FOR USE
                GOTO    CHK_P_BRIEF_1           ;   BY CHK_BRIEF.
CHK_P_BRIEF:    MOVLW   MAX_ON                  ;N. USE THE NORMAL UPPER LIMIT.
CHK_P_BRIEF_1:  MOVWF   TEMP
                MOVF    BUT_P_OLD,W             ;GET THE OLD BUTTON STATE.
                MOVWF   RESULT                  ;SAVE THE OLD BUTTON STATE.
                SUBWF   BUT_P_CNT,W             ;COMPARE CURRENT AND SAVED BUTTON STATE.
                CALL    CHK_BRIEF               ;SEE IF BUTTON RELEASED BETWEEN MIN & MAX.
                MOVF    BUT_P_CNT,W
                MOVWF   BUT_P_OLD               ;SAVE THE CURRENT BUTTON STATE.
                RETURN

;-----------------------------------------------------------------------------------------

CHK_N_MULT:     MOVLW   MAX_ON + 10 + REPEAT    ;HAS THE BUTTON BEEN PRESSED A LONG WHILE?
                SUBWF   BUT_N_CNT,W
                BTFSS   CARRY
                GOTO    CHK_N_BRIEF             ;N. USE CHK_N_BRIEF TO RETURN THE FLAGS.
                MOVLW   MAX_ON + 10             ;Y. FOOL CHK_BRIEF INTO THINKING THAT
                MOVWF   BUT_N_CNT               ;   THE BUTTON HAS BEEN RELEASED.
                MOVLW   MAX_ON + 12 + REPEAT    ;   GET A LARGER UPPER TIME LIMIT FOR USE
                GOTO    CKH_N_BRIEF_1           ;   BY CHK_BRIEF.
CHK_N_BRIEF:    MOVLW   MAX_ON                  ;N. USE THE NORMAL UPPER LIMIT.
CKH_N_BRIEF_1:  MOVWF   TEMP
                MOVF    BUT_N_OLD,W             ;GET THE OLD BUTTON STATE.
                MOVWF   RESULT                  ;SAVE THE OLD BUTTON STATE.
                SUBWF   BUT_N_CNT,W             ;COMPARE CURRENT AND SAVED BUTTON STATE.
                CALL    CHK_BRIEF               ;SEE IF BUTTON RELEASED BETWEEN MIN & MAX.
                MOVF    BUT_N_CNT,W
                MOVWF   BUT_N_OLD               ;SAVE THE CURRENT BUTTON STATE.
                RETURN

;-----------------------------------------------------------------------------------------

CHK_BRIEF:      BTFSC   CARRY                   ;HAS THE BUTTON BEEN RELEASED?
                GOTO    FALSE_FLAG              ;N. RETURN A FALSE FLAG.
                MOVF    RESULT,W                ;Y. GET THE OLD BUTTON STATE.
                SUBLW   MIN_ON                  ;   WAS IT ON LESS THAN THE MIN PERIOD?
                BTFSC   CARRY                   ;   (OLD-MIN=NEGATIVE, CARRY = 0)
                GOTO    FALSE_FLAG              ;   Y. RETURN A FALSE FLAG.
                MOVF    RESULT,W                ;GET THE OLD BUTTON STATE.
                SUBWF   TEMP,W                  ;   WAS IT ON LONGER THAN THE MAX PERIOD?
RETURN_FLAG:    MOVLW   H'FF'                   ;Y. PREPARE A TRUE FLAG.
                BTFSS   CARRY
FALSE_FLAG:     CLRW                            ;N. PREPARE A FALSE FLAG.
                MOVWF   RESULT                  ;RETURN THE FLAG.
                RETURN

;-----------------------------------------------------------------------------------------

CHK_S_DOUBLE:   CALL    CHK_S_BRIEF
                BTFSS   PRESSED                 ;WAS THE SHIFT PRESSED BRIEFLY?
                RETURN                          ;N. RETURN THE FALSE FLAG.
                MOVF    S_TIMER,W
                BTFSC   ZERO                    ;Y. WAS BUTTON PRESSED A SHORT WHILE AGO?.
                GOTO    FIRST_PRESS
                RETURN                          ;   Y. RETURN THE TRUE FLAG.
FIRST_PRESS:    MOVLW   DOUBLE_TIME             ;   N. THIS IS THE FIRST PRESS.
                MOVWF   S_TIMER                 ;      SET THE S_TIMER DOWN COUNTER.
                GOTO    FALSE_FLAG              ;      RETURN A FALSE FLAG.

;-----------------------------------------------------------------------------------------

                END


