; *****************************************************************************************************
; * W2EB - 9850-based VFO specifically for a Broadcast-Band (AM) Receiver                                       *
; *  Version Final                                                                                      *
; *  9 Apr 13                                                                                  *
; *  Author: Bill Lazure, W2EB									                                      *
; *	**Based on PICELGen 6.0 by AA0ZZ, Craig Johnson, as modified by Bob Okas W3CD, orginally by    *
; *       Curtis W. Preuss - WB2V                                                                      *
; *****************************************************************************************************
; Description:
; This is the control program for a DDS VFO built for an AM-Band Broadcast Receiver 
; with an AD9850 DDS chip, a Rotary Encoder, a push button switch and a Hitachi 44780-compatible
; Liquid crystal display. The VFO assumes an I.F. of 4.9152MHz.
;
; *******************************************************************************************************
                                                                             
; Target Controller -      PIC18F2520                             
;                        -------___-------                                         
; MCLR------------RE3 |1                 28| RB7---------LCD_D7 (LCD Pin 14)                  
; DDS_UPDATE------RA0 |2                 27| RB6---------LCD_D6 (LCD Pin 13)                   
; DDS_CLOCK-------RA1 |3                 26| RB5---------LCD_D5 (LCD Pin 12)                       
; dds_data--------RA2 |4                 25| RB4---------LCD_D4 (LCD Pin 11)                      
; PB1-------------RA3 |5                 24| RB3---------                       
; PB2-------------RA4 |6                 23| RB2---------LCD_E   (LCD Pin 6)                     
; PB3-------------RA5 |7                 22| RB1---------LCD_RW  (LCD Pin 5)       
; GND-------------VSS |8                 21| RB0---------LCD_RS  (LCD Pin 4)           
; N/C------------OSC2 |9                 20| VCC---------+5 V          
; N/C------------OSC1 |10                19| VSS---------Gnd                      
; N/C-------------RC0 |11                18| RC7---------Encoder                 
; N/C-------------RC1 |12                17| RC6---------Encoder         
; N/C-------------RC2 |13                16| RC5---------Spare           
; N/C-------------RC3 |14                15| RC4---------Spare         
;                       -------------------                                          
; *****************************************************************************************************
; * Device type and options.                                                                          *
; *****************************************************************************************************
;
				processor 18F2520
				#include	<P18F2520.inc>
				radix     dec
        errorlevel 0, -205, -220, -302  ; Skip nuisance messages 


; ****************************************************************************************************
; * DDS Frequency control equates.  These may be changed to accommodate the                          *
; * reference clock frequency and the desired upper frequency limit 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                  
;
; 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
;

;==== Currently set for 125 MHz Oscillator =======

ref_osc_3           equ 0x22           ; Most significant osc byte
ref_osc_2           equ 0x5C           ; Next byte
ref_osc_1           equ 0x17           ; Next byte
ref_osc_0           equ 0xD0           ; Least significant byte


; The following is the definition of the IF frequency...4.915MHz

int_0				equ	0xB8		;The I.F. Frequency
int_1				equ	0x02		;	"
int_2				equ	0x40		;	"
int_3				equ	0x00		;	"


; Info for power-up display                                                   
MCODE_REV_0  equ  ' '     ; Current code version is 7.0                     
MCODE_REV_1  equ  'B'     ;                                                   
MCODE_REV_2  equ  'C'     ;                                                   
MCODE_REV_3  equ  'B'     ;
MCODE_REV_4  equ  ' '     ;
MCODE_REV_5  equ  'V'     ;
MCODE_REV_6  equ  '2'	  ;
MCODE_REV_7  equ  ' '     ;


;******************************************************************************************************
;																				  *
;                                      General definitions									  *
;													    	                                *
;******************************************************************************************************

int_0				equ	0x00		;The I.F. Frequency; 4.9152MHz
int_1				equ	0x00		;	"
int_2				equ	0x4B		;	"
int_3				equ	0x00		;	"

TRANSMIT		    		equ 0x03		    ;Bit that says we are in Transmit Mode
UPDT	    		    		equ	0x05		    ;Bit that tells to Refresh DDS and LCD
;
; *****************************************************************************************************
; * Assign names to IO pins.                                                                          *
; *****************************************************************************************************
;
;   A Register Bits:
;
dds_ud		         equ	0x00
dds_clk		         equ	0x01
dds_data 	         equ	0x02
pb1	 		         equ	0x03
pb2	 		         equ	0x04
pb3	 		         equ	0x05
;
;   B register bits:
;
LCD_E    	         equ 0x02            ; LCD Enable Bit
LCD_RW   	         equ 0x01            ; LCD Read/Write Control
LCD_busy 	         equ 0x03            ; LCD busy bit 
LCD_RS   	         equ 0x00            ; LCD Mode Setting

; 
;   C register bits:
;
Enc1	  		        equ 0x06		  	    ; Encoder Input
Enc2	  		        equ 0x07		  	    ; Encoder input  
;

