; #define FSKON
; #define SIMULATORON                                                              
;
; ****************************************************************************
; * PEgen570 - Signal Generator (VFO) using the Si570 DSPLL by Silicon Labs  *
; *  Version 7.7                                                             *
; *  July 7, 2011                                                            *
; *  Copyright 2011 Craig B Johnson, AA0ZZ                                   *
; *                                                                          *
; *  EMAIL: aa0zz@cbjohn.com                                                 *
; *  WEB PAGE:  cbjohn@cbjohn.com/aa0zz                                      *
; ****************************************************************************
;
; Description:
; Originally, this was the control program for a DDS VFO built with an AD9850 
; DDS chip, a shaft encoder, two push button switches and an Liquid Crystal   
; Display.  This version supports a different device - the Si570 DSPLL from 
; Silicon Labs.  
;
; Features:
; CALIBRATE MODE is entered by pressing a pushbutton switch (Pushbutton 3) 
; during power-on. 
; 
; The frequency is changed by turning the encoder. The rate is changed by selecting
; which digit is being changed by the encoder, the current tuning digit being 
; underlined. The selected digit can be increased by pressing and releasing 
; pushbutton 3 (maximum is the 1M digit) and it can be decreased by pressing by
; pressing and releasing the "reset" pushbutton. The reset function has been disabled
; so this button can be used for this functionality.  The other two PIC-EL 
; pushbuttons are dual-use with the Paddles and, as such, are used for the I2C
; communications.  Pushing pushbutton 1 or 2 while tuning will cause incorrect data
; to be sent to the Si570.
; 
; The DIVBY2/4 feature allows a user to use PEgen570 with a quadrature sampling
; detector.  The QSD mechanism divides the input frequency by 2 or 4, so the Si570
; generated frequency must be 2 or 4 times as large to generate a chosen frequency.
; The modification, enabled via a Menu selection, causes the frequency word to 
; divided by 2 or 4 before being displayed on the LCD.  The original frequency is sent 
; to the Si570 and all frequency calculations (via encoder changes) are done with 
; this standard frequency.  The division factor changes the size of the base
; frequency updates such that the resulting frequency, as shown on the display,
; always increments/decrements by 1 Hz or more.  
; ===========================================================================
;
;******************************************************************************
; Modification History
;      - Start with PICELGen6.0c
; V1.1 - Basic operation
;      - Bug: No TRIS for SDA before read on I2C
; V2.0 - Changed PIC pins - different clock pin to daughtercard, separate data in and out
;      - Change to inverted logic for I2C
;      - Requires different interface circuitry with inversion
;      - Doesn't work because SDA INPUT on PB3 held low prevents write to LCD
; V2.1 - Changed PIC pins - Leave SCL on RB7 but move DATA OUT / DATA IN away 
;        from shared LCD pins.  Use jumpers to bring into paddle jack instead.
; V3.0 - Changed PIC pins again. Now use paddle jack for data connections.
;      - Back to 2-transistor bidirectional non-inverting interface
;      - NOW ABLE TO READ NVM BYTES.
; V3.1 - More code.  Now setting frequency to 14.025 MHz.  It works.
; V4.0 - Convert to 18F88. Works as before.                                            
; V4.1 - Change delays etc to internal oscillator running at 8 MHz                                            
; V4.2 - Add band tables to program memory
;      - Add bank 2 fxfactor tables
;      - Add EEPROM table with defaults for fxtal=114,285,000
;      - Initialize RAM from EEPROM
;      - BuildSiWords now works. Frequency set by equates for freq and band
;      - New SetUpFxtal called by Cal routine - works in simulator. (Cal not done.)
;      - Using DEFAULTFOUT to specify frequency - Works on hardware
;      - Using default Fxtal value of 114285000 for HSN1NX table. 
; V4.3 - Fix band tables in program memory
;      - Remove hard-code for N1 and HSDIV
;      - Now can use DEFAULTFOUT and DEFAULTBAND to set to any frequency in any band
; V4.4 - Change Mult8x8 routine for efficiency
;      - Calibrate now updates EEPROM with corrected values
;      - Activate calibrate by holding PB3 at start-up
; V4.5 - Change Fxtalx16 to Fxtalx256 to preserve accuracy
; V4.6 - Disable MCLR Reset via pushbutton with new CONFIG statement
;      - Enhance user interface - poll_encoder
;      - Change CalcRFREQ to multiply freq by 256 at start
;      - Start ShowActiveDigit mechanism
;      - Increment/Decrement via encoder now works
; V4.7 - HSN1FX table default values corrected slightly
;      - Add NEEDFREEZE mechanism to skip freeze whenever possible 
; V4.8 - Add TuningStepSize mechanism - up to 100K digit
;      - Possible stack overflows because of CheckForChanges->PB_Look->BuildSiWords->
;           DisplaySIRegs->DisplayByte->Data2LCD->Busy_Check->Interrupt....
;      - Fix by moving call to PB_Look from CheckForChanges to main 
;      - New detect_band_change
;      - SiBandTable values filled in
;      - Reverse the order of band tables (14 MHz is now band 3)
;      - Band displayed at end of second LCD line
;      - Add 100M digit in LCD frequency display
;      - Add code for checking for lower limit
; V4.9 - Save start-up frequency in EEPROM - retrieve at start-up
; V5.0 - Detect frequency span from center to determine need for FREEZE
; V5.1 - Change to detect RFREQ difference from center to determine need for FREEZE
; V5.2 - Fix bug in Fxtal calculation                                                   
; V5.3 - Fix bug in span calculation for 100k tuning 
; V5.4 - Add mask for encoder_data to guarantee a noisy encoder doesn't cause overflow
; V6.0 - Add FSK code
;      - Move I2C code and SaveSiWords to Page 1
; V6.1 - Add 1 MHz tuning step
;      - Add #DEFINE FSKON 
; V6.2 - Support DIVBY2 and DIVBY4
;      - Move interrupt handler to Page 1
;      - Remove all freq_4 bytes and related code. (Not needed)
; V6.3 - Fix bugs in band table entries 4 and 22
;      - Fix upper band limit (add entry to SiBandTable)
; V6.4 - Adjust delays in the I2C routines to meet I2C Spec and 
;        alleviate possible read-modify-write problems
; V6.5 - Remove leading zeros from frequency display
; V6.6 - Move FSKMARK from RA5 to RA2 to match the PCB (Header HDR7)
;      - Adjust I2C delays again
;      - Add Calibrating to display
;      - Remove SiWords from second LCD line unless DEBUGON
; V6.7 - Fix bug in leading zero suppression 
; V6.8 - Change FSK Modulation pin.  
; V7.0 - Change from Si570 to Si598
;      - Change startup frequency from 56.320 MHz to 10.0 MHz
;      - Use new Freeze M bit in SiWord 135
; V7.1 - Fix FSK bug - restore to MARK frequency if SPACE was last
; V7.2 - Fix EEPROM default tables for Si598 (39.17 MHz Fxtal instead of 114.285 MHz)
;      - Add Menu to select USB/LSB/CW+/CW-  (Sideband change via toggle switch) 
;      - Add CW offset shift
;      - Add SB Relay Select
; V7.3 - Change menu to change sideband also.  No switch needed.
;      - Change RA6 and RA7 from Inputs to Outputs to drive IQ relay. 
;      - Always set NEW FREQ bit for Si598, even if FREEZE-M done  << Bad
;      - Move Busy_Check inside LCD routine to save a stack level. 
; v7.4 - Move DisplaySiRegs from Page 0 to Page 1
;      - Change CWShiftCheck and FSKShiftCheck to inside ReadEncoderFor25mS loop
;      - Disable interrupts during DisplaySiRegs to prevent stack overflows
;      - FORCESEND
;      - Eliminate FSKCheck by combining FSKSetUpRegs with CWSetUpRegs in CWShiftCheck
;      - Combine XSiRegs with MSiRegs -> XMSiRegs, RSiRegs with SSiRegs -> RSSiRegs
;      - Fix bugs in CWSetUpRegs re banking
;      - Remove setting of NEW FREQ bit during UNFREEZE-M.  No pops in speaker now.
;      - Change from Si598 back to Si570. (Higher Fxtal freq gives better phase noise)
; v7.5 - Handle DIVBY2/DIVBY4 selection in menu instead of a config statement
;      - Handle DEBUG mode start (both PB3 and 4 at startup) instead of config statement
; v7.6 - Fix bugs re frequency update after menu operation
;        * Clear encoder_data after using it in frequency_update
;        * Clear C before second right shift on Divide-by-4 selection
;      - New feature - save ModeSelect in EEPROM when it's changed.  Restore on power-up.
; v7.7 - Fix two bugs  
;        1) Don't save or recover DEBUG mode from EEPROM upon power-up 
;        2) Fix the missing calls to update SiRegs when mode is U or L. (LCD update was OK.)
;
;*****************************************************************************
;                                                                              
; Target Controller -      PIC16F88 in PIC-EL board                            
;                               __________                                          
;     PB_3/INC/CAL/SAVE----RA2 |1       18| RA1---------ENCODER A                   
;     PB_2/SDA-------------RA3 |2       17| RA0---------ENCODER B   
;     PB_1/SCL-------------RA4 |3       16| RA7-------- LOWSELECTMINUS
;     PB_4/DEC-------------RA5 |4       15| RA6-------- LOWSELECTPLUS
;     Ground---------------Vss |5       14| VDD---------+5v                         
;     LCD11----------------RB0 |6       13| RB7---------FSKLOWSPACE/CWLOWRCV
;     LCD12----------------RB1 |7       12| RB6---------LCD_rs  (LCD Pin 4)         
;     LCD13----------------RB2 |8       11| RB5---------LCD_rw  (LCD Pin 5)           
;     LCD14----------------RB3 |9       10| RB4---------LCD_e   (LCD Pin 6)         
;                               ----------                                          
;                                                                              
; ****************************************************************************
; * Device type and options.                                                 *
; ****************************************************************************
;
        processor 16F88  
        radix     dec

; ****************************************************************************
; * Configuration fuse information for 16F88:                                *
; ****************************************************************************

        include   <P16F88.INC>
; 16F88 CONFIG - Shown here for info purposes only
;_CONFIG1                    EQU     H'2007'
;_CONFIG2                    EQU     H'2008'
;;Configuration Byte 1 Options
;_CP_ALL                      EQU     H'1FFF'
;_CP_OFF                      EQU     H'3FFF'
;_CCP1_RB0                    EQU     H'3FFF'
;_CCP1_RB3                    EQU     H'2FFF'
;_DEBUG_OFF                   EQU     H'3FFF'
;_DEBUG_ON                    EQU     H'37FF'
;_WRT_PROTECT_OFF             EQU     H'3FFF'    ;No program memory write protection
;_WRT_PROTECT_256             EQU     H'3DFF'    ;First 256 program memory protected
;_WRT_PROTECT_2048            EQU     H'3BFF'    ;First 2048 program memory protected
;_WRT_PROTECT_ALL             EQU     H'39FF'    ;All of program memory protected
;_CPD_ON                      EQU     H'3EFF'
;_CPD_OFF                     EQU     H'3FFF'
;_LVP_ON                      EQU     H'3FFF'
;_LVP_OFF                     EQU     H'3F7F'
;_BODEN_ON                    EQU     H'3FFF'
;_BODEN_OFF                   EQU     H'3FBF'
;_MCLR_ON                     EQU     H'3FFF'
;_MCLR_OFF                    EQU     H'3FDF'
;_PWRTE_OFF                   EQU     H'3FFF'
;_PWRTE_ON                    EQU     H'3FF7'
;_WDT_ON                      EQU     H'3FFF'
;_WDT_OFF                     EQU     H'3FFB'
;_EXTRC_CLKOUT                EQU     H'3FFF'
;_EXTRC_IO                    EQU     H'3FFE'
;_INTRC_CLKOUT                EQU     H'3FFD'
;_INTRC_IO                    EQU     H'3FFC'
;_EXTCLK                      EQU     H'3FEF'
;_HS_OSC                      EQU     H'3FEE'
;_XT_OSC                      EQU     H'3FED'
;_LP_OSC                      EQU     H'3FEC'
;;Configuration Byte 2 Options
;_IESO_ON                     EQU     H'3FFF'
;_IESO_OFF                    EQU     H'3FFD'
;_FCMEN_ON                    EQU     H'3FFF'
;_FCMEN_OFF                   EQU     H'3FFE'
;

  __config  _CONFIG1, _INTRC_IO & _WDT_OFF & _BODEN_OFF & _LVP_OFF & _PWRTE_ON & _CCP1_RB3 & _MCLR_OFF
  __config  _CONFIG2, _IESO_OFF & _FCMEN_OFF

;
; *************************************************************************** 
; Info for power-up display                                                   
MCODE_REV_0  equ  ' '     ; Current code version is 7.7
MCODE_REV_1  equ  'v'     ;                                                   
MCODE_REV_2  equ  '7'     ;                                                   
MCODE_REV_3  equ  '.'     ;

MCODE_REV_4  equ  '7'     ;
MCODE_REV_5  equ  ' '     ;
MCODE_REV_6  equ  ' '     ;
MCODE_REV_7  equ  ' '     ;
 
;                                                                             
; ****************************************************************************
; * General equates.  These may be changed to accommodate the reference clock* 
; * frequency, the desired upper frequency limit, and the default startup    *
; * frequency.                                                               *
; ****************************************************************************

; Limit contains the lower limit frequency as a 32 bit integer.
; Si570 lower limit is 10 MHz. 
; 
LLIMIT_3   equ 0x00                ; Most significant byte for 10MHz
LLIMIT_2   equ 0x98                ; Next byte                                 
LLIMIT_1   equ 0x96                ; Next byte                                 
LLIMIT_0   equ 0x80                ; Least significant byte                    
;
; Limit contains the upper limit frequency as a 32 bit integer
; based on Si570 spec.  For the CMOS part the limit is 160 MHz; however, band
; table size limits (24 bands) keep the upper limit to 157 MHz. 
;
ULIMIT_3   equ 0x09                ; Most significant byte for 157MHz
ULIMIT_2   equ 0x5B                ; Next byte                                 
ULIMIT_1   equ 0xA1                ; Next byte                                 
ULIMIT_0   equ 0x40                ; Least significant byte                    
;
HIGHEST_BAND_BASE equ 0x60        ; The offset to base of last band table entry         
;
; PB_flags bits                                                               
PB1first equ 0                    ; Bit set indicates PB1 pressed first       
PB1Calibrate_Active equ 1         ; Bit set indicates calibrate is now active
;
EEStartupFreqAdr equ 0x60         ; Location of startup frequency save in EEPROM v7.6            
EEModeSelectAdr   equ 0x64        ; Location of ModeSelect save in EEPROM   ; v7.6          

; Default start-up frequency Fout = 56,320,000 for this part
;DEFAULTSI570FREQ3 equ 0x03             ; MSB
;DEFAULTSI570FREQ2 equ 0x5B             ;
;DEFAULTSI570FREQ1 equ 0x60             ;
;DEFAULTSI570FREQ0 equ 0x00             ; LSB

; Default start-up frequency Fout = 10,000,000 for this part
DEFAULTSI570FREQ3 equ 0x00             ; MSB
DEFAULTSI570FREQ2 equ 0x98             ;
DEFAULTSI570FREQ1 equ 0x96             ;
DEFAULTSI570FREQ0 equ 0x80             ; LSB

HSN1FXTBLSTART equ 0          ; Location of start of HSN1FX Table in EEPROM

; With 8 MHz internal oscillator
; 8 MHz = .125 uS/cycle or .5 uS per instruction (1 tick per instruction). 
; 25ms = 25000us = 50,000 instructions(i.e, ticks).
; With 2:1 prescaler, count should be 25000. 
; Interrupt occurs when timer increments to 65535 and rolls over to 0000.
; 65535-25000 = 40535.= 0x09E57.  Thus initialize the timer to 0x09E57.  
TIM25MSLOW       EQU     0x57     ; Low byte for 25 ms timer                    
TIM25MSHIGH      EQU     0x9E     ; High byte for 25 ms timer
TIM25MSPRESCALE  EQU     0x11     ; Set 1:2 Prescale and TMR1ON for 25 ms timer 

; Equates for VFOcontrol register
;
DIR_UP      equ   7       ; Encoder UP direction (decode of ENC3 - for encdir)
NEEDUPDATE  equ   6       ; A change has been made, so need DDS and LCD update
NEEDFREEZE  equ   5       ; Set if a Si570 freeze needed on this update
INTOCCURRED equ   4       ; Interrupt Occurred
BANDCHANGED equ   3       ; Band changed            
FORCESEND   equ   2       ; Send SI bytes always
CWREGSOK    equ   1       ; Status of Si registers for CW Shift 
CWXMITLAST  equ   0       ; XMIT was last set up 

FSKCOMP_3   equ   0xFF    ; Complement of frequency shift size PLUS 1   MSB
FSKCOMP_2   equ   0xFF    ;  170 Hz = 0xAA, so (complement + 1) = 0x56
FSKCOMP_1   equ   0xFF    ; 
FSKCOMP_0   equ   0x56    ; Complement of 170 Hz (plus 1) frequency shift LSB

SHIFTCOMP_3 equ   0xFF    ; Complement of frequency shift size PLUS 1   MSB
SHIFTCOMP_2 equ   0xFF    ;  600 Hz = 0xDA7 so (complement + 1) = 0xFDA8
SHIFTCOMP_1 equ   0xFD    ; 
SHIFTCOMP_0 equ   0xA8    ; Complement of 600 Hz (plus 1) frequency shift LSB

SHIFT_3     equ   0x00    ; Frequency shift size  600 Hz   MSB
SHIFT_2     equ   0x00    ;               
SHIFT_1     equ   0x02    ; 
SHIFT_0     equ   0x58    ; Frequency shift size  600 Hz LSB

; Equates for FSKcontrol
FSKACTIVE   equ   7
MARKLAST    equ   6

; Equates for MenuResponse (temp1)              
MENUTOGGLE   equ  7       ;
MENUEXIT     equ  6       ;

; Equates for ModeSelect                 
MODELSB      equ  7       ;
MODEUSB      equ  6       ;
MODECWMINUS  equ  5       ;
MODECWPLUS   equ  4       ;
MODEDIVBY2   equ  3   ;
MODEDIVBY4   equ  2       ;
MODEDEBUG    equ  1       ;
RUNMENU      equ  0       ; 

;
; ****************************************************************************
; *                    Port and EEPROM Constants                             *
; ****************************************************************************
;
; (Listed here for reference only)
;PORTA   equ     0x05
;PORTB   equ     0x06
;TRISA   equ     0x85
;TRISB   equ     0x86
;EEDATA  equ     0x10C         ; Bank 2 for 16F88 
;EEadr   equ     0x10D         ; Bank 2 for 16F88 
;EECON1  equ     0x18C         ; Bank 3 for 16F88 
;EECON2  equ     0x18D         ; Bank 3 for 16F88
;
; ****************************************************************************
; * Setup the initial constant, based on the frequency of the reference      *
; * oscillator.  This can be tweaked with the calibrate function.            *
; ****************************************************************************
; 
        ORG     0x2100

; HSN1FX EEPROM Table for Si598 - start with default Fxtal (39,170,000) but update by calibrate
;   Fxtalx256 / HSDIVN1 
;
;  DATA 0x01,0x2E,0x63,0x11 ; B0   10 - 11 MHz  ((39,170,000)/(46*11)) * 256 = 012E6311
;  DATA 0x01,0x54,0x04,0x71 ; B1   11 - 12 MHz  ((39,170,000)/(50*9))  * 256 = 01540471
;  DATA 0x01,0x6E,0x0C,0x22 ; B2   12 - 13 MHz  ((39,170,000)/(38*11)) * 256 = 016E0C22
;  DATA 0x01,0x99,0x1C,0x9E ; B3   13 - 15 MHz  ((39,170,000)/(34*11)) * 256 = 01991C9E
;  DATA 0x01,0xCF,0xA8,0xF8 ; B4   15 - 17 MHz  ((39,170,000)/(30*11)) * 256 = 01CFA8F8
;  DATA 0x02,0x16,0xFE,0x0A ; B5   17 - 19 MHz  ((39,170,000)/(26*11)) * 256 = 0216FE0A
;  DATA 0x02,0x43,0x93,0x36 ; B6   19 - 21 MHz  ((39,170,000)/(24*11)) * 256 = 02439336
;  DATA 0x02,0x78,0x43,0x81 ; B7   21 - 23 MHz  ((39,170,000)/(22*11)) * 256 = 02784381
;  DATA 0x02,0xB7,0x7D,0x74 ; B8   23 - 25 MHz  ((39,170,000)/(20*11)) * 256 = 02B77D74
;  DATA 0x03,0x04,0xC4,0x48 ; B9   25 - 28 MHz  ((39,170,000)/(18*11)) * 256 = 0304C448
;  DATA 0x03,0x65,0x5C,0xD1 ; B10  28 - 32 MHz  ((39,170,000)/(16*11)) * 256 = 03655CD1
;  DATA 0x03,0xE1,0x8E,0xA6 ; B11  32 - 36 MHz  ((39,170,000)/(22*7)) * 256 =  03E18EA6
;  DATA 0x04,0x65,0x0E,0xB4 ; B12  36 - 41 MHz  ((39,170,000)/(34*4)) * 256 =  04650EB4
;  DATA 0x04,0xFB,0x10,0xAA ; B13  41 - 47 MHz  ((39,170,000)/(20*6)) * 256 =  04FB10AA
;  DATA 0x05,0xBF,0x3A,0x9D ; B14  47 - 54 MHz  ((39,170,000)/(26*4)) * 256 =  05BF3A9D
;  DATA 0x06,0xA4,0x16,0x38 ; B15  54 - 61 MHz  ((39,170,000)/(10*9)) * 256 =  06A41638
;  DATA 0x07,0x78,0x99,0x00 ; B16  61 - 70 MHz  ((39,170,000)/(16*5)) * 256 =  07789900
;  DATA 0x08,0x89,0xD3,0x6D ; B17  70 - 81 MHz  ((39,170,000)/(10*7)) * 256 =  0889D36D
;  DATA 0x09,0xF6,0x21,0x55 ; B18  81 - 90 MHz  ((39,170,000)/(10*6)) * 256 =  09F62155
;  DATA 0x0B,0x11,0x7A,0x5E ; B19  90 - 101 MHz ((39,170,000)/(6*9)) * 256 =   0B117A5E
;  DATA 0x0C,0x73,0xA9,0xAA ; B20 101 - 111 MHz ((39,170,000)/(8*6)) * 256 =   0C73A9AA
;  DATA 0x0D,0x95,0x73,0x45 ; B21 111 - 128 MHz ((39,170,000)/(4*11))* 256 =   0D957345
;  DATA 0x0E,0xF1,0x32,0x00 ; B22 128 - 135 MHz ((39,170,000)/(8*5)) * 256 =   0EF13200             
;  DATA 0x10,0x9A,0x37,0x8E ; B23 135 - 157 MHz ((39,170,000)/(4*9)) * 256 =   109A378E

; OLD HSN1FX EEPROM Table for Si570 - start with default Fxtal (114,285,000) but update by calibrate
;   Fxtalx256 / HSDIVN1 
;
  DATA 0x03,0x72,0x43,0xAF ; B0   10 - 11 MHz  ((114,285,000)/(46*11)) * 256 = 037243AF
  DATA 0x03,0xE0,0x0E,0xAA ; B1   11 - 12 MHz  ((114,285,000)/(50*9))  * 256 = 03E00EAA
  DATA 0x04,0x2C,0x01,0x17 ; B2   12 - 13 MHz  ((114,285,000)/(38*11)) * 256 = 042C0117
  DATA 0x04,0xA9,0xA6,0xDD ; B3   13 - 15 MHz  ((114,285,000)/(34*11)) * 256 = 04A9A6DD
  DATA 0x05,0x48,0xCE,0x2E ; B4   15 - 17 MHz  ((114,285,000)/(30*11)) * 256 = 0548CE2E
  DATA 0x06,0x18,0xED,0xE6 ; B5   17 - 19 MHz  ((114,285,000)/(26*11)) * 256 = 0618EDE6
  DATA 0x06,0x9B,0x01,0xBA ; B6   19 - 21 MHz  ((114,285,000)/(24*11)) * 256 = 069B01BA
  DATA 0x07,0x34,0xBC,0x10 ; B7   21 - 23 MHz  ((114,285,000)/(22*11)) * 256 = 0734BC10
  DATA 0x07,0xED,0x35,0x45 ; B8   23 - 25 MHz  ((114,285,000)/(20*11)) * 256 = 07ED3545
  DATA 0x08,0xCE,0xAC,0xF8 ; B9   25 - 28 MHz  ((114,285,000)/(18*11)) * 256 = 08CEACF8
  DATA 0x09,0xE8,0x82,0x97 ; B10  28 - 32 MHz  ((114,285,000)/(16*11)) * 256 = 09E88297
  DATA 0x0B,0x52,0xDE,0x63 ; B11  32 - 36 MHz  ((114,285,000)/(22*7)) * 256 =  0B52DE63
  DATA 0x0C,0xD2,0x8A,0xE1 ; B12  36 - 41 MHz  ((114,285,000)/(34*4)) * 256 =  0CD28AE1
  DATA 0x0E,0x88,0x37,0x00 ; B13  41 - 47 MHz  ((114,285,000)/(20*6)) * 256 =  0E883700
  DATA 0x10,0xC4,0x8E,0x3B ; B14  47 - 54 MHz  ((114,285,000)/(26*4)) * 256 =  10C48E3B
  DATA 0x13,0x60,0x49,0x55 ; B15  54 - 61 MHz  ((114,285,000)/(10*9)) * 256 =  13604955
  DATA 0x15,0xCC,0x52,0x80 ; B16  61 - 70 MHz  ((114,285,000)/(16*5)) * 256 =  15CC5280
  DATA 0x18,0xE9,0x82,0xDB ; B17  70 - 81 MHz  ((114,285,000)/(10*7)) * 256 =  18E982DB
  DATA 0x1D,0x10,0x6E,0x00 ; B18  81 - 90 MHz  ((114,285,000)/(10*6)) * 256 =  1D106E00
  DATA 0x20,0x4B,0x24,0xE3 ; B19  90 - 101 MHz ((114,285,000)/(6*9)) * 256 =   204B24E3
  DATA 0x24,0x54,0x89,0x80 ; B20 101 - 111 MHz ((114,285,000)/(8*6)) * 256 =   24548980 
  DATA 0x27,0xA2,0x0A,0x5D ; B21 111 - 128 MHz ((114,285,000)/(4*11))* 256 =   27A20A5D 
  DATA 0x2B,0x98,0xA5,0x00 ; B22 128 - 135 MHz ((114,285,000)/(8*5)) * 256 =   2B98A500                
  DATA 0x30,0x70,0xB7,0x55 ; B23 135 - 157 MHz ((114,285,000)/(4*9)) * 256 =   3070B755

   DATA 0x00,0xD6,0x01,0x28        ; startup = 14025000
   ; DATA 0x00,0x98,0x96,0x80        ; startup = 10M
   ; DATA 0x00,0x98,0x97,0x2A        ; startup = 10,000,170
   ; DATA 0x01,0x03,0x66,0x40        ; startup = 17M
   ; DATA 0x07,0xBF,0xA4,0x80        ; startup = 130M
   DATA 0x10                       ; ModeSelect     CW+                         ; v7.6

;    Clear unused EEPROM bytes (256 bytes for 16F88)
;    0x21XX - 0x21FF                    
; 
; ****************************************************************************
; * RAM page independent file registers:                                     *
; ****************************************************************************
;
; (Listed here for reference only)
;INDF    EQU     0x00
;PCL     EQU     0x02
;STATUS  EQU     0x03
;FSR     EQU     0x04
;PCLATH  EQU     0x0A
;INTCON  EQU     0x0B
;GIE     EQU     7                 ; INTCON Bit - Enable/Disable interrupts
;
; *****************************************************************************
; * Bit numbers for the STATUS file register:                                 *
; *****************************************************************************
;
; (Listed here for reference only)
;RP0   EQU     5
;NTO   EQU     4
;NPD   EQU     3
;Z     EQU     2
;DC    EQU     1
;C     EQU     0
;
; ****************************************************************************
; * Assign names to IO pins.                                                 *
; ****************************************************************************
; 
;   A register bits:
;
PB_3      equ   0x02              ; RA2 I PB 
PB_INC    equ   0x02              ; RA2 I PB Increment Tuning Digit
PB_CAL    equ   0x02              ; RA2 I Calibrate pushbutton
SDA       equ   0x03              ; RA3 O I2C Data 
SCL       equ   0x04              ; RA4 O I2C Clock
PB_4      equ   0x05              ; RA5 I PB (Was MCLR)
PB_DEC    equ   0x05              ; RA5 I PB-Decrement tuning digit
LOWSELECTPLUS equ 0x06            ; RA6 O Sideband Select Relay
LOWSELECTMINUS equ 0x07           ; RA7 O Sideband Select Relay

;
;   B register bits:
;
;               0x02              ; Daughtercard Pin 2 (unused)
;               0x03              ; Daughtercard Pin 3 (unused)
LCD_busy  equ   0x03              ; RB3 O LCD busy bit 
LCD_e     equ   0x04              ; RB4 O 0=disable, 1=enable
LCD_rw    equ   0x05              ; RB5 O 0=write, 1=read
LCD_rs    equ   0x06              ; RB6 O 0=instruction, 1=data
FSKLOWSPACE equ 0x07              ; RB7 I FSK SPACE when low (170 Hz lower than MARK) 
CWLOWRCV  equ   0x07              ; RB7 I CW SHIFT - RCV when low (Shift up/down from XMIT) 
;
; ****************************************************************************
; *           Allocate variables in general purpose register space           *
; ****************************************************************************
;
        CBLOCK  0x20              ; Start Data For Bank 0
