;CAPMET790.ASM 09MAY05 - COPYRIGHT JOHN BECKER - EPE IND/CAP/FREQ METER V2

;PIC16F628, 3.2768MHz, WDT OFF, POR ON, XTAL XS

;Config register bits
; CP1 CP0 CP1 CP0 NIL CPD LVP BOR MCL OS2 POR WDT OS1 OS0
;  1   1   1   1   1   1   0   0   1   0   0   0   0   1
;N.B. Logic 1/0 do NOT necessarily mean that the function is On/Off
;respectively - refer to PIC '627/8 data sheet

#DEFINE BANK0 BCF STATUS,5
#DEFINE BANK1 BSF STATUS,5

    List P=PIC16F628, R=DEC; 
    #INCLUDE "P16F628.INC"
	__CONFIG   0x3F21


        CBLOCK		0x0C
		REGAa0				;lsb
		REGAa1
		REGAa2
		REGAa3				;msb

		REGBb0				;lsb
		REGBb1
		REGBb2
		REGBb3				;msb

		REGC0				;lsb
		REGC1
		REGC2
		REGC3				;msb

		DSIGN				;Digit Sign. 0=positive,FF(or non zero)=negative
		DIGIT1				;MSD
		DIGIT2
		DIGIT3
		DIGIT4
		DIGIT5				;Decimal digits
		DIGIT6
		DIGIT7
		DIGIT8
		DIGIT9
		DIGIT10				;LSD
		MTEMP
		MCOUNT
		DCOUNT

		LOOP
		LOOPA
		RSLINE
		STORE
		SLOWIT
		POINT
		TIMEMSB
		OVERFLOW
		STORE2
		LARGE
		NANO
		CAPREF3
		CAPREF2
		CAPREF1
		CAPREF0
		INDREF3
		INDREF2
		INDREF1
		INDREF0
		TIMEOUT
		SWITCH
		ZERO
		INDCORRECT
		CAPCORRECT
		CORRECTLOC
        ENDC

PROMVAL  EQU	0x70   ; accessed via both BANKS

               ; locations up to $7F are available

; **************

        ORG		0
        goto	GIEOFF
        ORG		4          ; Interrupt vector address
        goto 	GIEOFF
        ORG 	5          ; Start of program memory

GIEOFF: BCF 	INTCON,GIE  ; turn off global interrupts
        BTFSC 	INTCON,GIE
        goto 	GIEOFF
        goto 	START

TABLCD: addwf 	PCL,F     ;LCD initialisation table
        retlw 	B'00110011' 		;initialise lcd - first byte
        retlw 	B'00110011' 		;2nd byte (repeat of first)
        retlw 	B'00110010' 		;set for 4-bit operation
        retlw 	B'00101100' 		;set for 2 lines
        retlw 	B'00000110' 		;set entry mode to increment each address
        retlw 	B'00001100' ;set display on, cursor off, blink off
        retlw 	B'00000001' ;clear display
        retlw 	B'00000010' ;return home, cursor & RAM to zero
                        ;end inititalisation table

LCDTITLE: addwf PCL,F
        retlw 	' '
        retlw 	'E'
        retlw 	'P'
        retlw 	'E'
        retlw 	' '
        retlw 	'L'
        retlw 	'C'
        retlw 	'F'
        retlw 	' '
        retlw 	'M'
        retlw 	'E'
        retlw 	'T'
        retlw 	'E'
        retlw 	'R'
        retlw 	' '
        retlw 	' '
        retlw 	' '

TITLEB: addwf 	PCL,F
        retlw 	' '
        retlw 	'W'
        retlw 	'A'
        retlw 	'I'
        retlw 	'T'
        retlw 	'I'
        retlw 	'N'
        retlw 	'G'
        retlw 	' '
        retlw 	'T'
        retlw 	'I'
        retlw 	'M'
        retlw 	'I'
        retlw 	'N'
        retlw 	'G'
        retlw 	' '


OVERFLOWED:  	addwf PCL,F
        retlw 	'O'
        retlw 	'V'
        retlw 	'E'
        retlw 	'R'
        retlw 	'F'
        retlw 	'L'
        retlw 	'O'
        retlw 	'W'

CALIB:  addwf 	PCL,F
        retlw 	' '
        retlw 	' '
        retlw 	' '
        retlw 	' '
        retlw 	' '
        retlw 	' '
        retlw 	'N'
        retlw 	'U'
        retlw 	'L'
        retlw	'L'
        retlw	'E'
        retlw 	'D'
        retlw 	' '
        retlw	' '
        retlw	' '
        retlw	' '

TIMEDOUT: addwf	PCL,F
        retlw 	' '
        retlw 	' '
        retlw 	' '
        retlw 	' '
        retlw 	'T'
        retlw 	'I'
        retlw 	'M'
        retlw 	'E'
        retlw 	' '
        retlw 	'O'
        retlw 	'U'
        retlw 	'T'
        retlw 	' '
        retlw 	' '
        retlw 	' '
        retlw	' '

CAPTIME: addwf	PCL,F
        retlw 	' '
        retlw 	' '
        retlw 	' '
        retlw 	'C'
        retlw 	'A'
        retlw 	'P'
        retlw 	'A'
        retlw 	'C'
        retlw 	'I'
        retlw 	'T'
        retlw 	'O'
        retlw 	'R'
        retlw 	' '
        retlw	' '
        retlw	' '
        retlw 	' '

INDTIME: addwf 	PCL,F
        retlw 	' '
        retlw 	' '
        retlw 	' '
        retlw 	' '
        retlw	'I'
        retlw 	'N'
        retlw 	'D'
        retlw 	'U'
        retlw 	'C'
        retlw 	'T'
        retlw 	'O'
        retlw 	'R'
        retlw 	' '
        retlw 	' '
        retlw 	' '
        retlw 	' '


CORRECTMSG: addwf PCL,F
        retlw 	' '
        retlw 	'C'
        retlw 	'O'
        retlw 	'R'
        retlw 	'R'
        retlw 	'E'
        retlw 	'C'
        retlw 	'T'
        retlw 	'I'
        retlw 	'O'
        retlw 	'N'
        retlw 	' '
        retlw 	' '
        retlw 	' '
        retlw 	' '
        retlw 	' '