; ****************************************************************************
; *           Allocate variables in general purpose register space           *
; ****************************************************************************
;
        CBLOCK  0x000                  ; Start Data Block

        freq_0                         ; Display frequency (hex) 
        freq_1                         ;  (4 bytes) 
        freq_2                         ; Used by bin2BCD only
        freq_3         

        freq_0A                        ; Freq info Placeholders 
        freq_1A                        ;  
        freq_2A                        ; 
        freq_3A                        ;

        BCD_0                          ; Display frequency (BCD) 
        BCD_1                          ;  (5 bytes)
        BCD_2                          ;
        BCD_3                          ;
        BCD_4                          ;

        AD9850_0                       ; AD9850 control word 
        AD9850_1                       ;  (5 bytes)
        AD9850_2                       ;
        AD9850_3                       ;
        AD9850_4                       ;

        fstep_0                        ; Frequency inc/dec 
        fstep_1                        ;  (4 bytes)
        fstep_2                        ; update_decade fills these in
        fstep_3                        ; with values from decade_table

        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                     ; Used by calibrate
        osc_temp_3

        LCD_char                       ; Character being sent to the LCD
        LCD_read                       ; Character read from the LCD

        timer1                         ; Used in delay routines
        timer2                         ;   "

        encdr_new                        ; New value of encoder pins A and B
        encdr_old                        ; Old value of encoder pins A and B
        encdr_read                       ; Encoder pins A and B and switches
        last_dir                       ; Indicates last direction of encoder
        next_dir                       ; Indicates expected direction

        count                          ; Multipurpose loop counter

        rs_value                       ; The LCD rs line flag value

        PB_flags                       ; Pushbutton flags
        PB_wait_count                  ; PB repeat control timer

        temp                           ; Miscellaneous variable

        decade                         ; Frequency decade subject to updating
        cur_pos                        ; Cursor position: 0-7 for 8 char LCD

                                       ; Don't disturb the order of the VFO
                                       ; variables as their adjacency is required.
        vfo_a_band                     ; Stores the current VFO A band
        vfo_a_0                        ; Used to store the frequency setting
        vfo_a_1                        ; of VFO A when B is in use
        vfo_a_2 
        vfo_a_3

        vfo_b_band                     ; Current VFO B band storage
        vfo_b_0                        ; Used to store the frequency setting
        vfo_b_1                        ; of VFO B.
        vfo_b_2
        vfo_b_3
		vfo_b_4

		startup_band
        vfo_pointer                    ; pointer to current vfo_X_0.
        tbloffst	                   ; Used in table reads
       holder 			               ; temporary storage


        ENDC                           ; End of Data Block


; 
; *****************************************************************************************************
; *                          The 18F2520 resets to 0x00.                                                * 
; *                    The Interrupt vector is at 0x04. (Unused)                                      *
; *****************************************************************************************************             
;

        ORG     0x0000                
reset_entry
        goto    start                  ; Jump around the band table to main program



; *****************************************************************************************************  
; *                                   Decade Table                                                    *
; *****************************************************************************************************
; *                                                                                                   *
; * Each entry is four instructions long, with each group of four numbers representing the multiplier *
; * of each successive decade. Each four byte entry represents the decade increment which can get be  *
; * assigned to fstep_3, fstep_2, fstep_1 and fstep_0. Data is stored MSB first.                      *
; *                                                                                                   *
; *****************************************************************************************************

                                         ; Jump to the appropriate value to return
decade_table	movwf		tbloffst	 ;This routine replaces the original Table counter jump computation.
				bcf			STATUS,C	 ;It must be used because the program counter
				rlcf		tbloffst,F	 ;on a 18F2520 counts by twos rather than ones.
				movlw		HIGH(dectbl)
				btfsc		STATUS,C
				incf		WREG,W
				movwf		PCLATH
				movlw		LOW(dectbl)
				addwf		tbloffst,W
				btfsc		STATUS,C
				incf		PCLATH,F
				movwf		PCL
dectbl
                retlw 		0x00                    ; 1Hz increment
                retlw		0x00
               retlw		0x00
                retlw		0x01
               
                retlw		0x00                    ; 10 Hz increment
                retlw		0x00
                retlw		0x00
                retlw		0x0a
        
                retlw		0x00                    ; 100 Hz increment
                retlw		0x00
               retlw		0x00
                retlw		0x64
        
                retlw		0x00                    ; 1 KHz increment
                retlw		0x00
                retlw		0x03
                retlw		0xE8
        
               retlw		0x00                    ; 10 KHz increment
                retlw		0x00
                retlw		0x27
                retlw		0x10
        
                retlw		0x00                    ; 100 KHz increment
                retlw		0x01
                retlw		0x86
                retlw		0xa0
       
 

; *****************************************************************************************************  
; *                              Cursor Positioning Table                                             *
; *****************************************************************************************************
; *                                                                                                   *
; *  This table tells where to position the cursor when each new decade is selected.                  *
; *****************************************************************************************************


cursor_table	movwf		tbloffst		    ;This routine replaces the original Table counter jump computation.
				bcf			STATUS,C			;It must be used because the program counter
				rlcf		tbloffst,F			;on a 18F2520 counts by twos rather than ones.
				movlw		HIGH(curtbl)
				btfsc		STATUS,C
				incf		WREG,W
				movwf		PCLATH
				movlw		LOW(curtbl)
				addwf		tbloffst,W
				btfsc		STATUS,C
				incf		PCLATH,F
				movwf		PCL

curtbl              
               	retlw		0x8A
              	retlw		0x89
             	retlw		0x88
               	retlw		0x86
                retlw		0x85
               ;retlw		4
                ;retlw		2
               ;retlw		1 


