;
; Water System Controller
;
; Copyright (C) 1996, 1999 Allan N. Hessenflow, Los Gatos, California.
;
; Permission is granted to use this code or portions thereof for non-
; commercial purposes provided this copyright notice is preserved
; intact and credit is given to the author in any accompanying
; documentation.
;
; allanh@kallisti.com
;

		list	c=80, f=INHX8M, p=16C74
		__config _CP_OFF & _PWRTE_ON & _WDT_OFF & _HS_OSC

		include	<p16c74.inc>


; Program locs
resetVector	equ	h'0000'		; Address of RESET Vector
interruptVector	equ	h'0004'		; Address of Interrupt Vector

; Misc. constants
OscFreq		equ	d'16000000'	; Oscillator Frequency is 16 MHz

; LCD memory locations
lcdLine0	equ	h'00'
lcdLine1	equ	h'40'

; PORTA bits
pressure	equ	0		; pressure transducer
serialDTR	equ	4		; handshake input

; PORTB bits
#define wellPump1	PORTB,0
#define pressurePump	PORTB,1
#define levelSwitch1    PORTB,2
#define levelSwitch2    PORTB,3
#define levelSwitch3    PORTB,4
#define levelSwitch4    PORTB,5
button1		equ	6
button2		equ	7

; PORTC bits
backlight	equ	0
serialCTS	equ	1		; handshake output
speaker		equ	2
eepromClock	equ	3
eepromData	equ	4
#define wellPump2	PORTC,5
serialXmit	equ	6
serialRecv	equ	7

; PORTD bits
lcdData		equ	PORTD		; LCD data lines interface
lcdDataTris	equ	TRISD

; PORTE bits
#define lcdRS	PORTE,0			; LCD Register-Select control line
#define lcdRW	PORTE,1			; LCD Read/Write control line
#define lcdE	PORTE,2			; LCD Enable control line

; variables (bank 0)
	cblock	h'20'
		savedW			; isr temp location - must have same
					; address in bank 1
		savedStatus		; isr temp location
		savedFSR		; isr temp location
		savedADCResult		; isr temp location
		lowByte			; isr temp
		highByte		; isr temp
		tempChannel		; isr temp

		; some temp variables used in main code
		lowTemp
		highTemp
		lowTemp2
		highTemp2
		digit
		lcdTemp			; LCD subroutines internal use
		tableIndex		; Index to table strings
		count			; A counter
		delay			; Used in delayxxx routines
		xDelay			; Used in xDelayxxx routines

		; following timers run at 7.6Hz
		pumpOnTimer		; minimum pump run time
		pumpOffTimer		; minimum pump relaxation time
		switchTimerLSB		; time between switching wells
		switchTimerMSB
		lastWell
		alarmTimer		; time between beeps for alarm
		beepTimer		; beep duration timer
		button1Timer		; button 1 debounce timer
		button2Timer		; button 2 debounce timer
		displayTimer		; status display update timer
		modeResetTimer		; reset mode to automatic after

		wantPressure		; want pressure pump on
		wantWells		; want wells on
		sampleCounter		; # of summed samples of each adc channel
		adcChannel		; current channel to digitize
		; the following eight locations must be sequential
		sum0LSB			; sum adc channel 0, low byte
                sum0MSB			; sum adc channel 0, high byte
		sum1LSB			; sum adc channel 1, low byte
                sum1MSB			; sum adc channel 1, high byte
		sum2LSB			; sum adc channel 2, low byte
                sum2MSB			; sum adc channel 2, high byte
		sum3LSB			; sum adc channel 3, low byte
                sum3MSB			; sum adc channel 3, high byte
		ssum1LSB		; sum of squares channel 1 low byte
		ssum1			; sum of squares channel 1 middle byte
		ssum1MSB		; sum of squares channel 1 high byte
		ssum2LSB		; sum of squares channel 2 low byte
		ssum2			; sum of squares channel 2 middle byte
		ssum2MSB		; sum of squares channel 2 high byte
		ssum3LSB		; sum of squares channel 3 low byte
		ssum3			; sum of squares channel 3 middle byte
		ssum3MSB		; sum of squares channel 3 high byte
		pressureLSB		; current pressure, low byte
		pressureMSB		; current pressure, high byte
                offset1			; pump 1 current offset
                offset2			; pump 2 current offset
                offset3			; pump 3 current offset
		current1LSB		; pump 1 current LSB
		current1		; pump 1 current middle byte
		current1MSB		; pump 1 current MSB
		current2LSB		; pump 2 current LSB
		current2		; pump 2 current middle byte
		current2MSB		; pump 2 current MSB
		current3LSB		; pump 3 current LSB
		current3		; pump 3 current middle byte
		current3MSB		; pump 3 current MSB
		time			; 15.625KHz timer
		newSampleValues		; new values available flag
		buttonsStoredState	; current button states
		button1Down		; button 1 pushed flag
		button2Down		; button 2 pushed flag


		endBank0
	endc
