;********************************************************************
;                       W A T T M E T E R
;********************************************************************
; Bruce Abbott                               bhabbott@paradise.net.nz
;
; - Measures up to 25 Volts and 50 Amps
; - Displayed on 16x1 LCD
; - Calculates Watts and Amp hours
; - Hold function.
;

;	processor  PIC16f870
;	processor  PIC16F88

	ifdef	__16F870
	include	P16f870.inc
#DEFINE TMR0IF	T0IF
	else
	include	P16F88.inc
	endif

        radix dec		; default number format is DECIMAL!!!

	errorlevel -302,-305	; not reporting bank<>0, dest=file

#DEFINE FALSE 0
#DEFINE movfw ERROR movwf? ; away with thee, abomination!

	ifdef	__16F870
 __config _BODEN_OFF&_WDT_OFF&_LVP_OFF&_PWRTE_ON&_HS_OSC
	else   ; 16F88
 __config _CONFIG1,_BODEN_OFF&_WDT_OFF&_LVP_OFF&_MCLR_OFF&_PWRTE_ON&_INTRC_IO
	endif

#DEFINE	REFRESH_DELAY 39

; I/O pin assignments

	ifdef	__16F870
#DEFINE	PGBTN	PORTC,0	; page button (PORTC)
#DEFINE	HDBTN	PORTC,1	; hold button (PORTC)
#DEFINE LCD_D7  PORTB,0
#DEFINE	LCD_D6  PORTB,1
#DEFINE LCD_D5  PORTB,2	; LCD panel (PortB)
#DEFINE LCD_D4  PORTB,3
#DEFINE LCD_E   PORTB,4
#DEFINE LCD_RS  PORTB,5
	else    ; 16f88
#DEFINE	PGBTN	PORTB,6	; page button (PORTB)
#DEFINE	HDBTN	PORTB,7	; hold button (PORTB)
#DEFINE LCD_D7  PORTB,0
#DEFINE	LCD_D6  PORTB,1
#DEFINE LCD_D5  PORTB,2	; LCD panel (PortB)
#DEFINE LCD_D4  PORTB,3
#DEFINE LCD_RS  PORTB,5
#DEFINE LCD_E   PORTB,4
	endif

;====================================================================
;             Macros to create variables in RAM
;

ByteAddr	SET 32     ; user RAM starts here

BYTE		MACRO	ByteName
ByteName	EQU	ByteAddr
ByteAddr	SET	ByteAddr+1
		ENDM

WORD		MACRO	WordName
WordName	EQU	ByteAddr
ByteAddr	SET	ByteAddr+2
		ENDM

LONG		MACRO	LongName
LongName	EQU	ByteAddr
ByteAddr	SET	ByteAddr+4
		ENDM


;====================================================================
;               16 bit macros (Big-Endian)
;
mov16	MACRO	src,dst
	movf	src,w
	movwf	dst
	movf	src+1,w
	movwf	dst+1
	ENDM

movi16	MACRO	dst,lit
	movlw	HIGH(lit)
	movwf	dst
	movlw	LOW(lit)
	movwf	dst+1
	ENDM

clr16	MACRO	dst
	clrf	dst
	clrf	dst+1
	ENDM

inc16	MACRO	dst
	incf	dst+1
	skpnz
	incf	dst
	ENDM

dec16	MACRO	dst
	decf	dst+1
	skpnz
	decf	dst
	ENDM

add16	MACRO	src,dst
	movf	src+1,w
	addwf	dst+1
	skpnc
	incf	dst
	movf	src,w
	addwf	dst
	ENDM

addi16	MACRO	dst,lit
	movf	LOW(lit),w
	addwf	dst+1
	skpnc
	incf	dst
	movf	HIGH(lit),w
	addwf	dst
	ENDM

sub16	MACRO   src,dst
	movf    src+1,w
	subwf   dst+1
	movf    src,w
	skpc
	incf    src,w
	subwf   dst
	ENDM

subi16	MACRO   dst,lit
	movlw   LOW(lit)
	subwf   dst+1
	movlw   HIGH(lit)
	skpc
	movlw   (HIGH(lit))+1
	subwf   dst
	ENDM