;
; *****************************************************************************************************
; *                                         setup                                                     *
; *****************************************************************************************************
; *                                                                                                   *
; * Purpose:  This is the setup of the program.  It initializes the LCD and DDS, sets PIC operating   *
; *           paramters, presets registers, and enables/disables device peripherals.  Further, it     *
; *           establishes the starting frequency and mode.                                            *
; *****************************************************************************************************

start
;Config statements
    movlw   0xF8        ;Set Internal Clock,
    movwf   _CONFIG1H   ; pins A6 & A7 are I/O
    
    movlw   0xF1        ;Brownout Reset Disabled
    movwf   _CONFIG2L   ; Power-Up imer Disabled
    
    movlw   0xFE        ;Watchdog Timer Disabled
    movwf   _CONFIG2H
    
    movlw   0x81        ;MCLR Enabled, PORTB A/D Disabled
    movwf   _CONFIG3H   ; CCP2 Mux on RC1
    
    movlw   0xC0        ;No Background Debugger
    movwf   _CONFIG4L   ; No ICSP, No Stack Resets
  
;Register and Peripheral Setup  
    clrf    PIE1         ;No Peripheral Interrupts
    clrf    PIE2        ;         "
    
    movlw   0xCE        ;Set Internal Oscillator for
    movwf   OSCCON      ; 1MHz
	clrf		INTCON			    ; No interrupts for now
	movlw	0x07			    ; Set the 3 LSBs of CMCON
	movwf	CMCON			    ; to enable normal I/O
	clrf		RCSTA			    ; Disable USART Receiver

	movlw	0x0F				;set up for ADCON1 to turn off
	movwf	ADCON1		;  all ADCs

	clrf		TXSTA			    ; Disable USART Transmitter

	clrf		CVRCON			    ; Disable voltage reference
	clrf		T1CON
	movlw	0x80				   ;Setup to enable PortB pullups
	movwf	INTCON2				  ;	 accomplished
	movlw	0x38				   ; Set Port A bits.
	movwf	TRISA			    ;
	clrf		LATA
   	clrf		TRISB			    ; Set port B to all outputs
	movlw	0xFF
	movwf	TRISC


     call    init_LCD                ; Initialize the LCD
     call    display_version         ; Display title and version number
	 call 	 wait_512ms


	movlw	0x00				;load a starting frequency of 1MHz
	movwf	freq_3
	movlw	0x0F
	movwf	freq_2
	movlw	0x42
	movwf	freq_1
	movlw	0x40
	movwf	freq_0
   
;
; Send power on-frequency to the DDS chip.

;
    call    send_dds_word	         ; Send the power-on frequency to the
                                       ;   AD9850 in serial mode
    call    send_dds_word	         ; Send it twice. Sometimes needed for
                                       ;   a clean start-up

	movlw	0x86
	movwf	cur_pos

	movlw	0x03
	movwf	decade

 	call    show_freq              ; COnvert to BCD and display it

	clrf	fstep_3				;pre-load fstep values
	clrf	fstep_2				;	"
	movlw	0x03
	movwf	fstep_1				;	"
	movlw	0xE8				;	"
	movwf	fstep_0				;	"

	call 	wait_256ms


        clrf    last_dir               ; Clear the knob direction indicator
  
; Proceed to the Main Program Loop
;
; *****************************************************************************************************
; *                                         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.                                                                                  *                       
; *                                                                                                   *
; *****************************************************************************************************
;

main
 
	    call    poll_encoder           ; Check for knob movement (wait there!)
                                       ; Return here when encoder change detected
  
        goto    step                   ; No, just step    
  
        goto    main

 
;******************************************************************************************************
; *                                           step                                                    *
; *****************************************************************************************************
; *  This routine Adjusts the frequency by examining the last_dir value and either adds or subtracts  *
; *  the value of fstep_3..0 from the current frequency setting.                                      *
; *                                                                                                   *
;******************************************************************************************************

step                                   ; Based on the knob direction, either add or
                                       ; subtract the increment, then update the
                                       ; LCD and DDS.
        btfsc   last_dir,6       ; Is the knob going up?
        goto    up                     ; Yes, then add the increment

down                                   ; Else, shaft direction is CCW
        call    sub_step               ; Subtract fstep from freq
        goto    update                 ; Update LCD and DDS

up
        call    add_step          ; Add fstep to freq

update
        call    show_freq         ; Display the new frequency on the LCD
        call    send_dds_word     ; Send the new control word to the DDS chip

        goto    main              ; Continue main loop


; *****************************************************************************************************
; *                                       add_step                                                    *
; *****************************************************************************************************
; *	This routine adds the 32 bit value of fstep to the 32 bit value in freq.                      *
; *                                                                                                   *     
; *****************************************************************************************************


add_step

	bcf	STATUS, C		;clear the carry bit

	   movf	fstep_0,w			;Place the "frequency Step" value into W
        addwf   freq_0,		;Add the frequency step value to Freq_0	
        movf	fstep_1,w		;Place Fstep_1 into W
        addwfc  freq_1,		;Add Fstep_1 to Freq_1 including the carry from the previous add
        movf	fstep_2,w		;Place FStep_2 into W
        addwfc  freq_2,F		;Add Fstep_2 to Freq_2 including the carry from the previous add
        movf	fstep_3,w		;Place FStep_3 into W
        addwfc  freq_3,F		;Add Fstep_3 to Freq_3 including the carry from the previous add
   	  
        return