;*******************

START:  clrf 	PORTA
        clrf 	PORTB
        movlw 	0x07
        movwf 	CMCON

        						;BANK1
        movlw 	B'11000000'
        movwf 	TRISB
        movlw 	B'00011100'     ; RA0,RA1 as output
        movwf 	TRISA
        movlw 	B'10000110'     ; timer 1:128, pull-ups off
        movwf 	OPTION_REG
        						;BANK0

        clrf 	INTCON
        call 	PAUSIT
        call 	LCDSET
        call 	PAUSIT

        movlw 	B'00000000'     ; T1 ext osc disable (bit3=0), T1 stopped (bit0=0), internal clock (bit1=0), bit2 dont care
        movwf 	T1CON

        btfsc 	PORTA,2       ; is S3 (RA2) pressed?
        goto 	CORRECTIT

        call 	LCD1
        bsf 	RSLINE,4

        clrf 	LOOP
TITLE2: movf 	LOOP,W
        call 	LCDTITLE
        call 	LCDOUT
        incf 	LOOP,F
        btfss 	LOOP,4
        goto 	TITLE2

        clrf 	INTCON
        clrf 	POINT
        clrf 	CAPREF3
        clrf 	CAPREF2
        clrf 	CAPREF1
        clrf 	CAPREF0
        clrf 	INDREF3
        clrf 	INDREF2
        clrf 	INDREF1
        clrf 	INDREF0

        movlw 	0
        call 	PRMGET
        movwf 	INDCORRECT
        movlw 	1
        call 	PRMGET
        movwf 	CAPCORRECT

        call 	PAUSIT         ; delay
        call 	PAUSIT         ; delay

        clrf 	INTCON
        movlw 	255
        movwf 	SWITCH

;****************** START OF MAIN

MAIN:   clrf 	TIMEOUT
        movf 	PORTA,W
        andlw 	B'00010000'
        xorwf 	SWITCH,W
        btfsc 	STATUS,Z
        goto 	MAIN2

        movf 	PORTA,W
        andlw 	B'00010000'
        movwf 	SWITCH

        call 	LCD21
        bsf 	RSLINE,4
        clrf 	LOOP
TITLE3: movf 	LOOP,W
        call 	TITLEB
        call 	LCDOUT
        incf 	LOOP,F
        btfss 	LOOP,4
        goto 	TITLE3

        call 	LCD1
        bsf 	RSLINE,4
        clrf 	LOOP
        btfsc 	PORTA,4
        goto 	TIMOUT4

MAIN3:  movf 	LOOP,W
        call 	CAPTIME
        call 	LCDOUT
        incf 	LOOP,F
        btfss 	LOOP,4
        goto 	MAIN3
        goto 	MAIN2

MAIN4:  movf 	LOOP,W
        call 	INDTIME
        call 	LCDOUT
        incf 	LOOP,F
        btfss 	LOOP,4
        goto 	TIMOUT4
        goto 	MAIN4

MAIN2:  btfsc 	SWITCH,4
        goto 	INDUCT
        goto 	CAPACITOR

; *****************

; QBASIC ROUTINE ILLUSTRATING INDUCTANCE FORMULA

;CLS
;DEFDBL A-Z
;PRINT "Formula:  L = ((1000000000 / (2 * PI * F) ^ 2)) / C"
;PRINT

;PI = 22 / 7
;F = 389305: PRINT "Frequency = "; F
;C = 200: C = (C * C) / (C + C): PRINT "Capacitance = "; C

;Process:
;L = F * 44: PRINT L
;L = INT(L / 7): PRINT L
;L = INT(1000000000 / L): PRINT L
;L = L * L: PRINT L
;L = INT(L / 10):
;L = L / 1000
;PRINT "Inductance = "; L; "uH"

; **************

INDUCT: movlw 	B'00000001'     ; set for correct osc
        movwf 	PORTA

        call 	INDTIMER       ; get osc frequency
        call 	COPY_TIME_REGA

        clrf 	POINT
        clrf 	LARGE

        call 	LCD1           ; set address    ; orig ****
        bsf 	RSLINE,4        ; set RS for data send
        call 	BIN2DEC
        call 	SHOWITALL
        movlw 	'H'
        call 	LCDOUT
        movlw 	'z'
        call 	LCDOUT
        movlw 	' '
        call 	LCDOUT
        movlw 	' '
        call 	LCDOUT

        movf 	TMR1H,W
        andlw 	B'11110000'     ; was %11000000 - changed 01MAR04
        iorwf 	TIMEMSB,W     ; is timing value less than 4096 ?

        btfsc 	STATUS,Z
        call 	LARGEVALUE     ; yes

        btfsc 	TIMEOUT,0
        goto 	TIMEEND

        call 	COPY_TIME_REGA

        movlw 	44            ; multiply freq x 44 (22 x 2) (2 x PI = 44/7)
        call 	MULTIPLYSMALL  ; REGA * 44 -> REGA        
        movf 	STATUS,W
        andlw 	1
        movwf 	OVERFLOW

        movlw 	'K'
        movf 	OVERFLOW,F
        btfss 	STATUS,Z
        goto 	SHOWOVERFLOW

        movlw 	7             ; divide by 7
        call 	DIVIDESMALL    ; REGA / 7 -> REGA
        movf 	STATUS,W
        andlw 	1
        movwf 	OVERFLOW

        movlw 	'L'
        movf 	OVERFLOW,F
        btfss 	STATUS,Z
        goto 	SHOWOVERFLOW

        call 	COPY_REGA_REGB ; copy answer into REGB
        call 	DIVIDEBILLION  ; 1,000,000,000 / REGB -> REGA
        movf 	STATUS,W
        andlw 	1
        movwf 	OVERFLOW

        movlw 	'J'
        movf 	OVERFLOW,F
        btfss 	STATUS,Z
        goto 	SHOWOVERFLOW

        call 	COPY_REGA_REGB ; copy answer into REGB
        call 	MULTIPLY       ; REGA * REGB -> REGA (squaring REGA)
        movf 	STATUS,W
        andlw 	1
        movwf 	OVERFLOW

        movlw 	'2'
        movf 	OVERFLOW,F
        btfss 	STATUS,Z
        goto 	SHOWOVERFLOW

        movlw 	100            ; divide by 10   **** 10
        call 	DIVIDESMALL    ; REGA / 100 -> REGA
        movf 	STATUS,W
        andlw 	1
        movwf 	OVERFLOW

        movlw 	'C'
        movf 	OVERFLOW,F
        btfss 	STATUS,Z
        goto 	SHOWOVERFLOW

        movf 	INDCORRECT,W   ; multiply by correction factor
        call 	MULTIPLYSMALL  ; REGA * REGB -> REGA
        movf 	STATUS,W
        andlw 	1
        movwf 	OVERFLOW

        movlw 	'A'
        movf 	OVERFLOW,F
        btfss 	STATUS,Z
        goto 	SHOWOVERFLOW

        movlw 	10            ; divide by 10    ***** 100  01MAR04
        call 	DIVIDESMALL    ; REGA / 100 -> REGA
        movf 	STATUS,W
        andlw 	1
        movwf 	OVERFLOW

        movlw 	'B'
        movf 	OVERFLOW,F
        btfss 	STATUS,Z
        goto 	SHOWOVERFLOW

        btfsc 	PORTA,2       ; is S3 (RA2) pressed?
        call 	CALIBIND

        movf 	LARGE,F
        btfsc 	STATUS,Z
        call 	SUBTRACTINDREF

        movlw 	4
        movwf 	POINT

        call 	LCD21          ; set address
        bsf 	RSLINE,4        ; set RS for data send

        call 	BIN2DEC        ; converts binary in REGA to decimal in DIGIT

        movf 	LARGE,F
        btfsc 	STATUS,Z
        call 	CHECKMILLI

        call 	SHOWITALL

        movf 	LARGE,F
        btfss 	STATUS,Z
        goto 	IND2

        movlw 	'u'
        movf 	NANO,F
        btfss 	STATUS,Z
        movlw 	'm'
        call 	LCDOUT
        goto 	IND3
