; ----- SELECT ONE OF THESE DDS TYPES AND TURN THE OTHER OFF !! ---------
;#define AD9850       ; Using the AD9850 (DDS-30)
#define AD9851        ; Using the AD9851 (DDS-60)
; ----- SELECT 16-CHAR OR 8-CHAR LCD BY ENABLING ONE OF THESE  --------- 
#define LCDCHAR 16    ; Turn on code for 16-character
;#define LCDCHAR 8    ; Turn on code for 8-character
;
;  ---- MAY SELECT ONE OF THESE MULT FACTORS (NOT BOTH!) NORMALLY BOTH OFF --
;#define MULTBY2       ; Generate DDS signal which is 2x frequency on LCD
;#define MULTBY4       ; Generate DDS signal which is 4x frequency on LCD
;
#define LCD2LINES     ; Turn on code for 2-line LCD (REQUIRES LCDCHAR 16)
;
; ****************************************************************************
; * PICELgen - Signal Generator (VFO) with AD9850/AD9851 DDS on PIC-EL       *
; *  Version 6.0c                                                            *
; *  January 13, 2007                                                        *
; *  Author: Craig B Johnson, AA0ZZ                                          *
; ****************************************************************************
; Description:
; Originally, this was the control program for a DDS VFO built with an AD9850 
; DDS chip, a shaft encoder, two push button switches and an Liquid crystal   
; display.  This version supports the newer DDS - the AD9851 as well as the 
; AD9850. Simply select the DDS that you are using at the top of this file.
;
; Features:
; VARIABLE RATE TUNING based on the speed at which the encoder is turned. 
; Pressing a pushbutton switch (PIC-EL PB_1) will change the step size from    
; 1Hz to 1kHz.                                                                 
;                                                                              
; BAND MEMORIES a pushbutton switch (PIC-EL PB_2) allows the frequency to      
; be cycled around the HF ham bands.
;
; CALIBRATE MODE is entered if a pushbutton switch (PIC-EL PB_1) is pressed    
; during power-on. The display is set to 10 MHz and remains fixed,              
; even as adjustments are being made. If pushbutton is held pressed, then 
; turning the shaft encoder will increase or decrease the value "osc" used to 
; calculate the DDS control word. The basic calibrate adjustment rate is very
; low (on the order of a few cycles per turn of the encoder).  A somewhat
; faster adjustment speed is available by pressing the encoder shaft down
; while turning.  

