;****************************************************************************
; * HF VFO with AD9854 Direct Digital Synthesis                             *
; *  Version 5.19b                                                          *
; *   (Has pushbutton debounce code and leading zero suppression)           *
; *  December 28, 2004                                                      *
; *                                                                         *
;****************************************************************************
; Authors - Craig Johnson, AA0ZZ   and Bruce Stough, AA0ED
;
; Description:
;   This code is for a PIC16F877 microcontroller which is used to control a 
;   VFO using an Analog Devices AD9854 DDS. 
;
;   In the July, 1997 issue of QEX magazine, Curtis Pruess, WB2V, published 
;   an article featuring a signal generator which used an Analog Devices 
;   AD9850 DDS device controlled by a PIC16F84 microcontroller.  
; 
;   Since that time, a newer device has been introduced by Analog Devices -
;   the AD9854.  This device has some significant advantages over the AD9850.
;
;   First, the AD9854 produces two outputs with a 90 degree phase difference.
;   This phase relationship remains constant regardless of the frequency.   
;
;   Second, the AD9854 has a 12 Bit DAC (D/A converter) while the AD9850 has 
;   a 10 bit DAC.  This is important in reducing the undesirable byproducts 
;   (spurs).  
;
;   Third, the AD9854 has a 300 MHz maximum clock speed, compared to 125 MHz
;   for the AD9850.  The faster clock speed raises the Nyquest limit, 
;   allowing the VFO to operate cleanly up through 30 MHz. 
;
;   This project is an extension of the concepts introduced in that original
;   AD9850 project, but there have been many major changes.   It still has 
;   an optical encoder (or optional mechanical encoder) for frequency 
;   selection and it still uses an LCD for displaying the frequency. However,
;   the HF-VFO has been changed in these ways: 
;
;   1)  The HF-VFO now uses two PIC microcontrollers - one controlling the 
;       optical encoder and the other controlling the AD9854, the LCD, the 
;       pushbuttons and the LEDs.   
;   2)  The HF-VFO now uses a two-line LCD so more information is displayed.
;   3)  The HF-VFO now operates as two VFOs with two frequencies.
;   4)  The two-part VFO supports split-frequency operation.  
;   5)  The two AD9854 outputs are amplified to produce signal levels which
;       are compatible with common receiver requirements (such as the R2 or
;       R2Pro by Rick Campbell - KK7B.)
;
;   Early versions of this HF-VFO project used a single PIC microcontroller
;   for all VFO functions.  However, the PIC's main code execution loop was
;   very large and it varied a great deal in length, depending on if the LCD
;   and DDS needed to be updated and if pushbuttons are being used.  Since 
;   this PIC also monitored the input signals from the optical encoder, 
;   reading the "gray code" to determine how fast the encoder was being 
;   turned, a variable size main execution loop made accurate timing and 
;   smooth operation very difficult to accomplish.
;
;   In this version we decided to use a dedicated PIC16F628 microcontroller
;   for the optical encoder.  Why?   They are inexpensive and provide a 
;   rather elegant solution to the problem of accurate timing for smooth 
;   tuning operation. 
;
;   The encoder PIC monitors the output of the encoder.  Periodically it
;   sends information to the main ('877) microcontroller to indicate that
;   the encoder tuning knob has been turned as well as the direction of 
;   change.  The encoder ('628) microcontroller sends the data to the 
;   '877 PIC by setting up 3 data lines and a direction line and then 
;   generating a pulse on the '877 PIC's interrupt line.   The interrupt
;   causes the '877 PIC to stop its current operation for a very brief 
;   period of time, to examine the data lines, save the new magnitude and
;   direction, set a flag saying a change has been made, and then return 
;   to the task it was executing at the time of the interrupt.   A very 
;   short time later, code in the main execution loop will detect the 
;   "change" flag and make the appropriate frequency change in the DDS 
;   and LCD.   
;
;   For further details and documentation regarding this project, please see
;   my web page:   www.cbjohns.com/aa0zz
;
;   The code for the encoder PIC, the PIC16F628, can also be found on the web
;   site.
;
;*****************************************************************************
;
;      Target Controller for optical shaft encoder -  PIC16F628
;                                  __________
;          (Unused)-----------RA2 |1       18| RA1-I-----Encoder A
;          (Unused)-----------RA3 |2       17| RA0-I-----Encoder B
;          (Unused)-----------RA4 |3       16| RA7------(Unused)
;               +5v---------!MCLR |4       15| RA6------(Unused)
;               GND-----------Vss |5       14| VDD------+5 V
;          (Unused)-----------RB0 |6       13| RB7-O-----ENC3 (16F877-RA0) (DIR)
;          (Unused)-----------RB1 |7       12| RB6-O-----ENC2 (16F877-RA1)
;          (Unused)-----------RB2 |8       11| RB5-O---- ENC1 (16F877-RA2)
; ENC Int.(16F877-RB0)------O-RB3 |9       10| RB4-O---- ENC0 (16F877-RA3)
;                                  ----------
;
;
;    Target Controller for DDS/LCD -  PIC16F877
;                                  __________
;                 +5v-------!MCLR |1       40| RB7-O---- (Rsvd) - (RX VFO A - LED3)
;(DIR)ENC3(16F84-RB7)-------I-RA0 |2       39| RB6-O---- (Rsvd) - (RX VFO B - LED4)
;    ENC2 (16F84-RB6)-------I-RA1 |3       38| RB5-I---- VFO FREQ A=B(Pushbutton5) 
;    ENC1 (16F84-RB5)-------I-RA2 |4       37| RB4-I---- Split       (Pushbutton6)
;    ENC0 (16F84-RB4)-------I-RA3 |5       36| RB3-O---- (Reserved) -(SPLIT - LED5)
;  XMIT_keyed_low_active----I-RA4 |6       35| RB2-I---- RX VFO Toggle(Pushbutton7)
;(Pushbutton1) MHZ UP-------I-RA5 |7       34| RB1-I---- Calibrate   (Pushbutton8)
;(Pushbutton2) MHZ DN-------I-RE0 |8       33| RB0/INT-I - ENC Int. (16F84-RB3)
;(Pushbutton3)FTToggle------I-RE1 |9       32| VDD------ +5v
;(Pushbutton4)-Debug--------I-RE2 |10      31| VSS------ GND
;                 +5v---------VDD |11      30| RD7-O---- Unused
;                 GND---------VSS |12      29| RD6-O---- DDS_clk  (AD9854 pin 21) 
;                XTAL--------OSC1 |13      28| RD5-O---- DDS_ioud (AD9854 pin 20) 
;                XTAL--------OSC2 |14      27| RD4-O---- DDS_data (AD9854 pin 19) 
;       (LED0) Debug -------O-RC0 |15      26| RC7-O/I-- LCD_D7--(LCD pin 14)
;  (LCD pin 6)-LCD_e -------O-RC1 |16      25| RC6-O/I-- LCD_D6--(LCD pin 13)
;  (LCD pin 5)-LCD_rw-------O-RC2 |17      24| RC5-O/I-- LCD_D5--(LCD pin 12)
;  (LCD pin 4)-LCD_rs-------O-RC3 |18      23| RC4-O/I-- LCD_D4--(LCD pin 11)
;       (LED1) Debug -------O-RD0 |19      22| RD3-O---- DDS_ioreset(AD9854 pin 17)   
;       (LED2) Debug -------O-RD1 |20      21| RD2-O---- DDS_mreset (AD9854 pin 71) 
;                                  ----------
;
;****************************************************************************
; * Device type and options. *
;****************************************************************************
;
        processor       PIC16F877
; include "p16f877.inc"   ; Include only the definitions that are needed 
        
        radix           dec

;
;==========================================================================
;
;       Configuration Bits
;
;==========================================================================
_CP_OFF           EQU     0x3FFF
_DEBUG_ON         EQU     0x37FF  ; In-circuit Debugger mode (RB6,RB7)
_DEBUG_OFF        EQU     0x3FFF
_LVP_OFF          EQU     0x3F7F  
_PWRTE_OFF        EQU     0x3FFF
_PWRTE_ON         EQU     0x3FF7
_WDT_ON           EQU     0x3FFF
_WDT_OFF          EQU     0x3FFB
_LP_OSC           EQU     0x3FFC
_XT_OSC           EQU     0x3FFD
_HS_OSC           EQU     0x3FFE
_RC_OSC           EQU     0x3FFF
;
    __config  _CP_OFF & _PWRTE_ON & _WDT_OFF & _HS_OSC & _DEBUG_OFF & _LVP_OFF
;
; Info for Power-on display on LCD
;
MCODE_REV_0 equ ' '      ; Current microcode revision level 5.17
MCODE_REV_1 equ '5'
MCODE_REV_2 equ '1'
MCODE_REV_3 equ '9'
MCODE_REV_4 equ 'b'
;
CLOCK_DISPLAY_0 equ '1'      ; Clock Speed = 125 MHz
CLOCK_DISPLAY_1 equ '2'
CLOCK_DISPLAY_2 equ '5'
;
ENCODER_DISPLAY_0 equ '1'    ; Optical Encoder - 128 positions
ENCODER_DISPLAY_1 equ '2'
ENCODER_DISPLAY_2 equ '8'
ENCODER_DISPLAY_0A equ ' '   ; Alternate - Mechanical Encoder - 24 pos.
ENCODER_DISPLAY_1A equ '2'
ENCODER_DISPLAY_2A equ '4'
;
;==========================================================================
;
;       Register Definitions
;
;==========================================================================

W       EQU     0x0000
F       EQU     0x0001
;
;----- Register Files------------------------------------------------------
;
;****************************************************************************
; * RAM page independent file registers:                                    *
;****************************************************************************
INDF                         EQU     0x0000
TMR0                         EQU     0x0001
PCL                          EQU     0x0002
STATUS                       EQU     0x0003
FSR                          EQU     0x0004
INTCON                       EQU     0x000B

;****************************************************************************
; * Bank 0 file registers:                                                  *
;****************************************************************************
PORTA                        EQU     0x0005
PORTB                        EQU     0x0006
PORTC                        EQU     0x0007
PORTD                        EQU     0x0008
PORTE                        EQU     0x0009
PCLATH                       EQU     0x000A
INTCON                       EQU     0x000B
;
;****************************************************************************
; * Bank 1 file registers:                                                  *
;****************************************************************************
OPTION_REG                   EQU     0x0081
TRISA                        EQU     0x0085
TRISB                        EQU     0x0086
TRISC                        EQU     0x0087
TRISD                        EQU     0x0088
TRISE                        EQU     0x0089
ADCON1                       EQU     0x009F
;
;****************************************************************************
; * Bank 2 file registers:                                                  *
;****************************************************************************
EEDATA                       EQU     0x010C
EEADR                        EQU     0x010D
;
;****************************************************************************
; * Bank 3 file registers:                                                  *
;****************************************************************************
EECON1                       EQU     0x018C
EECON2                       EQU     0x018D


;----- STATUS Bits  (All banks) ---------------------------------------------

IRP                          EQU     0x0007
RP1                          EQU     0x0006
RP0                          EQU     0x0005
;  RP1:RP0
;    1 1  = Bank 3
;    1 0  = Bank 2
;    0 1  = Bank 1
;    0 0  = Bank 0
Z                            EQU     0x0002
DC                           EQU     0x0001
C                            EQU     0x0000

;----- INTCON Bits --------------------------------------------------------
GIE                          EQU     0x0007   ; Global interrupt enable
INTE                         EQU     0x0004   ; RB0 external interrupt enable
INTF                         EQU     0x0001   ; RB0 external interrupt bit

;----- OPTION_REG Bits -----------------------------------------------------
NOT_RBPU                     EQU     0x0007

;----- EECON1 Bits --------------------------------------------------------
EEPGD                        EQU     0x0007
WRERR                        EQU     0x0003
WREN                         EQU     0x0002
WR                           EQU     0x0001
RD                           EQU     0x0000

;*****************************************************************************
; * General equates.  These may be changed to accommodate the reference clock*
; * frequency, the desired upper frequency limit, and the default startup    *
; * frequency.                                                               *
;*****************************************************************************
;
; ref_osc represents the change in the frequency control word which results
; in a 1 Hz change in output frequency.  It is interpreted as a fixed point
; real number in the format <ref_osc_3>.<ref_osc_2><ref_osc_1><ref_osc_0>
; 
; NOTE: This is the same 32-bit value that was used to calculate the 32-bit
; frequency control word for the AD9850.  The AD9854 being used here requires
; a 48-bit frequency control word instead of 32. However, since 48-bit 
; accuracy is not necessary for this application, for performance reasons we 
; will continue to use the 32 bit value calculated here.  We will construct 
; the 48-bit frequency control word from this 32 bit value.  We will multiply 
; this 32-bit value by the desired output frequency (in Hz).  We will use 
; this 32-bit product as the most significant 32 bits of the 48-bit frequency 
; tuning word, and will use 16 bits of zeros for the lowest order 16 bits.
; 
; The values for common oscillator frequencies are as follows:
;
; Frequency    ref_osc_3    ref_osc_2    ref_osc_1    ref_osc_0
;
; 125.00 MHz     0x22         0x5C         0x17         0xD0
; 120.00 MHz     0x23         0xCA         0x98         0xCE
; 100.00 MHz     0x2A         0xF3         0x1D         0xC4
;  90.70 MHz     0x2F         0x5A         0x82         0x7A
;  66.66 MHz     0x40         0x6E         0x52         0xE7
;  66.00 MHz     0x41         0x13         0x44         0x5F
;  60.00 MHz     0x47         0x95         0x31         0x9C  
;  50.00 MHz     0x55         0xE6         0x3B         0x88
;
; To calculate other values:
;    ref_osc_3 = (2^32 / oscillator_freq_in_Hertz).
;    ref_osc_2, ref_osc_1, and ref_osc_0 are the fractional part of
;     (2^32 / oscillator_freq_in_Hertz) times 2^24.
;    Note:   2^32 = 4294967296 and 2^24 = 16777216
;
; For example, for a 125 MHz clock:
;    ref_osc_3 is (2^32 / 125 x 10^6) = 34.359738368 truncated to 34 (0x22)
;    ref_osc_2 is the high byte of (.359738368 x 2^24) = 6035408.303
;      6035408.303 = 0x5C17D0, so high byte is 5C.
;    ref_osc_1 is the next byte of 0x5C17D0, or 17
;    ref_osc_0 is the last byte of 0x5C17D0, or D0
;
; For example, for a 120 MHz clock:
;    ref_osc_3 is (2^32 / 120 x 10^6) = 35.791394133 truncated to 35 (0x23)
;    ref_osc_2 is the high byte of (.791394133 x 2^24) = 13277390.32
;      13277390.32 = 0xCA98CE, so high byte is CA.
;    ref_osc_1 is the next byte of 0xCA98CE, or 98
;    ref_osc_0 is the last byte of 0xCA98CE, or CE
;
; for 60 MHz clock:                                                
;    ref_osc_3 is (2^32 / 60 x 10^6) = 71.58278827 truncated to 71 (0x47)
;    ref_osc_2 is the high byte of (.58278827 x 2^24) = 9777564.688
;      0999564.688 = 0x95319C, so high byte is 0x95
;    ref_osc_1 is the next byte of 0x95319C, or 0x31  
;    ref_osc_0 is the next byte of 0x95319C, or 0x9C
;
;
;==== Currently set for 125 MHz Oscillator =======                
;
ref_osc_3   equ 0x22              ; Most significant osc byte    
ref_osc_2   equ 0x5C              ; Next byte                    
ref_osc_1   equ 0x17              ; Next byte                    
ref_osc_0   equ 0xD0              ; Least significant byte       
;
; Limit contains the upper limit frequency as a 32 bit integer.
; This should not be set to more than one third of the reference oscillator
; frequency.  The output filter of the DDS board must be designed to pass
; frequencies up to the maximum.
;
;  Set up upper frequency limit = 30,000,000 = 0x01C9C380
limit_3   equ 0x01                ; Most significant byte for 30 MHz
limit_2   equ 0xC9                ; Next byte
limit_1   equ 0xC3                ; Next byte
limit_0   equ 0x80                ; Least significant byte
;
; Default contains the default startup frequency as a 32 bit integer.
;
default_3 equ 0x00                ; Most significant byte for 14.025 MHz
default_2 equ 0xD6                ; Next byte
default_1 equ 0x01                ; Next byte
default_0 equ 0x28                ; Least significant byte
;
band_end equ    0x28              ; The offset to the last band table entry
;
; The following definitions are used to drive the AD9854.
;
freq_write_cmd equ 0x02 ; The command to write to the freq-1 words
;freq_write_2_cmd equ 0x03 ; The command to write to the freq-2 words (NOT USED)
CR_write_cmd equ 0x07 ; The command to write to the CR registers
CR_read_cmd equ 0x87  ; The command to read from the CR registers
CR_byte_0  equ 0x10   ; The most significant CR byte (0x1D)           (COMP PD)
CR_byte_1  equ 0x20   ; The second CR byte (0x1E)                    (BYPASS PLL) 
CR_byte_2  equ 0x00   ; The third CR byte (0x1F)  (NO SRC QDAC +  EXTERNAL UPDATE)
CR_byte_3  equ 0x40   ; The least significant CR byte (0x20)  (BYPASS INV SINC)
;
;
; Equates for VFOcontrol register
;
VFOA        equ   0     ; Bit indicates VFO A active (for Receive)
VFOB        equ   1     ; Bit indicates VFO B active (for Receive)
SPLIT       equ   2     ; Bit indicates SPLIT operation active
TRANSMIT    equ   3     ; Bit indicates TRANSMIT active (use other VFO if SPLIT)
ALTERNATE   equ   4     ; Bit indicates Alternate Encoder is selected
NEEDUPDATE  equ   5     ; A change has been made, so need DDS and LCD update
FAST_TUNING equ   6     ; Bit indicates fast tuning
DIR_UP      equ   7     ; Encoder UP direction (decode of ENC3 - for encdir)
;
;
; Equates for DebugControl register  - Triggered by pushbutton 4
DDS_init_loop equ 0     ; Bit indicates DDS Init should loop 
;
;****************************************************************************
; * ID location information:                                                *
; * (MPASM warns about DW here, don't worry)                                *
;****************************************************************************
;
        ORG     0x2000  
        DATA    0x000F
        DATA    0x000F
        DATA    0x000F
        DATA    0x000F
;
;
;****************************************************************************
; * Setup the initial constant, based on the frequency of the reference     *
; * oscillator.  This can be tweaked with the calibrate function.           *
;****************************************************************************
;
        ORG     0x2100
;
; ref osc data starts at address 0 of EEPROM
        DATA    ref_osc_0
        DATA    ref_osc_1
        DATA    ref_osc_2
        DATA    ref_osc_3

;       Clear 252 unused EEPROM bytes 
;       (16F877 has 256 bytes)
;
        DATA    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
        DATA    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
        DATA    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
        DATA    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
        DATA    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
        DATA    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
        DATA    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
        DATA    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
        DATA    0,0,0,0,0,0,0,0,0,0,0,0
;
;
;
;****************************************************************************
; * Assign names to IO pins.                                                *
;
;   INPUT = 1   Output = 0    Bit Order: 7 6 5 4 3 2 1 0 
;****************************************************************************
;
;
;   A register bits:
;   TRISA - 0x03F
;
ENC3        equ     0x00          ; Input (from 16F84) (Direction) 
ENC2        equ     0x01          ; Input (from 16F84)         
ENC1        equ     0x02          ; Input (from 16F84)         
ENC0        equ     0x03          ; Input (from 16F84)         
XMIT_Keyed_Low_Active equ 0x04    ; Input - Transmitter Keyed (Low Active)
PBMHZUP     equ     0x05          ; Input - Pushbutton - MHZ UP
;                   0x06 - Does not exist in 16F877
;                   0x07 - Does not exist in 16F877
;
;   B register bits:
;   TRISB - 0x37
;
INTIN       equ     0x00          ; Input  - Interrupt from 16F84
PBCALIBRATE equ     0x01          ; Input  - Pushbutton - Calibrate mode 
PBVFOTOGGLE equ     0x02          ; Input  - Pushbutton - Receive VFO toggle
LEDSPLIT    equ     0x03          ; Output - LED5 - Split VFO operation 
PBSPLIT     equ     0x04          ; Input  - Pushbutton - Split operation select
PBVFOAEQB   equ     0x05          ; Input  - Pushbutton - Match other VFO to RCV VFO 
LEDRECVFOB  equ     0x06          ; Output - LED4 - Receive uses VFO B
LEDRECVFOA  equ     0x07          ; Output - LED3 - Receive uses VFO A
;
;   C register bits:
;   TRISC - 0x00
;
LED0        equ     0x00          ; Output - LED 0  (debug)
LCD_e       equ     0x01          ; Output 0=disable, 1=enable   (LCD pin 6) 
LCD_rw      equ     0x02          ; Output 0=write, 1=read       (LCD pin 5) 
LCD_rs      equ     0x03          ; Output 0=instruction, 1=data (LCD pin 4)
LCD_D4      equ     0x04          ; Output /(input status) Data  (LCD pin 11)
LCD_D5      equ     0x05          ; Output /(input status) Data  (LCD pin 12)
LCD_D6      equ     0x06          ; Output /(input status) Data  (LCD pin 13)
LCD_D7      equ     0x07          ; Output /(input status) Data  (LCD pin 14)
;
;   D register bits:
;   TRISD - 0x00
;
LED1        equ     0x00          ; Output - LED 1 (debug)   
LED2        equ     0x01          ; Output - LED 2 (debug)   
DDS_mreset  equ     0x02          ; Output - AD9854 master reset (AD9854 pin 71)    
DDS_ioreset equ     0x03          ; Output - AD9854 I/O reset    (AD9854 pin 17)
DDS_data    equ     0x04          ; Output - AD9854 serial data SDIO(AD9854 pin 19)
DDS_ioud    equ     0x05          ; Output - AD9854 IO Update   IOUD(AD9854 pin 20)
DDS_clk     equ     0x06          ; Output - AD9854 write clock  (AD9854 pin 21)    
;                   0x07          ; Unused
;
;   E register bits:
;   TRISE - 0x03
;
PBMHZDN     equ     0x00          ; Input - Pushbutton - MHZ DOWN
PBFTTOGGLE  equ     0x01          ; Input - Pushbutton - Fast tuning toggle
DEBUG       equ     0x02          ; Input - Pushbutton - Debug active toggle
;                   0x03 - Does not exist in 16F877
;                   0x04 - Does not exist in 16F877
;                   0x05 - Does not exist in 16F877
;                   0x06 - Does not exist in 16F877
;                   0x07 - Does not exist in 16F877
;
;****************************************************************************
; *           Allocate variables in general purpose register space          *
;****************************************************************************
;
        CBLOCK  0x20              ; Start Bank 0 Data Block (16F877)
;
        code_level_1              ; Microcode level - byte 1
        code_level_2              ; Microcode level - byte 2
        W_Save                    ; Save W (interrupt routine)
        Status_Save               ; Save Status (interrupt routine)
        FSR_Save                  ; Save FSR (interrupt routine)
        PCLATH_Save               ; Save PCLATH (interrupt routine)
        encindex                  ; Encoder - change index              
        tuning_index              ; Index for fstep table lookup
        VFOcontrol                ; Specifies active Receive VFO and if split
         ; VFOA        equ  0     ; Bit indicates VFO A active (for Receive)
         ; VFOB        equ  1     ; Bit indicates VFO B active (for Receive)
         ; SPLIT       equ  2     ; Bit indicates SPLIT operation active
         ; TRANSMIT    equ  3     ; Bit indicates TRANSMIT active (use other VFO if SPLIT)
         ; ALTERNATE   equ  4     ; Bit indicates Alternate Encoder is used. 
         ; NEEDUPDATE  equ  5     ; A change has been made, so need DDS and LCD update
         ; FAST_TUNING equ  6     ; Bit indicates fast tuning
         ; DIR_UP      equ  7     ; Bit indicates Tuning Direction UP
        DebugControl              ;
         ; DDS_init_loop equ  0   ; Bit indicates DDS initialization loop active
        freq_0                    ; VFO frequency - temp (hex) (4 bytes)
          freq_1                  ;   freq_0 is the least significant byte,
          freq_2                  ;  
          freq_3                  ;   freq_3 is the most significant byte.
        Afreq_0                   ; VFO A frequency (hex) (4 bytes)         
          Afreq_1                 ;   Afreq_0 is the least significant byte,
          Afreq_2                 ;                                         
          Afreq_3                 ;   Afreq_3 is the most significant byte. 
        Bfreq_0                   ; VFO B frequency (hex) (4 bytes)         
          Bfreq_1                 ;   Bfreq_0 is the least significant byte,
          Bfreq_2                 ;                                         
          Bfreq_3                 ;   Bfreq_3 is the most significant byte. 
        workfreq_0                ; Working registers for VFO updates (4 bytes)         
          workfreq_1              ;   workfreq_0 is the least significant byte,
          workfreq_2              ;                                         
          workfreq_3              ;   workfreq_3 is the most significant byte. 
        BCD_0                     ; Display frequency (BCD) (5 bytes)
          BCD_1                   ;            
          BCD_2                   ;
          BCD_3                   ;
          BCD_4                   ;
        AD9854_0                  ; AD9854 control word (5 bytes)
          AD9854_1                ;            
          AD9854_2                ;
          AD9854_3                ;
          AD9854_4                ;
        fstep_0                   ; Frequency inc/dec (4 bytes)
          fstep_1                 ;           
          fstep_2                 ;
          fstep_3                 ;
        BCD_count                 ; Used in bin2BCD routine
        BCD_temp                  ;   "   "    "       "
        mult_count                ; Used in calc_dds_word
        bit_count                 ;   "   "   "     "
        byte2send                 ;
        osc_0                     ; Current oscillator (4 bytes)
          osc_1                   ;            
          osc_2                   ;
          osc_3                   ;
        osc_temp_0                ; Oscillator frequency (4 bytes)
          osc_temp_1              ;           
          osc_temp_2              ;
          osc_temp_3              ;
        LCD_char                  ; Character being sent to the LCD
        LCD_nibble                ; Rearranged MS nibble for the LCD   
        LCD_read                  ; Character read from the LCD
        timer1                    ; Used in delay routines
        timer2                    ;   "   "   "      "
        count                     ; loop counter  (gets reused)
        band                      ; Used to index a table of frequencies
        rs_value                  ; The LCD rs line flag value
        status_save               ; Save status during interrupt processing
        int_stack_count           ; Count of Interrupts on Stack
        CR_work                   ; Work register for constructing CR byte
        CR_in0                    ; Read first CR byte from AD9854
        CR_in1                    ; Read second CR byte from AD9854
        CR_in2                    ; Read third CR byte from AD9854
        CR_in3                    ; Read fourth CR byte from AD9854
        button5set                ; pb 5 is pushed - possibly want alt encoder
        RememberLED0              ; Save state of LED0 in LCD-busy routine
        ENDC                      ; End of Bank 0 Data Block
;
;       ***************************
        CBLOCK  0xA0              ; Start Bank 1 Dummy Data Block (16F877)
        W_Save_bank1_reserve      ; Save W (interrupt routine)
        ENDC                      ; End of Data 1 Block
;
;       ***************************
        CBLOCK  0x120             ; Start Bank 2 Dummy Data Block (16F877)
        W_Save_bank2_reserve      ; Save W (interrupt routine)
        ENDC                      ; End of Data 2 Block
;
;       ***************************
        CBLOCK  0x1A0             ; Start Bank 3 Dummy Data Block (16F877)
        W_Save_bank3_reserve      ; Save W (interrupt routine)
        ENDC                      ; End of Data 3 Block
;
;****************************************************************************
;****************************************************************************
; >>>>>>>>>>>>>>>>>> RESET AND INTERRUPT VECTORS <<<<<<<<<<<<<<<<<<<<<<<<<<<<
;****************************************************************************
;****************************************************************************
;
;****************************************************************************
; * RESET VECTOR (16F877 resets to 0x00)                                    *
;****************************************************************************
        ORG     0x0000
reset_entry
        nop                       ; NOP required here for MPASM-ICD!
        goto    start             ; Jump around the band table to main
;****************************************************************************
; * INTERRUPT VECTOR (at 0x04)                                              *
;****************************************************************************
        ORG     0x0004 
interrupt_entry
        goto    interrupt_handler ; Jump to interrupt handler
;
;****************************************************************************
; * This is the band table.  Each entry is four instructions long, with each*
; * group of four literals representing the frequency as a 32 bit integer.  *
; * New entries can be added to the end of the table or between existing    *
; * entries.  The constant band_end must be incremented by 4 for each entry *
; * added.                                                                  *
; *                                                                         *
; * This table is placed near the top of the program to allow as large a    *
; * a table as possible to be indexed with the eight bit value in W.        *
; *                                                                         *
;****************************************************************************
;
band_table
        addwf   PCL,f             ;
        retlw   0x00              ; 0 Hz
        retlw   0x00              ;
        retlw   0x00              ;
        retlw   0x00              ;
        retlw   0x00              ; 160 meters
        retlw   0x1B              ;
        retlw   0x77              ;
        retlw   0x40              ;
        retlw   0x00              ; 80 meters
        retlw   0x35              ;
        retlw   0x67              ;
        retlw   0xE0              ;
        retlw   0x00              ; 40 meters
        retlw   0x6A              ;
        retlw   0xCF              ;
        retlw   0xC0              ;
        retlw   0x00              ; 30 meters
        retlw   0x9A              ;
        retlw   0x1D              ;
        retlw   0x20              ;
        retlw   0x00              ; 20 meters
        retlw   0xD5              ;
        retlw   0x9F              ;
        retlw   0x80              ;
        retlw   0x01              ; 17 meters
        retlw   0x13              ;
        retlw   0xB2              ;
        retlw   0x20              ;
        retlw   0x01              ; 15 meters
        retlw   0x40              ;
        retlw   0x6F              ;
        retlw   0x40              ;
        retlw   0x01              ; 12 meters
        retlw   0x7B              ;
        retlw   0xCA              ;
        retlw   0x90              ;
        retlw   0x01              ; 10 meters
        retlw   0xAB              ;
        retlw   0x3F              ;
        retlw   0x00              ;
        retlw   0x01              ; 30 MHz
        retlw   0xC9              ;
        retlw   0xC3              ;
        retlw   0x80              ;
;
; =======================================================
fstep_table   ;  Based on "Encshift"
       addwf     PCL,f            ; 
;
; OPTICAL ENCODER
; Slow tuning table
       retlw     0x00             ; 0 (1)
       retlw     0x00             ;
       retlw     0x00             ;
       retlw     0x01             ;
; -----------------------------------------
       retlw     0x00             ; 1 (2)       
       retlw     0x00             ;
       retlw     0x00             ;
       retlw     0x02             ;
; -----------------------------------------
       retlw     0x00             ; 2 (5)
       retlw     0x00             ;
       retlw     0x00             ;
       retlw     0x05             ;
; -----------------------------------------
       retlw     0x00             ; 3 (10)
       retlw     0x00             ;
       retlw     0x00             ;
       retlw     0x0A             ;
; -----------------------------------------
       retlw     0x00             ; 4 (100)
       retlw     0x00             ;
       retlw     0x00             ;
       retlw     0x64             ;
; -----------------------------------------
       retlw     0x00             ; 5 (300)
       retlw     0x00             ;
       retlw     0x01             ;
       retlw     0x2C             ;
; -----------------------------------------
       retlw     0x00             ; 6 (1000)
       retlw     0x00             ;
       retlw     0x03             ;
       retlw     0xE8             ;
; -----------------------------------------
       retlw     0x00             ; 7 (5000)
       retlw     0x00             ;
       retlw     0x13             ;
       retlw     0x88             ;
;
; -----------------------------------------
; OPTICAL ENCODER
; Fast tuning table
;
       retlw     0x00             ; 0 (1)    
       retlw     0x00             ;
       retlw     0x00             ;
       retlw     0x01             ;
; -----------------------------------------
       retlw     0x00             ; 1 (2)   
       retlw     0x00             ;
       retlw     0x00             ;
       retlw     0x02             ;
; -----------------------------------------
       retlw     0x00             ; 2 (10)
       retlw     0x00             ;
       retlw     0x00             ;
       retlw     0x0A             ;
; -----------------------------------------
       retlw     0x00             ; 3 (60)
       retlw     0x00             ;
       retlw     0x00             ;
       retlw     0x3C             ;
; -----------------------------------------
       retlw     0x00             ; 4 (600)
       retlw     0x00             ;
       retlw     0x02             ;
       retlw     0x58             ;
; -----------------------------------------
       retlw     0x00             ; 5 (1800)
       retlw     0x00             ;
       retlw     0x07             ;
       retlw     0x08             ;
; -----------------------------------------
       retlw     0x00             ; 6 (6000)
       retlw     0x00             ;
       retlw     0x17             ;
       retlw     0x70             ;
; -----------------------------------------
       retlw     0x00             ; 7 (30000)
       retlw     0x00             ;
       retlw     0x75             ;
       retlw     0x30             ;
; -----------------------------------------
;
; >>>>>>>> ALTERNATE ENCODER TABLE HERE <<<<<<
; MECHANICAL ENCODER
; Slow tuning table
;
       retlw     0x00             ; 0 (1)
       retlw     0x00             ;
       retlw     0x00             ;
       retlw     0x01             ;
; -----------------------------------------
       retlw     0x00             ; 1 (5)       
       retlw     0x00             ;
       retlw     0x00             ;
       retlw     0x05             ;
; -----------------------------------------
       retlw     0x00             ; 2 (25)
       retlw     0x00             ;
       retlw     0x00             ;
       retlw     0x19             ;
; -----------------------------------------
       retlw     0x00             ; 3 (50)
       retlw     0x00             ;
       retlw     0x00             ;
       retlw     0x32             ;
; -----------------------------------------
       retlw     0x00             ; 4 (500)
       retlw     0x00             ;
       retlw     0x01             ;
       retlw     0xF4             ;
; -----------------------------------------
       retlw     0x00             ; 5 (1500)
       retlw     0x00             ;
       retlw     0x05             ;
       retlw     0xDC             ;
; -----------------------------------------
       retlw     0x00             ; 6 (5000)
       retlw     0x00             ;
       retlw     0x13             ;
       retlw     0x88             ;
; -----------------------------------------
       retlw     0x00             ; 7 (25000)
       retlw     0x00             ;
       retlw     0x61             ;
       retlw     0xA8             ;

; -----------------------------------------
; MECHANICAL ENCODER
; Fast tuning table
;
       retlw     0x00             ; 0 (1)    
       retlw     0x00             ;
       retlw     0x00             ;
       retlw     0x01             ;
; -----------------------------------------
       retlw     0x00             ; 1 (7)   
       retlw     0x00             ;
       retlw     0x00             ;
       retlw     0x07             ;
; -----------------------------------------
       retlw     0x00             ; 2 (50)
       retlw     0x00             ;
       retlw     0x00             ;
       retlw     0x32             ;
; -----------------------------------------
       retlw     0x00             ; 3 (300)
       retlw     0x00             ;
       retlw     0x01             ;
       retlw     0x2C             ;
; -----------------------------------------
       retlw     0x00             ; 4 (3000)
       retlw     0x00             ;
       retlw     0x0B             ;
       retlw     0xB8             ;
; -----------------------------------------
       retlw     0x00             ; 5 (9000)
       retlw     0x00             ;
       retlw     0x23             ;
       retlw     0x28             ;
; -----------------------------------------
       retlw     0x00             ; 6 (30000)
       retlw     0x00             ;
       retlw     0x75             ;
       retlw     0x30             ;
; -----------------------------------------
       retlw     0x00             ; 7 (150000)
       retlw     0x02             ;
       retlw     0x49             ;
       retlw     0xF0             ;
; -----------------------------------------
;
;*****************************************************************************
;*****************************************************************************
; >>>>>>>>>>>>>>>>>>>>>>>> START AND MAIN <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
;*****************************************************************************
;*****************************************************************************
;
;*****************************************************************************
; *                                                                          *
; * Purpose:  This is the start of the program.  It initializes the LCD.     *
; *           It sets the power-on frequency and enters the main loop.       *
; *                                                                          *
; *   Input:  The start up frequency is defined in the default_3 ...         *
; *           definitions above, and relies on the reference oscillator      *
; *           constant defined in ref_osc_3 ... ref_osc_0.                   *
; *                                                                          *
; *  Output:  PIC, LCD, and DDS ready for normal VFO operation.              *
; *                                                                          *
;*****************************************************************************
; 
start
        call    init_PIC          ; Initialize the 16F877
        call    init_LCD          ; Initialize the LCD
        call    display_mcode_version ; Display microcode version on LCD for 2 sec.
        call    init_DDS          ; Initialize the AD9854
        call    init_freq         ; Initialize default frequency and registers
        call    allow_interrupts  ; Now allow interrupts
;
;       Display the power on frequency.
;
        call    bin2BCD           ; Convert it to BCD
        call    show_freq         ; Display it
;
;       Send power on frequency to the DDS chip.
;
        call    calc_dds_word     ; Convert to delta value
        call    send_dds_freq     ; Send the power-on frequency to the
                                  ;   AD9854 in serial mode
;
; Fall into the Main Program Loop
;
;*****************************************************************************
; *                                                                          *
; * Purpose:  This is the main program loop.                                 *
; *                                                                          *
; *           1) Check to see if frequency encoder knob has been turned      *
; *                If so, set NEEDUPDATE flag                                *
; *           2) Check for input from pushbuttons                            *
; *                If so, perform appropriate action, set NEEDUPDATE flag    *
; *           3) Check for input from keypad (future)                        *
; *                If so, perform appropriate action, set NEEDUPDATE flag    *
; *           4) Check for transmitter active                                *
; *                If so, set flag for possible frequency shift              *
; *           5) If NEEDUPDATE flag set, call routine to update DDS and LCD  *
; *                                                                          *
; *   Input:  None.                                                          *
; *                                                                          *
; *  Output:  None.                                                          *
; *                                                                          *
;*****************************************************************************
;
main
        call    encoder_look      ; Check for knob movement 
        call    buttons_look      ; Check for pushbuttons input 
        call    transmitter_active_look ; See if transmitter is active
        btfsc   VFOcontrol,NEEDUPDATE ; Need a DDS and LCD update? 
        call    update_DDS_LCD    ; Yes, update the DDS and LCD
        goto    main              ; Loop back