IND2:   call 	LCD29 
        bsf 	RSLINE,4        ; set RS for data send
IND3:   movlw 	'H'
        call 	LCDOUT
        movlw 	' '
        call 	LCDOUT
        movlw 	' '
        call 	LCDOUT
        movlw 	' '
        call 	LCDOUT
        movlw 	' '
        call 	LCDOUT
        movlw 	' '
        call 	LCDOUT
        movlw 	' '
        call 	LCDOUT

        goto 	MAIN

; ***************

CAPACITOR: movlw B'00000010'  ; set for correct osc
        movwf 	PORTA

        clrf 	TIMEMSB
        call 	INDTIMER

        call 	COPY_TIME_REGA

        clrf 	POINT
        clrf 	LARGE

        movf 	TMR1H,W
        andlw 	B'11111110'
        iorwf 	TIMEMSB,W     ; is timing value less than 512?
        btfsc 	STATUS,Z
        call 	LARGEVALUE     ; yes

        btfsc 	ZERO,0
        goto 	TIMEEND

        call 	LCD1           ; set address
        bsf 	RSLINE,4        ; set RS for data send
        call 	BIN2DEC
        call 	SHOWITALL
        movlw 	'H'
        call 	LCDOUT
        movlw 	'z'
        call 	LCDOUT
        movlw 	' '
        call 	LCDOUT
        movlw 	' '
        call 	LCDOUT

        call 	COPY_TIME_REGA

        movlw 	22             ; multiply freq x PI (R = 1k) (ignoring 3 zeros)
        call 	MULTIPLYSMALL
        movlw 	7
        call 	DIVIDESMALL

        call 	COPY_REGA_REGB ; copy answer into REGB
        call 	DIVIDEBILLION  ; REGA / REGB -> REGA

        movf 	CAPCORRECT,W   ; multiply by correction factor
        call 	MULTIPLYSMALL  ; REGA / REGB -> REGA
        movlw 	100           ; divide by 100
        call 	DIVIDESMALL    ; REGA / 100 -> REGA

        btfsc 	PORTA,2       ; is S3 (RA2) pressed?
        call 	CALIBCAP

        movf 	LARGE,F
        btfsc 	STATUS,Z
        call 	SUBTRACTCAPREF

        call 	LCD21          ; set address
        bsf 	RSLINE,4        ; set RS for data send
        call 	BIN2DEC
        call 	CHECKNANO

        call 	SHOWITALL

        movlw 	'u'
        movf 	LARGE,F
        btfss 	STATUS,Z
        goto 	CAP2

        movlw 	'n'
        movf 	NANO,F
        btfsc 	STATUS,Z
        movlw 	'p'
CAP2:   call 	LCDOUT
        movlw 	'F'
        call 	LCDOUT
        movlw 	' '
        call 	LCDOUT
        movlw 	' '
        call 	LCDOUT
        movlw 	' '
        call 	LCDOUT
        movlw 	' '
        goto 	MAIN

; **************

LARGEVALUE:
        clrf 	TIMEOUT
        incf 	LARGE,F
        movlw 	B'00000000'     ; stop timer 1
        movwf 	T1CON
        clrf 	TMR1L          ; reset timer 1
        clrf 	TMR1H
        bcf 	PIR1,0          ; clear timer rollover flag

        movf 	PORTB,W        ; get current status of RB6
        andlw 	B'01000000'
        movwf 	STORE         ; store it

        clrf 	SLOWIT
        bsf 	T1CON,0         ; start timer 1

WAITB1:
        movf 	PORTA,W
        andlw 	B'00010000'
        xorwf 	SWITCH,W
        btfss 	STATUS,Z
        return