;
        Save_W                    ; Save W      (for ISR)
        Save_S                    ; Save STATUS (for ISR) 
        Save_PCLATH               ; Save PCLATH (for ISR)
        Save_FSR                  ; Save FSR    (for ISR)

        tick_count                ; Raw tick counter incremented in POLL
        dir                       ; Direction of encoder
        VFOcontrol                ; Specifies VFO control flags
         ; DIR_UP      equ  7     ; Bit indicates Tuning Direction UP
         ; NEEDUPDATE  equ  6     ; A change has been made, so need DDS and LCD update
         ; NEEDFREEZE  equ  5
         ; INTOCCURRED equ  4     ; Interrupt Occurred
         ; BANDCHANGED equ  3     ; 
         ; FORCESEND   equ  2     ; 
         ; CWREGSOK    equ  1     ; 
         ; CWXMITLAST  equ  0     ; 

        encoder_data              ; Set up by interrupt handler, used for updating frequency
        ; Bits 5 - 0 = Tick_count ;   6 bits indicates number of ticks in 25 ms

        freq_3                    ; MSB  Si570 frequency (hex)  
        freq_2                    ;  (4 bytes) 
        freq_1                    ;
        freq_0                    ; LSB             

        Dfreq_3                   ; MSB  Display requency (hex)  
        Dfreq_2                   ;  (4 bytes) 
        Dfreq_1                   ;
        Dfreq_0                   ; LSB             

        RFREQCenter_High          ; High byte from SiRegs8 of last FREEZE
        RFREQCenter_Medium        ; Medium byte from SiRegs9 of last FREEZE
        RFREQCenter_Low           ; Low byte from SiRegs10 of last FREEZE
       
        BCD_4                     ; MSB Display frequency (BCD) (Order is critical)
        BCD_3                     ;  (5 bytes)
        BCD_2                     ;
        BCD_1                     ;
        BCD_0                     ; LSB
       
        fstep_3                   ; MSB Frequency inc/dec 
        fstep_2                   ;  (4 bytes)
        fstep_1                   ;
        fstep_0                   ; LSB

        tuning_digit              ; Current digit being incremented/decrement by encoder
        
        BCD_count                 ; Used in bin2BCD routine
        BCD_temp                  ;   "
        mult_count                ; Used in calc_dds_word 
        bit_count                 ;   "
        byte2send                 ;
        LCD_char                  ; Character being sent to the LCD
        LCD_read                  ; Character read from the LCD
        timer1                    ; Used in delay routines
        timer2                    ;   "
        ren_new                   ; New value of encoder pins A and B
        ren_old                   ; Old value of encoder pins A and B
        ren_read                  ; Encoder pins A and B and switch pin
        last_dir                  ; Indicates last direction of encoder
        next_dir                  ; Indicates expected direction
        count                     ; loop counter  (gets reused)
        
        band                      ; Current band number 
        band_index                ; Current band index  
        new_band_base             ; Index to lowest byte of new band in freq table
        new_band_index            ; Index to current byte of new band in freq table
                                  ; (NOTE: HIGHEST_BAND_BASE set up as an EQUATE!)

        rs_value                  ; The LCD rs line flag value
        PB_flags                  ; Pushbutton flags                          
;
        I2Cbytcnt
        I2Cebyte
        I2Ceadd

        CurHsdivIndex          ; Working
        CurN1Minus1            ; Working
        CurHsdivN1D2           ; Working

        Fxtalx256_4            ; MSB of Si570 internal crystal frequency * 256
        Fxtalx256_3            ; 
        Fxtalx256_2            ;
        Fxtalx256_1            ;
        Fxtalx256_0            ; LSB

        FxFactor3              ; MSB - (Fxtal/HSDIVN1)*256
        FxFactor2              ;
        FxFactor1              ;
        FxFactor0              ; LSB

        SiReg7                 ; HSDIV[2:0] and N1[6:2]
        SiReg8                 ; N1[1:0] and RFREQ4[37:32] 
        SiReg9                 ; RFREQ3[31:24]
        SiReg10                ; RFREQ2[23:16]
        SiReg11                ; RFREQ1[15:8]
        SiReg12                ; RFREQ0[7:0]
        SiReg135               ;
        SiReg137               ;

        temp1
        temp2
        temp3
        temp4

        ModeSelect
         ;MODELSB     equ   7
         ;MODEUSB     equ   6
         ;MODECWMINUS equ   5
         ;MODECWPLUS  equ   4
         ;MODEDIVBY2  equ   3
         ;MODEDIVBY4  equ   2
         ;MODEDEBUG   equ   1
         ;RUNMENU     equ   0

        FSKcontrol
     ;FSKACTIVE   equ   7
         ;MARKLAST    equ   6
;
        ENDC                      ; End of Data Block

;       WATCH IT!  Max unique is 0x6F, Then 16 shared bytes 0x70 through 0x7F

; ========================================================================
        CBLOCK  0x70             ; Start Data For SHARED BYTES

        RegA4                  ; MSB for 38-bit divide - Start with dividend here and end with quotient
        RegA3                  ; 
        RegA2                  ;
        RegA1                  ;
        RegA0                  ; LSB

        RegB4                  ; MSB - Start with dividend and end with quotient
        RegB3                  ; 
        RegB2                  ;
        RegB1                  ;
        RegB0                  ; LSB

        RegC4                  ; MSB - Contains remainder during division calculation
        RegC3                  ;
        RegC2                  ;
        RegC1                  ;
        RegC0                  ; LSB

        ENDC                      ; End of Data Block

;       WATCH IT!  Then 16 shared bytes 0x70 through 0x7F

; ========================================================================
        CBLOCK  0xA0              ; Start Data Block For Bank 1  (80 bytes)

        MarkFreq_3
        MarkFreq_2
        MarkFreq_1
        MarkFreq_0

        SpaceFreq_3
        SpaceFreq_2
        SpaceFreq_1
        SpaceFreq_0

        XmitFreq_3             ;
        XmitFreq_2             ;
        XmitFreq_1             ;
        XmitFreq_0             ;

        RcvFreq_3              ;
        RcvFreq_2              ;
        RcvFreq_1              ;
        RcvFreq_0              ;

        XMSiReg8                ; Required        
        XMSiReg9                ;  SiRegs 
        XMSiReg10               ;   for 
        XMSiReg11               ;    Transmit  (or MARK)
        XMSiReg12               ;     frequency

        RSSiReg8                ; Required        
        RSSiReg9                ;  SiRegs 
        RSSiReg10               ;   for 
        RSSiReg11               ;    Receive (shifted)  (or SPACE)
        RSSiReg12               ;     frequency

        Bank1Temp               ; Temporary storage in this bank

        ENDC                      ; End of Data Block

;       WATCH IT!  Max unique is 0xEF, Then 16 shared bytes 0xF0 through 0xFF (same as 0x70-0x7F)

; ========================================================================
    CBLOCK 0x110    ; Start Data Block For Bank 2       (96 bytes)

      ; Called FxFactor 
      ; Store (Fxtal/HSDIVN1) * 256 to retain extra significant HEX digits.

      HSN1FX000     ; B0   10 - 11 MHz  (114,285,000)/(46*11) * 256 = 037243AF
      HSN1FX001
      HSN1FX002
      HSN1FX003

      HSN1FX010     ; B1   11 - 12 MHz  (114,285,000)/(50*9)  * 256 = 03E00EAA
      HSN1FX011
      HSN1FX012
      HSN1FX013

      HSN1FX020     ; B2   12 - 13 MHz  (114,285,000)/(38*11) * 256 = 042C0117
      HSN1FX021
      HSN1FX022
      HSN1FX023

      HSN1FX030     ; B3   13 - 15 MHz  (114,285,000)/(34*11) * 256 = 04A9A6DD
      HSN1FX031                                       
      HSN1FX032
      HSN1FX033
      
      HSN1FX040     ; B4   15 - 17 MHz  (114,285,000)/(30*11) * 256 = 0548CE2E
      HSN1FX041
      HSN1FX042
      HSN1FX043

      HSN1FX050     ; B5   17 - 19 MHz  (114,285,000)/(26*11) * 256 = 0618EDE6
      HSN1FX051
      HSN1FX052
      HSN1FX053
      
      HSN1FX060     ; B6   19 - 21 MHz  (114,285,000)/(24*11) * 256 = 069B01BA
      HSN1FX061
      HSN1FX062
      HSN1FX063
      
      HSN1FX070     ; B7   21 - 23 MHz  (114,285,000)/(22*11) * 256 = 0734BC10
      HSN1FX071
      HSN1FX072
      HSN1FX073
      
      HSN1FX080     ; B8   23 - 15 MHz  (114,285,000)/(20*11) * 256 = 07ED3545
      HSN1FX081
      HSN1FX082
      HSN1FX083
      
      HSN1FX090     ; B9   25 - 28 MHz  (114,285,000)/(18*11) * 256 = 08CEACF8
      HSN1FX091
      HSN1FX092
      HSN1FX093
      
      HSN1FX100     ; B10  28 - 32 MHz  (114,285,000)/(16*11) * 256 = 09E88297
      HSN1FX101
      HSN1FX102
      HSN1FX103
      
      HSN1FX110     ; B11  32 - 36 MHz  (114,285,000)/(22*7) * 256 =  0B52DE63
      HSN1FX111
      HSN1FX112
      HSN1FX113
      
      HSN1FX120     ; B12  36 - 41 MHz  (114,285,000)/(34*4) * 256 =  0CD28AE1
      HSN1FX121
      HSN1FX122
      HSN1FX123
      
      HSN1FX130     ; B13  41 - 47 MHz  (114,285,000)/(20*6) * 256 =  0E883700
      HSN1FX131
      HSN1FX132
      HSN1FX133
      
      HSN1FX140     ; B14  47 - 54 MHz  (114,285,000)/(26*4) * 256 =  10C48E3B
      HSN1FX141
      HSN1FX142
      HSN1FX143
      
      HSN1FX150     ; B15  54 - 61 MHz  (114,285,000)/(10*9) * 256 =  13604955
      HSN1FX151
      HSN1FX152
      HSN1FX153
      
      HSN1FX160     ; B16  61 - 70 MHz  (114,285,000)/(16*5) * 256 =  15CC5280
      HSN1FX161
      HSN1FX162
      HSN1FX163
      
      HSN1FX170     ; B17  70 - 81 MHz  (114,285,000)/(10*7) * 256 =  18E982DB
      HSN1FX171
      HSN1FX172
      HSN1FX173
      
      HSN1FX180     ; B18  81 - 90 MHz  (114,285,000)/(10*6) * 256 =  1D106E00
      HSN1FX181
      HSN1FX182
      HSN1FX183
      
      HSN1FX190     ; B19  90 - 101 MHz (114,285,000)/(6*9) * 256 =   204B24E3
      HSN1FX191
      HSN1FX192
      HSN1FX193
      
      HSN1FX200     ; B20 101 - 111 MHz 114,285,000)/(8*6) * 256 =    24548980 
      HSN1FX201
      HSN1FX202
      HSN1FX203
      
      HSN1FX210     ; B21 111 - 128 MHz 114,285,000)/(4*11)* 256 =    27A20A5D
      HSN1FX211
      HSN1FX212
      HSN1FX213
      
      HSN1FX220     ; B22 128 - 135 MHz 114,285,000)/(8*5) * 256 =    2B98A500
      HSN1FX221                                        
      HSN1FX222
      HSN1FX223
            
      HSN1FX230     ; B23 135 - 157 MHz 114,285,000)/(4*9) * 256 =    3070B755
      HSN1FX231                             
      HSN1FX232
      HSN1FX233
       
    ENDC

;       WATCH IT!  Max unique is 0x16F, Then 16 shared bytes 0x170 through 0x17F (same as 0x70-0x7F)

; ========================================================================
    CBLOCK 0x190    ; Start Data Block For Bank 3      (96 bytes)

    ENDC

;       WATCH IT!  Max unique is 0x1EF, Then 16 shared bytes 0x1F0 through 0x1FF (same as 0x70-0x7F)

; ========================================================================

        errorlevel -302
        errorlevel -306
; 
; ****************************************************************************
; * The 16F88 resets to 0x00                                                 * 
; ****************************************************************************             
        ORG     0x0000                
reset_entry
        goto    start             ; Jump around the band table to main program
; ***************************************************************************
; * INTERRUPT VECTOR (at 0x04)                                              *
; ***************************************************************************
        ORG     0x0004 
interrupt_entry
        nop                       ; Fall through to interrupt-handler_shell
interrupt_handler_shell
;
; "BEWARE!!! You must avoid GOTO INTERRUPT at the interrupt vector as this 
; inherits the page bits from the page that's interrupted! Just put a NOP 
; at the interrupt vector and drop through to an interrupt routine in bank 0
; at address h'0005'"  (Per PICLIST, Roger Froud)
;
; Found to be very true in this project, since there is communications code 
; in PAGE-1. Previously, with a GOTO Interrupt_handler here, when the PAGE-1
; comm code got interrupted, the result was a hang since it tried to go to 
; the Interrupt_handler address in PAGE-1 while it's actually in PAGE-0.
;
        movwf   Save_W            ; Save the contents of W
        swapf   STATUS,w          ; Save STATUS contents in W
        clrf    STATUS            ; Change to bank 0, regardless of current bank
                                  ;   (Clears IRP, RP1, RP0)
        movwf   Save_S            ; Save status (in bank 0 status save location)
        movf    PCLATH,w          ; Move PCLATH into W
        movwf   Save_PCLATH       ;   and save it
        clrf    PCLATH            ; Change to page 0, regardless of current page
        movf    FSR,w             ; Move FSR into W
        movwf   Save_FSR          ;   and save it
;
        pagesel interrupt_handler_main ; Switch to Page 1
        call    interrupt_handler_main ; Go do the main interrupt processing
        pagesel main              ; Back to Page 0
;
; End  ISR Code. Now restore to saved state
        movf    Save_PCLATH,w     ; Restore saved PCLATH into W
        movwf   PCLATH            ;   and move W into PCLATH
        movf    Save_FSR,w        ; Restore saved FSR into W
        movwf   FSR               ;   and move W into FSR
        swapf   Save_S,w          ; Swap status save nibbles, and move into W
        movwf   STATUS            ; Move W into STATUS register
                                  ;   (Bank now in original state)
        swapf   Save_W,f          ; Swap nibbles within W_Save 
                                  ;  (for next swap instruction)
        swapf   Save_W,w          ; Swap nibbles of W_Save, and move into W
        retfie


; NOTE: KEEP TABLES AT THE START!
; addwf PCL,f only works if the target is in the first half of any 512 byte 
; address page.
; 
; ****************************************************************************  
;
HSDIVIndexToHSDIV    ; 4=0,5=1,6=2,7=3,9=5,11=7
        addwf   PCL,f             ;
        retlw   0x04              ; 000=4    
        retlw   0x05              ; 001=5    
        retlw   0x06              ; 010=6    
        retlw   0x07              ; 011=7    
        retlw   0x00              ; (unused)
        retlw   0x09              ; 101=9      
        retlw   0x00              ; (unused)
        retlw   0x0B              ; 111=11   

SiBandTable                       ;
        addwf   PCL,f             ; 
        retlw   0x00              ; B0 10 MHz (LSB)
        retlw   0x98              ;   
        retlw   0x96              ;   
        retlw   0x80              ;        (MSB)
        
        retlw   0x00              ; B1 11 MHz (LSB)
        retlw   0xA7              ;   
        retlw   0xD8              ;   
        retlw   0xC0              ;        (MSB)

        retlw   0x00              ; B2 12 MHz (LSB)
        retlw   0xB7              ;   
        retlw   0x1B              ;   
        retlw   0x00              ;        (MSB)

        retlw   0x00              ; B3 13 MHz (LSB)
        retlw   0xC6              ;   
        retlw   0x5D              ;   
        retlw   0x40              ;        (MSB)

        retlw   0x00              ; B4 15 MHz (LSB)
        retlw   0xE4              ;   
        retlw   0xE1              ;   
        retlw   0xC0              ;        (MSB)

        retlw   0x01              ; B5 17 MHz (LSB)
        retlw   0x03              ;   
        retlw   0x66              ;   
        retlw   0x40              ;        (MSB)

        retlw   0x01              ; B6 19 MHz (LSB)
        retlw   0x21              ;   
        retlw   0xEA              ;   
        retlw   0xC0              ;        (MSB)

        retlw   0x01              ; B7 21 MHz (LSB)
        retlw   0x40              ;   
        retlw   0x6F              ;   
        retlw   0x40              ;        (MSB)

        retlw   0x01              ; B7 23 MHz (LSB)
        retlw   0x5E              ;   
        retlw   0xF3              ;   
        retlw   0xC0              ;        (MSB)

        retlw   0x01              ; B8 25 MHz (LSB)
        retlw   0x7D              ;   
        retlw   0x78              ;   
        retlw   0x40              ;        (MSB)

        retlw   0x01              ; B10 28 MHz (LSB)
        retlw   0xAB              ;   
        retlw   0x3F              ;   
        retlw   0x00              ;        (MSB)

        retlw   0x01              ; B11 32 MHz (LSB)
        retlw   0xE8              ;   
        retlw   0x48              ;   
        retlw   0x00              ;        (MSB)

        retlw   0x02              ; B12 36 MHz (LSB)
        retlw   0x25              ;   
        retlw   0x51              ;   
        retlw   0x00              ;        (MSB)

        retlw   0x02              ; B13 41 MHz (LSB)
        retlw   0x71              ;   
        retlw   0x9C              ;   
        retlw   0x40              ;        (MSB)

        retlw   0x02              ; B14 47 MHz (LSB)
        retlw   0xCD              ;   
        retlw   0x29              ;   
        retlw   0xC0              ;        (MSB)

        retlw   0x03              ; B15 54 MHz (LSB)
        retlw   0x37              ;   
        retlw   0xF9              ;   
        retlw   0x80              ;        (MSB)

        retlw   0x03              ; B16 61 MHz (LSB)
        retlw   0xA2              ;   
        retlw   0xC9              ;   
        retlw   0x40              ;        (MSB)

        retlw   0x04              ; B17 70 MHz (LSB)
        retlw   0x2C              ;   
        retlw   0x1D              ;   
        retlw   0x80              ;        (MSB)

        retlw   0x04              ; B18 81 MHz (LSB)
        retlw   0xD3              ;   
        retlw   0xF6              ;   
        retlw   0x40              ;        (MSB)

        retlw   0x05              ; B19 90 MHz (LSB)
        retlw   0x5D              ;   
        retlw   0x4A              ;   
        retlw   0x80              ;        (MSB)

        retlw   0x06              ; B20 101 MHz (LSB)
        retlw   0x05              ;   
        retlw   0x23              ;   
        retlw   0x40              ;        (MSB)

        retlw   0x06              ; B21 111 MHz (LSB)
        retlw   0x9D              ;   
        retlw   0x89              ;   
        retlw   0xC0              ;        (MSB)

        retlw   0x07              ; B22 128 MHz (LSB)
        retlw   0xA1              ;   
        retlw   0x20              ;   
        retlw   0x00              ;        (MSB)

        retlw   0x08              ; B23 135 MHz (LSB)
        retlw   0x0B              ;   
        retlw   0xEF              ;   
        retlw   0xC0              ;        (MSB)
                                  ; MAX is about 157 MHz so set upper limit there
        retlw   0x09              ; B24  157,000,001 Hz (Limits prevent it from actually getting here.)
        retlw   0x5B              ;   
        retlw   0xA1              ;   
        retlw   0x41              ;        (MSB)

BandN1Minus1
        addwf   PCL, F  ; Return a selected item from table based on index
        retlw   0x2D    ; Band 0   46     Lowest Frequency
        retlw   0x31    ; Band 1   50
        retlw   0x25    ; Band 2   38
        retlw   0x21    ; Band 3   34
        retlw   0x1D    ; Band 4   30
        retlw   0x19    ; Band 5   26
        retlw   0x17    ; Band 6   24
        retlw   0x15    ; Band 7   22
        retlw   0x13    ; Band 8   20
        retlw   0x11    ; Band 9   18
        retlw   0x0F    ; Band 10  16
        retlw   0x15    ; Band 11  22
        retlw   0x21    ; Band 12  34
        retlw   0x13    ; Band 13  20
        retlw   0x19    ; Band 14  26
        retlw   0x09    ; Band 15  10
        retlw   0x0F    ; Band 16  16
        retlw   0x09    ; Band 17  10
        retlw   0x09    ; Band 18  10
        retlw   0x05    ; Band 19  6
        retlw   0x07    ; Band 20  8
        retlw   0x03    ; Band 21  4
        retlw   0x07    ; Band 22  8
        retlw   0x03    ; Band 23  4   


BandHSDIVIndex   ; Index positioned to upper 3 bits of byte. 
        addwf   PCL, F   ; Return a selected item from table based on index
        retlw   0xE0    ; Band 0   11    Lowest Frequency
        retlw   0xA0    ; Band 1   9
        retlw   0xE0    ; Band 2   11
        retlw   0xE0    ; Band 3   11
        retlw   0xE0    ; Band 4   11
        retlw   0xE0    ; Band 5   11
        retlw   0xE0    ; Band 6   11
        retlw   0xE0    ; Band 7   11
        retlw   0xE0    ; Band 8   11
        retlw   0xE0    ; Band 9   11
        retlw   0xE0    ; Band 10  11
        retlw   0x60    ; Band 11  7
        retlw   0x00    ; Band 12  4
        retlw   0x40    ; Band 13  6
        retlw   0x00    ; Band 14  4
        retlw   0xA0    ; Band 15  9
        retlw   0x20    ; Band 16  5
        retlw   0x60    ; Band 17  7
        retlw   0x40    ; Band 18  6
        retlw   0xA0    ; Band 19  9
        retlw   0x40    ; Band 20  6
        retlw   0xE0    ; Band 21  11
        retlw   0x20    ; Band 22  5
        retlw   0xA0    ; Band 23  9  Highest frequency   

BandHSN1D2   ; (HSDIV*N1/2)
        addwf   PCL, F  ; Return a selected item from table based on index
        retlw   0xFD    ; Band 0  (46*11)/2 = 253     Lowest Frequency 
        retlw   0xE1    ; Band 1  (50*9)/2 = 225
        retlw   0xD1    ; Band 2  (38*11)/2 = 209
        retlw   0xBB    ; Band 3  (34*11)/2 = 187
        retlw   0xA5    ; Band 4  (30*11)/2 = 165
        retlw   0x8F    ; Band 5  (26*11)/2 = 143
        retlw   0x84    ; Band 6  (24*11)/2 = 132
        retlw   0x79    ; Band 7  (22*11)/2 = 121
        retlw   0x6E    ; Band 8  (20*11)/2 = 110
        retlw   0x63    ; Band 9  (18*11)/2 = 99
        retlw   0x58    ; Band 10 (16*11)/2 = 88
        retlw   0x4D    ; Band 11 (22*7)/2 = 77
        retlw   0x44    ; Band 12 (34*4)/2 = 68
        retlw   0x3C    ; Band 13 (20*6)/2 = 60
        retlw   0x34    ; Band 14 (26*4)/2 = 52
        retlw   0x2D    ; Band 15 (10*9)/2 = 45
        retlw   0x28    ; Band 16 (16*5)/2 = 40
        retlw   0x23    ; Band 17 (10*7)/2 = 35
        retlw   0x1E    ; Band 18 (10*6)/2 = 30
        retlw   0x1B    ; Band 19 (6*9)/2 = 27
        retlw   0x18    ; Band 20 (8*6)/2 = 24
        retlw   0x16    ; Band 21 (4*11)/2 = 22
        retlw   0x14    ; Band 22 (8*5)/2 = 20
        retlw   0x12    ; Band 23 (4*9)/2 = 18        Highest frequency

TuningStepSizeLS     ; Least Significant Byte
        addwf   PCL, F   ; Amount to increment/decrement per encoder tick
        retlw   1        ; 1
        retlw   0x0A     ; 10
        retlw   0x64     ; 100
        retlw   0xE8     ; 1k
        retlw   0x10     ; 10k
        retlw   0xA0     ; 100k
        retlw   0x40     ; 1M

TuningStepSizeMid    ; Mid digit
        addwf   PCL, F   ;  
        retlw   0        ; 1
        retlw   0        ; 10
        retlw   0        ; 100
        retlw   0x03     ; 1k
        retlw   0x27     ; 10k
        retlw   0x86     ; 100k
        retlw   0x42     ; 1M

TuningStepSizeMS     ; Most Significant Byte
        addwf   PCL, F   ; 
        retlw   0        ; 1
        retlw   0        ; 10
        retlw   0        ; 100
        retlw   0        ; 1k
        retlw   0        ; 10k
        retlw   1        ; 100k
        retlw   0x0f     ; 1M

CursorUnderlineDigit  ; LCD character position - First Line
        addwf   PCL, F    ; 
        retlw   0x8B      ; 1 Hz position
        retlw   0x8A      ;
        retlw   0x89      ;
        retlw   0x87      ; 1k (skip comma)
        retlw   0x86      ;
        retlw   0x85      ;
        retlw   0x83      ; 1M (skip comma)

; *****************************************************************************
; *    Name:  start                                                           *
; *                                                                           *
; * Purpose:  This is the start of the program.  It initializes the LCD and   *
; *           detects whether to enter calibrate mode.  If so, it calls the   *
; *           Calibrate routine.  Otherwise, it sets the power-on frequency   *
; *           and enters the loop to poll the encoder.                        *
; *                                                                           *
; *   Input:  The start up frequency is defined in the default_3...default_0  *
; *           definitions above.  It uses the Fxtal/HSDIVN1 table which is    *
; *           loaded from EEPROM upon start-up.  Calibration will determine   *
; *           a more accurate value for Fxtal and recalculate the table in    *
; *           EEPROM so the resulting frequencies will be more accurate on    *
; *           subsequent power-ups.                                           *
; *                                                                           *
; *  Output:  Normal VFO operation.                                           *
; *                                                                           *
; *****************************************************************************
;
start
        clrf    INTCON            ; No interrupts for now           Any Bank

        banksel OSCCON            ; Switch to bank 1
        movlw   0x70              ; Set IRCF<2:0> = 7 (for 8 MHz Internal Osc)
        movwf   OSCCON            ;                                   Bank 1
        movlw   0x07              ; Code to turn off the analog comparitors
        movwf   CMCON             ; Turn off comparators  (CM2,CM1,CM0 = 1) 
                                  ;                          (16F88-> Bank 1)
        clrf    ANSEL             ; Turn off Analog ('88 ONLY)         Bank 1
        bcf     OPTION_REG,NOT_RBPU ; Enable weak pullups  (!RBPU)     Bank 1                 
        movlw   b'00100111'       ; RA7,6,4,3 are outputs, all others inputs
        movwf   TRISA             ;                                    Bank 1
        movlw   b'10000000'       ; Port B is
        movwf   TRISB             ;  all outputs except RB7 is input   Bank 1
        banksel PORTA             ; Switch back to bank 0
;
; New code for timer 1 and interrupt.
;
        movlw   0xC0              ; Get GIE and PEIE bits
        movwf   INTCON            ; Enable general interrupts
        banksel PIE1              ; Switch to bank 1 to enable timer 1
        movlw   0x01              ; Get bit to enable timer 1 interrupt
        movwf   PIE1              ; Set TMR1IE
        banksel PORTA             ; Switch to bank 0

        clrf    VFOcontrol        ; Initialize    (Clear CWRegsOK)
        clrf    FSKcontrol        ; Initialize   

        call    init_LCD          ; Initialize the LCD
        call    display_version   ; Display title and version                  
        clrf    tuning_digit      ; Initialize

        call    Load_HSN1FX_Table ; Load RAM table from EEPROM
  
        pagesel ReadStartupConfig ; Go to Page 1
        call    ReadStartupConfig ; Get reference SiRegs   PAGE 1
        pagesel main              ; Back to Page 0
  
;       Get ModeSelect byte from EEPROM                                           ; v7.6
      
        movlw   EEModeSelectAdr   ; Location of ModeSelect byte in EEPROM
        banksel EEADR             ; Get to proper bank
        movwf   EEADR             ; Point to correct EEPROM address for frequency
        call    read_EEPROM       ; Read EEPROM (switch to correct banks inside)
        movf    EEDATA,w          ; Get the next freq byte                     
        banksel PORTA             ; Back to bank 0 to store
        andlw   0xFD              ; Mask off DEBUG mode (never recover it)        ; v7.7
        movwf   ModeSelect        ; Save it

        pagesel  DisplaySiRegs    ; Go to Page 1
        call    DisplaySiRegs     ; Display Si Regs (always) DEBUG               PAGE 1        
        pagesel main              ; Back to Page 0

        btfsc   PORTA,PB_CAL     ; PB_3 / Calibrate pushbutton being held at startup?
        goto    MainNoCal        ; No, jump around calibrate
        
        btfsc   PORTA,PB_4       ; Is pushbutton 4 ALSO being held at startup?
        goto    StartCalibrate   ; No, just PB_3 so start calibrate     
        bsf     ModeSelect,MODEDEBUG ; Toggle debug mode on for this session
        ; call    update_ModeSelect_EEPROM ; Update in EEPROM                      ; v7.6, v7.7
        goto    MainNoCal        ;    and jump around calibrate 
        
StartCalibrate
        call    calibrate        ; Yes, Retrieve Fxtal and set up HSN1FX table in EEPROM
WaitForCalPBRelease
        btfss   PORTA,PB_CAL     ; PB_3 / Calibrate still held down?
        goto    WaitForCalPBRelease ; Yes, loop until release
        goto    start            ; No, ready to start over and use the calibrated values this time

MainNoCal
;       Get power on start-up frequency from EEPROM

        movlw   EEStartupFreqAdr     ; Start location for frequency in EEPROM
        banksel EEADR             ; Get to proper bank
        movwf   EEADR             ; Point to correct EEPROM address for frequency

        call    read_EEPROM       ; Read EEPROM (switch to correct banks inside)
        movf    EEDATA,w          ; Get the next freq byte                     
        banksel PORTA             ; Back to bank 0 to store
        movwf   freq_3            ; Save it
        call    read_EEPROM       ; Read EEPROM (switch to correct banks inside)                               
        movf    EEDATA,w          ; Get the next freq byte                     
        banksel PORTA             ; Back to bank 0 to store
        movwf   freq_2            ; Save it                                    
        call    read_EEPROM       ; Read EEPROM (switch to correct banks inside)                                
        movf    EEDATA,w          ; Get the last freq byte                     
        banksel PORTA             ; Back to bank 0 to store
        movwf   freq_1            ; Save it
        call    read_EEPROM       ; Read EEPROM (switch to correct banks inside)                                
        movf    EEDATA,w          ; Get the last freq byte                     
        banksel PORTA             ; Back to bank 0 to store
        movwf   freq_0            ; Save it

        btfsc   ModeSelect,MODELSB ; Is mode LSB?                             
        goto    ModeSelectMinus    ; Yes, go set relays to MINUS
        btfsc   ModeSelect,MODECWMINUS ; No, is mode CW-?
        goto    ModeSelectMinus    ; Yes, go set relays to MINUS
                                   ; No, fall through to Plus (+ or USB)
ModeSelectPlus        
        ; Set IQRelay
        bsf     PORTA,LOWSELECTPLUS  ; Set pin high to drive latching relay.
        nop                       ;    Let it settle (Read-modify-write problem),
        bcf     PORTA,LOWSELECTMINUS ; Set other side low 
        call    wait_16ms          ; Wait for 16mS for relay to set 
                                  ; (TQ2-L-5V needs 14 mA at 5v for 3 mS plus 
                                  ;    contact bounce time)
        bcf     PORTA,LOWSELECTPLUS ; Release it  (Now both sides low)
        goto    ModeSelectRelaysDone

ModeSelectMinus
        ; Set IQRelay
        bsf     PORTA,LOWSELECTMINUS ; Set pin high to drive latching relay.
        nop                       ;    Let it settle (Read-modify-write problem),
        bcf     PORTA,LOWSELECTPLUS ; Set other side low 
        call    wait_16ms          ; Wait for 16mS for relay to set 
                                  ; (TQ2-L-5V needs 14 mA at 5v for 3 mS plus 
                                  ;    contact bounce time)
        bcf     PORTA,LOWSELECTMINUS ; Release it  (Now both sides low)

ModeSelectRelaysDone

        clrf    fstep_3
        movlw   0x7F              ;
        movwf   fstep_2           ; Set large change so freeze will be done first time
        clrf    fstep_1
        clrf    fstep_0

        clrf    band               ; Initialize
        call    detect_band_change ; Update band number to match current frequency
        bsf     VFOcontrol,BANDCHANGED ; Force Band Changed activity

;       Display the power-on frequency 
;
        movf    freq_3,w          ; Set up display frequency
        movwf   Dfreq_3
        movf    freq_2,w
        movwf   Dfreq_2
        movf    freq_1,w
        movwf   Dfreq_1
        movf    freq_0,w
        movwf   Dfreq_0
        btfss   ModeSelect,MODEDIVBY2
        goto    NotDivBy2
        ; divide display frequency by 2 with one right shift 
        bcf     STATUS,C           ; Clear carry
        rrf     Dfreq_3,f
        rrf     Dfreq_2,f
        rrf     Dfreq_1,f
        rrf     Dfreq_0,f
NotDivBy2
        btfss   ModeSelect,MODEDIVBY4
        goto    NotDivBy4
        ; divide display frequency by 4 with two right shifts
        bcf     STATUS,C           ; Clear carry
        rrf     Dfreq_3,f
        rrf     Dfreq_2,f
        rrf     Dfreq_1,f
        rrf     Dfreq_0,f
        ; shift again
        rrf     Dfreq_3,f
        rrf     Dfreq_2,f
        rrf     Dfreq_1,f
        rrf     Dfreq_0,f
NotDivBy4
        call    bin2BCD           ; Convert it to BCD 
        call    show_freq         ; Display it
        
        bsf     VFOcontrol,FORCESEND ; Need to send SI bytes for sure
        bcf     VFOcontrol,CWREGSOK ; Say regs need to be set up for this frequency

        call    CWShiftCheck      ; Start at init frequency with CW frequency shift if needed

        bcf     VFOcontrol,FORCESEND ; Done with forced update
;
; Initialize the TIMER1 timer to expire after 25ms and generate an interrupt. 
; 1)Set TMR1IE bit so an interrupt is generated when the TIMER1 rolls over.
; 2)Set a value into <TMR1H:TMR1L>.  Note that it must be less than FFFF.  If more than FFFF, need Prescalar
;   Note: Will also set up this starting value when after an interrupt occurs.
;   Calculate a value for <TMR1H:TMR1L>:
;     With no prescaler, TIMER1 gets incremented on every instruction cycle which is (clock frequency / 4).
;     The Internal oscillator frequency is 8 MHz, so instruction cycle is 2.0 MHz.= .5 uS per instruction   
;     With no Prescaler we would have 5.0 x 10^-7 seconds per Timer tick.
;     This means that 25 ms we would have: .025 sec / (5.0 x10^-7 seconds per TIMER1 tick) = 50,000 TIMER1 ticks
;     This means we want TIMER1 to "roll over" after 50,000 TIMER1 Ticks.
;     50,000 is 0xC350.  Since this is smaller than FFFF, we could use it directly but use prescalar in case speed is changed later.
;     Try prescaler of 2. Now Timer gets updated on every TWO instructions.
;     Thus, we only need to need 25,500 TIMER1 ticks.   
;     25,000 is 0x61A8 
;     To get value for TIMER1 such that it will "roll-over" after 0x61A8 TIMER1 ticks, 
;        Subtract 0xFFFF - 0x61A8 = 0x9E57   
;     Thus we want to put 0x9E57 into TIMER1 now and again after every roll-over interrupt.