;
;*****************************************************************************
;*****************************************************************************
; >>>>>>>>>>>>>>>>>>>> INTERRUPT HANDLER <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
;*****************************************************************************
;*****************************************************************************
;
;*****************************************************************************
; * Purpose:  This interrupt handling routine is called when an interrupt    *
; *           at RB0 is detected.  Called from interrupt vector at 0x04.     *
; *                                                                          *
; *   Upon interrupt:                                                        *
; *    1) Save environment                                                   *
; *    2) Process encoder change - amount and direction                      *
; *    3) Restore saved environment                                          *
; *    4) Return to interrupted code                                         *
; *                                                                          *
;*****************************************************************************
; 
interrupt_handler
;
        movwf   W_Save            ; Copy W to save register IN CURRENT BANK!
        swapf   STATUS,w          ; Swap nibbles of STATUS, and move into W
        clrf    STATUS            ; Change to bank 0, regardless of current bank
                                  ;   (Clears IRP, RP1, RP0)
        movwf   Status_Save       ; Save status (in bank 0 status save location)
        movf    PCLATH,w          ; Move PCLATH into W
        movwf   PCLATH_Save       ;   and save it
        clrf    PCLATH            ; Change to page 0, regardless of current page
        movf    FSR,w             ; Move FSR into W
        movwf   FSR_Save          ;   and save it
