;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 $03,5
#DEFINE BANK1 BSF $03,5

        List P=PIC16F628, R=DEC; 
        __CONFIG   h'3F21'

        include P16F628.inc

        CBLOCK
REGA0				;lsb
REGA1
REGA2
REGA3				;msb

REGB0				;lsb
REGB1
REGB2
REGB3				;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 $70   ; 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 %00110011 ;initialise lcd - first byte
        retlw %00110011 ;2nd byte (repeat of first)
        retlw %00110010 ;set for 4-bit operation
        retlw %00101100 ;set for 2 lines
        retlw %00000110 ;set entry mode to increment each address
        retlw %00001100 ;set display on, cursor off, blink off
        retlw %00000001 ;clear display
        retlw %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 $07
        movwf CMCON

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

        clrf INTCON
        call PAUSIT
        call LCDSET
        call PAUSIT

        movlw %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 %00010000
        xorwf SWITCH,W
        btfsc STATUS,Z
        goto MAIN2

        movf PORTA,W
        andlw %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 %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 %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 %00000010  ; set for correct osc
        movwf PORTA

        clrf TIMEMSB
        call INDTIMER

        call COPY_TIME_REGA

        clrf POINT
        clrf LARGE

        movf TMR1H,W
        andlw %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 %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 %01000000
        movwf STORE         ; store it

        clrf SLOWIT
        bsf T1CON,0         ; start timer 1

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

WAITB2: movf PORTB,W        ; get current status of RB6
        andlw %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 REGA0,W        ; copy answer back into TIMER
        movwf TMR1L
        movf REGA1,W
        movwf TMR1H
        movf REGA2,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 %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 %00001111
        btfsc STATUS,Z
        return
        bsf NANO,0
        movlw 7
        movwf POINT
        return

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

CALIBCAP: movf REGA0,W
        movwf CAPREF0
        movf REGA1,W
        movwf CAPREF1
        movf REGA2,W
        movwf CAPREF2
        movf REGA3,W
        movwf CAPREF3
        call SHOWCALIB
        return

CALIBIND: movf REGA0,W
        movwf INDREF0
        movf REGA1,W
        movwf INDREF1
        movf REGA2,W
        movwf INDREF2
        movf REGA3,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 REGB3
        movf CAPREF2,W
        movwf REGB2
        movf CAPREF1,W
        movwf REGB1
        movf CAPREF0,W
        movwf REGB0
        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 REGB3
        movf INDREF2,W
        movwf REGB2
        movf INDREF1,W
        movwf REGB1
        movf INDREF0,W
        movwf REGB0
        call SUBTRACT
        movf STATUS,W
        andlw 1
        movwf OVERFLOW
        return

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

INDTIMER: movlw %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 %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 %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 %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 %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 %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 %00010000
        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 %01000000
        movwf STORE2
        xorwf STORE,W
        btfsc STATUS,Z
        goto OSCA2          ; yes
        movlw %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 %10000000
        goto LCDLIN
LCD5:   movlw %10000101
        goto LCDLIN
LCD6:   movlw %10000110
        goto LCDLIN
LCD8:   movlw %10001000
        goto LCDLIN
LCD9:   movlw %10001001
        goto LCDLIN
LCD13:  movlw %10001101
        goto LCDLIN
LCD15:  movlw %10001111
        goto LCDLIN

LCD21:  movlw %11000000
        goto LCDLIN
LCD25:  movlw %11000101
        goto LCDLIN
LCD26:  movlw %11000110
        goto LCDLIN
LCD28:  movlw %11001000
        goto LCDLIN
LCD29:  movlw %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 REGA2
        movf TMR1H,W
        movwf REGA1
        movf TMR1L,W
        movwf REGA0
        clrf REGA3
        return

; *********

MULTIPLYSMALL:
        movwf REGB0
        clrf REGB1
        clrf REGB2
        clrf REGB3
        call MULTIPLY
        movf STATUS,W
        andlw 1
        movwf OVERFLOW
        return

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

DIVIDESMALL:
        movwf REGB0
        clrf REGB1
        clrf REGB2
        clrf REGB3
        call DIVIDE
        movf STATUS,W
        andlw 1
        movwf OVERFLOW
        return

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

DIVIDEBILLION:
        movlw $3B      ; divide 1,000,000,000 ($3B9ACA00) by the answer
        movwf REGA3
        movlw $9A
        movwf REGA2
        movlw $CA
        movwf REGA1
        movlw $00
        movwf REGA0
        call DIVIDE
        movf STATUS,W
        andlw 1
        movwf OVERFLOW
        return

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

COPY_REGA_REGB:
        movf REGA0,W    
        movwf REGB0
        movf REGA1,W
        movwf REGB1
        movf REGA2,W
        movwf REGB2
        movf REGA3,W
        movwf REGB3
        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	REGA3,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	REGB0,w		;Trap division by zero
	iorwf	REGB1,w
	iorwf	REGB2,w
	iorwf	REGB3,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	REGB3,w		;Test if remainder (REGC) >= divisor (REGB)
	subwf	REGC3,w
	skpz
	goto	dtstgt
	movf	REGB2,w
	subwf	REGC2,w
	skpz
	goto	dtstgt
	movf	REGB1,w
	subwf	REGC1,w
	skpz
	goto	dtstgt
	movf	REGB0,w
	subwf	REGC0,w