WAITB2: movf 	PORTB,W        ; get current status of RB6
        andlw 	B'01000000'
        movwf 	STORE2        ; temp store it

        xorwf 	STORE,W       ; compare with prev val of RB6
        btfsc 	STATUS,Z      ; is it equal?
        goto 	WAITB1         ; yes
        movf 	STORE2,W       ; no, different, so store val
        movwf 	STORE

        call 	OSCILLATE      ; get timing between two changes of RB6 (1 full cycle)
        btfsc 	TIMEOUT,0
        return

        call 	COPY_TIME_REGA

        movlw 	122
        call 	MULTIPLYSMALL  ; REGA * 122 -> REGA multiply freq x 122 to get time re 3.2768MHz xtal (1000000/819200 =1.22)

        movlw 	100           ; divide by 100
        call 	DIVIDESMALL    ; REGA / 100 -> REGA

        call 	COPY_REGA_REGB ; copy answer into REGB
        call 	DIVIDEBILLION  ; 1,000,000,000 / REGB -> REGA 

        movf 	REGAa0,W        ; copy answer back into TIMER
        movwf 	TMR1L
        movf 	REGAa1,W
        movwf 	TMR1H
        movf 	REGAa2,W
        movwf 	TIMEMSB
        movlw 	4
        movwf 	POINT
        return

; ***********

CHECKNANO: clrf NANO
        movf 	DIGIT1,W
        iorwf 	DIGIT2,W
        iorwf 	DIGIT3,W
        iorwf 	DIGIT4,W
        iorwf 	DIGIT5,W
        iorwf 	DIGIT6,W
        andlw 	B'00001111'
        btfsc 	STATUS,Z
        return
        bsf 	NANO,0
        movlw 	4
        movwf 	POINT
        return

; ***********

CHECKMILLI: clrf NANO
        movf 	DIGIT1,W
        iorwf 	DIGIT2,W
        iorwf 	DIGIT3,W
        iorwf 	DIGIT4,W
        iorwf 	DIGIT5,W
        andlw 	B'00001111'
        btfsc 	STATUS,Z
        return
        bsf 	NANO,0
        movlw 	7
        movwf 	POINT
        return

; *************

CALIBCAP: movf 	REGAa0,W
        movwf 	CAPREF0
        movf 	REGAa1,W
        movwf 	CAPREF1
        movf 	REGAa2,W
        movwf 	CAPREF2
        movf 	REGAa3,W
        movwf 	CAPREF3
        call 	SHOWCALIB
        return

CALIBIND: movf 	REGAa0,W
        movwf 	INDREF0
        movf 	REGAa1,W
        movwf 	INDREF1
        movf 	REGAa2,W
        movwf 	INDREF2
        movf 	REGAa3,W
        movwf 	INDREF3
        call 	SHOWCALIB
        return

; **************

SHOWCALIB: call LCD1
        bsf 	RSLINE,4

        clrf 	LOOP
CALIB2: movf 	LOOP,W
        call 	CALIB
        call 	LCDOUT
        incf 	LOOP,F
        btfss 	LOOP,4
        goto 	CALIB2
        call 	PAUSIT
        return

; **************

SUBTRACTCAPREF: ; subtract ref from current val
                ; REGA - REGB -> REGA ;Return carry set if overflow
        movf 	CAPREF3,W
        movwf 	REGBb3
        movf 	CAPREF2,W
        movwf 	REGBb2
        movf 	CAPREF1,W
        movwf 	REGBb1
        movf 	CAPREF0,W
        movwf 	REGBb0
        call 	SUBTRACT
        movf 	STATUS,W
        andlw 	1
        movwf 	OVERFLOW
        return

SUBTRACTINDREF: ; subtract ref from current val
                ; REGA - REGB -> REGA ;Return carry set if overflow
        movf 	INDREF3,W
        movwf 	REGBb3
        movf 	INDREF2,W
        movwf 	REGBb2
        movf 	INDREF1,W
        movwf 	REGBb1
        movf 	INDREF0,W
        movwf 	REGBb0
        call 	SUBTRACT
        movf 	STATUS,W
        andlw 	1
        movwf 	OVERFLOW
        return

; **************

INDTIMER: movlw B'00000110'   ; T1 ext osc sync off (bit 2) timer1 off
        movwf 	T1CON         ; uses T1 ext clock input via RB6 and measures
                            ; number of clock pulses during 25 cycles of TMR0
        BANK1
        movlw 	B'10000110'     ; timer 1:128, pullups off
        movwf 	OPTION_REG
        BANK0

        clrf 	TMR1L          ; reset timer 1
        clrf 	TMR1H
        clrf 	TIMEMSB
        bcf 	PIR1,0          ; clr timer rollover flag

        movlw 	B'00000111'     ; T1 ext osc sync off (bit 2) timer1 on
        movwf 	T1CON

        movlw 	25            
        movwf 	SLOWIT
        clrf 	TMR0
        bcf 	INTCON,2
TIMEIT: btfss 	PIR1,0        ; has timer 1 overflowed?
        goto 	TIMIT2
        bcf 	PIR1,0
        incf 	TIMEMSB,F
TIMIT2: btfss 	INTCON,2      ; has timer 0 overflowed?
        goto 	TIMEIT
        bcf 	INTCON,2
        decfsz 	SLOWIT,F
        goto 	TIMEIT

        movlw 	B'00000000'
        movwf 	T1CON
        return

; ****************

OSCILLATE:   clrf ZERO
        clrf 	T1CON
        clrf 	TMR1H
        clrf 	TMR1L
        bcf 	PIR1,0
        bsf 	T1CON,0         ; start timer 1

        clrf 	TIMEMSB
        bsf 	T1CON,0         ; start timer 1
OSCA1:
        movf 	PORTA,W
        andlw 	B'00010000'
        xorwf 	SWITCH,W
        btfss 	STATUS,Z
        return

        btfsc 	PIR1,0        ; has timer 1 overflowed?
        call 	INCMSB         ; yes

OSCA1A: movf 	PORTB,W        ; is RB6 same as before?
        andlw 	B'01000000'
        movwf 	STORE2
        xorwf 	STORE,W
        btfsc 	STATUS,Z
        goto 	OSCA1          ; yes
        movf 	STORE2,W       ; no, so continue for next cycle
        movwf 	STORE