lsr16	MACRO	dst
	rrf	aa
	rrf	aa+1
	ENDM

lsl16	MACRO	dst
	rlf	aa+1
	rlf	aa
	ENDM

com16	MACRO	dst
	comf	dst+1
	comf	dst
	ENDM

;---------------------------------------------------
;               32 bit macros

clr32	MACRO	dst
	clrf	dst
	clrf	dst+1
	clrf	dst+2
	clrf	dst+3
	ENDM

mov32	MACRO	src,dst
	movf	src,w
	movwf	dst
	movf	src+1,w
	movwf	dst+1
	movf	src+2,w
	movwf	dst+2
	movf	src+3,w
	movwf	dst+3
	ENDM

; 32 bit left shift

rr32	MACRO	dst
	rrf	aa
	rrf	aa+1
	rrf	aa+2
	rrf	aa+3
	ENDM

; 32 bit right shift

rl32	MACRO	dst
	rlf	aa+3
	rlf	aa+2
	rlf	aa+1
	rlf	aa
	ENDM

com32	MACRO	dst
	comf	dst+3
	comf	dst+2
	comf	dst+1
	comf	dst
	ENDM

; add 16 bit to 32 bit

add32	MACRO	src,dst ; src(15:0), dst(31:0)
	movf	src+1,w
	addwf	dst+3	; dst = dst + src(7:0)
	skpc
	goto	$+6
	incf	dst+2
	skpnz
	incf	dst+1
	skpnz
	incf	dst
	movf	src,w
	addwf	dst+2	; dst = dst + src (15:8)
	skpc
	goto	$+4
	incf	dst+1
	skpnz
	incf	dst
	ENDM

;====================================================================


; Five digit decimal number

	BYTE	TenT
	BYTE	Thou
	BYTE	Hund
	BYTE	Tens
	BYTE	Ones
	BYTE	dp	 ; position of decimal point

; 16 bit binary number

	WORD	Number

; 16 bit register 'a'

	WORD	aa

; 16 bit register 'b'

	WORD    bb

; 16 bit register 'c'

	WORD	cc

; 32 bit register 'dd'

	LONG	dd

; Volts
	WORD    Volts	 ; Volts*100 (0.01V resolution)

; Amps
	WORD    Amps	 ; Amps*100 (0.01A resolution)

; Watts
	WORD    Watts	 ; Watts*10 (0.1W resolution)

; Amps Accumulated
	LONG	AmpSum   ; addition of all Amp readings

; AmpereHours
	WORD	AmpHours ; Amps * Hours

; rpm period

	WORD	Period	 ; time between blade detections

; rpm
	WORD	rpm	 ; revolutions per minute

; CCP1 overflows

	BYTE	Overflows

; Zero Amps

	WORD    ZeroAmps

; Number of Readings to Average

	BYTE	Readings

; pointer to character in message string

	BYTE	msg_pointer

; display paging

	BYTE	pagenum

; display refresh timer

	BYTE	ref_timer

; misc variables

	BYTE	Temp
	BYTE	Temp1
	BYTE	Temp2

; boolean flags

	BYTE	Flags

#DEFINE GOT_ZERO	0	; Amps zero'd
#DEFINE	NEG_AMPS	1	; Amps < zero
#DEFINE	MAX_VOLTS	2	; Volts >= max
#DEFINE MAX_AMPS	3	; Amps >= max
#DEFINE NEW_PAGE	4	; Page button pressed
#DEFINE	LEAD_ZERO	5	; leading zeros blanked


; reset vector
;////////////////////////////////////////////////////////////////////
	ORG	0
	goto 	Start

; interrupt vector
;////////////////////////////////////////////////////////////////////
	ORG	4
	goto	Interrupt
;////////////////////////////////////////////////////////////////////


	ORG	8

	dt	"WOTMTR"
	ifdef	__16F870
	dt	"70"
	else
	dt	"88"
	endif
	dt	"--V0.8--"

;--------------------------------------------------------------------
;                   get message characters
;
; Input:  W = address of character in message
; Output: W = character
;
; NOTE: Table read:- Must NOT traverse a program page boundary!
;

get_message:
        movwf   PCL