; *****************************************************************************************************
; *                                      sub_step                                                     *
; *****************************************************************************************************
; *                                                                                                   *
; *  Subtracts fstep0...3 from freq0...3 and leaves the result in freq0...3                 *
; *****************************************************************************************************

sub_step

		movf	fstep_0,w			;Place FStep_0 into W
		subwf	freq_0,f		;Subtract FStep_0 from Freq_0
		movf	fstep_1,w			;Place FStep_1 into W
		subwfb	freq_1,f		;Subtract FStep_1 and borrow from the previous subtract,from Freq_1
		movf	fstep_2,w			;Place FStep_2 into W
		subwfb	freq_2,f		;Subtract FStep_2 and borrow from the previous subtract,from Freq_2
		movf	fstep_3,w			;Place FStep_3 into W
		subwfb	freq_3,f		;Subtract FStep_3 and borrow from the previous subtract,from Freq_3

	    return
 
;
; *****************************************************************************************************
; *                                  poll_encoder                                                     *
; *****************************************************************************************************
; *                                                                                                   *
; * Purpose:  This routine does the following:                                                        *
; *          1. Reads the encoder bits until a change is detected, then determines the direction the  *
; *             knob was moved.                                                                       *
; *          2. Performs minor rotary encoder debounce.                                               *
;******************************************************************************************************

poll_encoder
		call	PB1

        movff    PORTC,encdr_old   ; Get the current encoder value place it into "old"
		movlw	0xC0				; Setup a mask
		andwf	encdr_old			;mask off unimportant bits


  		call	wait_1ms
         movff    PORTC,encdr_new   ; Get the current encoder value place it into "old"
		movlw	0xC0				; Setup a mask
		andwf	encdr_new			;mask off unimportant bits
		movf	encdr_new,w

		cpfseq	encdr_old			;compare W and Encdr_old, skip if they're equal
		goto	next				; they aren't equal, so proceed
        goto	poll_encoder               ; they are equal, there's been no change, return to main and do it again
                                
; It changed. Now determine which direction the encoder turned.

;=============================================================================P
;   Encoder bits are on RA0 and RA1 - the two low order bits of ren_new       P  
;   A and B are "gray code" - 90 degrees out of phase (quadrature)            P
;         ___     ___                                                         P
;        |   |   |   |                                                        P
; A  ____|   |___|   |___                                                     P
;           ___     ___                                                       P
;          |   |   |   |                                                      P
; B     ___|   |___|   |___                                                   P
;       ^ ^ ^ ^ ^ ^ ^ ^                                                       P
;       a b c d a b c d                                                       P
;                                                                             P
;              A B                                                            P
; At point a:  0 0                                                            P
; At point b:  1 0                                                            P
; At point c:  1 1                                                            P
; At point d:  0 1                                                            P
;                                                                             P
; Going UP, the sequence is a,b,c,d,a,b,c,d, etc. so the sequence is:         P
;     00, 10, 11, 01, 00, 10, 11, 01, etc.                                    P
;                                                                             P
; Going DOWN, the sequence is d,c,b,a,d,c,b,a, etc. so the sequence is:       P
;     01, 11, 10, 00, 01, 11, 10, 00, etc.                                    P
;                                                                             P
; To determine if the sequence is UP or DOWN:                                 P
;   1) Take the "Right-Bit" of any pair.                                      P
;   2) XOR it with the "Left-Bit" of the next pair in the sequence.           P
;   3) If the result is 1 it is UP                                            P
;      If the result is 0 it is DOWN                                          P
;=============================================================================P
next
        bcf     STATUS,C          ; Clear the carry bit to prepare for rotate 
        rrncf   encdr_new,f         ; Rotate old bits left to align "Right-Bit" 
        movf    encdr_old,w         ; Set up new bits in W                      
        xorwf   encdr_new,f         ; XOR old (left shifted) with new bits      
        movf    encdr_new,w         ; Put XOR results into W also               
        andlw   0x40              ; Mask to look at only " right 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        

        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    encdr_new,w         ; Save the current encoder bits (now in W)  
        movwf   encdr_old           ;   for next time                           
        goto    poll_encoder      ; Try again


pe_continue
        clrf    last_dir          ; Clear last_dir (default is DN)            
        btfsc   next_dir,6         ; Are we going UP?                          
        goto    pe_up             ; Yes, go process it.
                                  ; Else, we are goiong down

        goto    pe_movement       ; Indicate that the encoder has moved
pe_up 
        movlw   0x40            ; Get UP value
        movwf   last_dir          ;   and set in last_dir



pe_movement                       ; Arrive here when encoder is being turned
        movff   PORTC,encdr_new   ; Get the current encoder value place it into "old"
		movlw	0xC0				; Setup a mask
		andwf	encdr_new			;mask off unimportant bits
        movff   encdr_new,encdr_old         ; Get the current encoder bits
  
 
        goto    pe_exit           ; Finish up

pe_exit

        return                    ; Return to the caller