;
; Start ISR
;        
        incf    int_stack_count,f ; Increment interrupt stack count
;
; Determine encoder direction - save in encdir
;
        bcf     INTCON,INTF       ; Clear RB0 interrupt 
        bsf     VFOcontrol,DIR_UP ; Set direction to go UP (default)
        btfss   PORTA,ENC3        ; Is direction bit set? (indicating UP)
        bcf     VFOcontrol,DIR_UP ; No, clear UP direction bit (go DN)
        clrw                      ; Clear w 
;
; Now set up encoder acceleration amount - encshift
;
        movlw   0x00              ; Clear w
        btfsc   PORTA,ENC0        ; Is low order bit set? 
        addlw   0x01              ; Yes, add 1
        btfsc   PORTA,ENC1        ; Is next higher bit set? 
        addlw   0x02              ; Yes, add 2
        btfsc   PORTA,ENC2        ; Is next higher bit set? 
        addlw   0x04              ; Yes, add 4
        movwf   encindex          ; Store in encoder index       
;
; End  ISR Code. 
; Now restore to saved state
;
        movf    PCLATH_Save,w     ; Restore saved PCLATH into W
        movwf   PCLATH            ;   and move W into PCLATH
;
        movf    FSR_Save,w        ; Restore saved FSR into W
        movwf   FSR               ;   and move W into FSR
;
        swapf   Status_Save,w     ; Swap status save nibbles, and move into W
        movwf   STATUS            ; Move W into STATUS register
                                  ;   (Bank now in original state)
;
        swapf   W_Save,f          ; Swap nibbles within W_Save 
                                  ;  (for next swap instruction)
        swapf   W_Save,w          ; Swap nibbles of W_Save, and move into W
;        
        retfie
;       
;*****************************************************************************
;*****************************************************************************
; >>>>>>>>>>>>> ENCODER AND FREQUENCY CHANGE ROUTINES <<<<<<<<<<<<<<<<<<<<<<<<
;*****************************************************************************
;*****************************************************************************
;
;*****************************************************************************
; *                                                                          *
; * Purpose:  This routine examines the signals sent from the encoder PIC    *
; *             and determines whether or not a change has occurred.         *
; *           If no change has occurred (no interrupt has occurred), it will *  
; *             return to the caller.                                        *
; *           If a change has occurred (an interrupt was received), this     *
; *             routine will determine the direction and the amount of       *
; *             change.                                                      *
; *           It will increment or decrement the frequency displayed on the  *
; *             the LCD and send the updated frequency to the DDS. Then it   *
; *             will return to the caller.                                   *
; *                                                                          *
; *   Input:                                                                 *
; *                                                                          *
; *  Output:                                                                 *
; *                                                                          *
;*****************************************************************************
;
encoder_look
;
        movf    int_stack_count,w ; Get interrupt count
        btfsc   STATUS,Z          ; Is count zero?
        goto    enclook_ret       ; Yes, return
;
; An interrupt occurred.  
;
; We should have only received one interrupt and will process it here.  
; If more than one, we lost the encshift count!   Detect it here with breakpoints.
        decf    int_stack_count,f ; Decrement interrupt stack count
        btfsc   STATUS,Z          ; Is count zero now? 
        goto    encoder_none_lost ; If so, we didn't lose any               
;
; Start Debug - See on scope if we are losing any interrupts
;       bsf     PORTD,7           ; Lost one.  Set pulse to see on scope
;       nop
;       nop
;       nop 
;       bsf     PORTD,7           ; Clear pulse 
; End Debug
;
        clrf    int_stack_count   ; Clear count
encoder_none_lost
;      
; Set encoder acceleration LEDs from encindex (set up by interrupt handler)
;
        bcf     PORTC,LED0        ; Start with the three LEDs cleared
        bcf     PORTD,LED1        ;
        bcf     PORTD,LED2        ;
        btfsc   encindex,0        ; Is bit 0 set?
        bsf     PORTC,LED0        ; Yes, set LED0
        btfsc   encindex,1        ; Is bit 1 set?
        bsf     PORTD,LED1        ; Yes, set LED1
        btfsc   encindex,2        ; Is bit 2 set?
        bsf     PORTD,LED2        ; Yes, set LED2
;
; START DEBUG   See how hard it is to get all 3 bits set. 
;
;       btfss   encindex,0        ; Is bit 0 set?
;       goto    notall            ; No, notall
;       btfss   encindex,1        ; Is bit 1 set?
;       goto    notall            ; No, notall
;       btfss   encindex,2        ; Is bit 2 set?
;       goto    notall            ; No, notall
;       bsf     PORTD,7           ; Set debug bit (see on scope)
;       nop
;       nop
;       nop
;       bcf     PORTD,7           ; Clear debug bit
;notall
; END DEBUG
;
; Now calculate fstep amount based on encshift
; 
; Optical Encoder 
;        ENC
;       2 1 0   ENCINDEX   FSTEP - SLOW     FSTEP - FAST
;       -----   -------    --------------   --------------
;       0 0 0      0            1               1
;       0 0 1      1            2               2
;       0 1 0      2            5              10
;       0 1 1      3           10              60
;       1 0 0      4          100             600
;       1 0 1      5          300            1800
;       1 1 0      6         1000            6000
;       1 1 1      7         5000           30000
;
; Mechanical Encoder 
;        ENC
;       2 1 0   ENCINDEX   FSTEP - SLOW     FSTEP - FAST
;       -----   -------    --------------   --------------
;       0 0 0      0            1               1
;       0 0 1      1            5               7
;       0 1 0      2           25              50
;       0 1 1      3           50             300
;       1 0 0      4          500            3000
;       1 0 1      5         1500            9000
;       1 1 0      6         5000           30000
;       1 1 1      7        25000          150000


; Set up fstep (number of Hz to move)
;  - if Fast Tuning NOT selected (normal), use slow table
;  - if Fast Tuning selected, use fast table 
;
        rlf     encindex          ; Multiply by 4 for start of table
        rlf     encindex          ;   for this tuning digit
        movf    encindex,W        ; Default - slow tuning
        btfsc   VFOcontrol,FAST_TUNING ; Fast tuning selected?
        addlw   0x20              ; yes, inc past slow tuning table
        btfsc   VFOcontrol,ALTERNATE ; ALTERNATE encoder?
        addlw   0x40              ; yes, increment to correct table
        movwf   tuning_index      ; Save the index of the high byte    
        call    fstep_table       ; Get high byte value into W
        movwf   fstep_3           ; Save in fstep_3
        incf    tuning_index,f    ; Increment index to next byte
        movf    tuning_index,w    ; Get next index into W
        call    fstep_table       ; Get next value into W
        movwf   fstep_2           ; Save in fstep_2
        incf    tuning_index,f    ; Increment index to next byte
        movf    tuning_index,w    ; Get next index into W
        call    fstep_table       ; Get next value into W
        movwf   fstep_1           ; Save in fstep_1
        incf    tuning_index,f    ; Increment index to next byte
        movf    tuning_index,w    ; Get next index into W
        call    fstep_table       ; Get next value into W
        movwf   fstep_0           ; Save in fstep_0
; 
; Get current frequency to be updated from selected VFO (Afreq or Bfreq)
;    and load into <workfreq_0:workfreq_3> 
; 
        call    VFO_freq_load      ; Load <workfreq_0:workfreq_3> with VFO regs
        btfss   VFOcontrol,DIR_UP  ; Is it UP direction?
        goto    need_sub           ; No, go subtract the step
        call    add_step           ; Yes, add step and update <workfreq_0:workfreq_3>
        goto    step_done          ; Back to common code
need_sub 
        call    sub_step           ; Subtract a step and update <workfreq_0:workfreq_3>
step_done
        call    check_add          ; See if band limits reached. If so, adjust, 
                                   ;   and update <workfreq_0:workfreq_3> 
        call    VFO_freq_save      ; Save updated <workfreq_0:workfreq_3> into
                                   ;   selected VFO freq area (Afreq or Bfreq)
;
; Now set flag to indicate need to update DDS and LCD
;
        bsf     VFOcontrol,NEEDUPDATE ; Say we need a DDS and LCD update
;
enclook_ret
;
        return                    ; Return