signon_msg:
	RETLW   'W'
        RETLW   'O'
        RETLW   'T'
        RETLW   ' '
        RETLW   'M'
        RETLW   'e'
        RETLW   't'
	RETLW	'e'
	RETLW	'r'
	RETLW	' '
	RETLW	'V'
	RETLW	'0'
	RETLW	'.'
        RETLW   '0'
        RETLW   '8'
        RETLW   ' '
        RETLW   0

;--------------------------------------------------------------------
;                     Interrupt Handler
;
WorkSave = 0x7f
StatSave = 0x7e

Interrupt:
	movwf	WorkSave
	swapf	STATUS,w
	movwf	StatSave
	clrf	STATUS

	btfss	PIR1,CCP1IF
	goto	TMR1_Int
CCP1_Int:
	bcf	PIR1,CCP1IF
	movf	CCPR1L,w
	addwf	Period+1
	skpnc
	incf	Period
	movf	CCPR1H,w
	addwf	Period
	skpnc
	incf	Period
	clrf	Overflows
TMR1_Int:
	btfss	PIR1,TMR1IF
	goto	Int_done

	clrf	Period
	clrf	Period+1
Int_done:
	swapf 	StatSave,W
	movwf	STATUS
	swapf	WorkSave,F
	swapf	WorkSave,w
	RETFIE

;--------------------------------------------------------------------
;                 LCD Display Driver

	include lcd.asm

;--------------------------------------------------------------------
;            Display Sign-on Message on LCD
;
show_signon:
        movlw   0
        call    Set_Cursor
	movlw   signon_msg
;
;           Display Text Message on LCD
;
show_message:
        movwf   msg_pointer
        call    get_message     ; get next char in message
        andlw   0xFF		; end of message ?
        skpnz
        return
        call    Print_Char	; show character
 	movlw	LCDLENGTH
	subwf	lcd_address,w	; if end of 1st line
	skpz			; then skip to 2nd line
	goto	show_msg
	movlw	LCDLINE2
	call	Set_Cursor
show_msg:
        movf    msg_pointer,W
        addlw   1               ; next character
        goto    show_message


;--------------------------------------------------------------------
;          16x16 bit Multiply (32 bit result)
;
; dd = aa * bb
;
; Input:  multiplier = aa(15:0), multiplicand = bb(15:0)
;
; Output: result = dd(32:0)
;
; Uses: aa, w
;
Mult16:	clrf    dd
	clrf	dd+1
	clrf    dd+2
	movlw	0x80
	movwf	dd+3

m16_next:
	rrf	aa,f
	rrf	aa+1,f

	skpc
	goto	m16_nobit_l
	movf	bb+1,w
	addwf	dd+2,f

	movf	bb, w
	skpnc
	incfsz	bb, w
	addwf	dd+1, f
	skpnc
	incf	dd, f
	clrc

m16_nobit_l:
	btfss	aa+1, 7
	goto	m16_nobit_h
	movf	bb+1,w
	addwf	dd+1,f
	movf	bb, w
	skpnc
	incfsz	bb, w
	addwf	dd, f

m16_nobit_h:
	rrf	dd,f
	rrf	dd+1,f
	rrf	dd+2,f
	rrf	dd+3,f

	skpc
	goto	m16_next
	return


;--------------------------------------------------------------------
;                   32 x 16 bit integer divide
;
; dd = dd / aa
;
; Input:  dividend = dd(31:0), divisor = aa(15:0)
;
; Output: quotient = dd(31:0), remainder = bb(15:0)
;
; Uses: bb, Temp, w
;
Div32:	movlw	32		; 32 bits to divide
	movwf	Temp
	clrf	bb		; Clear remainder
	clrf	bb+1
div32_loop:
	clrc			; Set quotient bit to 0
	rlf	dd+3
	rlf	dd+2
	rlf	dd+1		; Shift dividend left into quotient
	rlf	dd
	rlf	bb+1
	rlf	bb
	skpnc			; Check for overflow
	goto	div32_sd
	movf	aa,w		; Compare partial remainder and divisor
	subwf	bb,w
	skpz 			; High Bytes equal?
	goto	div32_gt
	movf	aa+1,w 		; Yes, compare Low Bytes
	subwf	bb+1,w