; An external frequency counter on the DDS output is required to observe this
; adjustment. To exit calibrate mode, release the pushbutton and turn the 
; shaft encoder one more time. The calibrated value of "osc" will then be 
; stored in EEPROM memory.
; 
; MOVE UP 1 MHz - Press and hold PIC-EL pushbutton PB_2 and then press and     
; then press and release PIC-EL pushbutton PB_1.                               
;
; MOVE DOWN 1 MHz - Press and hold PIC-EL pushbutton PB_1 and then press and   
; then press and release PIC-EL pushbutton PB_2.                               
;
; SAVE NEW START-UP FREQUENCY - Select the frequency. Then press and hold      
; PIC-EL pushbuttons PB_1 and PB_2 for 2 seconds.  The frequency will be       
; stored in EEPROM and will be used on next start-up.                          
;
; The MULTBY2/4 feature allows a user to use PICELgen with a quadrature sampling
; detector.  The QSD mechanism divides the "input" frequency by 2 or 4, so the DDS
; generated frequency must be 2 or 4 times as large to generate a chosen frequency.
; The modification, enabled via a "DEFINE" statement at the top of this listing,  
; causes the frequency word to be multiplied by 2 or 4 before being sent to the DDS.
; The original frequency is displayed on the LCD and all frequency calculations
; (UP/DN MHz, encoder changes, etc) are done with the standard frequency. 
;
;******************************************************************************
; Original Author - Curtis W. Preuss - WB2V
;
; Modification History
;    8/19/98 - Version 1 - Initial Version by Curtis W. Preuss - WB2V
;   12/xx/98 - Version 2 - Converted to MPASM by Bruce Stough, AA0ED 
;    4/21/99 - Version 3 - Fixed and modified by 
;                            Bruce Stough, AAED (sbs1@visi.com) and
;                            Craig Johnson, AA0ZZ (cbjohns@cbjohns.com)
;
;   10/31/03 PICELgen1.0 Modify for PIC Elmer project by Craig Johnson, AA0ZZ  
;                          1) Change to use 1x8 LCD instead of 1x16            
;                              - Freq displayed as Hz (e.g. 14025000)          
;                              - CAL freq displayed as Hz (e.g. 10000000)      
;                          2) Set up to support the NJQRP DDS Daughterboard    
;                              - 50 MHz oscillator                             
;                          3) Change PORTB pin allocations to support PIC-EL   
;                            - Change RB4-RB7 to RB0-RB3                       
;                            - Change RB0 (DDS LOAD) to RB7                    
;                            - Change RB1 (LCD_rs) to RB6                      
;                            - Change RB2 (LCD_rw) to RB5                      
;                            - Change RB3 (LCD_e) to RB4                       
;                            - Change Busy_check routine to check correct bit  
;                            - Change cmnd2LCD/data2LCD routine - swap nibbles 
;                          4) Change RA2 to always be an LOW output            
;                          5) Support pushbutton on RA3 instead of encoder     
;                               shaft switch for bandswitch and calibrate      
;                          6) Support pushbutton on RA4 for fast tuning        
;                                                                              
;    1/3/04    PICELgen1.1  Add Title and Version on start-up                  
;    1/7/04    PICELgen1.2  Restructure main loop and change_band              
;    2/2/04    PICELgen1.2a Fix confusing comment in the header re shaft sw.   
;                           Fix comment in header regarding PB_2 for calib.    
;    2/8/04    PICELgen1.3  Fix for reliable startup of DDS                    
;                           Add code for 1 MHz steps (up or down)              
;                           Add code to save new start-up frequency            
;    3/12/04   PICELgen1.4  Remove use of watchdog timer  (temp sensitivity)   
;                           Add code to support either 1x8 or 1x16 LCDs        
;                            - Use #DEFINE to select the LCD type              
;                           Fix calibrate routine so it stays in cal_loop      
;   12/20/05   PICELgen4.0  Updated to use AD9851 instead of AD9850            
;   12/25/05   PICELgen5.0  Support BOTH AD9850 and AD9851 via complier option 
;   12/29/05   PICELgen5.1  Fix bug. Speaker being left on, wasting 73mA 
;    1/13/06   PICELgen5.2  Support MULTBY2 or MULTBY4
;                           Support 16x2 LCD also (#DEFINE LCD2LINES)
;    1/16/06   PICELgen6.0  Ported to 16F628
;    6/16/06   PICELgen6.0a Fix bug in x2 and x4 code (sometimes jumps in freq)
;                           (Thanks PA0RWE!)  
;   12/12/06   PICELgen6.0b Fix hang in calibration routine (pb conflict)
;                           Fix EEPROM WRITE mechanism after calibration
;    1/13/07   PICELgen6.0c Clean up. Use full Microchip dev files instead of
;                           bringing in subset
;*****************************************************************************
;                                                                              
; Target Controller -      PIC16F628A in PIC-EL board                            
;                          __________                                          
;     PB_3-Speaker----RA2 |1       18| RA1---------ENCODER A                   
;     PB_2-Bandswitch-RA3 |2       17| RA0---------ENCODER B                   
;     PB_1-Tuning etc-RA4 |3       16| OSC1--------XTAL                        
;     +5V-----------!MCLR |4       15| OSC2--------XTAL                        
;     Ground----------Vss |5       14| VDD---------+5 V                        
;     LCD11-----------RB0 |6       13| RB7---------DDS_LOAD                    
;     LCD12-----------RB1 |7       12| RB6---------LCD_rs  (LCD Pin 4)         
;     LCD13/DDS_CLK---RB2 |8       11| RB5---------LCD_rw  (LCD Pin 5)           
;     LCD14/DDS_DATA--RB3 |9       10| RB4---------LCD_e   (LCD Pin 6)         
;                          ----------                                          
;                                                                              
; ****************************************************************************
; * Device type and options.                                                 *
; ****************************************************************************
;
        processor 16F628A
        radix     dec

; ****************************************************************************
; * Configuration fuse information for 16F628A:                              *
; ****************************************************************************

        include   <P16F628A.INC>
; (Listed here for reference only)
;_BODEN_ON                    EQU     H'3FFF'
;_BODEN_OFF                   EQU     H'3FBF'
;_CP_ALL                      EQU     H'03FF'
;_CP_75                       EQU     H'17FF'
;_CP_50                       EQU     H'2BFF'
;_CP_OFF                      EQU     H'3FFF'
;_PWRTE_OFF                   EQU     H'3FFF'
;_PWRTE_ON                    EQU     H'3FF7'
;_WDT_ON                      EQU     H'3FFF'
;_WDT_OFF                     EQU     H'3FFB'
;_LVP_ON                      EQU     H'3FFF'
;_LVP_OFF                     EQU     H'3F7F'
;_MCLRE_ON                    EQU     H'3FFF'
;_MCLRE_OFF                   EQU     H'3FDF'
;_ER_OSC_CLKOUT               EQU     H'3FFF'
;_ER_OSC_NOCLKOUT             EQU     H'3FFE'
;_INTRC_OSC_CLKOUT            EQU     H'3FFD'
;_INTRC_OSC_NOCLKOUT          EQU     H'3FFC'
;_EXTCLK_OSC                  EQU     H'3FEF'
;_LP_OSC                      EQU     H'3FEC'
;_XT_OSC                      EQU     H'3FED'
;_HS_OSC                      EQU     H'3FEE'

 __config _CP_OFF&_LVP_OFF&_BODEN_OFF&_MCLRE_ON&_PWRTE_ON&_WDT_OFF&_XT_OSC  

;
; *************************************************************************** 
; Info for power-up display                                                   
MCODE_REV_0  equ  '6'     ; Current code version is 6.0                     
MCODE_REV_1  equ  '.'     ;                                                   
MCODE_REV_2  equ  '0'     ;                                                   
MCODE_REV_3  equ  '-'     ;

#ifdef AD9850
MCODE_REV_4  equ  '3'     ;
MCODE_REV_5  equ  '0'     ;
#endif

#ifdef AD9851
MCODE_REV_4  equ  '6'     ;
MCODE_REV_5  equ  '0'     ;
#endif                                                   

#ifdef MULTBY2
MCODE_REV_6  equ  'x'     ;
MCODE_REV_7  equ  '2'     ;
#else
#ifdef MULTBY4
MCODE_REV_6  equ  'x'     ;
MCODE_REV_7  equ  '4'     ;
#else
MCODE_REV_6  equ  ' '     ;
MCODE_REV_7  equ  ' '     ;
#endif
#endif
 
;                                                                             
; ****************************************************************************
; * 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
; integer in the format <ref_osc_3>.<ref_osc_2><ref_osc_1><ref_osc_0>
;
; The values for common oscillator frequencies are as follows:
;
; Frequency    ref_osc_3    ref_osc_2    ref_osc_1    ref_osc_0
;
; 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                  
;  50.00 MHz     0x55         0xE6         0x3B         0x88                  
;  30.00 MHz     0x8F         0x2A         0x63         0x39                  
;
; 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 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 example, for a 180 MHz clock:
;    ref_osc_3 is (2^32 / 180 x 10^6) = 23.860929422 truncated to 23 (0x17)  
;    ref_osc_2 is the high byte of (.860929422 x 2^24) = 14443998
;     14443998 = 0xDC65DE, so high byte is 14.                           
;    ref_osc_1 is the next byte of 0xDC65DE, or 65
;    ref_osc_0 is the last byte of 0xDC65DE, or DE
;
#ifdef AD9850
; For 100 MHz Oscillator =======
ref_osc_3   equ 0x2A              ; Most significant osc byte
ref_osc_2   equ 0xF3              ; Next byte
ref_osc_1   equ 0x1D              ; Next byte
ref_osc_0   equ 0xC4              ; Least significant byte
#endif
#ifdef AD9851
; For 180 MHz (30 MHz clock and 6x multiplier)                               
ref_osc_3   equ 0x17              ; Most significant osc byte                
ref_osc_2   equ 0xDC              ; Next byte                                
ref_osc_1   equ 0x65              ; Next byte                                
ref_osc_0   equ 0xDE              ; Least significant byte                   
#endif

; 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.
;
#ifdef AD9850
#ifdef MULTBY2
limit_3   equ 0x00                ; Most significant byte for 15 MHz    
limit_2   equ 0xE4                ; Next byte
limit_1   equ 0xE1                ; Next byte
limit_0   equ 0xC0                ; Least significant byte
#else
#ifdef MULTBY4
limit_3   equ 0x00                ; Most significant byte for 7.5 MHz 
limit_2   equ 0x72                ; Next byte
limit_1   equ 0x70                ; Next byte
limit_0   equ 0xE0                ; Least significant byte
#else  // No multiplier           
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
#endif
#endif
#endif  // AD9850

#ifdef AD9851
#ifdef MULTBY2
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                    
#else
#ifdef MULTBY4
limit_3   equ 0x00                ; Most significant byte for 15 MHz          
limit_2   equ 0xE4                ; Next byte                                 
limit_1   equ 0xE1                ; Next byte                                 
limit_0   equ 0xC0                ; Least significant byte                    
#else   ; No multiplier           
limit_3   equ 0x03                ; Most significant byte for 60 MHz            
limit_2   equ 0x93                ; Next byte                                 
limit_1   equ 0x87                ; Next byte                                 
limit_0   equ 0x00                ; Least significant byte                    
#endif
#endif           
#endif  ; AD9851
;
; Default contains the default startup frequency as a 32 bit integer.
;
#ifdef AD9850
#ifdef MULTBY4                    ; Max is 7.5 MHz, so start at 7.0 MHz
default_3 equ 0x00                ; Most significant byte for 7.0 MHz
default_2 equ 0x6A                ; Next byte
default_1 equ 0xCF                ; Next byte
default_0 equ 0xC0                ; Least significant byte 
#else
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 
#endif 
#endif

#ifdef AD9851
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 
#endif
;
band_end equ    0x28              ; The offset to the last band table entry 
;
; PB_flags bits                                                               
PB1first equ 0                    ; Bit set indicates PB1 pressed first       
PB1Calibrate_Active equ 1         ; Bit set indicates calibrate is now active
;
EEstartup_adr equ 4               ; Location of startup frequency in EEPROM   
;
; ****************************************************************************
; *                    Port and EEPROM Constants                             *
; ****************************************************************************
;
; (Listed here for reference only)
;PORTA   equ     0x05
;PORTB   equ     0x06
;TRISA   equ     0x05
;TRISB   equ     0x06
;EEDATA  equ     0x9A          ; Changed for 16F628
;EEadr   equ     0x9B          ; Changed for 16F628
;EECON1  equ     0x9C          ; New for 16F628
;EECON2  equ     0x9D          ; New for 16F628
;WREN    equ     0x02
;WR      equ     0x01
;RD      equ     0x00
;CMCON   equ     0x1F
;
; ****************************************************************************
; * Setup the initial constant, based on the frequency of the reference      *
; * oscillator.  This can be tweaked with the calibrate function.            *
; ****************************************************************************
; 
        ORG     0x2100
; ref_osc bytes must be first 4 bytes of EEPROM                               
        DATA    ref_osc_0
        DATA    ref_osc_1
        DATA    ref_osc_2
        DATA    ref_osc_3
; startup frequency bytes must be next 4 bytes of EEPROM                      
        DATA    default_0  ; startup -> freq_0                                
        DATA    default_1  ; startup -> freq_1                                
        DATA    default_2  ; startup -> freq_2                                
        DATA    default_3  ; startup -> freq_3                                
;
;       Clear unused EEPROM bytes (128 bytes for 16F628)
;
        DATA    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
        DATA    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0           
        DATA    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
        DATA    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0           
; 
; ****************************************************************************
; * RAM page independent file registers:                                     *
; ****************************************************************************
;
; (Listed here for reference only)
;INDF    EQU     0x00
;PCL     EQU     0x02
;STATUS  EQU     0x03
;FSR     EQU     0x04
;PCLATH  EQU     0x0A
;INTCON  EQU     0x0B
;GIE     EQU     7                 ; INTCON Bit - Enable/Disable interrupts
;
; *****************************************************************************
; * Bit numbers for the STATUS file register:                                 *
; *****************************************************************************
;
; (Listed here for reference only)
;RP0   EQU     5
;NTO   EQU     4
;NPD   EQU     3
;Z     EQU     2
;DC    EQU     1
;C     EQU     0
;
; ****************************************************************************
; * Assign names to IO pins.                                                 *
; ****************************************************************************
;
;   B register bits:
;
DDS_clk equ     0x02              ; AD9850/AD9851 write clock
DDS_dat equ     0x03              ; AD9850/AD9851 serial data input
LCD_busy equ    0x03              ; LCD busy bit 
LCD_e   equ     0x04              ; 0=disable, 1=enable
LCD_rw  equ     0x05              ; 0=write, 1=read
LCD_rs  equ     0x06              ; 0=instruction, 1=data
DDS_load equ    0x07              ; Update pin on AD9850/AD9851
; 
;   A register bits:
;
speaker   equ   0x02              ; Speaker (always exit with a low output)    
pb_3      equ   0x02              ; PB also on this pin                        
pb_2      equ   0x03              ; Bandswitch Pushbutton,                     
pb_1      equ   0x04              ; Tuning-increment/Calibrate Pushbutton      
;
; ****************************************************************************
; *           Allocate variables in general purpose register space           *
; ****************************************************************************
;
        CBLOCK  0x20              ; Start Data Block
;
        freq_0                    ; Display frequency (hex) 
          freq_1                  ;  (4 bytes) 
          freq_2
          freq_3         
        freq_0A                   ; Display temp frequency (hex) (MULTBY2/4)
          freq_1A                 ;  (4 bytes) 
          freq_2A
          freq_3A        
        BCD_0                     ; Display frequency (BCD) 
          BCD_1                   ;  (5 bytes)
          BCD_2
          BCD_3
          BCD_4     
        AD9851_0                  ; AD9850/AD9851 control word                
          AD9851_1                ;  (5 bytes)                                
          AD9851_2                                                            
          AD9851_3                                                            
          AD9851_4                                                            
        fstep_0                   ; Frequency inc/dec 
          fstep_1                 ;  (4 bytes)
          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 
          osc_1                   ;  (4 bytes)
          osc_2
          osc_3
        osc_temp_0                ; Oscillator frequency 
          osc_temp_1              ;  (4 bytes)
          osc_temp_2        
          osc_temp_3
        LCD_char                  ; Character being sent to the LCD
        LCD_read                  ; Character read from the LCD
        timer1                    ; Used in delay routines
        timer2                    ;   "
        ren_timer_0               ; For variable rate tuning 
          ren_timer_1             ;  (2 bytes)
        ren_new                   ; New value of encoder pins A and B
        ren_old                   ; Old value of encoder pins A and B
        ren_read                  ; Encoder pins A and B and switch pin
        last_dir                  ; Indicates last direction of encoder
        next_dir                  ; Indicates expected direction
        count                     ; loop counter  (gets reused)
        band                      ; Used to index a table of frequencies
        rs_value                  ; The LCD rs line flag value
        PB_flags                  ; Pushbutton flags                          
        PB_wait_count             ; Wait for 2 seconds                        
;
        ENDC                      ; End of Data Block
; 
; ****************************************************************************
; * The 16F84 resets to 0x00.                                                * 
; * The Interrupt vector is at 0x04. (Unused)                                *
; ****************************************************************************             
;
        ORG     0x0000                
reset_entry
        goto    start             ; Jump around the band table to main program
;
; ****************************************************************************  
; * 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              ; 
;
; *****************************************************************************
; *                                                                           *
; * Purpose:  This is the start of the program.  It initializes the LCD and   *
; *           detects whether to enter calibrate mode.  If so, it calls the   *
; *           Calibrate routine.  Otherwise, it sets the power-on frequency   *
; *           and enters the loop to poll the encoder.                        *
; *                                                                           *
; *   Input:  The start up frequency is defined in the default_3 ...          *
; *           definitions above, and relies on the reference oscillator       *
; *           constant defined in ref_osc_3 ... ref_osc_0.                    *
; *                                                                           *
; *  Output:  Normal VFO operation.                                           *
; *                                                                           *
; *****************************************************************************
;
start
        clrf    INTCON            ; No interrupts for now
        movlw   0x07              ; Code to turn off the analog comparitors
        movwf   CMCON             ; Turn off comparators
        call    wait_8ms          ; Wait for LCD to settle
        bsf     STATUS,RP0        ; Switch to bank 1
        bcf     0x01,7            ; Enable weak pullups                        
        movlw   0xFB              ; Tristate PORTA (all Inputs except RA2)     
        movwf   TRISA             ;
        clrf    TRISB             ; Set port B to all outputs



        bcf     STATUS,RP0        ; Switch back to bank 0
        bcf     PORTA,speaker     ; Set speaker output low (don't use here)
        call    init_LCD          ; Initialize the LCD
        call    display_version   ; Display title and version                  
;
;       Enter Calibrate Mode if push button is pressed while turning the 
;       power on.
;
        bcf     PB_flags,PB1Calibrate_Active ; Default - calibrate is not active
        btfsc   PORTA,pb_1        ; Tuning-increment/Cal pushbutton pressed?   
        goto    read_EEocs        ; No, get clock freq from EEPROM
        bsf     PB_flags,PB1Calibrate_Active ; Default - calibrate is not active
        call    calibrate         ;   and call routine
        bcf     PB_flags,PB1Calibrate_Active ; Now calibrate is done
;       
;       Get the reference oscillator constant from the EEPROM.
;
read_EEocs
        bsf     STATUS,RP0        ; Switch to bank 1
        clrf    EEADR             ; Reset the EEPROM read address
        call    read_EEPROM       ; Read EEPROM (all in bank 1)
        movf    EEDATA,w          ; Get the first osc byte
        bcf     STATUS,RP0        ; Back to bank 0 for store
        movwf   osc_0             ; Save osc frequency
        bsf     STATUS,RP0        ; Switch to bank 1
        call    read_EEPROM       ; Read EEPROM (all in bank 1)
        movf    EEDATA,w          ; 
        bcf     STATUS,RP0        ; Back to bank 0 for store
        movwf   osc_1             ; Save it
        bsf     STATUS,RP0        ; Switch to bank 1
        call    read_EEPROM       ; Read EEPROM (all in bank 1)
        movf    EEDATA,w          ; 
        bcf     STATUS,RP0        ; Back to bank 0 for store
        movwf   osc_2             ; Save it
        bsf     STATUS,RP0        ; Switch to bank 1
        call    read_EEPROM       ; Read EEPROM (all in bank 1)
        movf    EEDATA,w          ;
        bcf     STATUS,RP0        ; Back to bank 0 for store
        movwf   osc_3             ; Save it
;
;       Set the power on frequency to the defined value.
;       (They always follow the osc bytes)                                     
;
        bsf     STATUS,RP0        ; Switch to bank 1
        call    read_EEPROM       ; Read EEPROM                                
        movf    EEDATA,w          ; Get the first default freq byte            
        bcf     STATUS,RP0        ; Back to bank 0 for store
        movwf   freq_0            ; Save it
        bsf     STATUS,RP0        ; Switch to bank 1
        call    read_EEPROM       ; Read EEPROM                                
        movf    EEDATA,w          ; Get the next freq byte                     
        bcf     STATUS,RP0        ; Back to bank 0 for store
        movwf   freq_1            ; Save it
        bsf     STATUS,RP0        ; Switch to bank 1
        call    read_EEPROM       ; Read EEPROM                                
        movf    EEDATA,w          ; Get the next freq byte                     
        bcf     STATUS,RP0        ; Back to bank 0 for store
        movwf   freq_2            ; Save it                                    
        bsf     STATUS,RP0        ; Switch to bank 1
        call    read_EEPROM       ; Read EEPROM                                 
        movf    EEDATA,w          ; Get the last freq byte                     
        bcf     STATUS,RP0        ; Back to bank 0 for store
        movwf   freq_3            ; Save it
;
;       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_word     ; Send the power-on frequency to the
                                  ;   AD9850/AD9851 in serial mode
        call    send_dds_word     ; Send it twice. Sometimes needed for        
                                  ;   a clean start-up                          
;
;       Get the power on encoder value.
;
        movf    PORTA,w           ; Read port A
        movwf   ren_read          ; Save it in ren_read
        movlw   0x03              ; Get encoder mask (RA0 and RA1)  
        andwf   ren_read,w        ; Get encoder bits
        movwf   ren_old           ; Save in ren_old
;
;       Initialize variables.
;
        clrf    ren_timer_1       ; Initialize the encoder speed timer        
        movlw   0x40              ;   to                                     
        movwf   ren_timer_0       ;     0x0040                               
        clrf    last_dir          ; Clear the knob direction indicator
        clrf    band              ; Clear the band indicator
; 
; Fall into the Main Program Loop
;
; *****************************************************************************
; *                                                                           *
; * Purpose:  This is the Main Program Loop. The program's main loop          *
; *           calls poll_encoder, which continuously polls the rotary shaft   *
; *           encoder.  When the shaft encoder has changed, the direction     *
; *           it moved is determined and stored in last_dir.  The subroutine  *
; *           then returns to main.                                           *                       
; *                                                                           *
; *           If the tuning-increment pushbutton (PIC-EL PB_1) is not pressed,*
; *           then the variable fstep is calculated based on the delay        *
; *           between shaft encoder changes.  ren_timer contains the delay    *
; *           value determined by the poll_encoder subroutine.  The variable  *
; *           fstep is added or subtracted from the current VFO frequency     *
; *           stored in freq. The contents of freq are then converted to a    *
; *           BCD number in subroutine bin2BCD.  The subroutine show_freq is  *
; *           then called to display the result on the Liquid Crystal Display.*
; *           Next, the subroutine calc_dds_word is used to calculate the DDS *
; *           frequency control word from the values in freq and osc.         *
; *           The result is stored in AD9850/AD9851.  This data is transferred*
; *           to the AD9851 DDS chip by calling the subroutine send_dds_word. *
; *                                                                           *
; *           If the bandswitch pushbutton (PIC-EL PB_2) is pressed while     *
; *           turning the encoder then the freq is loaded with a constant     *
; *           stored in band_table.  The variable band is used as an index    *
; *           into the table.  Band is incremented or decremented based on    *
; *           the encoder direction.                                          *
; *                                                                           *
; *   Input:  None.                                                           *
; *                                                                           *
; *  Output:  None.                                                           *
; *                                                                           *
; *****************************************************************************
;
main
        call    poll_encoder      ; Check for knob movement (wait there!)      
                                  ; Return here when encoder change detected   
        btfsc   ren_read,pb_2     ; Is bandswitch pushbutton pressed?          
        goto    step              ; No, just step                              
        call    change_band       ; Yes, change_band                           
        goto    main              ; Continue main loop                         
step    ;                                                                      
;
;       Determine step size to use (1 Hz or 1 kHz).
;
        clrf    fstep_3           ; Guess that we want 1 Hz steps by 
        clrf    fstep_2           ;   setting fstep to one.
        clrf    fstep_1           ; 
        movlw   0x01              ;
        movwf   fstep_0           ; 
        btfsc   ren_read,pb_1     ; Is the tuning-increment button pressed?    
        goto    go_step           ; No, use the 1 Hz step
        movlw   0xE8              ; Yes, set the step value to 1 kHz
        movwf   fstep_0           ;   by setting fstep_0 to 0xE8 and
        movlw   0x03              ;   fstep_1 to 0x03
        movwf   fstep_1           ; 
        goto    go_step           ; Use the 1 kHz step
;
;       Adjust the tuning step based on ren_timer.  ren_timer is incremented
;       by 8 from its initial value of 0x0040 each time the poll_encoder finds 
;       no change in the encoder input, until the high bit of ren_timer_1
;       becomes a one.  The default fstep of 1 Hz is multiplied by two for
;       each leading zero in ren_timer, up to a maximum of 9 times. (This is
;       because ren_timer starts at 0x0040, only the first nine bits can be 
;       zero in a row).  The faster the knob is turned, the lower the number 
;       in ren_timer will be, and the larger the step value will be.
;   
;
bump_step
        bcf     STATUS,C          ; Clear the carry flag
        rlf     fstep_0,f         ; Multiply the step by 2 by rotating left
        rlf     fstep_1,f         ; 
        rlf     fstep_2,f         ; 
        rlf     fstep_3,f         ; 
go_step
        rlf     ren_timer_0,f     ; Multiply the encoder timer by 2
        rlf     ren_timer_1,f     ;
        btfss   STATUS,C          ; Has a one floated to the carry yet?
        goto    bump_step         ; No, then double the step size
;
;       Based on the knob direction, either add or subtract the increment, 
;       then update the LCD and DDS.
; 
        btfsc   last_dir,1        ; Is the knob going up?
        goto    up                ; Yes, then add the increment
down
        call    sub_step          ; Subtract fstep from freq
        goto    update            ; Update LCD and DDS                         
up
        call    add_step          ; Add fstep to freq
        call    check_add         ; Make sure we did not exceed the maximum
update 
        call    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_word     ; Send the control word to the DDS chip
        goto    main              ; Continue main loop                         
;
; *****************************************************************************
; *                                                                           *
; * 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             ; 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
        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_word     ; Send the control word to the DDS chip
        return                    ; Return to main loop                        
;
; *****************************************************************************
; *                                                                           *
; * Purpose:  This routine reads the frequency value of a band table entry    *
; *           pointed to by band and returns it in freq_3...freq_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   freq_3            ; Save it in freq_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   freq_2            ; Save it in freq_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   freq_1            ; Save it in freq_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   freq_0            ; Save it in freq_0
        movlw   0x03              ; Get a constant three
        subwf   band,f            ; Restore original value of band
        return                    ; Return to the caller
;
; *****************************************************************************
; *                                                                           *
; * Purpose:  Power on initialization of Liquid Crystal Display.  The LCD     *
; *           controller chip must be equivalent to an Hitachi 44780.  The    *
; *           LCD is assumed to be a 8x1 or a 16x1 display.                   *
; *                                                                           *
; *   Input:  None                                                            *
; *                                                                           *
; *  Output:  None                                                            *
; *                                                                           *
; *****************************************************************************
;
init_LCD
        call    wait_64ms         ; Wait for LCD to power up
;        Put 4-bit command in RB3..RB0                                         
;        PIC's RB3..RB0 lines connect to LCD's DB7..DB4 (pins 14-11)            
        movlw   0x03              ; LCD init instruction (First)               
        movwf   PORTB             ; Send to LCD via RB3..RB0                   
        bsf     PORTB,LCD_e       ; Set the LCD E line high,
        call    wait_64ms         ;   wait a "long" time,
        bcf     PORTB,LCD_e       ;   and then Clear E 
        movlw   0x03              ; LCD init instruction (Second)              
        movwf   PORTB             ; Send to LCD via RB3..RB0                   
        bsf     PORTB,LCD_e       ; Set E high,
        call    wait_32ms         ;   wait a while,
        bcf     PORTB,LCD_e       ;   and then Clear E 
        movlw   0x03              ; LCD init instruction (Third)               
        movwf   PORTB             ; Send to LCD via RB3..RB0                   
        bsf     PORTB,LCD_e       ; Set E high,
        call    wait_32ms         ;   wait a while,
        bcf     PORTB,LCD_e       ;   and then Clear E
        movlw   0x02              ; 4-bit mode instruction                     
        movwf   PORTB             ; Send to LCD via RB3..RB0                   
        bsf     PORTB,LCD_e       ; Set E high,
        call    wait_16ms         ;   wait a while,
        bcf     PORTB,LCD_e       ;   and then Clear E
        movlw   0x28              ; 1/16 duty cycle, 5x8 matrix
        call    cmnd2LCD          ; Send command in w to LCD
        movlw   0x08              ; Display off, cursor and blink off 
        call    cmnd2LCD          ; Send command to LCD
        movlw   0x01              ; Clear and reset cursor
        call    cmnd2LCD          ; Send command in w to LCD
        movlw   0x06              ; Set cursor to move right, no shift
        call    cmnd2LCD          ; Send command in w to LCD
        movlw   0x0C              ; Display on, cursor and blink off
        call    cmnd2LCD          ; Send command in w to LCD
        return                    ; 
;
; ****************************************************************************P
; *                                                                           P
; * Purpose:  Display version and other info on LCD for 2 seconds             P
; *           upon power-up                                                   P
; *                                                                           P
; *   Input:  MCODE_REV_0 through MCODE_REV_4 set up                          P
; *                                                                           P
; *  Output:  LCD displays debug info                                         P
; *                                                                           P
; ****************************************************************************P
;
display_version
#IF LCDCHAR == 8
        movlw   0x80              ; Point LCD at digit 1                
        call    cmnd2LCD          ;
        movlw   'P'               ; digit 1              
        call    data2LCD          ;
        movlw   'I'               ; digit 2                  
        call    data2LCD          ;
        movlw   'C'               ; digit 3                
        call    data2LCD          ;
        movlw   'E'               ; digit 4               
        call    data2LCD          ;
        movlw   'L'               ; digit 5              
        call    data2LCD          ;
        movlw   'g'               ; digit 6              
        call    data2LCD          ;
        movlw   'e'               ; digit 7               
        call    data2LCD          ;
        movlw   'n'               ; digit 8               
        call    data2LCD          ;
        call    wait_a_sec        ; Wait one second
;
        movlw   0x80              ; Point LCD at digit 1              
        call    cmnd2LCD          ;
        movlw   MCODE_REV_0       ; Get mcode rev byte
        call    data2LCD          ;   and display it (digit 1)
        movlw   MCODE_REV_1       ; Get mcode rev byte
        call    data2LCD          ;   and display it (digit 2)
        movlw   MCODE_REV_2       ; Get mcode rev byte
        call    data2LCD          ;   and display it (digit 3)
        movlw   MCODE_REV_3       ; Get mcode rev byte
        call    data2LCD          ;   and display it (digit 4)
        movlw   MCODE_REV_4       ; Get mcode rev byte
        call    data2LCD          ;   and display it (digit 5)
        movlw   MCODE_REV_5       ; Get mcode rev byte
        call    data2LCD          ;   and display it (digit 6)
        movlw   MCODE_REV_6       ; Get mcode rev byte
        call    data2LCD          ;   and display it (digit 7)
        movlw   MCODE_REV_7       ; Get mcode rev byte
        call    data2LCD          ;   and display it (digit 8)
#ENDIF ; End of 8-character LCD code
#IF LCDCHAR == 16 ; Start 16-character LCD code
        movlw   0x80              ; Point LCD at digit 1                
        call    cmnd2LCD          ;
        movlw   'P'               ; digit 1              
        call    data2LCD          ;
        movlw   'I'               ; digit 2                  
        call    data2LCD          ;
        movlw   'C'               ; digit 3                
        call    data2LCD          ;
        movlw   'E'               ; digit 4               
        call    data2LCD          ;
        movlw   'L'               ; digit 5              
        call    data2LCD          ;        
        movlw   'g'               ; digit 6              
        call    data2LCD          ;
        movlw   'e'               ; digit 7               
        call    data2LCD          ;
        movlw   'n'               ; digit 8               
        call    data2LCD          ;
#IFDEF LCD2LINES  ; 16 x 2 LCD
        movlw   0x88              ; Point LCD at digit 9
#ELSE             ; 16 x 1 LCD
        movlw   0xC0              ; Point LCD at digit 9
#ENDIF
        movwf   LCD_char          ; 
        call    cmnd2LCD          ; Send command to LCD
        movlw   MCODE_REV_0       ; Get mcode rev byte
        call    data2LCD          ;   and display it (digit 9)
        movlw   MCODE_REV_1       ; Get mcode rev byte
        call    data2LCD          ;   and display it (digit 10)
        movlw   MCODE_REV_2       ; Get mcode rev byte
        call    data2LCD          ;   and display it (digit 11)
        movlw   MCODE_REV_3       ; Get mcode rev byte
        call    data2LCD          ;   and display it (digit 12)
        movlw   MCODE_REV_4       ; Get mcode rev byte
        call    data2LCD          ;   and display it (digit 13)
        movlw   MCODE_REV_5       ; Get mcode rev byte
        call    data2LCD          ;   and display it (digit 14)
        movlw   MCODE_REV_6       ; Get mcode rev byte
        call    data2LCD          ;   and display it (digit 15)
        movlw   MCODE_REV_7       ; Get mcode rev byte
        call    data2LCD          ;   and display it (digit 16)
#IFDEF LCD2LINES
        movlw   0xC0              ; Point LCD at digit 1 of second line               
        call    cmnd2LCD          ;
        movlw   'A'               ; digit 1              
        call    data2LCD          ;
        movlw   'A'               ; digit 2                  
        call    data2LCD          ;
        movlw   '0'               ; digit 3                
        call    data2LCD          ;
        movlw   'Z'               ; digit 4               
        call    data2LCD          ;
        movlw   'Z'               ; digit 5              
        call    data2LCD          ;        
        movlw   ' '               ; digit 6              
        call    data2LCD          ;
        movlw   '/'               ; digit 7               
        call    data2LCD          ;
        movlw   ' '               ; digit 8               
        call    data2LCD          ;
        movlw   'K'               ; Space in digit 9
        call    data2LCD          ;
        movlw   'a'               ; digit 10             
        call    data2LCD          ;
        movlw   'n'               ; digit 11                 
        call    data2LCD          ;
        movlw   'g'               ; digit 12               
        call    data2LCD          ;
        movlw   'a'               ; digit 13              
        call    data2LCD          ;
        movlw   ' '               ; digit 14              
        call    data2LCD          ;
        movlw   'U'               ; digit 15             
        call    data2LCD          ;        
        movlw   'S'               ; digit 16             
        call    data2LCD          ;        
#ENDIF 
#ENDIF ; End of 16-character LCD
        call    wait_a_sec        ; Wait one second
;
#IFDEF LCD2LINES
        movlw   0xC0              ; Point LCD at digit 1 of second line 
        call    cmnd2LCD          ;
        movlw   ' '               ; digit 1              
        call    data2LCD          ;
        movlw   ' '               ; digit 2                  
        call    data2LCD          ;
        movlw   ' '               ; digit 3                
        call    data2LCD          ;
        movlw   ' '               ; digit 4               
        call    data2LCD          ;
        movlw   ' '               ; digit 5              
        call    data2LCD          ;        
        movlw   ' '               ; digit 6              
        call    data2LCD          ;
        movlw   ' '               ; digit 7               
        call    data2LCD          ;
        movlw   ' '               ; digit 8               
        call    data2LCD          ;
        movlw   ' '               ; digit 9
        call    data2LCD          ;
        movlw   ' '               ; digit 10             
        call    data2LCD          ;
        movlw   ' '               ; digit 11                 
        call    data2LCD          ;
        movlw   ' '               ; digit 12               
        call    data2LCD          ;
        movlw   ' '               ; digit 13              
        call    data2LCD          ;
        movlw   ' '               ; digit 14              
        call    data2LCD          ;
        movlw   ' '               ; digit 15             
        call    data2LCD          ;        
        movlw   ' '               ; digit 16             
        call    data2LCD          ;        
#ENDIF 
;
        return

;
; *****************************************************************************
; *                                                                           *
; * Purpose:  This routine adds the 32 bit value of fstep to the 32 bit       *
; *           value in freq.  When incrementing, the fstep value is a         *
; *           positive integer.  When decrementing, fstep is the complement   *
; *           of the value being subtracted.                                  *
; *                                                                           *
; *   Input:  The 32 bit values in fstep and freq                             *
; *                                                                           *
; *  Output:  The sum of fstep and freq is stored in freq.  When incrementing *
; *           this value may exceed the maximum.  When decrementing, it may   *
; *           go negative.                                                    *
; *                                                                           *
; *****************************************************************************
add_step
        movf    fstep_0,w         ; Get low byte of the increment
        addwf   freq_0,f          ; Add it to the low byte of freq
        btfss   STATUS,C          ; Any carry?
        goto    add1              ; No, add next byte
        incfsz  freq_1,f          ; Ripple carry up to the next byte
        goto    add1              ; No new carry, add next byte
        incfsz  freq_2,f          ; Ripple carry up to the next byte
        goto    add1              ; No new carry, add next byte
        incf    freq_3,f          ; Ripple carry up to the highest byte
add1
        movf    fstep_1,w         ; Get the next increment byte
        addwf   freq_1,f          ; Add it to the next higher byte
        btfss   STATUS,C          ; Any carry?
        goto    add2              ; No, add next byte
        incfsz  freq_2,f          ; Ripple carry up to the next byte
        goto    add2              ; No new carry, add next byte
        incf    freq_3,f          ; Ripple carry up to the highest byte
add2
        movf    fstep_2,w         ; Get the next to most significant increment
        addwf   freq_2,f          ; Add it to the freq byte
        btfss   STATUS,C          ; Any carry?
        goto    add3              ; No, add last byte
        incf    freq_3,f          ; Ripple carry up to the highest byte
add3
        movf    fstep_3,w         ; Get the most significant increment byte
        addwf   freq_3,f          ; Add it to the most significant freq
        return                    ; Return to the caller
;
; *****************************************************************************
; *                                                                           *
; * Purpose:  Check if freq exceeds the upper limit.                          *
; *                                                                           *
; *   Input:  The 32 bit values in freq                                       *
; *                                                                           *
; *  Output:  If freq is below the limit, it is unchanged.  Otherwise, it is  *
; *           set to equal the upper limit.                                   *
; *                                                                           *
; *****************************************************************************
;
check_add
;
;       Check the most significant byte.
;
        movlw   0xFF-limit_3      ; Get (FF - limit of high byte)
        addwf   freq_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   freq_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   freq_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   freq_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   freq_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   freq_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   freq_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   freq_0            ; Set it in freq
        movlw   limit_1           ; Get the next byte limit
        movwf   freq_1            ; Set it in freq_1
        movlw   limit_2           ; Get the next byte limit
        movwf   freq_2            ; Set it in freq_2
        movlw   limit_3           ; Get the most significant limit
        movwf   freq_3            ; Set it in freq_3
exit1
        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 freq.                                   *
; *                                                                           *
; *  Output:  The updated value in freq.                                      *
; *                                                                           *
; *****************************************************************************
;
sub_step
        comf    fstep_0,f         ; Subtraction of fstep from
        comf    fstep_1,f         ;   freq is done by adding the
        comf    fstep_2,f         ;   twos compliment of fstep to
        comf    fstep_3,f         ;   freq.
        incfsz  fstep_0,f         ; Increment last byte
        goto    comp_done         ; Non-zero, continue
        incfsz  fstep_1,f         ; Increment next byte
        goto    comp_done         ; Non-zero, continue
        incfsz  fstep_2,f         ; Increment next byte
        goto    comp_done         ; Non-zero, continue
        incf    fstep_3,f         ; Increment the high byte
comp_done
        call    add_step          ; Add the compliment to do the subtraction
;
;       If the frequency has gone negative, clear it to zero.
;
        btfss   freq_3,7          ; Is high order frequency byte "negative"?   
        goto    exit2             ; No, keep going
set_min
        clrf    freq_0            ; Yes, set the frequency to zero
        clrf    freq_1            ; 
        clrf    freq_2            ; 
        clrf    freq_3            ; 
exit2
        return                    ; Return to the caller
;
; *****************************************************************************
; *                                                                           *
; * Purpose:  This routine does the following:                                *
; *             1.  Records how long it took for the knob to move a notch     *
; *                 in ren_timer.                                             *
; *             2.  Clears the watchdog timer.                                *
; *             3.  Reads the encoder bits until a change is detected, then   *
; *                 determines the direction the knob was moved.              *
; *                                                                           *
; *   Input:  Knob input read from port A                                     *
; *           ren_old -> the last encoder bits read                           *
; *           last_dir -> the last direction moved                            *
; *                                                                           *
; *  Output:  ren_timer -> an indication the speed of the knob.               *
; *           ren_new -> the current encoder bits                             *
; *           last_dir -> the last direction (0 = down, 2 = up)               *
; *                                                                           *
; *****************************************************************************
;
poll_encoder
        clrf    ren_timer_1       ; Put starting values in ren_timer cells     
        movlw   0x40              ; Start with the high bit set                
        movwf   ren_timer_0       ;   in ren_timer_0                           
read_encoder
        btfss   PB_flags,PB1Calibrate_Active ; Is calibration now active? 
        call    PB_look           ; No, look at PB's for possible 1 MHz jump       
        btfsc   ren_timer_1,7     ; Has the bit floated to top of ren_timer_1? 
        goto    no_inc            ; Yes, don't move it any further
        movlw   0x08              ; No, keep going                             
        addwf   ren_timer_0,f     ; Add constant to ren_timer_0                
        btfsc   STATUS,C          ; Did the add force a carry?
        incf    ren_timer_1,f     ; Yes, then add one to ren_timer_1
no_inc                            ; 
        movf    PORTA,w           ; Get the current encoder value
        movwf   ren_read          ; Save it
        movlw   0x03              ; Get encoder mask (to isolate RA0 and RA1)  
        andwf   ren_read,w        ; Isolated encoder bits into W               
        movwf   ren_new           ; Save new value
        xorwf   ren_old,w         ; Has it changed?
        btfsc   STATUS,Z          ; Check zero-flag (zero if no change)        
        goto    read_encoder      ; No change, keep looking until it changes   
;                                 ; Zero-flag is not set, so continue on        
; It changed. Now determine which direction the encoder turned.                
;============================================================================= 
;   Encoder bits are on RA0 and RA1 - the two low order bits of ren_new          
;   A and B are "gray code" - 90 degrees out of phase (quadrature)             
;         ___     ___                                                          
;        |   |   |   |                                                         
; A  ____|   |___|   |___                                                      
;           ___     ___                                                        
;          |   |   |   |                                                       
; B     ___|   |___|   |___                                                    
;       ^ ^ ^ ^ ^ ^ ^ ^                                                        
;       a b c d a b c d                                                        
;                                                                              
;              A B                                                             
; At point a:  0 0                                                             
; At point b:  1 0                                                             
; At point c:  1 1                                                             
; At point d:  0 1                                                             
;                                                                              
; Going UP, the sequence is a,b,c,d,a,b,c,d, etc. so the sequence is:          
;     00, 10, 11, 01, 00, 10, 11, 01, etc.                                     
;                                                                              
; Going DOWN, the sequence is d,c,b,a,d,c,b,a, etc. so the sequence is:        
;     01, 11, 10, 00, 01, 11, 10, 00, etc.                                     
;                                                                              
; To determine if the sequence is UP or DOWN:                                  
;   1) Take the "Right-Bit" of any pair.                                       
;   2) XOR it with the "Left-Bit" of the next pair in the sequence.            
;   3) If the result is 1 it is UP                                             
;      If the result is 0 it is DOWN                                           
;                                                                              
; The direction flag is 0 (DOWN) or 2 (UP) because of bit positioning          
;============================================================================= 
        bcf     STATUS,C          ; Clear the carry bit to prepare for rotate  
        rlf     ren_old,f         ; Rotate old bits left to align "Right-Bit"  
        movf    ren_new,w         ; Set up new bits in W                       
        xorwf   ren_old,f         ; XOR old (left shifted) with new bits       
        movf    ren_old,w         ; Put XOR results into W also                
        andlw   0x02              ; Mask to look at only "Left-Bit" of pair    
        movwf   next_dir          ; Save result (in W) as direction (bit=UP)   
        xorwf   last_dir,w        ; See if direction is same as before         