;
;*****************************************************************************
; *                                                                          *
; * Purpose:  This routine will determine if there are any pushbuttons       *
; *           pressed.  If so, this routine will perform the required action.*
; *           NOTE: PUSHBUTTONS ARE LOW ACTIVE                               *
; *                                                                          *
; *   Input:                                                                 *
; *                                                                          *
; *  Output:                                                                 *
; *                                                                          *
;*****************************************************************************
;
buttons_look
;
buttons_look_1
;
; Look for A=B button. (Don't need DDS update for this one) 
;
        btfsc   PORTB,PBVFOAEQB   ; Is pushbuttonset pressed for A=B ?
        goto    buttons_1_done    ; No, done with button
; 
; Button A=B is pressed. First find active VFO and set this into other VFO.
;
        call    wait_32ms         ; Debounce press of button       (v5.19a) 

        btfsc   VFOcontrol,VFOA   ; Is VFO A selected now?
        goto    buttons_setAtoB   ; No, B is selected so set A to B
        movf    Bfreq_0,w         ; Get VFO B value
        movwf   Afreq_0           ;   and store in VFO A
        movf    Bfreq_1,w         ; Get VFO B value
        movwf   Afreq_1           ;    and store in VFO A
        movf    Bfreq_2,w         ; Get VFO B value
        movwf   Afreq_2           ;   and store in VFO A
        movf    Bfreq_3,w         ; Get VFO B value
        movwf   Afreq_3           ;   and store in VFO A
        goto    buttons_1_wait    ; Done
buttons_setAtoB
        movf    Afreq_0,w         ;  Get VFO A value
        movwf   Bfreq_0           ;   and store in VFO B
        movf    Afreq_1,w         ; Get VFO A value
        movwf   Bfreq_1           ;   and store in VFO B
        movf    Afreq_2,w         ; Get VFO A value
        movwf   Bfreq_2           ;   and store in VFO B
        movf    Afreq_3,w         ; Get VFO A value
        movwf   Bfreq_3           ;   and store in VFO B
;
buttons_1_wait
        btfss   PORTB,PBVFOAEQB   ; Is pushbutton released?
        goto    buttons_1_wait    ; No, wait for release
;
        call    wait_32ms         ; Debounce release of button     (v5.19a) 
;
buttons_1_done
;
; --------------------------------------------------------
buttons_look_2                    
;
; Look for SPLIT button. 
;
        btfsc   PORTB,PBSPLIT     ; Is pushbutton set for SPLIT ?
        goto    buttons_2_done    ; No, button done
;
        call    wait_32ms         ; Debounce press of button       (v5.19a) 

; SPLIT button is set.  Wait for release.
;
buttons_2_wait
        btfss   PORTB,PBSPLIT     ; Is pushbutton released?
        goto    buttons_2_wait    ; No, wait for release
;
        call    wait_32ms         ; Debounce release of button     (v5.19a) 
;
; See if SPLIT already set.  If so, this button press is to END SPLIT
;       
        btfss   VFOcontrol,SPLIT  ; Is SPLIT already set?
        goto    SetSplit          ; No, go to set SPLIT
        bcf     VFOcontrol,SPLIT  ; Yes, Clear SPLIT control flag
        bcf     PORTB,LEDSPLIT    ; Clear LED for SPLIT
        movlw   0xC0              ; Point LCD at digit 0 of 2nd line
        call    cmnd2LCD          ;
        movlw   ' '               ; Clear "SPLIT" on LCD
        call    data2LCD          ;
        movlw   ' '               ; 
        call    data2LCD          ;
        movlw   ' '               ; 
        call    data2LCD          ;
        movlw   ' '               ; 
        call    data2LCD          ;
        movlw   ' '               ; 
        call    data2LCD          ;
        goto    buttons_2_done    ; Done
SetSplit
        bsf     VFOcontrol,SPLIT  ; Button SPLIT is pressed.  Set control flag
        bsf     PORTB,LEDSPLIT    ; Turn on LED for SPLIT
;
        movlw   0xC0              ; Point LCD at digit 0 of 2nd line
        call    cmnd2LCD          ;
        movlw   'S'               ; Say "SPLIT" on LCD
        call    data2LCD          ;
        movlw   'P'               ; 
        call    data2LCD          ;
        movlw   'L'               ; 
        call    data2LCD          ;
        movlw   'I'               ; 
        call    data2LCD          ;
        movlw   'T'               ; 
        call    data2LCD          ;
;
buttons_2_done
;
; --------------------------------------------------------
buttons_look_3                    
;
        btfsc   PORTB,PBVFOTOGGLE ; Pushbutton asking for select OTHER VFO?
        goto    buttons_3_done    ; No, done with button
;
; Toggle pressed. Change VFO and update
;
        call    wait_32ms         ; Debounce press of button       (v5.19a) 
;
        btfss   VFOcontrol,VFOA   ; Is VFO A selected now?
        goto    buttons_select_VFOA ; No, B is selected, so go and select A
buttons_select_VFOB
        bsf     VFOcontrol,VFOB   ; Yes, select VFO B
        bcf     VFOcontrol,VFOA   ;    and clear VFO A
        bcf     PORTB,LEDRECVFOA  ; Turn off LED for VFO A
        bsf     PORTB,LEDRECVFOB  ; Turn on LED for VFO B
        movlw   0xCB              ; Point LCD at digit 11 of 2nd line
        call    cmnd2LCD          ;
        movlw   'V'               ; Say "VFO B"
        call    data2LCD          ;
        movlw   'F'               ; 
        call    data2LCD          ;
        movlw   'O'               ; 
        call    data2LCD          ;
        movlw   ' '               ; 
        call    data2LCD          ;
        movlw   'B'               ; 
        call    data2LCD          ;
        goto    toggle_done       ;   
buttons_select_VFOA
        bsf     VFOcontrol,VFOA   ; Select VFO A
        bcf     VFOcontrol,VFOB   ;   and clear VFO B
        bsf     PORTB,LEDRECVFOA  ; Turn on LED for VFO A
        bcf     PORTB,LEDRECVFOB  ; Turn off LED for VFO B
        movlw   0xCB              ; Point LCD at digit 11 of 2nd line
        call    cmnd2LCD          ;
        movlw   'V'               ; Say "VFO A"
        call    data2LCD          ;
        movlw   'F'               ; 
        call    data2LCD          ;
        movlw   'O'               ; 
        call    data2LCD          ;
        movlw   ' '               ; 
        call    data2LCD          ;
        movlw   'A'               ; 
        call    data2LCD          ;
toggle_done
        bsf     VFOcontrol,NEEDUPDATE; Say we need an update of DDS, LCD
;
buttons_3_wait
        btfss   PORTB,PBVFOTOGGLE ; Is pushbutton released?
        goto    buttons_3_wait    ; No, wait for release
;
        call    wait_32ms         ; Debounce release of button  (v5.19a) 
;
buttons_3_done
;
; --------------------------------------------------------
buttons_look_4
;
; Check for Calibrate mode pushbutton
;
        btfsc   PORTB,PBCALIBRATE ; Is pushbutton pressed for Calibrate?
        goto    buttons_4_done    ; No, done with button
;
; Calibrate pushbutton is set.
;
        call    wait_32ms         ; Debounce press of button       (v5.19a) 
;
buttons_4_wait
        btfss   PORTB,PBCALIBRATE ; Is pushbutton released?
        goto    buttons_4_wait    ; No, wait for release
;
        bcf     VFOcontrol,SPLIT  ; Make sure SPLIT control flag is CLEAR
        bcf     PORTB,LEDSPLIT    ; Clear LED for SPLIT too
;
        call    calibrate         ; Go adjust osc values.  Calibrate calls
                                  ; encoder_look to adjust. Calibrate keeps 
                                  ; looking for calibrate pushbutton to be 
                                  ; pressed again to release. Then it returns
                                  ; here, and calibrate is done.
;                                 ;
        bsf     VFOcontrol,NEEDUPDATE; Say we need an update of DDS, LCD
                                  ;      to use new osc values
;
        call    wait_32ms         ; Debounce release of button   (v5.19a) 
;
buttons_4_done
;
; --------------------------------------------------------
buttons_look_5
;
; Look for MHz UP button. 
;
        btfsc   PORTA,PBMHZUP     ; Is pushbutton pressed for MHz UP ?
        goto    buttons_5_done    ; No, done with button
;
        call    wait_32ms         ; Debounce press of button       (v5.19a) 
;
buttons_5_wait
        btfss   PORTA,PBMHZUP    ; Is pushbutton released?
        goto    buttons_5_wait    ; No, wait for release
;
        call    wait_32ms         ; Debounce release of button     (v5.19a) 
;
;
; MHz UP button is pressed
;   (1 MHz = 0xF4240)
;
        btfsc   VFOcontrol,TRANSMIT ; Now transmitting?
        goto    buttons_5_done    ; Yes, ignore the MH UP!
; 
        bsf     button5set,1      ; Possibly want alt encoder (if pb8 also pushed)
                                  ; This flag will be cleared if PB8 is not pushed.
;
        clrf    fstep_3           ;
        movlw   0x0F              ; Set up step of 1 M 
        movwf   fstep_2           ;
        movlw   0x42              ; 
        movwf   fstep_1           ; 
        movlw   0x40              ;
        movwf   fstep_0           ;
        call    VFO_freq_load     ; Load <workfreq_0:workfreq_3> with VFO regs
        call    add_step          ; Add a step and update <workfreq_0:workfreq_3>
        call    check_add         ; See if band limits reached. If so, adjust, 
                                  ;   and update <workfreq_0:workfreq_3> 
        call    VFO_freq_save     ; Save updated <workfreq_0:workfreq_3> into
                                  ;   selected VFO freq area (Afreq or Bfreq)
        bsf     VFOcontrol,NEEDUPDATE; Say we need an update of DDS, LCD
;
buttons_5_done 
; 
; --------------------------------------------------------
buttons_look_6
;
; Look for MHZ DN button. 
;
        btfsc   PORTE,PBMHZDN     ; Is pushbutton pressed for MHz DN ?
        goto    buttons_6_done    ; No, done with button
;
        call    wait_32ms         ; Debounce press of button       (v5.19a) 
;
buttons_6_wait
        btfss   PORTE,PBMHZDN     ; Is pushbutton released?
        goto    buttons_6_wait    ; No, wait for release
;
        call    wait_32ms         ; Debounce release of button    (v5.19a) 
;
; MHz DN button is pressed
;   (1 MHz = 0xF4240)
;
        btfsc   VFOcontrol,TRANSMIT ; Now transmitting?
        goto    buttons_6_done    ; Yes, ignore the MH DN!
;
        clrf    fstep_3           ;
        movlw   0x0F              ; Set up step of 1 M 
        movwf   fstep_2           ;
        movlw   0x42              ; 
        movwf   fstep_1           ; 
        movlw   0x40              ;
        movwf   fstep_0           ;
        call    VFO_freq_load     ; Load <workfreq_0:workfreq_3> with VFO regs
        call    sub_step          ; Subtract a step and update <workfreq_0:workfreq_3> 
        call    VFO_freq_save     ; Save updated <workfreq_0:workfreq_3> into          
                                  ;   selected VFO freq area (Afreq or Bfreq)
        bsf     VFOcontrol,NEEDUPDATE; Say we need an update of DDS, LCD
;
buttons_6_done
;
; --------------------------------------------------------
buttons_look_7
;
; Look for Fast Tuning Select button. 
;
        btfsc   PORTE,PBFTTOGGLE  ; Is pushbutton pressed for Fast Tuning TOGGLE ?
        goto    buttons_7_done    ; No, done with button
;
; Fast Tuning Select Toggle button is pressed
;
        call    wait_32ms         ; Debounce press of button       (v5.19a) 
;
; See if fast tuning already set?
;
        btfsc   VFOcontrol,FAST_TUNING ; Is Fast Tuning already set?
        goto    buttons_clear_fast_tuning ; Yes, go and clear it
        bsf     VFOcontrol,FAST_TUNING ; No, set Fast Tuning
        movlw   0x8F              ; Point LCD at digit 15 of 1st line
        call    cmnd2LCD          ;
        movlw   '+'               ; Indicate Fast Tuning with "+" (default)
        btfsc   VFOcontrol,ALTERNATE ; Is alternate encoder selected?
        movlw   '#'               ; Indicate Fast Tuning with "#" 
        call    data2LCD          ; Update LCD
        goto    buttons_7_wait    ;   and exit
buttons_clear_fast_tuning
        bcf     VFOcontrol,FAST_TUNING   ; Clear Fast tuning
        movlw   0x8F              ; Point LCD at digit 15 of 1st line
        call    cmnd2LCD          ;
        movlw   ' '               ; Clear Fast Tuning indicator
        call    data2LCD          ; Update LCD
buttons_7_wait
        btfss   PORTE,PBFTTOGGLE  ; Is pushbutton released?
        goto    buttons_7_wait    ; No, wait for release
;
        call    wait_32ms         ; Debounce release of button    (v5.19a) 
;
buttons_7_done
;
; --------------------------------------------------------
buttons_look_8                    ;
;
; Check for Debug pushbutton
; This button must be held down at power-up time (DDS_init) to be useful!
; Pressing button again will release debug mode. 
;
        btfsc   PORTE,DEBUG       ; Is pushbutton pressed for Debug?
        goto    buttons_8_done    ; No, done with button
;
; Debug pushbutton is pressed.
;
        call    wait_32ms         ; Debounce press of button       (v5.19a) 
;
        btfsc   button5set,1        ; Is button5 also pressed at this time?
        bsf     VFOcontrol,ALTERNATE ; Set Alternate encoder in use.
                                    ;  Only clear is via power down.  Too messy! 
; See if Debug mode already set?
;
        btfsc   DebugControl,DDS_init_loop ; Is DDS_init_loop already set?
        goto    buttons_clear_init_loop ; Yes, go and clear it
        bsf     DebugControl,DDS_init_loop ; No, set debug mode active
        goto    buttons_8_wait    ; 
buttons_clear_init_loop
        bcf     DebugControl,DDS_init_loop ; Clear DDS init loop active
buttons_8_wait
        btfss   PORTE,DEBUG       ; Is pushbutton released?
        goto    buttons_8_wait    ; No, wait for release
;
        call    wait_32ms         ; Debounce release of button    (v5.19a) 
;        
buttons_8_done
        clrf    button5set        ; Clear button-combo flag every time 
;
        return                    ; All buttons have been examined
;
;*****************************************************************************
; *                                                                          *
; * Purpose:  This routine will determine if the transmitter is active.      *
; *           It will set or clear TRANSMIT flag based on the input signal   *
; *           from transmitter keying circuitry.                             *
; *                                                                          *
; *           Why can't we just use the transmitter_keyed signal alone, and  *
; *             forget about setting/clearing TRANSMIT flag?                 *
; *           Because we need to control it and test the flag at several     * 
; *           places in the code.  We can't allow the transmit state to      *
; *           change while we are making decisions.                          *
; *                                                                          *
; *   Input:   A wire comming from the transmitter keying circuitry goes to  *
; *            an input pin (RA4) of the 16F877.                             *
; *             - This pin is set LOW when transmitting                      * 
; *             - This pin is set HIGH when not transmitting                 * 
; *                                                                          *
; *  Output:  TRANSMIT (in VFOcontrol) is set or clear, based on             *
; *             XMIT_Keyed_Low_Active being set or clear                     *
; *                                                                          *
;*****************************************************************************
;
transmitter_active_look
;
; Detect whether XMIT_keyed pin has just changed on this pass
;
        btfsc   PORTA,XMIT_Keyed_Low_Active
        goto    transmitter_not_being_keyed
; Transmitter is being keyed.  See if it's a change from previous pass
        btfsc   VFOcontrol,TRANSMIT ;  Was it transmitting on last pass?
        goto    transmit_setup_exit ; Yes,  no change, so exit. 
        bsf     VFOcontrol,TRANSMIT ; No, set transmit active
        bsf     VFOcontrol,NEEDUPDATE ; We need an update of DDS and LCD
;
; Write to LCD
;
        movlw   0xC6              ; Point LCD at digit 6 of 2nd line
        call    cmnd2LCD          ;
        movlw   'X'               ; Say "XMIT "
        call    data2LCD          ;
        movlw   'M'               ; 
        call    data2LCD          ;
        movlw   'I'               ; 
        call    data2LCD          ;
        movlw   'T'               ; 
        call    data2LCD          ;
        goto    transmit_setup_exit ;  exit 
;        
transmitter_not_being_keyed
        btfss   VFOcontrol,TRANSMIT ;  Was it transmitting on last pass?
        goto    transmit_setup_exit ; No,  no change, so exit. 
        bcf     VFOcontrol,TRANSMIT ; Yes, clear transmit active
        bsf     VFOcontrol,NEEDUPDATE ; We need an update of DDS and LCD
;
; Write to LCD
;
        movlw   0xC6              ; Point LCD at digit 6 of 2nd line
        call    cmnd2LCD          ;
        movlw   'R'               ; Say "RCV "
        call    data2LCD          ;
        movlw   'C'               ; 
        call    data2LCD          ;
        movlw   'V'               ; 
        call    data2LCD          ;
        movlw   ' '               ; 
        call    data2LCD          ;
;
transmit_setup_exit
        return                    ; Return to caller 
;
;*****************************************************************************
; *                                                                          *
; * Purpose:  This routine adds the 32 bit value of fstep to the 32 bit      *
; *           value in workfreq.  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 <workfreq_0:workfreq_3>         *
; *                                                                          *
; *  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   workfreq_0,f      ; Add it to the low byte of freq
        btfss   STATUS,C          ; Any carry?
        goto    add1              ; No, add next byte
        incfsz  workfreq_1,f      ; Ripple carry up to the next byte
        goto    add1              ; No new carry, add next byte
        incfsz  workfreq_2,f      ; Ripple carry up to the next byte
        goto    add1              ; No new carry, add next byte
        incf    workfreq_3,f      ; Ripple carry up to the highest byte
add1
        movf    fstep_1,w         ; Get the next increment byte
        addwf   workfreq_1,f      ; Add it to the next higher byte
        btfss   STATUS,C          ; Any carry?
        goto    add2              ; No, add next byte
        incfsz  workfreq_2,f      ; Ripple carry up to the next byte
        goto    add2              ; No new carry, add next byte
        incf    workfreq_3,f      ; Ripple carry up to the highest byte
add2
        movf    fstep_2,w         ; Get the next to most significant
increment
        addwf   workfreq_2,f      ; Add it to the freq byte
        btfss   STATUS,C          ; Any carry?
        goto    add3              ; No, add last byte
        incf    workfreq_3,f      ; Ripple carry up to the highest byte
add3
        movf    fstep_3,w         ; Get the most significant increment byte
        addwf   workfreq_3,f      ; Add it to the most significant freq
        return                    ; Return to the caller
;
;*****************************************************************************
; *                                                                          *
; * Purpose:  Subtract the increment step from freq, checking that it does   *
; *           not go below zero.                                             *
; *                                                                          *
; *   Input:  The values in fstep and <workfreq_0:workfreq_3>.               *
; *                                                                          *
; *  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   workfreq_3,7      ; Is high order frequency byte "negative"?
        goto    exit2             ; No, keep going
set_min
        clrf    workfreq_0        ; Yes, set the frequency to zero
        clrf    workfreq_1        ;
        clrf    workfreq_2        ;
        clrf    workfreq_3        ;
exit2
        return                    ; Return to the caller
;
;*****************************************************************************
; *                                                                          *
; * Purpose:  Check if freq exceeds the upper limit (30 MHz).                *
; *                                                                          *
; *   Input:  The 32 bit values in <workfreq_0:workfreq_3>                   *
; *                                                                          *
; *  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-limit_3      ; Get (FF - limit of high byte)
        addwf   workfreq_3,w      ; Add it to the current high byte
        btfsc   STATUS,C          ; Was high byte too large?
        goto    set_max           ; Yes, apply limit
        movlw   limit_3           ; Get high limit value
        subwf   workfreq_3,w      ; Subtract the limit value
        btfss   STATUS,C          ; Are we at the limit for the byte?
        goto    exit1             ; No, below.  Checks are done.
;
;       Check the second most significant byte.
;
        movlw   0xFF-limit_2      ; Get (FF - limit of next byte)
        addwf   workfreq_2,w      ; Add it to the current byte
        btfsc   STATUS,C          ; Is the current value too high?
        goto    set_max           ; Yes, apply the limit
        movlw   limit_2           ; Second limit byte
        subwf   workfreq_2,w      ; Subtract limit value
        btfss   STATUS,C          ; Are we at the limit for the byte?
        goto    exit1             ; No, below.  Checks are done.
;
;       Check the third most significant byte.
;
        movlw   0xFF-limit_1      ; Get (FF - limit of next byte)
        addwf   workfreq_1,w      ; Add it to the current byte
        btfsc   STATUS,C          ; Is the current value too high?
        goto    set_max           ; Yes, apply the limit
        movlw   limit_1           ; Third limit byte
        subwf   workfreq_1,w      ; Subtract limit value
        btfss   STATUS,C          ; Are we at the limit for the byte?
        goto    exit1             ; No, below.  Checks are done.
;
;       Check the least significant byte.
;
        movlw   limit_0           ; Fourth limit byte
        subwf   workfreq_0,w      ; Subtract limit value
        btfss   STATUS,C          ; Are we at the limit for the byte?
        goto    exit1             ; No, below.  Checks are done.
set_max
        movlw   limit_0           ; Get least significant limit
        movwf   workfreq_0        ; Set it in freq
        movlw   limit_1           ; Get the next byte limit
        movwf   workfreq_1        ; Set it in freq_1
        movlw   limit_2           ; Get the next byte limit
        movwf   workfreq_2        ; Set it in freq_2
        movlw   limit_3           ; Get the most significant limit
        movwf   workfreq_3        ; Set it in freq_3
exit1
        return                    ; Return to the caller
;
;*****************************************************************************
; *                                                                          *
; * Purpose:  Load <workfreq_0:workfreq_3> with with frequency of the        *
; *              selected RECEIVE VFO.                                       *
; *                                                                          *
; *   Input:  VFO_control flag byte set up                                   *
; *           <Afreq_0:Afreq_3> or Bfreq_0:Bfreq_3> set up                   *
; *                                                                          *
; *  Output:  <workfreq_0:workfreq_3> set up                                 *
; *                                                                          *
;*****************************************************************************
;
VFO_freq_load
        btfss   VFOcontrol,VFOA  ; Is VFO A selected ?
        goto    VFO_freq_load_B  ; No, VFO_freq_load_B
        movf    Afreq_0,w        ; Yes, set up for VFO A in freq registers
        movwf   workfreq_0
        movf    Afreq_1,w
        movwf   workfreq_1
        movf    Afreq_2,w
        movwf   workfreq_2
        movf    Afreq_3,w
        movwf   workfreq_3
        goto    VFO_freq_load_exit
VFO_freq_load_B
        movf    Bfreq_0,w        ; Set up for VFO B in freq registers
        movwf   workfreq_0
        movf    Bfreq_1,w
        movwf   workfreq_1
        movf    Bfreq_2,w
        movwf   workfreq_2
        movf    Bfreq_3,w
        movwf   workfreq_3
VFO_freq_load_exit
        return
;
;*****************************************************************************
; *                                                                          *
; * Purpose:  Save <workfreq_0:workfreq_3> into frequency registers for      *
; *              the selected VFO                                            *
; *                                                                          *
; *   Input:  VFO_control flag byte set up                                   *
; *           <workfreq_0:workfreq_> set up                                  *
; *                                                                          *
; *  Output:  <Afreq_0:Afreq_3> or Bfreq_0:Bfreq_3> set up                   *
; *                                                                          *
;*****************************************************************************
;
VFO_freq_save
        btfss   VFOcontrol,VFOA   ; Is VFO A selected ?
        goto    VFO_freq_save_B   ; No, VFO_freq_save_B
        movf    workfreq_0,w      ; Yes, save updated workfreq registers
        movwf   Afreq_0           ;   in VFO A
        movf    workfreq_1,w
        movwf   Afreq_1
        movf    workfreq_2,w
        movwf   Afreq_2
        movf    workfreq_3,w
        movwf   Afreq_3
        goto    VFO_freq_save_exit
VFO_freq_save_B
        movf    workfreq_0,w      ; Save updated workfreq registers 
        movwf   Bfreq_0           ;   in VFO B
        movf    workfreq_1,w
        movwf   Bfreq_1
        movf    workfreq_2,w
        movwf   Bfreq_2
        movf    workfreq_3,w
        movwf   Bfreq_3
VFO_freq_save_exit
        return
;
;*****************************************************************************
; *                                                                          *
; * Purpose:  Update the DDS and LCD.                                        *
; *           This routine is called after a change has been detected,       *
; *             either via the encoder or a pushbutton.                      *
; *                                                                          *
; *           1) Update <freq_0:freq_3>.                                     *
; *           2) Display this frequency on the LCD                           *
; *           3) Send this frequency to the DDS                              *
; *                                                                          *
; *   Input:  VFOcontrol flag word set up                                    *
; *           <AFreq_0:AFreq_3>  or <BFreq_0:BFreq_3> set up                 *
; *                                                                          *
; *  Output:  <freq_0:freq_3> set up                                         *
; *           LCD updated                                                    *
; *           DDS updated                                                    *
; *           NEEDUPDATE flag cleared                                        *
; *                                                                          *
;*****************************************************************************
;
update_DDS_LCD
        btfsc   VFOcontrol,VFOA   ; Is VFO A selected for rx ?
        goto    select_AR         ; Yes, use VFO_A for receive