;******************************************************************************************************
; *                                            PB1                                                    *
; *****************************************************************************************************
;                                                                                                     *
;    Purpose:   This function examines the state of pb1 by lloking for its corresponding input pin    * 
;               to go low.  It then waits briefly and checks if the pin is still low (being pushed)   *
;               or has returned to high (released).  If it's still low, it waits again, and checks    *
;               again.  If it's high, it then:                                                        *
;                   1.  Increments the cursor position to the right                                   *
;                                                                                                     *
;                   2.  Changes the step value by calling the next decade's values from the decade    *
;                       table and placing those values into fstep.                                    *
;                                                                                                     *
;******************************************************************************************************

PB1
	movff	PORTA, temp		;is PB1 pressed?
	btfsc	temp,pb1
	goto	pb_exit			; No.  Exit this subroutine
	call	wait_1ms
	movff	PORTA,temp
	BTFSS	temp, pb1		; Yes.  Has it been released yet?
	goto 	PB1				;   No.  Wait for PB1 to be released
	incf	decade			;   Yes, incement the decade value
	movlw	0x05			; Load the "highest_decade" comparison value
	cpfslt	decade			; Does the current decade value exceed the highest value?
	clrf	decade			; Yes.  Reset the decade value

	movff	decade,holder	; No. accept the value and place it into "holder"

	movf	decade,w		;prepare to read the proper  cursor position
	call	cursor_table	;read the position
	movwf	cur_pos			;store the cursor position info
 
	rlcf	holder			; multiply holder by 4 for the table read
	rlcf	holder			;  "
	movf	holder,w		;move holder into w for the table read
	call decade_table		; table read
	movwf	fstep_3			; move the obtained value into Step_3
	incf	holder			;increment holder
	movf	holder,w		;move holder into w for the next table read
	call	decade_table	; next table read
	movwf	fstep_2			;move obtained value into step_2
	incf	holder			;increment holder for next table read
	movf	holder,w		;move holder into w for the next table read
	call	decade_table	;call the table read
	movwf	fstep_1			;move the results into step_1
	incf	holder			;increment holder
	movf	holder,w		;move to w for the next table read
	call	decade_table	;perform the final read
	movwf	fstep_0			;move the results into step_0

	movf	cur_pos,w
	call   	cmnd2LCD         ; Display the new frequency on the LCD	
pb_exit	
	return
	

;******************************************************************************************************
; *							                calc_dds_word                                             *
; *                                                                                                   *	  
;******************************************************************************************************
; * Purpose:  Multiply the 32 bit number for oscillator frequency times the 32-bit number for the     *
; *		      displayed frequency. The result is stored in AD9850_3...0.                              *
; *																					                  *
; *****************************************************************************************************
;
calc_dds_word
				clrf	   	AD9850_0		 ; Clear the AD9850/AD9851 control word bytes
				clrf	   	AD9850_1		 ; 
				clrf	   	AD9850_2		 ; 
				clrf	   	AD9850_3		 ; 
				clrf	   	AD9850_4		 ;

		bcf	STATUS, C		;clear the carry bit
						;**This routine adds the IF frequency to the 
        movlw	int_0		;  DDS word.  
        addwf   vfo_a_0,f
        movlw	int_1
        addwfc  vfo_a_1,f
        movlw	int_2
        addwfc  vfo_a_2,f
        movlw	int_3
        addwfc  vfo_a_3,f 

				
			 
				clrf		osc_temp_3
				clrf		osc_temp_2
				clrf		osc_temp_1
				clrf		osc_temp_0
				movlw   	0x20			 ; Set count  to 32	  (4 osc bytes of 8 bits)
				movwf  		mult_count		 ; Keep running count
	
				movlw		ref_osc_3
				movwf		osc_temp_3

				movlw		ref_osc_2
				movwf		osc_temp_2

				movlw		ref_osc_1
				movwf		osc_temp_1
	
				movlw		ref_osc_0
				movwf		osc_temp_0


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		vfo_a_0,w		 ; Get the "normal" freq_0 term
 
				addwf   AD9850_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  	AD9850_2,f		 ; Yes, add one and check for another carry
				goto	   	add7			 	; No, continue with next freq term
				incfsz  	AD9850_3,f		 ; Yes, add one and check for another carry
				goto	   	add7			 	; No, continue with next freq term
				incf	   	AD9850_4,f		 ; Yes, add one and continue
add7
				movf	vfo_a_1,w		 	; Get the "normal" freq_0 term
 
				addwf   AD9850_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  	AD9850_3,f		 ; Yes, add one and check for another carry
				goto	   	add8			 	; No, continue with next freq term
				incf	   	AD9850_4,f		 ; Yes, add one and continue
add8
				movf	vfo_a_2,w		 	; Get the "normal" freq_2 term
				addwf   AD9850_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	   	AD9850_4,f		 ; Yes, add one and continue
add9
				movf	vfo_a_3,w		 	; Get the "normal" freq_3 term

				addwf   AD9850_4,f		 ; Add freq term to total in correct position
noAdd
				rrcf	    AD9850_4,f		  ; Shift next multiplier bit into position
				rrcf	    AD9850_3,f		  ; Rotate bits to right from byte to byte
				rrcf	    AD9850_2,f		  ; 
				rrcf	    AD9850_1,f		  ; 
				rrcf	    AD9850_0,f		  ; 
				rrcf	    osc_temp_3,f,1	  	; Shift next multiplicand bit into position
				rrcf	    osc_temp_2,f,1	  	; Rotate bits to right from byte to byte
				rrcf	    osc_temp_1,f,1	  	; 
				rrcf	    osc_temp_0,f,1	  	; 
				decfsz  mult_count,f	 	; One more bit has been done.  Are we done?
				goto	   	mult_loop		 	; No, go back to use this bit
				clrf         AD9850_4		; Last byte to be sent					
												; Mult answer is in bytes _3 .. _0		    
				return				 		; Done.