; 3)Set Prescaler value of 1:2, (set <T1CKPS1:T1CKPS0> = 01) for reason explained above.
;
        clrf    T1CON             ; Turn TIMER1 off
        movlw   TIM25MSLOW        ; Get low byte for 25 ms
        movwf   TMR1L             ; Set low timer byte
        movlw   TIM25MSHIGH       ; Get high byte for 25 ms
        movwf   TMR1H             ; Set high timer byte
        movlw   TIM25MSPRESCALE   ; Turn on TIMER1 with 1:2 Prescale
        movwf   T1CON             ; Turn the timer on
        banksel PORTA             ; Switch back to bank 0

;       Get the power on encoder value.
;
        movf    PORTA,w           ; Read port A
        movwf   ren_read          ; Save it in ren_read
        movlw   0x03              ; Get encoder mask (RA0 and RA1)  
        andwf   ren_read,w        ; Get encoder bits
        movwf   ren_old           ; Save in ren_old
        movwf   ren_new           ; Also save in ren_new
;
;       Initialize variables.
;
        clrf    last_dir          ; Clear the knob direction indicator
        clrf    tuning_digit      ; Initially 1-Hz digit is being changed by encoder
        clrf    tick_count        ; Initialize encoder tick count
        bcf     VFOcontrol,INTOCCURRED ; Initialize flag
        bcf     VFOcontrol,NEEDUPDATE ; Don't need update until frequency changes
; 
; Fall into the Main Program Loop
;
; *****************************************************************************
; *    Name:  main                                                            *
; *                                                                           *
; * Purpose:  This is the Main Program Loop. The program's main loop          *
; *           calls poll_encoder, which continuously polls the encoder.       *
; *           When the encoder has turned, the direction is determined and    *
; *           stored in last_dir.  On each encoder pulse a counter is         *
; *           incremented. Then, in the interrupt handler, which executes     *
; *           every 25 ms, the count is examined and, if non-zero, the count  *
; *           is transferred another variable and the encoder count is        *
; *           cleared.  The interrupt handler then sets a flag to indicate a  *
; *           frequency update is needed.  The ReadEncoderFor25mS routine     *
; *           detects this need-update flag and exits back to the main        *
; *           routine for frequency update processing.                        *
; *                                                                           *
; *           The updated frequency value is used to construct Si570          *
; *           parameter words in routine BuildSiWords and they are sent to    *
; *           the Si570 by the subroutine SendSiWords.                        * 
; *                                                                           *
; *   Input:  None.                                                           *
; *                                                                           *
; *  Output:  None.                                                           *
; *                                                                           *
; *****************************************************************************
;
main
;
        call    ReadEncoderFor25mS ; Check for knob movement until 25ms interrupt
                                  ; (Shift frequency if necessary.)
        bcf     VFOcontrol,INTOCCURRED ; Clear flag
        btfsc   VFOcontrol,NEEDUPDATE ; Need Update?
        call    UpdateFrequency   ; Yes, Update to new frequency and clear flag
        call    PB_look           ; Look for PB presses and take action if found
        goto    main              ; No, back to top
;
; *****************************************************************************
; *    Name:  ReadEncoderFor25mS                                              *
; *                                                                           *
; * Purpose:  This routine reads the encoder bits until a 25ms interrupt      *
; *           occurs. If the encoder changed it determines the direction.     *
; *           If FSK modulation pin or T/R pin changes during the 25ms        *
; *           interval, handle it immediately.  Change frequency as needed.   *
; *                                                                           *
; *   Input:  Knob input read from port A                                     *
; *           ren_old -> the last encoder bits read                           *
; *           last_dir -> the last direction moved                            *
; *                                                                           *
; *  Output:  ren_timer -> an indication the speed of the knob.               *
; *           ren_new -> the current encoder bits                             *
; *           last_dir -> the last direction (0 = down, 2 = up)               *
; *                                                                           *
; *****************************************************************************
;
ReadEncoderFor25mS
        btfsc   VFOcontrol,INTOCCURRED ; Did interrupt occur?
        return                    ; Yes, return

        call    CWShiftCheck      ; See if CW Shift needed
        
        movf    PORTA,w           ; Get the current encoder value
        movwf   ren_read          ; Save it
        movlw   0x03              ; Get encoder mask (to isolate RA0 and RA1)  
        andwf   ren_read,w        ; Isolated encoder bits into W               
        movwf   ren_new           ; Save new value
        xorwf   ren_old,w         ; Has it changed?
        btfsc   STATUS,Z          ; Zero flag clear if it changed
        goto    ReadEncoderFor25mS ; No change, look again 
;                                 ; 
; It changed. 

; Now determine which direction the encoder turned.                
;============================================================================= 
;   Encoder bits are on RA0 and RA1 - the two low order bits of ren_new          
;   A and B are "gray code" - 90 degrees out of phase (quadrature)             
;         ___     ___                                                          
;        |   |   |   |                                                         
; A  ____|   |___|   |___                                                      
;           ___     ___                                                        
;          |   |   |   |                                                       
; B     ___|   |___|   |___                                                    
;       ^ ^ ^ ^ ^ ^ ^ ^                                                        
;       a b c d a b c d                                                        
;                                                                              
;              A B                                                             
; At point a:  0 0                                                             
; At point b:  1 0                                                             
; At point c:  1 1                                                             
; At point d:  0 1                                                             
;                                                                              
; Going UP, the sequence is a,b,c,d,a,b,c,d, etc. so the sequence is:          
;     00, 10, 11, 01, 00, 10, 11, 01, etc.                                     
;                                                                              
; Going DOWN, the sequence is d,c,b,a,d,c,b,a, etc. so the sequence is:        
;     01, 11, 10, 00, 01, 11, 10, 00, etc.                                     
;                                                                              
; To determine if the sequence is UP or DOWN:                                  
;   1) Take the "Right-Bit" of any pair.                                       
;   2) XOR it with the "Left-Bit" of the next pair in the sequence.            
;   3) If the result is 1 it is UP                                             
;      If the result is 0 it is DOWN                                           
;                                                                              
; The direction flag is 0 (DOWN) or 2 (UP) because of bit positioning          
;============================================================================= 
        bcf     STATUS,C          ; Clear the carry bit to prepare for rotate  
        rlf     ren_old,f         ; Rotate old bits left to align "Right-Bit"  
        movf    ren_new,w         ; Set up new bits in W                       
        xorwf   ren_old,f         ; XOR old (left shifted) with new bits       
        movf    ren_old,w         ; Put XOR results into W also                
        andlw   0x02              ; Mask to look at only "Left-Bit" of pair    
        movwf   next_dir          ; Save result (in W) as direction (bit=UP)   
        xorwf   last_dir,w        ; See if direction is same as before         
;
;       Prevent encoder slip from giving a false change in direction.
;
        btfsc   STATUS,Z          ; Zero flag set? (i.e, is direction same?)   
        goto    pe_continue       ; Yes, same direction so no slip; keep going 
        movf    next_dir,w        ; No Zero-flag, so direction changed         
        movwf   last_dir          ; Update the direction indicator             
        movf    ren_new,w         ; Save the current encoder bits (now in W)   
        movwf   ren_old           ;   for next time                            
        goto    ReadEncoderFor25mS ; Try again
pe_continue
        clrf    last_dir          ; Clear last_dir (default is DN)             
        btfss   ren_old,1         ; Are we going UP?                           
        goto    exit3             ; No, exit3                                  
up2 
        movlw   0x02              ; Get UP value                               
        movwf   last_dir          ;   and set in last_dir                      
exit3 
        incf    tick_count,f      ; Increment counter
        movf    ren_new,w         ; Get the current encoder bits
        movwf   ren_old           ; Save them in ren_old for the next time
        goto    ReadEncoderFor25mS ; Loop until interrupt handler sets flag to exit
;
; **************************************************************************** 
; *    Name:  PB_look                                                        *                
; *                                                                          *
; * Purpose:  This routine is entered to see if pushbutton PB_3 or           *
; *           PB_4 have been pressed.                                        *  
; *           Note that the "RESET" pushbutton is been reconfigured to allow *
; *           it to be used as a normal pushbutton instead of resetting      *
; *           the microprocessor.                                            *
; *                                                                          * 
; *           There is another usage for PB_3 also. During operation, if     * 
; *           PB_3 is held for more than 2 seconds, the current frequency    *
; *           is saved in EEPROM and used on subsequent start-ups as the     *
; *           start-up frequency.                                            * 
; *                                                                          * 
; *   Input:  Pushbuttons 3 and 4                                            *  
; *           tuning_digit                                                   * 
; *                                                                          * 
; *   Output: Updated tuning_digit (0 = 1Hz digit up to 6 = 1MHz digit)      *   
; **************************************************************************** 
;                                                                              
PB_look ;                                                                      
        btfss   PORTA,PB_3        ; Is PB_3 / PB_INC / PB_SAVE being pressed?                     
        goto    PB_yes3           ; Yes,                                

        btfss   PORTA,PB_4        ; Is PB_4 / PB_DEC being pressed?                     
        goto    PB_yes4           ; Yes,                              

        goto    PB_exit           ; No, exit

PB_yes3
        ; Wait for PB_3 to be released                                         
        ; (If wait is longer than 2 seconds, call update_StartFreq_EEPROM and exit)      
        movlw   0x10              ; Set up                                     
        movwf   temp1             ;   loop count                               
PB3_release_wait ;                                                           
        call    wait_64ms         ; Wait a bit (debounce)
        btfsc   PORTA,PB_3        ; Is PB_3/ PB_INC / PB_SAVE still behing held?                 
        goto    PB3_released      ; No, it's released, so move to higher digit              
        decfsz  temp1,f           ; Have we waited long enough?                
        goto    PB3_release_wait  ; No, continue waiting                      
                                  ; Button was held longer than 2 seconds, so  
        call    update_StartFreq_EEPROM ; Update EEPROM with current frequency       
        ; Wait for release
PB3_release_wait2 ;                                                           
        call    wait_64ms         ; Wait a bit (debounce)
        btfss   PORTA,PB_3        ; Is PB_3 / PB_INC / PB_SAVE still behing held?                 
        goto    PB3_release_wait2 ; Yes, wait until released (forever)                  
        goto    PB_exit_pb        ;   and exit without changing tuning digit
;                                                                              
PB3_released ;                                                               
        incf    tuning_digit,f    ; Increment tuning digit
        movlw   0x07              ; One greater than max
        subwf   tuning_digit,w    ; 
        btfsc   STATUS,Z          ; Have we now reached 7?
        decf    tuning_digit,f    ; Yes, bring it back to 6 (1M digit is max) 
        call    show_freq         ; Display frequency with new underline position
        goto    PB_exit_pb        ; Exit. 
;
PB_yes4 ;                                                                        
        ; Wait for PB_4 (old RESET pushbutton) to be released                                         
        ; (wait forever)      
PB4_release_wait ;                                                           
        call    wait_64ms         ; Wait a bit  (debounce)
        btfsc   PORTA,PB_4        ; Is PB_4 still behing held?                 
        goto    PB4_released      ; No, it's released, so move to lower digit              
        ; See if PB3 is also set (after PB4)
        call    wait_64ms         ; Wait a bit  (debounce)
        btfss   PORTA,PB_3        ; Is PB_3 ALSO being held after PB4 was pressed?                 
        bsf     ModeSelect,RUNMENU ; Yes, set flag to run menu when released                                     
        goto    PB4_release_wait  ; Look again  (expect PB_3 to be released first)
PB4_released ;                                                               
        btfss   ModeSelect,RUNMENU ; Is flag set to run the menu?
        goto    PB4_tuning_digit  ; No, continue to change tuning digit instead
        pagesel RunMenu           ; Switch to Page 1
        call    RunMenu           ; Go and run menu, solicit responses, set flags <<PAGE 1
        pagesel main              ; Switch back to Page 0
        clrf    encoder_data      ; No encoder updates were done - just menu operations v7.6
        call    UpdateFrequency   ; Update in case Div factor was changed in menu
        goto    PB_Menu_exit           ; Done 
PB4_tuning_digit        
        movf    tuning_digit,w
        btfsc   STATUS,Z          ; Are we already at zero?
        goto    PB_exit_pb        ; Yes, don't decrement any more
        decf    tuning_digit,f    ; No, decrement tuning digit
PB_Menu_exit
        call    show_freq         ; Display frequency with new underline position
        goto    PB_exit_pb        ;   and exit

PB_exit_pb ;                                                                      
        ; Ignore any possible encoder changes during the PB press
        bcf     VFOcontrol,INTOCCURRED ; Clear flag
        bcf     VFOcontrol,NEEDUPDATE ; Clear flag

PB_exit
        return  ;                                                              
;                                                                              
; *****************************************************************************
; *    Name:  UpdateFrequency                                                 *
; *                                                                           *
; * Purpose: This routine uses the 25 ms encoder count and direction          *
; *          determined by the interrupt handler and saved by ReadEncoder.    *
; *          It applies the update (size and direction) to the selected       *
; *          tuning digit and updates the Si570 frequency accordingly.        *
; *                                                                           *
; * Input:  fstep set up with tick counter                                    *
; *         last_dir (1 = UP)                                                 *
; *         tuning_digit (0 = 1Hz up to 6 = 1MHz)                             *
; *                                                                           *
; * Output: Si570 frequency is updated                                        *
; *                                                                           *
; *****************************************************************************

UpdateFrequency

;       Determine mult factor
;
        clrf    fstep_3           ; Guess that we want 1 Hz steps by 
        clrf    fstep_2           ;   setting fstep to one.
        clrf    fstep_1           ; 
        movf    encoder_data,w    ; Retrieve Tick count as set up by int handler

        btfss   ModeSelect,MODEDIVBY2
        goto    UpdateNotDivBy2
        ; shift left by 1
        bcf     STATUS,C          ; Clear carry
        rlf     encoder_data,w    ; 
UpdateNotDivBy2
        btfss   ModeSelect,MODEDIVBY4
        goto    UpdateNotDivBy4
        ; shift left by 2
        bcf     STATUS,C          ; Clear carry
        rlf     encoder_data,f    ; 
        rlf     encoder_data,w    ; 
UpdateNotDivBy4
        clrf    encoder_data      ; Clear for next time                   v7.6
        movwf   RegB0

        ; Multiply by increment size

        movf    tuning_digit,w    ; Get step size
        call    TuningStepSizeLS  ; Get Least Significant Digit of step size
        movwf   RegA0
        movf    tuning_digit,w    ; Get step size
        call    TuningStepSizeMid ; Get Mid Digit of step size
        movwf   RegA1
        movf    tuning_digit,w    ; Get step size
        call    TuningStepSizeMS  ; Get Most Significant Digit of step size
        movwf   RegA2
        clrf    RegA3
        clrf    RegA4

        call    Mult32x8       ; Multiply RegA3:RegA0 by RegB0 => Result in RegC4:RegC0

        clrf    fstep_3        ; Never need more than 3 bytes for updates           v7.6
        movf    RegC2,w
        movwf   fstep_2
        movf    RegC1,w
        movwf   fstep_1
        movf    RegC0,w
        movwf   fstep_0

;       Based on the knob direction, either add or subtract the increment, 
;       then update the LCD and DDS.
; 
        ; Use last_dir as set up  by poll
        
        btfsc   last_dir,1        ; Is the knob going up?   
        goto    up                ; Yes, then add the increment
down
        call    sub_step          ; Subtract fstep from freq
        call    check_sub         ; Make sure we did not go below the minimum
        goto    update            ; Update LCD and DDS                         
up
        call    add_step          ; Add fstep to freq
        call    check_add         ; Make sure we did not exceed the maximum
update 
        call    detect_band_change ; Update band if it changed

        movf    freq_3,w          ; Set up display frequency
        movwf   Dfreq_3
        movf    freq_2,w
        movwf   Dfreq_2
        movf    freq_1,w
        movwf   Dfreq_1
        movf    freq_0,w
        movwf   Dfreq_0
        btfss   ModeSelect,MODEDIVBY2
        goto    Update2NotDivBy2
        ; divide display frequency by 2 with one right shift 
        bcf     STATUS,C           ; Clear carry
        rrf     Dfreq_3,f
        rrf     Dfreq_2,f
        rrf     Dfreq_1,f
        rrf     Dfreq_0,f
Update2NotDivBy2
        btfss   ModeSelect,MODEDIVBY4
        goto    Update2NotDivBy4
        ; divide display frequency by 4 with two right shifts
        bcf     STATUS,C           ; Clear carry
        rrf     Dfreq_3,f
        rrf     Dfreq_2,f
        rrf     Dfreq_1,f
        rrf     Dfreq_0,f
        ; shift again
        bcf     STATUS,C           ; Clear carry            v7.6
        rrf     Dfreq_3,f
        rrf     Dfreq_2,f
        rrf     Dfreq_1,f
        rrf     Dfreq_0,f
Update2NotDivBy4                                                                                                      
        call    bin2BCD           ; Convert the frequency in <Dfreq_3...Dfreq_0> to BCD
        call    show_freq         ; Display the frequency in <BCD_4...BCD_0> on the LCD

        ; If CW, Build and send in ShiftCheck instead.  Don't want to send twice on RCV  ; v7.7 
        btfsc   ModeSelect,MODECWPLUS ; Is it CW+?                                       ; v7.7
        goto    UpdateCW           ; Yes, see if we need CW shift                        ; v7.7
        btfsc   ModeSelect,MODECWMINUS ; Or is it CW-?                                   ; v7.7
        goto    UpdateCW           ; Yes, see if we need CW shift                        ; v7.7
                                                                                         ; v7.7
        ; Not CW so always update for USB or LSB                                         ; v7.7
        call    BuildSiWords      ;                                                      ; v7.7
        pagesel SendSiWords       ; Switch to Page 1                                     ; v7.7
        call    SendSiWords       ; (In Page 1)                                          ; v7.7
        pagesel main              ; Back to Page 0                                       ; v7.7
        goto    UpdateDone                                                               ; v7.7

UpdateCW                                                                                 ; v7.7
        bsf     VFOcontrol,FORCESEND ; Need to send SI bytes for sure
        bcf     VFOcontrol,CWREGSOK ; Say regs need to be set up for this new frequency

        call    CWShiftCheck      ; See if CW Shift needed

UpdateDone                                                                               ; v7.7
        bcf     VFOcontrol,NEEDUPDATE ; Clear flag
        bcf     VFOcontrol,FORCESEND  ; Clear flag

        return
;

; *****************************************************************************
; *    Name:  add_step                                                        *
; *                                                                           *
; * Purpose:  This routine adds the 32 bit value of fstep to the 32 bit       *
; *           value in freq.  When incrementing, the fstep value is a         *
; *           positive integer.  When decrementing, fstep is the complement   *
; *           of the value being subtracted.                                  *
; *                                                                           *
; *   Input:  The 32 bit values in fstep and freq                             *
; *                                                                           *
; *  Output:  The sum of fstep and freq is stored in freq.  When incrementing *
; *           this value may exceed the maximum.  When decrementing, it may   *
; *           go negative.                                                    *
; *                                                                           *
; *****************************************************************************
;
add_step
        movf    fstep_0,w         ; Get low byte of the increment
        addwf   freq_0,f          ; Add it to the low byte of freq
        btfss   STATUS,C          ; Any carry?
        goto    add1              ; No, add next byte
        incfsz  freq_1,f          ; Ripple carry up to the next byte
        goto    add1              ; No new carry, add next byte
        incfsz  freq_2,f          ; Ripple carry up to the next byte
        goto    add1              ; No new carry, add next byte
        incf    freq_3,f          ; Ripple carry up to the highest byte
add1
        movf    fstep_1,w         ; Get the next increment byte
        addwf   freq_1,f          ; Add it to the next higher byte
        btfss   STATUS,C          ; Any carry?
        goto    add2              ; No, add next byte
        incfsz  freq_2,f          ; Ripple carry up to the next byte
        goto    add2              ; No new carry, add next byte
        incf    freq_3,f          ; Ripple carry up to the highest byte
add2
        movf    fstep_2,w         ; Get the next to most significant increment
        addwf   freq_2,f          ; Add it to the freq byte
        btfss   STATUS,C          ; Any carry?
        goto    add3              ; No, add last byte
        incf    freq_3,f          ; Ripple carry up to the highest byte
add3
        movf    fstep_3,w         ; Get the most significant increment byte
        addwf   freq_3,f          ; Add it to the most significant freq
        return                    ; Return to the caller
;
; *****************************************************************************
; *    Name:  check_add                                                       *
; *                                                                           *
; * Purpose:  Check if freq exceeds the upper limit.                          *
; *                                                                           *
; *   Input:  The 32 bit values in freq                                       *
; *                                                                           *
; *  Output:  If freq is below the limit, it is unchanged.  Otherwise, it is  *
; *           set to equal the upper limit.                                   *
; *                                                                           *
; *****************************************************************************
;
check_add
;
;       Check the most significant byte.
;
        movlw   0xFF-ULIMIT_3     ; Get (FF - upper limit of high byte)
        addwf   freq_3,w          ; Add it to the current high byte
        btfsc   STATUS,C          ; Was high byte too large?
        goto    add_set_max       ; Yes, apply limit
        movlw   ULIMIT_3          ; Get high limit value
        subwf   freq_3,w          ; Subtract the limit value
        btfss   STATUS,C          ; Are we at the limit for the byte?
        goto    add_exit1         ; No, below.  Checks are done.
;
;       Check the second most significant byte.
;
        movlw   0xFF-ULIMIT_2     ; Get (FF - upper limit of next byte)
        addwf   freq_2,w          ; Add it to the current byte
        btfsc   STATUS,C          ; Is the current value too high?
        goto    add_set_max       ; Yes, apply the limit
        movlw   ULIMIT_2          ; Second limit byte
        subwf   freq_2,w          ; Subtract limit value
        btfss   STATUS,C          ; Are we at the limit for the byte?
        goto    add_exit1             ; No, below.  Checks are done.
;
;       Check the third most significant byte.
; 
        movlw   0xFF-ULIMIT_1     ; Get (FF - upper limit of next byte)
        addwf   freq_1,w          ; Add it to the current byte
        btfsc   STATUS,C          ; Is the current value too high?
        goto    add_set_max       ; Yes, apply the limit
        movlw   ULIMIT_1          ; Third limit byte
        subwf   freq_1,w          ; Subtract limit value
        btfss   STATUS,C          ; Are we at the limit for the byte?
        goto    add_exit1             ; No, below.  Checks are done.
; 
;       Check the least significant byte.
;
        movlw   ULIMIT_0          ; Fourth upper limit byte
        subwf   freq_0,w          ; Subtract limit value
        btfss   STATUS,C          ; Are we at the limit for the byte?
        goto    add_exit1             ; No, below.  Checks are done.
add_set_max
        movlw   ULIMIT_0          ; Get least significant upper limit
        movwf   freq_0            ; Set it in freq
        movlw   ULIMIT_1          ; Get the next byte upper limit
        movwf   freq_1            ; Set it in freq_1
        movlw   ULIMIT_2          ; Get the next byte upper limit
        movwf   freq_2            ; Set it in freq_2
        movlw   ULIMIT_3          ; Get the most significant upperlimit
        movwf   freq_3            ; Set it in freq_3
add_exit1
        return                    ; Return to the caller
;
; *****************************************************************************
; *    Name:  sub_step                                                        *
; *                                                                           *
; * Purpose:  Subtract the increment step from freq, checking that it does    *
; *           not go below zero.                                              *
; *                                                                           *
; *   Input:  The values in fstep and freq.                                   *
; *                                                                           *
; *  Output:  The updated value in freq.                                      *
; *                                                                           *
; *****************************************************************************
;
sub_step
        comf    fstep_0,f         ; Subtraction of fstep from
        comf    fstep_1,f         ;   freq is done by adding the
        comf    fstep_2,f         ;   twos compliment of fstep to
        comf    fstep_3,f         ;   freq.
        incfsz  fstep_0,f         ; Increment last byte
        goto    comp_done         ; Non-zero, continue
        incfsz  fstep_1,f         ; Increment next byte
        goto    comp_done         ; Non-zero, continue
        incfsz  fstep_2,f         ; Increment next byte
        goto    comp_done         ; Non-zero, continue
        incf    fstep_3,f         ; Increment the high byte
comp_done
        call    add_step          ; Add the compliment to do the subtraction
;
;       If the frequency has gone negative, clear it to zero.
;
        btfss   freq_3,7          ; Is high order frequency byte "negative"?   
        goto    exit2             ; No, keep going
set_min
        clrf    freq_0            ; Yes, set the frequency to zero
        clrf    freq_1            ; 
        clrf    freq_2            ; 
        clrf    freq_3            ; 
exit2
        return                    ; Return to the caller
; *****************************************************************************
; *    Name:  check_sub                                                       *
; *                                                                           *
; * Purpose:  Check if freq goes below the lower limit.                       *
; *                                                                           *
; *   Input:  The 32 bit values in freq                                       *
; *                                                                           *
; *  Output:  If freq is above the limit, it is unchanged.  Otherwise, it is  *
; *           set to equal the lower limit.                                   *
; *                                                                           *
; *****************************************************************************
;
check_sub
;
;       Check the most significant byte.
;
        movlw   LLIMIT_3          ; Get lower limit high byte
        subwf   freq_3,w          ; Sub limit from the current high byte
        btfss   STATUS,C          ; Current byte above or equal to the limit?  (Carry set if positive or zero)
        goto    sub_set_min       ; No, we're below so apply limit 
        movlw   LLIMIT_3          ; We are at or above limit so find out which
        subwf   freq_3,w          ; Sub limit from the current high byte
        btfss   STATUS,Z          ; Are we at the limit for the byte?
        goto    sub_exit          ; No, we are above so checks are done.
                                  ; Yes, at the limit so need to check next byte
;       Check the second most significant byte.
;
        movlw   LLIMIT_2          ; Get lower limit byte
        subwf   freq_2,w          ; Sub limit from the current byte
        btfss   STATUS,C          ; Current byte above or equal to the limit?  (Carry set if positive or zero)
        goto    sub_set_min       ; No, we're below so apply limit 
        movlw   LLIMIT_2          ; We are at or above limit so find out which
        subwf   freq_2,w          ; Sub limit from the current high byte
        btfss   STATUS,Z          ; Are we at the limit for the byte?
        goto    sub_exit          ; No, we are above so checks are done.
                                  ; Yes, at the limit so need to check next byte
;       Check the third most significant byte.
; 
        movlw   LLIMIT_1          ; Get lower limit byte
        subwf   freq_1,w          ; Sub limit from the current byte
        btfss   STATUS,C          ; Current byte above or equal to the limit?  (Carry set if positive or zero)
        goto    sub_set_min       ; No, we're below so apply limit 
        movlw   LLIMIT_1          ; We are at or above limit so find out which
        subwf   freq_1,w          ; Sub limit from the current high byte
        btfss   STATUS,Z          ; Are we at the limit for the byte?
        goto    sub_exit          ; No, we are above so checks are done.
                                  ; Yes, at the limit so need to check next byte
;       Check the least significant byte.
;
        movlw   LLIMIT_0          ; Get lower limit byte
        subwf   freq_0,w          ; Sub limit from the current byte
        btfsc   STATUS,C          ; Current byte above or equal to the limit?  (Carry set if positive or zero)
        goto    sub_exit          ; Yes, at the limit so OK              
                                  ; No, we're below so apply limit              
sub_set_min
        movlw   LLIMIT_0          ; Get least significant limit
        movwf   freq_0            ; Set it in freq
        movlw   LLIMIT_1          ; Get the next byte limit
        movwf   freq_1            ; Set it in freq_1
        movlw   LLIMIT_2          ; Get the next byte limit
        movwf   freq_2            ; Set it in freq_2
        movlw   LLIMIT_3          ; Get the most significant limit
        movwf   freq_3            ; Set it in freq_3
sub_exit 
        return                    ; Return to the caller


