; Simple 4-digit 0-20 volt DVM. File: dvm.asm
;
; Global Assembler directives
list c=132, n=80
list p=16F873A
errorlevel -302 ; Disable messages for register banking
errorlevel -306 ; Disable messages for page crossing
;
; This program reads a 8:1 scaled voltage on AN0 and displays it's value
; on a multiplexed 4-digit LED display. Uses a 2.5 volt reference.
;
; Copyright (c) 2007 Dan Julio. Some rights reserved.
; Licensed under a Creative Commons Attribution 3.0 License
; Please see http://creativecommons.org/licenses/by/3.0/legalcode
;
; Revision History
; 05/17/07 Revision 1.0 DJJ First pass
;
;
; ------------------------------------------------------------------------
; Processor Information:
;
; Target processor: PIC 16C873A
; Processor frequency: 10 MHz (400 ns instruction cycle)
;
; Memory Layout
; Page 0: 0x0000 - 0x07FF Main code, Interrupt handler, subroutines
; Page 1: 0x0800 - 0x0FFF Not used
;
; Input/Output Pin Assignments (PDIP/SOIC package)
;
; Pin Signal Polarity I/O Default State
; ------------------------------------------------------------------------
; 2 RA0 Analog In - Input 0
; 3 RA1 Reserved - Output 0
; 4 RA2 Vref - In - Input 0
; 5 RA3 Vref + In - Input 0
; 6 RA4 Reserved - Output 0 open drain output
; 7 RA5 Reserved - Output 0
; 21 RB0 Segment 'a' Active Low Output 1
; 22 RB1 Segment 'b' Active Low Output 1
; 23 RB2 Segment 'c' Active Low Output 1
; 24 RB3 Segment 'd' Active Low Output 1
; 25 RB4 Segment 'e' Active Low Output 1
; 26 RB5 Segment 'f' Active Low Output 1
; 27 RB6 Segment 'g' Active Low Output 1
; 28 RB7 Segment 'dp' Active Low Output 1
; 11 RC0 Digit 0 Anode Active High Output 0
; 12 RC1 Digit 1 Anode Active High Output 0
; 13 RC2 Digit 2 Anode Active High Output 0
; 14 RC3 Digit 3 Anode Active High Output 0
; 15 RC4 Reserved - Output 0
; 16 RC5 Reserved - Output 0
; 17 RC6 Reserved - Output 0
; 18 RC7 Reserved - Output 0
;
;
;
; ------------------------------------------------------------------------
; File Layout:
;
; 1. Configuration Bits
; 2. Program Constants
; 3. Macro definitions
; 4. Data EEPROM assembler initialization directives
; 5. Main code
; 6. Main code subroutines
; 7. Interrupt routines
; 8. Interrupt subroutines
;
;
; ------------------------------------------------------------------------
; Configuration Bits
_CP_ALL EQU H'0FCF'
_CP_HALF EQU H'1FDF'
_CP_UPPER_256 EQU H'2FEF'
_CP_OFF EQU H'3FFF'
_DEBUG_ON EQU H'37FF'
_DEBUG_OFF EQU H'3FFF'
_WRT_ENABLE_ON EQU H'3FFF'
_WRT_ENABLE_OFF EQU H'3DFF'
_CPD_ON EQU H'3EFF'
_CPD_OFF EQU H'3FFF'
_LVP_ON EQU H'3FFF'
_LVP_OFF EQU H'3F7F'
_BODEN_ON EQU H'3FFF'
_BODEN_OFF EQU H'3FBF'
_PWRTE_OFF EQU H'3FFF'
_PWRTE_ON EQU H'3FF7'
_WDT_ON EQU H'3FFF'
_WDT_OFF EQU H'3FFB'
_LP_OSC EQU H'3FFC'
_XT_OSC EQU H'3FFD'
_HS_OSC EQU H'3FFE'
_RC_OSC EQU H'3FFF'
; Set the device configuration bits
; CP1:CP0 = 11 (code protection off)
; DEBUG = 1 (RB6/7 are general purpose IO pins)
; WRT = 1 (unprotected program memory may be written)
; CPD = 1 (Data EE code protection off)
; LVP = 0 (RB3 is digital IO)
; BODEN = 1 (Brown-out reset enabled)
; PWRTE_ = 1 (Power-up Timer enabled)
; WDTE = 1 (WDT enabled)
; FOSC1:0 = 10 (High-speed osc)
__config _HS_OSC & _LVP_OFF & _WRT_ENABLE_OFF & _BODEN_ON & _PWRTE_ON
; ------------------------------------------------------------------------
; Program Constants
;
; PIC Internal register equates
W equ .0
F equ .1
; Bank 0 and registers in all banks
INDF equ 0x0
TMR0 equ 0x1
PCL equ 0x2
STATUS equ 0x3
FSR equ 0x4
PORT_A equ 0x5
PORT_B equ 0x6
PORT_C equ 0x7
PCLATH equ 0xA
INTCON equ 0xB
PIR1 equ 0xC
PIR2 equ 0xD
TMR1L equ 0xE
TMR1H equ 0xF
T1CON equ 0x10
TMR2 equ 0x11
T2CON equ 0x12
SSPBUF equ 0x13
SSPCON equ 0x14
CCPR1L equ 0x15
CCPR1H equ 0x16
CCP1CON equ 0x17
RCSTA equ 0x18
TXREG equ 0x19
RCREG equ 0x1A
CCPR2L equ 0x1B
CCPR2H equ 0x1C
CCP2CON equ 0x1D
ADRESH equ 0x1E
ADCON0 equ 0x1F
; Bank 1 specific
OPTION_REG equ 0x1
TRISA equ 0x5
TRISB equ 0x6
TRISC equ 0x7
PIE1 equ 0xC
PIE2 equ 0xD
PCON equ 0xE
SSPCON2 equ 0x11
PR2 equ 0x12
SSPADD equ 0x13
SSPSTAT equ 0x14
TXSTA equ 0x18
SPBRG equ 0x19
ADRESL equ 0x1E
ADCON1 equ 0x1F
; Bank 2 specific
EEDATA equ 0xC
EEADR equ 0xD
EEDATH equ 0xE
EEADRH equ 0xF
; Bank 3 specific
EECON1 equ 0xC
EECON2 equ 0xD
;
; PIC Internal STATUS register bits
CARRY equ .0
DC equ .1
Z equ .2
PD equ .3
TO equ .4
RP0 equ .5
RP1 equ .6
IRP equ .7
;
; Interrupt sources in the INTCON register
RBIF_INT equ .0
INTF_INT equ .1
TMR0_INT equ .2
;
; Interrupt enable bits in the INTCON register
RBIE equ .3
INTE equ .4
T0IE equ .5
PEIE equ .6
GIE equ .7
;
; Interrupt sources in the PIR1 register
TMR1IF equ .0
TMR2IF equ .1
CCP1IF equ .2
SSPIF equ .3
TXIF equ .4
RCIF equ .5
ADIF equ .6
PSPIF equ .7
;
; Status/Control bits in the RCSTA register
OERR equ .1
FERR equ .2
CREN equ .4
;
; Status/Control bits in the ADCON0
ADC_GO equ .2
ADC_ON equ .0
;
; EECON1 control bits
RD equ .0
WR equ .1
WREN equ .2
WRERR equ .3
EEPGD equ .7
;
; Flags variable bits
Trigger equ .0 ; Set to trigger a calculation and display update
;
; Variable register equates
TempData equ 0x20 ; Scratch register
Digit0 equ 0x21 ; BCD value for digit 0 (values > 9 blank display)
Digit1 equ 0x22
Digit2 equ 0x23
Digit3 equ 0x24
Flags equ 0x26
ReadCounter equ 0x27 ; Counts ADC slots to trigger a ADC reading
DigitCount equ 0x28 ; 0-4 (0-3-> digit, 4-> possible ADC reading slot)
;
; Math routine
ACCaHI equ 0x30 ; Operand 1 hi
ACCaLO equ 0x31 ; Operand 1 lo
ACCbHI equ 0x32 ; Operand 2 hi / Result hi
ACCbLO equ 0x33 ; Operand 2 lo / Result lo
ACCcHI equ 0x34 ; Result hi
ACCcLO equ 0x35 ; Result lo
ACCdHI equ 0x36 ; Temporary operand / Result hi
ACCdLO equ 0x37 ; Temporary operand / Result lo
ACCeHI equ 0x38 ; Temporary operand
ACCeLO equ 0x39 ; Temporary operand
ACCfHI equ 0x3A ; Temporary operand
ACCfLO equ 0x3B ; Temporary operand
;
; Additional temp registers for math routines
ACCtemp equ 0x3C
;
; Interrupt state save registers
FSR_Temp equ 0x7C
STATUS_Temp equ 0x7D
PCLATH_Temp equ 0x7E
W_Temp equ 0x7F ; Note, shadowed in bank 1 as well (see PIC16F87X spec 12.11)
;
;
; Initialization constants
STATUS_SETUP equ B'00011000' ; 7: IRP = 0 (page 0-1)
; 6:5: RP1:0 = 00b (page 0)
; 4: TO = 1 (default state)
; 3: PD = 1 (default state)
; 2: Z = 0
; 1: DC = 0
; 0: C = 0
OPTION_SETUP equ B'11000100' ; 7: RBPU = 1 (disable pull-ups)
; 6: INTEDG = 1 (Int on rising edge)
; 5: T0CS = 0 (internal TMR0 clock)
; 4: T0SE = 0 (rising TMR0 clock)
; 3: PSA = 0 (prescaler to TMR0)
; 2:0: prescaler = 100b (1:32)
INTCON_SETUP equ B'10100000' ; 7: GIE = 1 (interrupts enabled)
; 6: PEIE = 0 (Peripheral ints disabled)
; 5: T0IE = 1 (TMR0 ints enabled)
; 4: INTE = 0 (INT ints disabled)
; 3: RBIE = 0 (RB ints disabled)
; 2: T0IF = 0
; 1: INTF = 0
; 0: RBIF = 0
PIE1_SETUP equ B'00000000' ; 7: PSPIE = 0 (Parallel Slave)
; 6: ADIE = 0 (A/D converter)
; 5: RCIE = 0 (USART RX Enable)
; 4: TXIE = 0 (USART TX Enable)
; 3: SSPIE = 0 (Sync Serial Port)
; 2: CCP1IE = 0 (CCPQ Int enable)
; 1: TMR2IE = 0 (TMR2 to PR2 match)
; 0: TMR1IE = 0 (TMR1 Overflow)
PIE2_SETUP equ B'00000000' ; 7: Unimplemented
; 6: Reserved
; 5: Unimplemented
; 4: EEIE = 0 (EEPROM Write)
; 3: BCLIE = 0 (Bus collision)
; 2: Unimplemented
; 1: Unimplemented
; 0: CCP2IE = 0 (CCP2 Int Enable)
PORTA_INIT equ B'00000000' ; No outputs enabled
PORTB_INIT equ B'11111111' ; No outputs enabled
PORTC_INIT equ B'00000000' ; No outputs enabled
PORTA_OE equ B'00001101' ; RA[5:4], RA[1] outputs
PORTB_OE equ B'00000000' ; All outputs
PORTC_OE equ B'00000000' ; All outputs
TXSTA_INIT equ B'00000100' ; 7: CSRC = 0 (Don't care)
; 6: TX9 = 0 (8-bit transmission)`
; 5: TXEN = 0 (transmit disabled)
; 4: SYNC = 0 (asynch mode)
; 3: Reserved
; 2: BRGH = 1 (high speed mode)
; 1: TRMT = 0
; 0: TX9D = 0 (9th xmit data)
RCSTA_INIT equ B'00010000' ; 7: SPEN = 0 (Serial port disabled)
; 6: RX9 = 0 (8-bit reception)
; 5: SREN = 0 (Don't care)
; 4: CREN = 1 (enable continuous)
; 3: ADDEN = 0 (disable addr detection)
; 2: FERR = 0
; 1: OERR = 0
; 0: RX9D = 0
ADCON0_EN_CH0 equ B'10000001' ; 7:6: ADCS[1:0] = 10 (Fosc/32)
; 5:3: CHS[2:0] = 000 (Channel 0)
; 2: GO/~DONE = 0
; 1: Reserved
; 0: ADON = 1 (ADC on)
ADCON1_INIT equ B'10001111' ; 7: ADFM = 1 (Right justify results)
; 6:4: Reserved
; 3:0: PCFG3:0 = 1111
; (RA5:4,1 digital ports)
;
; Global constants
TMR0_PRESET equ .256 - .156 ; 156 x 32 = 4992 cycles => approx 500 Hz int rate
ADC_READ_COUNT equ .33 ; Number of 100 Hz intervals between ADC readings
NUM_DIGITS equ .4 ; Number of multiplexed digits
NUM_SLOTS equ NUM_DIGITS+.1 ; Add slot for ADC readings
;
; Digit Enable Masks
EN_NO_DIGITS equ 0x00
EN_DIGIT0 equ 0x01
EN_DIGIT1 equ 0x02
EN_DIGIT2 equ 0x04
EN_DIGIT3 equ 0x08
;
; Segment combinations
CHAR_0 equ ~B'00111111'
CHAR_1 equ ~B'00000110'
CHAR_2 equ ~B'01011011'
CHAR_3 equ ~B'01001111'
CHAR_4 equ ~B'01100110'
CHAR_5 equ ~B'01101101'
CHAR_6 equ ~B'01111101'
CHAR_7 equ ~B'00000111'
CHAR_8 equ ~B'01111111'
CHAR_9 equ ~B'01101111'
CHAR_B equ ~B'00000000' ; Blank display
; ------------------------------------------------------------------------
; Macro Definitions
;
SET_DIGIT macro Val
movf PORT_C,W
andlw 0xF0
iorlw Val
movwf PORT_C
endm
BLANK_DIGITS macro
movf PORT_C,W
andlw 0xF0
movwf PORT_C
endm
; ------------------------------------------------------------------------
; Program EEPROM Initialization
;
; ------------------------------------------------------------------------
; Data EEPROM Initialization
;
; ------------------------------------------------------------------------
; Code Block
;
; Reset Vector
goto MAIN_VECTOR
; ------------------------------------------------------------------------
; Interrupt Code Block
org 0x4
; ISR: Executed each time TIMER0 counts down, approximately 500 Hz. Each time
; through this code we process one Digit or we read the ADC:
;
; Digit0 - Digit1 - Digit2 - Digit3 - Read ADC
;
; resulting in a display update of about 100 Hz. The Read ADC activity is done
; only every ADC_READ_COUNT times since we don't need to
;
; On entry: DigitCount indicates which digit (or ADC slot) to execute
; Digit3:0 contain 7-segment data for each digit
; ReadCounter contains current ADC slot timer count value
;
; On exit: Flags.Trigger set when the ADC has been read
; TMR0 setup to countdown to next interrupt
;
; Save interrupt state
movwf W_Temp
swapf STATUS,W
clrf STATUS
movwf STATUS_Temp
movf PCLATH,W
movwf PCLATH_Temp
clrf PCLATH
; Re-Setup TMR0
movlw TMR0_PRESET
movwf TMR0
; Jump to code based on DigitCount
movf DigitCount,W
andlw 0x07
addwf PCL,F
goto HANDLE_DIGIT0
goto HANDLE_DIGIT1
goto HANDLE_DIGIT2
goto HANDLE_DIGIT3
goto HANDLE_ADC
goto ISR_RESTORE ; Should never be reached...
goto ISR_RESTORE
goto ISR_RESTORE
HANDLE_DIGIT0
; Don't need to do inner-digit blank here
SET_DIGIT EN_DIGIT0
movf Digit0,W
call Calc7segment
movwf PORT_B
goto SETUP_NEXT_DIGIT
HANDLE_DIGIT1
BLANK_DIGITS
SET_DIGIT EN_DIGIT1
movf Digit1,W
call Calc7segment
movwf PORT_B
goto SETUP_NEXT_DIGIT
HANDLE_DIGIT2
BLANK_DIGITS
SET_DIGIT EN_DIGIT2
movf Digit2,W
call Calc7segment
andlw 0x7F ; Set the decimal point
movwf PORT_B
goto SETUP_NEXT_DIGIT
HANDLE_DIGIT3
BLANK_DIGITS
SET_DIGIT EN_DIGIT3
movf Digit3,W
call Calc7segment
movwf PORT_B
goto SETUP_NEXT_DIGIT
HANDLE_ADC
BLANK_DIGITS
decfsz ReadCounter,F ; Time to read the ADC?
goto SETUP_NEXT_DIGIT ; N: Done for now
movlw ADC_READ_COUNT ; Y: Reset for next time and read ADC
movwf ReadCounter
bsf Flags,Trigger ; Note to do calculations in the main loop
; Trigger the ADC
bsf ADCON0,ADC_GO
GET_ANALOG_VAL_POLL
btfsc ADCON0,ADC_GO
goto $-1
SETUP_NEXT_DIGIT
incf DigitCount,F ; Next Digit
movf DigitCount,W
sublw NUM_SLOTS ; Reset if DigitCount = NUM_SLOTS
btfsc STATUS,Z
clrf DigitCount
ISR_RESTORE
; Finally, clear this interrupt before returning
bcf INTCON,TMR0_INT
; Restore interrupt state
movf PCLATH_Temp,W
movwf PCLATH
swapf STATUS_Temp,W
movwf STATUS
swapf W_Temp,F
swapf W_Temp,W
; Return from the interrupt (and re-enable interrupts)
retfie
; ------------------------------------------------------------------------
; Main Code Block
;
MAIN_VECTOR
; Initialize unique PIC control registers in page 0
clrf TMR0
clrf FSR
clrf PCLATH
movlw STATUS_SETUP
movwf STATUS
movlw PORTA_INIT
movwf PORT_A
movlw PORTB_INIT
movwf PORT_B
movlw PORTC_INIT
movwf PORT_C
movlw RCSTA_INIT
movwf RCSTA
movlw ADCON0_EN_CH0
movwf ADCON0
; Initialize unique PIC control registers in page 1
bsf STATUS,RP0 ; Point to page 1 registers
movlw OPTION_SETUP
movwf OPTION_REG
movlw PORTA_OE
movwf TRISA
movlw PORTB_OE
movwf TRISB
movlw PORTC_OE
movwf TRISC
movlw TXSTA_INIT
movwf TXSTA
movlw PIE1_SETUP
movwf PIE1
movlw PIE2_SETUP
movwf PIE2
movlw ADCON1_INIT
movwf ADCON1
bcf STATUS,RP0 ; Point to page 0 registers again
; Initialize unique PIC control registers in page 2
bsf STATUS,RP1 ; Point to page 2 registers
clrf EEADRH
clrf EEDATH
bcf STATUS,RP1 ; Point to page 0 registers again
; Initialize variables
clrf Digit0
clrf Digit1
clrf Digit2
clrf Digit3
clrf Flags
movlw ADC_READ_COUNT
movwf ReadCounter
clrf DigitCount
movlw TMR0_PRESET
movwf TMR0
; Finally, enable interrupts before starting normal operation
movlw INTCON_SETUP
movwf INTCON
MAIN_LOOP
; Main loop: wait for a trigger then update the measured voltage
; reading and digit variables
clrwdt ; Clear the watchdog timer
; Check for a trigger from the last ADC read in the ISR
btfss Flags,Trigger
goto MAIN_LOOP
bcf Flags,Trigger
; For four digits in the for II.FF with a range of 0-20.00 volts:
; Ideal Voltage = (AdcCount * 2000) / 1023
; Calculate AdcCount * 2000
movf ADRESH,W
movwf ACCaHI
bsf STATUS,RP0
movf ADRESL,W
bcf STATUS,RP0
movwf ACCaLO
; Check for special case of AdcCount = 0
movf ACCaHI,W
btfss STATUS,Z
goto MAIN_DO_CALC
movf ACCaLO,W
btfsc STATUS,Z
goto MAIN_ADC_EQ_0
MAIN_DO_CALC
movlw (.2000 >> 8) & 0xFF
movwf ACCbHI
movlw .2000 & 0xFF
movwf ACCbLO
call mul32 ; Results in {ACCb, ACCc}
; Calculate divide by 1023
movlw (.1023 >> 8) & 0xFF
movwf ACCaHI
movlw .1023 & 0xFF
movwf ACCaLO
call div32 ; Result in {ACCb, ACCc} remainder ACCd
; ACCb should be 0 (ACCc <= 2000)
; Round the result in {ACCb, ACCc} up if ACCd > 511
movlw (.511 >> 8) & 0xFF
movwf ACCbHI
movlw .511 & 0xFF
movwf ACCbLO
movf ACCdHI,W
movwf ACCaHI
movf ACCdLO,W
movwf ACCaLO
call comp16
addlw 0 ; Update Z: W = 1 if ACCd > 511
btfsc STATUS,Z ; Z = 0? => W = 1
goto MAIN_CONVERT_TO_BCD
incf ACCcLO,F
btfsc STATUS,Z ; rollover?
incf ACCcHI,F
goto MAIN_CONVERT_TO_BCD
MAIN_ADC_EQ_0
clrf ACCcHI
clrf ACCcLO
MAIN_CONVERT_TO_BCD
; Convert to BCD
movf ACCcHI,W
movwf ACCaHI
movf ACCcLO,W
movwf ACCaLO
call bin2bcd ; Results in {ACCdLO[3:0], ACCcHI, ACCcLO}
; Move packed BCD to digits and do leading 0 blanking
movf ACCbLO,W
andlw 0x0F
movwf Digit0
swapf ACCbLO,W
andlw 0x0F
movwf Digit1
movf ACCbHI,W
andlw 0x0F
movwf Digit2
swapf ACCbHI,W
andlw 0x0F
btfsc STATUS,Z ; Digit 3 = 0?
movlw 0xF ; Y: Force this digit to be blanked
movwf Digit3
goto MAIN_LOOP
; ------------------------------------------------------------------------
; Start of Page 0 subroutines
;
; Calc7segment: Return segment enable bits in W
;
; On entry: W contains the BCD value
;
; On exit: W contains segment data
;
Calc7segment
andlw 0x0F
addwf PCL,F
retlw CHAR_0
retlw CHAR_1
retlw CHAR_2
retlw CHAR_3
retlw CHAR_4
retlw CHAR_5
retlw CHAR_6
retlw CHAR_7
retlw CHAR_8
retlw CHAR_9
retlw CHAR_B
retlw CHAR_B
retlw CHAR_B
retlw CHAR_B
retlw CHAR_B
retlw CHAR_B
; sub16, add16: 16-bit unsigned subtraction and addition routines. Taken from
; Microchip AN-00526. These routines implement 16-bit unsigned operations.
;
; On entry: ACCbHI, ACCbLO contain operand 1
; ACCaHI, ACCaLO contain operand 2
;
; On exit: ACCbHI, ACCbLO contain the result, either ACCb - ACCa or
; ACCb + ACCa
;
; Notes: These routines are together because sub16 uses add16
;
; Timing: sub16 requires 13 cycles
; add16 requires 8 cycles
;
sub16
; Negate ACCa
comf ACCaLO,F
incf ACCaLO,F
btfsc STATUS,Z
decf ACCaHI,F
comf ACCaHI,F
; Then add (ACCb + negated ACCa)
add16
movf ACCaLO,W
addwf ACCbLO,F ; add lsb
btfsc STATUS,CARRY ; add in carry
incf ACCbHI,F
movf ACCaHI,W
addwf ACCbHI,F ; add msb
return
; comp16: Return true if B=A
; W = 1 (true) if B=A
; Negate ACCa
comf ACCaLO,F
incf ACCaLO,F
btfsc STATUS,Z
decf ACCaHI,F
comf ACCaHI,F
; Then add (ACCb + negated ACCa) with carry out
movf ACCaLO,W
addwf ACCbLO,F ; Add lsb
btfss STATUS,CARRY ; Carry out of lsb addition?
goto COMP16_MSB_ADD ; N: Just add msb
incfsz ACCbHI,F ; Y: Add carry to msb, check for rollover?
goto COMP16_MSB_ADD ; N: Then add msb
bsf STATUS,CARRY ; Y: There will be carry out of msb,
movf ACCaHI,W ; Can just set ACCbHI = ACCaHI
movwf ACCbHI ; since ACCbHI = 0 and leave C set
goto COMP16_CHECK_C
COMP16_MSB_ADD
movf ACCaHI,W
addwf ACCbHI,F
COMP16_CHECK_C
; Check the CARRY out of the 16-bit subtraction
btfsc STATUS,CARRY ; CARRY = 1 => B>=A (CARRY = 0 => B ACCb)
addwf ACCbLO,F
btfsc STATUS,CARRY ; add in carry
incf ACCbHI,F
movf ACCaHI,W
addwf ACCbHI,F ; add msb
MUL_UPDATE_RES
rrf ACCbHI,F
rrf ACCbLO,F
rrf ACCcHI,F
rrf ACCcLO,F
decfsz ACCtemp,F
goto MUL_32_LOOP
return
; div32: Divide routine for a 32/16 = 16 bit result with 16 bit remainder. Taken
; from a routine in Microchip AN-00526. This routine implements an unsigned divide.
;
; On entry: ACCaHI, ACCaLO contain the denominator
; {ACCbHI, ACCbLO, ACCcHI, ACCcLO} contain the numerator
;
; On exit: 32-bit result in {ACCbHI, ACCbLO, ACCcHI, ACCcLO} remainder in {ACCcHI, ACCcLO}
;
; Notes: The numerator must be >= denominator
;
; Timing: Requires 721 - 945 cycles
;
div32
; Setup
movlw .32 ; setup shift counter
movwf ACCtemp
movf ACCbHI,W ; move ACCb to ACCf
movwf ACCfHI
movf ACCbLO,W
movwf ACCfLO
movf ACCcHI,W ; move ACCc to ACCe
movwf ACCeHI
movf ACCcLO,W
movwf ACCeLO
clrf ACCbHI
clrf ACCbLO
clrf ACCcHI
clrf ACCcLO
clrf ACCdHI
clrf ACCdLO
DIV_32_LOOP
bcf STATUS,CARRY
rlf ACCeLO,F
rlf ACCeHI,F
rlf ACCfLO,F
rlf ACCfHI,F
rlf ACCdLO,F
rlf ACCdHI,F
movf ACCaHI,W
subwf ACCdHI,W ; check if a>d
btfss STATUS,Z
goto DIV32_NO_CHK
movf ACCaLO,W
subwf ACCdLO,W ; if msb equal then check lsb
DIV32_NO_CHK
btfss STATUS,CARRY ; carry set if d>a
goto DIV32_NOGO
movf ACCaLO,W ; d-a into d
subwf ACCdLO,F
btfss STATUS,CARRY
decf ACCdHI,F
movf ACCaHI,W
subwf ACCdHI,F
bsf STATUS,CARRY ; shift a 1 into b (result)
DIV32_NOGO
rlf ACCcLO,F
rlf ACCcHI,F
rlf ACCbLO,F
rlf ACCbHI,F
decfsz ACCtemp,F ; loop until all bits checked
goto DIV_32_LOOP
return
; bin2bcd: Convert a 16-bit binary number into a 5-digit packed BCD result. Taken
; from Microchip AN526.
;
; On entry: {ACCaHI, ACCaLO} contain the number to convert
;
; On exit: {ACCcLO[3:0], ACCbHI, ACCbLO} contain the converted result
;
; Timing: Requires 935 cycles
;
bin2bcd
bcf STATUS,CARRY
movlw .16
movwf ACCtemp
clrf ACCbLO
clrf ACCbHI
clrf ACCcLO
bin2bcdloop
rlf ACCaLO, F
rlf ACCaHI, F
rlf ACCbLO, F
rlf ACCbHI, F
rlf ACCcLO, F
decfsz ACCtemp,F
goto adjDEC
retlw 0
adjDEC
movlw ACCbLO
movwf FSR
call adjBCD
movlw ACCbHI
movwf FSR
call adjBCD
movlw ACCcLO
movwf FSR
call adjBCD
goto bin2bcdloop
adjBCD
movlw .3
addwf INDF,W
movwf TempData
btfsc TempData,.3 ; test if result > 7
movwf INDF
movlw 0x30
addwf INDF,W
movwf TempData
btfsc TempData,.7 ; test if result > 7
movwf INDF ; save as MSD
retlw 0
end