;
; *****************************************************************************************************
; *	                                     send_dds_word								                  *
;******************************************************************************************************
; * Purpose:  This routine sends the computed frequency word to the DDS	using a serial data transfer. *
; *                                                                                                   *
; *****************************************************************************************************

send_dds_word
		call calc_dds_word

		movff	AD9850_0, vfo_b_0
		movff	AD9850_1, vfo_b_1	
		movff	AD9850_2, vfo_b_2
		movff	AD9850_3, vfo_b_3
		movff	AD9850_4, vfo_b_4

        bcf     LATA, dds_data          ;preset data output to "0"

        movlw   0x08
        movwf   bit_count                ;preset bit_count with 8

serial1
        btfsc   vfo_b_0,0                ;is bit "0" low?
        bsf     LATA, dds_data          ; -->no, so set the data bit High
        bsf     LATA, dds_clk           ; whether data is set high or low, 
        bcf     LATA, dds_clk           ;    toggle the clock
        bcf     LATA, dds_data          ; set the data bit back low to prepare for the next bit
        rrcf    vfo_b_0                  ; rotate the original data 1 bit to the right
        decfsz  bit_count                ; decrement bit_count by 1.  Have we examined & sent 8 bits yet?
        goto    serial1                  ; -->no, go back to the beginning and examine another bit
                                         ; -->yes, move on to the next word
 

        movlw   0x08                     ; same routine, next digit
        movwf   bit_count               

serial2
        btfsc   vfo_b_1,0
        bsf     LATA, dds_data
        bsf     LATA, dds_clk
        bcf     LATA, dds_clk
        bcf     LATA, dds_data
        rrcf    vfo_b_1
        decfsz  bit_count
        goto    serial2  
    
        movlw   0x08                   ; same routine, next digit
        movwf   bit_count    

serial3
        btfsc   vfo_b_2,0
        bsf     LATA, dds_data
        bsf     LATA, dds_clk
        bcf     LATA, dds_clk
        bcf     LATA, dds_data
        rrcf    vfo_b_2
        decfsz  bit_count
        goto    serial3  

        movlw   0x08                   ; same routine, next digit
        movwf   bit_count   

serial4
        btfsc   vfo_b_3,0
        bsf     LATA, dds_data
        bsf     LATA, dds_clk
        bcf     LATA, dds_clk
        bcf     LATA, dds_data
        rrcf    vfo_b_3
        decfsz  bit_count
        goto    serial4

        movlw   0x08                   ; same routine, last digit
        movwf   bit_count     

serial5
        ;btfsc   vfo_b_4,0
        bcf     LATA, dds_data
        bsf     LATA, dds_clk
        bcf     LATA, dds_clk
        bcf     LATA, dds_data
        rrcf    vfo_b_4
        decfsz  bit_count
        goto    serial5
        
        bcf    LATA, dds_data        ; Leave the data bit low (just in case)
        
        bsf	   LATA, dds_ud          ; Send load signal to the AD9850/AD9851			 
		bcf	   LATA, dds_ud          ;								    

    return                            ; Go back to the calling routine

;
; ******************************************************************************************************
; *                              init_LCD                                                              *
; ******************************************************************************************************
; *                                                                           						   *
; * Purpose:  Power on initialization of Liquid Crystal Display.  
; *                                                                           						   *
; ******************************************************************************************************
;
;
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   0x30              	; LCD init instruction (First)               
				movwf   PORTB             	; Send to LCD via RB7..RB4                   
				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   0x30              ; LCD init instruction (Second)              
				movwf   PORTB             ; Send to LCD                   
				bsf     PORTB,LCD_E       ; Set E high,
				call    wait_64ms         ;   wait a while,
				bcf     PORTB,LCD_E       ;   and then Clear E 
				
				movlw   0x30              ; LCD init instruction (Third)               
				movwf   PORTB             ; Send to LCD                   
				bsf     PORTB,LCD_E       ; Set E high,
				call    wait_64ms         ;   wait a while,
				bcf     PORTB,LCD_E       ;   and then Clear E
				
				movlw   0x20              ; 4-bit mode instruction                     
				movwf   PORTB             ; Send to LCD via RB3..RB0                   
				bsf     PORTB,LCD_E       ; Set E high,
				call    wait_64ms         ;   wait a while,
				bcf     PORTB,LCD_E       ;   and then Clear E
				
				movlw	0x28						; 2 line, 5x7 matrix
			call	cmnd2LCD
				
				movlw	0x08						;Turn Display Off
			call	cmnd2LCD				

				movlw	0x01						; Clear Display
			call	cmnd2LCD

				movlw	0x06						; Write from Left to right, each
			call	cmnd2LCD						;  write advanced address by 1
				
				movlw	0x0E						; Turn Display on with underline Cursor
			call	cmnd2LCD

				return                    ; 