OSCA2:
        movf 	PORTA,W
        andlw 	B'000100001'
        xorwf 	SWITCH,W
        btfss 	STATUS,Z
        return

        btfsc 	PIR1,0        ; has timer 1 overflowed?
        call 	INCMSB         ; yes

OSCA2A: movf 	PORTB,W        ; is RB6 same as before?
        andlw 	B'01000000'
        movwf 	STORE2
        xorwf 	STORE,W
        btfsc 	STATUS,Z
        goto 	OSCA2          ; yes
        movlw 	B'00000000'     ; no, stop timer 1
        movwf 	T1CON
        return

INCMSB: incf 	TIMEMSB,F
        decf 	SLOWIT,F
        bcf 	PIR1,0
        clrf 	TMR1H
        clrf 	TMR1L
        movf 	SLOWIT,F
        btfsc 	STATUS,Z
        bsf 	ZERO,0
        return

;******** LCD ROUTINES **********

LCD1:   movlw 	B'10000000'
        goto 	LCDLIN
LCD5:   movlw 	B'10000101'
        goto 	LCDLIN
LCD6:   movlw 	B'10000110'
        goto 	LCDLIN
LCD8:   movlw 	B'10001000'
        goto 	LCDLIN
LCD9:   movlw 	B'10001001'
        goto 	LCDLIN
LCD13:  movlw 	B'10001101'
        goto 	LCDLIN
LCD15:  movlw 	B'10001111'
        goto 	LCDLIN

LCD21:  movlw 	B'11000000'
        goto 	LCDLIN
LCD25:  movlw 	B'11000101'
        goto 	LCDLIN
LCD26:  movlw 	B'11000110'
        goto 	LCDLIN
LCD28:  movlw 	B'11001000'
        goto 	LCDLIN
LCD29:  movlw 	B'11001001'

LCDLIN: BCF 	RSLINE,4

LCDOUT: movwf 	STORE
        movlw 	50
        movwf 	LOOPA
DELAYIT: decfsz LOOPA,F
        goto 	DELAYIT
        call 	SENDIT

SENDIT: swapf 	STORE,F
        movf 	STORE,W
        andlw 	15
        iorwf 	RSLINE,W
        movwf 	PORTB
        BSF 	PORTB,5
        nop 
        nop
        BCF 	PORTB,5
        RETURN

; *************

PAUSIT: movlw 	25
        movwf 	SLOWIT
        bcf 	INTCON,2
PAUSE:  btfss 	INTCON,2
        goto 	PAUSE
        bcf 	INTCON,2
        decfsz 	SLOWIT,F
        goto 	PAUSE
        return

PAUSIT2: clrf 	TMR0
        movlw 	10
        movwf 	SLOWIT
        bcf 	INTCON,2
PAUSE2: btfss 	INTCON,2
        goto 	PAUSE2
        bcf 	INTCON,2
        decfsz 	SLOWIT,F
        goto 	PAUSE2
        return

;..............

LCDSET: clrf 	LOOP       ;clr LCD set-up loop
        clrf 	RSLINE     ;clear RS line for instruction send
LCDST2: movf 	LOOP,W     ;get table address
        call 	TABLCD     ;get set-up instruction
        call 	LCDOUT     ;perform it
        incf 	LOOP,F     ;inc loop
        btfss 	LOOP,3    ;has last LCD set-up instruction now been done?
        goto 	LCDST2     ;no
        return


CLRLINE1: call 	LCD1     ;set address for line 1 cell 1
        bsf 	RSLINE,4    ;set RS for data send
        clrf 	LOOP       ;
CLRL1:  movlw 	' '       ;clear cell
        call 	LCDOUT     ;
        incf 	LOOP,F     ;inc loop
        btfss 	LOOP,4    ;has last LCD letter been sent?
        goto 	CLRL1      ;no
        return

CLRLINE2: call 	LCD21
        bsf 	RSLINE,4
        movlw 	16
        movwf 	LOOP
CL2:    MOVLW 	' '
        CALL 	LCDOUT
        DECFSZ 	LOOP,F
        GOTO 	CL2
        RETURN

; ***********

SHOWITALL:
        movlw 	DIGIT1
        movwf 	FSR
        movlw 	10
        movwf 	LOOP
SHOW2:  movf 	INDF,W
        call 	LCDOUT
        movf 	LOOP,W
        xorwf 	POINT,W
        btfss 	STATUS,Z
        goto 	SHOW3
        movlw 	'.'
        call 	LCDOUT
SHOW3:  incf 	FSR,F
        decfsz 	LOOP,F
        goto 	SHOW2
        return

; ***********

COPY_TIME_REGA:
        movf 	TIMEMSB,W
        movwf 	REGAa2
        movf 	TMR1H,W
        movwf 	REGAa1
        movf 	TMR1L,W
        movwf 	REGAa0
        clrf 	REGAa3
        return

; *********

MULTIPLYSMALL:
        movwf 	REGBb0
        clrf 	REGBb1
        clrf 	REGBb2
        clrf 	REGBb3
        call 	MULTIPLY
        movf 	STATUS,W
        andlw 	1
        movwf 	OVERFLOW
        return

; *************

DIVIDESMALL:
        movwf 	REGBb0
        clrf 	REGBb1
        clrf 	REGBb2
        clrf 	REGBb3
        call 	DIVIDE
        movf 	STATUS,W
        andlw 	1
        movwf 	OVERFLOW
        return

; ***********

DIVIDEBILLION:
        movlw 	0x3B      ; divide 1,000,000,000 ($3B9ACA00) by the answer
        movwf 	REGAa3
        movlw 	0x9A
        movwf 	REGAa2
        movlw 	0xCA
        movwf 	REGAa1
        movlw 	0x00
        movwf 	REGAa0
        call 	DIVIDE
        movf 	STATUS,W
        andlw 	1
        movwf 	OVERFLOW
        return

; ***********

COPY_REGA_REGB:
        MOVF 	REGAa0,W    
        MOVWF 	REGBb0
       	MOVF 	REGAa1,W
        MOVWF 	REGBb1
        MOVF 	REGAa2,W
        movwf 	REGBb2
        movf 	REGAa3,W
        movwf 	REGBb3
        return