; variables (bank 1)
	cblock	h'a0'
		savedW2			; isr temp location - must have same
					; address in bank 0


		endBank1
	endc

	if ( endBank0 > h'80' )
		messg   "Warning: Out of space in bank 0"
	endif
	if ( endBank1 > h'100' )
		messg   "Warning: Out of space in bank 1"
	endif
	if ( savedW != (savedW2 & h'7f') )
		messg   "Warning: W save location not equivalent in both banks"
	endif

; Program start
		org	resetVector
reset		goto	start


; This is the Periperal Interrupt routine.
		org	interruptVector	; Interrupt vector location
interrupt	movwf	savedW		; save W; could be in either bank
		swapf	STATUS,W
		bcf	STATUS,RP0	; change to bank 0
		movwf	savedStatus	; save status
		movf	FSR,W
		movwf	savedFSR

		btfsc	PIR1,ADIF
		call	handleADC	
		btfsc	PIR1,TMR2IF
		call	handleTimer

		movf	savedFSR,W
		movwf	FSR
		swapf	savedStatus,W	; get original status back
		movwf	STATUS
		swapf	savedW,F
		swapf	savedW,W
		retfie


handleADC	movlw	sum0LSB
		addwf	adcChannel,W
		addwf	adcChannel,W
		movwf	FSR		; FSR is now index pointing at lsb of sum
		movf	ADRES,W
		movwf	savedADCResult
		bcf	PIR1,ADIF
		addwf	INDF,F
		incf	FSR,F		; increment doesn't effect Carry
		btfsc	STATUS,C
		incf	INDF,F
		; FSR now points to sumxMSB
		movf	adcChannel,F	; check for channel 0
		btfsc	STATUS,Z
		return			; for pressure channel, we're done

		movlw	offset1-1	; -1 because we start at channel 1
		addwf	adcChannel,W
		movwf	FSR		; we now point at offset for this channel
		movf	savedADCResult,W
		subwf	INDF,W		; subtract from offset
		btfss	STATUS,C	; take absolute value
		xorlw	h'ff'
		btfss	STATUS,C
		addlw	h'01'
		movwf	savedADCResult
		movlw	ssum1LSB-3	; -3 because we start at channel 1
		addwf	adcChannel,W
		addwf	adcChannel,W
		addwf	adcChannel,W
		movwf	FSR		; now point at LSB of sum of squares
		clrf	highByte
		clrf	lowByte
		movf	savedADCResult,W
		bcf	STATUS,C
		btfsc	savedADCResult,0
		addwf	highByte,F
		rrf	highByte,F
		rrf	lowByte,F
		btfsc	savedADCResult,1
		addwf	highByte,F
		rrf	highByte,F
		rrf	lowByte,F
		btfsc	savedADCResult,2
		addwf	highByte,F
		rrf	highByte,F
		rrf	lowByte,F
		btfsc	savedADCResult,3
		addwf	highByte,F
		rrf	highByte,F
		rrf	lowByte,F
		btfsc	savedADCResult,4
		addwf	highByte,F
		rrf	highByte,F
		rrf	lowByte,F
		btfsc	savedADCResult,5
		addwf	highByte,F
		rrf	highByte,F
		rrf	lowByte,F
		btfsc	savedADCResult,6
		addwf	highByte,F
		rrf	highByte,F
		rrf	lowByte,F
		btfsc	savedADCResult,7
		addwf	highByte,F
		rrf	highByte,F
		rrf	lowByte,F

		movf	lowByte,W	; add 16 bit value to 24 bits
		addwf	INDF,F
		incf	FSR,F
		btfss	STATUS,C
		goto	noCarry
		incf	INDF,F
		btfss	STATUS,Z
		goto	noCarry
		incf	FSR,F
		incf	INDF,F
		decf	FSR,F
noCarry		movf	highByte,W
		addwf	INDF,F
		incf	FSR,F
		btfsc	STATUS,C
		incf	INDF,F
		return