div32_gt:
	skpc			; Carry set if remainder >= divisor
	goto	dvi32_lt
div32_sd:
	movf	aa+1,w		; Subtract divisor from partial remainder
	subwf	bb+1
	skpc			; Test for borrow

	decf	bb		; Subtract borrow
	movf	aa,w
	subwf	bb
	bsf	dd+3,0		; Set quotient bit (quotient replaces dividend)
dvi32_lt:
	decfsz	Temp		; next bit
	goto	div32_loop
	return

;--------------------------------------------------------------------
;            16 bit Integer Divide by 12.8
;
; Input: W->dst
;
; output: dst(15:0) = dst / 12.8
;
; uses: FSR, Temp, w
;
;
; ALGORITHM:
; Add dst / 4 to dst
; Add dst / 16 to dst
; Shift dst right (Bit0 to carry)
; If carry set then increment dst, ie. round up

Divx12:	movwf	FSR
	movf	INDF, w
	movwf	Temp
	incf	FSR
	movf	INDF, w
	decf	FSR

;shift dst right twice
	clrc
	rrf	INDF, f
	incf	FSR
	rrf	INDF, f
	decf	FSR
	clrc
	rrf	INDF, f
	incf	FSR
	rrf	INDF, f

;add dst to dst / 4
	addwf	INDF, f
	decf	FSR
	movf	Temp, w
	skpnc
	incfsz	Temp, w
	addwf	INDF, f

;shift dst right 3 times
	rrf	INDF, f
	incf	FSR
	rrf	INDF, f
	decf	FSR
	clrc
	rrf	INDF, f
	incf	FSR
	rrf	INDF, f
	decf	FSR
	clrc
	rrf	INDF, f
	incf	FSR
	rrf	INDF, f
	decf	FSR

;shift dst right once and increment if carry set
	clrc
	rrf	INDF, f
	incf	FSR
	rrf	INDF, f
	skpc
	goto	divx12_done
	incf	INDF, f
	skpz
	goto	divx12_done
	decf	FSR
	incf	INDF, f
divx12_done:
	return

;--------------------------------------------------------------------
;              16 bit Binary to Decimal
;
;  Input: Number(15:0)
;
; Output: TenT, Thou, Hund, Tens, Ones
;
; Uses: Number, w
;
;
bin2dec:
	clrf	TenT
        goto	$+2
sub10k: incf	TenT
        movlw	10000&255
        subwf	Number+1
        movlw	10000>>8
        skpc
        movlw	(10000>>8)+1
        subwf	Number
        skpnc
        goto	sub10k

        movlw	10
        movwf	Thou
add1k:  decf	Thou
        movlw	1000&255
        addwf	Number+1
        movlw	1000>>8
        skpnc
        movlw	(1000>>8)+1
        addwf	Number
        skpc
        goto	add1k

	clrf	Hund
	movlw	100
	goto	$+2
sub100:	incf	Hund
	subwf	Number+1
	skpnc
	goto	sub100
	decf	Number
	btfss	Number,7
	goto	sub100

        movlw	10
        movwf	Tens
add10:  decf	Tens
        addwf	Number+1
        skpc
        goto	add10
        movf	Number+1,w
        movwf	Ones
        return

;--------------------------------------------------------------------
;            Display 5 digit Fixed-point Decimal on LCD
;
; - leading zero blanked
; - 1 digit after decimal point
;
;       "9999.9"
;	" 999.9"
;       "  99.9"
;	"   0.0"
;
ShowDec1:
	bsf	Flags,LEAD_ZERO
        movf    TenT,w		; leading 0 ?
	skpz
	bcf	Flags,LEAD_ZERO
	btfss	Flags,LEAD_ZERO
	addlw	'0'-' '		; convert to ASCII '0'~'9'
	addlw	' '
        call    Print_Char	; show Ten Thousands or ' '
	movf	Thou,w
	skpz
	bcf	Flags,LEAD_ZERO
	btfss	Flags,LEAD_ZERO
	addlw	'0'-' '
	addlw	' '
        call    Print_Char	; show Thousands or ' '
	movf	Hund,w
	skpz
	bcf	Flags,LEAD_ZERO
	btfss	Flags,LEAD_ZERO
	addlw	'0'-' '
	addlw	' '
        call    Print_Char	; show Hundreds or ' '
        movf    Tens,w
	addlw	'0'
        call    Print_Char	; show Tens
	movlw	'.'
	call	Print_Char	; show '.'
        movf    Ones,w
	addlw	'0'
        call    Print_Char	; show Ones
        RETURN