; *********** PETER HEMSLEY'S 32-BIT MATHS ROUTINES *******

;*** SIGNED MULTIPLY ***
;REGA * REGB -> REGA
;Return carry set if overflow

MULTIPLY
		clrf	MTEMP		;Reset sign flag
		call	chksgna		;Make REGA positive
		skpc
		call	chksgnb		;Make REGB positive
		skpnc
		return			;Overflow

		call	movac		;Move REGA to REGC
		call	clra		;Clear product

		movlw	D'31'		;Loop counter
		movwf	MCOUNT

muloop	call	slac		;Shift left product and multiplicand
	
		rlf		REGC3,w		;Test MSB of multiplicand
		skpnc			;If multiplicand bit is a 1 then
		call	addba		;add multiplier to product

		skpc			;Check for overflow
		rlf		REGAa3,w
		skpnc
		return

		decfsz	MCOUNT,f	;Next
		goto	muloop

		btfsc	MTEMP,0		;Check result sign
		call	negatea		;Negative



		return


; *************

;*** SIGNED DIVIDE ***
;REGA / REGB -> REGA
;Remainder in REGC
;Return carry set if overflow or division by zero

DIVIDE	clrf	MTEMP		;Reset sign flag
		movf	REGBb0,w		;Trap division by zero
		iorwf	REGBb1,w
		iorwf	REGBb2,w
		iorwf	REGBb3,w
		sublw	0
		skpc
		call	chksgna		;Make dividend (REGA) positive
		skpc
		call	chksgnb		;Make divisor (REGB) positive
		skpnc
		return			;Overflow

		clrf	REGC0		;Clear remainder
		clrf	REGC1
		clrf	REGC2
		clrf	REGC3

		movlw	D'32'		;Loop counter
		movwf	MCOUNT

dvloop	call	slac		;Shift dividend (REGA) msb into remainder (REGC)

		movf	REGBb3,w		;Test if remainder (REGC) >= divisor (REGB)
		subwf	REGC3,w
		skpz
		goto	dtstgt
		movf	REGBb2,w
		subwf	REGC2,w
		skpz
		goto	dtstgt
		movf	REGBb1,w
		subwf	REGC1,w
		skpz
		goto	dtstgt
		movf	REGBb0,w
		subwf	REGC0,w
dtstgt	skpc			;Carry set if remainder >= divisor
		goto	dremlt

		movf	REGBb0,w		;Subtract divisor (REGB) from remainder (REGC)
		subwf	REGC0,f
		movf	REGBb1,w
		skpc
		incfsz	REGBb1,w
		subwf	REGC1,f
		movf	REGBb2,w
		skpc
		incfsz	REGBb2,w
		subwf	REGC2,f
		movf	REGBb3,w
		skpc
		incfsz	REGBb3,w
		subwf	REGC3,f
		clrc
		bsf		REGAa0,0		;Set quotient bit

dremlt	decfsz	MCOUNT,f	;Next
		goto	dvloop

		btfsc	MTEMP,0		;Check result sign
		call	negatea		;Negative
		return

;*** SQUARE ROOT ***
;sqrt(REGA) -> REGA
;Return carry set if negative

sqrt	rlf		REGAa3,w		;Trap negative values
		skpnc
		return

		call	movac		;Move REGA to REGC
		call	clrba		;Clear remainder (REGB) and root (REGA)

		movlw	D'16'		;Loop counter
		movwf	MCOUNT

sqloop	rlf		REGC0,f		;Shift two msb's
		rlf		REGC1,f		;into remainder
		rlf		REGC2,f
		rlf		REGC3,f
		rlf		REGBb0,f
		rlf		REGBb1,f
		rlf		REGBb2,f
		rlf		REGC0,f
		rlf		REGC1,f
		rlf		REGC2,f
		rlf		REGC3,f
		rlf		REGBb0,f
		rlf		REGBb1,f
		rlf		REGBb2,f

		setc			;Add 1 to root
		rlf		REGAa0,f		;Align root
		rlf		REGAa1,f
		rlf		REGAa2,f

		movf	REGAa2,w		;Test if remdr (REGB) >= root (REGA)
		subwf	REGBb2,w
		skpz
		goto	ststgt
		movf	REGAa1,w
		subwf	REGBb1,w
		skpz
		goto	ststgt
		movf	REGAa0,w
		subwf	REGBb0,w
ststgt	skpc			;Carry set if remdr >= root
		goto	sremlt

		movf	REGAa0,w		;Subtract root (REGA) from remdr (REGB)
		subwf	REGBb0,f
		movf	REGAa1,w
		skpc
		incfsz	REGAa1,w
		subwf	REGBb1,f
		movf	REGAa2,w
		skpc
		incfsz	REGAa2,w
		subwf	REGBb2,f
		bsf		REGAa0,1		;Set current root bit

sremlt	bcf		REGAa0,0		;Clear test bit
		decfsz	MCOUNT,f	;Next
		goto	sqloop

		clrc
		rrf		REGAa2,f		;Adjust root alignment
		rrf		REGAa1,f
		rrf		REGAa0,f
		return


;*** SIGNED BINARY TO DECIMAL ***
;REGA -> DIGITS 1 (MSD) TO 10 (LSD) & DSIGN
;DSIGN = 0 if REGA is positive, FF if negative
;Return carry set if overflow
;Uses FSR register

BIN2DEC
        call    clrdig          ;Clear all digits
        clrf    MTEMP           ;Reset sign flag
		call	chksgna		;Make REGA positive
		skpnc
        goto 	BLANKIT            ;Overflow

		movlw	D'32'		;Loop counter
		movwf	MCOUNT

b2dloop	rlf		REGAa0,f		;Shift msb into carry
		rlf		REGAa1,f
		rlf		REGAa2,f
		rlf		REGAa3,f

		movlw	DIGIT10
		movwf	FSR		;Pointer to digits
		movlw	D'10'		;10 digits to do
		movwf	DCOUNT