handleTimer	bcf	PIR1,TMR2IF
		incf	time,F
		btfsc	time,0
		bsf	ADCON0,GO
		btfsc	time,0
		return			; bit 0 is set, so time!=0, so we're done
		; update adc channel
		incf	adcChannel,F
		btfsc	adcChannel,2
		clrf	adcChannel
		swapf	adcChannel,W
		movwf	tempChannel
		bcf	STATUS,C
		rrf	tempChannel,W
		iorlw	h'81'		; fosc/32 mode, not currently converting, operating
		movwf	ADCON0

		movf	adcChannel,F
		btfss	STATUS,Z
		goto	doneWithADC
		incf	sampleCounter,F
		btfss	STATUS,Z
		goto	doneWithADC
		; copy values into final locations, zero sums
		movf	sum0LSB,W	; pressure
		movwf	pressureLSB
		clrf	sum0LSB
		movf	sum0MSB,W
		movwf	pressureMSB
		clrf	sum0MSB
		movf	sum1MSB,W	; current motor 1 offset
		movwf	offset1
		clrf	sum1MSB
		clrf	sum1LSB
		movf	sum2MSB,W	; current motor 2 offset
		movwf	offset2
		clrf	sum2MSB
		clrf	sum2LSB
		movf	sum3MSB,W	; current motor 3 offset
		movwf	offset3
		clrf	sum3MSB
		clrf	sum3LSB
		movf	ssum1LSB,W	; motor 1 current
		movwf	current1LSB
		clrf	ssum1LSB
		movf	ssum1,W
		movwf	current1
		clrf	ssum1
		movf	ssum1MSB,W
		movwf	current1MSB
		clrf	ssum1MSB
		movf	ssum2LSB,W	; motor 2 current
		movwf	current2LSB
		clrf	ssum2LSB
		movf	ssum2,W
		movwf	current2
		clrf	ssum2
		movf	ssum2MSB,W
		movwf	current2MSB
		clrf	ssum2MSB
		movf	ssum3LSB,W	; motor 3 current
		movwf	current3LSB
		clrf	ssum3LSB
		movf	ssum3,W
		movwf	current3
		clrf	ssum3
		movf	ssum3MSB,W
		movwf	current3MSB
		clrf	ssum3MSB
		bsf	newSampleValues,0

doneWithADC	movf	time,F
		btfss	STATUS,Z
		return
		; do time==0 stuff
		movf	button1Timer,F		; check button 1
		btfss	STATUS,Z
		goto	timer1NotZero
		movf	buttonsStoredState,W
		xorwf	PORTB,W
		andlw	h'40'
		btfsc	STATUS,Z
		goto	button1Done
		movlw	h'40'
		xorwf	buttonsStoredState,F
		movlw	d'11'
		movwf	button1Timer
		btfss	buttonsStoredState,6
		bsf	button1Down,0
timer1NotZero	decf	button1Timer,F
button1Done	movf	button2Timer,F		; now button 2
		btfss	STATUS,Z
		goto	timer2NotZero
		movf	buttonsStoredState,W
		xorwf	PORTB,W
		andlw	h'80'
		btfsc	STATUS,Z
		return			; all done!
		movlw	h'80'
		xorwf	buttonsStoredState,F
		movlw	d'11'
		movwf	button2Timer
		btfss	buttonsStoredState,7
		bsf	button2Down,0
timer2NotZero	decf	button2Timer,F
		return


; Initialize processor registers
start

; As a general design guideline, we'll be leaving the processor in register
; bank 0.  When we need to access bank 1, that piece of code will switch
; to bank 1 and back to 0 when done.
; The LCD data port will be left as outputs.

	; bank 0 init
		clrf	STATUS		; Do initialization, Select bank 0
		clrf	INTCON		; Clear int-flags, Disable interrupts
		clrf	PCLATH		; Keep in lower 2KByte

		clrf	ADCON0

		clrf	PORTA
		clrf	PORTB
		clrf	PORTC
		clrf	PORTD
		clrf	PORTE

		movlw	h'20'		; zero all bank 0 general purpose memory
		movwf	FSR