;
;----- VFO B on receive---------------------------------------------------------
;
select_BR
        btfsc   VFOcontrol,SPLIT  ; Are we operating SPLIT?
        goto    BRsplit_check_transmit ; Yes, go check for reverse 
; 
; VFO B receive, not split, so just select B
;
        goto    select_DDS_VFO_B  ; Go set DDS to use VFO B
;
; VFO B receive, SPLIT, so check for transmit
;
BRsplit_check_transmit
        btfsc   VFOcontrol,TRANSMIT ; Now transmitting?
        goto    select_DDS_VFO_A  ; Yes, set up DDS to use A 
        goto    select_DDS_VFO_B  ; Not transmitting, so set up DDS to use B
;
;----- VFO A on receive---------------------------------------------------------
;
select_AR
        btfsc   VFOcontrol,SPLIT  ; Are we operating SPLIT?
        goto    ARsplit_check_transmit ; Yes, go check for reverse 
;
; VFO A receive, not split, so select A
;
select_VFO_A         
        goto    select_DDS_VFO_A  ;  Go set DDS to use VFO A
;
; VFO A receive, SPLIT, so check for transmit
;
ARsplit_check_transmit 
        btfsc   VFOcontrol,TRANSMIT ; Now transmitting?
        goto    select_DDS_VFO_B  ; Yes, set up DDS to use B 
        goto    select_DDS_VFO_A  ; No, set up DDS to use A
; 
; ---- Select the DDS VFO --------------------------------------------------------
;
; Set up <freq_0:freq_3> for DDS and LCD routines
; 
select_DDS_VFO_A
        movf    Afreq_0,w         ; Set up VFO A save into active freq registers
        movwf   freq_0            ;
        movf    Afreq_1,w         ;
        movwf   freq_1            ;
        movf    Afreq_2,w         ;
        movwf   freq_2            ;
        movf    Afreq_3,w         ;
        movwf   freq_3            ;
;
;       bcf     PORTD,DDS_select_freq2 ; Clear 16F877 signal for pin for AD9854 pin 29 
;                                 
        movlw   0xCB              ; Point LCD at digit 11 of 2nd line
        call    cmnd2LCD          ;
        movlw   'V'               ; Say "VFO A"
        call    data2LCD          ;
        movlw   'F'               ; 
        call    data2LCD          ;
        movlw   'O'               ; 
        call    data2LCD          ;
        movlw   ' '               ; 
        call    data2LCD          ;
        movlw   'A'               ; 
        call    data2LCD          ;
        goto    select_VFO_exit   ;   and exit
;
select_DDS_VFO_B
        movf    Bfreq_0,w         ; Set up VFO B save into active freq registers
        movwf   freq_0            ;
        movf    Bfreq_1,w         ;
        movwf   freq_1            ;
        movf    Bfreq_2,w         ;
        movwf   freq_2            ;
        movf    Bfreq_3,w         ;
        movwf   freq_3            ;
;
;       bsf     PORTD,DDS_select_freq2 ; Set 16F877 signal for pin for AD9854 pin 29
;
        movlw   0xCB              ; Point LCD at digit 11 of 2nd line
        call    cmnd2LCD          ;
        movlw   'V'               ; Say "VFO B"
        call    data2LCD          ;
        movlw   'F'               ; 
        call    data2LCD          ;
        movlw   'O'               ; 
        call    data2LCD          ;
        movlw   ' '               ; 
        call    data2LCD          ;
        movlw   'B'               ; 
        call    data2LCD          ;
;
select_VFO_exit
;
        call    bin2BCD           ; Convert the frequency to BCD
        call    show_freq         ; Display the frequency on the LCD
        call    calc_dds_word     ; Find the control word for the DDS chip
        call    send_dds_freq     ; Send the control word to the DDS chip
;
        bcf     VFOcontrol,NEEDUPDATE ; Clear needupdate flag
;
        return
;
;*****************************************************************************
; *                                                                          *
; * Purpose:  This routine increments through the band table each time the   *
; *           knob moves a notch, updating the LCD and DDS, until the band   *
; *           button is no longer pushed.                                    *
; *                                                                          *
; *   Input:  The value of the band push button and the encoder bits         *
; *                                                                          *
; *  Output:  Updated freq value, and new frequency on the LCD and DDS.      *
; *                                                                          *
;*****************************************************************************
;
;change_band
;        btfsc   last_dir,1        ; Are we going up in the band list?
;        goto    band_up           ; Yes, increment band address
;        movlw   0x04              ; No, get 4 bytes to subtract
;        subwf   band,f            ; Move down in band list
;        movlw   0xFF-band_end     ; Check to see if we have fallen off the
;        addwf   band,w            ;   bottom of the table.
;        btfss   STATUS,C          ; Off the bottom?
;        goto    valid             ; No, continue
;        movlw   band_end          ; Yes, go to highest entry
;        movwf   band              ;
;valid
;        call    get_band          ; Get the new band frequency
;        goto    write_LCD_DDS     ; Set the frequency and continue
;band_up
;        movlw   0x04              ; Table entries are 4 bytes apart
;        addwf   band,f            ; Increment the band pointer
;        movlw   0xFF-band_end     ; Check to see if we have gone over the
;        addwf   band,w            ;   top of the table.
;        btfsc   STATUS,C          ; Did we go over the top of the table?
;        clrf    band              ; Yes, go to the bottom entry
;        call    get_band          ; Get the new band frequency
;write_LCD_DDS
;        call    bin2BCD           ; Convert the frequency to BCD
;        call    show_freq         ; Display the frequency on the LCD
;        call    calc_dds_word     ; Find the control word for the DDS chip
;        call    send_dds_freq     ; Send the control word to the DDS chip
;        return                    ; Return
;
;
;*****************************************************************************
; *                                                                          *
; * Purpose:  This routine reads the frequency value of a band table entry   *
; *           pointed to by band and returns it in workfreq_3...workfreq_0.  *
; *                                                                          *
; *   Input:  band must contain the index of the desired band entry * 4      *
; *           (with the entries numbered from zero).                         *
; *                                                                          *
; *  Output:  The band frequency in freq.                                    *
; *                                                                          *
;*****************************************************************************
;
;get_band
;        movf    band,w            ; Get the index of the high byte
;        call    band_table        ; Get the value into W
;        movwf   workfreq_3        ; Save it in workfreq_3
;        incf    band,f            ; Increment index to next byte
;        movf    band,w            ; Get the index of the next byte
;        call    band_table        ; Get the value into W
;        movwf   workfreq_2        ; Save it in workfreq_2
;        incf    band,f            ; Increment index to the next byte
;        movf    band,w            ; Get the index to the next byte
;        call    band_table        ; Get the value into W
;        movwf   workfreq_1        ; Save it in workfreq_1
;        incf    band,f            ; Increment index to the low byte
;        movf    band,w            ; Get the index to the low byte
;        call    band_table        ; Get the value into W
;        movwf   workfreq_0        ; Save it in workfreq_0
;        movlw   0x03              ; Get a constant three
;        subwf   band,f            ; Restore original value of band
;        return                    ; Return to the caller
;
;
;*****************************************************************************
;*****************************************************************************
; >>>>>>>>>>>>>>>>>>>>>>>>> DDS ROUTINES <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
;*****************************************************************************
;*****************************************************************************
;
;*****************************************************************************
; *                                                                          *
; * Purpose:  Multiply the 32 bit number for oscillator frequency times the  *
; *           32 bit number for the displayed frequency.                     *
; *                                                                          *
; *                                                                          *
; *   Input:  The reference oscillator value in osc_3 ... osc_0 and the      *
; *           current frequency stored in freq_3...freq_0.                   *
; *           The reference oscillator value is treated as a fixed point     *
; *           real, with a 24 bit mantissa.                                  *
; *                                                                          *
; *  Output:  The result is stored in AD9854_3 ... AD9854_0.                 *
; *                                                                          *
;*****************************************************************************
;
calc_dds_word
        clrf    AD9854_0          ; Clear the AD9854 control word bytes
        clrf    AD9854_1          ;
        clrf    AD9854_2          ;
        clrf    AD9854_3          ;
        clrf    AD9854_4          ;
        movlw   0x20              ; Set count  to 32   (4 osc bytes of 8 bits)
        movwf   mult_count        ; Keep running count
        movf    osc_0,w           ; Move the four osc bytes
        movwf   osc_temp_0        ;   to temporary storage for this multiply
        movf    osc_1,w           ; (Don't disturb original osc bytes)
        movwf   osc_temp_1        ;
        movf    osc_2,w           ;
        movwf   osc_temp_2        ;
        movf    osc_3,w           ;
        movwf   osc_temp_3        ;
mult_loop
        bcf     STATUS,C          ; Start with Carry clear
        btfss   osc_temp_0,0      ; Is bit 0 (Least Significant bit) set?
        goto    noAdd             ; No, don't need to add freq term to total
        movf    freq_0,w          ; Yes, get the freq_0 term
        addwf   AD9854_1,f        ;   and add it in to total
        btfss   STATUS,C          ; Does this addition result in a carry?
        goto    add7              ; No, continue with next freq term
        incfsz  AD9854_2,f        ; Yes, add one and check for another carry
        goto    add7              ; No, continue with next freq term
        incfsz  AD9854_3,f        ; Yes, add one and check for another carry
        goto    add7              ; No, continue with next freq term
        incf    AD9854_4,f        ; Yes, add one and continue
add7
        movf    freq_1,w          ; Use the freq_1 term
        addwf   AD9854_2,f        ; Add freq term to total in correct position
        btfss   STATUS,C          ; Does this addition result in a carry?
        goto    add8              ; No, continue with next freq term
        incfsz  AD9854_3,f        ; Yes, add one and check for another carry
        goto    add8              ; No, continue with next freq term
        incf    AD9854_4,f        ; Yes, add one and continue
add8
        movf    freq_2,w          ; Use the freq_2 term
        addwf   AD9854_3,f        ; Add freq term to total in correct position
        btfss   STATUS,C          ; Does this addition result in a carry?
        goto    add9              ; No, continue with next freq term
        incf    AD9854_4,f        ; Yes, add one and continue
add9
        movf    freq_3,w          ; Use the freq_3 term
        addwf   AD9854_4,f        ; Add freq term to total in correct position
noAdd
        rrf     AD9854_4,f        ; Shift next multiplier bit into position
        rrf     AD9854_3,f        ; Rotate bits to right from byte to byte
        rrf     AD9854_2,f        ;
        rrf     AD9854_1,f        ;
        rrf     AD9854_0,f        ;
        rrf     osc_temp_3,f      ; Shift next multiplicand bit into position
        rrf     osc_temp_2,f      ; Rotate bits to right from byte to byte
        rrf     osc_temp_1,f      ;
        rrf     osc_temp_0,f      ;
        decfsz  mult_count,f      ; One more bit has been done.  Are we done?
        goto    mult_loop         ; No, go back to use this bit
        clrf    AD9854_4          ; Yes, clear _4.  Answer is in bytes _3 .._0
        return                    ; Done.
;
;****************************************************************************
; *                                                                         *
; * Purpose:  This routine sends the AD9854 control word to the DDS chip    *
; *           using a serial data transfer.                                 *
; *                                                                         *
; *   Input:  AD9854_3 ... AD9854_0                                         *
; *           Note:  Obviously this is a 32 bit value used as input.        *
; *                  The 32 bit value will be used as the upper 32 bits of  *  <<
; *                  the AD9854's 48-bit frequency control register.  Then  *  <<
; *                  two bytes of zero will be sent for the last 16 bits.   *  <<
; *                  (48 bit accuracy is not needed in this application.)   *  <<
; *                                                                         *
; *  Output:  The AD9854 48-bit DDS frequency control register is updated.  *  <<
; *                                                                         *
;****************************************************************************
;
send_dds_freq
;
; Send the command word and the frequency value.
;
        bsf     PORTD,DDS_ioreset ; Pulse the I/O reset line
        call    wait_10_inst      ; Wait 10 instructions
        bcf     PORTD,DDS_ioreset ;
;
        movlw   freq_write_cmd    ; Get the command byte to load frequency
        movwf   byte2send         ; Pass it
        call    send_DDS_byte     ; Send it
        movf    AD9854_3,w        ; Get the most significant byte
        movwf   byte2send         ; Pass it
        call    send_DDS_byte     ; Send it
        movf    AD9854_2,w        ; Get the next byte
        movwf   byte2send         ; Pass it
        call    send_DDS_byte     ; Send it
        movf    AD9854_1,w        ; Get the next byte
        movwf   byte2send         ; Pass it
        call    send_DDS_byte     ; Send it
        movf    AD9854_0,w        ; Get the least significant byte
        movwf   byte2send         ; Pass it
        call    send_DDS_byte     ; Send it
        movlw   0x00              ; Get zero for last two bytes
        movwf   byte2send         ; Pass it
        call    send_DDS_byte     ; Send it
        call    send_DDS_byte     ; Send it again
;
        bsf     PORTD,DDS_ioud    ; Raise the IO update
        call    wait_10_inst      ; Wait 10 instructions
        bcf     PORTD,DDS_ioud    ; Drop the IO update
;
        return                    ; Return to the caller
;
;
;*****************************************************************************
; *                                                                          *
; * Purpose:  Send a byte to the AD9854 through the serial interface.        *
; *                                                                          *
; *   Input:  byte2send is the byte to transfer to the DDS, most significant *
; *           bit first.                                                     *
; *                                                                          *
; *  Output:  None                                                           *
; *                                                                          *
;*****************************************************************************
;
send_DDS_byte
;
        movlw   0x08              ; Get the loop count
        movwf   bit_count         ; Set counter
next_DDS_bit
        bsf     PORTD,DDS_data    ; Guess we are sending a one
        rlf     byte2send,f       ; Test if next bit is 1 or zero
        btfss   STATUS,C          ; Was it zero?
        bcf     PORTD,DDS_data    ; Yes, clear data line
        nop
        nop
        nop
        nop
        nop
        nop
        bsf     PORTD,DDS_clk     ; Toggle write clock
        nop
        nop
        nop
        nop
        nop
        nop
        bcf     PORTD,DDS_clk     ;
;
        decfsz  bit_count,f       ; Has the whole byte been sent?
        goto    next_DDS_bit      ; No, keep going
        return
;
;*****************************************************************************
; *                                                                          *
; * Purpose:  Read a CR byte from the AD9854                                 *
; *                                                                          *
; *   Input:  None                                                           *
; *                                                                          *
; *  Output:  byte2read in W register.  It is the byte transferred from the  *
; *           DDS, most significant bit first.                               *
;*****************************************************************************
;
read_DDS_byte
        clrf    CR_work           ;
        movlw   0x08              ; Get the loop count
        movwf   bit_count         ; Set counter
read_next_DDS_bit
        bcf     STATUS,C          ; Clear Carry bit (default)
        bsf     PORTD,DDS_clk     ; Toggle write clock
        nop
        nop
;
; Set up Carry bit with data
        btfsc   PORTD,DDS_data    ; Skip next if Data = 0
        bsf     STATUS,C          ; Data = 1, so set Carry bit
        nop
        nop
        bcf     PORTD,DDS_clk     ; Clear write clock
; Now rotate Carry bit into work byte
        rlf     CR_work,f         ; Rotate carry bit into LS bit
        decfsz  bit_count,f       ; Has the whole byte been read?
        goto    read_next_DDS_bit ; No, keep going
;
        movf    CR_work,W         ; Move final byte into W
;
        return
;
;*****************************************************************************
; >>>>>>>>>>>>>>>>>>>>>>>>> LCD ROUTINES <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
;*****************************************************************************
;
;*****************************************************************************
; *                                                                          *
; * Purpose:  This subroutine converts a 32 bit binary number to a 10 digit  *
; *           BCD number.  The input value taken from freq(0 to 3) is        *
; *           preserved.  The output is in BCD(0 to 4). Each byte holds =>   *
; *           (hi_digit,lo_digit); most significant digits are in BCD_4.     *
; *           This routine is a modified version of one described in         *
; *           MicroChip application note AN526.                              *
; *                                                                          *
; *   Input:  The value in freq_0 ... freq_3                                 *
; *                                                                          *
; *  Output:  The BCD number in BCD_0 ... BCD_4                              *
; *                                                                          *
;*****************************************************************************
;
bin2BCD
        movlw   0x20              ; Set loop counter
        movwf   BCD_count         ;   to 32
        clrf    BCD_0             ; Clear output
        clrf    BCD_1             ;   "     "
        clrf    BCD_2             ;   "     "
        clrf    BCD_3             ;   "     "
        clrf    BCD_4             ;   "     "
bin_loop
        bcf     STATUS,C          ; Clear carry bit in STATUS
;
; Rotate bits in freq bytes.  Move from LS byte (freq_0) to next byte freq_1).
; Likewise, move from freq_1 to freq_2 and from freq_2 to freq_3.
;
        rlf     freq_0,f          ; Rotate left, 0 -> LS bit, MS bit -> Carry
        rlf     freq_1,f          ; Rotate left, Carry->LS bit, MS bit->Carry
        rlf     freq_2,f          ; Rotate left, Carry->LS bit, MS bit->Carry
        rlf     freq_3,f          ; Rotate left, Carry->LS bit, MS bit->Carry
        btfsc   STATUS,C          ; Is Carry clear? If so, skip next instruction
        bsf     freq_0,0          ; Carry is set so wrap and set bit 0 in freq_0