; *****************************************************************************
; *    Name:  CWShiftCheck                                                    *
; *                                                                           *
; * Purpose:  Need to shift the frequency up or down by sidetone amount when  *
; *           in receive mode.  When in transmit mode, no shift is done.      *
; *           Sidetone amount is added if CW+ mode or subtracted if CW- mode  *
; *           Set up "holding registers" for both transmit and receive such   *
; *           that they can be picked up quickly when switching between       *
; *           transmit and receive.                                           *
; *                                                                           *
; * Input:    ModeSelect, VFOcontrol                                          *
; *                                                                           *
; * Output:   XMSiReg8..12 set up with for current frequency                  *
; *           RSSiReg8..12 set up with for current frequency +/- sidetone     *
; *                                                                           *
; *****************************************************************************

CWShiftCheck   
        ; Check for CW Active. If not, exit
        btfsc   ModeSelect,MODECWPLUS ; Is it CW+?
        goto    CWShiftYesCW      ; Yes, see if we need shift
        btfss   ModeSelect,MODECWMINUS ; Or is it CW-?
        goto    CWShiftCheckExit  ; No, not CW so exit

CWShiftYesCW
        btfsc   VFOcontrol,CWREGSOK ; Are regs OK now as they are? 
        goto    CWShiftRegsOK     ; Yes, RegsOK

#ifdef  FSKON
        btfsc   FSKcontrol,FSKACTIVE ; Is FSK active (via menu)?
        goto    SetUpFSKRegs      ; Yes, go set up Registers for FSK Shift size
#endif  ; FSKON       

        pagesel CWSetUpRegs       ; Go to Page 1
        call    CWSetUpRegs       ; No, go set up CW Shift Regs  
                                  ; Input:  freq_3..freq_0  (will be XMIT Frequency)  
                                  ; Output: XmitFreq_3..XmitFreq_0 set up 
                                  ;         RcvFreq_3..RcvFreq_0 set up
                                  ;         XMSiReg8..XMSiReg12  set up   
                                  ;         RSSiReg8..RSSiReg12 set up       
        pagesel CWShiftCheck      ; Back to Page 0
        goto    CWShiftRegsOK     ;

#ifdef FSKON
SetUpFSKRegs
        pagesel FSKSetUpRegs      ; Switch to Page 1
        call    FSKSetUpRegs      ; No, set up FSK SiRegs
                                  ; Input:  freq_3..freq_0 (will be Mark Frequency)  
                                  ; Output: MarkFreq_3..MarkFreq_0 set up 
                                  ;         SpaceFreq_3..SpaceFreq_0 set up
                                  ;         MSiReg12..MSiReg8  set up   
                                  ;         SSiReg12..SSiReg8 set up       
        pagesel CWShiftCheck      ; Back to Page 0
#endif ; FSKON


CWShiftRegsOK

        btfsc   PORTB,CWLOWRCV    ; Is modulator requesting a RCV?
        goto    CWShiftXmit       ; No, go handle the XMIT.  

CWShiftRcv                                                    ;  or SPACE
        ; It's RCV so need a shift              
        btfsc   VFOcontrol,FORCESEND ; Do we want to send RCV SI bytes always?
        goto    CWShiftRcvSend    ; Yes, send Si bytes 

        btfss   VFOcontrol,CWXMITLAST ; Was XMIT the last?      ; MARK
        goto    CWShiftCheckExit   ; No, RCV was last so OK.    ; SPACE
                                 ; Yes, XMIT was last so set up RCV and send
CWShiftRcvSend    
        ; Xmit was last and this is RCV so set up shift
        banksel RSSiReg8         ; Switch to bank 1                               
        movf    RSSiReg8,w       ; Get RCV OFFSET frequency reg     ; SPACE
        banksel SiReg8           ; Back to bank 0
        movwf   SiReg8
        banksel RSSiReg9         ; Switch to bank 1
        movf    RSSiReg9,w       ; Get RCV OFFSET frequency reg     ; SPACE
        banksel SiReg9           ; Back to bank 0
        movwf   SiReg9
        banksel RSSiReg10        ; Switch to bank 1
        movf    RSSiReg10,w      ; Get RCV OFFSET frequency reg     ; SPACE
        banksel SiReg10          ; Back to bank 0
        movwf   SiReg10
        banksel RSSiReg11        ; Switch to bank 1
        movf    RSSiReg11,w      ; Get RCV OFFSET frequency reg     ; SPACE
        banksel SiReg11          ; Back to bank 0
        movwf   SiReg11
        banksel RSSiReg12        ; Switch to bank 1
        movf    RSSiReg12,w      ; Get RCV OFFSET frequency reg     ; SPACE
        banksel SiReg12          ; Back to bank 0
        movwf   SiReg12

        pagesel SendSiWords       ; Switch to Page 1
        call    SendSiWords       ; Send RCV regs (In Page 1)       ; SPACE
        pagesel main              ; Back to Page 0

        bcf     VFOcontrol,CWXMITLAST ; Say RCV is last sent        ; SPACE  
        goto    CWShiftCheckExit

CWShiftXmit
        ; It's Xmit so no shift
        btfsc   VFOcontrol,FORCESEND ; Do we want to send RCV SI bytes always?
        goto    CWShiftXmitSend    ; Yes, send Si bytes 

        btfsc   VFOcontrol,CWXMITLAST ; Was XMIT the last?
        goto    CWShiftCheckExit   ; Yes, XMIT was last so OK. 
                                 ; No, RCV was last so set up XMIT and send
CWShiftXmitSend    
    ; RCV was last and this was XMIT so need to change 
        banksel XMSiReg8         ; Switch to bank 1                                            
        movf    XMSiReg8,w       ; Get Xmit frequency reg           ; MARK  
        banksel SiReg8           ; Back to bank 0
        movwf   SiReg8           ; Save as Current SiReg
        banksel XMSiReg9         ; Switch to bank 1
        movf    XMSiReg9,w       ; Get Xmit frequency reg           ; MARK  
        banksel SiReg9           ; Back to bank 0
        movwf   SiReg9           ; Save as Current SiReg
        banksel XMSiReg10        ; Switch to bank 1
        movf    XMSiReg10,w      ; Get Xmit frequency reg           ; MARK  
        banksel SiReg10          ; Back to bank 0
        movwf   SiReg10          ; Save as Current SiReg
        banksel XMSiReg11        ; Switch to bank 1
        movf    XMSiReg11,w      ; Get Xmit frequency reg           ; MARK  
        banksel SiReg11          ; Back to bank 0
        movwf   SiReg11          ; Save as Current SiReg
        banksel XMSiReg12        ; Switch to bank 1
        movf    XMSiReg12,w      ; Get Xmit frequency reg           ; MARK  
        banksel SiReg12          ; Back to bank 0
        movwf   SiReg12          ; Save as Current SiReg

        pagesel SendSiWords       ; Switch to Page 1
        call    SendSiWords       ; Send XMIT registers  (In Page 1) ; MARK  
        pagesel main              ; Back to Page 0

        bsf     VFOcontrol,CWXMITLAST ; Say XMIT is last sent        ; MARK  

CWShiftCheckExit
        return