initLoop1	clrf	INDF
		incf	FSR,F
		btfss	FSR,7
		goto	initLoop1

		movlw	h'30'		; start time non-zero so button checking
		movwf	time		; task won't coincide with grabbing values
		movlw	h'80'		; start with reasonable starting values for offsets
		movwf	offset1
		movwf	offset2
		movwf	offset3

	; bank 1 init
		bsf	STATUS, RP0	; Select bank 1

		clrf	PIE1 & h'7f'
		clrf	PIE2 & h'7f'

		movlw	h'3f'		; all inputs
		movwf	TRISA&0x7f
		movlw	h'fc'		; RB0-1 outputs, all others inputs
		movwf	TRISB&0x7f
		movlw	h'90'		; RC0-3,5,6 outputs, all others inputs
		movwf	TRISC&0x7f
		movlw	h'00'		; RD0-7 outputs
		movwf	TRISD&0x7f
		movlw	h'00'		; RE0-2 outputs
		movwf	TRISE&0x7f

		movlw	h'02'
		movwf	ADCON1&0x7f	; RA0,1,2,5,3 analog inputs, vref==vdd
		bsf	OPTION_REG&0x7f, NOT_RBPU
					; Disable PORTB pull-ups


					; we won't bother clearing bank 1; we
					; don't have anything there yet that
					; cares



		bcf	STATUS, RP0	; Select bank 0

		call	lcdInit

		bsf	STATUS, RP0	; Select bank 1
		movlw	h'ff'
		movwf	PR2 & h'7f'	; maximum period on timer2/ccp1
		bcf	STATUS, RP0	; Select bank 0
		clrf	TMR2		; start at known state
		movlw	h'04'
		movwf	T2CON		; prescaler=postscaler=1
		clrf	CCPR1L		; minimum duty cycle
		movlw	h'0c'
		movwf	CCP1CON		; setup ccp1 as pwm
		bsf	INTCON,PEIE
		bsf	STATUS, RP0	; Select bank 1
		bsf	PIE1&h'7f',TMR2IE
		bsf	PIE1&h'7f',ADIE
		bcf	STATUS, RP0	; Select bank 0

		bcf	PIR1,ADIF
		bcf	PIR1,TMR2IF	; clear pending interrupts
		bsf	INTCON,GIE

		call	lcdClear	; put status frame up
		movlw	lcdLine0
		call	lcdSDDA
		call	waitMessage

;debugLoop
;		movlw	h'ff'
;		call	xDelay500	; 128ms
;		movlw	h'ff'
;		call	xDelay500	; 128ms
;		movlw	h'ff'
;		call	xDelay500	; 128ms
;		movlw	h'ff'
;		call	xDelay500	; 128ms
;		movlw	lcdLine1
;		call	lcdSDDA
;		movf	time,W
;		call	displayHex
;		movlw	lcdLine1+d'3'
;		call	lcdSDDA
;		movf	TMR2,W
;		call	displayHex
;		movlw	lcdLine1+d'6'
;		call	lcdSDDA
;		movf	T2CON,W
;		call	displayHex
;		movlw	lcdLine1+d'9'
;		call	lcdSDDA
;		bsf	STATUS,RP0
;		movf	PR2&h'7f',W
;		bcf	STATUS,RP0
;		call	displayHex
;		movlw	lcdLine1+d'12'
;		call	lcdSDDA
;		bsf	STATUS,RP0
;		movf	PIE2&h'7f',W
;		bcf	STATUS,RP0
;		call	displayHex
;		movlw	lcdLine1+d'15'
;		call	lcdSDDA
;		movf	INTCON,W
;		call	displayHex
;		movlw	lcdLine1+d'18'
;		call	lcdSDDA
;		bsf	STATUS,RP0
;		movf	PIE1&h'7f',W
;		bcf	STATUS,RP0
;		call	displayHex
;		goto	debugLoop

waitLoop1	btfss	newSampleValues,0
		goto	waitLoop1	; wait for one set of values to be valid
		bcf	newSampleValues,0
waitLoop2	btfss	newSampleValues,0
		goto	waitLoop2	; and a second one, just to be sure things are stable
		bcf	newSampleValues,0

		call	lcdClear	; put status frame up
		movlw	lcdLine0
		call	lcdSDDA
		call	pumpStatusMessage
		movlw	lcdLine1
		call	lcdSDDA
		call	pressureStatusMessage

mainLoop	
		; handle tone generation first, as it's the only really
		; CPU time intensive part of the main code



		btfss	newSampleValues,0
		goto	mainLoop
		; everything from here on we only run at 7.6Hz
		bcf	newSampleValues,0


                movf    pumpOffTimer,F
                btfss   STATUS,Z
                decf    pumpOffTimer,F
                movf    pumpOnTimer,F
                btfss   STATUS,Z
                decf    pumpOnTimer,F
                movf    switchTimerLSB,F
                btfss   STATUS,Z
                goto    switchNotZero
                movf    switchTimerMSB,F
                btfss   STATUS,Z
                goto    switchNotZero
                goto    doneWithSwitchTimer
switchNotZero   movf    switchTimerLSB,F
                btfsc   STATUS,Z
                decf    switchTimerMSB,F
                decf    switchTimerLSB,F