dtstgt	skpc			;Carry set if remainder >= divisor
	goto	dremlt

	movf	REGB0,w		;Subtract divisor (REGB) from remainder (REGC)
	subwf	REGC0,f
	movf	REGB1,w
	skpc
	incfsz	REGB1,w
	subwf	REGC1,f
	movf	REGB2,w
	skpc
	incfsz	REGB2,w
	subwf	REGC2,f
	movf	REGB3,w
	skpc
	incfsz	REGB3,w
	subwf	REGC3,f
	clrc
	bsf	REGA0,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	REGA3,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	REGB0,f
	rlf	REGB1,f
	rlf	REGB2,f
	rlf	REGC0,f
	rlf	REGC1,f
	rlf	REGC2,f
	rlf	REGC3,f
	rlf	REGB0,f
	rlf	REGB1,f
	rlf	REGB2,f

	setc			;Add 1 to root
	rlf	REGA0,f		;Align root
	rlf	REGA1,f
	rlf	REGA2,f

	movf	REGA2,w		;Test if remdr (REGB) >= root (REGA)
	subwf	REGB2,w
	skpz
	goto	ststgt
	movf	REGA1,w
	subwf	REGB1,w
	skpz
	goto	ststgt
	movf	REGA0,w
	subwf	REGB0,w
ststgt	skpc			;Carry set if remdr >= root
	goto	sremlt

	movf	REGA0,w		;Subtract root (REGA) from remdr (REGB)
	subwf	REGB0,f
	movf	REGA1,w
	skpc
	incfsz	REGA1,w
	subwf	REGB1,f
	movf	REGA2,w
	skpc
	incfsz	REGA2,w
	subwf	REGB2,f
	bsf	REGA0,1		;Set current root bit

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

	clrc
	rrf	REGA2,f		;Adjust root alignment
	rrf	REGA1,f
	rrf	REGA0,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	REGA0,f		;Shift msb into carry
	rlf	REGA1,f
	rlf	REGA2,f
	rlf	REGA3,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	REGA3,w
	skpc
	return			;Positive

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

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

	comf	REGA0,f		;2's complement
	comf	REGA1,f
	comf	REGA2,f
	comf	REGA3,f
	incfsz	REGA0,f
	goto	nega1
	incfsz	REGA1,f
	goto	nega1
	incfsz	REGA2,f
	goto	nega1
	incf	REGA3,f
nega1
	incf	MTEMP,f		;flip sign flag
	addwf	REGA3,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	REGA0,f
	rlf	REGA1,f
	rlf	REGA2,f
	rlf	REGA3,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	REGB3,w
	skpc
	return			;Positive

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

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

	comf	REGB0,f		;2's complement
	comf	REGB1,f
	comf	REGB2,f
	comf	REGB3,f
	incfsz	REGB0,f
	goto	negb1
	incfsz	REGB1,f
	goto	negb1
	incfsz	REGB2,f
	goto	negb1
	incf	REGB3,f
negb1
	incf	MTEMP,f		;flip sign flag
	addwf	REGB3,w		;Return carry set if -2147483648
	return

movac	movf	REGA0,w
	movwf	REGC0
	movf	REGA1,w
	movwf	REGC1
	movf	REGA2,w
	movwf	REGC2
	movf	REGA3,w
	movwf	REGC3
	return

;Clear REGB and REGA
;Used by sqrt

clrba	clrf	REGB0
	clrf	REGB1
	clrf	REGB2
	clrf	REGB3

;Clear REGA
;Used by multiply, sqrt

clra	clrf	REGA0
	clrf	REGA1
	clrf	REGA2
	clrf	REGA3
	return

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

addba	movf	REGB0,w		;Add lo byte
	addwf	REGA0,f

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

	movf	REGB2,w		;Add mid-hi byte
	skpnc
	incfsz	REGB2,w
	addwf	REGA2,f

	movf	REGB3,w		;Add hi byte
	skpnc
	incfsz	REGB3,w
	addwf	REGA3,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	REGA3,w		;Compare signs
	xorwf	REGB3,w
	movwf	MTEMP

	call	addba		;Add REGB to REGA

	clrc			;Check signs
	movf	REGB3,w		;If signs are same
	xorwf	REGA3,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 %00000100
        movwf OVERFLOW

TIMEEND: movlw %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 $55       ;these lines cause the action required by
        movwf EECON2    ;by the eeprom to store the data in EEDATA
        movlw $AA       ;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 REGA0
        clrf REGA1
        clrf REGA2
        clrf REGA3

        call LCD21
        bsf RSLINE,4

        call BIN2DEC

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

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

        .org $2100             ; data eeprom values

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


        END