;
;       Prevent encoder slip from giving a false change in direction.
;
        btfsc   STATUS,Z          ; Zero flag set? (i.e, is direction same?)   
        goto    pe_continue       ; Yes, same direction so no slip; keep going 
        movf    next_dir,w        ; No Zero-flag, so direction changed         
        movwf   last_dir          ; Update the direction indicator             
        movf    ren_new,w         ; Save the current encoder bits (now in W)   
        movwf   ren_old           ;   for next time                            
        goto    read_encoder      ; Try again
pe_continue
        clrf    last_dir          ; Clear last_dir (default is DN)             
        btfss   ren_old,1         ; Are we going UP?                           
        goto    exit3             ; No, exit3                                  
up2 
        movlw   0x02              ; Get UP value                               
        movwf   last_dir          ;   and set in last_dir                      
exit3 
        movf    ren_new,w         ; Get the current encoder bits
        movwf   ren_old           ; Save them in ren_old for the next time
        return                    ; Return to the caller
;
; **************************************************************************** 
; *                                                                            
; * Purpose:  This routine is entered to see if the pushbuttons PB1 and PB2    
; *           being pressed.                                                   
; *           If both are being pressed, determine direction (depending on     
; *             which was first) and change the frequency UP/DN by 1 MHz.      
; *           If only one is being pressed, remember it as being "first".      
; *                                                                            
; *   Input:  None                                                             
; *   Output: PB_flag set up                                                    
; **************************************************************************** 
;                                                                              
PB_look ;                                                                      
        btfss   PORTA,pb_1        ; Is PB_1 being pressed?                     
        goto    PB_yes1           ; Yes, PB_yes1                               