doneWithSwitchTimer
                btfsc   levelSwitch2
                bcf     wantWells,0
                btfsc   wantWells,0
                goto    leaveWellsAlone
                movf    pumpOnTimer,F
                btfss   STATUS,Z
                goto    leaveWellsAlone
                btfss   wellPump1
                goto    leaveWell1Alone
                bcf     wellPump1
                movlw   h'6'
                movwf   pumpOffTimer
leaveWell1Alone btfss   wellPump2
                goto    leaveWellsAlone
                bcf     wellPump2
                movlw   h'6'
                movwf   pumpOffTimer
leaveWellsAlone
                btfsc   levelSwitch2
                goto    bypassSwitch3
                btfss   levelSwitch3
                bsf     wantWells,0
bypassSwitch3
                btfss   wantWells,0
                goto    dontTurnWellsOn
                btfsc   wantPressure,0
                goto    dontTurnWellsOn
                btfsc   pressurePump
                goto    dontTurnWellsOn
                movf    pumpOffTimer,F
                btfss   STATUS,Z
                goto    dontTurnWellsOn
                btfsc   wellPump1
                goto    notBothOff
                btfsc   wellPump2
                goto    notBothOff
                movlw   h'28'
                movwf   pumpOnTimer
                movlw   (d'6840'>>8) & h'ff'
                movwf   switchTimerMSB
                movlw   d'6840' & h'ff'
                movwf   switchTimerLSB
                btfsc   lastWell,0
                goto    lastWellWas2
                bsf     wellPump2
                movlw   h'28'
                movwf   pumpOnTimer
                bsf     lastWell,0
                goto    notBothOff
lastWellWas2    bsf     wellPump1
                movlw   h'28'
                movwf   pumpOnTimer
                bcf     lastWell,0
notBothOff      movf    switchTimerLSB,F
                btfss   STATUS,Z
                goto    dontTurnWellsOn
                movf    switchTimerMSB,F
                btfss   STATUS,Z
                goto    dontTurnWellsOn
                bcf     wellPump1
                bcf     wellPump2
                movlw   h'6'
                movwf   pumpOffTimer
dontTurnWellsOn
                btfss   wantPressure,0
                goto    dontNeedPressure
                btfsc   pressurePump
                goto    dontNeedPressure
                btfss   wellPump1
                btfsc   wellPump2
                goto    onePumpOn
                movf    pumpOffTimer,F
                btfss   STATUS,Z
                goto    dontNeedPressure
                bsf     pressurePump

                movlw   h'28'
                movwf   pumpOnTimer
                goto    dontNeedPressure
onePumpOn       movf    pumpOnTimer,F
                btfss   STATUS,Z
                goto    dontNeedPressure
                movlw   h'6'
                movwf   pumpOffTimer
                bcf     wellPump1
                bcf     wellPump2
dontNeedPressure
; low setpoint 28723
; high setpoint 39168
                btfss   levelSwitch4
                goto    disablePressure
                movlw   (-d'28723'>>8) & h'ff'
                addwf   pressureMSB,W
                btfsc   STATUS,Z
                goto    checkLowPointLSB
                btfss   STATUS,C
                goto    enablePressure
                goto    checkHighPoint
checkLowPointLSB
                ; check lsb
                movlw   -d'28723' & h'ff'
                addwf   pressureLSB,W
                btfsc   STATUS,C
                goto    checkHighPoint
enablePressure  bsf     wantPressure,0
                goto    pressureDone
checkHighPoint
                movlw   (-d'39168'>>8) & h'ff'
                addwf   pressureMSB,W
                btfsc   STATUS,Z
                goto    checkHighPointLSB
                btfsc   STATUS,C
                goto    disablePressure
                goto    pressureDone
checkHighPointLSB
                movlw   -d'39168' & h'ff'
                addwf   pressureMSB,W
                btfss   STATUS,C
                goto    pressureDone
disablePressure bcf     wantPressure,0
pressureDone    btfsc   wantPressure,0
                goto    pressureReallyDone
                btfss   pressurePump
                goto    pressureReallyDone
                movf    pumpOnTimer,F
                btfss   STATUS,Z
                goto    pressureReallyDone
                bcf     pressurePump

                movlw   h'6'
                movwf   pumpOffTimer

pressureReallyDone
		; only updating status display is left here
		incf	displayTimer,F
		movlw	h'03'
		andwf	displayTimer,W
		btfss	STATUS,Z
		goto	mainLoop

		; display pump status messages
		movlw	lcdLine0+d'3'
		call	lcdSDDA
		btfsc	wellPump1
		goto	pump1Running
		call	pumpOffMessage
		goto	checkPump2