;--------------------------------------------------------------------
;            Display 4 digit Fixed-point Decimal on LCD
;
; - leading zero blanked
; - 2 digits after decimal point
;
;       "99.99"
;	" 9.99"
;	" 0.00"
;
ShowDec2:
	movf	Thou,w
	skpz			; leading zero ?
	addlw	'0'-' '		; convert to ASCII '0'~'9'
	addlw	' '
        call    Print_Char	; show thousands or ' '
	movf	Hund,w
	addlw	'0'
        call    Print_Char	; show hundreds
	movlw	'.'
	call	Print_Char	; show '.'
        movf    Tens,w
	addlw	'0'
        call    Print_Char	; show tens
        movf    Ones,w
	addlw	'0'
        call    Print_Char	; show ones
        RETURN


;--------------------------------------------------------------------
;                Display 5 digit Decimal on LCD
;
; - leading zero blanked
;
;       "65535"
;       " 9999"
;	"   99"
;	"    0"
;
ShowDec:
	bsf	Flags,LEAD_ZERO
        movf    TenT,w
	skpz
	bcf	Flags,LEAD_ZERO
	btfss	Flags,LEAD_ZERO
	addlw	'0'-' '
	addlw	' '
        call    Print_Char	; show ten thousands or ' '
        movf    Thou,w
	skpz
	bcf	Flags,LEAD_ZERO
	btfss	Flags,LEAD_ZERO
	addlw	'0'-' '
	addlw	' '
        call    Print_Char	; show thousands or ' '
        movf    Hund,w
	skpz
	bcf	Flags,LEAD_ZERO
	btfss	Flags,LEAD_ZERO
	addlw	'0'-' '
	addlw	' '
        call    Print_Char	; show hundreds or ' '
        movf    Tens,w
	skpz
	bcf	Flags,LEAD_ZERO
	btfss	Flags,LEAD_ZERO
	addlw	'0'-' '
	addlw	' '
        call    Print_Char	; show tens or ' '
        movf    Ones,w
	addlw	'0'
        call    Print_Char	; show ones
        RETURN

;--------------------------------------------------------------------
;                      Wait W * 20mS
;
waitx20k:
        movwf   Temp
_waitx20k:
	movlw	80
	call	waitx250	; wait W*250uS
	decfsz	Temp,F
	goto	_waitx20k
	RETURN

;--------------------------------------------------------------------
;                  Display rpm on LCD
;
Show_rpm:
	movf	rpm,w
	movwf	Number
	movf	rpm+1,w
	movwf	Number+1
	movlw	' '
	call	Print_Char

	call	ShowDec		; display "xxxxx"

	movlw	'r'
	call	Print_Char
	movlw	'p'
	call	Print_Char
	movlw	'm'
	call	Print_Char
	movlw	' '
	call	Print_Char
	return

;--------------------------------------------------------------------
;                    Display Watts on LCD
;
Show_Watts:
	mov16	Watts,Number
	call	bin2dec
	call	ShowDec1	; display "xxxx.x"
	movlw	'W'
	call	Print_Char	; display 'W'
	movlw	' '
	call	Print_Char
	return

;--------------------------------------------------------------------
;                     Display Volts on LCD
;
Show_Volts:
	mov16	Volts,Number
	call	bin2dec
	movlw	0
	call	Set_Cursor
	call	Print_Char
	movlw	' '
	call	ShowDec2	; display "xx.xx"
	movlw	'V'
	call	Print_Char
	movlw	' '
	btfsc	Flags,MAX_AMPS
	movlw	'+'		; display '+' if overflow
	call	Print_Char
	return