PB_no1       ; 0,X                                                             
        btfsc   PORTA,pb_2        ; Not1, but is PB_2 being pressed?           
        goto    PB_neither        ; No, Neither                                
        goto    PB_2butnot1       ; Yes, PB_2butnot1                           
PB_yes1     ; 1,X                                                              
        btfsc   PORTA,pb_2        ; Is PB_2 being pressed?                     
        goto    PB_1butnot2       ; No, PB_1butnot2                            
        ; fall through to PB_both ; Yes, PB_both                               
PB_both     ; 1,2                                                              
        ; both set, so add code to find out which was first.                   
        btfsc   PB_flags,PB1first ; Was PB1 pressed first?                     
        goto    PB_up             ; Yes, going down by 1 MHz                   
        goto    PB_dn             ; NO, PB2 was first, so going UP by 1 MHz    
PB_2butnot1 ; 0,2                                                              
        bsf     PB_flags,PB1first ; Set PB1 first                              
        goto    PB_exit           ; Exit                                       
PB_1butnot2 ; 1,0                                                              
        bcf     PB_flags,PB1first ; Clear PB1 first                            
        goto    PB_exit           ; Exit                                       
PB_neither  ; 0,0                                                              
        clrf    PB_flags          ; Neither, so clear flags                    
        goto    PB_exit           ; Exit                                       