pump1Running	call	pumpRunMessage
checkPump2
		movlw	lcdLine0+d'10'
		call	lcdSDDA
		btfsc	wellPump2
		goto	pump2Running
		call	pumpOffMessage
		goto	checkPump3
pump2Running	call	pumpRunMessage
checkPump3
		movlw	lcdLine0+d'17'
		call	lcdSDDA
		btfsc	pressurePump
		goto	pump3Running
; check for dry
                btfsc   levelSwitch4
                goto    pressurePumpWet
                call    pumpDryMessage
                goto    checkPressure
pressurePumpWet
		call	pumpOffMessage
		goto	checkPressure
pump3Running	call	pumpRunMessage
checkPressure
                movlw   lcdLine1        ; display switch status
                call    lcdSDDA
                movlw   h'ff'
                btfss   levelSwitch4
                movlw   ' '
                call    lcdPutChar
                movlw   h'ff'
                btfss   levelSwitch3
                movlw   ' '
                call    lcdPutChar
                movlw   h'ff'
                btfss   levelSwitch2
                movlw   ' '
                call    lcdPutChar
                movlw   h'ff'
                btfss   levelSwitch1
                movlw   ' '
                call    lcdPutChar

		movlw	lcdLine1+d'12'
		call	lcdSDDA
		movf	pressureLSB,W
		movwf	lowTemp
		movf	pressureMSB,W
		movwf	highTemp

		movlw	-d'13107' & h'ff'		; take care of 1V offset
		addwf	lowTemp,F
		btfsc	STATUS,C
		incf	highTemp,F
		movlw	(-d'13107'>>8) & h'ff'
		addwf	highTemp,F
		btfsc	STATUS,C
		goto	noBorrow
		clrf	lowTemp
		clrf	highTemp
noBorrow	clrf	digit
tensLoop	movf	lowTemp,W
		movwf	lowTemp2
		movf	highTemp,W
		movwf	highTemp2
		movlw	-d'5243' & h'ff'
		addwf	lowTemp,F
		btfsc	STATUS,C
		incf	highTemp,F
		movlw	(-d'5243'>>8) & h'ff'
		addwf	highTemp,F
		btfss	STATUS,C
		goto	tensDone
		incf	digit,F
		goto	tensLoop
tensDone	movf	lowTemp2,W	; get value back before underflow
		movwf	lowTemp
		movf	highTemp2,W
		movwf	highTemp
		movf	digit,W
		addlw	'0'
		movf	digit,F
		btfsc	STATUS,Z
		movlw	' '
		call	lcdPutChar
		clrf	digit
onesLoop	movf	lowTemp,W
		movwf	lowTemp2
		movf	highTemp,W
		movwf	highTemp2
		movlw	-d'525' & h'ff'
		addwf	lowTemp,F
		btfsc	STATUS,C
		incf	highTemp,F
		movlw	(-d'525'>>8) & h'ff'
		addwf	highTemp,F
		btfss	STATUS,C
		goto	onesDone
		incf	digit,F
		goto	onesLoop
onesDone	movf	lowTemp2,W	; get value back before underflow
		movwf	lowTemp
		movf	highTemp2,W
		movwf	highTemp
		movf	digit,W
		addlw	'0'
		call	lcdPutChar
		movlw	'.'
		call	lcdPutChar
		clrf	digit
fracLoop	movlw	-d'53' & h'ff'
		addwf	lowTemp,F
		btfsc	STATUS,C
		incf	highTemp,F
		movlw	(-d'53'>>8) & h'ff'
		addwf	highTemp,F
		btfss	STATUS,C
		goto	fracDone
		incf	digit,F
		goto	fracLoop
fracDone	movf	digit,W
		addlw	'0'
		call	lcdPutChar

		goto	mainLoop	; Stay in this loop forever



pressureStatusMessage
		movlw	high msg2
		movwf	PCLATH
		movlw	0
dispMsg2	movwf	tableIndex
		call	msg2
		andlw	h'ff'
		btfsc	STATUS,Z
		goto	pressureMessageEnd
		call	lcdPutChar
		movf	tableIndex,W
		addlw	1
		goto	dispMsg2
pressureMessageEnd
		return

waitMessage
		movlw	high msg4
		movwf	PCLATH
		movlw	0
dispMsg4	movwf	tableIndex
		call	msg4
		andlw	h'ff'
		btfsc	STATUS,Z
		goto	waitMessageEnd
		call	lcdPutChar
		movf	tableIndex,W
		addlw	1
		goto	dispMsg4