;--------------------------------------------------------------------
;                    Display Amps on LCD
;
Show_Amps:
	mov16	Amps,Number
	movlw	' '
	btfsc	Flags,NEG_AMPS
	movlw	'-'		; display '-' if negative
	call	Print_Char
	call	bin2dec
	call	ShowDec2	; display "xx.xx"
	movlw	'A'
	call	Print_Char
	movlw	' '
	btfsc	Flags,MAX_AMPS
	movlw	'+'		; display '+' if overflow
	call	Print_Char
	return

;--------------------------------------------------------------------
;                  Display Amperes * Hours on LCD
;
Show_AmpHours:
	mov16	AmpHours,Number
	call	bin2dec
	movlw	' '
	call	Print_Char
	call	ShowDec2	; display "xx.xx"
	movlw	'A'
	call	Print_Char
	movlw	'h'
	call	Print_Char
	return

;--------------------------------------------------------------------
;                  Main Program Starts Here!
;
Start:
	clrf 	PORTA
	clrf	PORTB
	bsf	STATUS,RP0	; bank 1
	movlw	B'11111111'	; PortA directions
	movwf	TRISA
	movlw	B'00000000'	; PortB directions
	movwf	TRISB
	ifdef	__16F870
	movlw	B'11111111'	; PortC directions
	movwf	TRISC
	endif
	ifdef	__16F88
	movlw	B'01100000'	; 4MHz internal osc (16F88)
	movwf	OSCCON
	endif
	movlw   B'11010111'	; Timer0 source = clkout/256
	movwf	OPTION_REG
	bcf	STATUS,RP0	; bank 0

; Set up Interrupts

;	bsf	INTCON,PEIE	; enable peripheral ints
;	bsf	INTCON,GIE	; enable interrupts

; set up LCD Display

	movlw	25		; wait 500mS
	call	waitx20k

	call	LCDinit		; Set up the LCD display
	call	show_signon	; show signon message

	movlw	25		; wait 500mS
	call	waitx20k

; set up A/D convertor

	bsf	STATUS,RP0	; bank 1
	movlw	b'10001101'	; right-justify, ext ref, inputs RA1/RA0
	movwf	ADCON1
;	bsf	PIE1,ADIE	; enable A/D interrupts
	bcf	STATUS,RP0	; bank 0

; Start Timer0

	movlw	REFRESH_DELAY
	movwf	TMR0
	bcf	INTCON,TMR0IF

; initialize variables

	clrf	Flags
	clrf	pagenum
	clr32	AmpSum

ReadInputs:
	movlw	15		; wait 3mS
	call	waitx250
	clrf	Volts
	clrf	Volts+1
	clrf	Amps
	clrf	Amps+1
	clrf	Watts
	clrf	Watts+1
	movlw	64		; 64 * 10 bit readings
	movwf	Readings	; (16 bit results)
NextRead:
	movlw	b'01000001'	; CLK/8, select RA0 (Volts)
	movwf	ADCON0
	call	wait100		; wait 100uS to stabilize analog input
	bsf	ADCON0,2	; start A/D conversion
waitv:	btfsc	ADCON0,2
	goto	waitv		; wait until conversion Done
	movf	ADRESH,w
	movwf	Temp1
	addwf	Volts
	bsf	STATUS,RP0
	movf	ADRESL,w	; add 10 bit A/D result to Volts
	bcf	STATUS,RP0
	movwf	Temp2
	addwf	Volts+1
	skpnc
	incf	Volts

	bcf	Flags,MAX_VOLTS
	movf	Temp1,w
	xorlw	b'00000011'
	skpnz
	bsf	Flags,MAX_VOLTS	; Volts overloaded ?
	movf	Temp2,w
	xorlw	b'11111111'
	skpz
	bcf	Flags,MAX_VOLTS

	movlw	b'01001001'	; CLK/8, select RA1 (Amps)
	movwf	ADCON0
	call	wait100		; wait 100uS to stabilize analog input
	bsf	ADCON0,2	; start A/D conversion