;                                                                              
PB_up ;                                                                        
        ; PB_2 was pressed first, then PB_1                                    
        ; Wait for PB_1 to be released                                         
        ; (If wait is longer than 2 seconds, call update_EEPROM and exit)      
        movlw   0x10              ; Set up                                     
        movwf   PB_wait_count     ;   loop count                               
PB_up_release_wait ;                                                           
        btfsc   PORTA,pb_1        ; Is PB_1 still behing held?                 
        goto    PB_up_released    ; No, it's released, so move up              
        call    wait_128ms        ; Wait a bit                                 
        decfsz  PB_wait_count,f   ; Have we waited long enough?                
        goto    PB_up_release_wait ; No, continue waiting                      
                                  ; Button was held longer than 2 seconds, so  
        call    update_EEPROM     ; Update EEPROM with current frequency       
        goto    PB_exit           ;   and exit                                 
;                                                                              
PB_up_released ;                                                               
        clrf    fstep_3           ;                                            
        movlw   0x0F              ; Setup for step of 1 M                      
        movwf   fstep_2           ;                                            
        movlw   0x42              ;                                            
        movwf   fstep_1           ;                                            
        movlw   0x40              ;                                            
        movwf   fstep_0           ;                                            
        call    add_step          ; Add a step and update <freq_0:freq_3>      
        call    check_add         ; See if band limits reached. If so, adjust   
                                  ;   and update <freq_0:freq_3>               
        goto    PB_update         ;                                            