; 
;
; *****************************************************************************
; *    Name:  CalcRFREQ                                                       *
; *           Called by BuildSiWords                                          *
; *           Do this for every freq update so keep it as fast as possible    *
; *                                                                           *
; * Purpose:  Divide Fout by FxFactor to create a 38-bit RFREQ value with     *
; *           correct positioning for quick entry into SiRegs.                *
; *                                                                           *
; * Use 40x32-bit divide with quotient positioned for the Si570 bytes.        *
; *   Math routine was adapted from:                                          *
; *   SIGNED 32-BIT INTEGER MATHS ROUTINES FOR pic16 SERIES BY PETER HEMSLEY  *
; *                                                                           *
; * Divide RegA by RegB and return results in RegA                            *
; * RegC is used for the remainder in the calculation                         *
; *                                                                           *
; *  Input: <freq_3..freq_0> is Fout                                          *
; *         FxFactor for this band  ((Fxtal/(HSDIV)(N1))*256                  *
; *                                                                           *
; * Output: SiRegs set up with RFREQ                                          *
; *****************************************************************************
; * Example: <freq_3..freq_0> = 0x00 D6 01 28 = 14025000                      *
; *          Shifted here to give (Fout*256) = D6 01 28 00 = 3590400000       *
; *          <FxFactor3..FxFactor0> = 0x04 A9 A6 DD = 78227165                * <<<< FIXED FOR 10 MHz Si570
; *           (FxFactor is ((114285000/(34)(11))*256)                         * <<<< FIXED FOR 10 MHz Si570
; *          (D6 01 28 00 / 04 A9 A6 DD) *2^28 = 0x02 DE 5A 85 55             * <<<< FIXED FOR 10 MHz Si570
; *****************************************************************************
;
CalcRFREQ
        movf    freq_3,w          ; Dividend (numerator)   
        movwf   RegA4             ; MSB  (Fout shifted one byte to give Fout*256)
        movf    freq_2,w          ;
        movwf   RegA3             ;        
        movf    freq_1,w          ;
        movwf   RegA2             ;
        movf    freq_0,w          ;
        movwf   RegA1             ;
        clrf    RegA0             ; LSB
        movf    FxFactor3,w       ; Divisor (denominator) is FxFactor
        movwf   RegB3             ; MSB
        movf    FxFactor2,w       ;
        movwf   RegB2             ;    
        movf    FxFactor1,w       ;
        movwf   RegB1             ;    
        movf    FxFactor0,w       ;
        movwf   RegB0             ; LSB
RFREQDivide40by32  
        clrf    RegC0             ; Clear remainder
        clrf    RegC1
        clrf    RegC2
        clrf    RegC3
        movlw   D'68'             ; Loop counter- position for 36 bits in 40 bit field                 
        movwf   temp1 
FoDvloop  
        rlf     RegA0,f           ; Shift dividend (REGA) msb into remainder (REGC)
        rlf     RegA1,f
        rlf     RegA2,f
        rlf     RegA3,f
        rlf     RegA4,f
        rlf     RegC0,f
        rlf     RegC1,f
        rlf     RegC2,f
        rlf     RegC3,f
        movf    RegB3,w           ; Test if remainder (REGC) >= divisor (REGB)
        subwf   RegC3,w           ;
        BTFSS   STATUS,Z          ; 
        goto    FoDtstgt          ; Byte3 <> Byte3 so jump
        movf    RegB2,w           ; (Byte3=Byte3) so 
        subwf   RegC2,w           ;   look at Byte2
        BTFSS   STATUS,Z          ;
        goto    FoDtstgt          ; Byte2 <> Byte2 so jump
        movf    RegB1,w           ; (Byte3=Byte3) and (Byte2=Byte2) so 
        subwf   RegC1,w           ;   look at Byte1
        BTFSS   STATUS,Z          ;
        goto    FoDtstgt          ; Byte1 <> Byte1 so jump 
        movf    RegB0,w           ; (Byte3=Byte3) and (Byte2=Byte2) and (Byte1=Byte1) so look at Byte0
        subwf   RegC0,w           ;
FoDtstgt                      
        BTFSS   STATUS,C          ; Carry set if remainder >= divisor
        goto    FoDremlt          ; Divisor byte greater than remainder so quit 
        movf    RegB0,w           ; Subtract divisor (REGB) from remainder (REGC)
        subwf   RegC0,f           ;
        movf    RegB1,w           ;
        BTFSS   STATUS,C          ;
        incfsz  RegB1,w           ;
        subwf   RegC1,f           ;
        movf    RegB2,w           ;
        BTFSS   STATUS,C          ;
        incfsz  RegB2,w           ;
        subwf   RegC2,f           ;
        movf    RegB3,w           ;
        BTFSS   STATUS,C          ;
        incfsz  RegB3,w           ;
        subwf   RegC3,f           ;
        clrc                      ;              
        bsf     RegA0,0           ; Set quotient bit
FoDremlt  
        bcf     STATUS,C          ;
        decfsz  temp1,f           ; Next
        goto    FoDvloop
        movf    RegA4,w           ; Move to Si Registers
        movwf   SiReg8            ;
        movf    RegA3,w           ;
        movwf   SiReg9            ;
        movf    RegA2,w           ;
        movwf   SiReg10           ;
        movf    RegA1,w           ;
        movwf   SiReg11           ;
        movf    RegA0,w           ;
        movwf   SiReg12           ;
        return
;
;
; *****************************************************************************
; *    Name: BuildSiWords                                                     *
; *                                                                           *
; * Purpose: Take a guess...                                                  *
; *                                                                           *
; *   Input: freq_3..freq_0 set up with desired output frequency              *
; *          Band number                                                      *
; *          FxFactor3..FxFactor0                                             *
; *                                                                           *
; *  Output: SiRegs set up                                                    *
; *                                                                           *
; *   Calls: CalcRFREQ                                                        *
; *                                                                           *
; * Example: freq=14025000  = 0x00 D6 01 28                                   *
; *          Band 3                                                           * <<<< OK for Si570
; *          Fxtal=114,285,000 = (non-calibrated)                             * <<<< OK for Si570
; *          FxFactor3..0 = 04 A9 A6 DD (non-calibrated)                      * <<<< Fixed for 10MHz Si570
; *          HSDIV=11  (Index=7)                                              *
; *          N1=34                                                            *
; *          Result: SiRegs8..12 = 0xE8 42 DE 5A 85 55                        * <<<< Fixed for 10MHz Si570
; *****************************************************************************
;
BuildSiWords                 

; Get FxFactor from Band Table 
        movf    band,w            ; Band number
        movwf   new_band_index    ; Convert to index
        bcf     STATUS,C          ;
        rlf     new_band_index,f  ;   by multiplying
        rlf     new_band_index,f  ;   by 4
        movf    new_band_index,w  ; 

        addlw   low HSN1FX000     ; Now have pointer to MSB of FxFactor for this band
        movwf   FSR               ; Set up for first byte load
        bsf     STATUS,IRP        ; Set for Indirect Addressing into Bank 2 
        movf    INDF,w            ; Get MSB
        movwf   FxFactor3         ;
        incf    FSR,f             ;
        movf    INDF,w            ; Get next
        movwf   FxFactor2         ;
        incf    FSR,f             ;
        movf    INDF,w            ; Get next
        movwf   FxFactor1         ;
        incf    FSR,f             ;
        movf    INDF,w            ; Get LSB
        movwf   FxFactor0         ;
        bcf     STATUS,IRP        ; Clear bit for Indirect addressing into Bank 2 

        call    CalcRFREQ         ; In -  freq_3..freq_0 set up with FreqOut and
                                  ;       <FxFactor3..FxFactor0> set up with 
                                  ;           FxFactor per band               
                                  ; Out - RFREQ in RegA4..RegA0

        movf    RegA4,w           ; MSB from division
        movwf   SiReg8            ;   
        movf    RegA3,w           ;
        movwf   SiReg9            ;
        movf    RegA2,w           ;
        movwf   SiReg10           ;
        movf    RegA1,w           ;
        movwf   SiReg11           ;
        movf    RegA0,w           ; LSB
        movwf   SiReg12           ;

; Now add HSDIV and N1

        movf    band,w            ; Index into table
        call    BandN1Minus1      ; Get N1-1 in w
        movwf   CurN1Minus1       ;   for this band
        
        movf    band,w            ; Index into table
        call    BandHSDIVIndex    ; Get HSDIVIndex in w
        movwf   CurHsdivIndex     ;   for this band

        clrf    temp2             ; initialize
        movf    CurHsdivIndex,w   ; HSDIV[2:0] pre-positioned to bits 7:5
        movwf   SiReg7            ; 
        movf    CurN1Minus1,w     ; N1-1
        movwf   temp1             ; move to temporary storage
        clrf    temp2             ; intialize
        bcf     STATUS,C          ; Clear carry (don't want junk in bit 7 after rrf)
        rrf     temp1,f           ; rotate into position, bit into C
        rrf     temp2,f           ; rotate C into new temp reg
        rrf     temp1,f           ; rotate into position, bit into C              
        rrf     temp2,f           ; rotate C into new temp reg
        movf    temp1,w           ; 
        iorwf   SiReg7,f          ; Now (N1-1)[6:2] is in SiReg7[4:0] with HSDIV[2:0]
        movf    temp2,w           ; 
        iorwf   SiReg8,f          ; Now have (N1-1)[1:0] in [7:6] and RFREQ[37:32] in [5:0]

        btfss   ModeSelect,MODEDEBUG ; Is Debug Mode turned on?
        goto    BuildSiWordsDone  ; No, end  
        pagesel DisplaySiRegs
        call    DisplaySiRegs     ; Yes, Display Si Regs    DEBUG                  PAGE 1
        pagesel BuildSiWords

BuildSiWordsDone
        ; Now SiRegs set up.

        return

; *****************************************************************************
; *    Name: CalcSpan                                                         *
; *                                                                           *
; * Purpose: Calculate frequency span since                                   *
; *                                                                           *
; *   Input: fstep_4:fstep_0                                                  *
; *          RFREQCenter_High, RFREQCenter_Low                                *
; *          VFOcontrol                                                       *
; *                                                                           *
; *  Output: VFOcontrol NEEDFREEZE flag set up if freeze needed               *
; *                                                                           *
; *****************************************************************************
;
CalcSpan
        btfsc   VFOcontrol,BANDCHANGED ;  Is band changed?
        goto    SpanNeedFreeze         ; Yes, freeze needed 

        bcf     VFOcontrol,NEEDFREEZE ; Default 

        ; Get absolute value of (current freq - center frequency)
        ; Then compare to max span size
       
        ; See which is larger
        movlw    0x3F             ; Mask off high 2 bits (keep only integer of RFREQ)
        andwf    SiReg8,w         ; Integer portion of SiReg8 in W 
        subwf    RFREQCenter_High,w ; Subtract high byte from center high byte
        btfss    STATUS,C         ; C set if positive or zero 
        goto     CurrentRFREQLarger ; Negative result so CurrentRFREQ is larger
        btfss    STATUS,Z         ; See if equal 
        goto     CenterLarger     ; No, center is larger. 
        ; Equal so far, so check next digit
        movf     SiReg9,w         ; 4 integer plus 4 fraction bits in SiReg9 into W 
        subwf    RFREQCenter_Medium,w ; Subtract high byte from center high byte
        btfss    STATUS,C         ; C set if positive or zero 
        goto     CurrentRFREQLarger ; Negative result so CurrentRFREQ is larger
        btfss    STATUS,Z         ; See if equal 
        goto     CenterLarger     ; No, center is larger. 
        ; Equal so far, so check next digit
        movf     SiReg10,w         ; 
        subwf    RFREQCenter_Low,w ; Subtract low byte from center high byte
        btfss    STATUS,C         ; C set if positive or zero
        goto     CurrentRFREQLarger ; Negative result so CurrentRFREQ is larger
        btfss    STATUS,Z         ; See if equal 
        goto     CenterLarger     ; No, center is larger 
        goto     CalcSpanDone     ; Yes, equal so exit as is

CenterLarger
        movf     RFREQCenter_High,w ; Get high byte from RFREQCenter
        movwf    RegA2            ; as high byte of larger
        movf     RFREQCenter_Medium,w ; Get medium byte from RFREQCenter
        movwf    RegA1            ; as medium byte of larger
        movf     RFREQCenter_Low,w ;  Get low byte from RFREQCenter
        movwf    RegA0            ; as low byte of larger
                                  ;
        movlw    0x3F             ; Mask off high 2 bits (keep only integer of RFREQ)
        andwf    SiReg8,w         ; Integer portion of SiReg8 in W 
        movwf    RegB2            ;   as high byte of smaller
        movf     SiReg9,w         ; Get medium byte from SiRegs
        movwf    RegB1            ;   as medium byte of smaller
        movf     SiReg10,w        ; Get low byte from SiRegs
        movwf    RegB0            ;   as low byte of smaller
        goto     FindDifference   ;

CurrentRFREQLarger
        movlw    0x3F             ; Mask off high 2 bits (keep only integer of RFREQ)
        andwf    SiReg8,w         ; Integer portion of SiReg8 in W 
        movwf    RegA2            ;   as high byte of larger
        movf     SiReg9,w         ; Get medium byte from SiRegs
        movwf    RegA1            ;   as medium byte of larger
        movf     SiReg10,w        ; Get low byte from SiRegs
        movwf    RegA0            ;   as low byte of larger
                                  ;
        movf     RFREQCenter_High,w ; Get high byte from RFREQCenter
        movwf    RegB2            ;    as high byte of smaller
        movf     RFREQCenter_Medium,w ; Get medium byte from RFREQCenter
        movwf    RegB1            ;    as medium byte of smaller
        movf     RFREQCenter_Low,w ; Get low byte from RFREQCenter
        movwf    RegB0            ;  as low byte of smaller

FindDifference
        ; Add the compliment to do the subtraction
        comf    RegB2,f           ;   twos compliment of 
        comf    RegB1,f           ;   smaller to
        comf    RegB0,f           ;   larger
        incfsz  RegB0,f           ; Increment low byte
        goto    SpanCompDone      ; Non-zero, continue
        incfsz  RegB1,f           ; Increment medium byte
        goto    SpanCompDone      ; Non-zero, continue
        incf    RegB2,f           ; Increment high byte
SpanCompDone
        movf    RegB0,w           ; Get low byte of the increment
        addwf   RegA0,f           ; Add it to the low byte of freq
        btfss   STATUS,C          ; Any carry?
        goto    SpanAdd1          ; No, add next byte
        incfsz  RegA1,f           ; Increment medium byte
        goto    SpanAdd1          ; Non-zero, continue
        incf    RegA2,f           ; Ripple carry up to the high byte
SpanAdd1
        movf    RegB1,w           ; Get medoi, byte of the increment
        addwf   RegA1,f           ; Add it to the medium byte of freq
        btfss   STATUS,C          ; Any carry?
        goto    SpanAdd2          ; No, add next byte
        incf    RegA2,f           ; Ripple carry up to the high byte
SpanAdd2
        movf    RegB2,w           ; Get the most significant increment byte
        addwf   RegA2,f           ; Add it to the high byte of freq
        
        ; Span now in RegA2..RegA0
        ; See if difference is less than 0x0.25000

        movf    RegA2,w           ; Check highest byte of difference
        btfss   STATUS,Z          ; If this byte is non-zero
        goto    SpanNeedFreeze    ;   then need freeze for sure

        movlw   0x02              ; 0x2 = maximum high-fractional RFREQ diff w/o freeze
        subwf   RegA1,w           ; See if A1 >= 0x02 
        btfss   STATUS,C          ; Carry is set if result is positive or zero
        goto    CalcSpanDone      ; A1,A0 < 0x0.2500 so no freeze needed
        btfss   STATUS,Z          ; If this result is non-zero
        goto    SpanNeedFreeze    ;   then need freeze for sure
        ; 
        movlw   0x50              ; 0x50 = maximum low-fractional RFREQ diff w/o freeze
        subwf   RegA0,w           ; See if A0 >= 0x50 
        btfss   STATUS,C          ; Carry is set if result is positive or zero
        goto    CalcSpanDone      ; A1,A0 < 0x0.2500 so no freeze needed
                                  ; If >= then need freeze
SpanNeedFreeze 
        bcf     VFOcontrol,BANDCHANGED ; Band changed so get ready for next time 
        bsf     VFOcontrol,NEEDFREEZE ; Need freeze
                                  ; Set up center freq with RFREQ byte
        movlw   0x3F              ; Mask off high 2 bits (part of N1)
        andwf   SiReg8,w          ; Integer portion of SiReg8 in W 
        movwf   RFREQCenter_High  ;   as high byte for comparison
        movf    SiReg9,w          ; Set up center freq with RFREQ byte
        movwf   RFREQCenter_Medium ;  as medium byte for comparison
        movf    SiReg10,w         ; Set up center freq with RFREQ byte
        movwf   RFREQCenter_Low   ;   as low byte for comparison

CalcSpanDone
        return

;*****************************************************************************
; *    Name:  detect_band_change                                             *
; *           NOTE: called ONLY by UpdateFrequency                           *
; *                                                                          *
; * Purpose:  This routine detects the current band that is pointed to by    *
; *           band table each time one of the band-change pushbuttons is     *
; *           pressed or the encoder moves.                                  *
; *                                                                          *
; *   Input:  band contains the number of the band before this check         *
; *           <freq_3:freq_0> contains the current frequency                 *
; *                                                                          *
; *  Output:  band updated to contain current band number                    *
; *           band_index updated to contain current band base index          *
; *           band_changed_flag = 1 if band changed, 0 if it did not         *
;*****************************************************************************
;
detect_band_change
        movlw   0x00              ; Start at the ms digit of the lowest freq band
        movwf   new_band_index    ; Save as current index
        movwf   new_band_base     ; Save as base of current band also 
        goto    band_table_check  ; Look to see if we are within limits
detect_loop
        movlw   0x04              ; Increment to
        addwf   new_band_base,f   ;   next band 
        movf    new_band_base,w   ; Get updated base 
        movwf   new_band_index    ;   and save as new starting index 
        movf    new_band_base,w   ; Load into W for index for band table call
band_table_check
        call    SiBandTable       ; Get the most significant byte into W
        subwf   freq_3,w          ; Compare to current frequency byte
        btfsc   STATUS,Z          ; See if byte matches
        goto    band_check_2eq    ; Yes, match, so look at next digit
        btfsc   STATUS,C          ; Carry is set if result is positive or zero
        goto    detect_next_band  ; C Set, so work>band so go to next band
        goto    band_back_up      ; C Clear, so work < band, so back up and exit
band_check_2eq
        incf    new_band_index,f  ; Point to next byte of current band
        movf    new_band_index,w  ; Put index W for call 
        call    SiBandTable       ; Get the next most significant byte into W
        subwf   freq_2,w          ; Compare to current frequency byte
        btfsc   STATUS,Z          ; See if byte matches
        goto    band_check_1eq    ; Yes, match, so look at next digit
        btfsc   STATUS,C          ; Carry is set if result is positive or zero
        goto    detect_next_band  ; C Set, so work>band so go to next band
        goto    band_back_up      ; C Clear, so work < band, so back up and exit
band_check_1eq  
        incf    new_band_index,f  ; Point to next byte of current band
        movf    new_band_index,w  ; Put index W for call 
        call    SiBandTable       ; Get the next most significant byte into W
        subwf   freq_1,w      ; Compare to current frequency byte
        btfsc   STATUS,Z          ; See if byte matches
        goto    band_check_0eq    ; Yes, match, so look at next digit
        btfsc   STATUS,C          ; Carry is set if result is positive or zero
        goto    detect_next_band  ; C Set, so work>band so go to next band
        goto    band_back_up      ; C Clear, so work < band, so back up and exit
band_check_0eq
        incf    new_band_index,f  ; Point to next byte of current band
        movf    new_band_index,w  ; Put index W for call 
        call    SiBandTable       ; Get the least significant byte into W
        subwf   freq_0,w      ; Compare to current frequency byte
        btfsc   STATUS,Z          ; See if byte matches
        goto    detect_change     ; Yes, match.  All digits match. 
        btfsc   STATUS,C          ; Carry is set if result is positive or zero
        goto    detect_next_band  ; C Set, so work>band so go to next band
        goto    band_back_up      ; 
;
band_back_up
        movlw   0x04              ; Move pointer back by 4 to use previous band
        subwf   new_band_base,f   ; Save updated base index
        goto    detect_change     ;   and leave

detect_next_band
        ; See if we are at highest band already. 
        movlw   HIGHEST_BAND_BASE ; See if we are at
        subwf   new_band_base,w   ;  the highest band
        btfss   STATUS,Z          ; At hightest band?
        goto    detect_loop       ; No, loop back and keep looking
                                  ; Yes, quit looking
detect_change
;
;       We found the lowest valid band.  Now see if it has changed.
;      
        movf     band,w           ; Current Band number into w
        movwf    band_index       ; Convert to index
        bcf      STATUS,C         ; 
        rlf      band_index,f     ;   by multiplying
        rlf      band_index,f     ;   by 4
        movf     band_index,w     ; 
        
        bsf      VFOcontrol,BANDCHANGED ; Default - band changed
        movf     new_band_base,w   ; Get new band base index
        subwf    band_index,w      ; Compare to current band index 
        btfsc    STATUS,Z          ; If band did not change
        bcf      VFOcontrol,BANDCHANGED ;   then clear the band changed flag
        movf     new_band_base,w   ; Update the band to
        movwf    band_index        ;   the current band 
        movwf    band              ; Construct band from index
        bcf      STATUS,C          ; 
        rrf      band,f            ;   by dividing
        rrf      band,f            ;     by 4
;
; NOTE: If band and band_index get updated, it is done here!
; In this case, old_band_index has just been updated as well.
; (Will use old_band_index to reset the band relay.)
;
detect_exit
        return                    ; Return
;
; *****************************************************************************
; *    Name:  init_LCD                                                        *
; *                                                                           *
; * Purpose:  Power on initialization of Liquid Crystal Display.  The LCD     *
; *           controller chip must be equivalent to an Hitachi 44780.  The    *
; *           LCD is assumed to be a 16x2 display.                            *
; *                                                                           *
; *   Input:  None                                                            *
; *                                                                           *
; *  Output:  None                                                            *
; *                                                                           *
; *****************************************************************************
;
init_LCD
        call    wait_64ms         ; Wait for LCD to power up
;        Put 4-bit command in RB3..RB0                                         
;        PIC's RB3..RB0 lines connect to LCD's DB7..DB4 (pins 14-11)            
        movlw   0x03              ; LCD init instruction (First)               
        movwf   PORTB             ; Send to LCD via RB3..RB0                   
        bsf     PORTB,LCD_e       ; Set the LCD E line high,
        call    wait_64ms         ;   wait a "long" time,
        bcf     PORTB,LCD_e       ;   and then Clear E 
        movlw   0x03              ; LCD init instruction (Second)              
        movwf   PORTB             ; Send to LCD via RB3..RB0                   
        bsf     PORTB,LCD_e       ; Set E high,
        call    wait_32ms         ;   wait a while,
        bcf     PORTB,LCD_e       ;   and then Clear E 
        movlw   0x03              ; LCD init instruction (Third)               
        movwf   PORTB             ; Send to LCD via RB3..RB0                   
        bsf     PORTB,LCD_e       ; Set E high,
        call    wait_32ms         ;   wait a while,
        bcf     PORTB,LCD_e       ;   and then Clear E
        movlw   0x02              ; 4-bit mode instruction                     
        movwf   PORTB             ; Send to LCD via RB3..RB0                   
        bsf     PORTB,LCD_e       ; Set E high,
        call    wait_16ms         ;   wait a while,
        bcf     PORTB,LCD_e       ;   and then Clear E
        movlw   0x28              ; 1/16 duty cycle, 5x8 matrix
        call    cmnd2LCD          ; Send command in w to LCD
        movlw   0x08              ; Display off, cursor and blink off 
        call    cmnd2LCD          ; Send command to LCD
        movlw   0x01              ; Clear and reset cursor
        call    cmnd2LCD          ; Send command in w to LCD
        movlw   0x06              ; Set cursor to move right, no shift
        call    cmnd2LCD          ; Send command in w to LCD

        ;movlw   0x0C              ; Display on, cursor and blink off
        movlw   0x0E              ; Display on, cursor and blink off, Underline on

        call    cmnd2LCD          ; Send command in w to LCD
        return                    ; 
;
; *****************************************************************************
; *    Name:  Load_HSN1FX_Table                                               *
; *                                                                           *
; * Purpose:  Called at power-up, this routine moves the Fxtal/HSDIVN1 table  *
; *           from EEPROM to the local memory.                                *
; *                                                                           *
; *   Input:  None                                                            *
; *                                                                           *
; *  Output:  Updated Fxtal/HSDIVN1 table in memory                           *
; *                                                                           *
; *****************************************************************************
;
Load_HSN1FX_Table
        movlw   0x5F              ; 96 bytes for 24 band tables (4 entries each)
        movwf   count             ; Highest byte number to access      (Bank 0)

        clrf    temp1             ; Start current pointer              (Bank 0)
        movlw   low HSN1FX000     ; First location in RAM              (Bank 2)
        bsf     STATUS,IRP        ; Set for Bank 2 indirect addressing
        addwf   temp1,w           ; add current pointer into table
        movwf   FSR               ; Set up for indirect addressing   (any bank)
        banksel EEADR             ; Switch to Bank 1
        clrf    EEADR             ; Reset the EEPROM read address      (Bank 1)
        banksel PORTA
        decf    temp1,f           ; prepare for loop increment

Load_loop
        incf    temp1,f           ; Current byte count                 (Bank 0)
        banksel EEADR             ; Switch to Bank 2 (16F88) 
        call    read_EEPROM       ; Read EEPROM (all in bank 1)
        movf    EEDATA,w          ; Get the EEPROM data byte           (Bank 2)
        banksel PORTA             ;                                    (Bank 0)
        movwf   INDF              ; Save byte in table in RAM        (any bank)
        incf    FSR,f
        banksel PORTA             ;                                    (Bank 0)
        movfw   count             ; Max byte number                    (Bank 0)
        subwf   temp1,w           ; See if we are there yet            (Bank 0)
        btfss   STATUS,Z          ; Done?
        goto    Load_loop         ; No, loop back
        return                    ; Yes, return
;
; *****************************************************************************
; *    Name:  display_version                                                 *
; *                                                                           *
; * Purpose:  Display version and other info on LCD for 2 seconds             *
; *           upon power-up                                                   *
; *                                                                           *
; *   Input:  MCODE_REV_0 through MCODE_REV_4                                 *
; *                                                                           *
; *  Output:  LCD displays debug info                                         * 
; *                                                                           *
; *****************************************************************************
;
display_version
        movlw   0x80              ; Point LCD at digit 1                
        call    cmnd2LCD          ;
        movlw   'P'               ; digit 1              
        call    data2LCD          ;
        movlw   'E'               ; digit 2                  
        call    data2LCD          ;
        movlw   'g'               ; digit 3                
        call    data2LCD          ;
        movlw   'e'               ; digit 4               
        call    data2LCD          ;
        movlw   'n'               ; digit 5              
        call    data2LCD          ;        
        movlw   '5'               ; digit 6              
        call    data2LCD          ;
        movlw   '7'               ; digit 7               
        call    data2LCD          ;
        movlw   '0'               ; digit 8               
        call    data2LCD          ;
        movlw   0x88              ; Point LCD at digit 9
        ;movwf   LCD_char          ; 
        call    cmnd2LCD          ; Send command to LCD
        movlw   MCODE_REV_0       ; Get mcode rev byte
        call    data2LCD          ;   and display it (digit 9)
        movlw   MCODE_REV_1       ; Get mcode rev byte
        call    data2LCD          ;   and display it (digit 10)
        movlw   MCODE_REV_2       ; Get mcode rev byte
        call    data2LCD          ;   and display it (digit 11)
        movlw   MCODE_REV_3       ; Get mcode rev byte
        call    data2LCD          ;   and display it (digit 12)
        movlw   MCODE_REV_4       ; Get mcode rev byte
        call    data2LCD          ;   and display it (digit 13)
        movlw   MCODE_REV_5       ; Get mcode rev byte
        call    data2LCD          ;   and display it (digit 14)
        movlw   MCODE_REV_6       ; Get mcode rev byte
        call    data2LCD          ;   and display it (digit 15)
        movlw   MCODE_REV_7       ; Get mcode rev byte
        call    data2LCD          ;   and display it (digit 16)
        movlw   0xC0              ; Point LCD at digit 1 of second line               
        call    cmnd2LCD          ;
        movlw   'A'               ; digit 1              
        call    data2LCD          ;
        movlw   'A'               ; digit 2                  
        call    data2LCD          ;
        movlw   '0'               ; digit 3                
        call    data2LCD          ;
        movlw   'Z'               ; digit 4               
        call    data2LCD          ;
        movlw   'Z'               ; digit 5              
        call    data2LCD          ;        
        movlw   ' '               ; digit 6              
        call    data2LCD          ;
        movlw   '/'               ; digit 7               
        call    data2LCD          ;
        movlw   ' '               ; digit 8               
        call    data2LCD          ;
        movlw   'K'               ; Space in digit 9
        call    data2LCD          ;
        movlw   'a'               ; digit 10             
        call    data2LCD          ;
        movlw   'n'               ; digit 11                 
        call    data2LCD          ;
        movlw   'g'               ; digit 12               
        call    data2LCD          ;
        movlw   'a'               ; digit 13              
        call    data2LCD          ;
        movlw   ' '               ; digit 14              
        call    data2LCD          ;
        movlw   'U'               ; digit 15             
        call    data2LCD          ;        
        movlw   'S'               ; digit 16             
        call    data2LCD          ;        

        call    wait_a_sec        ; Wait one second  
        call    wait_4ms          ; SHORT WAIT

;
        movlw   0xC0              ; Point LCD at digit 1 of second line 
        call    cmnd2LCD          ;
        movlw   ' '               ; digit 1              
        call    data2LCD          ;
        movlw   ' '               ; digit 2                  
        call    data2LCD          ;
        movlw   ' '               ; digit 3                
        call    data2LCD          ;
        movlw   ' '               ; digit 4               
        call    data2LCD          ;
        movlw   ' '               ; digit 5              
        call    data2LCD          ;        
        movlw   ' '               ; digit 6              
        call    data2LCD          ;
        movlw   ' '               ; digit 7               
        call    data2LCD          ;
        movlw   ' '               ; digit 8               
        call    data2LCD          ;
        movlw   ' '               ; digit 9
        call    data2LCD          ;
        movlw   ' '               ; digit 10             
        call    data2LCD          ;
        movlw   ' '               ; digit 11                 
        call    data2LCD          ;
        movlw   ' '               ; digit 12               
        call    data2LCD          ;
        movlw   ' '               ; digit 13              
        call    data2LCD          ;
        movlw   ' '               ; digit 14              
        call    data2LCD          ;
        movlw   ' '               ; digit 15             
        call    data2LCD          ;        
        movlw   ' '               ; digit 16             
        call    data2LCD          ;        
;
        return

;
; *****************************************************************************
; *    Name:  DisplayCalibrating                                              *
; *                                                                           *
; * Purpose:  Display "Calibrating" on second line during calibration         *
; *                                                                           *
; *   Input:  None                                                            *
; *                                                                           *
; *  Output:  LCD displays "Calibrating"                                      *
; *                                                                           *
; *****************************************************************************

DisplayCalibrating
        movlw   0xC0              ; Point LCD at Line 2, position 1                
        call    cmnd2LCD          ;
        movlw   " "               ; 
        call    data2LCD          ;
        movlw   " "               ; 
        call    data2LCD          ;
        movlw   "C"               ; 
        call    data2LCD          ;
        movlw   "a"               ; 
        call    data2LCD          ;
        movlw   "l"               ; 
        call    data2LCD          ;
        movlw   "i"               ; 
        call    data2LCD          ;
        movlw   "b"               ; 
        call    data2LCD          ;
        movlw   "r"               ; 
        call    data2LCD          ;
        movlw   "a"               ; 
        call    data2LCD          ;
        movlw   "t"               ; 
        call    data2LCD          ;
        movlw   "i"               ; 
        call    data2LCD          ;
        movlw   "n"               ; 
        call    data2LCD          ;
        movlw   "g"               ; 
        call    data2LCD          ;
        movlw   " "               ; 
        call    data2LCD          ;
        movlw   " "               ; 
        call    data2LCD          ;
        movlw   " "               ; 
        call    data2LCD          ;
        return

; *****************************************************************************
; *    Name:  DisplayCalibrateDone                                            *
; *                                                                           *
; * Purpose:  Display "Calibrate done" on second line during calibration      *
; *                                                                           *
; *   Input:  None                                                            *
; *                                                                           *
; *  Output:  LCD displays "Calibrate done"                                   *
; *                                                                           *
; *****************************************************************************

DisplayCalibrateDone
        movlw   0xC0              ; Point LCD at Line 2, position 1                
        call    cmnd2LCD          ;
        movlw   " "               ; 
        call    data2LCD          ;
        movlw   "C"               ; 
        call    data2LCD          ;
        movlw   "a"               ; 
        call    data2LCD          ;
        movlw   "l"               ; 
        call    data2LCD          ;
        movlw   "i"               ; 
        call    data2LCD          ;
        movlw   "b"               ; 
        call    data2LCD          ;
        movlw   "r"               ; 
        call    data2LCD          ;
        movlw   "a"               ; 
        call    data2LCD          ;
        movlw   "t"               ; 
        call    data2LCD          ;
        movlw   "e"               ; 
        call    data2LCD          ;
        movlw   " "               ; 
        call    data2LCD          ;
        movlw   "d"               ; 
        call    data2LCD          ;
        movlw   "o"               ; 
        call    data2LCD          ;
        movlw   "n"               ; 
        call    data2LCD          ;
        movlw   "e"               ; 
        call    data2LCD          ;
       
        call    ShowActiveDigit   ; Set up underline on line 1 again

        return

;
; *****************************************************************************
; *    Name:  calibrate                                                       *
; *                                                                           *
; * Purpose: This routine will read the SiRegs from the Si570 and calculate   *
; *          the frequency of the internal crystal based on these values and  *
; *          the default output frequency frequency of this Si770 part number.*
; *          The calculated Fxtal is then used to update the Fxtal/HSDIVN1    *
; *          table in EEPROM. The table is loaded into memory at start-up.    *
; *                                                                           *
; *          The default Si570 frequency is set up in the factory for         *
; *          different Si570 part numbers.  The default is then entered in    *
; *          the #DEFINE statements.                                          *
; *                                                                           *
; *          This routine is entered if the calibrate pushbutton (PIC-EL PB_3)*
; *          is pressed at start-up time.                                     *
; *                                                                           *
; *   Input:  None                                                            *
; *                                                                           *
; *  Output:  The updated Fxtal/HSDIVN1 table in EEPROM                       *
; *                                                                           *
; *  Uses:    temp1, temp2, temp3                                             *
; *****************************************************************************
;
calibrate

        call    DisplayCalibrating ; Display "Calibrating" on line 2 of LCD

; Note: The HSN1FX table will be loaded from EEPROM into RAM at startup
; Build Band Table in EEPROM using actual "retrieved" Fxtal value. 
; Each band table entry is a 4-byte value of (Fxtal/HSDIVN1) * 256;    
; Entries constructed by dividing (Fxtal * 256) by HSDIVN1       
; 
; First, calculate Fxtal

        call    SetUpFxtal        ; Get default Fxtal from this Si570
                                  ; Upon exit, Fxtalx256_4..Fxtalx256_0 set up  
; Populate table in EEPROM
;
        movlw   D'96'             ; Count of total bytes in table
        movwf   temp3             ; 
        movlw   0xFF              ; Band number 
        movwf   band              ; Will be Band 0 after increment
        movlw   HSN1FXTBLSTART    ; Point to HSN1FX table in EEPROM 
        banksel EEADR             ;   
        movwf   EEADR             ; Set up starting EEPROM address       Bank 2
        banksel PORTA             
        
calHSN1loop                     
        incf    band,f            ; Increment to next band
        movf    band,w            ; current band as index
        call    BandHSN1D2        ; Get HSN1D2 in w
        movwf   CurHsdivN1D2      ; Save as current
         
        call    calcHSN1Entry     ; In:  Fxtal and CurHsdivN1D2
                                  ; Out: FxFactor3..0 for this band
                                  ; Uses Temp1

        movlw   D'4'              ;
        movwf   temp2             ; Count of bytes in FxFactor entry
        movlw   FxFactor3         ; Address of FxFactor MSB                                 
        movwf   FSR
CalByteLoop
        bcf     STATUS,IRP        ; INDF to banks 0 or 1
        movf    INDF,w            ; Get FxFactor byte indirectly

        banksel EEDATA
        movwf   EEDATA            ; This is the byte to write to EEPROM
        banksel PORTA
        
        call    write_EEPROM      ; Write byte to EEPROM           (all in Bank 2)
                                  ; EEADR incremented in routine
        banksel PORTA
        incf    FSR,f             ; Move pointer to next FxFactor byte

        decf    temp3,f           ; Decrement table byte count remaining
        btfsc   STATUS,Z          ; Done?
        goto    CalHSN1Done       ; Yes, exit

        decfsz  temp2,f           ; Done with this FxFactor entry?
        goto    CalByteLoop       ; No, loop back

        goto    calHSN1loop       ; No, loop

CalHSN1Done
        call    DisplayCalibrateDone ; Done
        
        return 
;                                                                             
; **************************************************************************** 
; *    Name:  update_StartFreq_EEPROM                                        *                      
; *                                                                          *
; * Purpose:  This routine will save the current frequency in EEPROM.        *  
; *                                                                          *  
; *   Input:  Current frequency in freq_3.. freq_0                           *  
; *                                                                          *  
; *  Output:  Current frequency saved in EEPROM for subsequent start-ups     *  
; *                                                                          *  
; **************************************************************************** 
;                                                                              
update_StartFreq_EEPROM ;                                                                
        movlw   EEStartupFreqAdr     ; Get EEPROM location for startup frequency
        banksel EEADR             ; Change to EEADR bank 
        movwf   EEADR             ;   and set up for start of EEPROM writes    
        banksel PORTA             ; Change back to bank 0 for next byte
        movf    freq_3,w          ; Get the second freq byte to write (Bank 0)
        banksel EEDATA            ; Change to EEDATA bank
        movwf   EEDATA            ; Second freq byte to EEPROM Write register
        call    write_EEPROM      ; Write it (Bank 1)                                    
        banksel PORTA             ; Change back to bank 0 for next byte
        movf    freq_2,w          ; Get the third freq byte to write (Bank 0)
        banksel EEDATA            ; Change to EEDATA bank
        movwf   EEDATA            ; Third freq byte to EEPROM Write register
        call    write_EEPROM      ; Write it (Bank 1)                                    
        banksel PORTA             ; Change back to bank 0 for next byte
        movf    freq_1,w          ; Get the fourth freq byte to write (Bank 0)
        banksel EEDATA            ; Change to EEDATA bank
        movwf   EEDATA            ; Fourth freq byte to EEPROM Write register
        call    write_EEPROM      ; Write it (Bank 1)                                    
        banksel PORTA             ; Change back to bank 0 for next byte
        movf    freq_0,w          ; Get the fourth freq byte to write (Bank 0)
        banksel EEDATA            ; Change to EEDATA bank
        movwf   EEDATA            ; Fourth freq byte to EEPROM Write register
        call    write_EEPROM      ; Write it (Bank 1)                                    
        banksel PORTA             ; Change back to bank 0

        return                    ;                                            
;
; **************************************************************************** 
; *    Name:  update_ModeSelect_EEPROM                           ; v7.6      *                      
; *                                                                          *
; * Purpose:  This routine will save the ModeSelect byte in EEPROM.          *  
; *                                                                          *  
; *   Input:  ModeSelect byte                                                *  
; *                                                                          *  
; *  Output:  Current ModeSelect value saved in EEPROM for subsequent start-ups*  
; *                                                                          *  
; **************************************************************************** 
;                                                                              
update_ModeSelect_EEPROM ;                                              ; v7.6                         
        movlw   EEModeSelectAdr   ; Get EEPROM location for ModeSelect byte
        banksel EEADR             ; Change to EEADR bank 
        movwf   EEADR             ;   and set up for start of EEPROM writes    
        banksel PORTA             ; Change back to bank 0 for next byte
        movf    ModeSelect,w      ; Get the ModeSelect byte to write (Bank 0)
        banksel EEDATA            ; Change to EEDATA bank
        movwf   EEDATA            ; ModeSelect byte into EEPROM Write register
        call    write_EEPROM      ; Write it (Bank 1)                                    
        banksel PORTA             ; Change back to bank 0 
        return                    ;                                            
;
; *****************************************************************************
; *    Name:  bin2BCD                                                         *
; *                                                                           *
; * Purpose:  This subroutine converts a 32 bit binary number to a 10 digit   *
; *           BCD number.  The input value taken from freq(0 to 3) is         *
; *           preserved.  The output is in BCD(0 to 4), each byte holds =>    *
; *           (hi_digit,lo_digit), most significant digits are in BCD_4.      *
; *           This routine is a modified version of one described in          *
; *           MicroChip application note AN526.                               *
; *                                                                           *
; *   Input:  The frequency in Dfreq_3 ... Dfreq_0                            *
; *                                                                           *
; *  Output:  The BCD number in BCD_4 ... BCD_0                               *
; *                                                                           *
; *****************************************************************************
;
bin2BCD
        movlw   0x20              ; Set loop counter
        movwf   BCD_count         ;   to 32
        clrf    BCD_0             ; Clear output
        clrf    BCD_1             ;   "     "
        clrf    BCD_2             ;   "     "
        clrf    BCD_3             ;   "     "
        clrf    BCD_4             ;   "     "
bin_loop
        bcf     STATUS,C          ; Clear carry bit in STATUS
;
; Rotate bits in freq bytes.  Move from LS byte (Dfreq_0) to next byte (Dfreq_1).
; Likewise, move from Dfreq_1 to Dfreq_2 and from Dfreq_2 to Dfreq_3.
;
        rlf     Dfreq_0,f         ; Rotate left, 0 -> LS bit, MS bit -> Carry
        rlf     Dfreq_1,f         ; Rotate left, Carry->LS bit, MS bit->Carry
        rlf     Dfreq_2,f         ; Rotate left, Carry->LS bit, MS bit->Carry
        rlf     Dfreq_3,f         ; Rotate left, Carry->LS bit, MS bit->Carry
        btfsc   STATUS,C          ; Is Carry clear? If so, skip next instruction
        bsf     Dfreq_0,0         ; Carry is set so wrap and set bit 0 in freq_1
;
; Build BCD bytes. Move into LS bit of BCD bytes (LS of BCD_0) from MS bit of
; freq_3 via the Carry bit.  
;
        rlf     BCD_0,f           ; Rotate left, Carry->LS bit, MS bit->Carry
        rlf     BCD_1,f           ; Rotate left, Carry->LS bit, MS bit->Carry
        rlf     BCD_2,f           ; Rotate left, Carry->LS bit, MS bit->Carry
        rlf     BCD_3,f           ; Rotate left, Carry->LS bit, MS bit->Carry
        rlf     BCD_4,f           ; Rotate left, Carry->LS bit, MS bit->Carry
        decf    BCD_count,f       ; Decrement loop count
        btfss   STATUS,Z          ; Is loop count now zero?
        goto    adjust            ; No, go to adjust
        return                    ; Yes, EXIT 
; ============================================================================
adjust  ; Internal subroutine, called by bin2BCD main loop only
; 
; As BCD bytes are being built, make sure the nibbles do not grow larger than 9. 
; If a nibble gets larger than 9, increment to next higher nibble.  
; (If the LS nibble of a byte overflows, increment the MS nibble of that byte.)
; (If the MS nibble of a byte overflows, increment the LS nibble of next byte.)
;
        movlw   BCD_0             ; Get pointer to BCD_0
        movwf   FSR               ; Put pointer in FSR for indirect addressing
        bcf     STATUS,IRP        ; Point to banks 1 or 2
        call    adj_BCD           ; 
        decf    FSR,f             ; Move indirect addressing pointer to BCD_1
        call    adj_BCD           ; 
        decf    FSR,f             ; Move indirect addressing pointer to BCD_2
        call    adj_BCD           ; 
        decf    FSR,f             ; Move indirect addressing pointer to BCD_3
        call    adj_BCD           ; 
        decf    FSR,f             ; Move indirect addressing pointer to BCD_4
        call    adj_BCD           ; 
        goto    bin_loop          ; Back to main loop of bin2BCD
; ============================================================================
adj_BCD  ; Internal subroutine, called by adjust only
        movlw   3                 ; Add 3
        addwf   INDF,w            ;   to LS digit
        movwf   BCD_temp          ; Save in temp
        btfsc   BCD_temp,3        ; Is LS digit + 3 > 7  (Bit 3 set)
        movwf   INDF              ; Yes, save incremented value as LS digit
        movlw   0x30              ; Add 3
        addwf   INDF,w            ;   to MS digit
        movwf   BCD_temp          ; Save as temp
        btfsc   BCD_temp,7        ; Is MS digit + 3 > 7  (Bit 7 set)
        movwf   INDF              ; Yes, save incremented value as MS digit
        return                    ; Return to adjust subroutine
;
; *****************************************************************************
; *    Name:  show_freq                                                       *
; *                                                                           *
; * Purpose:  Display the frequency setting on the LCD.                       *
; *           2x16 LCD display used so display freq kHz - e.g  14,025.000 kHz *
; *                                                                           *
; *   Input:  The values in BCD_4 ... BCD_0                                   *
; *                                                                           *
; *  Output:  The number displayed on the LCD                                 *
; *                                                                           *
; *  NOTE: When using a 16x2 LCD                                              *
; *    Line 1 addresses are  0x00 to 0x0F   (0x80 to 0x8F)                    *
; *    Line 2 addresses are  0x40 to 0x4F   (0xC0 to 0xCF)                    *
; *                                                                           *
; *****************************************************************************
;
show_freq
        movlw   0x80              ; Point the LCD to first LCD digit location  
        call    cmnd2LCD          ; Send starting digit location to LCD
        movlw   ' '               ; Send a space
        call    data2LCD          ;   to position 1 of LCD
;
; Extract and send "YYYY" from byte containing "XXXXYYYY"
;   - Mask with 0x0F to get 0000YYYY
;   - Add offset for ASCII character set 
;
        clrf    temp4             ;
        movf    BCD_4,w           ; Put 1MHz BCD digit into lower nibble of W
        andlw   0x0F              ; Mask for lower nibble only       (0000YYYY)
        btfss   STATUS,Z          ; Is this digit a zero?
        goto    digit_non_zero_4  ; No, go and construct the non-zero digit
        movlw   ' '               ; Yes, pick up a ASCII space character instead
        goto    send_it_4           ;   and go send it
digit_non_zero_4 
        addlw   0x30              ; Add offset for ASCII char set (0030XXXX)
        bsf     temp4,0           ; Flag for upper digit existing
send_it_4
        call    data2LCD          ; Send byte in W to LCD

; Extract and send "XXXX" from byte containing "XXXXYYYY"
;  - Swap halves to get YYYYXXXX
;  - Mask with 0x0F to get 0000XXXX
;  - Add ASCII bias 
;
        swapf   BCD_3,w           ; Swap 10MHz BCD digit into lower nibble of W
        andlw   0x0F              ; Mask for lower nibble only       (0000XXXX)
        btfss   STATUS,Z          ; Is this digit a zero?
        goto    digit_non_zero_3  ; No, go and construct the non-zero digit
        btfsc   temp4,0           ; Is indicator non-zero (ie, upper digit exists)
        goto    digit_non_zero_3  ; Yes, don't suppress this one 
        movlw   ' '               ; Yes, pick up a ASCII space character instead
        goto    send_it_3           ;   and go send it
digit_non_zero_3 
        addlw   0x30              ; Add offset for ASCII char set (0030XXXX)
send_it_3
        call    data2LCD          ; Send byte in W to LCD
;
; Extract and send "YYYY" from byte containing "XXXXYYYY"
;   - Mask with 0x0F to get 0000YYYY
;   - Add offset for ASCII character set 
;
        movf    BCD_3,w           ; Put 1MHz BCD digit into lower nibble of W
        andlw   0x0F              ; Mask for lower nibble only       (0000YYYY)
        addlw   0x30              ; Add offset for ASCII char set (0030XXXX)
        call    data2LCD          ; Send byte in W to LCD
; 
        movlw   ','               ; Get a comma
        call    data2LCD          ; Send byte in W to LCD
;
        swapf   BCD_2,w           ; Swap 100KHz BCD digit into lower nibble of W
        andlw   0x0F              ; Mask for lower nibble only       (0000XXXX)
        addlw   0x30              ; Add offset for ASCII char set    (0030XXXX)
        call    data2LCD          ; Send byte in W to LCD
;
        movf    BCD_2,w           ; Put 10KHz BCD digit into lower nibble of W
        andlw   0x0F              ; Mask for lower nibble only       (0000YYYY)
        addlw   0x30              ; Add offset for ASCII char set    (0030YYYY)
        call    data2LCD          ; Send byte in W to LCD
;
        swapf   BCD_1,w           ; Swap 1KHz BCD digit into lower nibble of W
        andlw   0x0F              ; Mask for lower nibble only       (0000XXXX)
        addlw   0x30              ; Add offset for ASCII char set    (0030XXXX)
        call    data2LCD          ; Send byte in W to LCD
;
        movlw   '.'               ; Get a period
        call    data2LCD          ; Send byte in W to LCD
        movlw   0x89              ; Point to LCD digit number nine
        call    cmnd2LCD          ; Send command byte in W to LCD
;
        movf    BCD_1,w           ; Put 100 Hz BCD digit into lower nibble of W
        andlw   0x0F              ; Mask for lower nibble only       (0000YYYY)
        addlw   0x30              ; Add offset for ASCII char set    (0030YYYY)
        call    data2LCD          ; Send data byte in W to LCD
;
        swapf   BCD_0,w           ; Swap 10 Hz BCD digit into lower nibble of W
        andlw   0x0F              ; Mask for lower nibble only       (0000XXXX)
        addlw   0x30              ; Add offset for ASCII char set    (0030XXXX)
        call    data2LCD          ; Send data byte in W to LCD
;
        movf    BCD_0,w           ; Put 1 Hz BCD digit into lower nibble of W
        andlw   0x0F              ; Mask for lower nibble only       (0000YYYY)
        addlw   0x30              ; Add offset for ASCII char set    (0030YYYY)
        call    data2LCD          ; Send byte in W to LCD
;
        movlw   ' '               ; Send a space
        call    data2LCD          ;   to position 12 of LCD
;
        movlw   'k'               ; Send a 'k'
        call    data2LCD          ;   to position 13 of LCD
;
        movlw   'H'               ; Send an "H"
        call    data2LCD          ;   to position 14 of LCD
;
        movlw   'z'               ; Send a 'z'
        call    data2LCD          ;   to position 15 of LCD
;
        movlw   ' '               ; Send a space
        call    data2LCD          ;   to position 16 of LCD

        pagesel DisplayMode       ; Go to Page 1
        call    DisplayMode       ; Display the mode
        pagesel show_freq         ; Back to Page 0

        call    ShowActiveDigit ; 

        return                    ; 

; *****************************************************************************
; *    Name:  ShowActiveDigit                                                 *
; *                                                                           *
; * Purpose:  Display the frequency digit that is currently being updated     *
; *           by turning the encoder                                          *
; *                                                                           *
; *   Input:  tuning_digit                                                    *
; *                                                                           *
; * On exit:  Digit currently being updated by encoder is underlined on LCD   *
; *                                                                           *
; *****************************************************************************
ShowActiveDigit
         
         movf   tuning_digit,w    ; Move left by x digits
         call   CursorUnderlineDigit ; Move underline digit into w
         call   cmnd2LCD          ;
         return
;
; *****************************************************************************
; *    Name:  busy_check                                                      *
; *                                                                           *
; * Purpose:  Check if LCD is done with the last operation.                   *
; *           This subroutine polls the LCD busy flag to determine if         *
; *           previous operations are completed.                              *
; *                                                                           *
; *   Input:  None                                                            *
; *                                                                           *
; * On exit:  PORTB set as:  RB3          input                               *
; *                          all others   outputs                             *
; *****************************************************************************
; 
;busy_check
;        clrf    PORTB             ; Clear all outputs on PORTB
;        banksel TRISB             ; Change to bank 1 for Tristate operation
;        movlw   b'10001100'       ; Set RB7,RB2,RB3 input, others outputs              
;        movwf   TRISB             ;   via Tristate
;        banksel PORTB             ; Change back to bank 0
;        bcf     PORTB,LCD_rs      ; Set up LCD for Read Busy Flag (RS = 0) 
;        bsf     PORTB,LCD_rw      ; Set up LCD for Read (RW = 1)  
;        movlw   0xFF              ; Set up constant 255
;        movwf   timer1            ;   for timer loop counter
;LCD_is_busy
;        bsf     PORTB,LCD_e       ; Set E high
;        movf    PORTB,w           ; Read PORTB into W
;        movwf   LCD_read          ; Save W for later testing
;        bcf     PORTB,LCD_e       ; Drop E again
;        nop                       ; Wait a
;        nop                       ;   while
;        bsf     PORTB,LCD_e       ; Pulse E high (dummy read of lower nibble),
;        nop                       ;   wait,
;        bcf     PORTB,LCD_e       ;   and drop E again
;        decf    timer1,f          ; Decrement loop counter
;        btfsc   STATUS,Z          ; Is loop counter down to zero?
;        goto    not_busy          ; If yes, return regardless
;        btfsc   LCD_read,LCD_busy ; Busy Flag (RB3) in save byte clear?        
;        goto    LCD_is_busy       ; If not, it is busy so jump back
;not_busy
;        return                    ; 
;
; *****************************************************************************
; *    Name:  cmnd2LCD                                                        *
; *           data2LCD                                                        *
; *                                                                           *
; * Purpose:  Send Command or Data byte to the LCD                            *
; *           Entry point cmnd2LCD:  Send a Command to the LCD                *
; *           Entry Point data2LCD:  Send a Data byte to the LCD              *
; *                                                                           *
; *   Input:  W has the command or data byte to be sent to the LCD.           *
; *                                                                           *
; *  Output:  None                                                            *
; *****************************************************************************
;
cmnd2LCD   ; ****** Entry point ******
        movwf   LCD_char          ; Save byte to write to LCD
        clrf    rs_value          ; Remember to clear RS  (clear rs_value)   
        bcf     PORTB,LCD_rs      ; Set RS for Command to LCD
        goto    write2LCD         ; Go to common code
data2LCD   ; ****** Entry point ********
        movwf   LCD_char          ; Save byte to write to LCD
        bsf     rs_value,0        ; Remember to set RS (set bit 0 of rs_value)
        bsf     PORTB,LCD_rs      ; Set RS for Data to LCD
write2LCD
        ; Begin Busy Check
        clrf    PORTB             ; Clear all outputs on PORTB
        banksel TRISB             ; Change to bank 1 for Tristate operation
        movlw   b'10001100'       ; Set RB7,RB2,RB3 input, others outputs              
        movwf   TRISB             ;   via Tristate
        banksel PORTB             ; Change back to bank 0
        bcf     PORTB,LCD_rs      ; Set up LCD for Read Busy Flag (RS = 0) 
        bsf     PORTB,LCD_rw      ; Set up LCD for Read (RW = 1)  
        movlw   0xFF              ; Set up constant 255
        movwf   timer1            ;   for timer loop counter
LCD_is_busy
        bsf     PORTB,LCD_e       ; Set E high
        movf    PORTB,w           ; Read PORTB into W
        movwf   LCD_read          ; Save W for later testing
        bcf     PORTB,LCD_e       ; Drop E again
        nop                       ; Wait a
        nop                       ;   while
        bsf     PORTB,LCD_e       ; Pulse E high (dummy read of lower nibble),
        nop                       ;   wait,
        bcf     PORTB,LCD_e       ;   and drop E again
        decf    timer1,f          ; Decrement loop counter
        btfsc   STATUS,Z          ; Is loop counter down to zero?
        goto    not_busy          ; If yes, return regardless
        btfsc   LCD_read,LCD_busy ; Busy Flag (RB3) in save byte clear?        
        goto    LCD_is_busy       ; If not, it is busy so jump back
not_busy
        clrf    PORTB             ; Clear all of Port B (inputs and outputs)
        banksel TRISB             ; Change to bank 1 for Tristate operation
        movlw   b'10000000'       ; Set up to enable PORTB data pins
        movwf   TRISB             ; All pins (RB7..RB0) are back to outputs
        banksel PORTB             ; Change back to bank 0
        bcf     PORTB,LCD_rw      ; Set LCD back to Write mode  (RW = 0)
        bcf     PORTB,LCD_rs      ; Guess RS should be clear              
        btfsc   rs_value,0        ; Should RS be clear?  (is bit 0 == 0?) 
        bsf     PORTB,LCD_rs      ; No, set RS                            
;
; Transfer Most Significant nibble  (XXXX portion of XXXXYYYY)
;

        movlw   0xF0              ; Set up mask                                        
        andwf   PORTB,f           ; Keep RB7..RB4 but clear old RB3..RB0          
        swapf   LCD_char,w        ; Put byte into W (reverse nibbles)          
        andlw   0x0F              ; Mask to give 0000XXXX in W                 
        iorwf   PORTB,f           ; To RB3..RB0 with RB7..RB4 unchanged        
        bsf     PORTB,LCD_e       ; Pulse the E line high,
        nop                       ;   wait, 
        bcf     PORTB,LCD_e       ;   and drop it again
;
; Transfer Least Significant nibble  (YYYY portion of XXXXYYYY)
;
        movlw   0xF0              ; Set up mask                                        
        andwf   PORTB,f           ; Clear old RB3..RB0                         
        movf    LCD_char,w        ; Move LS nibble of into W                   
        andlw   0x0F              ; Mask to give 0000YYYY in W                 
        iorwf   PORTB,f           ; To RB3..RB0 with RB7..RB4 unchanged        
        bsf     PORTB,LCD_e       ; Pulse the E line high,
        nop                       ;   wait, 
        bcf     PORTB,LCD_e       ;   and drop it again
        return
;
; *****************************************************************************
; *    Name:  write_EEPROM                                                    *
; *                                                                           *
; * Purpose:  Write the byte of data at EEDATA to the EEPROM at address       *
; *           EEADR.                                                          *
; *                                                                           *
; *   Input:  The values at EEDATA and EEADR.                                 *
; *                                                                           *
; *  Output:  The EEPROM value is updated.                                    *
; *                                                                           *
; *  NOTE:  ALL IN EEDATA BANK  (1 for 16F628, 2 for 16F88)                   *  
; *****************************************************************************
;
write_EEPROM
        banksel EECON1            ; Bank of EECON1
        bcf     EECON1,EEPGD      ; Point to DATA memory              BANK 3  
        bsf     EECON1,WREN       ; Set the EEPROM write enable bit   BANK 3 

        ; Start required sequence
        
        bcf     INTCON,GIE        ; Disable interrupts                Any Bank
        movlw   0x55              ; Write 0x55 and 0xAA to EECON2     
        movwf   EECON2            ;   control register, as required
        movlw   0xAA              ;   
        movwf   EECON2            ;
        bsf     EECON1,WR         ; Set WR bit to begin write 
        ; End required sequence

bit_check
        btfsc   EECON1,WR         ; Has the write completed?
        goto    bit_check         ; No, keep checking
        bsf     INTCON,GIE        ; Enable  interrupts
        bcf     EECON1,WREN       ; Clear the EEPROM write enable bit

        banksel EEADR
        incf    EEADR,f           ; Increment the EE write address
        return                    ; Return to the caller


;***************************************************************************
;*  Mult32x8    
;*  Multiply 32-bit x 8-bit = 40-bit (unsigned)
;*
;*  Factor1:   RegA3:RegA0 
;*  Factor2:   RegB0
;*  Product:   RegA4:RegA0
;***************************************************************************
;
Mult32x8
        clrf    RegC4             ; Clear the product
        clrf    RegC3             ;
        clrf    RegC2             ;
        clrf    RegC1             ;
        clrf    RegC0             ;                                              
        clrf    RegA4             ; Clear the top byte of Factor1                    
mult32x8loop:
        movf    RegB0,W           ; Check to see if Factor2 is empty
        btfsc   STATUS,Z          ; Done if zero
        goto    mult32x8done

        bcf     STATUS,C          ; clear carry
        rrf     RegB0,F           ; Get the next bit

        btfss   STATUS,C          ; Add if carry is set
        goto    mult32x8shift     ; shift only if 0

        movf    RegA0,W           ; Add the 32 bits of Factor1 to product
        addwf   RegC0,F           ; each addwf sets the carry
        btfsc   STATUS,C          ; Don't increment if no carry
        call    mult32x8carry1    ; Propigate carry to next byte(s)

        movf    RegA1,W           ;
        addwf   RegC1,F           ;
        btfsc   STATUS,C          ; Don't increment if no carry
        call    mult32x8carry2    ; Propigate carry to next byte(s)
                                                                            
        movf    RegA2,W           ;                                         
        addwf   RegC2,F     ;                                         
        btfsc   STATUS,C          ; Don't increment if no carry             
        call    mult32x8carry3    ; Propigate carry to next byte(s)         

        movf    RegA3,W           ;                                         
        addwf   RegC3,F           ;                                         
        btfsc   STATUS,C          ; Don't increment if no carry              
        incf    RegC4,F           ; Add one to next byte if carry occured
                                
        movf    RegA4,W           ;                                         
        addwf   RegC4,F           ;                                               
                           
mult32x8shift:
        bcf     STATUS,C          ; clear carry
        rlf     RegA0,F           ; Shift the 24 bits one bit to the left
        rlf     RegA1,F           ;
        rlf     RegA2,F           ;
        rlf     RegA3,F           ;
        rlf     RegA4,F           ;                                             
        goto    mult32x8loop
; ===== subroutines required by multiply above ======
mult32x8carry1:
        incfsz  RegC1,F           ;
        return            
mult32x8carry2:                   ;
        incfsz  RegC2,F           ;
        return                    ;
mult32x8carry3:                   ;                     
        incfsz  RegC3,F           ;                    
        return                    ;                    
        incf    RegC4,F           ;                       
        return
; ==============================================
mult32x8done
        return
        

;***************************************************************************
;*  Mult8x8    
;*  Multiply 8-bit x 8-bit = 16-bit (unsigned)
;*
;*  multiplier:   RegB0       
;*  multiplicand: RegA0
;*  result:       RegA3:RegA2
;***************************************************************************
;
Mult8x8  
        clrf    RegA3             ; Clear the product regs
        clrf    RegA2
        clrf    RegA1             ; Clear working register
mult8x8loop:
        movf    RegB0,W           ; Check to see if multiplier bit is empty
        btfsc   STATUS,Z          ; Done if zero
        goto    mult8x8exit       ;

        bcf     STATUS,C          ; clear carry
        rrf     RegB0,F           ; Get the next bit

        btfss   STATUS,C          ; Add if carry is set
        goto    mult8x8shift      ; shift only if 0

        movf    RegA0,W           ; Add the 8 bits of multiplicand to product
        addwf   RegA2,F           ; each addwf sets the carry
        btfsc   STATUS,C          ; Don't increment if no carry
        incf    RegA3,F

        movf    RegA1,W           ; 
        addwf   RegA3,F           ;

mult8x8shift:
        bcf     STATUS,C          ; clear carry
        rlf     RegA0,F           ; Shift the 16 bits one bit to the left
        rlf     RegA1,F           ;                    
        goto    mult8x8loop

mult8x8exit
        return
;
; *****************************************************************************
; *    Name:  calcHSN1Entry                                                   *
; *          (Done at Calibration time to create values in EEPROM)            *
; *  Purpose:                                                                 *
; *                                                                           *
; *  Adapted and extended from:                                               *
; *    SIGNED 32-BIT INTEGER MATHS ROUTINES FOR pic16 SERIES BY PETER HEMSLEY *
; *                                                                           *
; * Purpose:  Divide Fxtal by HSDIVN1 to create FxFactor                      *
; * The final RFREQ value will be created by dividing Fout by FxFactor.       *
; *                                                                           *
; * Get HSDIVN1/2 from band table in RAM.  This table is recalculated during  *
; * calibration using constants in EEPROM.                                    *
; *                                                                           *
; * Note: Actually divide FXTAL by HSDIVN1/2 since HSDIVN1/2 always fits      *
; *       into one byte and no loss of accuracy since HSDIV is always an      *
; *       even number.  (Two bytes will be used for the divisor because the   *
; *       higher register is needed for the mechanics of the division -       *
; *       shifting left before subtracting - but the higher byte will always  *
; *       be zero when starting the division.                                 *
; *                                                                           *
; * Use 40x16-bit signed divide                                               *
; * Divide RegA by RegB and return results in RegA                            *
; * RegC is used for the remainder in the calculation                         *
; *                                                                           *
; * Example: RegA set up with (06 CF 0A B0 60) << Fxtal*256 (114,231,984*256) * <<<< FIXED FOR 10MHz Si570
; *                                                 (example calibrated Fxtal)* 
; *          RegB set up with (00 BB)        <<< HSDIVN1/2 for band 3         * <<<< OK for Si570
; *          When division is complete, quotient is in RegA                   *
; *   06 CF 0A B0 60 / 00 BB  = 09 52 32 3A                                   * <<<< FIXED FOR 10MHz Si570
; *   (Then divide quotient by 2 because HSDIVN1/2 was used in denominator)   *
; *   09 52 32 3A / 2 =  04 A9 19 1D   <<< This is FxFactor                   * <<<< FIXED FOR 10MHz Si570
; *                                                                           *
; * Input   Fxtalx256_4..Fxtalx256_0 and CurHsdivN1D2                         *
; *                                                                           *
; * Output: FxFactor3..0                                                      *
; *                                                                           *
; * Uses:   temp1                                                             *
; *****************************************************************************
;
calcHSN1Entry  
        movf    Fxtalx256_4,w     ;                                                  
        movwf   RegA4             ; MSB                    
        movf    Fxtalx256_3,w     ;                                                
        movwf   RegA3             ; MSB
        movf    Fxtalx256_2,w     ;
        movwf   RegA2             ;
        movf    Fxtalx256_1,w     ;
        movwf   RegA1             ;
        movf    Fxtalx256_0,w     ;
        movwf   RegA0             ; LSB
        clrf    RegB1             ; Clear MSB 
        movf    CurHsdivN1D2,w    ; HSDIVN1/2 for this frequency band
        movwf   RegB0             ;     into LSB
FxFacDivide  
        clrf    RegC0             ; Clear remainder
        clrf    RegC1
        movlw   D'40'             ; For 40 by 8 divide
        movwf   temp1
FxFacDvloop  
        rlf     RegA0,f           ; Shift dividend (REGA) msb into remainder (REGC)
        rlf     RegA1,f
        rlf     RegA2,f
        rlf     RegA3,f
        rlf     RegA4,f
        rlf     RegC0,f
        rlf     RegC1,f
        movf    RegB1,w           ; Test if remainder (REGC) >= divisor (REGB)
        subwf   RegC1,w           ;
        btfss   STATUS,Z          ; 
        goto    FxFacDtstgt       ; Byte1 <> Byte1 so jump
        movf    RegB0,w           ; Test if remainder (REGC) >= divisor (REGB)
        subwf   RegC0,w      
FxFacDtstgt  
        btfss   STATUS,C          ; Carry set if remainder >= divisor
        goto    FxFacDremlt
        movf    RegB0,w           ;
        btfss   STATUS,C          ;
        incfsz  RegB0,w           ;
        subwf   RegC0,f           ;
        movf    RegB1,w           ;
        btfss   STATUS,C          ;
        incfsz  RegB1,w           ;
        subwf   RegC1,f           ;
        clrc
        bsf     RegA0,0           ; Set quotient bit
FxFacDremlt  
        bcf     STATUS,C
        decfsz  temp1,f           ; Next
        goto    FxFacDvloop
; Need to divide by 2 because HSDIVN1/2 was used in denominator
        bcf     STATUS,C          ;
        rrf     RegA4,f           ;
        rrf     RegA3,f           ;
        rrf     RegA2,f           ;
        rrf     RegA1,f           ;
        rrf     RegA0,f           ;
        movf    RegA3,w           ; Result is always contained in 32 bits      
        movwf   FxFactor3         ;
        movf    RegA2,w           ;
        movwf   FxFactor2         ;
        movf    RegA1,w           ;
        movwf   FxFactor1         ;
        movf    RegA0,w           ;
        movwf   FxFactor0         ;
        return
;
; *****************************************************************************
; *    Name:  SetUpFxtal      (Called from Calibrate only)                    *
; *                                                                           *
; * Purpose:  Retrieve Si570 registers use values to calculate default Fxtal  *
; *                                                                           *
; *                                                                           *
; *  Input: None                                                              *
; *                                                                           *
; * Output: Fxtal                                                             *
; *   Uses: Temp1                                                             *
; *****************************************************************************
;
SetUpFxtal

        pagesel  ReadStartupConfig ; Switch to Page 1
        call     ReadStartupConfig ; Read default Fxtal for this Si570 - set up SiRegs
        pagesel  main             ; Back to Page 0

; Extract INITIAL_HSDIV-INDEX from SiReg[7]
        movlw   0xE0              ; Mask upper 3 bits
        andwf   SiReg7,w          ; Isolate HSDIV-INDEX
        movwf   CurHsdivIndex     ; Index is in upper 3 bits
        clrc                 ; 
        rrf     CurHsdivIndex,f   ; position 
        rrf     CurHsdivIndex,f   ; position 
        rrf     CurHsdivIndex,f   ; position 
        rrf     CurHsdivIndex,f   ; position 
        rrf     CurHsdivIndex,f   ; Now HSDIV-INDEX positioned in lower bits

; Convert HSDIV-INDEX to HSDIV
        movf    CurHsdivIndex,w   ; 
        call    HSDIVIndexToHSDIV ; Start of table 
        movwf   RegA0             ; Save HSDIV
        
; Extract N1               
        movlw   0x1F              ; Mask lower 5 bits
        andwf   SiReg7,w          ; 
        movwf   temp1             ; Now has N1[6:2] in temp1[4:0]
        clrc   
        rlf     SiReg8,f          ; Get upper bit into C
        rlf     temp1,f           ; Move into temp LSB
        rlf     SiReg8,w          ; Move another (this time don't modify SiReg8)
        rlf     temp1,f           ; Move into temp LSB,  Now temp1 has N1[7:0]
        rrf     SiReg8,f          ; Reposition back to original
        incf    temp1,w           ; Now w has N1 instead of (N1-1)

; Multiply N1 by HSDIV to get HSDIVN1 
        movwf   RegB0             ; N1 into working register for multiply
        call    Mult8x8           ; Multiply RegB0 by RegA0, Result in RegA3,RegA2
; Now divide by 2
        clrc   
        rrf     RegA3,f           ; Move LSB of upper byte into C
        rrf     RegA2,w           ; Move C into lower byte, result into w
        movwf   CurHsdivN1D2      ; Now into HSDIVN1/2

; Calculate Fxtal/HSDIVN1 by dividing Fout(default) by RFREQ(extracted)

; Set up numerator in RegA4..RegA0 with default Fout for this type of Si570
       clrf     RegA4             ; MSB
       movlw    DEFAULTSI570FREQ3 ;
       movwf    RegA3
       movlw    DEFAULTSI570FREQ2  
       movwf    RegA2
       movlw    DEFAULTSI570FREQ1  
       movwf    RegA1
       movlw    DEFAULTSI570FREQ0  
       movwf    RegA0             ; LSB
;
; Convert to Fout to (Fout*16) by left shift 4 times             Version 4.5
       bcf      STATUS,C
       rlf      RegA0,f
       rlf      RegA1,f
       rlf      RegA2,f
       rlf      RegA3,f
       rlf      RegA4,f
       bcf      STATUS,C
       rlf      RegA0,f
       rlf      RegA1,f
       rlf      RegA2,f
       rlf      RegA3,f
       rlf      RegA4,f
       bcf      STATUS,C
       rlf      RegA0,f
       rlf      RegA1,f
       rlf      RegA2,f
       rlf      RegA3,f
       rlf      RegA4,f
       bcf      STATUS,C
       rlf      RegA0,f
       rlf      RegA1,f
       rlf      RegA2,f
       rlf      RegA3,f
       rlf      RegA4,f

; Extract RFREQ (38-bit) from SiReg[8:12]
;   and set up as denominator
       movlw    0x3F              ; Mask RFREQ bits 
       andwf    SiReg8,w          ; RFREQ[37:32] is in Reg8[5:0]
       movwf    RegB4             ; Set up MSB for divide
       movf     SiReg9,w
       movwf    RegB3
       movf     SiReg10,w
       movwf    RegB2
       movf     SiReg11,w
       movwf    RegB1
       movf     SiReg12,w
       movwf    RegB0             ; LSB of RFREQ now set up for divide

; Now divide by RFREQ AND MULTIPLY BY 256
;   Divide 40-bit RegA4..RegA0 by 40-bit (RegB4..RegB0)  AND MULTIPLY BY 256
; ============================================================================
; Example: Start with (Fout * 16) in RegA4..RegA0                                             
;            Fout = 10,000,000 so Fout*16 = 0x09896800                            << FIXED FOR 10 MHz Si570
;                 (RegA4..RegA0 = 00 09 89 68 00)                                 << FIXED FOR 10 MHz Si570
;          Divide by RFREQ-extracted in RegB4..RegB0
;            RFREQ * 2^28 = 0x02A050E9FD                                          << FIXED FOR 10 MHz Si570
;                 (RegB4..RegB0 = 02 A0 50 E9 FD)                                 << FIXED FOR 10 MHz Si570
;
;            Since RFREQ is already multiplied by 2^28, divide routine 
;              multiplies numerator by 2^28 also. 
;
;          RESULT: This divide routine produces 0x3A19F4D                         << FIXED FOR 10 MHz Si570
;                 (Note: uses (Fout * 16) to keep extra significant figure)
;                  so this result gives (Fxtal*256) / (HSDIVN1) 
; 
;    Division Result is in RegA4:RegA0 = 0x00 03 A1 9F 4D (This is now x256)      << FIXED FOR 10 MHz Si570
;    Nultiply this by HSDIVN1 and divide by 256 to give Fxtal
; ============================================================================

CalDivide  
        clrf    RegC0             ; Clear remainder
        clrf    RegC1
        clrf    RegC2
        clrf    RegC3
        clrf    RegC4
        movlw   D'72'             ; Loop counter increment because sign-shift removed
        movwf   temp1
CalDvloop  
        rlf     RegA0,f           ; Shift dividend (REGA) msb into remainder (REGC)
        rlf     RegA1,f
        rlf     RegA2,f
        rlf     RegA3,f
        rlf     RegA4,f
        rlf     RegC0,f
        rlf     RegC1,f
        rlf     RegC2,f
        rlf     RegC3,f
        rlf     RegC4,f
        movf    RegB4,w           ; Test if remainder (REGC) >= divisor (REGB)
        subwf   RegC4,w           ;
        BTFSS   STATUS,Z          ; 
        goto    CalDtstgt         ; Byte4 <> Byte4 so jump
        movf    RegB3,w           ; (Byte4=Byte4) so
        subwf   RegC3,w           ;    look at Byte3
        BTFSS   STATUS,Z          ; 
        goto    CalDtstgt         ; Byte3 <> Byte3 so jump
        movf    RegB2,w           ; (Byte4=Byte4) and (Byte3=Byte3) so 
        subwf   RegC2,w           ;   look at Byte2
        BTFSS   STATUS,Z          ;
        goto    CalDtstgt         ;  Byte2 <> Byte2 so jump
        movf    RegB1,w           ; (Byte4=Byte4) and (Byte3=Byte3) and (Byte2=Byte2) so 
        subwf   RegC1,w           ;   look at Byte1
        BTFSS   STATUS,Z          ;
        goto    CalDtstgt         ; Byte1 <> Byte1 so jump 
        movf    RegB0,w           ; (Byte4=Byte4) and (Byte3=Byte3) and (Byte2=Byte2) and (Byte1=Byte1) so
        subwf   RegC0,w           ;   look at Byte0
CalDtstgt                       
        BTFSS   STATUS,C          ; Carry set if remainder >= divisor
        goto    CalDremlt         ; Divisor byte greater than remainder so quit 
        movf    RegB0,w           ; Subtract divisor (REGB) from remainder (REGC)
        subwf   RegC0,f           ;
        movf    RegB1,w           ;
        BTFSS   STATUS,C          ;
        incfsz  RegB1,w           ;                     
        subwf   RegC1,f           ;
        movf    RegB2,w           ;
        BTFSS   STATUS,C          ;
        incfsz  RegB2,w           ;
        subwf   RegC2,f           ;
        movf    RegB3,w           ;
        BTFSS   STATUS,C          ;
        incfsz  RegB3,w           ;
        subwf   RegC3,f           ;
        movf    RegB4,w           ;
        BTFSS   STATUS,C          ;
        incfsz  RegB4,w           ;
        subwf   RegC4,f           ;
        clrc
        bsf     RegA0,0           ; Set quotient bit
CalDremlt  
        bcf     STATUS,C
        decfsz  temp1,f           ; Next
        goto    CalDvloop
;
; End of division
; EXAMPLE:   RegA4:RegA0 = 0x00 03 A1 9F 4D   (This is now x256)                          << FIXED FOR 10 MHz Si570
; =================================

; Calculate Fxtal*256 by multiplying result above by HSDIVN1 for given N1 and HSDIV 
;   ((Fxtal*256)/HSDIVN1-extracted) = (Fout-default * 256) / Rfreq-extracted
;
; EXAMPLE:  Fout*256/RFREQ = 0x00 03 A1 9F 4D                                             << FIXED FOR 10 MHz Si570
;           HSDIV(extracted) = 6, N1(extracted)= 80  so HSDIVN1/2 = 240 = 0xF0            << FIXED FOR 10 MHz Si570
;     NOTE: Uses HSDIVN1/2 in denominator so need to compensate by multiplying 
;             result by 2 at the end. 
;
; Mult routine has been tailored to do 32 bits by 8 bits. 
; HSDIVN1/2 is always 8 bits 
;
        movf   CurHsdivN1D2,w    ; (Don't disturb 
        movwf  RegB0             ;   original)

        call     Mult32x8         ; New mult RegA3:RegA0 by RegB0 -> result in RegC4..RegC0

; MOVE RegC4:RegC0 to Fxtalx256_4:Fxtalx256_0
        movf   RegC4,w
        movwf  Fxtalx256_4
        movf   RegC3,w
        movwf  Fxtalx256_3
        movf   RegC2,w
        movwf  Fxtalx256_2
        movf   RegC1,w
        movwf  Fxtalx256_1
        movf   RegC0,w
        movwf  Fxtalx256_0

; Now multiply result by 2 with one left shift since we multiplied by HSN1DIV/2
        bcf    STATUS,C           ;         
        rlf    Fxtalx256_0,f      ; LSB
        rlf    Fxtalx256_1,f      ;
        rlf    Fxtalx256_2,f      ;
        rlf    Fxtalx256_3,f      ;
        rlf    Fxtalx256_4,f      ; MSB

; Now have Fxtalx256 set up             
; EXAMPLE:  Fxtalx256 = 0x06 CF 0A B0 60                                     << FIXED FOR 10 MHz Si570
;           0x06CF0AB060 = 29243388000;   10009982720/256 = 114,231,984      << FIXED FOR 10 MHz Si570
;
        return
;
; *****************************************************************************
DebugPulse
        bsf     PORTB,7           ; DEBUG - Sync Pulse on RB7
        nop
        nop
        bcf     PORTB,7           ; DEBUG - End of pulse
        nop
        nop
        return
;
; *****************************************************************************
; *    Name:  read_EEPROM                                                     *
; *                                                                           *
; * Purpose:  Read a byte of EEPROM data at address EEADR into EEDATA.        *
; *                                                                           *
; *   Input:  The address EEADR.                                              *
; *                                                                           *
; *  Output:  The value in EEDATA.                                            *
; *                                                                           *
; *  NOTE:    Enter with any bank, leave with BANK 2 set up                   *
; *****************************************************************************
;
read_EEPROM
        banksel EECON1            ;                            Bank 3 for 16F88
        bcf     EECON1,EEPGD      ; Point to Data memory        (New for 16F88)
        bsf     EECON1,RD         ; Request the read
        banksel EEDATA            ;                            Bank 2 for 16F88
        movf    EEDATA,W          ; Get the data
        incf    EEADR,f           ; Increment the read address
        return                    ; Return to the caller
;
; *****************************************************************************
; *    Name:  wait_a_sec                                                      *
; *           wait_256ms                                                      *
; *           etc                                                             *
; *                                                                           *
; * Purpose:  Wait for a specified number of milliseconds.                    *
; *                                                                           *
; *           Entry point wait_a_sec:  Wait for 1 second                      *
; *           Entry point wait_256ms:  Wait for 256 msec                      *
; *           Entry point wait_128ms:  Wait for 128 msec                      *
; *           Entry point wait_64ms :  Wait for 64 msec                       *
; *           Entry point wait_32ms :  Wait for 32 msec                       *
; *           Entry point wait_16ms :  Wait for 16 msec                       *
; *           Entry point wait_8ms  :  Wait for 8 msec                        *
; *           Entry point wait_8ms  :  Wait for 4 msec                        *
; *           Entry point wait_8ms  :  Wait for 2 msec                        *
; *           Entry point wait_8ms  :  Wait for 1 msec                        *
; *                                                                           *
; *   Input:  None                                                            *
; *                                                                           *
; *  Output:  None                                                            *
; *                                                                           *
; *****************************************************************************
;
wait_a_sec  ; ****** Entry point ******    
        call    wait_256ms        ;       
        call    wait_256ms        ;       
        call    wait_256ms        ;       
        call    wait_256ms        ;       
        return
wait_256ms  ; ****** Entry point ******    
        call    wait_128ms        ;
        call    wait_128ms        ;
        return
wait_128ms  ; ****** Entry point ******    
        call    wait_64ms         ;
        call    wait_64ms         ;
        return
wait_64ms  ; ****** Entry point ******    
        movlw   0xFF              ; Set up outer loop 
        movwf   timer1            ;   counter to 255
        goto    outer_loop        ; Go to wait loops
wait_32ms  ; ****** Entry point ******     
        movlw   0x80              ; Set up outer loop
        movwf   timer1            ;   counter to 128
        goto    outer_loop        ; Go to wait loops
wait_16ms   ; ****** Entry point ******    
        movlw   0x40              ; Set up outer loop
        movwf   timer1            ;   counter to 64
        goto    outer_loop        ; Go to wait loops
wait_8ms   ; ****** Entry point ******    
        movlw   0x20              ; Set up outer loop
        movwf   timer1            ;   counter to 32  
        goto    outer_loop        ; Go to wait loops
wait_4ms   ; ****** Entry point ******     
        movlw   0x10              ; Set up outer loop
        movwf   timer1            ;   counter to 16
                                  ; Fall through into wait loops
;
; Wait loops used by other wait routines
;  - .5 microsecond per instruction (with a 8 MHz internal oscillator)
;  - 510 instructions per inner loop
;  - (Timer1 * 514) instructions (.257 msec) per outer loop
;  - Round off to .25 ms per outer loop
;
outer_loop                        
        movlw   0xFF              ; Set up inner loop counter
        movwf   timer2            ;   to 255
inner_loop
        decfsz  timer2,f          ; Decrement inner loop counter
        goto    inner_loop        ; If inner loop counter not down to zero, 
                                  ;   then go back to inner loop again
        decfsz  timer1,f          ; Yes, Decrement outer loop counter
        goto    outer_loop        ; If outer loop counter not down to zero,
                                  ;   then go back to outer loop again
        return                    ; Yes, return to caller


; PAGE 0 MAX 0x7FF

;*****************************************************************************
;*****************************************************************************
; >>>>>>>>>>>>>>>>>>>>>>>>>>>>> PAGE 1   <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
;*****************************************************************************
;*****************************************************************************
;                           ----------------------------------------------

         ORG     0x0800     ; NOTE!!!  NOW SWITCHING TO PAGE 1    NOTE!!!!   
;                           ----------------------------------------------
;*****************************************************************************
;*****************************************************************************
; >>>>>>>>>>>>>>>>>>>> INTERRUPT HANDLER <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
;*****************************************************************************
;*****************************************************************************
; ****************************************************************************
; *    Name: interrupt_handler_main                                          *
; *                                                                          *
; * Purpose: This interrupt handler gets called on each timer interrupt.     *
; *          It sends any encoder steps to the Driver PIC by sending a       *
; *          message to the Driver PIC.  The message contains the            *
; *          tick_count and the direction.                                   *
; *                                                                          *
; *          Note that this interrupt_handler_main routine is called from    *
; *          the interrupt_handler routine starting at location 0x005. The   *
; *          first part HAS to be there; we cannot simply put a GOTO in the  *
; *          interrupt vector. (Reason is explained there.)                  * 
; *                                                                          *
; *   Input: None                                                            *
; *                                                                          *
; *  Output: None  (encoder updates sent to Driver PIC)                      *
; ****************************************************************************
;
interrupt_handler_main
        movlw   0x00              ; Clear 
        movwf   PIR1              ;  timer 1 interrupt flag
;
; Check the tick_count.  If non-zero, send a message
;
        movf    tick_count,f      ; Look at the tick_count
        btfsc   STATUS,Z          ; Is the tick_count zero?            
        goto    reset_timer       ; Yes, tick_count was zero, skip strobe
;
; The encoder has been turned and the direction has been determined.  
; Move the tick_count to encoder_msg bits 5 - 0.  Don't worry about tick_counter 
; being more than 6 bits (63 ticks) because the upper bits will be overwritten to
; the correct value anyway.  Bit 6 will set/cleared to indicate direction and 
; bit 7 will always be set to indicate it's an encoder message.
;
; Is a 6-bit tick_counter sufficient?  Yes.  A 128-position optical encoder 
; produces a total of 512 transitions per revolution.  Assuming we want to be able
; to use all 512 transitions when turning the encoder at a rate of 2 rev/sec, we 
; must be able to handle 1024 transitions (ticks) per second.  Since we are sending
; the tick counter over every 25 ms (40 times per second), the largest tick count
; we have to handle is 1024/40 = 25.6.  Thus, we have plenty of spare bits (6 bits)
; for design changes or for turning the encoder faster than two rev/sec.
;
;
; Use last_dir for direction.  No need to put in encoder_data

        movf    tick_count,w      ; Get tick_count set up by poll
        movwf   encoder_data      ; Set up for processing
        clrf    tick_count        ; Start over for next interval
        bsf     VFOcontrol,NEEDUPDATE ; Tell poll routine we need a frequency update
        bcf     VFOcontrol,NEEDFREEZE ; Default - no freeze needed

; Reset the timer to start another 25 ms interval.
;
reset_timer
        bsf     VFOcontrol,INTOCCURRED ; Say an interrupt occurred

        clrf    T1CON             ; Turn TIMER1 off
        movlw   TIM25MSLOW        ; Get low byte for 25 ms timer
        movwf   TMR1L             ; Set low timer byte
        movlw   TIM25MSHIGH       ; Get high byte for 25 ms timer                        
        movwf   TMR1H             ; Set high timer byte
        movlw   TIM25MSPRESCALE   ; Bits to turn on TIMER1 with 1:2 Prescale
        movwf   T1CON             ; Turn the timer on

        return                    ; Return and restore registers
;      
; *****************************************************************************
; *    Name:  ReadStartupConfig               >>>>> PAGE 1 <<<<<              *
; *                                                                           *
; * Purpose:  Read the 6 Si570 registers via I2C. (Not 135 and 137)           *
; *                                                                           *
; *  Input: None                                                              *
; *                                                                           *
; * Output: SiReg[7..12] set up with extracted RFREQ bytes                    *
; *****************************************************************************
; * Example:   SiReg[7..12] = 0x53 C2 A0 50 E9 FD                             * << FIXED for 10 MHz Si570
; *****************************************************************************
ReadStartupConfig   
; 
; First set bit 0 (RECALL) in Si570 register 135.  This recalls NVM bits into Si570 RAM
; 
        movlw   0x87              ; Register 135 - Reset / Memory control register in Si570
        movwf   I2Ceadd           ; Set address for first byte to read 
        call    I2Copenw          ; Open for Write (signal start, send 0xAA, re-send until ACK)
                                  ; Then send address byte in eadd

        movlw   0x01              ; Set RECALL bit 
        movwf   I2Cebyte          ;   in byte to write
        call    I2Cputbyte        ; Write to Register 135
        call    I2Cstop           ; Stop
 
; Now read registers 7 through 12 from Si570
; 
        movlw   0x07    
        movwf   I2Ceadd           ; address of 1st frequency define register in Si570

        call    I2Copenrd         ; Set up for read
        
        call    I2Cgetbyte        ; Get byte

        call    I2Csendack        ; ACK - continue reading

        movf    I2Cebyte,W
        movwf   SiReg7            ; Save Si570 Byte 0
        
        call    I2Cgetbyte 
        call    I2Csendack        ; ACK - continue reading
        movf    I2Cebyte,W
        movwf   SiReg8            ; Save Si570 Byte 1
            
        call    I2Cgetbyte 
        call    I2Csendack        ; ACK - continue reading
        movf    I2Cebyte,W
        movwf   SiReg9            ; Save Si570 Byte 2

        call    I2Cgetbyte 
        call    I2Csendack        ; ACK - continue reading
        movf    I2Cebyte,W
        movwf   SiReg10           ; Save Si570 Byte 3

        call    I2Cgetbyte 
        call    I2Csendack        ; ACK - continue reading
        movf    I2Cebyte,W
        movwf   SiReg11           ; Save Si570 Byte 4
        
        call    I2Cgetbyte        ; Get byte
        call    I2CsendNOACK      ; Last byte so send NOACK and 
        call    I2Cstop           ;   stop instead of ACK this time
        movf    I2Cebyte,W
        movwf   SiReg12           ; Save Si570 Byte 5

#ifdef SIMULATORON          ; For simulator only  (extracted from 10 MHz Si570)  <<<<<< OK FOR 10MHz Si570
        movlw   0x53
        movwf   SiReg7
        movlw   0xC2
        movwf   SiReg8
        movlw   0xA0
        movwf   SiReg9
        movlw   0x50
        movwf   SiReg10
        movlw   0xE9
        movwf   SiReg11
        movlw   0xFD
        movwf   SiReg12
#endif  
        return
;
; *****************************************************************************
; *    Name:  SendSiWords          >>>>> PAGE 1 <<<<<                         *
; *                                                                           *
; * Purpose: Send SiRegs to Si570 via I2C                                     *
; *                                                                           *
; *   Input: SiReg<7:12>                                                      *
; *                                                                           *
; *  Output: Si570 has new data in its registers                              *
; *                                                                           *
; *****************************************************************************
;
SendSiWords                 
;        
        call    DisplaySiRegs     ; DEBUG                  PAGE 1

        pagesel CalcSpan          ; Switch back to PAGE 0
        call    CalcSpan          ; See if frequency change requires FREEZE/UNFREEZE  (Page 0)
                                  ; If so, set NEEDFREEZE flag
        pagesel SendSiWords       ; Switch back to PAGE 1
;
; First Freeze DCO (if necessary) by setting bit 4 in Si570 register 137. 
; 
        btfss   VFOcontrol,NEEDFREEZE ; Is freq span large such that freeze is needed?
        goto    NoFreezeDCO       ; No, jump around Freeze DCO

        movlw   0x89              ; Register 137 - Freeze DCO control register in Si570
        movwf   I2Ceadd           ; Set address for first byte to write
        call    I2Copenw          ; Open for Write (signal start, send 0xAA, re-send until ACK)
                                  ; Then send address byte in eadd
        movlw   0x10              ; Set Freeze DCO bit
        movwf   I2Cebyte          ;   in byte to write
        call    I2Cputbyte        ; Write to Register 137
        call    I2Cstop           ; Stop

        goto    FreezeOK          ; Skip the Freeze M
;
NoFreezeDCO
;
; Need to "Freeze M" for Si570 so RF glitches aren't sprayed while sending the new SiBytes.
; Do this by setting bit 5 in Si570 register 135. Clear the "Freeze M" after sending SiBytes. 
; (Don't need to Freeze M if DCO is frozen.)
; 
        movlw   0x87              ; Register 135 -  control register in Si570
        movwf   I2Ceadd           ; Set address for first byte to write
        call    I2Copenw          ; Open for Write (signal start, send 0xAA, re-send until ACK)
                                  ; Then send address byte in eadd
        movlw   0x20              ; Set Freeze M bit
        movwf   I2Cebyte          ;   in byte to write
        call    I2Cputbyte        ; Write to Register 135
        call    I2Cstop           ; Stop

;
FreezeOK   
; 
; Send parameter words to Si570 registers 7 - 12
; 
        movlw   0x07              ; Register 7 - Address of first parameter data register
        movwf   I2Ceadd           ; Set address for first byte to write 
        call    I2Copenw          ; Open for Write (signal start, send 0xAA, re-send until ACK)
                                  ; Then send address byte in eadd

        movf    SiReg7,w          ; First parameter data
        movwf   I2Cebyte          ;   in byte to write
        call    I2Cputbyte        ; Write to Register 7

        movf    SiReg8,w          ; Next parameter data
        movwf   I2Cebyte          ;   in byte to write
        call    I2Cputbyte        ; Write to Register 8

        movf    SiReg9,w          ; Next parameter data
        movwf   I2Cebyte          ;   in byte to write
        call    I2Cputbyte        ; Write to Register 9

        movf    SiReg10,w         ; Next parameter data
        movwf   I2Cebyte          ;   in byte to write
        call    I2Cputbyte        ; Write to Register 10

        movf    SiReg11,w         ; Next parameter data
        movwf   I2Cebyte          ;   in byte to write
        call    I2Cputbyte        ; Write to Register 11

        movf    SiReg12,w         ; Next parameter data
        movwf   I2Cebyte          ;   in byte to write
        call    I2Cputbyte        ; Write to Register 12
        
        call    I2Cstop           ; Stop
; 
; Restart Si570 at new frequency
; 
        btfss   VFOcontrol,NEEDFREEZE ; Was freeze DCO done so we need to unfreeze?
        goto    UnfreezeM         ; No, just Unfreeze-M
        movlw   0x89              ; Register 137 - Unfreeze DCO register in Si570
        movwf   I2Ceadd           ; Set address 
        call    I2Copenw          ; Open to Write
        clrf    I2Cebyte          ; Clear Freeze-DCO bit 
        call    I2Cputbyte        ; Send data
        call    I2Cstop           ; Stop
        movlw   0x87              ; Register 135 - Reset/Memory Control register in Si570
        movwf   I2Ceadd           ; Set address for first byte to write
        call    I2Copenw          ; Open for Write (signal start, send 0xAA, re-send until ACK)
                                  ; Then send address byte in eadd
        movlw   0x40              ; Set New Freq bit
        movwf   I2Cebyte          ;   in byte to write
        call    I2Cputbyte        ; Write to Register 135
        call    I2Cstop           ; Stop
        goto    UnfreezeDone

UnfreezeM
; No Unfreeze DCO is needed but we still need to Unfreeze-M

        movlw   0x87              ; Register 135 - Reset/Memory Control register in Si570
        movwf   I2Ceadd           ; Set address for first byte to write
        call    I2Copenw          ; Open for Write (signal start, send 0xAA, re-send until ACK)
                                  ; Then send address byte in eadd
        clrf    I2Cebyte          ; Clear Freeze-M bit but DON'T SET NEW FREQ BIT in byte to write
        call    I2Cputbyte        ; Write to Register 135
        call    I2Cstop           ; Stop

UnfreezeDone
        return                    ; OK to switch back to PAGE 0 code. (16 bits on return stack)

; *****************************************************************************
; * I2C Routines          <<< ALL IN PAGE 1 >>>>                              *
; *                                                                           *
; *****************************************************************************
;
I2Cstrt ; Start condition; data goes from hi to low with SCL high.
        bsf     PORTA,SDA         ; SDA High 
        call    I2Cshortdelay     ; (Just go and return)
        bsf     PORTA,SCL         ; SCL high    
        call    I2Cshortdelay     ; 
        bcf     PORTA,SDA         ; SDA  low   Data high-to-low with CLK High
        call    I2Cshortdelay     ;
        bcf     PORTA,SCL         ; SCL low    Release SCL  
        return
;------------------------------------------------
I2Cstop    ; Stop condition; data goes from low to hi with SCL high.  Normal state is: scl low, data high
        
        banksel TRISA             ; Change to bank 1 for Tristate operation
        movlw   b'00100111'       ; Turn SDA (RB4) into an Output
        movwf   TRISA             ; 
        banksel PORTA             ; Change back to bank 0

        bcf     PORTA,SCL         ; Start with SCL low
        call    I2Cshortdelay     ;
        bcf     PORTA,SDA         ; and SDA low
        call    I2Cshortdelay     ;
        bsf     PORTA,SCL         ; SCL high
        call    I2Cshortdelay     ;
        bsf     PORTA,SDA         ; SDA goes high while SCL is high
        call    I2Cshortdelay     ;
        bcf     PORTA,SCL         ; SCL low

        ;banksel TRISA             ; Change to bank 1 
        ;movlw   b'00101111'       ; Turn SDA (RB3) back into an Input 
        ;movwf   TRISA             ; 
        ;banksel PORTA             ; Change back to bank 0

        return
;------------------------------------------------
I2Cgetack  ; Read acknowledge signal from Si570. Returns C set if no acknowledge (SDA high) from Si570 

        bcf     STATUS,C          ; Clear c (default)
        
        banksel TRISA             ; Change to bank 1 
        movlw   b'00101111'       ; Turn SDA (RB3) into an Input 
        movwf   TRISA             ; 
        banksel PORTA             ; Change back to bank 0

        bsf     PORTA,SCL         ; 
        nop                       ; For Read-Modify-Write problem
        btfss   PORTA,SDA         ; Test data input  
        goto    I2Cgax            ; SDA is low (ACK) so jump around with Carry clear    
        bsf     STATUS,C          ; SDA is high (NOACK) so set Carry flag 
I2Cgax     
        bcf     PORTA,SCL   
        banksel TRISA             ; Change to bank 1 for Tristate operation
        movlw   b'00100111'       ; Turn SDA (RB4) back into an Output
        movwf   TRISA             ; 
        banksel PORTA             ; Change back to bank 0
        call    I2Cshortdelay     ;
        return                    ; Exit with C clear if ACK, C set if NOACK
;------------------------------------------------       
I2Csendack ; 

        banksel TRISA             ; Change to bank 1 for Tristate operation
        movlw   b'00100111'       ; Turn SDA (RB4) into an Output
        movwf   TRISA             ; 
        banksel PORTA             ; Change back to bank 0

        ; ACK - low SDA during a clock pulse. 
        bcf     PORTA,SCL         ; SCL low (should alredy be low, but OK)
        call    I2Cshortdelay     ;
        bcf     PORTA,SDA         ; SDA low  <<  This is key for ACK
        call    I2Cshortdelay     ;
        bsf     PORTA,SCL         ; SCL high
        call    I2Clongerdelay    ;
        bcf     PORTA,SCL         ; SCL low
        call    I2Cshortdelay     ;
        ; bsf     PORTA,SDA       ; Set up SDA with exit condition (Not needed with this I/F) 
        
        banksel TRISA             ; Change to bank 1 
        movlw   b'00101111'       ; Turn SDA (RB3) back into an Input 
        movwf   TRISA             ; 
        banksel PORTA             ; Change back to bank 0

        return              

;------------------------------------------------
I2CsendNOACK ; 
        
        banksel TRISA             ; Change to bank 1 for Tristate operation
        movlw   b'00100111'       ; Turn SDA (RB4) into an Output
        movwf   TRISA             ; 
        banksel PORTA             ; Change back to bank 0

        ; NOACK - low SDA during a clock pulse. 
        bcf     PORTA,SCL         ; SCL low (should alredy be low, but OK)
        call    I2Cshortdelay     ;
        bsf     PORTA,SDA         ; SDA HIGH <<  This is key for NOACK
        call    I2Cshortdelay     ;
        bsf     PORTA,SCL         ; SCL high
        call    I2Clongerdelay    ;
        bcf     PORTA,SCL         ; SCL low

        banksel TRISA             ; Change to bank 1 
        movlw   b'00101111'       ; Turn SDA (RB3) back into an Input 
        movwf   TRISA             ; 
        banksel PORTA             ; Change back to bank 0

        return

;------------------------------------------------
I2Cgetbyte ;input a byte from Si570; you must send ack after this routine !

        movlw   0x08              ; 
        movwf   I2Cbytcnt         ; counter                            

        clrf    I2Cebyte          ; I2Cebyte will contain the data
        call    I2Cshortdelay     ;

        banksel TRISA             ; Change to bank 1 for Tristate operation
        movlw   b'00101111'       ; Turn SDA (RB4) into an Input 
        movwf   TRISA             ; 
        banksel PORTA             ; Change back to bank 0

I2Cinbit   
        bcf     STATUS,C
        rlf     I2Cebyte,f
        bsf     PORTA,SCL   
        call    I2Cshortdelay     ;
        btfss   PORTA,SDA         ; 
        goto    I2Cbitlo          ;
        incf    I2Cebyte,F        ; set lsb in I2Cebyte  
I2Cbitlo   
        bcf     PORTA,SCL         ;
        call    I2Cshortdelay     ;
        decfsz  I2Cbytcnt,f       ;
        goto    I2Cinbit          ;
        movf    I2Cebyte,W        ;

        banksel TRISA             ; Change to bank 1 for Tristate operation
        movlw   b'00100111'       ; Turn SDA (RB4) back into an Output 
        movwf   TRISA             ; 
        banksel PORTA             ; Change back to bank 0

        return                    ; with data in I2Cebyte
;------------------------------------------------   
I2Cputbyte     ;output a byte in I2Cebyte to Si570 and then get an rxACK 

        movlw   0x08              ; 8 bits 
        movwf   I2Cbytcnt         ;  to counter                                              

I2Csenlp   
        bcf     PORTA,SDA         ; SDA bit low (default)
        rlf     I2Cebyte,F        ; Shift LSB to Carry bit
        btfss   STATUS,C          ; Is C set?
        goto    I2Czerb           ; No, jump (Leave SDA bit low)
        bsf     PORTA,SDA         ; Yes, set SDA high
I2Czerb    
        call    I2Cshortdelay     ;
        bsf     PORTA,SCL         ; Raise Clock
        call    I2Clongerdelay    ;
        bcf     PORTA,SCL         ; Drop Clock
        ; call    I2Cshortdelay     ;
        decfsz  I2Cbytcnt,F       ; Done?
        goto    I2Csenlp          ; No, loop back
        ;bsf     PORTA,SDA        ; Yes, set up SDA with exit condition (Not needed with this I/F) 

        call    I2Cgetack         ; Get ACK/NOACK  Return with C clear if ACK

        return

;------------------------------------------------
I2Copenrd
; Initial part of Si570 read sequence for random read. 
; Call with initial address in eadd 

I2CRdStartLoop    

        call    I2Cstrt           ; Signal Start condition

; Slave address for Si570 is 0x55. 
; For I2C we shift left by 1 bit and OR in the Read/Not_Write bit 
        
        movlw   0xAA              ; Address 0x55. shifted 1 bit, zero for Read/NotWrite bit gives Write  
        movwf   I2Cebyte          ;   as data byte
        call    I2Cputbyte        ; Output the byte and get ACK/NOACK
        btfsc   STATUS,C          ; C will be set if NO ACK
        goto    I2CRdStartLoop    ; C is set (NOACK) so loop until Ack

        movf    I2Ceadd,W         ; Carry is clear (ACK) so set up address  
        movwf   I2Cebyte          ;   as data byte
        call    I2Cputbyte        ; Si570 start word address in b, getack         

        call    I2Cstrt           ; Send signal for start of data
; Slave address for Si570 is 0x55. 
; For I2C we shift left by 1 bit and OR Read/!Write bit 
; 0x55 shifted left by 1 and LSB is ONE for Read  
        movlw   0xAB              ; 
        movwf   I2Cebyte          ; slave addr/read 
        call    I2Cputbyte        ; output a byte and get ack.
              
        ; Now ready to read a single byte followed by stop 
        ;   or read a byte followed by ack and continue reading
        return
;------------------------------------------------
I2Copenw
; open for write.  Call with start address in eadd

I2CWrStartLoop
        call    I2Cstrt           ; Signal Start condition

        movlw   0xAA              ; Address 0x55. shifted 1 bit, zero for Read/NotWrite bit gives Write  
        movwf   I2Cebyte          ; identifier/slave address with write
        call    I2Cputbyte        ; output a byte and get ACK

        btfsc   STATUS,C          ; C will be set if NO ACK
        goto    I2CWrStartLoop    ; C is set (NOACK) so loop until Ack

        movf    I2Ceadd,W         ; start address for write
        movwf   I2Cebyte          ;                         
        call    I2Cputbyte        ; start word address, getack

        return                    ; now send bytes using putbyte.  end with stop


;------------------------------------------------
I2Cshortdelay                     ; Delay  (Fast Mode is OK for Si570 -> 400k bits/sec)
        return
;------------------------------------------------
I2Clongerdelay                    ; A bit longer Delay  
        nop
        nop
        nop
        return
        
;--------------------------------------     


;I2Cwriterfreq ; EXAMPLE -  WRITE SiBytes directly   >>>>>>FOR DEBUG.  NOT USED NOW! <<<<<<<<<<<<<<<<<<<<<<<
;       movlw   0x89              ; Register 137 - Freeze DCO register in Si570
;       movwf   I2Ceadd           ; Set address 
;       call    I2Copenw          ; Open for Write (signal start, send 0xAA, re-send until ACK)
;
;       movlw   0x10              ; Mask for DCO Freeze in Register 137
;       movwf   I2Cebyte          ; Set in Data Byte
;       call    I2Cputbyte        ; Send it
;       call    I2Cstop           ;   and then stop
;
;       movlw   0x07              ; Register 7 is 1st frequency define register
;       movwf   I2Ceadd           ; Set address
;       call    I2Copenw          ; Open for Write
;   
;       movlw   0xE7              ; Set up data byte  HS_DIV[2:0] = 111  N1[6:2]=00111 
;       movwf   I2Cebyte          ; This is hs-div = 7 (1/11), N1 = 28 (0x1C) 
;       call    I2Cputbyte        ; Send byte
;       
;       movlw   0x02              ; Remainder of N1[1:0]= 00 and RFREQ[37:32]=000010      
;       movwf   I2Cebyte          ; 
;
;       call    I2Cputbyte        ; Send data
;               
;       movlw   0xC9              ; 0xC9
;       movwf   I2Cebyte          ;   as data byte
;       call    I2Cputbyte        ; Send data
;               
;       movlw   0x5E              ; 0x5E
;       movwf   I2Cebyte          ;   as data byte
;       call    I2Cputbyte        ; Send data
;           
;       movlw   0xFC              ; 0xFC
;       movwf   I2Cebyte          ;   as data byte
;       call    I2Cputbyte        ; Send data
;       
;       clrf    I2Cebyte          ; 0x00 as data byte
;       call    I2Cputbyte        ; Send data
;       call    I2Cstop           ; Stop  
;       
;       movlw   0x89              ; Register 137 - Freeze DCO register in Si570
;       movwf   I2Ceadd           ; Set address 
;       call    I2Copenw          ; Open to Write
;       clrf    I2Cebyte          ; Clear DCO bit to unfreeze
;       call    I2Cputbyte        ; Send data
;       call    I2Cstop           ; Stop
;       
;       movlw   0x87              ; Register 135 - Reset / Memory control register in Si570
;       movwf   I2Ceadd           ; Set address
;       call    I2Copenw          ; Open for Write (signal start, send 0xAA, re-send until ACK)
;       movlw   0x40              ; New Freq bit
;       movwf   I2Cebyte          ;   as data byte
;       call    I2Cputbyte        ; Send data 
;       call    I2Cstop           ; Stop           
;       return                                  
;
; *****************************************************************************
; *    Name:  RunMenu         <<<< PAGE 1 >>>>>                               *
; *                                                                           *
; * Purpose:  Display menu on LCD, solicit responses, and set FSK/SB Modes    *
; *                                                                           *
; *  Input: None                                                              *
; *                                                                           *
; * Output: FSKcontrol,FSKATIVE flag set up and displayed on LCD              *
; *         ModeSelect (LSB, USB, CW, RevCW) set up and displayed on LCD      *
; *   Uses: temp1                                                             *
; *****************************************************************************
;
RunMenu
        movlw   0x80              ; Point LCD at digit 1                
        pagesel cmnd2LCD          ; Change to page 0
        call    cmnd2LCD          ; Send command
        movlw   ' '               ; digit 1              
        call    data2LCD          ;            PAGE 0
        movlw   'M'               ; digit 1              
        call    data2LCD          ;            PAGE 0
        movlw   'e'               ; digit 1              
        call    data2LCD          ;            PAGE 0
        movlw   'n'               ; digit 1              
        call    data2LCD          ;            PAGE 0
        movlw   'u'               ; digit 1              
        call    data2LCD          ;            PAGE 0
        movlw   ' '               ; digit 1              
        call    data2LCD          ;            PAGE 0
        movlw   '('               ; digit 1              
        call    data2LCD          ;            PAGE 0
        movlw   'P'               ; digit 1              
        call    data2LCD          ;            PAGE 0
        movlw   'B'               ; digit 1              
        call    data2LCD          ;            PAGE 0
        movlw   '4'               ; digit 1              
        call    data2LCD          ;            PAGE 0
        movlw   ' '               ; digit 1              
        call    data2LCD          ;            PAGE 0
        movlw   'E'               ; digit 1              
        call    data2LCD          ;            PAGE 0
        movlw   'x'               ; digit 1              
        call    data2LCD          ;            PAGE 0
        movlw   'i'               ; digit 1              
        call    data2LCD          ;            PAGE 0
        movlw   't'               ; digit 1              
        call    data2LCD          ;            PAGE 0
        movlw   ')'               ; digit 1              
        call    data2LCD          ;            PAGE 0
        pagesel RunMenu           ; Back to page 1

RunMenuModeLoop
        call    DisplayMode       ; Display current mode
        call    SolicitMenuResponse  ; Wait for PB3 or PB4 for response
        btfsc   temp1,MENUEXIT    ; Done with this menu item (PB4)?
        goto    RunMenuSetRelays  ; Yes, go set relays and move on
        ; It must be a PB3 tap 
        movf    ModeSelect,w      ; Save current mode
        movwf   temp1             ;   in temp
        ; clrf    ModeSelect        ; Clear current mode    
        bcf     ModeSelect,MODELSB
        bcf     ModeSelect,MODEUSB
        bcf     ModeSelect,MODECWMINUS
        bcf     ModeSelect,MODECWPLUS
        btfsc   temp1,MODELSB     ; Is current mode LSB?
        bsf     ModeSelect,MODEUSB ; Yes, set new mode to USB
        btfsc   temp1,MODEUSB     ; Is current mode USB?
        bsf     ModeSelect,MODECWMINUS ; Yes, set new mode to CW- 
        btfsc   temp1,MODECWMINUS  ; Is current mode CW-?
        bsf     ModeSelect,MODECWPLUS ; Yes, set new mode to CW+
        btfsc   temp1,MODECWPLUS  ; Is current mode CW+?
        bsf     ModeSelect,MODELSB ; Yes, set new mode to LSB
        goto    RunMenuModeLoop   ; Loop back in this mode selection

RunMenuSetRelays
        btfsc   ModeSelect,MODELSB
        goto    RunMenuSetRelayMinus
        btfsc   ModeSelect,MODEUSB
        goto    RunMenuSetRelayPlus
        btfsc   ModeSelect,MODECWMINUS
        goto    RunMenuSetRelayMinus
        btfsc   ModeSelect,MODECWPLUS
        goto    RunMenuSetRelayPlus

RunMenuSetRelayPlus
        bsf     PORTA,LOWSELECTPLUS  ; Set pin high to drive latching relay
        nop                       ;    let it settle (Read-modify-write problem),
        bcf     PORTA,LOWSELECTMINUS ; Set other side low 
        ; wait for 8mS  
        ; (TQ2-L-5V needs 14 mA at 5v for 3 mS plus contact bounce time)
        pagesel wait_8ms          ; Switch to Page 0
        call    wait_8ms          ; Wait for Relay to set/reset
        bcf     PORTA,LOWSELECTPLUS  ; Release it (Both sides low)
        bsf     VFOcontrol,FORCESEND ; Need to send SI bytes for sure
        bcf     VFOcontrol,CWREGSOK ; Say regs need to be set up for this new frequency
        call    CWShiftCheck      ; See if Shift needed             PAGE 0
        pagesel RunMenu           ; Change back to Page 1
        bcf     VFOcontrol,FORCESEND ; Clear flag
        goto    RunMenuSetRelaysDone ; Done so go to next menu section

RunMenuSetRelayMinus
        bsf     PORTA,LOWSELECTMINUS ; Set pin high to drive latching relay
        nop                       ;    let it settle (Read-modify-write problem),
        bcf     PORTA,LOWSELECTPLUS  ; Set other side low 
        ; wait for 8mS  
        ; (TQ2-L-5V needs 14 mA at 5v for 3 mS plus contact bounce time)
        pagesel wait_8ms          ; Switch to Page 0
        call    wait_8ms          ; Wait for Relay to set/reset
        bcf     PORTA,LOWSELECTMINUS ; Release it (both sides low)
        bsf     VFOcontrol,FORCESEND ; Need to send SI bytes for sure
        bcf     VFOcontrol,CWREGSOK ; Say regs need to be set up for this new frequency
        call    CWShiftCheck      ; See if Shift needed             PAGE 0
        pagesel RunMenu           ; Change back to Page 1
        bcf     VFOcontrol,FORCESEND ; Clear flag
RunMenuSetRelaysDone
; 
RunMenuDivCheckLoop
        call    DisplayFreqDivStatus  ; Display current Frequency Division status
        call    SolicitMenuResponse  ; Wait for PB3 or PB4 for response
        btfsc   temp1,MENUEXIT    ; Done with this menu item (PB4)?
        goto    RunMenuDivCheckDone ; Yes, move on
        ; It must be a PB3 tap 
        movf    ModeSelect,w      ; Save current mode
        movwf   temp1             ;   in temp
        bcf     ModeSelect,MODEDIVBY2 ; Clear current DIV bit
        bcf     ModeSelect,MODEDIVBY4 ; Clear current DIV bit
        btfsc   temp1,MODEDIVBY2    ; Is it currently DIVBY2? 
        goto    DivCheckSetDiv4     ; Yes, go advance to DIVBY4
        btfsc   temp1,MODEDIVBY4    ; No, Is it currently DIVBY4? 
        goto    RunMenuDivCheckLoop ; Yes, neither bit will be set so continue
        bsf     ModeSelect,MODEDIVBY2 ; No, it must be No DIV so set up for Div By 2
        goto    RunMenuDivCheckLoop ;     and go to back for next select 
DivCheckSetDiv4
        bsf     ModeSelect,MODEDIVBY4 ; Set for Div by 4
        goto    RunMenuDivCheckLoop ;     and go to back for next select 
RunMenuDivCheckDone
;
RunMenuFSKLoop
#ifdef FSKON
        call    DisplayFSKStatus  ; Display current FSK status
        call    SolicitMenuResponse  ; Wait for PB3 or PB4 for response
        btfsc   temp1,MENUEXIT    ; Done with this menu item (PB4)?
        goto    RunMenuExit       ; Done with Mode; go to exit
        ; It must be a PB3 tap    
        movf    FSKcontrol,w      ; Save current FSK bits
        movwf   temp1             ;   in temp
        clrf    FSKcontrol        ; Clear current FSK status

        btfss   temp1,FSKACTIVE   ; Is current FSK OFF?
        goto    RunMenuFSKSetActive ; Yes, go change to active
RunMenuFSKSetInactive
        bcf     FSKcontrol,FSKACTIVE ; No, set new FSK OFF
        ; FSK going from active to inactive so force update
        bsf     VFOcontrol,FORCESEND ; Need to send SI bytes for sure
        bcf     VFOcontrol,CWREGSOK ; Say regs need to be set up for this new frequency
        pagesel CWShiftCheck      ; Change to Page 0
        call    CWShiftCheck      ; See if Shift needed             PAGE 0
        pagesel RunMenu           ; Change back to Page 1
        bcf     VFOcontrol,FORCESEND ; Clear flag
        goto    RunMenuFSKSetOK     ; FSK Change done

RunMenuFSKSetActive
        btfsc   temp1,FSKACTIVE    ; Is current FSK ON?
        goto    RunMenuFSKSetOK    ; Yes, continue loop
        bsf     FSKcontrol,FSKACTIVE ; No, set new FSK ON 
        ; FSK going from inactive to active so force update
        bsf     VFOcontrol,FORCESEND ; Need to send SI bytes for sure
        bcf     VFOcontrol,CWREGSOK ; Say regs need to be set up for this new frequency
        pagesel CWShiftCheck      ; Change to Page 0
        call    CWShiftCheck      ; See if Shift needed             PAGE 0
        pagesel RunMenu           ; Change back to Page 1
        bcf     VFOcontrol,FORCESEND ; Clear flag

RunMenuFSKSetOK
        goto    RunMenuFSKLoop    ; Loop back in this mode selection
#endif  ; FSKON

RunMenuExit
        bcf     ModeSelect,RUNMENU  ; RunMenu is done so clear flag    
        pagesel update_ModeSelect_EEPROM ; Change to Page 0        
        call    update_ModeSelect_EEPROM ; Update ModeSelect in EEPROM      ; v7.6
        pagesel RunMenu             ; Change back to page 1
       
        return                    


; *****************************************************************************
; *    Name:  DisplayMode         <<<< PAGE 1 >>>>>                           *
; *                                                                           *
; * Purpose:  Display LSB/USB/CW-/CW+ on second line of LCD                   *
; *                                                                           *
; *  Input: None                                                              *
; *                                                                           *
; * Output: LCD second line says LSB or USB or CW+ or CW-                     *
; *   Uses:                                                                   *
; *****************************************************************************
;
DisplayMode  
        btfss      ModeSelect,MODELSB
        goto       DisplayCheckUSB
        ; It's LSB
        movlw   0x80              ; Point LCD at digit 1                 
        pagesel cmnd2LCD          ; Change to page 0
        call    cmnd2LCD          ; Send command
        pagesel DisplayMode       ; Back to Page 1
        movlw   'L'               ; digit 2              
        pagesel data2LCD          ; Change to page 0
        call    data2LCD          ;            PAGE 0
        pagesel DisplayMode       ; Back to Page 1
        goto    DisplayModeExit   ; exit

DisplayCheckUSB
        btfss      ModeSelect,MODEUSB
        goto       DisplayCheckCWPLUS
        ; It's USB
        movlw   0x80              ; Point LCD at digit 1                 
        pagesel cmnd2LCD          ; Change to page 0
        call    cmnd2LCD          ; Send command
        pagesel DisplayMode       ; Back to Page 1
        movlw   'U'               ; digit 2              
        pagesel data2LCD          ; Change to page 0
        call    data2LCD          ;            PAGE 0
        pagesel DisplayMode       ; Back to Page 1
        goto    DisplayModeExit   ; exit

DisplayCheckCWPLUS
        btfss      ModeSelect,MODECWPLUS
        goto       DisplayCheckCWMINUS
        ; It's CW+
        movlw   0x80              ; Point LCD at digit 1                 
        pagesel cmnd2LCD          ; Change to page 0
        call    cmnd2LCD          ; Send command
        pagesel DisplayMode       ; Back to Page 1
        movlw   '+'               ; digit 4              
        pagesel data2LCD          ; Change to page 0
        call    data2LCD          ;            PAGE 0
        pagesel DisplayMode       ; Back to Page 1
        goto    DisplayModeExit   ; exit

DisplayCheckCWMINUS
        btfss      ModeSelect,MODECWMINUS
        goto       DisplayModeExit
        ; It must be CWMINUS
        movlw   0x80              ; Point LCD at digit 1                 
        pagesel cmnd2LCD          ; Change to page 0
        call    cmnd2LCD          ; Send command
        pagesel DisplayMode       ; Back to Page 1
        movlw   '-'               ; digit 4              
        pagesel data2LCD          ; Change to page 0
        call    data2LCD          ;            PAGE 0
        pagesel DisplayMode       ; Back to Page 1
        ; goto    DisplayModeExit   ; exit
        ; fall through to exit           

DisplayModeExit
        movlw   0x8F              ; Point LCD Line 1 at digit 16 
        pagesel cmnd2LCD          ; Change to page 0
        call    cmnd2LCD          ; Send command
        pagesel DisplayMode       ; Back to Page 1
        return                    ; return

; *****************************************************************************
; *    Name:  DisplayFreqDivStatus         <<<< PAGE 1 >>>>>                  *
; *                                                                           *
; * Purpose:  Display Frequency Division status on Line 1, Position 1 of LCD  *
; *                                                                           *
; *  Input: None                                                              *
; *                                                                           *
; * Output: LCD displays "-" if no divide                     *
; *         LCD displays "2" if Divide by 2                   *
; *         LCD displays "4" if Divide by 4                                   *
; *   Uses:                                                                   *
; *****************************************************************************
;
DisplayFreqDivStatus  
        btfss   ModeSelect,MODEDIVBY2
        goto    DisplayCheckDivBy4
        ; It's Divide by 2
        movlw   0x80              ; Point LCD at digit 1                 
        pagesel cmnd2LCD          ; Change to page 0
        call    cmnd2LCD          ; Send command
        pagesel DisplayMode       ; Back to Page 1
        movlw   '2'               ; digit = 2              
        pagesel data2LCD          ; Change to page 0
        call    data2LCD          ;            PAGE 0
        pagesel DisplayMode       ; Back to Page 1
        goto    DisplayDivExit    ; exit

DisplayCheckDivBy4
        btfss   ModeSelect,MODEDIVBY4
        goto    DisplayNoDiv
        ; It's Divide by 4
        movlw   0x80              ; Point LCD at digit 1                 
        pagesel cmnd2LCD          ; Change to page 0
        call    cmnd2LCD          ; Send command
        pagesel DisplayMode       ; Back to Page 1
        movlw   '4'               ; digit = 4              
        pagesel data2LCD          ; Change to page 0
        call    data2LCD          ;            PAGE 0
        pagesel DisplayMode       ; Back to Page 1
        goto    DisplayDivExit    ; exit

DisplayNoDiv
        ; It must be No Divide
        movlw   0x80              ; Point LCD at digit 1                 
        pagesel cmnd2LCD          ; Change to page 0
        call    cmnd2LCD          ; Send command
        pagesel DisplayMode       ; Back to Page 1
        movlw   '1'               ; digit = 1              
        pagesel data2LCD          ; Change to page 0
        call    data2LCD          ;            PAGE 0
        pagesel DisplayMode       ; Back to Page 1
        ; fall through to exit

DisplayDivExit
        movlw   0x8F              ; Point LCD Line 1 at digit 16 
        pagesel cmnd2LCD          ; Change to page 0
        call    cmnd2LCD          ; Send command                               
        pagesel DisplayFreqDivStatus ; Back to Page 1
    return
 
; *****************************************************************************
; *    Name:  DisplayFSKStatus         <<<< PAGE 1 >>>>>                      *
; *                                                                           *
; * Purpose:  Display FSK ON/OFF on Line 1, Position 1 of LCD                 *
; *                                                                           *
; *  Input: None                                                              *
; *                                                                           *
; * Output: LCD "F" if FSK ON and blank if FSK OFF                            *
; *   Uses:                                                                   *
; *****************************************************************************
;
DisplayFSKStatus  
        btfss      FSKcontrol,FSKACTIVE
        goto       DisplayCheckFSKOFF
        ; It's FSK
        movlw   0x80              ; Point LCD at Line 1 digit 1                 
        pagesel cmnd2LCD          ; Change to page 0
        call    cmnd2LCD          ; Send command
        movlw   'F'               ;  
        call    data2LCD          ;            PAGE 0
        pagesel DisplayFSKStatus  ; Back to Page 1
        goto    DisplayFSKExit    ; exit

DisplayCheckFSKOFF
        btfsc      FSKcontrol,FSKACTIVE
        goto       DisplayFSKExit
        ; It's FSK
        movlw   0x80              ; Point LCD at Line 1 digit 1                 
        pagesel cmnd2LCD          ; Change to page 0
        call    cmnd2LCD          ; Send command
        movlw   ' '               ;               
        call    data2LCD          ;            PAGE 0
        pagesel DisplayFSKStatus  ; Back to Page 1
        ; goto    DisplayModeExit   ; exit
    ; fall through to exit

DisplayFSKExit
        movlw   0x8F              ; Point LCD Line 1 at digit 16 
        pagesel cmnd2LCD          ; Change to page 0
        call    cmnd2LCD          ; Send command
        pagesel DisplayMode       ; Back to Page 1
    return


; *****************************************************************************
; *    Name:  SolicitMenuResponse      <<<< PAGE 1 >>>>>                      *
; *                                                                           *
; * Purpose:  Wait for pushbutton response to menu item and set up flag       *
; *           Look for PB3, PB4 pressed                                       *
; *           If PB4, set temp1,MENUEXIT                                      *
; *           If PB3, set temp1,MENUTOGGLE                                    *
; *                                                                           *
; *  Input: None                                                              *
; *                                                                           *
; * Output: temp1 set up with MENUEXIT or MENUTOGGLE                          *
; *   Uses: temp1                                                             *
; *****************************************************************************
;
SolicitMenuResponse
        clrf    temp1             ; Clear response flags
        btfss   PORTA,PB_3        ; Is PB_3 being pressed?
        goto    MenuPB3_release_wait ; Yes,                                
        btfss   PORTA,PB_4        ; Is PB_4 being pressed?                     
        goto    MenuPB4_release_wait ; Yes,                              
        goto    SolicitMenuResponse ; No, loop

MenuPB3_release_wait                                                            
        pagesel wait_64ms         ; Go to Page 0
        call    wait_64ms         ; wait (debounce)
        pagesel SolicitMenuResponse; Back to Page 1
        btfss   PORTA,PB_3        ; Is PB_3 still behing held?                 
        goto    MenuPB3_release_wait ; Yes, wait until released (forever)                  
        bsf     temp1,MENUTOGGLE ; Toggle this menu item
        goto    SolicitExit       ;   and exit
        
MenuPB4_release_wait                                                            
        pagesel wait_64ms         ; Go to Page 0
        call    wait_64ms         ; wait (debounce)
        pagesel SolicitMenuResponse; Back to Page 1
        btfss   PORTA,PB_4        ; Is PB_4 still behing held?                 
        goto    MenuPB4_release_wait ; Yes, wait until released (forever)                  
        bsf     temp1,MENUEXIT ; End of this menu item    
        goto    SolicitExit       ;   and exit

SolicitExit
        return                    ; return


; *****************************************************************************
; *    Name:  CWSetUpRegs             <<<< PAGE 1 >>>>>                       *
; *                                                                           *
; * Purpose:  Set up XMIT and RCV Si registers for easy access                *
; *                                                                           *
; *   Input:  freq_3..freq_0  (Will be XMIT or MARK Frequency)                *
; *                                                                           *
; *  Output:  XmitFreq_3..XmitFreq_0 and RcvFreq_3..RcvFreq_0 set up          *
; *           XMSiReg8..XMSiReg12  and   RSSiReg8..RSSiReg12 set up           *
; *                                                                           *
; *                                                                           *
; *****************************************************************************
CWSetUpRegs
        ; Save current frequency as XMIT frequency  

        banksel freq_3
        movf    freq_3,w          ;                                  (bank 0)
        banksel XmitFreq_3
        movwf   XmitFreq_3        ; Save as Xmit frequency MSB       (bank 1)
        movwf   RcvFreq_3         ; This Rcv freq will be updated    (bank 1)
        
        banksel freq_2
        movf    freq_2,w          ;                                  (bank 0)
        banksel XmitFreq_2
        movwf   XmitFreq_2        ; Save as Xmit frequency           (bank 1)
        movwf   RcvFreq_2         ; This Rcv freq will be updated    (bank 1)

        banksel freq_1
        movf    freq_1,w          ;                                  (bank 0)
        banksel XmitFreq_1
        movwf   XmitFreq_1        ; Save as Xmit frequency           (bank 1)
        movwf   RcvFreq_1         ; This Rcv freq will be updated    (bank 1)

        banksel freq_0
        movf    freq_0,w          ;                                  (bank 0)
        banksel XmitFreq_0
        movwf   XmitFreq_0        ; Save as Xmit frequency LSB       (bank 1)
        movwf   RcvFreq_0         ; This Rcv freq will be updated    (bank 1)
        
        banksel freq_0            ; Back to bank 0 for build
        pagesel BuildSiWords      ; Switch to Page 0
        call    BuildSiWords      ; Use freq_3..freq_0 to build SI words for XMIT
        pagesel CWSetUpRegs       ; Back to Page 1

        banksel SiReg8            ; Save current SiRegs as Xmit SiRegs 
        movf    SiReg8,w          ;                                   (bank 0)
        banksel XMSiReg8          ;
        movwf   XMSiReg8          ;                                   (bank 1)

        banksel SiReg9            ; 
        movf    SiReg9,w          ;                                   (bank 0)
        banksel XMSiReg9          ;
        movwf   XMSiReg9          ;                                   (bank 1)
        
        banksel SiReg10           ;
        movf    SiReg10,w         ;                                   (bank 0)
        banksel XMSiReg10         ; 
        movwf   XMSiReg10         ;                                   (bank 1)

        banksel SiReg11           ;
        movf    SiReg11,w         ;                                   (bank 0)
        banksel XMSiReg11         ; 
        movwf   XMSiReg11         ;                                   (bank 1)

        banksel SiReg12           ;
        movf    SiReg12,w         ;                                   (bank 0)
        banksel XMSiReg12         ; 
        movwf   XMSiReg12         ;                                   (bank 1)

        
;       Add or subtract a offset value so we can hear an audio tone      
;       in receiver.  Add value if CW+, subtract value if CW- mode.      
;       Note that this shift is only done for the receive frequency.     
;       The transmit frequency is NOT shifted.                           
;       Add or subtract offset by adding positive or adding complement

CWadd_step
        banksel ModeSelect        ; Change to Bank 0
        movf    ModeSelect,w      ; Move to temporary                     (bank 0)
        banksel Bank1Temp         ;   storage in
        movwf   Bank1Temp         ;   bank 1   (to save bank switching)   (bank 1)
        movlw   SHIFT_0           ; Get LSB of the CW+ shift (default)
        btfss   Bank1Temp,MODECWPLUS ; Is it CW+ (shift UP)               (bank 1)
        movlw   SHIFTCOMP_0       ; No, Get LSB of the shift complement
        addwf   RcvFreq_0,f       ; Add it to the low byte of freq        (bank 1)
        btfss   STATUS,C          ; Any carry?
        goto    CWadd1            ; No, add next byte
        incfsz  RcvFreq_1,f       ; Ripple carry up to the next byte      (bank 1)
        goto    CWadd1            ; No new carry, add next byte
        incfsz  RcvFreq_2,f       ; Ripple carry up to the next byte      (bank 1)
        goto    CWadd1            ; No new carry, add next byte
        incf    RcvFreq_3,f       ; Ripple carry up to the highest byte   (bank 1)
CWadd1
        movlw   SHIFT_1           ; Get next byte of the CW+ shift (default)
        btfss   Bank1Temp,MODECWPLUS ; Is it CW+ (shift UP)               (bank 1)
        movlw   SHIFTCOMP_1       ; No, Get LSB of the shift complement
        addwf   RcvFreq_1,f       ; Add it to the next higher byte        (bank 1)
        btfss   STATUS,C          ; Any carry?
        goto    CWadd2            ; No, add next byte
        incfsz  RcvFreq_2,f       ; Ripple carry up to the next byte      (bank 1)
        goto    CWadd2            ; No new carry, add next byte
        incf    RcvFreq_3,f       ; Ripple carry up to the highest byte   (bank 1)
CWadd2
        movlw   SHIFT_2           ; Get next byte of the CW+ shift (default)
        btfss   Bank1Temp,MODECWPLUS ; Is it CW+ (shift UP)               (bank 1)
        movlw   SHIFTCOMP_2       ; No, Get LSB of the shift complement
        addwf   RcvFreq_2,f       ; Add it to the freq byte               (bank 1)
        btfss   STATUS,C          ; Any carry?
        goto    CWadd3            ; No, add last byte
        incf    RcvFreq_3,f       ; Ripple carry up to the highest byte   (bank 1)
CWadd3
        movlw   SHIFT_3           ; Get MSB of the CW+ shift     (default)
        btfss   Bank1Temp,MODECWPLUS ; Is it CW+ (shift UP)               (bank 1)
        movlw   SHIFTCOMP_3       ; No, Get LSB of the shift complement
        addwf   RcvFreq_3,f       ; Add it to the most significant freq   (bank 1)

        ; Now need to call BuildSiWords for RCV frequency 
        ;  (XMIT frequency is LCD frequency to be restored in freq_3..freq_0 at end)
        
        ; Move RcvFreq_3.. RcvFreq_0 to freq_3..freq_0 
        
        ;banksel RcvFreq_3        ; Switch to new bank
        movf    RcvFreq_3,w       ;                                       (bank 1)
        banksel freq_3            ; Switch to bank 0
        movwf   freq_3            ;                                       (bank 0)

        banksel RcvFreq_2         ; Switch to new bank
        movf    RcvFreq_2,w       ;                                       (bank 1)
        banksel freq_2            ; Switch to bank 0
        movwf   freq_2            ;                                       (bank 0)

        banksel RcvFreq_1         ; Switch to new bank
        movf    RcvFreq_1,w       ;                                       (bank 1)
        banksel freq_1            ; Switch to bank 0
        movwf   freq_1            ;                                       (bank 0)

        banksel RcvFreq_0         ; Switch to new bank
        movf    RcvFreq_0,w       ;                                       (bank 1)
        banksel freq_0            ; Switch to bank 0
        movwf   freq_0            ;                                       (bank 0)

        pagesel BuildSiWords      ; Switch to Page 0
        call    BuildSiWords      ; Use freq_3..freq_0 to build SI words for RCV    PAGE 0
        pagesel CWSetUpRegs       ; Switch to Page 1

        banksel SiReg8            ; Save current SiRegs as RCV SiRegs 
        movf    SiReg8,w          ;                                       (bank 0)
        banksel RSSiReg8          ;
        movwf   RSSiReg8          ;                                       (bank 1)
                                                                          
        banksel SiReg9            ;
        movf    SiReg9,w          ;                                       (bank 0)
        banksel RSSiReg9          ;
        movwf   RSSiReg9          ;                                       (bank 1)
        
        banksel SiReg10           ;
        movf    SiReg10,w         ;                                       (bank 0)
        banksel RSSiReg10         ; 
        movwf   RSSiReg10         ;                                       (bank 1)

        banksel SiReg11           ;
        movf    SiReg11,w         ;                                       (bank 0)
        banksel RSSiReg11         ; 
        movwf   RSSiReg11         ;                                       (bank 1)

        banksel SiReg12           ;
        movf    SiReg12,w         ;                                       (bank 0)
        banksel RSSiReg12         ; 
        movwf   RSSiReg12         ;                                       (bank 1)
        
        ; restore freq_3..freq_0 from XmitFreq_3..XmitFreq_0

        banksel XmitFreq_3        ;
        movf    XmitFreq_3,w      ;                                       (bank 1)
        banksel freq_3            ;                                   
        movwf   freq_3            ;                                       (bank 0)

        banksel XmitFreq_2        ;
        movf    XmitFreq_2,w      ;                                       (bank 1)
        banksel freq_2            ;
        movwf   freq_2            ;                                       (bank 0)

        banksel XmitFreq_1        ;
        movf    XmitFreq_1,w      ;                                       (bank 1)
        banksel freq_1            ;
        movwf   freq_1            ;                                       (bank 0)

        banksel XmitFreq_0        ;
        movf    XmitFreq_0,w      ;                                       (bank 1)
        banksel freq_0            ;
        movwf   freq_0            ;                                       (bank 0)

        bsf     VFOcontrol,CWREGSOK ; Say CW SiRegs set up

        return

#ifdef FSKON
; *****************************************************************************
; *    Name:  FSKSetUpRegs           <<<< PAGE 1 >>>>>                        *
; *                                                                           *
; * Purpose:  Set up MARK and SPACE Si registers for easy access              *
; *                                                                           *
; *   Input:  freq_3..freq_0  (Will be Mark Frequency)                        *
; *                                                                           *
; *  Output:  MarkFreq_3..MarkFreq_0 and SpaceFreq_3..SpaceFreq_0 set up      *
; *           XMSiReg8..XMSiReg12  and   RSSiReg8..RSSiReg12 set up           *
; *                                                                           *
; *                                                                           *
; *****************************************************************************
FSKSetUpRegs
        ; Save current frequency as MARK frequency  

        banksel freq_3
        movf    freq_3,w          ;
        banksel MarkFreq_3
        movwf   MarkFreq_3        ; Save as Mark frequency MSB
        movwf   SpaceFreq_3       ; This Space freq will be updated
        
        banksel freq_2
        movf    freq_2,w          ;
        banksel MarkFreq_2
        movwf   MarkFreq_2        ; Save as Mark frequency    
        movwf   SpaceFreq_2       ; This Space freq will be updated

        banksel freq_1
        movf    freq_1,w          ;
        banksel MarkFreq_1
        movwf   MarkFreq_1        ; Save as Mark frequency    
        movwf   SpaceFreq_1       ; This Space freq will be updated

        banksel freq_0
        movf    freq_0,w          ; 
        banksel MarkFreq_0
        movwf   MarkFreq_0        ; Save as Mark frequency LSB
        movwf   SpaceFreq_0       ; This Space freq will be updated

        banksel freq_0            ; Back to bank 0 for build
        pagesel BuildSiWords      ; Switch to Page 0
        call    BuildSiWords      ; Use freq_3..freq_0 to build SI words for XMIT
        pagesel FSKSetUpRegs      ; Back to Page 1

        banksel SiReg8            ; Save current SiRegs as Mark SiRegs 
        movf    SiReg8,w
        banksel XMSiReg8
        movwf   XMSiReg8            ; 

        banksel SiReg9             ; 
        movf    SiReg9,w
        banksel XMSiReg9
        movwf   XMSiReg9            ; 
        
        banksel SiReg10
        movf    SiReg10,w
        banksel XMSiReg10           ; 
        movwf   XMSiReg10           

        banksel SiReg11
        movf    SiReg11,w
        banksel XMSiReg11           ; 
        movwf   XMSiReg11           

        banksel SiReg12
        movf    SiReg12,w
        banksel XMSiReg12           ; 
        movwf   XMSiReg12           
        
        ; Subtract frequency shift by adding complement

FSKadd_step
        banksel SpaceFreq_0       ; Go to SpaceFreq bank 

        movlw   FSKCOMP_0         ; Get LSB of the shift complement
        addwf   SpaceFreq_0,f     ; Add it to the low byte of freq
        btfss   STATUS,C          ; Any carry?
        goto    FSKadd1           ; No, add next byte
        incfsz  SpaceFreq_1,f     ; Ripple carry up to the next byte
        goto    FSKadd1           ; No new carry, add next byte
        incfsz  SpaceFreq_2,f     ; Ripple carry up to the next byte
        goto    FSKadd1           ; No new carry, add next byte
        incf    SpaceFreq_3,f     ; Ripple carry up to the highest byte
FSKadd1
        movlw   FSKCOMP_1         ; Get next byte of the shift complement
        addwf   SpaceFreq_1,f     ; Add it to the next higher byte
        btfss   STATUS,C          ; Any carry?
        goto    FSKadd2           ; No, add next byte
        incfsz  SpaceFreq_2,f     ; Ripple carry up to the next byte
        goto    FSKadd2           ; No new carry, add next byte
        incf    SpaceFreq_3,f     ; Ripple carry up to the highest byte
FSKadd2
        movlw   FSKCOMP_2         ; Get next byte of the shift complement
        addwf   SpaceFreq_2,f     ; Add it to the freq byte
        btfss   STATUS,C          ; Any carry?
        goto    FSKadd3           ; No, add last byte
        incf    SpaceFreq_3,f     ; Ripple carry up to the highest byte
FSKadd3
        movlw   FSKCOMP_3         ; Get MSB of the shift complement
        addwf   SpaceFreq_3,f     ; Add it to the most significant freq

        ; Now need to call BuildSiWords for SPACE frequency 
        ;  (Mark frequency is LCD frequency to be restored in freq_3..freq_0 at end)
        
        ; Move SpaceFreq_3.. SpaceFreq_0 to freq_3..freq_0 
        
        banksel SpaceFreq_3       ; Switch to new bank
        movf    SpaceFreq_3,w
        banksel freq_3            ; Switch to bank 0
        movwf   freq_3

        banksel SpaceFreq_2       ; Switch to new bank
        movf    SpaceFreq_2,w
        banksel freq_2            ; Switch to bank 0
        movwf   freq_2

        banksel SpaceFreq_1       ; Switch to new bank
        movf    SpaceFreq_1,w
        banksel freq_1            ; Switch to bank 0
        movwf   freq_1

        banksel SpaceFreq_0       ; Switch to new bank
        movf    SpaceFreq_0,w
        banksel freq_0            ; Switch to bank 0
        movwf   freq_0           

        pagesel BuildSiWords      ; Switch to Page 0
        call    BuildSiWords      ; Use freq_3..freq_0 to build SI words for SPACE    PAGE 0
        pagesel FSKSetUpRegs      ; Back to Page 1

        banksel SiReg8            ; Save current SiRegs as Space SiRegs 
        movf    SiReg8,w
        banksel RSSiReg8
        movwf   RSSiReg8            ; 

        banksel SiReg9             ;
        movf    SiReg9,w
        banksel RSSiReg9
        movwf   RSSiReg9            ; 
        
        banksel SiReg10
        movf    SiReg10,w
        banksel RSSiReg10           ; 
        movwf   RSSiReg10           

        banksel SiReg11
        movf    SiReg11,w
        banksel RSSiReg11           ; 
        movwf   RSSiReg11           

        banksel SiReg12
        movf    SiReg12,w
        banksel RSSiReg12           ; 
        movwf   RSSiReg12           
        
        ; restore freq_3..freq_0 from MarkFreq_3..MarkFreq_0

        banksel MarkFreq_3         
        movf    MarkFreq_3,w
        banksel freq_3
        movwf   freq_3            ; 

        banksel MarkFreq_2         
        movf    MarkFreq_2,w
        banksel freq_2
        movwf   freq_2            ; 

        banksel MarkFreq_1         
        movf    MarkFreq_1,w
        banksel freq_1
        movwf   freq_1            ; 

        banksel MarkFreq_0         
        movf    MarkFreq_0,w
        banksel freq_0
        movwf   freq_0            ; 

        bsf     VFOcontrol,CWREGSOK ; Say FSK SiRegs set up (Same flag as CW)

        return


#endif  ; FSKON

; *****************************************************************************
; *    Name:  DisplaySiRegs    (for display info only)   <<<<< PAGE 1 >>>>    *
; *                                                                           *
; * Purpose:  Display the current SiRegs on the LCD                           *
; *                                                                           *
; *   Input:  Current SiReg7..SiReg12                                         *
; *                                                                           *
; *  Output:  LCD displays the SiRegs on line 2 of the LCD positions 1-12     *
; *           LCD displays the current band number on line 2 positions 15-16  * 
; *    Uses:  Temp3, Temp4                                                    *
; *****************************************************************************

DisplaySiRegs
        btfss   ModeSelect,MODEDEBUG ; Is Debug mode turned on?
        goto    DisplaySiRegsDone ; No, done 
       
        clrf    INTCON            ; DISABLE INTERRUPTS WHILE DISPLAYING.  PREVENT STACK OVERFLOW.

        movlw   0xC0              ; Point LCD at Line 2, position 1                
        pagesel cmnd2LCD          ; Switch to PAGE 0
        call    cmnd2LCD          ;                             PAGE 0
        pagesel DisplaySiRegs     ; Back to PAGE 1
        movf    SiReg7,w          ; Get SiReg byte to display at current position
        call    DisplayByte       ; 
        movf    SiReg8,w          ; Get SiReg byte to display at current position
        call    DisplayByte       ; 
        movf    SiReg9,w          ; Get SiReg byte to display at current position
        call    DisplayByte       ; 
        movf    SiReg10,w         ; Get SiReg byte to display at current position
        call    DisplayByte       ; 
        movf    SiReg11,w         ; Get SiReg byte to display at current position
        call    DisplayByte       ; 
        movf    SiReg12,w         ; Get SiReg byte to display at current position
        call    DisplayByte       ; 
       
        movlw   0xCE              ; Move LCD pointer to Line 2, position 15               
        pagesel cmnd2LCD          ; Switch to PAGE 0
        call    cmnd2LCD          ;                             PAGE 0
        pagesel DisplaySiRegs     ; Back to PAGE 1
        movf    band,w            ; Get current band  display at current position
        call    DisplayByte       ; 

        pagesel ShowActiveDigit   ; Switch to PAGE 0
        call    ShowActiveDigit   ; Set up underline on line 1 again     PAGE 0
        pagesel DisplaySiRegs     ; Back to PAGE 1

        movlw   0xC0              ; Get GIE and PEIE bits
        movwf   INTCON            ; Enable general interrupts again

DisplaySiRegsDone

        return

; ========subroutine used by DisplaySiRegs only =====
DisplayByte
; Note:  Numbers start at 0x30 in ASCII table,
;        Alpha starts at 0x41
;
        movwf   temp3             ; Working register
; Extract and send "XXXX" from byte containing "XXXXYYYY"
;  - Swap halves to get YYYYXXXX
;  - Mask with 0x0F to get 0000XXXX
;  - Add ASCII bias 
        swapf   temp3,w           ; Swap upper 4 bits into lower nibble of W
        andlw   0x0F              ; Mask for lower nibble only       (0000XXXX)
        addlw   0x30              ; Add offset for ASCII char set    (0030XXXX)
; If it's an alpha character, need to add 8 
        movwf   temp4             ; save in working reg
        movlw   0x3A              ; See if alpha 
        subwf   temp4,w           ; 
        btfss   STATUS,C          ; Look at Carry (set if positive or zero)
        goto    DisplayNum        ; Not set, so it's numeric
        movf    temp4,w
        addlw   0x07              ; Add on alpha offset
        movwf   temp4
DisplayNum
        movf    temp4,w
        pagesel ShowActiveDigit   ; Switch to PAGE 0
        call    data2LCD          ; Send byte in W to LCD    PAGE 0
        pagesel DisplaySiRegs     ; Back to PAGE 1
;
; Extract and send "YYYY" from byte containing "XXXXYYYY"
;   - Mask with 0x0F to get 0000YYYY
;   - Add offset for ASCII character set
        movf    temp3,w           ; Swap                                           
        andlw   0x0F              ; Mask for lower nibble only       (0000YYYY)
        addlw   0x30              ; Add offset for ASCII char set    (0030YYYY)
; If it's an alpha character, need to add 8 
        movwf   temp4             ; save in working reg
        movlw   0x3A              ; See if alpha 
        subwf   temp4,w           ; 
        btfss   STATUS,C          ; Look at Carry (set if positive or zero)
        goto    DisplayNum2       ; Not set, so it's numeric
        movf    temp4,w
        addlw   0x07              ; Add on alpha offset (0x3A becomes 0x41, etc)
        movwf   temp4
DisplayNum2
        movf    temp4,w
        pagesel ShowActiveDigit   ; Switch to PAGE 0
        call    data2LCD          ; Send byte in W to LCD    PAGE 0
        pagesel DisplaySiRegs     ; Back to PAGE 1
; 
        return

; PAGE 1 MAX 0xFFF                              

        END