waitMessageEnd
		return

pumpStatusMessage
		movlw	high msg3
		movwf	PCLATH
		movlw	0
dispMsg3	movwf	tableIndex
		call	msg3
		andlw	h'ff'
		btfsc	STATUS,Z
		goto	pressureMessageEnd
		call	lcdPutChar
		movf	tableIndex,W
		addlw	1
		goto	dispMsg3
pumpStatusMessageEnd
		return

displayHex	movwf	digit
		swapf	digit,W
		andlw	h'0f'
		call	displayNibble
		movf	digit,W
		andlw	h'0f'
displayNibble	addlw	h'f6'
		btfsc	STATUS,C
		addlw	'a'-'0'-d'10'
		addlw	'0'-h'f6'
		call	lcdPutChar
		return
		
pumpOffMessage
		movlw	'O'
		call	lcdPutChar
		movlw	'f'
		call	lcdPutChar
		movlw	'f'
		call	lcdPutChar
		return

pumpRunMessage
		movlw	'R'
		call	lcdPutChar
		movlw	'u'
		call	lcdPutChar
		movlw	'n'
		call	lcdPutChar
		return

pumpDryMessage
		movlw	'D'
		call	lcdPutChar
		movlw	'r'
		call	lcdPutChar
		movlw	'y'
		call	lcdPutChar
		return

; lcdInit
lcdInit
					; Busy-flag is not yet valid
		bcf	lcdRW
		bcf	lcdRS
		bcf	lcdE
		movlw	h'1e'
		call	xDelay500	; 30 * 0.5mS = 15mS
					; Busy Flag should be valid from here
		movlw	h'38'		; 8-bit-interface, 2-lines
		call	lcdPutCmd
		movlw	h'00'		; disp.off, curs.off, no-blink
		call	lcdDMode
		call	lcdClear
		movlw	h'04'		; disp.on, curs.off
		call	lcdDMode
		movlw	h'02'		; auto-inc (shift-cursor)
		call	lcdEMode
		return

; lcdEnable
; Pulses LCD enable pin
lcdEnable
		bsf	lcdE		; LCD E-line High
		bcf	lcdE		; LCD E-line Low
		return

; lcdBusy
; Returns when LCD busy-flag is inactive
lcdBusy
		bsf	STATUS,RP0	; Select Register page 1
		movlw	h'ff'		; Set LCD data port for input
		movwf	lcdDataTris&0x7f
		bcf	STATUS, RP0	; Select Register page 0
		bcf	lcdRS		; Set LCD for command mode
		bsf	lcdRW		; Setup to read busy flag
		bsf	lcdE		; LCD E-line High
		movf	lcdData, W	; Read busy flag + DDram address
		bcf	lcdE		; LCD E-line Low
		andlw	h'80'		; Check Busy flag, High = Busy
		btfss	STATUS, Z
		goto	lcdBusy
lcdNotBusy	bcf	lcdRW
		bsf	STATUS, RP0	; Select Register page 1
		movlw	h'00'
		movwf	lcdDataTris&0x7f; Set PORTB for output
		bcf	STATUS, RP0	; Select Register page 0
		return

; lcdClear
; Clears display and returns cursor to home position (upper-left corner).
lcdClear
		movlw	h'01'
		call	lcdPutCmd
		return

; lcdHome
; Returns cursor to home position.
; Returns display to original position (when shifted).
lcdHome
		movlw	h'02'
		call	lcdPutCmd
		return

; lcdEMode
; Sets entry mode of display.
; Required entry mode must be set in W
;  b0	: 0 = no display shift	1 = display shift
;  b1	: 0 = auto-decrement	1 = auto-increment
;  b2-7	: don't care
lcdEMode
		andlw	h'03'		; Strip upper bits
		iorlw	h'04'		; Function set
		call	lcdPutCmd
		return

; lcdDMode
; Sets display control.
; Required display mode must be set in W
;  b0	: 0 = cursor blink off	1 = cursor blink on
;  b1	: 0 = cursor off	1 = cursor on
;  b2	: 0 = display off	1 = display on (display data remains in DDRAM)
;  b3-7	: don't care
lcdDMode
		andlw	h'07'		; Strip upper bits
		iorlw	h'08'		; Function set
		call	lcdPutCmd
		return