;                                                                              
PB_dn ;                                                                        
        ; PB_1 was pressed first, then PB_2                                    
        ; Wait for PB_2 to be released                                         
        ; (If wait is longer than 2 seconds, call update_EEPROM and exit)      
        movlw   0x10              ; Set up                                     
        movwf   PB_wait_count     ;   loop count                               
PB_dn_release_wait ;                                                           
        btfsc   PORTA,pb_2        ; Is PB_2 still being held?                  
        goto    PB_dn_released    ; No, it's released, so move up              
        call    wait_128ms        ; Wait a bit                                 
        decfsz  PB_wait_count,f   ; Have we waited long enough?                
        goto    PB_dn_release_wait ; No, continue waiting                      
                                  ; Button was held longer than 2 seconds, so  
        call    update_EEPROM     ; Update EEPROM with current frequency       
        goto    PB_exit           ;   and exit                                 
;                                                                              
PB_dn_released ;                                                               
        clrf    fstep_3           ;                                            
        movlw   0x0F              ; Setup for step of 1 M                      
        movwf   fstep_2           ;                                            
        movlw   0x42              ;                                            
        movwf   fstep_1           ;                                            
        movlw   0x40              ;                                            
        movwf   fstep_0           ;                                            
        call    sub_step          ; Add a step and update <freq_0:freq_3>      
;                                                                              
PB_update ;                                                                    
        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_word     ; Send the control word to the DDS chip      
;                                                                              
PB_exit ;                                                                      
        return  ;                                                              
;                                                                              
; *****************************************************************************
; *                                                                           *
; * Purpose:  This routine is entered at start up if the calibrate            *
; *              push button is (PIC-EL PB_1) is pressed at power-on time.    *
; *                                                                           *
; *           If a 8-character LCD is used,"10000000" is displayed on the LCD *
; *           If a 16-character LCD is used," 10,000.000 CAL " is displayed   *
; *             on the LCD.                                                   *
; *                                                                           *
; *           The DDS chip is programmed to produce 10 MHz, based on the      *
; *           osc value stored in the EEPROM.  As long as the button is       *
; *           pressed, the osc value is slowly altered to allow the output    *
; *           to be trimmed to exactly 10 MHz.  Once the encoder is turned    *
; *           after the button is released, 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 setting freq
        movwf   freq_0            ;   to the binary equivalent of 10 MHz
        movlw   0x96              ;    
        movwf   freq_1            ;    
        movlw   0x98              ;    
        movwf   freq_2            ;   
        movlw   0x00              ;   
        movwf   freq_3            ;   
;
; Read the starting reference oscillator value from EEPROM.
;
        bsf     STATUS,RP0        ; Switch to bank 1
        clrf    EEADR             ; Reset the EEPROM read address
        call    read_EEPROM       ; Read first byte from EEPROM (all in bank 1)
        movf    EEDATA,w          ; Get the first osc byte
        bcf     STATUS,RP0        ; Back to bank 0 for store
        movwf   osc_0             ; Save osc frequency
        bsf     STATUS,RP0        ; Switch to bank 1
        call    read_EEPROM       ; Read second byte from EEPROM (all in bank 1)
        movf    EEDATA,w          ; 
        bcf     STATUS,RP0        ; Back to bank 0 for store
        movwf   osc_1             ; Save it
        bsf     STATUS,RP0        ; Switch to bank 1
        call    read_EEPROM       ; Read third byte from EEPROM (all in bank 1)
        movf    EEDATA,w          ; 
        bcf     STATUS,RP0        ; Back to bank 0 for store
        movwf   osc_2             ; Save it
        bsf     STATUS,RP0        ; Switch to bank 1
        call    read_EEPROM       ; Read fourth byte from EEPROM (all in bank 1)
        movf    EEDATA,w          ;
        bcf     STATUS,RP0        ; Back to bank 0 for store
        movwf   osc_3             ; Save it
        call    bin2BCD           ; Calculate BCD version of 10 MHz
        call    show_freq         ; Display the frequency on the LCD

#IF LCDCHAR == 16
        movlw   0xC4              ; Point LCD at position 13
        movwf   LCD_char          ; 
        call    cmnd2LCD          ; Send command to LCD
        movlw   'C'               ; Send a C (position 13)
        movwf   LCD_char          ; 
        call    data2LCD          ; 
        movlw   'A'               ; Send an A (position 14)
        movwf   LCD_char          ; 
        call    data2LCD          ; 
        movlw   'L'               ; Send an L (position 15)
        movwf   LCD_char          ; 
        call    data2LCD          ; 
        ;                         ; (position 16 is blank)
#ENDIF

cal_loop
        call    calc_dds_word     ; Calculate DDS value based on current osc
        call    send_dds_word     ; Update the DDS chip
        call    poll_encoder      ; Wait until the encoder has moved.
        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
        btfsc   ren_read,2        ; Was the encoder changing slowly?
        goto    update_osc        ; Yes, then continue with small increment
        movlw   0x80              ; No, then use the large increment
        movwf   fstep_0           ;
update_osc
        nop                       ; Wait a cycle
        btfsc   last_dir,1        ; Are we moving down?
        goto    faster            ; No, increase the osc value
;
;       slower
;
        comf    fstep_0,f         ; Subtraction of fstep is done by
        comf    fstep_1,f         ;   adding the twos compliment of fsetp
        comf    fstep_2,f         ;   to osc
        comf    fstep_3,f         ; 
        incfsz  fstep_0,f         ; Increment last byte
        goto    faster            ; Non-zero, continue
        incfsz  fstep_1,f         ; Increment next byte
        goto    faster            ; Non-zero, continue
        incfsz  fstep_2,f         ; Increment next byte
        goto    faster            ; Non-zero, continue
        incf    fstep_3,f         ; Increment the high byte
faster
        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
        btfss   PORTA,pb_1        ; Tuning-increment pushbutton pressed?       
        goto    cal_loop          ; Yes, stay in calibrate mode
;
; Write final value to EPROM
;
        movf    osc_0,w           ; Get the first osc byte to record (bank 0)
        bsf     STATUS,RP0        ; Switch to bank 1
        clrf    EEADR             ; osc bytes start at EEPROM address 0
        movwf   EEDATA            ; Put byte in EEPROM write location 
        call    write_EEPROM      ; (all in bank 1)
        bcf     STATUS,RP0        ; Back to bank 0
        movf    osc_1,w           ; Get the second byte to record (bank 0)
        bsf     STATUS,RP0        ; Switch to bank 1
        movwf   EEDATA            ; Put byte in EEPROM write location 
        call    write_EEPROM      ; (all in bank 1)
        bcf     STATUS,RP0        ; Back to bank 0
        movf    osc_2,w           ; Get the third byte to record (bank 0)
        bsf     STATUS,RP0        ; Switch to bank 1
        movwf   EEDATA            ; Put byte in EEPROM write location 
        call    write_EEPROM      ; (all in bank 1)
        bcf     STATUS,RP0        ; Back to bank 0
        movf    osc_3,w           ; Get the fourth byte to record (bank 0)
        bsf     STATUS,RP0        ; Switch to bank 1
        movwf   EEDATA            ; Put byte in EEPROM write location 
        call    write_EEPROM      ; (all in bank 1)
        bcf     STATUS,RP0        ; Back to bank 0
        return                    ; Return to the caller

;                                                                             
; **************************************************************************** 
; *                                                                            
; * Purpose:  This routine will save the current frequency in EEPROM. This     
; *           frequency will then be used as the initial frequency upon start  
; *           up.  The routine is entered by pressing and holding both PB_1    
; *           and PB_2 for more than 2 seconds.         .                      
; *                                                                            
; *   Input:  The original osc constant in EEPROM                              
; *                                                                            
; *  Output:  The corrected osc constant in EEPROM                             
; *                                                                            
; **************************************************************************** 
;                                                                              
update_EEPROM ;                                                                
        bsf     STATUS,RP0        ; Switch to bank 1
        movlw   EEstartup_adr     ; Get startup address location
        movwf   EEADR             ;   and set up for start of EEPROM writes    
        bcf     STATUS,RP0        ; Back to bank 0
        movf    freq_0,w          ; Get the first freq byte to write (Bank 0)
        bsf     STATUS,RP0        ; Switch to bank 1
        movwf   EEDATA            ; First freq byte to EEPROM Write register
        call    write_EEPROM      ; Write it (Bank 1)                                    
        bcf     STATUS,RP0        ; Back to bank 0
        movf    freq_1,w          ; Get the second freq byte to write (Bank 0)
        bsf     STATUS,RP0        ; Switch to bank 1
        movwf   EEDATA            ; Second freq byte to EEPROM Write register
        call    write_EEPROM      ; Write it (Bank 1)                                    
        bcf     STATUS,RP0        ; Back to bank 0
        movf    freq_2,w          ; Get the third freq byte to write (Bank 0)
        bsf     STATUS,RP0        ; Switch to bank 1
        movwf   EEDATA            ; Third freq byte to EEPROM Write register
        call    write_EEPROM      ; Write it (Bank 1)                                    
        bcf     STATUS,RP0        ; Back to bank 0
        movf    freq_3,w          ; Get the fourth freq byte to write (Bank 0)
        bsf     STATUS,RP0        ; Switch to bank 1
        movwf   EEDATA            ; Fourth freq byte to EEPROM Write register
        call    write_EEPROM      ; Write it (Bank 1)                                    
        bcf     STATUS,RP0        ; Back to bank 0
        return                    ;                                            