adjlp	rlf		INDF,f		;Shift digit and carry 1 bit left
        movlw   -D'10'
		addwf	INDF,w		;Check and adjust for decimal overflow
		skpnc
		movwf	INDF

		decf	FSR,f		;Next digit
		decfsz	DCOUNT,f
		goto	adjlp

		decfsz	MCOUNT,f	;Next bit
		goto	b2dloop

		btfsc	MTEMP,0		;Check sign
		comf	DSIGN,f		;Negative
		clrc

BLANKIT: movlw 48
        iorwf 	DIGIT1,F
        iorwf 	DIGIT2,F
        iorwf 	DIGIT3,F
        iorwf 	DIGIT4,F
        iorwf 	DIGIT5,F
        iorwf 	DIGIT6,F
        iorwf 	DIGIT7,F
        iorwf 	DIGIT8,F
        iorwf 	DIGIT9,F
        iorwf 	DIGIT10,F

        movlw 	10          ; blank leading zeros
        movwf 	LOOP
        movlw 	DIGIT1
        movwf 	FSR
BLANK:  movf 	LOOP,W
        xorwf 	POINT,W
        btfsc 	STATUS,Z
        return
        movf 	INDF,W
        andlw 	15
        btfss 	STATUS,Z
        return
        bcf 	INDF,4
        incf 	FSR,F
        decfsz 	LOOP,F
        goto 	BLANK
        movlw 	48
        iorwf 	DIGIT10,F
        return

; **************

;Check sign of REGA and convert negative to positive
;Used by multiply, divide, bin2dec

chksgna	rlf		REGAa3,w
		skpc
		return			;Positive

;Negate REGA
;Used by chksgna, multiply, divide, mod, bin2dec, dec2bin

negatea	movf	REGAa3,w		;Save sign in w
		andlw	0x80

		comf	REGAa0,f		;2's complement
		comf	REGAa1,f
		comf	REGAa2,f
		comf	REGAa3,f
		incfsz	REGAa0,f
		goto	nega1
		incfsz	REGAa1,f
		goto	nega1
		incfsz	REGAa2,f
		goto	nega1
		incf	REGAa3,f
nega1
		incf	MTEMP,f		;flip sign flag
		addwf	REGAa3,w		;Return carry set if -2147483648
		return

;Set all digits to 0
;Used by bin2dec

clrdig	clrf	DSIGN
		clrf	DIGIT1
		clrf	DIGIT2
		clrf	DIGIT3
		clrf	DIGIT4
		clrf	DIGIT5
		clrf	DIGIT6
		clrf	DIGIT7
		clrf	DIGIT8
		clrf	DIGIT9
		clrf	DIGIT10
		return

;Shift left REGA and REGC
;Used by multiply, divide

slac	rlf		REGAa0,f
		rlf		REGAa1,f
		rlf		REGAa2,f
		rlf		REGAa3,f
		rlf		REGC0,f
		rlf		REGC1,f
		rlf		REGC2,f
		rlf		REGC3,f
		return

;Check sign of REGB and negative convert to positive
;Used by multiply, divide, mod

chksgnb	rlf	REGBb3,w
		skpc
		return			;Positive

;Negate REGB
;Used by chksgnb, subtract, multiply, divide, mod

negateb	movf	REGBb3,w		;Save sign in w
		andlw	0x80

		comf	REGBb0,f		;2's complement
		comf	REGBb1,f
		comf	REGBb2,f
		comf	REGBb3,f
		incfsz	REGBb0,f
		goto	negb1
		incfsz	REGBb1,f
		goto	negb1
		incfsz	REGBb2,f
		goto	negb1
		incf	REGBb3,f

negb1	incf	MTEMP,f		;flip sign flag
		addwf	REGBb3,w		;Return carry set if -2147483648
		return

movac	movf	REGAa0,w
		movwf	REGC0
		movf	REGAa1,w
		movwf	REGC1
		movf	REGAa2,w
		movwf	REGC2
		movf	REGAa3,w
		movwf	REGC3
		return

;Clear REGB and REGA
;Used by sqrt

clrba	clrf	REGBb0
		clrf	REGBb1
		clrf	REGBb2
		clrf	REGBb3

;Clear REGA
;Used by multiply, sqrt

clra	clrf	REGAa0
		clrf	REGAa1
		clrf	REGAa2
		clrf	REGAa3
		return

;Add REGB to REGA (Unsigned)
;Used by add, multiply,

addba	movf	REGBb0,w		;Add lo byte
		addwf	REGAa0,f

		movf	REGBb1,w		;Add mid-lo byte
		skpnc			;No carry_in, so just add
		incfsz	REGBb1,w		;Add carry_in to REGB
		addwf	REGAa1,f		;Add and propagate carry_out

		movf	REGBb2,w		;Add mid-hi byte
		skpnc
		incfsz	REGBb2,w
		addwf	REGAa2,f

		movf	REGBb3,w		;Add hi byte
		skpnc
		incfsz	REGBb3,w
		addwf	REGAa3,f
		return


;*** SIGNED SUBTRACT ***
;REGA - REGB -> REGA
;Return carry set if overflow

SUBTRACT
		call	negateb		;Negate and add
		skpnc
		return			;Overflow

;*** SIGNED ADD ***
;REGA + REGB -> REGA
;Return carry set if overflow

add		movf	REGAa3,w		;Compare signs
		xorwf	REGBb3,w
		movwf	MTEMP

		call	addba		;Add REGB to REGA

		clrc			;Check signs
		movf	REGBb3,w		;If signs are same
		xorwf	REGAa3,w		;so must result sign
		btfss	MTEMP,7		;else overflow
		addlw	0x80
		return

; *************

SHOWOVERFLOW: movwf OVERFLOW
        call 	CLRLINE2
        call 	LCD21
        bsf 	RSLINE,4

;        movf INDCORRECT,W      ; author's test section
;        movwf REGA0
;        movf LARGE,W
;        movwf REGA0
;        clrf REGA1
;        clrf REGA2
;        clrf REGA3
;        call BIN2DEC
;        call SHOWITALL

        clrf 	LOOP
