;********************************************************************************
;                              William Lazure									*
;							W2EB - 18 Apr 11									*
;********************************************************************************
;1 Apr 11 - Started with old Power Meter v36 program from 2006.  Built a test 	*
;			PCB to allow me to use multiple ADCs for testing.					*
;3 Apr 11 - I changed the ADC to test another input...one of multiples I plan	*
;			to use.   															*
;			-I moved all of the power diplay templates to the leftmost 6 		*
;			positions on line 1 of the LCD.										*
;			-I added the addition and subtraction routines						*
;4 Apr 11 - Implemented the divide routine										*
;			-Display flickers and acts erratic.  Trying a different divide		*
;			  routine 															*		
;5 Apr 11-Replaced a 16 bit subtract routine with one from the internet			*
;																				*
;6 Apr 11 - When run, rev 1h simply wrote a solid block to each of the first	*
;			5 or 6 characters of the LCD about 1/2 second per block.  Then, 	*
;			it did nothing else.  Thus, I'm assuming it's the custom write that	*
;			caused that.  Instead of MIDDELAY between each iteration of custom	*
;			write I've quadrupled the old-version delays.						*
;   	**-I may have had some variables swapped in the previous divide routine.*
;	  		I should run this newer version again to determine if that was an	*
;			issue.																*
;14 Apr 11 - Found an error in the divide routine.  Repaired.					*
;18 Apr 11 - Replaced internet divide routine with one of MY design.  It is		*
;   		a Successive Subtraction-type. 										*
;			-Also replaced multiplication routine with a simple multiple-		*
;			  Addition type.  Much more reliable, and MY ORIGINAL DESIGN!		*
;			-Added SWR High condition to prevent display from rolling over		*
;			  back to zero.														*
;			-Explored adding more granularity to Power display, but can't...	*
;			  I'm already using all 10 bits...3 for range, 7 for value!			*
;--------This Version is set up for INTERNAL REFERENCES for SWR functions,		*
;		  but EXTERNAL REFERENCES for Power ADC Reads.	It WILL NOT work on 	*
;		  the test board!!														*
;21 May 11 - The ADC inputs were wrong on V9 (Pwr and FWD were swapped.  Thus,	*
;			I'm making V9a to repair this.										*
;																				*
;							 |-----____-----|									*
;		MCLR				-|				|-	LCD DB7							*
;		PWR  ADC			-|				|-	LCD DB6							*
;		SWR(-) ADC			-|				|-	LCD DB5							*
;		Ref-				-|				|-	LCD DB4							*
;		Ref+				-|				|-	LCD DB3							*
;		N/C					-|				|-	LCD DB2							*
;		SWR FWD	ADC			-|				|-  LCD DB1							*
;		GND					-|				|-	LCD DB0							*
;		SPARE				-|				|-  V+								*
;		SPARE				-|				|-  GND								*
;		SPARE				-|				|-	LCD "ENABLE"					*
;		SPARE				-|				|-	LCD READ/WRITE SELECT			*
;		SPARE				-|				|-	LCD DATA/INSTRUCTION SELECT		*
;		SPARE				-|				|-	SPARE							*
;							 |--------------|									*					
;********************************************************************************

        	processor       PIC18F2520
			#include	<P18F2520.inc>

; *******************************************************************************
;*************************    DECLARE CONSTANTS          ************************
;********************************************************************************
; The "EQU" statement is used to define constants that are substituted in the	*
; program for the value they represent.  For example, LCDENBL equals 7.  In  	*
; the program, whenever LCDENBL appears, a "7" is actually called there.	 	*
; For example, BUSY EQU 7 is the pin number of the Busy signal on PORTB.	 	*
; *******************************************************************************

;Port B pin Representations
BUSY 		equ    	07             ;LCD busy bit 

;Port C pin Representations
LCDENBL   	equ     07             ;LCD Enable Pin
LCDRW  		equ     06             ;LCD Read/Write Pin
LCDRS  		equ     05             ;LCD Register Status Pin
z			equ		02
c			equ		00

;********************************************************************************
;***************************  DECLARE VARIABLES   *******************************
;********************************************************************************
; The "CBLOCK" directive tells MPASM to automatically assign addresses to 		*	
; all entries that follow it starting with the address next to the directive	*
; and continuing sequentially.													*
; *******************************************************************************

        CBLOCK  0x000
			counter					;Used to control Binary to ASCII conversion
			huns					;ASCII Hundreds digit for the LCD
			tens					;ASCII Tens digit for the LCD
			ones   	 	        	;ASCII Ones digit for the LCD  
            RESULT 	   				;Result of the division, Lower-Byte
			RESULT1					;Result of the division, Upper byte
            DIVISOR 	  			; Divisor (bottom part) Lower-Byte
			DIVISOR1				; Divisor Upper-Byte
			MULRES					;Storage for multiplier value, Lower-Byte
			MULRES1					;Storage for multiplier value, Upper-Byte
            DIVIDEND 	 			; Dividend (top part) Lower-Byte
			DIVIDEND1				; Dividend Upper-Byte
			TEMP1					; Counter for Multiplier routine
			HBYTE					;Data storage for all ADC reads
			LBYTE					; "		"		"
        	HBYTEFWD 				;Holds raw Upper-Byte information from the FWD ADC read		                   
        	LBYTEFWD 		       	;Holds raw Lower-Byte information from the FWD ADC read
			HBYTEREV				;Holds raw Upper Byte information from the Rev ADC Read
			LBYTEREV				;Holds raw Lower-Byte information from the Rev ADC Read
			HBYTEPWR				;Holds raw "Range" information from the Power ADC read
			LBYTEPWR				;Holds value information from the Power ADC read
        	TEST      		   		;Holds Range information from the HIGHTBL table
        	DIGIT1   				;The LSD sent to the LCD. Called from DIGTBL1              
        	DIGIT2    		      	;The second digit sent to the LCD. Called from DIGTBL2
        	DIGIT3					;The MSD of the LCD.  Called from DIGTBL3
        	CHAR     		       	;Used to transfer a character to the write subroutines     
        	TEMP 					;Temporarily holds the LCD word for "Busy Status" testing                 
       		COUNT1  				;These two variables Hold the values that are counted                  
        	COUNT2    				;  down for the Delay routines.                 
        	RSFLAG					;Used to tell whether we're writing Data or Instructions.                  
			SampA					;Used to hold previous readings for the Sample-Averaging
			SampB					;	"
			SampC					;	"
			SampD					;	"
			SampE					;	"
			SampF					;	"
			SampG					;	"
			SampH					;	"
			HolderA					;Place Holders for the Sample-Averaging mathematics
			HolderB					;	"
			HolderC					;	"
			HolderD					;	"
			HolderE					;	"
			HolderF					;	"
			tbloffst				;Used to compute the Table-Jump address
			HIgrph					;Holder for the Solid blocks used in the Bar Graph
			LOgrph					;Holder for the custom character used in the Bar Graph
			SPCCNTR					;Counter that ensures each digit of the Bar Graph is written
	      ENDC                      

; *******************************************************************************
; * The ORG 0x00 command tells the assembler the official "power-Up" start of	* 
;	the program in case of internal fault.						    			*
; *******************************************************************************            

        ORG     0x0000                

;********************************************************************************
;***************     DEVICE "OVERHEAD" (Configuration and Setup)        *********
;********************************************************************************

	  	clrf    INTCON      		;Clear the interrupt controller
      	clrf	BSR      			;Work from registry bank 1
		movlw	0x5E
		movwf	OSCCON				;Set up for a 2 MHz internal Clock
		movlw	0x08	
		movwf	_CONFIG1H			;Set internal Osc, no fail-safe functions
		clrf	_CONFIG2L			;No Brown-out reset, but Power-Up timer enabled
		clrf	_CONFIG2H			;No Watchdog timer
		movlw	0x81	
		movwf	_CONFIG3H			;MCLR enabled, PortB as I/O Pins
		movlw	0x84
		movwf	_CONFIG4L			;No Debugger, No extended instruction set, No stack overflow resets
		movlw	0x07
		movwf	CMCON				;Turn off the comparators
		movlw	0x80				;Turn on internal pullup resistors on PORTB
		movwf	INTCON2
		clrf	CCP2CON				;Turn CCP functions off
		clrf	PORTA				;Clear all data from Port A pins	
		movlw	0xFF
		movwf	TRISA				;Set Port A up for the ADC, All pins are inputs
        clrf    TRISB       		;Set Port B up for the LCD data, all pins are outputs
		clrf	TRISC				;Set Port C up for the LCD control, all pins are outputs

;********************************************************************************
;Following are steps of the program that need only be called once at the 		*
;beginning of the program.														*
;********************************************************************************
     	
   	Call    LCDINIT         		;Initialize the LCD


;********************************************************************************
;**************	CREATE CUSTOM LCD CHARACTERS	*********************************
;  This routine creates 5 custom LCD characters.  These characters are actually	*
; just vertical lines. The first character is blank.  The second has a single	*
; line at the left of the position, the third has two lines starting from the	*
; left side, the fourth has three lines, and so on.  These characters  			* 
; correspond to LCD memory addresses 0 through 5. They are used in the Bargraph.*	
;********************************************************************************

		bcf		PORTC,LCDRS			;clear R/S line to send an instruction
		movlw	0x40
		movwf	PORTB				;Place CGRAM starting address command on Port B
		bsf		PORTC,LCDENBL		;Set the LCD Enable Line High
	Call 	SMLDELAY
		bcf		PORTC,LCDENBL		;Lower the LCD Enable line
	Call 	SMLDELAY
		bsf		PORTC,LCDRS			;Set R/S line to send Data
	Call 	SMLDELAY
		movlw	0x00				;Define my first custom Character (a blank)
	Call	Write_Custom
		nop
		nop
		movlw	0x10				;Define my second custom Character
	Call	Write_Custom	
		nop
		nop
		movlw	0x10				;Define my third custom Character
	Call	Write_Custom			; (The same as the second character)
		nop
		nop
		movlw	0x18				;Define my fourth Custom Character		
	Call	Write_Custom
		nop
		nop
		movlw	0x1C				;Define my fifth custom character		
	Call	Write_Custom	
		nop
		nop
		movlw	0x1E				;Define my sixth custom character	
	Call	Write_Custom
		nop
		nop
		movlw	0x1E				;Define my seventh custom character	
	Call	Write_Custom			;  (The same as the sixth)
		nop
		nop
		movlw	0x1F				;Define my eighth custom character		
	Call	Write_Custom
		nop
		nop
		bcf	PORTC,LCDRS				;Return the R/S line to instruction mode
		movlw	0x01				;Reset the cursor to position 1, and reset the display
		movwf	PORTB				;  -(which essentially ends the custom writing)
		bsf	PORTC,LCDENBL			;Set the LCD enable line high
		Call SMLDELAY
		bcf	PORTC,LCDENBL			;Clear the LCD Enable line 


;********************************************************************************
;	The Main Program Loop														*
;********************************************************************************
	Call	HEADER1					;Display the Device name "Bill Lazure"
	Call	MIDDELAY				;Hold the Device name for a while
	Call	HEADER2					;Display File Name and Version Number
	Call	MIDDELAY				;Hold this display for a while
		movlw	0x01
	Call	SENDINSTR				;Clear the display

Main								;Return point for the Main Program Loop
		CLRF	DIVIDEND			;Clear variables
		CLRF	DIVIDEND1			;		"
		CLRF	DIVISOR				;		"
		CLRF	DIVISOR1			;		"
		clrf	TEST				;**Added in V30 as a safety measure
		bcf		STATUS,C			;Clear registers
		bcf		STATUS,N			;	"

	Call 	ADCREADREV				;Read Reverse Voltage
	Call 	ADCREADFWD				;Read Forward Voltage
		CLRF	STATUS,C

;********************************************************************************
;***********************		16-bit Add		*********************************
;	This routine takes the data from the Forward and Reverse ADC Reads, and 	*
;   adds it all together into a 16-bit word in Dividend and Dividend1			*
;********************************************************************************

		movff	LBYTEREV, DIVIDEND	;Move LBYTEREV into Dividend
		movff	LBYTEFWD,WREG
		addwf	DIVIDEND			;Add LBYTEFWD and Dividend
		movff	HBYTEREV,DIVIDEND1	;Move HBYTEREV into DIVIDEND+1
		movff	HBYTEFWD,WREG
		addwfc	DIVIDEND1			;Add Dividend+1, Hbytefwd, and any carry together
;********************************************************************************
	Call	Multiply				;Multiply Dividend by 10 to enable a "xx.x" 
									; display format for SWR

;********************************************************************************
;**********************		16-bit Subtract		*********************************
;  This routine subtracts the data from the Forward and Reverse (FWD - REV) ADC	*
;  Reads and puts it into a 16-bit word, Divisor & Divisor1						*
;********************************************************************************
		bcf		STATUS,N
		movff	LBYTEFWD, DIVISOR
		MOVFF	HBYTEFWD, DIVISOR1

		movff	LBYTEREV, WREG		;Move LBYTEFWD into DIVISOR 
    	SUBWF  	DIVISOR,F        	;Subtract LBYTEFWD(low) - LBYTEREV(low)
   		MOVFF   HBYTEREV,WREG   	; Now get high byte of subtrahend
    	BTFSS   STATUS,C        	; If there was a borrow, rather than
    	INCF    WREG	       		; decrement high byte of dst we inc src
    	SUBWF   DIVISOR1,F      	; Subtract the high byte and we're done
;*******************************************************************************
	
	Call	Divide					;Divide Dividend by Divisor and place in Result			

		MOVLW	0X00				;Set up to test Result & Result 1 to see if
		CPFSEQ	RESULT1				;result has rolled over 0xFF.  If so, rather
	GOTO	HISWR					;   than display digits, display "SWR HIGH"
	Call	BIN2BCD					;OK to diplay numbers, so convert binary to
									;   ASCII
	Call	DISPLAYX				;Display ASCII Digits on SWR area

point								;point is the return point for the "SWR HIGH" routine	
	Call	ADCREADPWR				;Read Input Power

		movff	LBYTE,HIgrph		;Transfer the reading into a bargraph holders
		movff	LBYTE,LOgrph		;    "      "     "      "        "
	Call	SAMPAVG					;Average the latest reading with the previous 8
	Call	HIGHTBL					;Get our "Range" template information
		movwf	TEST				;Move the table value from W To the "TEST" Register (for later use)
		clrf	W
	Call	DIGTBL1					;Get the LSD
		movwf	DIGIT1				;Place it in DIGIT1
		clrf	W
	Call	DIGTBL2					;Get the second digit
		movwf	DIGIT2				;Place it in DIGIT2.
		clrf	W
	Call	DIGTBL3					;Get the MSD
		movwf	DIGIT3				;Place it in DIGIT3.
		clrf	W

;*********************************************************************************
; At this point, we've taken an ADC reading, placed the range info into "Test",  *
; and placed the digit info into dig 1, 2, or 3.  We have only to assemble that  *
; data into one of 6 templates determined by the Range information in "TEST". 	 *
;*********************************************************************************

			btfsc	TEST,0			;Test bit 0 of "TEST". Skip the next step if it is 0.
		goto		OURANGE			;Bit 0 is set, go to the template "OUT OF RANGE".
			btfsc	TEST,1			;Test bit 1 of "TEST". Skip the next step if it is 0.
		goto 		DSP1MW			;Bit 1 is high, use the "X.XX mW" template
			btfsc	TEST,2			;Test bit 2 of "TEST". Skip the next step if it is 0.
		goto		DSP10MW			;Bit 2 is high, call the "XX.X mWatt" template
			btfsc	TEST,3			;Test bit 3 of "TEST". Skip the next step if it is 0.
		goto		DISPMW			;Bit 3 is high, call the "XXX mWatt" template
			btfsc	TEST,4			;Test bit 4 of "TEST". Skip the next step if it is 0.
		goto		DISPWATTS		;Bit 4 is high, call the "X.XX Watt" template
			btfsc	TEST,5			;Test bit 5 of "TEST". Skip the next step if it is 0.
		goto		DISPTENS		;Bit 5 is high, call the "XX.X Watt" template


	goto	Main					;The main body of the program is complete.  The remainder 
									;of the program is the subroutines and data tables. This
									;command simply return us to the beginning of the Program 
									;loop again.

;********************************************************************************
;***********		Subroutines, Displays, and Tables		*********************
;********************************************************************************

; *******************************************************************************
; *******************      LCD INITIALIZATION         ***************************
; -Initialization of the display involves sending several specific commands in a*
;  very specific order:															*
;		-Send 0x30 thrice to tell the LCD we're going to program it.			*
;		-Tell it whether we'll be writing in 4- or 8-bit mode					*
;		-Tell it how many lines we wish to use and what type of characters		*
;		-Tell it whether we want a visible cursor, and if so, what type			*
;		-Tell it whether each character emanates from the center, right, or left*
;		-Finally, clear the display for use										*
;  -This subroutine accomplishes initialization.  It need only be called once.  * 
;  -Unfortunately, when sending the initial setup codes, the LCD can not be		*
;   tested to see if it has finished.  Delays must be used to allow the LCD		*
;   time to complete its instructions.  That makes this routine relatively slow.*
; *******************************************************************************

LCDINIT
 	Call    SMLDELAY      			;Wait for LCD to fully power up

        movlw   0x30              	;The first 0x03              
        movwf   PORTB             	;Place the 0x03 on PORTB
        bsf     PORTC,LCDENBL       ;Set the LCD-Enable line high
  	Call    SMLDELAY         		; hold it high for a time
        bcf     PORTC,LCDENBL       ;Clear the LCD-enable line
        movlw   0x30              	;This group of instructions is
        movwf   PORTB             	; exactly the same as the previous.
        bsf     PORTC,LCDENBL       ; It is used to send the second
 	Call    SMLDELAY         		; 0x03.
        bcf     PORTC,LCDENBL      
        movlw   0x30              	;Finally, the third 0x03
        movwf   PORTB             
        bsf     PORTC,LCDENBL       
 	Call    SMLDELAY         
        bcf     PORTC,LCDENBL       
		bcf		PORTC,LCDRS			;Clear the Register-Select Line
		bcf		PORTC,LCDRW			;Clear the Read/Write Line
        movlw   0x38              	;Place it in 8-bit mode, single line, 5X7 character.
        movwf   PORTB             	; and Send the instruction
        bsf     PORTC,LCDENBL       ;Set the LCD-Enable line high
   	Call    SMLDELAY         		; wait a minimum time
        bcf     PORTC,LCDENBL       ;Clear the LCD-Enable line

;********************************************************************************
;    At this point, the display is initialized enough that we can use the		*
;"write" subroutines.															*
;********************************************************************************

        movlw   0x08              	;Turn the Display and cursor off
	Call    SENDINSTR          	;Send the instruction
        movlw   0x01              	;Clear the display
	Call    SENDINSTR          	;Send the instruction
        movlw   0x06              	;Move cursor after each byte is written
  	Call    SENDINSTR          	;Send the instruction
        movlw   0x0C              	;Turn Display on with no visible cursor
  	Call    SENDINSTR          	;Send the instruction
        return                    


;********************************************************************************
;*****Added in V30.  This actually writes the Custom Characters to the LCD*******
;  Each custom character consists of a matrix of dots: 8 dots vertically by 5	*
;  dots horizontally.  To define each character, we send a series of 8 "words"	*
;  to the LCD.  Each word represents one of the 8 horizontal lines.  Each word	*
;  is 5 bits wide (right justified), representing the five pixels in each row.	*
;  In this fashion, my first character, a single vertical line at the left of 	*
;  the digit, would be represented by repeating the number b'10000' 8 times:	*
;  The next character is the two left-most lines, which is simply b'11000' 8	*
;  times, leading up to a full digit which would be b'11111' repeated 8 times.	*
;  This routine accomplishes the task of sending these to the LCD.  The value	*
;  to be written is sent in the working register.  That value is put on Port B	*
;  and the enable line is raised.  This state is held for a brief period, and	*
;  the enable line is lowered.  Ultimately, the enable line is toggled 8 times,	*
;  once for each line to be written.											*
;********************************************************************************

Write_Custom
		movwf	PORTB				;Place the information on Port B
		bsf	PORTC,LCDENBL			;Set the LCD Enable line
		movlw	0x01				;Preset the delay routine,
		movwf	COUNT1				;		then
	Call VARLOOP					; run the delay routine
		bcf	PORTC,LCDENBL			;Clear the LCD Enable line
		movlw	0x01	
		movwf	COUNT1				;Repeat the preceding procedure 7 more times
	Call VARLOOP					; (because we're writing a specific "column"
		bsf	PORTC,LCDENBL			; on the character into all 8 rows of that
		movlw	0x01				;  character)
		movwf	COUNT1
	Call VARLOOP
		bcf	PORTC,LCDENBL
		movlw	0x01
		movwf	COUNT1
	Call VARLOOP
		bsf	PORTC,LCDENBL	
		movlw	0x01
		movwf	COUNT1
	Call VARLOOP
		bcf	PORTC,LCDENBL
		movlw	0x01
		movwf	COUNT1
	Call VARLOOP
		bsf	PORTC,LCDENBL
		movlw	0x01
		movwf	COUNT1
	Call VARLOOP
		bcf	PORTC,LCDENBL
		movlw	0x01
		movwf	COUNT1
	Call VARLOOP
		bsf	PORTC,LCDENBL
		movlw	0x01
		movwf	COUNT1
	Call VARLOOP
		bcf	PORTC,LCDENBL
		movlw	0x01
		movwf	COUNT1
	Call VARLOOP
		bsf	PORTC,LCDENBL
		movlw	0x01
		movwf	COUNT1
	Call VARLOOP
		bcf	PORTC,LCDENBL
		movlw	0x01
		movwf	COUNT1
	Call VARLOOP
		bsf	PORTC,LCDENBL
		movlw	0x01
		movwf	COUNT1
	Call VARLOOP
		bcf	PORTC,LCDENBL
		movlw	0x01
		movwf	COUNT1
	Call VARLOOP
		bsf	PORTC,LCDENBL
		movlw	0x01
		movwf	COUNT1
	Call VARLOOP
		bcf	PORTC,LCDENBL
		movlw	0x01
		movwf	COUNT1
	Return


;****************************************************************************
;*******************  	Multiply by 10		*********************************
;*******************		16 Apr 11		*********************************
;****************************************************************************
;-  After an extensive search of the internet, I gave up and made my own 	*
;	16-bit X 8-bit multiplier.  It simply loads a temp register with the	*
;	8-bit value and keeps adding the 16-bit to itself that number of times.	*
;	This particular sub is set up to multiply by 10, but any 8-bit			*
;	value COULD be loaded into it (with the addition of a third "result"	*
;	byte).																	*
;	-FULLY TESTED & WORKING OK!!											*
;****************************************************************************
Multiply
		movlw	0x0A			;Place '10' in the loop counter
		movwf	TEMP1

		MOVFF	DIVIDEND,MULRES	;place the dividend into the placeholder
		MOVFF	DIVIDEND1,MULRES1 ;		"
	
Mul		decfsz	TEMP1,f			;decrement the loop counter, skip the next instruction
		goto	ADD				;   if the result is 0
		Return					;Loop counter is zero, we added 10 times, return.

; DIVISOR = DIVISOR + MULRES                       
; DIVIDEND is replaced, MULRES is preserved, Carry is set correctly

ADD								;Add mulres to dividend, leave in dividend
        MOVF    MULRES,W        ;Get low byte
        ADDWF   DIVIDEND,F      ;Add to destination
        MOVF    MULRES1,W       ;Get high byte
        BTFSC   STATUS,C        ;Check for carry
        INCF    MULRES1,W       ;Add one for carry
        ADDWF   DIVIDEND1,F     ;Add high byte into DST
	goto	Mul

;*************************************************************************

;********************************************************************************	
;***************************     SAMPLE-AVERAGING        ************************
;     Reading from an ADC at a high rate, or slight noise on the input of an ADC*
;can make the LSD "jitter" and become unreadable. One method of eliminating this*
;effect is to slow the master clock to the point that each reading is viewable	*
;for a time until the next reading is available.  The disadvantage of this 		*
;method is that there is no reduction in changes of readings due to noise.		*
;     My method of reducing both the LSD jitter and incoming noise is to average*
;each reading with the previous 7 readings.  									*
;     This averaging is implemented by shifting each successive reading up in	*
;several registers called "SampA" through "SampH".  Samp A will always be the 	*
;latest reading.  These "Samples" are then added together and divided by two in	*
;pairs: SampA & SampB, SampC & SampD, etc.  The results of these intermediate	*
;averages are stored in Holders A through D.  These holders are then averaged 	*
;the same way and stored in Holders E & F.  Finally, these two holders are 		*
;averaged and the result is placed back in LBYTE for further processing.		*
;********************************************************************************

;********************************************************************************
; Move the contents of registers "Sampa" thru "SampH" up one.					*
;********************************************************************************

SAMPAVG
		clrf	SampH			;Empty the "oldest" reading
		movff	SampG,SampH		;Move old "G" read to "H"
		movff	SampF,SampG		;Move old "F" read to "G"
		movff	SampE,SampF		;Move old "E" read to "F"
		movff	SampD,SampE		;Move old "D" read to "E"
		movff	SampC,SampD		;Move old "C" read to "D"
		movff	SampB,SampC		;Move old "B" read to "C"
		movff	SampA,SampB		;Move old "A" read to "B"
		movff	LBYTE,SampA		;Place the newest reading into A

;********************************************************************************
;   Average each Pair and place in Holders										*
;********************************************************************************
		movf	SampA,W			;Place SampA in the Working Register
		addwf	SampB,0			;Perform the addition with SampB and place back in W
		movwf	HolderA			;Move the sum into HolderA
		rrncf	HolderA,F		;Divide by 2 and place back into HolderA
		bcf		HolderA,7		;Clear bit 7 of HolderA (a safety measure)

		movf	SampC,W			;Same process as SampA and SampB above
		addwf	SampD,0
		movwf	HolderB
		rrncf	HolderB,F
		bcf		HolderB,7

		movf	SampE,W			;Same process as SampA and SampB above
		addwf	SampF,0
		movwf	HolderC
		rrncf	HolderC,F
		bcf		HolderC,7

		movf	SampG,W			;Same process as SampA and SampB above
		addwf	SampH,0
		movwf	HolderD
		rrncf	HolderD,F
		bcf		HolderD,7

;********************************************************************************
;   Average the resultant averages												*
;********************************************************************************
		movf	HolderA,W		;Same process as SampA and SampB above
		addwf	HolderB,0		; only using the averages stored in
		movwf	HolderE			; Holders instead
		rrncf	HolderE,F
		bcf		HolderE,7

		movf	HolderC,W		;Same process as above
		addwf	HolderD,0
		movwf	HolderF
		rrncf	HolderF,F
		bcf		HolderF,7

		movf	HolderE,W		;Same process as above
		addwf	HolderF,0
		movwf	LBYTE
		rrncf	LBYTE,F
		bcf		LBYTE,7

		clrf	HolderA			;Clear all "Holder" registers before leaving subroutine
		clrf	HolderB
		clrf	HolderC
		clrf	HolderD
		clrf	HolderE
		clrf	HolderF
	Return



; *******************************************************************************
; ****************               WRITE OPERATIONS     		*********************
; This section performs the actual writing to the LCD.  It begins one of two 	*
; ways: Send an Instruction, or Send Data.  If it is sending an instruction		*
; it clears the RS bit;  if data, it sets the RS bit.  Once that operation is 	*
; complete, it tests the LCD to assure that it's ready to be written. If not, it*
; waits until the LCD says it is ready.  Once ready, the information is written.*
; *******************************************************************************

SENDINSTR   
        movwf   CHAR          		;Save byte to write to LCD
        clrf    RSFLAG          	;Remember to clear RS (clear RSFLAG)   
        bcf     PORTC,LCDRS      	;Set RS for Command to LCD
        goto    WRITELCD         	;Go straight to the write routine, bypassing
									; the following section
SENDDAT  						
        movwf   CHAR          		;Save byte to write to LCD
        bsf     RSFLAG,0        	;Remember to set RS (set bit 0 of RSFLAG)
        bsf     PORTC,LCDRS      	;Set RS for Data to LCD


;********************************************************************************
; The next section tests the LCD to see if the previous operation has been		*
; completed.  Rather than simply use delays which waste time, we ask the LCD if *
; it is done, and wait for it to respond affirmatively.							*
;********************************************************************************

WRITELCD 
        clrf    PORTB             	;Clear all old data on PORTB
        movlw   0x80       			;Make pin 3 an input (to read the "busy" pin)             
        movwf   TRISB             	
        bcf     PORTC,LCDRS      	;Set RS pin to the instruction mode
        bsf     PORTC,LCDRW      	;Set the R/W pin to "read"

LCDBUSY
        bsf     PORTC,LCDENBL       ;Set LCD-Enable high
        movff   PORTB,TEMP          ;Move this reading into a temporary register for testing
        bcf     PORTC,LCDENBL       ;Clear the LCD-Enable line
        btfsc   TEMP,BUSY 			;If the "busy" bit in temp is clear, skip the next line
        goto    LCDBUSY       		; If not, keep trying until it is.
        clrf    PORTB             	;The "Busy signal is clear now, so clear Port B
        movlw   0x00              	;Make all Port B pins outputs again
        movwf   TRISB             	
        bcf     PORTC,LCDRW      	;Set LCD R/W pin back to Write mode
        btfsc   RSFLAG,0        	;If RSFlag is clear, skip the next instruction
        bsf     PORTC,LCDRS      	;Set the RS pin on Port C                          
   		movff	CHAR,PORTB			;Place the character to be written on Port B
		bsf     PORTC,LCDENBL       ;Set the LCD Enable pin high
		nop
		nop							;Take a very brief pause
        nop
	nop
	nop                       	
        bcf     PORTC,LCDENBL       ;Clear the LCD Enable pin
	nop
	nop
	nop
	Return							;Return to the calling point




;********************************************************************************
;**********************  WRITE THE BAR GRAPH  ***********************************	
;  This subroutine writes the bar graph on the lower line of the LCD.  It uses 	*
; the variables HIgrph (the High nibble) to tell how many full blocks to write, *
; and LOgrph (the low nibble) to tell which custom character to write.  It 		*
; finally fills in the rest of the line with spaces.  It does all this by 		*
; setting a "Space Holder" to 16, and checking HIgrph's number.  If Higrph is	*
; zero, it checks LOgrph's value.  If HIgrph isn't 0, it writes a Full block	*
; and decrements HIgrph and SPACHLDR.  It checks for 0 again, and if still not 	*
; 0, writes another full block and decrements HIgrph and SPACHLDR again.  It 	*
; repeats this process until HIgrph reaches zero at which point it checks		*
; LOgrph. Whatever LOgrph contains is written and SPACHLDR is decremented.  	*
; Finally, SPACHOLDER itself is checked for 0.  If it is, we return to the point*
; where this subroutine was called.  If not, we write a space, decrement 		*
; SPACHLDR, and test again. When SPACHLDR reaches zero, we simply return to the	*
; program where this routine was called.  In this fashion, all 16 digits of 	*
; the line are accounted for and filled in.										*
;********************************************************************************

Write_Graph				;The actual subroutine name

; The following series of steps formats the data in HIgrph and LOgrph to suit	*
;  our needs.																	*
		rrncf	HIgrph		;These three RRNCFs divide HIgrph by 8
		rrncf	HIgrph
		rrncf	HIgrph
		movlw	0x0F		;These stpes ensure that the value of HIgrph
		andwf	HIgrph		;  cannot be greater than 16 (a safety measure)
		movlw	0x07		;Likewise, these two steps ensure that LOgrph
		andwf	LOgrph		;  cannot be greater than 8

		movlw	0xC0
	Call	SENDINSTR		;Start writing on the second line of the LCD.

		movlw	0x0F		;Set the Space Counter to 16
		movwf	SPCCNTR
		movff	HIgrph,WREG	;Place the value of HIgrph into the working register
		subwf	SPCCNTR,F	;Subtract the value of HIgrph from the Space Counter

		tstfsz	HIgrph		;If HIgrph is zero, go straight to LOWRITE, otherwise,
		goto	HIWRITE		; go to HIWRITE
		goto	LOWRITE

HIWRITE
		clrf	WREG		;HIWRITE first tests HIgrph to see if it is 0, this allows
		cpfsgt	HIgrph		;  it to write the same number of blocks as the value of HIgrph
		goto 	LOWRITE		;If HIgrph is zero, go to LOWRITE
		setf	WREG		;If HIgrph isn't 0, load 0xFF into W, and
	Call SENDDAT			;  write that value (a solid block) to the LCD
		decf	HIgrph		;Decrement HIgrph by one
		goto 	HIWRITE		;Return to HIWRITE for another test/write

LOWRITE
		movff	LOgrph,WREG	;Move the value of LOgprh into the working register
		Call	SENDDAT		;Write that custom value to the LCD

SPCWRITE
		clrf	WREG		;Clear the working register
		cpfsgt	SPCCNTR		;If space counter is zero, 
		goto Retn			;  return to where this subroutine was called
		movlw	' '			;Otherwise, place a space into the working register
	Call SENDDAT			;   and write that space to the LCD
		decf	SPCCNTR		;decrement the Space Counter by one
		goto 	SPCWRITE	;Return to SPCWRITE for another test/write

		clrf	HIgrph		;Safety measure, clear both holders
		clrf	LOgrph

Retn	Return


;********************************************************************************
;***************		BINARY To 3-Digit ASCII		*****************************
;***************									*****************************
;  I obtained this from an anonymous source on the internet.  It inputs an 8-bit*
;	word into "RESULT" and outputs into huns, tens, ones in ASCII suitable for	*
;	display on an LCD.									  						*
;  -16 Apr 11 - Verified correct operation...This subroutine works well!	  	*
;********************************************************************************
;********************************************************************************

BIN2BCD
	movlw 8
	movwf counter
		clrf huns
		clrf tens
		clrf ones

BCDADD3
		movlw 5
		subwf huns,0
		btfsc STATUS,C
	CALL ADD3HUNS

		movlw 5
		subwf tens,0
		btfsc STATUS,C
	CALL ADD3TENS

		movlw 5
		subwf ones,0
		btfsc STATUS,C
	CALL ADD3ONES

		decf counter,1
		bcf STATUS,C
		rlcf RESULT,1
		rlcf ones,1
		btfsc ones,4  
	CALL CARRYONES
		rlcf tens,1

		btfsc tens,4 
	CALL CARRYTENS
		rlcf huns,1
		bcf STATUS,C

		movf counter,0
		btfss STATUS,Z
	GOTO BCDADD3


		movlw	0x00		;Here, I'm setting up for leading zero	
		cpfsgt	huns		; suppression.  If the value in "hundreds"
		goto	null		; is zero, I skip writing and write a blank instead.
		movf huns,0 		; add ASCII Offset
		addlw h'30'
		movwf huns
back
		movf tens,0 		; add ASCII Offset
		addlw h'30'
		movwf tens

		movf ones,0 		; add ASCII Offset
		addlw h'30'
		movwf ones

	RETURN

null
		movlw	' '
		movwf	huns
		goto 	back

ADD3HUNS
		movlw 3
		addwf huns,1
	RETURN

ADD3TENS
		movlw 3
		addwf tens,1
	RETURN

ADD3ONES
		movlw 3
		addwf ones,1
	RETURN

CARRYONES
		bcf ones, 4
		bsf STATUS,C
	RETURN

CARRYTENS
		bcf tens, 4
		bsf STATUS,C
	RETURN


;********************************************************************************
;*************						DIVIDE				*************************
;********************************************************************************
;18 Apr 11 - Digital dividing can be accomplished by "successive division", 	*
;   wherein the divisor (bottom number) is subtracted from the dividend repeatedly
;   until the operation produces a negative number.  The number of subtractions	*
;   is the result.																*
; **This routine has been tested and is functional.								*
;********************************************************************************
 
Divide
	Clrf	RESULT
	CLRF	RESULT1
	BCF		STATUS,C
	BCF		STATUS,N
	TSTFSZ	DIVISOR					;If the divisor is zero, return undone
	Goto	test					; Otherwise, start the division
	Return
test
        MOVF    DIVISOR,WREG        ; Get low byte of subtrahend
        SUBWF  	DIVIDEND,F         	; Subtract DST(low) - SRC(low)
        MOVF    DIVISOR1,WREG       ; Now get high byte of subtrahend
        BTFSS   STATUS,C        	; If there was a borrow, rather than
        INCFSZ  DIVISOR1,WREG      	; decrement high byte of dst we inc src
		BCF		STATUS,N
        SUBWF  	DIVIDEND1,F       	; Subtract the high byte and we're done

	btfss	STATUS,N				;Is the result negative?
	Call 	Increment				;Result is NOT negative, Increment result and repeat
	return							;Result is negative, end here		

Increment
	Incf	RESULT					;result is not negative, Increment Result
	BTFSC	STATUS,C				;If Carry bit is set, we "rolled" Result
	INCF	RESULT1					; over, so incement the higher byte
	goto	test					; we counted up, return for another test
	Return

;*********************************************************************************

;********************************************************************************
;*************************         DELAY SUBROUTINES    *************************
;  These subroutines work by "wasting" operations.  Specifically, they place	* 
;  the program into a loop to count down from preset values.  Since we can know	*
;  the exact amount of time it takes for each instruction cycle (1/Fosc/4) 		*
;  (about 2 microseconds), we can simply set each preset to multiply that time	*
;  up to the desired delay length.											    *
; *******************************************************************************

LNGDELAY 						;Delay of about 1S					
 		Call 	MIDDELAY
		Call	MIDDELAY
		Call 	MIDDELAY
 		Call 	MIDDELAY
		Call	MIDDELAY
		Call 	MIDDELAY
		Call	MIDDELAY
MIDDELAY						;MIDDELAY provides about 130mS delay 
        movlw   0xFF           	;Preset COUNT1 with 255
        movwf   COUNT1          
        goto    VARLOOP         ;Go to the loops to begin decrementing
SMLDELAY   						;SMLDELAY provides about 32mS of delay    
        movlw   0x40            ;Preset COUNT1 with 0x40
        movwf   COUNT1          
        goto    VARLOOP        	;Go to the loops to begin decrementing

VARLOOP                        
        movlw   0xFF   			;Preset Fixed loop counter to FF which
        movwf   COUNT2          ; corresponds to about a 500 microS delay
FIXDLOOP
        decfsz  COUNT2,f        ;Decrement fixed loop count, skip next if COUNT2 = 0
        goto    FIXDLOOP        ;COUNT2 isn't 0 yet, so decrement again
        decfsz  COUNT1,f        ;Decrement the variable count loop, skip next when COUNT1 reaches 0
        goto    VARLOOP        	;COUNT1 isn't 0 yet, so load COUNT2 again and restart the loops
	Return                  	;Both counters are zero, return to caller
       
;********************************************************************************
;****************************  ADC READ   ***************************************
; ADCREAD is used to read the Analog to Digital Converter. On a 18F2520, this is*
;simply a matter of setting a "start" bit high in the ADCON0 register.  This bit*
;in conjunction with some presets at the beginning of the program, starts the	*
;ADC.  When it has completed its reading, it returns a bit in the same register	*
;that we can use to tell us the conversion is complete.							*
;	Once the "ADC is Complete" bit is set, we then proceed to format the data	*
;for our purposes.  This involves simply transferring the readings from the ADC-*
;result registers "ADRESH" and "ADRESL" to our test registers "HBYTE" and		*
;"LBYTE*, and moving the MSB of LBYTE to the LSB of HBYTE.						*
;********************************************************************************

ADCREADFWD

			movlw	0x08			;Set ADC references Internal, Set AN0 thru AN4 as Analog
			movwf	ADCON1
			movlw	0x01			;Enable ADC
			movwf	ADCON0			;		"
			movlw	0xBA			;Set up for Right justified result, Long Tad, Fosc/8
			movwf	ADCON2
			bsf		ADCON0,1		;Begin the actual reading		
Statloop	btfsc	ADCON0,1		;Read the "ADC Complete" flag, keep reading
			goto 	Statloop		;  until the ADC is finished and sets the flag.
			movff	ADRESH,HBYTEFWD	;Move the ADC High byte to HBYTE
			movff	ADRESL,LBYTEFWD	;Move the ADC Low byte to LBYTE
			clrf	ADRESH			;Clear these registers prior to the next read
			clrf	ADRESL
		RETURN

ADCREADREV
			movlw	0x08			;Set ADC references Internal, Set AN0 thru AN4 as Analog
			movwf	ADCON1
			movlw	0x05			;Enable ADC
			movwf	ADCON0			;		"
			movlw	0xBA			;Set up for Right justified result, Long Tad, Fosc/8
			movwf	ADCON2
			bsf		ADCON0,1		;Start the reading
Statloop2	btfsc	ADCON0,1		;Read the "ADC Complete" flag, keep reading
			goto 	Statloop2		;  until the ADC is finished and sets the flag.
			movff	ADRESH,HBYTEREV	;Move the ADC High byte to HBYTE
			movff	ADRESL,LBYTEREV	;Move the ADC Low byte to LBYTE
			clrf	ADRESH			;Clear these registers prior to the next read
			clrf	ADRESL
		RETURN

ADCREADPWR
			movlw	0x38			;Set External ADC references, Set AN0 thru AN4 as Analog
			movwf	ADCON1
			movlw	0x11			;Enable ADC
			movwf	ADCON0			;		"
			movlw	0xBA			;Set up for Right justified result, Long Tad, Fosc/8
			movwf	ADCON2
			BSF		ADCON0,1		;Start the actual reading
Statloop3	btfsc	ADCON0,1		;Read the "ADC Complete" flag, keep reading
			goto 	Statloop3		;  until the ADC is finished and sets the flag.
			movff	ADRESH,HBYTE	;Move the ADC High byte to HBYTE
			movff	ADRESL,LBYTE	;Move the ADC Low byte to LBYTE
			rlncf	HBYTE			;Rotate the ADC High byte one digit to the left
			bcf		HBYTE,0			;Clear the ADC High byte's LSD
			btfsc	LBYTE,7			;Is the ADC Low byte's MSD High?
			bsf		HBYTE,0			;If so, set the High-byte's LSD High too.
			bcf		LBYTE,7			;Ensure that LBYTE's MSD is clear
			clrf	ADRESH			;Clear these registers prior to the next read
			clrf	ADRESL
		RETURN




;********************************************************************************
;**********************       DISPLAY HEADERS    ********************************
;  This sections displays 2 brief messages at the device turn-on.  It serves no	*
;  operational purpose to the program.											*
;********************************************************************************	

HEADER1
        movlw   0x80              	;Start writing at the first digit
	Call    SENDINSTR          
        movlw   'B'      			;Send each of these characters to the LCD         	    
    Call    SENDDAT          		; to be written as character strings
        movlw  	'i'                   
    Call    SENDDAT         
        movlw   'l'              
    Call    SENDDAT          
        movlw  	'l'              
    Call    SENDDAT          
        movlw	' '             
    Call    SENDDAT         
        movlw   'L'          
    Call    SENDDAT         
        movlw   'a'    
    Call    SENDDAT 
        movlw   'z'   
    Call    SENDDAT
        movlw   'u'    
    Call    SENDDAT 
        movlw   'r'      
    Call    SENDDAT 
        movlw   'e'      
    Call    SENDDAT  
        movlw   ' '     
    Call    SENDDAT 
        movlw   'W'      
    Call    SENDDAT 
        movlw   '2'      
    Call    SENDDAT   
        movlw   'E'        
    Call    SENDDAT  
        movlw   'B'       
    Call    SENDDAT  
  	Return

HEADER2
        movlw   0x80              	;Start writing at the first digit
    Call    SENDINSTR          
        movlw   'S'      			;Send each of these characters to the LCD         	    
    Call    SENDDAT          		; to be written as character strings
        movlw  	'W'                   
    Call    SENDDAT         
        movlw   'R'             
    Call    SENDDAT          
        movlw  	'/'              
    Call    SENDDAT          
        movlw	'P'             
    Call    SENDDAT         
        movlw   'W'          
    Call    SENDDAT         
        movlw   'R'    
    Call    SENDDAT 
        movlw   ' '   
    Call    SENDDAT
        movlw   'M'    
    Call    SENDDAT 
        movlw   'T'      
    Call    SENDDAT 
        movlw   'R'      
    Call    SENDDAT  
        movlw   ' '     
    Call    SENDDAT 
        movlw   'v'      
    Call    SENDDAT 
        movlw   '9'      
    Call    SENDDAT   
        movlw   'a'        
    Call    SENDDAT  
        movlw   ' '       
    Call    SENDDAT  
 	Return


;********************************************************************************
;**************************    DISPLAY TEMPLATES       **************************
; These are the templates called for by the contents of the test register as 	*
; directed by the HBYTE read.  They are:										*
;	-OURANGE, "Out of Range"													*
;	-DSP1MW,  "X.XX mWatts"														*
;	-DSP10MW, "XX.X mWatts"														*
;	-DSPMW,   "XXX mWatts"														*
;	-DSPWATTS, "X.XX Watts"														*
;	-DSPTENS,  "XX.X Watts"														*
;**Added in V36- The last active step of each numeric template is to update the	*
;	bargraph display.															*
;********************************************************************************

DISPLAYX
									;SWR Display Template
		movlw   0x87              	;Start writing at the first digit
    Call    SENDINSTR          		;Send the instruction
        movlw   'S'               
    Call    SENDDAT          
		movlw	'W'
	Call	SENDDAT					
		movlw	'R'
	Call	SENDDAT
		movff huns,WREG
	Call	SENDDAT	
 		movff tens,WREG
	Call	SENDDAT	
       movlw   '.'               
    Call    SENDDAT
		movff	ones,WREG
	Call	SENDDAT          					
		movlw	':'
	Call	SENDDAT
		movlw	'1'
	Call	SENDDAT
	Return 

HISWR								;Over-Range condition for SWR readings
		movlw   0x87              	;Start writing at the first digit
    Call    SENDINSTR          		;Send the instruction
        movlw   'S'               
    Call    SENDDAT          
		movlw	'W'
	Call	SENDDAT					
		movlw	'R'
	Call	SENDDAT
		movlw	' '
	Call	SENDDAT	
 		movlw	'H'
	Call	SENDDAT	
       movlw  	'I'               
    Call    SENDDAT
		movlw	'G'
	Call	SENDDAT          					
		movlw	'H'
	Call	SENDDAT
		movlw	' '
	Call	SENDDAT

	goto point            

OURANGE								;Power "Out of Range" Display Template
		movlw   0x80              	;Start writing at the first digit
    Call    SENDINSTR          		;Send the instruction
        movlw   'R'               
    Call    SENDDAT          
		movlw	'A'
	Call	SENDDAT					
		movlw	'N'
	Call	SENDDAT
		movlw	'G'
	Call	SENDDAT
		movlw	'E'
	Call	SENDDAT
       movlw   ' '               
    Call    SENDDAT          
		clrf	TEST	
       	goto Main                    

DSP1MW								;Display Template for X.XX mWatts
        movlw   0x80              	;Start writing at the first digit
    Call    SENDINSTR          		;Send the instruction
 		movf	DIGIT3,W
	Call	SENDDAT
		movlw	'.'
	Call	SENDDAT
		movf	DIGIT2,W
	Call	SENDDAT
		movf	DIGIT1,W
	Call	SENDDAT
		movlw	'm'
	Call	SENDDAT
		movlw	'W'
	Call	SENDDAT
	Call	Write_Graph				;When done writing digits, write the Bar Graph
		clrf	TEST	
        goto	Main                   

DSP10MW								;Display Template for XX.X mWatts
        movlw   0x80              	;Start writing at the first digit
    Call    SENDINSTR          		;Send the instruction
 		movf	DIGIT3,W
	Call	SENDDAT
		movf	DIGIT2,W
	Call	SENDDAT
		movlw	'.'
	Call	SENDDAT
		movf	DIGIT1,W
	Call	SENDDAT
		movlw	'm'
	Call	SENDDAT
		movlw	'W'
	Call	SENDDAT

	Call	Write_Graph				;When done writing digits, write the Bar Graph
		clrf	TEST	
        goto	Main                    

DISPMW								;Display Template for XXX mWatts
        movlw   0x80              	;Start writing at the first digit
    Call    SENDINSTR          		;Send the instruction
		movf	DIGIT3,W
	Call	SENDDAT
		movf	DIGIT2,W
	Call	SENDDAT
		movf	DIGIT1,W
	Call	SENDDAT
       movlw   ' '               
    Call    SENDDAT          
		movlw	'm'
	Call	SENDDAT
		movlw	'W'
	Call	SENDDAT
	Call	Write_Graph				;When done writing digits, write the Bar Graph
		clrf	TEST	
        goto	Main                    

DISPWATTS							;Display Template for X.XX Watts
        movlw   0x80              	;Start writing at the first digit
    Call    SENDINSTR          		;Send the instruction
		movf	DIGIT3,W
	Call	SENDDAT
		movlw	'.'
	Call	SENDDAT
		movf	DIGIT2,W
	Call	SENDDAT
		movf	DIGIT1,W
	Call	SENDDAT
       movlw   ' '               
    Call    SENDDAT          
		movlw	'W'
	Call	SENDDAT
	Call	Write_Graph				;When done writing digits, write the Bar Graph
		clrf	TEST	
        goto	Main                    

DISPTENS							;Display Template for XX.X Watts
        movlw   0x80              	;Start writing at the first digit
    Call    SENDINSTR          		;Send the instruction
		movf	DIGIT3,W
	Call	SENDDAT
		movf	DIGIT2,W
	Call	SENDDAT
		movlw	'.'
	Call	SENDDAT
		movf	DIGIT1,W
	Call	SENDDAT
       movlw   ' '               
    Call    SENDDAT          
		movlw	'W'
	Call 	SENDDAT
		Call	Write_Graph				;When done writing digits, write the Bar Graph
		clrf	TEST	
      goto 	Main                     



;********************************************************************************
;***********************              TABLES              ***********************
; The tables work by holding their values in a specific location; namely, 		*
; their number from the top of the table.  For example, If I want to read the	*
; table and I have just read number 7 from LBYTE, I call the table.  The first	*
; line of the table adds the contents of LBYTE (7) to the program counter at 	*
; that point (753).  When  that addition is made, the program jumps to the new 	*
; point in the program (line 760).  Line 760 holds the command RETLW 31 which 	*
; means "return with literal 31 in the working register".  The return they mean *
; is the line of code that called the table. The number 31 corresponds to ASCII *
; "1" which will later be used as a character for an LCD display.				*
;********************************************************************************

DIGTBL1	movff	LBYTE,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(TBL1)
		btfsc	STATUS,C
		incf	WREG,W
		movwf	PCLATH
		movlw	LOW(TBL1)
		addwf	tbloffst,W
		btfsc	STATUS,C
		incf	PCLATH,F
		movwf	PCL
		
TBL1	RETLW	30H				;This table holds the value of the LSD	
		RETLW	32H				
		RETLW	34H				
		RETLW	35H				;**The digit notations 0x00 and 00H mean		
		RETLW	37H				; the same thing.  They are simply two different
		RETLW	39H				; ways of showing digits in hexadecimal.  The			
		RETLW	31H				; tables use the notation ??H because they retain			
		RETLW	33H			 	; that format from the previous version of the
		RETLW	35H				; program.  New lines of code use the 0x00 format.
		RETLW	37H	;10			
		RETLW	39H				
		RETLW	31H				
		RETLW	34H				
		RETLW	36H				
		RETLW	39H				
		RETLW	31H				
		RETLW	33H				
		RETLW	36H				
		RETLW	38H
		RETLW	31H	;20
		RETLW	33H
		RETLW	36H
		RETLW	39H
		RETLW	31H
		RETLW	34H
		RETLW	37H
		RETLW	30H
		RETLW	33H
		RETLW	35H
		RETLW	38H	;30
		RETLW	32H
		RETLW	35H
		RETLW	38H
		RETLW	31H
		RETLW	34H
		RETLW	38H
		RETLW	31H
		RETLW	35H
		RETLW	38H
		RETLW	31H	;40
		RETLW	35H
		RETLW	39H
		RETLW	33H
		RETLW	37H
		RETLW	31H
		RETLW	35H
		RETLW	39H
		RETLW	33H
		RETLW	37H
		RETLW	31H	;50
		RETLW	36H
		RETLW	35H
		RETLW	35H
		RETLW	39H
		RETLW	34H
		RETLW	39H
		RETLW	34H
		RETLW	39H
		RETLW	34H
		RETLW	39H	;60
		RETLW	34H
		RETLW	30H
		RETLW	35H
		RETLW	31H
		RETLW	36H
		RETLW	32H
		RETLW	38H
		RETLW	34H
		RETLW	30H
		RETLW	36H	;70
		RETLW	32H
		RETLW	39H
		RETLW	35H
		RETLW	32H
		RETLW	39H
		RETLW	35H
		RETLW	32H
		RETLW	30H
		RETLW	37H
		RETLW	34H	;80
		RETLW	32H
		RETLW	39H
		RETLW	37H
		RETLW	35H
		RETLW	33H
		RETLW	31H
		RETLW	30H
		RETLW	38H
		RETLW	37H
		RETLW	36H	;90
		RETLW	35H
		RETLW	34H
		RETLW	33H
		RETLW	33H
		RETLW	32H
		RETLW	32H
		RETLW	32H
		RETLW	33H
		RETLW	33H
		RETLW	34H	;100
		RETLW	34H
		RETLW	35H
		RETLW	36H
		RETLW	37H
		RETLW	39H
		RETLW	31H
		RETLW	33H
		RETLW	35H
		RETLW	38H
		RETLW	31H	;110
		RETLW	33H
		RETLW	36H
		RETLW	30H
		RETLW	33H
		RETLW	37H
		RETLW	31H
		RETLW	36H
		RETLW	30H
		RETLW	35H
		RETLW	30H	;120
		RETLW	36H
		RETLW	31H
		RETLW	38H
		RETLW	33H
		RETLW	31H
		RETLW	37H
		RETLW	35H
		RETLW	32H
		RETURN

DIGTBL2	movff	LBYTE,tbloffst	;This routine replaces the original Table counter routine.
		bcf		STATUS,C		;It must be used because the program counter
		rlcf	tbloffst,F		;on a 18F2520 counts by twos.
		movlw	HIGH(TBL2)
		btfsc	STATUS,C
		incf	WREG,W
		movwf	PCLATH
		movlw	LOW(TBL2)
		addwf	tbloffst,W
		btfsc	STATUS,C
		incf	PCLATH,F
		movwf	PCL

TBL2	RETLW	30H				;This table holds the Second display digit
		RETLW	30H
		RETLW	30H
		RETLW	30H
		RETLW	30H
		RETLW	30H
		RETLW	31H
		RETLW	31H
		RETLW	31H
		RETLW	31H	;10
		RETLW	31H
		RETLW	32H
		RETLW	32H
		RETLW	32H
		RETLW	32H
		RETLW	33H
		RETLW	33H
		RETLW	33H
		RETLW	33H
		RETLW	34H	;20
		RETLW	34H
		RETLW	34H
		RETLW	34H
		RETLW	35H
		RETLW	35H
		RETLW	35H
		RETLW	36H
		RETLW	36H
		RETLW	36H
		RETLW	36H	;30
		RETLW	37H
		RETLW	37H
		RETLW	37H
		RETLW	38H
		RETLW	38H
		RETLW	38H
		RETLW	39H
		RETLW	39H
		RETLW	39H
		RETLW	30H	;40
		RETLW	30H
		RETLW	30H
		RETLW	31H
		RETLW	31H
		RETLW	32H
		RETLW	32H
		RETLW	32H
		RETLW	33H
		RETLW	33H
		RETLW	34H	;50
		RETLW	34H
		RETLW	35H
		RETLW	35H
		RETLW	35H
		RETLW	36H
		RETLW	36H
		RETLW	37H
		RETLW	37H
		RETLW	38H
		RETLW	38H	;60
		RETLW	39H
		RETLW	30H
		RETLW	30H
		RETLW	31H
		RETLW	31H
		RETLW	32H
		RETLW	32H
		RETLW	33H
		RETLW	34H
		RETLW	34H	;70
		RETLW	35H
		RETLW	35H
		RETLW	36H
		RETLW	37H
		RETLW	37H
		RETLW	38H
		RETLW	39H
		RETLW	30H
		RETLW	30H
		RETLW	31H	;80
		RETLW	32H
		RETLW	32H
		RETLW	33H
		RETLW	34H
		RETLW	35H
		RETLW	36H
		RETLW	37H
		RETLW	37H
		RETLW	38H
		RETLW	39H	;90
		RETLW	30H
		RETLW	31H
		RETLW	32H
		RETLW	33H
		RETLW	34H
		RETLW	35H
		RETLW	36H
		RETLW	37H
		RETLW	38H
		RETLW	39H	;100
		RETLW	30H
		RETLW	31H
		RETLW	32H
		RETLW	33H
		RETLW	34H
		RETLW	36H
		RETLW	37H
		RETLW	38H
		RETLW	39H
		RETLW	31H	;110
		RETLW	32H
		RETLW	33H
		RETLW	35H
		RETLW	36H
		RETLW	37H
		RETLW	39H
		RETLW	30H
		RETLW	32H
		RETLW	33H
		RETLW	35H	;120
		RETLW	36H
		RETLW	38H
		RETLW	39H
		RETLW	31H
		RETLW	33H
		RETLW	34H
		RETLW	36H
		RETLW	38H
		RETLW	30H
		RETURN

HIGHTBL	movff	HBYTE,tbloffst	;This routine replaces the original Table counter routine.
		bcf		STATUS,C		;It must be used because the program counter
		rlcf	tbloffst,F		;on a 18F2520 counts by twos.
		movlw	HIGH(TBLHI)
		btfsc	STATUS,C
		incf	WREG,W
		movwf	PCLATH
		movlw	LOW(TBLHI)
		addwf	tbloffst,W
		btfsc	STATUS,C
		incf	PCLATH,F
		movwf	PCL

TBLHI	RETLW	01H				;This table holds "Range" Information		
		RETLW	02H				
		RETLW	04H
		RETLW	08H
		RETLW	10H
		RETLW	20H
		RETLW	01H
		RETLW	01H
		RETURN

DIGTBL3	movff	LBYTE,tbloffst	;This routine replaces the original Table counter routine.
		bcf		STATUS,C		;It must be used because the program counter
		rlcf	tbloffst,F		;on a 18F2520 counts by twos.
		movlw	HIGH(TBL3)
		btfsc	STATUS,C
		incf	WREG,W
		movwf	PCLATH
		movlw	LOW(TBL3)
		addwf	tbloffst,W
		btfsc	STATUS,C
		incf	PCLATH,F
		movwf	PCL

TBL3	RETLW	31H	;0			;This table holds value of the MSD
		RETLW	31H
		RETLW	31H
		RETLW	31H
		RETLW	31H
		RETLW	31H
		RETLW	31H
		RETLW	31H
		RETLW	31H
		RETLW	31H
		RETLW	31H	;10
		RETLW	31H
		RETLW	31H
		RETLW	31H
		RETLW	31H
		RETLW	31H
		RETLW	31H
		RETLW	31H
		RETLW	31H
		RETLW	31H
		RETLW	31H	;20
		RETLW	31H
		RETLW	31H
		RETLW	31H
		RETLW	31H
		RETLW	31H
		RETLW	31H
		RETLW	31H
		RETLW	31H
		RETLW	31H
		RETLW	31H	;30
		RETLW	31H
		RETLW	31H
		RETLW	31H
		RETLW	31H
		RETLW	31H
		RETLW	31H
		RETLW	31H
		RETLW	31H
		RETLW	32H	
		RETLW	32H	;40
		RETLW	32H
		RETLW	32H
		RETLW	32H
		RETLW	32H
		RETLW	32H
		RETLW	32H
		RETLW	32H
		RETLW	32H
		RETLW	32H	
		RETLW	32H	;50
		RETLW	32H
		RETLW	32H
		RETLW	32H
		RETLW	32H
		RETLW	32H
		RETLW	32H
		RETLW	32H
		RETLW	32H
		RETLW	32H	
		RETLW	32H	;60
		RETLW	33H	
		RETLW	33H
		RETLW	33H
		RETLW	33H
		RETLW	33H
		RETLW	33H
		RETLW	33H
		RETLW	33H	
		RETLW	33H
		RETLW	33H	;70
		RETLW	33H
		RETLW	33H
		RETLW	33H
		RETLW	33H
		RETLW	33H
		RETLW	33H
		RETLW	34H
		RETLW	34H	
		RETLW	34H
		RETLW	34H	;80
		RETLW	34H
		RETLW	34H
		RETLW	34H
		RETLW	34H
		RETLW	34H
		RETLW	34H
		RETLW	34H
		RETLW	34H	
		RETLW	34H
		RETLW	35H	;90
		RETLW	35H
		RETLW	35H
		RETLW	35H
		RETLW	35H
		RETLW	35H
		RETLW	35H
		RETLW	35H
		RETLW	35H	
		RETLW	35H
		RETLW	35H	;100
		RETLW	36H
		RETLW	36H
		RETLW	36H
		RETLW	36H
		RETLW	36H
		RETLW	36H
		RETLW	36H	
		RETLW	36H	
		RETLW	36H
		RETLW	37H	;110
		RETLW	37H
		RETLW	37H
		RETLW	37H
		RETLW	37H
		RETLW	37H
		RETLW	37H
		RETLW	38H
		RETLW	38H	
		RETLW	38H
		RETLW	38H	;120
		RETLW	38H
		RETLW	38H
		RETLW	38H
		RETLW	39H
		RETLW	39H
		RETLW	39H
		RETLW	39H
		RETLW	39H
		RETURN

        END