;                                                                              
; *****************************************************************************
; *                                                                           *
; * 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 AD9851_3 ... AD9851_0.                  *
; *                                                                           *
; *****************************************************************************
;
calc_dds_word
#ifdef MULTBY2 
        movf    freq_0,w          ; Move
        movwf   freq_0A           ;  freq
        movf    freq_1,w          ;   bytes
        movwf   freq_1A           ;    to
        movf    freq_2,w          ;     temporary
        movwf   freq_2A           ;      locations
        movf    freq_3,w          ;
        movwf   freq_3A           ;
        ; Multiply freq by 2 by rotating left once. Store in temp locations
        ; (freq_3A is most significant byte, freq_0A is least significant byte)
        bcf     STATUS,C          ; Clear carry bit
        rlf     freq_0A,f         ; Rotate left, 0 -> LS bit, MS bit -> Carry
        rlf     freq_1A,f         ; Rotate left, Carry->LS bit, MS bit->Carry
        rlf     freq_2A,f         ; Rotate left, Carry->LS bit, MS bit->Carry
        rlf     freq_3A,f         ; Rotate left, Carry->LS bit, MS bit->Carry
#endif
#ifdef MULTBY4
        movf    freq_0,w          ; Move
        movwf   freq_0A           ;  freq
        movf    freq_1,w          ;   bytes
        movwf   freq_1A           ;    to
        movf    freq_2,w          ;     temporary
        movwf   freq_2A           ;      locations
        movf    freq_3,w          ;
        movwf   freq_3A           ;
        ; Multiply freq by 4 by rotating left twice. Store in temp locations
        ; (freq_3A is most significant byte, freq_0A is least significant byte)
        bcf     STATUS,C          ; Clear carry bit
        rlf     freq_0A,f         ; Rotate left, 0 -> LS bit, MS bit -> Carry
        rlf     freq_1A,f         ; Rotate left, Carry->LS bit, MS bit->Carry
        rlf     freq_2A,f         ; Rotate left, Carry->LS bit, MS bit->Carry
        rlf     freq_3A,f         ; Rotate left, Carry->LS bit, MS bit->Carry
        bcf     STATUS,C          ; Clear carry bit
        rlf     freq_0A,f         ; Rotate left, 0 -> LS bit, MS bit -> Carry
        rlf     freq_1A,f         ; Rotate left, Carry->LS bit, MS bit->Carry
        rlf     freq_2A,f         ; Rotate left, Carry->LS bit, MS bit->Carry
        rlf     freq_3A,f         ; Rotate left, Carry->LS bit, MS bit->Carry
#endif
        clrf    AD9851_0          ; Clear the AD9850/AD9851 control word bytes
        clrf    AD9851_1          ; 
        clrf    AD9851_2          ; 
        clrf    AD9851_3          ; 
        clrf    AD9851_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          ; Get the "normal" freq_0 term
#ifdef MULTBY2
        movf    freq_0A,w         ; Get the "multiplied" freq_0 term
#endif
#ifdef MULTBY4
        movf    freq_0A,w         ; Get the "multiplied" freq_0 term
#endif
        addwf   AD9851_1,f        ; Add it in to total
        btfss   STATUS,C          ; Does this addition result in a carry?
        goto    add7              ; No, continue with next freq term
        incfsz  AD9851_2,f        ; Yes, add one and check for another carry
        goto    add7              ; No, continue with next freq term
        incfsz  AD9851_3,f        ; Yes, add one and check for another carry
        goto    add7              ; No, continue with next freq term
        incf    AD9851_4,f        ; Yes, add one and continue
add7
        movf    freq_1,w          ; Get the "normal" freq_0 term
#ifdef MULTBY2
        movf    freq_1A,w         ; Get the "multiplied" freq_1 term
#endif
#ifdef MULTBY4
        movf    freq_1A,w         ; Get the "multiplied" freq_1 term
#endif
        addwf   AD9851_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  AD9851_3,f        ; Yes, add one and check for another carry
        goto    add8              ; No, continue with next freq term
        incf    AD9851_4,f        ; Yes, add one and continue
add8
        movf    freq_2,w          ; Get the "normal" freq_2 term
#ifdef MULTBY2
        movf    freq_2A,w         ; Get the "multiplied" freq_2 term
#endif
#ifdef MULTBY4
        movf    freq_2A,w         ; Get the "multiplied" freq_2 term
#endif
        addwf   AD9851_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    AD9851_4,f        ; Yes, add one and continue
add9
        movf    freq_3,w          ; Get the "normal" freq_3 term
#ifdef MULTBY2
        movf    freq_3A,w         ; Get the "multiplied" freq_3 term
#endif
#ifdef MULTBY4
        movf    freq_3A,w         ; Get the "multiplied" freq_3 term
#endif
        addwf   AD9851_4,f        ; Add freq term to total in correct position
noAdd
        rrf     AD9851_4,f        ; Shift next multiplier bit into position
        rrf     AD9851_3,f        ; Rotate bits to right from byte to byte
        rrf     AD9851_2,f        ; 
        rrf     AD9851_1,f        ; 
        rrf     AD9851_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
#ifdef AD9850
        movlw   0x00              ; No clock multiplier (AD9850)              
#endif
#ifdef AD9851
        movlw   0x01              ; Turn on 6x clock multiplier (AD9851)      
#endif
        movwf   AD9851_4          ; Last byte to be sent                      
                                  ; Mult answer is in bytes _3 .. _0          
        return                    ; Done.
;
; *****************************************************************************
; *                                                                           *
; * Purpose:  This routine sends the AD9850/AD9851 control word to the DDS    *
; *           using a serial data transfer.                                   *
; *                                                                           *
; *   Input:  AD9851_4 ... AD9851_0                                           *
; *                                                                           *
; *  Output:  The DDS chip register is updated.                               *
; *                                                                           *
; *****************************************************************************
;
send_dds_word
        movlw   AD9851_0          ; Point FSR at Least Significant Byte       
        movwf   FSR               ; 
next_byte
        movf    INDF,w            ; 
        movwf   byte2send         ; 
        movlw   0x08              ; Set counter to 8
        movwf   bit_count         ; 
next_bit
        rrf     byte2send,f       ; Test if next bit is 1 or 0
        btfss   STATUS,C          ; Was it zero?
        goto    send0             ; Yes, send zero
        bsf     PORTB,DDS_dat     ; No, send one                               
        bsf     PORTB,DDS_clk     ; Toggle write clock                         
        bcf     PORTB,DDS_clk     ;                                            
        goto    break             ; 
send0
        bcf     PORTB,DDS_dat     ; Send zero                                  
        bsf     PORTB,DDS_clk     ; Toggle write clock                         
        bcf     PORTB,DDS_clk     ;                                            
break
        decfsz  bit_count,f       ; Has the whole byte been sent?
        goto    next_bit          ; No, keep going.
        incf    FSR,f             ; Start the next byte unless finished
        movlw   AD9851_4+1        ; Next byte (past the end)
        subwf   FSR,w             ; 
        btfss   STATUS,C          ;
        goto    next_byte         ;
        bsf     PORTB,DDS_load    ; Send load signal to the AD9850/AD9851             
        bcf     PORTB,DDS_load    ;                                            
        return                    ;
;
; *****************************************************************************
; *                                                                           *
; * 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.                       *
; *           If a 1x8 LCD display so display freq in Hz -  e.g. 14025000     *
; *           If a 1x16 LCD display so display freq kHz - e.g  14,025.000 kHz *
; *                                                                           *
; *   Input:  The values in BCD_4 ... BCD_0                                   *
; *                                                                           *
; *  Output:  The number displayed on the LCD                                 *
; *                                                                           *
; *  NOTE:                                                                    *
; *  When using 8x1 LCD:                                                      *
; *    Line 1 (left) addresses are  0x00 to 0x07   (0x80 to 0x87)             *
; *  When using a 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)             *
; *  When using a 16x2 LCD:                                                   *
; *    Line 1 addresses are  0x00 to 0x0F   (0x80 to 0x8F)                    *
; *    Line 2 addresses are  0x40 to 0x4F   (0xC0 to 0xCF)                    *
; *                                                                           *
; *****************************************************************************
;
show_freq
        movlw   0x80              ; Point the LCD to first LCD digit location  
        call    cmnd2LCD          ; Send starting digit location to LCD
#IF LCDCHAR == 16
        movlw   ' '               ; Send a space
        call    data2LCD          ;   to position 1 of LCD
#ENDIF
;
; Running 4-bit mode, so need to send Most Significant Nibble first.
;
; Extract and send "XXXX" from byte containing "XXXXYYYY"
;  - Swap halves to get YYYYXXXX
;  - Mask with 0x0F to get 0000XXXX
;  - Add ASCII bias (0030XXXX)
;
        swapf   BCD_3,w           ; Swap 10MHz BCD digit into lower nibble of W
        andlw   0x0F              ; Mask for lower nibble only       (0000XXXX)
        addlw   0x30              ; Add offset for ASCII char set    (0030XXXX)
        call    data2LCD          ; Send byte in W to LCD
;
; Extract and send "YYYY" from byte containing "XXXXYYYY"
;   - Mask with 0x0F to get 0000YYYY
;   - Add offset for ASCII character set in LCD  (0030YYYY)
;
        movf    BCD_3,w           ; Put 1MHz BCD digit into lower nibble of W
        andlw   0x0F              ; Mask for lower nibble only       (0000YYYY)
        addlw   0x30              ; Add offset for ASCII char set    (0030YYYY)
        call    data2LCD          ; Send byte in W to LCD
; 
#IF LCDCHAR == 16
        movlw   ','               ; Get a comma
        call    data2LCD          ; Send byte in W to LCD
#ENDIF
;
        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
;
#IF LCDCHAR == 16                                                              
        movlw   '.'               ; Get a period
        call    data2LCD          ; Send byte in W to LCD
#IFDEF LCD2LINES       ; if 16x2 LCD
        movlw   0x88              ; Point to LCD digit number nine