OVER2:  movf 	LOOP,W
        call 	OVERFLOWED
        call 	LCDOUT
        incf 	LOOP,F
        btfss 	LOOP,3
        goto 	OVER2
        movlw 	' '
        call 	LCDOUT
        movf 	OVERFLOW,W
        call 	LCDOUT
        goto 	MAIN

; ***********

CHECKZERO:
        movf 	TMR1H,W
        iorwf 	TMR1L,W
        iorwf 	TIMEMSB,W
        movf 	STATUS,W
        andlw 	B'00000100'
        movwf 	OVERFLOW

TIMEEND: 
		movlw 	B'00000000'      ;stop timer 1
        movwf 	T1CON
        clrf 	TMR1H
        clrf 	TMR1L
        clrf 	TIMEMSB
        call 	LCD21
        bsf 	RSLINE,4
        clrf 	LOOP
TIMOUT2: movf 	LOOP,W
        call 	TIMEDOUT
        call 	LCDOUT
        incf 	LOOP,F
        btfss 	LOOP,4
        goto 	TIMOUT2

        call 	LCD1
        bsf 	RSLINE,4
        clrf 	LOOP
        btfsc 	PORTA,4
        goto 	TIMOUT4

TIMOUT3: movf 	LOOP,W
        call 	CAPTIME
        call 	LCDOUT
        incf 	LOOP,F
        btfss 	LOOP,4
        goto 	TIMOUT3
        goto 	MAIN

TIMOUT4: movf 	LOOP,W
        call 	INDTIME
        call 	LCDOUT
        incf 	LOOP,F
        btfss 	LOOP,4
        goto 	TIMOUT4
        goto 	MAIN


; ******* WRITE DATA TO EEPROM ROUTINE modified for PIC16F62x devices ********
          ;according to data sheet page 93 (is the same as for 16F87x devices
	  ; except that PIR2 of '87x has become PIR1 for '62x and page 2/3 not used)
	
                        ;This routine is entered with W holding
                        ;the eeprom byte address at which data
                        ;is to be stored. The data to be stored
                        ;is held in PROMVAL, which is located in both pages at or above $70
SETPRM: 
        				;BANK1
        movwf 	EEADR     ;copy W into EEADR to set eeprom address
        movf 	PROMVAL,W  ;get data value from PROMVAL and hold in W
        movwf 	EEDATA    ;copy W into eeprom data byte register
        bsf 	EECON1,WREN ;enable write flag

MANUAL: movlw 	0x55       ;these lines cause the action required by
        movwf 	EECON2    ;by the eeprom to store the data in EEDATA
        movlw 	0xAA       ;at the address held by EEADR.
        movwf 	EECON2
        bsf 	EECON1,WR   ;set the ``perform write'' flag
        		BANK0

CHKWRT: btfss 	PIR1,EEIF ;wait until bit 4 of PIR2 is set
        goto 	CHKWRT
        bcf 	PIR1,EEIF   ;clear bit 4 of PIR2
        return

;******** READ DATA FROM EEPROM ROUTINE modified for PIC16F62x devices ****
;         the data sheet page 93 is wrong!  This routine here works!

                        ;This routine is entered with W holding
                        ;the eeprom byte address to be read.
PRMGET: 				;BANK1
        movwf 	EEADR     ;copy W into EEADR to set eeprom address
        bsf 	EECON1,RD   ;enable read flag
        movf 	EEDATA,W   ;read eeprom data now in EEDATA into W
        		BANK0
        return

; ***********

CORRECTIT:
        call 	LCD1
        bsf 	RSLINE,4
        btfss 	PORTA,4
        goto 	CAPCRT
        
INDCRT: movlw 	'I'
        call 	LCDOUT
        movlw 	'N'
        call 	LCDOUT
        movlw 	'D'
        call 	LCDOUT
        clrf 	CORRECTLOC
        goto 	CORRECT2

CAPCRT: movlw 	'C'
        call 	LCDOUT
        movlw 	'A'
        call 	LCDOUT
        movlw 	'P'
        call 	LCDOUT
        movlw 	1 
        movwf 	CORRECTLOC

CORRECT2: clrf 	LOOP
CORRECT3: movf 	LOOP,W
        call 	CORRECTMSG
        call 	LCDOUT
        incf 	LOOP,F
        btfss 	LOOP,4
        goto 	CORRECT3

COR3:   call 	SHOWCORRECT
    
COR4:   btfsc 	PORTA,2       ; is S3 (RA2) still pressed?
        goto 	COR4           ; yes
        call 	PAUSIT

COR5:   btfss 	PORTA,2       ; is S3 (RA2) pressed?
        goto 	COR5           ; no

        btfsc 	PORTA,4
        goto 	COR7

COR6:   call 	INCIT
        call 	SHOWCORRECT
        call 	PAUSIT2
        call 	PAUSIT2
        goto 	COR5

COR7:   call 	DECIT
        call 	SHOWCORRECT
        call 	PAUSIT2
        call 	PAUSIT2
        goto 	COR5

INCIT:  movf 	CORRECTLOC,W
        call 	PRMGET
        movwf 	PROMVAL
        addlw 	1
        movwf 	PROMVAL
        xorlw 	200
        btfsc 	STATUS,Z
        decf 	PROMVAL,F
        movf 	CORRECTLOC,W
        call 	SETPRM
        return

DECIT:  movf 	CORRECTLOC,W
        call 	PRMGET
        movwf 	PROMVAL
        decf 	PROMVAL,F
        btfsc 	STATUS,Z
        incf 	PROMVAL,F
        movf 	CORRECTLOC,W
        call 	SETPRM
        return

; **********

SHOWCORRECT:
        movf 	CORRECTLOC,W
        call 	PRMGET
        movwf 	REGAa0
        clrf 	REGAa1
        clrf 	REGAa2
        clrf 	REGAa3

        call 	LCD21
        bsf 	RSLINE,4

        call 	BIN2DEC

        movf 	DIGIT8,W
        call 	LCDOUT
        movf 	DIGIT9,W
        call 	LCDOUT
        movf 	DIGIT10,W
        call 	LCDOUT
        return

; **************

        org 	0x2100             ; data eeprom values

        DE 100,100,0,0,0,0


        END