;
; Build BCD bytes. Move into LS bit of BCD bytes (LS of BCD_0) from MS bit of
; freq_3 via the Carry bit.
;
        rlf     BCD_0,f           ; Rotate left, Carry->LS bit, MS bit->Carry
        rlf     BCD_1,f           ; Rotate left, Carry->LS bit, MS bit->Carry
        rlf     BCD_2,f           ; Rotate left, Carry->LS bit, MS bit->Carry
        rlf     BCD_3,f           ; Rotate left, Carry->LS bit, MS bit->Carry
        rlf     BCD_4,f           ; Rotate left, Carry->LS bit, MS bit->Carry
        decf    BCD_count,f       ; Decrement loop count
        btfss   STATUS,Z          ; Is loop count now zero?
        goto    adjust            ; No, go to adjust
        return                    ; Yes, EXIT
;============================================================================
adjust
; Internal subroutine, called by bin2BCD main loop only
;
; As BCD bytes are being built, make sure the nibbles do not grow larger than 9.
; If a nibble gets larger than 9, increment to next higher nibble.
; (If the LS nibble of a byte overflows, increment the MS nibble of that byte.)
; (If the MS nibble of a byte overflows, increment the LS nibble of next byte.)
;
        movlw   BCD_0             ; Get pointer to BCD_0
        movwf   FSR               ; Put pointer in FSR for indirect addressing
        call    adj_BCD           ;
        incf    FSR,f             ; Move indirect addressing pointer to BCD_1
        call    adj_BCD           ;
        incf    FSR,f             ; Move indirect addressing pointer to BCD_2
        call    adj_BCD           ;
        incf    FSR,f             ; Move indirect addressing pointer to BCD_3
        call    adj_BCD           ;
        incf    FSR,f             ; Move indirect addressing pointer to BCD_4
        call    adj_BCD           ;
        goto    bin_loop          ; Back to main loop of bin2BCD
;============================================================================
adj_BCD
; Internal subroutine, called by adjust only
        movlw   3                 ; Add 3
        addwf   INDF,w            ;   to LS digit
        movwf   BCD_temp          ; Save in temp
        btfsc   BCD_temp,3        ; Is LS digit + 3 > 7  (Bit 3 set)
        movwf   INDF              ; Yes, save incremented value as LS digit
        movlw   0x30              ; Add 3
        addwf   INDF,w            ;   to MS digit
        movwf   BCD_temp          ; Save as temp
        btfsc   BCD_temp,7        ; Is MS digit + 3 > 7  (Bit 7 set)
        movwf   INDF              ; Yes, save incremented value as MS digit
        return                    ; Return to adjust subroutine
;
;*****************************************************************************
; *                                                                          *
; * Purpose:  Display the frequency setting on the LCD.                      *
; *           Initial value is 14,025.000 kHz (14 characters)                *
; *                                                                          *
; *   Input:  The values in BCD_4 ... BCD_0                                  *
; *                                                                          *
; *  Output:  The number displayed on the LCD                                *
; *                                                                          *
;*****************************************************************************
;
show_freq

; ============================================================================
; Using for 16x1 LCD. (Actually 2 8-char lines, side by side.)
;   Line 1 (left) addresses are  0x00 to 0x07   (0x80 to 0x87) 
;   Line 1 (right) addresses are 0x40 to 0x47   (0xC0 to 0xC7)
; Using for 16x2 LCD. 
;   Line 1 (left) addresses are  0x00 to 0x07   (0x80 to 0x8F)
;   Line 1 (right) addresses are 0x08 to 0x0F   (0x90 to 0x9F)
;   Line 2 (left) addresses are  0x40 to 0x47   (0xC0 to 0xCF) 
;   Line 2 (right) addresses are 0x48 to 0x4F   (0xD0 to 0xDF) 
;
; Command: 1aaaaaaa (to position cursor to address aaaaaaa)
;          Use 0x81 to position cursor to address 0x01
;          (We only display 14 characters so indent by 1)
; ============================================================================
        movlw   0x81              ; Point the LCD to first LCD digit location
        call    cmnd2LCD          ; Send starting digit location to LCD
;
; Running 4-bit mode, so need to send Most Significant Nibble first.
;
; Extract and send "XXXX" from byte containing "XXXXYYYY"
;  - Swap halves to get YYYYXXXX
;  - Mask with 0x0F to get 0000XXXX
;  - Add ASCII bias (0030XXXX)
;
        swapf   BCD_3,w           ; Swap 10MHz BCD digit into lower nibble of W
        andlw   0x0F              ; Mask for lower nibble only (0000XXXX)
        btfss   STATUS,Z          ; Is this digit a zero?
        goto    digit_non_zero    ; No, go and construct the non-zero digit
        movlw   ' '               ; Yes, pick up a ASCII space character instead
        goto    send_it           ;   and go send it
digit_non_zero 
        addlw   0x30              ; Add offset for ASCII char set (0030XXXX)
send_it
        call    data2LCD          ; Send byte in W to LCD
;
; Extract and send "YYYY" from byte containing "XXXXYYYY"
;   - Mask with 0x0F to get 0000YYYY
;   - Add offset for ASCII character set in LCD  (0030YYYY)
;
        movf    BCD_3,w           ; Put 1MHz BCD digit into lower nibble of W
        andlw   0x0F              ; Mask for lower nibble only (0000YYYY)
        addlw   0x30              ; Add offset for ASCII char set (0030YYYY)
        call    data2LCD          ; Send byte in W to LCD
;
        movlw   ','               ; Get a comma
        call    data2LCD          ; Send byte in W to LCD
;
        swapf   BCD_2,w           ; Swap 100KHz BCD digit into lower nibble of W
        andlw   0x0F              ; Mask for lower nibble only (0000XXXX)
        addlw   0x30              ; Add offset for ASCII char set (0030XXXX)
        call    data2LCD          ; Send byte in W to LCD
;
        movf    BCD_2,w           ; Put 10KHz BCD digit into lower nibble of W
        andlw   0x0F              ; Mask for lower nibble only (0000YYYY)
        addlw   0x30              ; Add offset for ASCII char set (0030YYYY)
        call    data2LCD          ; Send byte in W to LCD
;
        swapf   BCD_1,w           ; Swap 1KHz BCD digit into lower nibble of W
        andlw   0x0F              ; Mask for lower nibble only (0000XXXX)
        addlw   0x30              ; Add offset for ASCII char set (0030XXXX)
        call    data2LCD          ; Send byte in W to LCD
;
        movlw   '.'               ; Set up W with ASCII Period
        call    data2LCD          ; Send data byte in W to LCD
; =================================================================================
; For 16x1 LCD, need to move cursur to address 0x40 for the 9th character position
; Command: 1aaaaaaa (to position cursor to address aaaaaaa)
;       movlw   0xC0              ; Point to LCD digit to address 0x40 for char 9
;       call    cmnd2LCD          ; Send command byte in W to LCD
;==================================================================================
; For 16x2 LCD, don't move cursor address.  Just continue for 9th character positon
; =================================================================================
        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 LCD
;
        movlw   'k'               ; Send a 'k'
        call    data2LCD          ;   to LCD
;
        movlw   'H'               ; Send an "H"
        call    data2LCD          ;   to LCD
;
        movlw   'z'               ; Send a 'z'
        call    data2LCD          ;   to LCD
;
        return                    ;
; *****************************************************************************
; *                                                                           *
; * 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                                                            *
; *                                                                           *
; *  Output:  PORTC set as all outputs                                        *
; *****************************************************************************
; 
busy_check
;
; First we need to save the state of LED0 since it happens to be on the same
; port as the LCD and it is going to get destroyed when the entire port is 
; cleared by this routine. 
;
        bcf     RememberLED0,0    ; Default - LED0 is clear 
        btfsc   PORTC,LED0        ; Is LED0 clear?
        bsf     RememberLED0,0        ; No - save LED0 as set
;
; Now ready to clear the port and set up for busy check.
; 
        clrf    PORTC             ; Clear all outputs on PORTC
        bcf     STATUS,RP1        ; Select       
        bsf     STATUS,RP0        ;   bank 1
        movlw   b'11110000'       ; Set <RC7:RC4> as inputs, <RC3:RC0> outputs
        movwf   TRISC             ;   via Tristate
        bcf     STATUS,RP1        ; Select       
        bcf     STATUS,RP0        ;   bank 0
        bcf     PORTC,LCD_rs      ; Set up LCD for Read Busy Flag (RS = 0) 
        bsf     PORTC,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     PORTC,LCD_e       ; Set E high
        nop                       ; Wait 
        nop                       ;
        nop                       ;
        nop                       ;
        movf    PORTC,w           ; Read PORTC into W
        movwf   LCD_read          ; Save W for later testing
        bcf     PORTC,LCD_e       ; Drop E again
        nop                       ; Wait at least .450 us 
        nop                       ; (Each NOP instruction takes about .2 us
        nop                       ;   when using 20 MHz clock)
        nop                       ;    
        bsf     PORTC,LCD_e       ; Pulse E high (dummy read of lower nibble),
        nop                       ; Wait at least .450 us 
        nop                       ; (Each NOP instruction takes about .2 us
        nop                       ;   when using 20 MHz clock)
        nop                       ;    
        bcf     PORTC,LCD_e       ;   and drop E again
        decf    timer1,f          ; Decrement loop counter
        btfsc   STATUS,Z          ; Is loop counter down to zero?
        goto    bailout           ; If yes, bailout (end regardless)
        btfsc   LCD_read,7        ; Is Busy Flag (RC7) in save byte clear?
        goto    LCD_is_busy       ; If not, it is busy so jump back
        goto    not_busy          ; Not busy
bailout 
        bsf     PORTD,LED2        ; Set LED 2 - Interrupt bailout occurred 
not_busy
        clrf    PORTC             ; Clear all of Port C (inputs and outputs)
        bcf     STATUS,RP1        ; Select       
        bsf     STATUS,RP0        ;   bank 1
        movlw   0x00              ; Set up to enable PORTC data pins
        movwf   TRISC             ; All pins (RC7..RC0) are back to outputs
        bcf     STATUS,RP1        ; Select       
        bcf     STATUS,RP0        ;   bank 0
;
; Now restore LED0 to the state it was in at the start of this routine
;
        bcf     PORTC,LED0        ; Clear LED0 (default)
        btfsc   RememberLED0,0    ; Was LED0 clear when saved?      
        bsf     PORTC,LED0        ; No, set LED0
;
        return                    ; 
;
; *****************************************************************************
; * 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     PORTC,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     PORTC,LCD_rs      ; Set RS for Data to LCD
write2LCD
        call    busy_check        ; Check to see if LCD is ready for new data
        bcf     PORTC,LCD_rw      ; Set LCD back to Write mode  (RW = 0)
        bcf     PORTC,LCD_rs      ; Guess RS should be clear              
        btfsc   rs_value,0        ; Should RS be clear?  (is bit 0 == 0?) 
        bsf     PORTC,LCD_rs      ; No, set RS                            
;
; Transfer Most Significant nibble  (XXXX portion of XXXXYYYY)
;
        movlw   0x0F              ; Set up mask                                 
        andwf   PORTC,f           ; Clear old <RC7:RC4> 
        movf    LCD_char,w        ; Put byte of data into W
        andlw   0xF0              ; Mask to give XXXX0000 in W
        iorwf   PORTC,f           ; Send to <RC7:RC4> without changing <RC3:RC0>
        bsf     PORTC,LCD_e       ; Pulse the E line high,
        nop                       ; Wait at least .450 us 
        nop                       ; (Each NOP instruction takes about .2 us
        nop                       ;   when using 20 MHz clock)
        nop                       ;    
        bcf     PORTC,LCD_e       ; Now drop it again
;
; Transfer Least Significant nibble  (YYYY portion of XXXXYYYY)
;
        movlw   0x0F              ; Set up mask                                 
        andwf   PORTC,f           ; Clear old <RC7:RC4>
        swapf   LCD_char,w        ; Move LS nibble of data to MS position in W
        andlw   0xF0              ; Mask to give YYYY0000 in W
        iorwf   PORTC,f           ; Send to <RC7:RC4> without changing <RC3:RC0>
        bsf     PORTC,LCD_e       ; Pulse the E line high,
        nop                       ; Wait at least .450 us 
        nop                       ; (Each NOP instruction takes about .2 us
        nop                       ;   when using 20 MHz clock)
        nop                       ;    
        bcf     PORTC,LCD_e       ;   and drop it again
        return