;
;
; *****************************************************************************************************
; *                                 display_version                                                   * 
; *****************************************************************************************************
; *                                                                                                   *          
; * Purpose:  Display version and other info on LCD for a moment when powered up.                     *
; *                                                                           					      *
; *****************************************************************************************************
;
display_version


				movlw   '9'               ; digit 1              
				call    data2LCD          ;
				movlw   '8'               ; digit 2                  
				call    data2LCD          ;
				movlw   '5'               ; digit 3                
				call    data2LCD          ;
				movlw   '0'               ; digit 4               
				call    data2LCD          ;
				movlw   ' '               ; digit 5              
				call    data2LCD          ;        
				movlw   'V'               ; digit 6              
				call    data2LCD          ;
				movlw   'F'               ; digit 7               
				call    data2LCD          ;
				movlw   'O'               ; digit 8               
				call    data2LCD          ;

				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)
 
				return
;
; *****************************************************************************************************
; *		                                     bin2bcd    											  *
; *****************************************************************************************************
; * Purpose:  This subroutine converts a 32 bit binary number to a 10 digit ASCII number suitable for *
; *           display on the LCD.   																  *
; *                                                                                                   *                                                                      
; *****************************************************************************************************

								;This subroutine operates this way:
								;The first "rlcf"s rotate the digits through the entire
								;frequency word and make each digit available one at a 
								;time in the "carry" position.  This carry is then used 
								;to increment the registers BCD_0 through BCD_4.  It is 
								;not enough to simply increment these registers though,
								;they must also be adjusted decimally---BCD digits
								;represent up to 16 digits, while decimal (what we're
								;actually displaying on the LCD) use 10. The decimal
								;adjust must roll upward through each BCD register.   Thus,
								;after each "DAW" (Decimal Adjust "W"), the carry from
								;that operation is introduced into the next higher 
								;register's DAW operation.
								;     This complete routine is then performed 32 times, 
								;once per digit, and counted down through the use of 
								;"BCD_Count" and the decfsz command.

bin2BCD

				movlw	0x20		;Load a count of 32
				movwf	BCD_count	; into bcd_count

				clrf	   WREG		;clear all local registers
				clrf	   BCD_0
				clrf	   BCD_1
				clrf	   BCD_2
				clrf	   BCD_3
				clrf	   BCD_4

				clrf	freq_0A
				clrf	freq_1A
				clrf	freq_2A
				clrf	freq_3A

				movff	freq_0, freq_0A	;place freq info into temp local registers
				movff	freq_1, freq_1A	;                  "
				movff	freq_2, freq_2A ;                  "
				movff	freq_3, freq_3A ;                  "

bcdloop	

				rlcf	freq_0A		;Rotate Freq_0 right with carry
				rlcf	freq_1A		; same for freq_1
				rlcf	freq_2A		; same for freq_2
				rlcf	freq_3A		; same for freq_3
				
				movf	BCD_0,w		
				addwfc	BCD_0,w
				daw
				movwf	BCD_0

				movf	BCD_1,w
				addwfc	BCD_1,w
				daw
				movwf	BCD_1

				movf	BCD_2,w
				addwfc	BCD_2,w
				daw
				movwf	BCD_2

				movf	BCD_3,w
				addwfc	BCD_3,w
				daw
				movwf	BCD_3

				movf	BCD_4,w
				addwfc	BCD_4,w
				daw
				movwf	BCD_4

				decfsz	BCD_count
				bra		bcdloop
				return	

;
; ******************************************************************************************************
; *                                     show_freq                                                      *
; ******************************************************************************************************
; *                                                                           													  *
; * Purpose:  Display the frequency setting on the LCD in actual Hz; 14,025,000 Hz                      						  *
; *                                                                           													  *
; ******************************************************************************************************
;
show_freq

		call bin2BCD
				movlw   0x80              ; Point the LCD to first LCD digit location  
				call    cmnd2LCD          ; Send starting digit location to LCD

				movlw   ' '               ; Send a space
				call    data2LCD          ;   to position 1 of LCD

;
; 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
 

				movlw   ','               ; Get a comma
				call    data2LCD          ; Send byte in W to LCD


				swapf   BCD_2,w           ; Swap 100KHz BCD digit into lower nibble of W
				andlw   0x0F              ; Mask for lower nibble only       (0000XXXX)
				addlw   0x30              ; Add offset for ASCII char set    (0030XXXX)
				call    data2LCD          ; Send byte in W to LCD
;
				movf    BCD_2,w           ; Put 10KHz BCD digit into lower nibble of W
				andlw   0x0F              ; Mask for lower nibble only       (0000YYYY)
				addlw   0x30              ; Add offset for ASCII char set    (0030YYYY)
				call    data2LCD          ; Send byte in W to LCD
;
				swapf   BCD_1,w           ; Swap 1KHz BCD digit into lower nibble of W
				andlw   0x0F              ; Mask for lower nibble only       (0000XXXX)
				addlw   0x30              ; Add offset for ASCII char set    (0030XXXX)
				call    data2LCD          ; Send byte in W to LCD
;
																															
				movlw   ','               ; Get a period
				call    data2LCD          ; Send byte in W to LCD
;
				movf    BCD_1,w           ; Put 100 Hz BCD digit into lower nibble of W
				andlw   0x0F              ; Mask for lower nibble only       (0000YYYY)
				addlw   0x30              ; Add offset for ASCII char set    (0030YYYY)
				call    data2LCD          ; Send data byte in W to LCD
