Dividing 24-bit number by 8-bit number with 8051

Thread Starter

testuserabcdef

Joined Jul 12, 2016
127
I'm trying to figure out how to speed up this code for my 8051 processor.
I'm currently using it to divide numbers by 10 so I can pick up digits to directly
print on an LCD. The part that takes the longest is this code. The reason why I'm using
24-bit dividend is because I want to display more than 5 digits for a number, and with
24-bits, I can go up to 8 digits.

Is there a way I can modify all this code so it can execute faster?

I tried removing subtraction lines with stars and that made things worse.

Code:
;DIVIDH/DIVIDM/DIVIDL = 24-bit dividend temporary storage
;DIVISH/DIVISM/DIVISL = 24-bit divisor temporary storage
;DIVTH/DIVTM/DIVTL = 24-bit temporary number
;QUOTH/QUOTM/QOTL = 24-bit quotient
;NUMSPC = number space in memory (uses up to 8 bytes)

;number is 12345678 in decimal
mov R7,#0BCh
mov DPTR,#0614Eh
lcall digits
;returns R0 indicating # of digits and
;NUMSPC to NUMSPC+R0 memory locations filled with the correct digits
sjmp $


digits:
;breaks R7:DPH:DPL (24-bit) to single digits
;storage starting at NUMSPC. Digits are reversed
mov R0,#NUMSPC
moredig:
;divide by 10
mov DIVISH,#0h
mov DIVISM,#0h
mov DIVISL,#0Ah
lcall ldiv
;store remainder and increment memory address
mov @R0,A
inc R0
mov A,DPL
orl A,DPH
orl A,R7
jnz noz1
;R0 = number of digits
mov A,R0
subb A,#NUMSPC
mov R0,A
ret
noz1:
;keep going until quotient = 0
sjmp moredig

;24-bit division
ldiv:
;R7:DPH:DPL = 24-bit dividend in, 24-bit quotient out, R6:A=remainder
;get input + clear quotient, counter and carry
mov DIVIDL,DPL
mov DIVIDM,DPH
mov DIVIDH,R7
mov QUOTL,#0h
mov QUOTM,#0h
mov QUOTH,#0h
clr C/mov R6,#0h
div1:
;Shift 24-bit divisor left until MSB shifted is a 1
mov A,DIVISL
rlc A
mov DIVISL,A
mov A,DIVISM
rlc A
mov DIVISM,A
mov A,DIVISH
rlc A
mov DIVISH,A
inc R6
jnc div1
div2:
;Shift 24-bit divisor right only once
mov A,DIVISH
rrc A
mov DIVISH,A
mov A,DIVISM
rrc A
mov DIVISM,A
mov A,DIVISL
rrc A
mov DIVISL,A
;store dividend as a temporary number
mov DIVTL,DIVIDL/mov DIVTH,DIVIDH/mov DIVTM,DIVIDM
;subtract divisor from dividend
mov A,DIVIDL
subb A,DIVISL 
mov DIVIDL,A
mov A,DIVIDM
subb A,DIVISM ;**
mov DIVIDM,A
mov A,DIVIDH
subb A,DIVISH ;**
mov DIVIDH,A
jnc norevdivid
;restore original dividend if result of subtraction is negative number
mov DIVIDL,DIVTL
mov DIVIDM,DIVTM
mov DIVIDH,DIVTH
norevdivid:
;complement carry so correct bit value gets shifted in result
cpl C
;shift bit into result
mov A,QUOTL
rlc A
mov QUOTL,A
mov A,QUOTM
rlc A
mov QUOTM,A
mov A,QUOTH
rlc A
mov QUOTH,A
;keep going until divisor is shifted back
djnz R6,div2
;R7:DPH:DPL=quotient
mov DPL,QUOTL
mov DPH,QUOTM
mov R7,QUOTH
R6:A=remainder
mov R6,DIVIDM
mov A,DIVIDL
ret
 

MrChips

Joined Oct 2, 2009
30,714
You can speed up the code knowing that the divisor is only one byte.

Here is one possible algorithm.

Initialization
Set 3-byte quotient to zero.
Set a 1-byte divisor (B) to ten.
Set a 1-byte dividend REM to zero.
Set loop counter to 24.