;
;*****************************************************************************
;*****************************************************************************
; >>>>>>>>>>>>>>>>>>>>>> EEPROM ROUTINES <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
;*****************************************************************************
;*****************************************************************************
;
; *****************************************************************************
; *                                                                           *
; * Purpose:  Write the byte of data at EEDATA to the EEPROM at address       *
; *           EEADR and then increment the address.                           *
; *                                                                           *
; *   Input:  The values at EEDATA and EEADR.                                 *
; *                                                                           *
; *  Output:  The EEPROM value is updated.                                    *
; *           Exit in Bank 0 !                                                *
; *                                                                           *
; *****************************************************************************
;
write_EEPROM
        bsf     STATUS,RP1        ; Select       
        bsf     STATUS,RP0        ;   bank 3
        bsf     EECON1,WREN       ; Set the EEPROM write enable bit
        bcf     EECON1,EEPGD      ; Point to data memory (not program memory) (16f877)
; Start required sequence
        bcf     INTCON, GIE       ; Disable interrupts
        movlw   0x55              ; Write 0x55 and 0xAA to EEPROM
        movwf   EECON2            ;   control register, as required
        movlw   0xAA              ;   for the write
        movwf   EECON2            ; 
        bsf     EECON1,WR         ; Set WR to initiate write
        bsf     INTCON, GIE       ; Enable interrupts again
; End required sequence
bit_check
        btfsc   EECON1,WR         ; Has the write completed?
        goto    bit_check         ; No, keep checking
        bcf     EECON1,WREN       ; Clear the EEPROM write enable bit
        ;bsf    STATUS,RP1        ; Select bank 2      
        bcf     STATUS,RP0        ;   (was 3)
        incf    EEADR,f           ; Increment the EE write address
        bcf     STATUS,RP1        ; Select bank 0      
        ;bcf    STATUS,RP0        ;   (was 2)
        return                    ; Return to the caller
;
;*****************************************************************************
; *                                                                          *
; * Purpose:  Read a byte of EEPROM data at address EEADR into EEDATA        *
; *            and then increment the address.                               *
; *                                                                          *
; *   Input:  The address EEADR.                                             *
; *                                                                          *
; *  Output:  EEDATA value in W                                              *
; *           Exit in Bank 0 !                                               *
; *                                                                          *
;*****************************************************************************
;
read_EEPROM
        bsf     STATUS,RP1        ; Select       
        bsf     STATUS,RP0        ;   bank 3
        bcf     EECON1,EEPGD      ; Point to DATA memory (new - 16f877)
        bsf     EECON1,RD         ; Request the read EE
        nop                       ; Any instruction here is ignored as program
        nop                       ; memory is read in third cycle after BSF EECON1,RD
        ;bsf    STATUS,RP1        ; Select bank 2
        bcf     STATUS,RP0        ;   (was 3)
        incf    EEADR,f           ; Increment the read address
        movf    EEDATA,w          ; W = EEDATA
        bcf     STATUS,RP1        ; Select bank 0
        ;bcf    STATUS,RP0        ;   (was 2)
        return                    ; Return to the caller
;
;
;*****************************************************************************
; *                                                                          *
; * Purpose:  Read the CR bytes from the AD9854 into PIC memory (DEBUG ONLY) *
; *           Need to add Trigger with some button press.                    *
; *                                                                          *
; *  Note:  AD9854 APP note says "Reads are not guaranteed at 100 MHz as     *
; *         they are intended for software debug only."                      *
; *                                                                          *
; *    Need to enhance this to write the data to the LCD.                    *             
; *    Currently only works with Debugger, looking at registers.             *
; *     (Not fully debugged and guaranteed to work.)                         *
; *                                                                          *
; *   Input:  None                                                           *
; *                                                                          *
; *  Output:  CR Bytes in PIC memory                                         *
; *           Bank 0 selected                                                *
; *                                                                          *
;*****************************************************************************
;
read_CR_bytes 
;
; Pulse the IO reset 
;
        bsf     PORTD,DDS_ioreset ; Raise the IO reset
        nop                       ; Wait a bit
        nop                       ; And again
        nop                       ; And again
        bcf     PORTD,DDS_ioreset  ; Drop the IO reset
        nop
        nop
        nop
        nop
; Clear Input CR registers
        movlw   0xFF
        movwf   CR_in0
        movwf   CR_in1
        movwf   CR_in2
        movwf   CR_in3
;
; Send the command to Read the CR register.
;
        movlw   CR_read_cmd       ; Get the read CR bytes command
        movwf   byte2send         ; Pass it
        call    send_DDS_byte     ; Send it
;
        bcf     STATUS,RP1        ; Select       
        bsf     STATUS,RP0        ;   bank 1
        movlw   0x10              ; Tristate port D
        movwf   TRISD             ;   to all outputs except DDS_Data (RD4) as input
        bcf     STATUS,RP1        ; Select       
        bcf     STATUS,RP0        ;   bank 0 
; 
        call    read_DDS_byte     ; Read first CR byte
        movwf   CR_in0            ; Save it
;
        call    read_DDS_byte     ; Read second CR byte
        movwf   CR_in1            ; Save it
;
        call    read_DDS_byte     ; Read third CR byte
        movwf   CR_in2            ; Save it
;
        call    read_DDS_byte     ; Read fourth CR byte
        movwf   CR_in3            ; Save it
;
        bcf     STATUS,RP1        ; Select       
        bsf     STATUS,RP0        ;   bank 1
        clrf    TRISD             ; Set port D back to all outputs
        bcf     STATUS,RP1        ; Select       
        bcf     STATUS,RP0        ;   bank 0 
;
        return
; 
;*****************************************************************************
;*****************************************************************************
; >>>>>>>>>>>>>> INITIALIZATION ROUTINES <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
;*****************************************************************************
;*****************************************************************************
;
;*****************************************************************************
; *                                                                          *
; * Purpose:  Power on initialization of 16F877.                             *
; *                                                                          *
; *   Input:  None                                                           *
; *                                                                          *
; *  Output:  None                                                           *
; *                                                                          *
;*****************************************************************************
;
init_PIC
        call    wait_128ms        ; Wait for stable environment
;
        clrf    INTCON            ; Disable interrupts until we are ready
;
        bcf     STATUS,RP1        ; Select       
        bsf     STATUS,RP0        ;   bank 1