waita:	btfsc	ADCON0,2
	goto	waita		; wait until conversion Done
	movf	ADRESH,w
	movwf	Temp1
	addwf	Amps
	bsf	STATUS,RP0
	movf	ADRESL,w	; get 10 bit A/D result
	bcf	STATUS,RP0
	movwf	Temp2
	addwf	Amps+1
	skpnc
	incf	Amps

	bcf	Flags,MAX_AMPS
	movf	Temp1,w
	xorlw	b'00000011'
	skpnz
	bsf	Flags,MAX_AMPS	; Amps overloaded ?
	movf	Temp2,w
	xorlw	b'11111111'
	skpz
	bcf	Flags,MAX_AMPS

	decfsz	Readings
	goto	NextRead	; accumulate readings

	clrc
	rrf	Volts		; Volts / 2
	rrf	Volts+1

	movlw	Volts
	call	Divx12		; Volts / 12.8

	movlw	Amps
	call	Divx12		; Amps / 12.8

	btfsc	Flags,GOT_ZERO	; have Amps been zeroed ?
	goto	sub_zero
get_zero:
	movf	Amps,w
	movwf	ZeroAmps	; record zero Amps value
	movf	Amps+1,w
	movwf	ZeroAmps+1
	bsf	Flags,GOT_ZERO
sub_zero:
	bcf	Flags,NEG_AMPS
	sub16	ZeroAmps,Amps	; subtract zero value from Amps
	skpnc
	goto	got_amps
	bsf	Flags,NEG_AMPS
	com16	Amps		; Amps = -0 to -0.99
got_amps:
	mov16	Amps,aa
	mov16	Volts,bb
	call	Mult16		; Watts = Volts * Amps
	movi16	aa,1000
	call	Div32		; Watts = Watts / 1000
	subi16	bb,500
	skpc			; remainder > 0.5 ?
	goto	store_watts
	inc16	dd+2		; yes, round up
store_watts:
	mov16	dd+2,Watts

	add32	Amps,AmpSum	; AmpSum = accumulated Amps
	mov32	AmpSum,dd
	movi16	aa,3600*3	; 3 reads per second, 3600 seconds per hour
	call	Div32		; AmpHours = AmpSum  / (reads per hour)
store_amphrs:
	mov16	dd+2,AmpHours

; show results on LCD screen

do_display:
	movlw	6		; to repeat every 333.3mS
	movwf	ref_timer
	btfsc	PGBTN		; page button pressed ?
	goto	newline		; no
nextpage:
	incf	pagenum		; yes, page+1
	movlw	3
	subwf	pagenum,w
	skpnc			; page=0~2
	clrf	pagenum
	bsf	Flags,NEW_PAGE
newline:
	movlw	0
	call	Set_Cursor	; set cursor to start of line

	movf	pagenum,w
	addlw	-1
	skpc			; page=0 ?
	goto	page0
	skpnz			; page=1 ?
	goto	page1
				; page=2
page2:	call	Show_Volts
	movlw	LCDLINE2
	call	Set_Cursor	; "99.99V  99.99Ah"
	call	Show_AmpHours
	goto	pagedone

page1:	call	Show_Watts
	movlw	LCDLINE2
	call	Set_Cursor	; "999.9W  99.99A"
	call	Show_Amps
	goto	pagedone

page0:	call	Show_Volts
	movlw	LCDLINE2
	call	Set_Cursor	; "99.99V  99.99A"
	call	Show_Amps

pagedone:
	btfss	Flags,NEW_PAGE	; was page button on ?
	goto	checkhold	; no

waitpageoff:
	btfss	PGBTN		; wait for page button off
	goto	waitpageoff
	bcf	Flags,NEW_PAGE
	movlw	10		; debounce 200mS
	call	waitx20k

checkhold:
	btfsc	HDBTN		; hold button on ?
	goto	hold_off	; no
	btfss	PGBTN		; page button pressed ?
	goto	nextpage	; yes, show next page
	goto	checkhold	; no, wait until hold button off
hold_off:
	btfss	INTCON,TMR0IF
	goto	checkhold	; wait 55.555mS
	movlw	REFRESH_DELAY
	addwf	TMR0
	bcf	INTCON,TMR0IF
	decfsz	ref_timer	; wait 55.555 * 6 = 333.3mS
	goto	checkhold
	goto	ReadInputs
	END