;
				swapf   BCD_0,w           ; Swap 10 Hz BCD digit into lower nibble of W
				andlw   0x0F              ; Mask for lower nibble only       (0000XXXX)
				addlw   0x30              ; Add offset for ASCII char set    (0030XXXX)
				call    data2LCD          ; Send data byte in W to LCD
;
				movf    BCD_0,w           ; Put 1 Hz BCD digit into lower nibble of W
				andlw   0x0F              ; Mask for lower nibble only       (0000YYYY)
				addlw   0x30              ; Add offset for ASCII char set    (0030YYYY)
				call    data2LCD          ; Send byte in W to LCD
;

				movlw   ' '               ; Send a space
				call    data2LCD          ;   to position 12 of LCD
;
				movlw   'H'               ; Send a 'k'
				call    data2LCD          ;   to position 13 of LCD
;
				movlw   'z'               ; Send an "H"
				call    data2LCD          ;   to position 14 of LCD
;
				movlw   ' '               ; Send a 'z'
				call    data2LCD          ;   to position 15 of LCD
;
				movlw   ' '               ; Send a space
				call    data2LCD          ;   to position 16 of LCD
				
				movf	cur_pos,w
				call	cmnd2LCD
	return

; ******************************************************************************************************
; *                                  busy_check                                                        *
; ******************************************************************************************************
; *                                                                           													  *
; * Purpose:  Check if LCD is done with the last operation. This subroutine polls the LCD busy flag     *
; *           to determine if the LCD is still working on any previous operations.                              								  *
; ******************************************************************************************************
; 
busy_check
				clrf    PORTB             ; Clear all outputs on PORTB
   			movlw   b'10000000'       ; Set RB7 input, others outputs              
				movwf   TRISB             ;   via Tristate
				bcf     PORTB,LCD_RS      ; Set up LCD for Read Busy Flag (RS = 0) 
				bsf     PORTB,LCD_RW      ; Set up LCD for Read (RW = 1)  
				movlw   0xFF              ; Set up constant 255
				movwf   timer1            ;   for timer loop counter
LCD_is_busy
				bsf     PORTB,LCD_E       ; Set E high
				movf    PORTB,w           ; Read PORTB into W
				movwf   LCD_read          ; Save W for later testing
				bcf     PORTB,LCD_E       ; Drop E again
				nop                       ; Wait a
				nop                       ;   while
				bsf     PORTB,LCD_E       ; Pulse E high (dummy read of lower nibble),
				nop                       ;   wait,
				bcf     PORTB,LCD_E       ;   and drop E again
				decf    timer1,f          ; Decrement loop counter
				btfsc   STATUS,Z          ; Is loop counter down to zero?
				goto    not_busy          ; If yes, return regardless
          btfsc   LCD_read,7        ; Is Busy Flag (RB7) in save byte clear?       
;OLD				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                    ; 


; ******************************************************************************************************
; *                                      cmnd2LCD                                                      *
; ******************************************************************************************************
; * Purpose:  Send Command or Data byte to the LCD                            						  *
; ******************************************************************************************************
;
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)
				movlw   0x00              ; Set up to enable PORTB data pins
				movwf   TRISB             ; All pins (RB7..RB0) are back to outputs
				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   0x0F              ; Set up mask                                        
				andwf   PORTB,f           ; Keep RB3..RB0 but clear old RB7..RB4          
				movf    LCD_char,w        ; Put byte into W           
				andlw   0xF0              ; Mask to give XXXX0000 in W                 
				iorwf   PORTB,f           ; To RB7..RB4 with RB3..RB0 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   0x0F              ; Set up mask                                        
				andwf   PORTB,f           ; Clear old RB7..RB4                        
				swapf   LCD_char,w        ; Move LS nibble of into W (reverse nibbles)                
				andlw   0xF0              ; Mask to give YYYY0000 in W                 
				iorwf   PORTB,f           ; To RB7..RB4 with RB3..RB0 unchanged        
				bsf     PORTB,LCD_E       ; Pulse the E line high,
				nop                       ;   wait, 
				bcf     PORTB,LCD_E       ;   and drop it again
				return

; *****************************************************************************
; *                             Delays                                        *
; *****************************************************************************
; *                                                                           *
; * Purpose:  Wait for a specified number of milliseconds.                    *
; *                                                                           *
; *           Entry point wait_a_sec:  Wait for 1 second                      P
; *           Entry point wait_256ms:  Wait for 256 msec                      P
; *           Entry point wait_128ms:  Wait for 128 msec                      *
; *           Entry point wait_64ms :  Wait for 64 msec                       *
; *           Entry point wait_32ms :  Wait for 32 msec                       *
; *           Entry point wait_16ms :  Wait for 16 msec                       *
; *           Entry point wait_8ms  :  Wait for 8 msec                        *
; *           Entry point wait_1ms                                            *
; *                                                                           *
; *   Input:  None                                                            *
; *                                                                           *
; *  Output:  None                                                            *
; *                                                                           *
; *****************************************************************************
;
wait_a_sec  ; ****** Entry point ******    
        call    wait_256ms        ;       
        call    wait_256ms        ;       
wait_512ms  ; ****** Entry point ******
        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
        goto	outer_loop        ; Into the wait loops
wait_1ms
        movlw	0x2
        movwf	timer1
;
; 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