;        bsf     OPTION_REG,NOT_RBPU ; Disable weak pullups (LOW ACTIVE)

        movlw   00111111b         ; (5,4,3,2,1,0) = inputs
        movwf   TRISA             ;   to Port A   (bits 7,6 don't exist)

        movlw   00110111b         ; (5,4,2,1,0) = inputs, (7,6,3) = outputs
        movwf   TRISB             ;   to Port B 

        movlw   00000000b         ; (7,6,5,4,3,2,1,0) = outputs
        movwf   TRISC             ;  to Port C 

        clrf    TRISD             ; Set Port D to all outputs

        movlw   00000111b         ; (2, 1, 0) = inputs
        movwf   TRISE             ;   to Port E  (bits 7,6,5,4,3 don't exist)

        movlw   00000111b          ; Set 0111 in PCFG3:PCFG0 for all 
        movwf   ADCON1            ;   digital pins RA0-RA5 and RE0-RE2

        bcf     STATUS,RP1        ; Select       
        bcf     STATUS,RP0        ;   bank 0
;
        bcf     PORTC,LED0        ; Clear LED 0
        bcf     PORTD,LED1        ; Clear LED 1
        bcf     PORTD,LED2        ; Clear LED 2
        bsf     PORTB,LEDRECVFOA  ; Turn on LED for VFO A
        bcf     PORTB,LEDRECVFOB  ; Turn off LED for VFO B
;
        bcf     PORTB,LEDSPLIT    ; Clear LED 
;
        clrf    encindex          ; Initialize
        clrf    int_stack_count   ; Initialize interrupt stack count
        bcf     VFOcontrol,NEEDUPDATE ; Clear needupdate flag
        bcf     VFOcontrol,TRANSMIT ; Clear transmitting flag
        bcf     VFOcontrol,SPLIT  ; Start with split off
        bcf     VFOcontrol,ALTERNATE  ; Start with primary encoder selected
        clrf    DebugControl      ; Clear debug control word
;       bcf     PORTD,DDS_select_freq2 ; Clear it (use freq1 only)    (unused)
        return                    ; return
;
;*****************************************************************************
; *                                                                          *
; * Purpose:  Power on initialization of Liquid Crystal Display.  The LCD    *
; *           controller chip must be equivalent to an Hitachi 44780.        *
; *                                                                          *
; *   Input:  None                                                           *
; *                                                                          *
; *  Output:  None                                                           *
; *                                                                          *
;*****************************************************************************
;
init_LCD
        bsf     PORTD,LED1        ; Set LED - Entering Init LCD code 
;
        call    wait_64ms         ; Wait for LCD to power up
        movlw   0x30              ; LCD init instruction (First)
        movwf   PORTC             ; Send to LCD via <RC7:RC0>    
        bsf     PORTC,LCD_e       ; Set the LCD E line high,
        call    wait_64ms         ;   wait a "long" time
        bcf     PORTC,LCD_e       ;   and then Clear E 
        movlw   0x30              ; LCD init instruction (Second)
        movwf   PORTC             ; Send to LCD via RC7..RC0     
        bsf     PORTC,LCD_e       ; Set E high,
        call    wait_32ms         ;   wait a while,
        bcf     PORTC,LCD_e       ;   and then Clear E 
        movlw   0x30              ; LCD init instruction (Third)
        movwf   PORTC             ; Send to LCD via <RC7:RC0>
        bsf     PORTC,LCD_e       ; Set E high,
        call    wait_32ms         ;   wait a while,
        bcf     PORTC,LCD_e       ;   and then Clear E
        movlw   0x20              ; 4-bit mode instruction
        movwf   PORTC             ; Send to LCD via <RC7:RC0>
        bsf     PORTC,LCD_e       ; Set E high,
        call    wait_16ms         ;   wait a while,
        bcf     PORTC,LCD_e       ;   and then Clear E
        movlw   0x28              ; 1/16 duty cycle, 5x8 matrix
        call    cmnd2LCD          ; Send command in w to LCD
        movlw   0x08              ; Display off, cursor and blink off 
        call    cmnd2LCD          ; Send command to LCD
        movlw   0x01              ; Clear and reset cursor
        call    cmnd2LCD          ; Send command in w to LCD
        movlw   0x06              ; Set cursor to move right, no shift
        call    cmnd2LCD          ; Send command in w to LCD
        movlw   0x0C              ; Display on, cursor and blink off
        call    cmnd2LCD          ; Send command in w to LCD
;
        bcf     PORTD,LED1        ; Clear LED 0 - Leaving Init LCD code 
;
        return                    ; 
;
;*****************************************************************************
; *                                                                          *
; * Purpose:  Display microcode revision and other info on LCD for 2 seconds *
; *           upon power-up                                                  *
; *                                                                          *
; *   Input:  MCODE_REV_0 through MCODE_REV_4 set up                         *
; *                                                                          *
; *  Output:  LCD displays debug info                                        *
; *                                                                          *
;*****************************************************************************
;
display_mcode_version
        movlw   0x80              ; Point LCD at char 0 of first line
        call    cmnd2LCD          ;
        movlw   ' '               ; Clear char 0 of first line
        call    data2LCD          ;
        movlw   'H'               ; Char 1 of first line
        call    data2LCD          ;
        movlw   'F'               ; Char 2 of first line
        call    data2LCD          ;
        movlw   'V'               ; Char 3 of first line
        call    data2LCD          ;
        movlw   'F'               ; Char 4 of first line
        call    data2LCD          ;
        movlw   'O'               ; Char 5 of first line
        call    data2LCD          ;
        movlw   ' '               ; Char 6 of first line
        call    data2LCD          ;
        movlw   'V'               ; Char 7 of first line
        call    data2LCD          ;
        movlw   'e'               ; Char 8 of first line
        call    data2LCD          ;
        movlw   'r'               ; Clear char 9 of first line
        call    data2LCD          ;
        movlw   MCODE_REV_0       ; Get mcode rev byte
        call    data2LCD          ;   and display it
        movlw   MCODE_REV_1       ; Get mcode rev byte
        call    data2LCD          ;   and display it
        movlw   MCODE_REV_2       ; Get mcode rev byte
        call    data2LCD          ;   and display it
        movlw   MCODE_REV_3       ; Get mcode rev byte
        call    data2LCD          ;   and display it
        movlw   MCODE_REV_4       ; Get mcode rev byte
        call    data2LCD          ;   and display it
        movlw   ' '               ; char 15
        call    data2LCD          ;
;
; Now display second line on LCD
;
        movlw   0xC0              ; Point LCD at char 0 of second line
        call    cmnd2LCD          ;
        movlw   'C'               ; CLK starts at char 0 of second line
        call    data2LCD          ;
        movlw   'l'               ; 
        call    data2LCD          ;
        movlw   'k'               ; 
        call    data2LCD          ;
        movlw   ':'               ; 
        call    data2LCD          ;
        movlw   CLOCK_DISPLAY_0   ; Get first clock display byte
        call    data2LCD          ;   and display it
        movlw   CLOCK_DISPLAY_1   ; Get second clock display byte
        call    data2LCD          ;   and display it
        movlw   CLOCK_DISPLAY_2   ; Get third clock display byte
        call    data2LCD          ;   and display it
        movlw   ' '               ; Clear char 7 of second line
        call    data2LCD          ;
        movlw   ' '               ; Clear char 8 of second line
        call    data2LCD          ;
        movlw   'E'               ; Char 9 of second line
        call    data2LCD          ;
        movlw   'n'               ; Char 10 of second line
        call    data2LCD          ;
        movlw   'c'               ; Char 11 of second line
        call    data2LCD          ;
        movlw   ':'               ; Char 12 of second line
        call    data2LCD          ;
        btfsc   VFOcontrol,ALTERNATE ; Is alternate encoder selected?
        goto    DisplayAlternate  ; Yes, go to display alternate
        movlw   ENCODER_DISPLAY_0 ; Get first encoder display byte
        call    data2LCD          ;   and display it
        movlw   ENCODER_DISPLAY_1 ; Get next encoder display byte
        call    data2LCD          ;   and display it
        movlw   ENCODER_DISPLAY_2 ; Get next encoder display byte
        call    data2LCD          ;   and display it
        goto    DisplayEncoderDone ;
DisplayAlternate
        movlw   ENCODER_DISPLAY_0A ; Get first encoder display byte
        call    data2LCD          ;   and display it
        movlw   ENCODER_DISPLAY_1A ; Get next encoder display byte
        call    data2LCD          ;   and display it
        movlw   ENCODER_DISPLAY_2A ; Get next encoder display byte
        call    data2LCD          ;   and display it
DisplayEncoderDone 
;
        call    wait_a_sec        ; Wait one second
        call    wait_a_sec        ; Wait one second
;
; Now clear parts of the second line that need clearing.
;
        movlw   0xC0              ; Point LCD at char 0 of second line
        call    cmnd2LCD          ;
        movlw   ' '               ; Clear char 0 of second line
        call    data2LCD          ;
        movlw   ' '               ; Clear char 1 of second line
        call    data2LCD          ;
        movlw   ' '               ; Clear char 2 of second line
        call    data2LCD          ;
        movlw   ' '               ; Clear char 3 of second line
        call    data2LCD          ;
        movlw   ' '               ; Clear char 4 of second line
        call    data2LCD          ;
        movlw   ' '               ; Clear char 5 of second line
        call    data2LCD          ;
; 
        return

;*****************************************************************************
; *                                                                          *
; * Purpose:  Initialize the AD9854 by sending the CR register bit settings. *
; *                                                                          *
; *   Input:  None                                                           *
; *                                                                          *
; *  Output:  None                                                           *
; *                                                                          *
;*****************************************************************************
;
init_DDS
        bsf     PORTD,LED2        ; Set LED - Entering Init DDS code 
        call    wait_8ms          ;
;
; Pulse the Master Reset to initialize the DDS chip.
;
        bsf     PORTD,DDS_mreset  ; Raise the master reset
        call    wait_8ms          ; Wait a long time
        bcf     PORTD,DDS_mreset  ; Drop the master reset
        call    wait_8ms          ; Wait a long time
;
; Now pulse the IO reset 
;
        bsf     PORTD,DDS_ioreset ; Raise the IO reset
        call    wait_8ms          ; Wait a long time
        bcf     PORTD,DDS_ioreset  ; Drop the IO reset
;
; Send the command to Write the CR register.
;
        movlw   CR_write_cmd      ; Get the write CR bytes command
        movwf   byte2send         ; Pass it
        call    send_DDS_byte     ; Send it
;
; Send the four CR bytes.
;
        movlw   CR_byte_0         ; Get the first DDS CR byte
        movwf   byte2send         ; Pass it
        call    send_DDS_byte     ; Send it
        movlw   CR_byte_1         ; Get the second DDS CR byte
        movwf   byte2send         ; Pass it
        call    send_DDS_byte     ; Send it
        movlw   CR_byte_2         ; Get the third DDS CR byte
        movwf   byte2send         ; Pass it
        call    send_DDS_byte     ; Send it
        movlw   CR_byte_3         ; Get the 4th DDS CR byte
        movwf   byte2send         ; Pass it
        call    send_DDS_byte     ; Send it
;  
;       External Update is now set. The 4th byte probably not sent,
;         so need a new IO Reset and new command
;
; Now pulse the IO reset to start a new command
;
        bsf     PORTD,DDS_ioreset ; Raise the IO reset
        call    wait_8ms          ; Wait a long time
        bcf     PORTD,DDS_ioreset  ; Drop the IO reset
;
; Send the command to Write the CR register again.
;
        movlw   CR_write_cmd      ; Get the write CR bytes command
        movwf   byte2send         ; Pass it
        call    send_DDS_byte     ; Send it
;
; Send the four CR bytes again.
;
        movlw   CR_byte_0         ; Get the first DDS CR byte
        movwf   byte2send         ; Pass it
        call    send_DDS_byte     ; Send it
        movlw   CR_byte_1         ; Get the second DDS CR byte
        movwf   byte2send         ; Pass it
        call    send_DDS_byte     ; Send it
        movlw   CR_byte_2         ; Get the third DDS CR byte
        movwf   byte2send         ; Pass it
        call    send_DDS_byte     ; Send it
        movlw   CR_byte_3         ; Get the 4th DDS CR byte
        movwf   byte2send         ; Pass it
        call    send_DDS_byte     ; Send it
;
; Now we need to send external update to make the entire Init "take"
;
        bsf     PORTD,DDS_ioud    ; Raise the IO update
        call    wait_10_inst      ; Wait 10 instructions
        bcf     PORTD,DDS_ioud    ; Drop the IO update
;
        call    buttons_look      ; Look at buttons to possibly clear debug mode
        btfsc   DebugControl,DDS_init_loop ; Is Debug mode set?
        goto    init_DDS          ; Yes, loop forever  (clear by power down)
;
        bcf     PORTD,LED2        ; Clear LED - Leaving Init DDS code 
;
        return                    ; Return to the caller
;
;*****************************************************************************
; *                                                                          *
; * Purpose:  Initialize default frequency and constants.                    *
; *           Get the reference oscillator constant from the EEPROM.         *
; *           (Add code later to tweak these values in EEPROM with a         *
; *           calibrate routine.)                                            *
; *                                                                          *
; *   Input:  None                                                           *
; *                                                                          *
; *  Output:  None                                                           *
; *                                                                          *
;*****************************************************************************
;
init_freq
;
        bcf     VFOcontrol,FAST_TUNING ; Start by fast tuning off (Normal tuning)
;
        bsf     STATUS,RP1        ; Select       
        bcf     STATUS,RP0        ;   bank 2
        clrf    EEADR             ; Set the EEPROM read address to zero
;
        call    read_EEPROM       ; Read EEPROM and increment address 
                                  ;  (Sets up Bank3, EEDATA into W, exits in Bank0)
        movwf   osc_0             ; Save osc frequency
;
        call    read_EEPROM       ; Get next byte and increment address
                                  ;  (Sets up Bank3, EEDATA into W, exits in Bank0)
        movwf   osc_1             ; Save it 
;
        call    read_EEPROM       ; Get the third byte and increment address
                                  ;  (Sets up Bank3, EEDATA into W, exits in Bank0)
        movwf   osc_2             ; Save it
;
        call    read_EEPROM       ; Get the fourth byte and increment address
                                  ;  (Sets up Bank3, EEDATA into W, exits in Bank0)
        movwf   osc_3             ; Save it
;
;       Set the power on frequency to the defined value.
;
        movlw   default_0         ; Get the least significant byte
        movwf   freq_0            ; Save it
        movwf   Afreq_0           ; Save it
        movwf   Bfreq_0           ; Save it
        movlw   default_1         ; Get the next byte
        movwf   freq_1            ; Save it
        movwf   Afreq_1           ; Save it
        movwf   Bfreq_1           ; Save it
        movlw   default_2         ; And the next
        movwf   freq_2            ; Save it
        movwf   Afreq_2           ; Save it
        movwf   Bfreq_2           ; Save it
        movlw   default_3         ; Get the most significant byte
        movwf   freq_3            ; Save it
        movwf   Afreq_3           ; Save it
        movwf   Bfreq_3           ; Save it
;
        bsf     VFOcontrol,VFOA   ; Set VFO A selected for rx 
;
; Write to LCD
;
        movlw   0xC6              ; Point LCD at digit 6 of 2nd line
        call    cmnd2LCD          ;
        movlw   'R'               ; Say "RCV "
        call    data2LCD          ;
        movlw   'C'               ; 
        call    data2LCD          ;
        movlw   'V'               ; 
        call    data2LCD          ;
        movlw   ' '               ; 
        call    data2LCD          ;
        movlw   ' '               ; 
        call    data2LCD          ;
        movlw   'V'               ; Say "VFO A"
        call    data2LCD          ;
        movlw   'F'               ; 
        call    data2LCD          ;
        movlw   'O'               ; 
        call    data2LCD          ;
        movlw   ' '               ; 
        call    data2LCD          ;
        movlw   'A'               ; 
        call    data2LCD          ;
;
        return                    ; Return
;
;*****************************************************************************
; *                                                                          *
; * Purpose:  Change INTCON bits to allow interrupts.                        *
; *                                                                          *
; *   Input:  None                                                           *
; *                                                                          *
; *  Output:  None                                                           *
; *                                                                          *
;*****************************************************************************
;
allow_interrupts
        bcf     INTCON,INTF       ; Clear any RB0 interrupt that may now exist
        bsf     INTCON,GIE        ; Global interrupt enable 
        bsf     INTCON,INTE       ; RB0 interrupt enable
        
        return                    ; Return


;
;*****************************************************************************
; *                                                                          *
; * Purpose:  This routine is entered if the Calibrate push button is        *
; *           pressed.  "10,000.00 CAL" is displayed on the LCD, and the     *
; *           the DDS chip is programmed to produce 10 MHz, based on the     *
; *           osc value stored in the EEPROM.  As long as calibrate mode is  *
; *           active, the osc value is slowly altered to allow the output    *
; *           to be trimmed to exactly 10 MHz.  Once the Calibrate push      *
; *           button is pressed again, the new osc value is stored in        *
; *           the EEPROM and normal operation begins.                        *
; *                                                                          *
; *   Input:  The original osc constant in EEPROM                            *
; *                                                                          *
; *  Output:  The corrected osc constant in EEPROM                           *
; *                                                                          *
;*****************************************************************************
;
calibrate
;
        movlw   0x80              ; Set frequency to 10MHz by
        movwf   freq_0            ;   setting freq to the binary equivalent
        movlw   0x96              ;   of 10,000,000.
        movwf   freq_1            ;   .
        movlw   0x98              ;   .
        movwf   freq_2            ;   .
        movlw   0x00              ;   .
        movwf   freq_3            ;   .
;
;  Read the starting reference oscillator value from EEPROM.
;
        bsf     STATUS,RP1        ; Select       
        bcf     STATUS,RP0        ;   bank 2
        clrf    EEADR             ; Set the EEPROM read address to zero

        call    read_EEPROM       ; Read EEPROM and increment address
                                  ;  (Sets up Bank3, EEDATA into W, exits in Bank0)
        movwf   osc_0             ; Save osc frequency

        call    read_EEPROM       ; Get next byte and increment address
                                  ;  (Sets up Bank3, EEDATA into W, exits in Bank0)
        movwf   osc_1             ; Save it

        call    read_EEPROM       ; Get the third byte and increment address
                                  ;  (Sets up Bank3, EEDATA into W, exits in Bank0)
        movwf   osc_2             ; Save it

        call    read_EEPROM       ; Get the fourth byte and increment address
                                  ;  (Sets up Bank3, EEDATA into W, exits in Bank0)
        movwf   osc_3             ; Save it
        
        call    bin2BCD           ; Calculate BCD version of 10,000.00
        call    show_freq         ; Display the frequency on the LCD
        movlw   0xC0              ; Point LCD at digit 0 of 2nd line
        call    cmnd2LCD          ;
        movlw   'C'               ; Send a C
        call    data2LCD          ;
        movlw   'A'               ; Send an A
        call    data2LCD          ;
        movlw   'L'               ; Send an L
        call    data2LCD          ;
        movlw   ' '               ; Send an SPACE
        call    data2LCD          ;
        movlw   ' '               ; Send an SPACE
        call    data2LCD          ;
calibrate_loop
        call    calc_dds_word     ; Calculate DDS value based on current osc
        call    send_dds_freq     ; Update the DDS 
calibrate_wait_for_int
        movf    int_stack_count,w ; Get interrupt stack count
        btfss   STATUS,Z          ; Any interrupts?
        goto    calibrate_interrupt_occurred ; Yes, go process the change
;
; Check to see if Calibrate pushbutton has been pressed again (to end calibrate)
;
        btfsc   PORTB,PBCALIBRATE ; Is Calibrate pushbutton pressed for CAL release?
        goto    calibrate_wait_for_int ; No, loop back - wait for interrupt
calibrate_release_wait
        btfss   PORTB,PBCALIBRATE ; pushbutton released yet
        goto    calibrate_release_wait ; No, wait for release
        goto    calibrate_done    ; Yes, go end calibrate
;
calibrate_interrupt_occurred
        clrf    int_stack_count   ; Clear count (start over with interrupts)
        clrf    fstep_3           ; Clear the three most significant
        clrf    fstep_2           ;   bytes of fstep
        clrf    fstep_1           ;
        movlw   0x10              ; Assume that we are adjusting slowly 
        movwf   fstep_0           ; Use small increment (default)
        movf    encindex,w        ; Get amount into w
        btfsc   STATUS,Z          ; Is it zero
        goto    update_osc        ; Yes, go use default
        movlw   0x80              ; No, then use the large increment
        movwf   fstep_0           ;
update_osc
        btfsc   PORTA,ENC3        ; Is direction bit clear? (indicating DN)
        goto    calibrate_increase ; No, go and increase the osc value
                                  ; Yes, decrease it
        comf    fstep_0,f         ; Subtraction of fstep is done by
        comf    fstep_1,f         ;   adding the twos compliment of fstep
        comf    fstep_2,f         ;   to osc
        comf    fstep_3,f         ;
        incfsz  fstep_0,f         ; Increment last byte
        goto    calibrate_increase ; Non-zero, continue
        incfsz  fstep_1,f         ; Increment next byte
        goto    calibrate_increase ; Non-zero, continue
        incfsz  fstep_2,f         ; Increment next byte
        goto    calibrate_increase ; Non-zero, continue
        incf    fstep_3,f         ; Increment the high byte
calibrate_increase
        movf    fstep_0,w         ; Get the low byte increment
        addwf   osc_0,f           ; Add it to the low osc byte
        btfss   STATUS,C          ; Was there a carry?
        goto    add4              ; No, add the next bytes
        incfsz  osc_1,f           ; Ripple carry up to the next byte
        goto    add4              ; No new carry, add the next bytes
        incfsz  osc_2,f           ; Ripple carry up to the next byte
        goto    add4              ; No new carry, add the next bytes
        incf    osc_3,f           ; Ripple carry up to the highest byte
add4
        movf    fstep_1,w         ; Get the second byte increment
        addwf   osc_1,f           ; Add it to the second osc byte
        btfss   STATUS,C          ; Was there a carry?
        goto    add5              ; No, add the third bytes
        incfsz  osc_2,f           ; Ripple carry up to the next byte
        goto    add5              ; No new carry, add the third bytes
        incf    osc_3,f           ; Ripple carry up to the highest byte
add5
        movf    fstep_2,w         ; Get the third byte increment
        addwf   osc_2,f           ; Add it to the third osc byte
        btfss   STATUS,C          ; Was there a carry?
        goto    add6              ; No, add the fourth bytes
        incf    osc_3,f           ; Ripple carry up to the highest byte
add6
        movf    fstep_3,w         ; Get the fourth byte increment
        addwf   osc_3,f           ; Add it to the fourth byte
;
; Check to see if Calibrate pushbutton has been pressed again (to end calibrate)
;
        btfsc   PORTB,PBCALIBRATE ; Is Calibrate pushbutton pressed for release?
        goto    calibrate_loop    ; No, stay in calibrate mode
;
calibrate_done
;
; Write final value to EEPROM

        bsf     STATUS,RP1        ; Select       
        bcf     STATUS,RP0        ;   bank 2
        clrf    EEADR             ; Set the EEPROM write address to zero
        bcf     STATUS,RP1        ; Select bank 0
        ;bcf    STATUS,RP0        ;   (was 2)
        movf    osc_0,w           ; Record the first osc byte
        bsf     STATUS,RP1        ; Select bank 2      
        ;bcf    STATUS,RP0        ;   (was 0)
        movwf   EEDATA            ; Set up osc byte as EEDATA
        call    write_EEPROM      ; Write it to EEPROM and increment address
                                  ;  (Sets up Bank3, EEDATA to EEPROM, exits in Bank0)
        movf    osc_1,w           ; Record the second
        bsf     STATUS,RP1        ; Select bank 2      
        ;bcf    STATUS,RP0        ;   (was 0)
        movwf   EEDATA            ; Set up osc byte as EEDATA
        call    write_EEPROM      ; Write it to EEPROM and increment address
                                  ;  (Sets up Bank3, EEDATA to EEPROM, exits in Bank0)
        movf    osc_2,w           ; Record the third
        bsf     STATUS,RP1        ; Select bank 2      
        ;bcf    STATUS,RP0        ;   (was 0)
        movwf   EEDATA            ; Set up osc byte as EEDATA
        call    write_EEPROM      ; Write it to EEPROM and increment address
                                  ;  (Sets up Bank3, EEDATA to EEPROM, exits in Bank0)
        movf    osc_3,w           ; Record the fourth 
        bsf     STATUS,RP1        ; Select bank 2      
        ;bcf    STATUS,RP0        ;   (was 0)
        movwf   EEDATA            ; Set up osc byte as EEDATA
        call    write_EEPROM      ; Write it to EEPROM and increment address
                                  ;  (Sets up Bank3, EEDATA to EEPROM, exits in Bank0)
        movlw   0xC0              ; Point LCD at digit 0 of 2nd line
        call    cmnd2LCD          ;
        movlw   ' '               ; Clear it
        call    data2LCD          ;
        movlw   ' '               ; Clear it
        call    data2LCD          ;
        movlw   ' '               ; Clear it
        call    data2LCD          ;
 
        return                    ; Return to the caller

;*****************************************************************************
;*****************************************************************************
; >>>>>>>>>>>>>>>>> GENERAL USAGE ROUTINES <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
;*****************************************************************************
;*****************************************************************************
;
;*****************************************************************************
; *                                                                          *
; * Purpose:  Wait for a specified number of instructions or milliseconds.   *
; *                                                                          *
; *           Entry point wait_10_inst :  Wait for 10 instructions           * 
; *           Entry point wait_20_inst :  Wait for 20 instructions           * 
; *           Entry point wait_a_sec   :  Wait for 1 second                  *
; *           Entry point wait_256ms   :  Wait for 256 msec                  *
; *           Entry point wait_128ms   :  Wait for 128 msec                  *
; *           Entry point wait_64ms    :  Wait for 64 msec                   *
; *           Entry point wait_32ms    :  Wait for 32 msec                   *
; *           Entry point wait_16ms    :  Wait for 16 msec                   *
; *           Entry point wait_8ms     :  Wait for 8 msec                    *
; *                                                                          *
; *   Input:  None                                                           *
; *                                                                          *
; *  Output:  None                                                           *
; *                                                                          *
;*****************************************************************************
;
wait_20_inst ; ***** Entry point ******
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
; fall through
wait_10_inst ; ***** Entry point ******
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        return  ; (call and return account for 2 instructions)
;=========================================================
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
        goto    outer_loop        ; Go to wait loops
wait_32ms   ; ****** Entry point ******
        movlw   0x80              ; Set up outer loop to 64
        goto    outer_loop        ; Go to wait loops
wait_16ms   ; ****** Entry point ******
        movlw   0x40              ; Set up outer loop to 32
        goto    outer_loop        ; Go to wait loops
wait_8ms   ; ****** Entry point ******
        movlw   0x20              ; Set up outer loop to 16
                                  ; Fall through into wait loops
;
; Wait loops used by other wait routines
;  - .2 microsecond per instruction cycle
;      (with a 20 MHz microprocessor clock)
;      (.05 microseconds per clock) * (4 clocks per instruction cycle) 
;  - Most instructions take 1 cycle, but some take two.
;  - (255 * 5) = 1275 instruction cycles per inner loop
;  - 1275 instruction cycles * .2us/cycle = 255 us per outer loop
;     (Measured inner loop time is 270 us.)
;  - Divide required time by  ms to get timer1 value
;      For example,  8ms / .255ms = 31.3; use 0x20 for timer1 in wait_8ms
;
        movwf   timer1            ; Store outer loop count
outer_loop
        movlw   0xFF              ; Set up inner loop counter
        movwf   timer2            ;   to 255
inner_loop
        nop                       ; 1 instruction cycle
        nop                       ; 1 instruction cycle
        decfsz  timer2,f          ; Decrement inner loop counter (1 cycle)
        goto    inner_loop        ; Inner loop counter down to zero ?
                                  ; No, go back to inner loop again (2 cycles)
        decfsz  timer1,f          ; Yes, decrement outer loop counter
        goto    outer_loop        ; Outer loop counter down to zero ?
                                  ; No, then go back to outer loop again
        return                    ; Yes, return to caller
;
        END