#ELSE                 ; if 16x1 LCD
        movlw   0xC0              ; Point to LCD digit number nine
#ENDIF
        call    cmnd2LCD          ; Send command byte in W to LCD
#ENDIF
;
        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
;
#IF LCDCHAR == 16
        movlw   ' '               ; Send a space
        call    data2LCD          ;   to position 12 of LCD
;
        movlw   'k'               ; Send a 'k'
        call    data2LCD          ;   to position 13 of LCD
;
        movlw   'H'               ; Send an "H"
        call    data2LCD          ;   to position 14 of LCD
;
        movlw   'z'               ; Send a 'z'
        call    data2LCD          ;   to position 15 of LCD
;
        movlw   ' '               ; Send a space
        call    data2LCD          ;   to position 16 of LCD

#ENDIF
;
        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                                                            *
; *                                                                           *
; * On exit:  PORTB set as:  RB3          input                               *
; *                          all others   outputs                             *
; *****************************************************************************
; 
busy_check
        clrf    PORTB             ; Clear all outputs on PORTB
        bsf     STATUS,RP0        ; Switch to bank 1 for Tristate operation    
        movlw   b'00001000'       ; Set RB3 input, others outputs              
        movwf   TRISB             ;   via Tristate
        bcf     STATUS,RP0        ; Switch back to bank 0
        bcf     PORTB,LCD_rs      ; Set up LCD for Read Busy Flag (RS = 0) 
        bsf     PORTB,LCD_rw      ; Set up LCD for Read (RW = 1)  
        movlw   0xFF              ; Set up constant 255
        movwf   timer1            ;   for timer loop counter
LCD_is_busy
        bsf     PORTB,LCD_e       ; Set E high
        movf    PORTB,w           ; Read PORTB into W
        movwf   LCD_read          ; Save W for later testing
        bcf     PORTB,LCD_e       ; Drop E again
        nop                       ; Wait a
        nop                       ;   while
        bsf     PORTB,LCD_e       ; Pulse E high (dummy read of lower nibble),
        nop                       ;   wait,
        bcf     PORTB,LCD_e       ;   and drop E again
        decf    timer1,f          ; Decrement loop counter
        btfsc   STATUS,Z          ; Is loop counter down to zero?
        goto    not_busy          ; If yes, return regardless
;OLD     btfsc   LCD_read,7        ; Is Busy Flag (RB7) in save byte clear?       
        btfsc   LCD_read,LCD_busy ; Busy Flag (RB3) in save byte clear?        
        goto    LCD_is_busy       ; If not, it is busy so jump back
not_busy
        return                    ; 
;
; *****************************************************************************
; * Purpose:  Send Command or Data byte to the LCD                            *
; *           Entry point cmnd2LCD:  Send a Command to the LCD                *
; *           Entry Point data2LCD:  Send a Data byte to the LCD              *
; *                                                                           *
; *   Input:  W has the command or data byte to be sent to the LCD.           *
; *                                                                           *
; *  Output:  None                                                            *
; *****************************************************************************
;
cmnd2LCD   ; ****** Entry point ******
        movwf   LCD_char          ; Save byte to write to LCD
        clrf    rs_value          ; Remember to clear RS  (clear rs_value)   
        bcf     PORTB,LCD_rs      ; Set RS for Command to LCD
        goto    write2LCD         ; Go to common code
data2LCD   ; ****** Entry point ********
        movwf   LCD_char          ; Save byte to write to LCD
        bsf     rs_value,0        ; Remember to set RS (set bit 0 of rs_value)
        bsf     PORTB,LCD_rs      ; Set RS for Data to LCD
write2LCD
        call    busy_check        ; Check to see if LCD is ready for new data
        clrf    PORTB             ; Clear all of Port B (inputs and outputs)
        bsf     STATUS,RP0        ; Switch to bank 1 for Tristate operation
        movlw   0x00              ; Set up to enable PORTB data pins
        movwf   TRISB             ; All pins (RB7..RB0) are back to outputs
        bcf     STATUS,RP0        ; Switch to bank 0
        bcf     PORTB,LCD_rw      ; Set LCD back to Write mode  (RW = 0)
        bcf     PORTB,LCD_rs      ; Guess RS should be clear              
        btfsc   rs_value,0        ; Should RS be clear?  (is bit 0 == 0?) 
        bsf     PORTB,LCD_rs      ; No, set RS                            
;
; Transfer Most Significant nibble  (XXXX portion of XXXXYYYY)
;

        movlw   0xF0              ; Set up mask                                        
        andwf   PORTB,f           ; Keep RB7..RB4 but clear old RB3..RB0          
        swapf   LCD_char,w        ; Put byte into W (reverse nibbles)          
        andlw   0x0F              ; Mask to give 0000XXXX in W                 
        iorwf   PORTB,f           ; To RB3..RB0 with RB7..RB4 unchanged        
        bsf     PORTB,LCD_e       ; Pulse the E line high,
        nop                       ;   wait, 
        bcf     PORTB,LCD_e       ;   and drop it again
;
; Transfer Least Significant nibble  (YYYY portion of XXXXYYYY)
;
        movlw   0xF0              ; Set up mask                                        
        andwf   PORTB,f           ; Clear old RB3..RB0                         
        movf    LCD_char,w        ; Move LS nibble of into W                   
        andlw   0x0F              ; Mask to give 0000YYYY in W                 
        iorwf   PORTB,f           ; To RB3..RB0 with RB7..RB4 unchanged        
        bsf     PORTB,LCD_e       ; Pulse the E line high,
        nop                       ;   wait, 
        bcf     PORTB,LCD_e       ;   and drop it again
        return
;
; *****************************************************************************
; *                                                                           *
; * Purpose:  Write the byte of data at EEDATA to the EEPROM at address       *
; *           EEADR.                                                          *
; *                                                                           *
; *   Input:  The values at EEDATA and EEADR.                                 *
; *                                                                           *
; *  Output:  The EEPROM value is updated.                                    *
; *                                                                           *
; *  NOTE:  ALL IN BANK 1                                                     *
; *****************************************************************************
;
write_EEPROM
        bsf     EECON1,WREN       ; Set the EEPROM write enable bit
        ; Start required sequence
        bcf     INTCON,GIE        ; Disable interrupts
        movlw   0x55              ; Write 0x55 and 0xAA to EECON2
        movwf   EECON2            ;   control register, as required
        movlw   0xAA              ;   
        movwf   EECON2            ;
        bsf     EECON1,WR         ; Set WR bit to begin write
        ; End required sequence
bit_check
        btfsc   EECON1,WR         ; Has the write completed?
        goto    bit_check         ; No, keep checking
        bsf     EECON1,GIE        ; Enable  interrupts
        bcf     EECON1,WREN       ; Clear the EEPROM write enable bit
        incf    EEADR,f           ; Increment the EE write address
        return                    ; Return to the caller
;
; *****************************************************************************
; *                                                                           *
; * Purpose:  Read a byte of EEPROM data at address EEADR into EEDATA.        *
; *                                                                           *
; *   Input:  The address EEADR.                                              *
; *                                                                           *
; *  Output:  The value in EEDATA.                                            *
; *                                                                           *
; *  NOTE:  All in BANK 1                                                     *
; *****************************************************************************
;
read_EEPROM
;        bsf     EEDATA,RD         ; Request the read
        bsf     EECON1,RD         ; Request the read
        movf    EEDATA,W          ; Get the data
        incf    EEADR,f           ; Increment the read address
        return                    ; Return to the caller
;
; *****************************************************************************
; *                                                                           *
; * Purpose:  Wait for a specified number of milliseconds.                    *
; *                                                                           *
; *           Entry point wait_a_sec:  Wait for 1 second                      *
; *           Entry point wait_256ms:  Wait for 256 msec                      *
; *           Entry point wait_128ms:  Wait for 128 msec                      *
; *           Entry point wait_64ms :  Wait for 64 msec                       *
; *           Entry point wait_32ms :  Wait for 32 msec                       *
; *           Entry point wait_16ms :  Wait for 16 msec                       *
; *           Entry point wait_8ms  :  Wait for 8 msec                        *
; *                                                                           *
; *   Input:  None                                                            *
; *                                                                           *
; *  Output:  None                                                            *
; *                                                                           *
; *****************************************************************************
;
wait_a_sec  ; ****** Entry point ******    
        call    wait_256ms        ;       
        call    wait_256ms        ;       
        call    wait_256ms        ;       
        call    wait_256ms        ;       
        return
wait_256ms  ; ****** Entry point ******    
        call    wait_128ms        ;
        call    wait_128ms        ;
        return
wait_128ms  ; ****** Entry point ******    
        movlw   0xFF              ; Set up outer loop 
        movwf   timer1            ;   counter to 255
        goto    outer_loop        ; Go to wait loops
wait_64ms  ; ****** Entry point ******     
        movlw   0x80              ; Set up outer loop
        movwf   timer1            ;   counter to 128
        goto    outer_loop        ; Go to wait loops
wait_32ms   ; ****** Entry point ******    
        movlw   0x40              ; Set up outer loop
        movwf   timer1            ;   counter to 64
        goto    outer_loop        ; Go to wait loops
wait_16ms   ; ****** Entry point ******    
        movlw   0x20              ; Set up outer loop
        movwf   timer1            ;   counter to 32  
        goto    outer_loop        ; Go to wait loops
wait_8ms   ; ****** Entry point ******     
        movlw   0x10              ; Set up outer loop
        movwf   timer1            ;   counter to 16
                                  ; Fall through into wait loops
;
; Wait loops used by other wait routines
;  - 1 microsecond per instruction (with a 4 MHz microprocessor crystal)
;  - 510 instructions per inner loop
;  - (Timer1 * 514) instructions (.514 msec) per outer loop
;  - Round off to .5 ms per outer loop
;
outer_loop                        
        movlw   0xFF              ; Set up inner loop counter
        movwf   timer2            ;   to 255
inner_loop
        decfsz  timer2,f          ; Decrement inner loop counter
        goto    inner_loop        ; If inner loop counter not down to zero, 
                                  ;   then go back to inner loop again
        decfsz  timer1,f          ; Yes, Decrement outer loop counter
        goto    outer_loop        ; If outer loop counter not down to zero,
                                  ;   then go back to outer loop again
        return                    ; Yes, return to caller
;       
; *****************************************************************************
;
        END