1. Shift left by one bit, 24-bit dividend into REM.
2. Shift left by one bit, 24-bit quotient
3. if REM >= ten, subtract ten from REM, set LSB of quotient to 1.
4. Decrement loop counter.
5. If loop counter is not zero go back to 1, else done.
 

Thread Starter

testuserabcdef

Joined Jul 12, 2016
127
You can speed up the code knowing that the divisor is only one byte.

Here is one possible algorithm.

Initialization
Set 3-byte quotient to zero.
Set a 1-byte divisor (B) to ten.
Set a 1-byte dividend REM to zero.
Set loop counter to 24.

1. Shift left by one bit, 24-bit dividend into REM.
2. Shift left by one bit, 24-bit quotient
3. if REM >= ten, subtract ten from REM, set LSB of quotient to 1.
4. Decrement loop counter.
5. If loop counter is not zero go back to 1, else done.
It seems to work ok with following step 2 after step 3
 

Sensacell

Joined Jun 19, 2012
3,432
Here is a way to convert numbers for display, it uses the "shift and add" technique so it's faster than using a normal divide.
It's PIC code, but you can alter it for 8051 without too much trouble.

It takes a binary word and converts it to BCD which you can send to the display.


Code:
;*******************************************************************
bin_to_bcd
;40 bit binary to 6 packed BCD Digits
;max input = 0F FF FF FF FF
;BIN0-4 is input (binary)
;BCD0-5 is output (10 packed digits)

    MOVLW    .40                ; 40-BITS
    MOVWF    bit_counter        ; MAKE CYCLE COUNTER
    CLRF    BCD0
    CLRF    BCD1
    CLRF    BCD2
    CLRF    BCD3
    CLRF    BCD4
    CLRF    BCD5   
B2BCD2   
    LFSR    FSR0,BCD0        ; MAKE POINTER
    MOVLW    .6
    MOVWF    digit_counter

B2BCD3   
    MOVLW    0X33       
    ADDWF    INDF0,F        ; ADD TO BOTH NYBBLES
    BTFSC    INDF0,3        ; TEST IF LOW RESULT > 7
    ANDLW    0XF0        ; LOW RESULT >7 SO TAKE THE 3 OUT
    BTFSC    INDF0,7        ; TEST IF HIGH RESULT > 7
    ANDLW    0X0F        ; HIGH RESULT > 7 SO OK
    SUBWF    INDF0,F        ; ANY RESULTS <= 7, SUBTRACT BACK
    INCF    FSR0L,F        ; POINT TO NEXT
    DECFSZ    digit_counter
    GOTO    B2BCD3
   
    RLCF    BIN0,F        ; GET ANOTHER BIT
    RLCF    BIN1,F
    RLCF    BIN2,F
    RLCF    BIN3,F
    RLCF    BIN4,F
    RLCF    BCD0,F        ; PUT IT INTO BCD
    RLCF    BCD1,F
    RLCF    BCD2,F
    RLCF    BCD3,F
    RLCF    BCD4,F
    RLCF    BCD5,F
    DECFSZ    bit_counter,F        ; ALL DONE?
    GOTO    B2BCD2                ; NO, LOOP
    RETLW    .0        ; yes


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

Thread Starter

testuserabcdef

Joined Jul 12, 2016
127
Here is a way to convert numbers for display,
It takes a binary word and converts it to BCD which you can send to the display.
My method uses the shift and subtract. the 8052 doesn't have variables to store words except for DPTR, and my objective is to split the decimal digits into separate memory cells.

For example, if a number I'm working with is of value 101h then one cell will contain the number 2, one contains the number 5, and one contains the number 7, because 101h to decimal is 257.
 

MrChips

Joined Oct 2, 2009
30,714
Here is another algorithm.

Count how many times you can subtract 1000000 from the dividend.
Repeat for 100000, 10000, 1000, 100, 10 on the remainders after each step.

The advantage is that digits are extracted high-order first and can be displayed immediately.
 

Thread Starter

testuserabcdef

Joined Jul 12, 2016
127
Here is another algorithm.

Count how many times you can subtract 1000000 from the dividend.
Repeat for 100000, 10000, 1000, 100, 10 on the remainders after each step.

The advantage is that digits are extracted high-order first and can be displayed immediately.
Your first idea seems to work good. For 8 digits, I went down from 5.6ms to about 3.4ms total processing time
 
Top