; lcdSCGA
; Sets Character-Generator-RAM address. CGRAM is read/written after
;  this setting.
; Required CGRAM address must be set in W
;  b0-5	: required CGRAM address
;  b6-7	: don't care
lcdSCGA
		andlw	h'3f'		; Strip upper bits
		iorlw	h'40'		; Function set
		call	lcdPutCmd
		return

; lcdSDDA
; Sets the Display-Data-RAM address. DDRAM data is read/written after
;  this setting.
; Required DDRAM address must be set in W
;  b0-6	: required DDRAM address
;  b7	: don't care
lcdSDDA
		iorlw	h'80'		; Function set
		call	lcdPutCmd
		return

; lcdGAddr
; Returns address counter contents, used for both DDRAM and CGRAM.
; RAM address is returned in W
lcdGAddr
		bsf	STATUS,RP0	; Select Register page 1
		movlw	h'ff'		; Set LCD data port for input
		movwf	lcdDataTris&0x7f
		bcf	STATUS, RP0	; Select Register page 0
		bcf	lcdRS		; Set LCD for command mode
		bsf	lcdRW		; Setup to read busy flag
		bsf	lcdE		; LCD E-line High
		movf	lcdData, W	; Read busy flag + RAM address
		bcf	lcdE		; LCD E-line Low
		andlw	h'7f'		; Strip upper bit
		bcf	lcdRW
		bsf	STATUS, RP0	; Select Register page 1
		movlw	h'00'
		movwf	lcdDataTris&0x7f; Set PORTB for output
		bcf	STATUS, RP0	; Select Register page 0
		return

; lcdPutChar
; Sends character to LCD
; Required character must be in W
lcdPutChar
		movwf	lcdTemp		; Character to be sent is in W
		call	lcdBusy		; Wait for LCD to be ready
		bcf	lcdRW		; Set LCD in read mode
		bsf	lcdRS		; Set LCD in data mode
		bsf	lcdE		; LCD E-line High
		movf	lcdTemp, W
		movwf	lcdData		; Send data to LCD
		bcf	lcdE		; LCD E-line Low
		return

; lcdPutCmd
; Sends command to LCD
; Required command must be in W
lcdPutCmd
		movwf	lcdTemp		; Command to be sent is in W
		call	lcdBusy		; Wait for LCD to be ready
		bcf	lcdRW		; Set LCD in read mode
		bcf	lcdRS		; Set LCD in command mode
		bsf	lcdE		; LCD E-line High
		movf	lcdTemp, W
		movwf	lcdData		; Send data to LCD
		bcf	lcdE		; LCD E-line Low
		return


delay500	movlw	d'165'		; +1		1 cycle
		movwf	delay		; +2		1 cycle
delay500Loop	decfsz	delay, F	; step 1	1 cycle
		goto	delay500Loop	; step 2	2 cycles

		movlw	d'165'		; +1		1 cycle
		movwf	delay		; +2		1 cycle
delay500Loop2	decfsz	delay, F	; step 1	1 cycle
		goto	delay500Loop2	; step 2	2 cycles

		movlw	d'165'		; +1		1 cycle
		movwf	delay		; +2		1 cycle
delay500Loop3	decfsz	delay, F	; step 1	1 cycle
		goto	delay500Loop3	; step 2	2 cycles

		movlw	d'165'		; +1		1 cycle
		movwf	delay		; +2		1 cycle
delay500Loop4	decfsz	delay, F	; step 1	1 cycle
		goto	delay500Loop4	; step 2	2 cycles

delay500End	return			; +3		2 cycles


xDelay500	movwf	xDelay		; +1		1 cycle
xDelay500Loop	call	delay500	; step1		wait 500uSec
		decfsz	xDelay, F	; step2		1 cycle
		goto	xDelay500Loop	; step3		2 cycles
xDelay500End	return			; +2		2 cycles


msg2		addwf	PCL,F
		dt	"                 PSI", 0
msg2End		equ	$-1

	if ( (msg2 & h'ff') >= (msg2End & h'ff') )
		messg   "Warning: Table 'msg2' crosses page boundary in computed jump"
	endif


    org ($+h'ff') & h'3f00'   ; start at next page boundary

msg3		addwf	PCL,F
		dt	"W1:    W2:    Pr:", 0
msg3End		equ	$-1

	if ( (msg3 & h'ff') >= (msg3End & h'ff') )
		messg   "Warning: Table 'msg3' crosses page boundary in computed jump"
	endif

msg4		addwf	PCL,F
		dt	"Wait...", 0
msg4End		equ	$-1

	if ( (msg4 & h'ff') >= (msg4End & h'ff') )
		messg   "Warning: Table 'msg4' crosses page boundary in computed jump"
	endif


	end
