AVR Code for Dividing 16 Bits by 8-Bit Constant

Status
Not open for further replies.

Kerim

Member
Hello,

Here is a common AVR assembly code for dividing 16-bit register by any 8-bit constant divisor [2 to 255] with rounding.
By the way, about a year ago, I also shared it at “AVRfreaks forum” in case there are members here who used visiting it (unfortunately, I can’t join it lately, due to world’s regulations).

The speed of this versatile code is not bad [37/33 cycles, max/min; excluding RET and RCALL].
It uses 32 words.
To use this code (posted below), only the constant divisor ‘reg_N’ needs to be changed,

The next thread will be about how this code could be updated to divide 16-Bit register by an 8-Bit register (if its value is not zero).

Kerim

Code:
;==============================================================
; *** Division of 16-bit register {A} by 8-bit constant {N} ***
;==============================================================

; {R}={A}/{N} = r17:r16 / {N} = r17:r16 * {Kd} /256 /256 then rounding

; {A} dividend in r17:r16 [0 to 65535]
; {N} constant divisor [N=2 to 255]
; {R} result in r21:r20 , {A}/{N} rounded
; {Kd} = round(256*256/{N},0), the division constant
; {Kr} = INT[ (1-{N})/2 ]    , the rounding constant
; used registers: r19,r18,r14,r13,r1,r0
; 32 words , excluding RET and RCALL
; 40, 43, or 44 cycles = 33c|36c|37 code + 4c RET + 3c RCALL

.set reg_N = 113                  ; {N}
.set reg_Kd=(2*256*256/reg_N+1)/2 ; {Kd}
.set reg_Kr=(1-reg_N)/2           ; {Kr}

D16_nnn:
    LDI   r18,  low(reg_Kd)     ;1abc
    MOV   r13, r18              ;1abc
    LDI   r18, high(reg_Kd)     ;1abc
    MOV   r14, r18              ;1abc, 4abc
; r14:r13 = reg_Kd

; multiplicand in r17:r16 = {A}
; multiplier   in r14:r13 = {Kd}
; mul. result  in r21:r20:r19:r18
; valid result in r21:r20
    MUL   r17, r14              ;2abc
    MOVW  r21:r20, r1:r0        ;1abc
    MUL   r16, r13              ;2abc
    MOVW  r19:r18, r1:r0        ;1abc
    MUL   r17, r13              ;2abc
    CLR   r13                   ;1abc
    ADD   r19, r0               ;1abc
    ADC   r20, r1               ;1abc
    ADC   r21, r13              ;1abc
    MUL   r14, r16              ;2abc
    ADD   r19, r0               ;1abc
    ADC   r20, r1               ;1abc
    ADC   r21, r13              ;1abc +17abc= 21abc
; {B} = r21:r20 = r17:r16 * r14:r13 /256/256 = {A}*{Kd}/256/256
; {R} = {B} or {B}+1

; for rounding
    LDI   r18, reg_N            ;1abc
    MOV   r13, r18              ;1abc
; r13 = {N}

    MUL   r20, r13              ;2abc
    MOVW  r19:r18, r1:r0        ;1abc
    MUL   r21, r13              ;2abc
    ADD   r19, r0               ;1abc, +8abc= 29abc
; {C} = r19:r18 = {B}*{N} = r21:r20 * r13

; the following conditions were deduced empirically
; if( Carry_1=0, {R}={B}, if({D2}>0, {R}={B}, {R}={B}+1 ) )

    SUB   r18, r16              ;1abc
    SBC   r19, r17              ;1abc
; {D1} = r19:r18 = {C} - {A} = r19:r18 - r17:r16

    BRCC  DIV_ret               ;2a|1bc +4a=[33a]
; if Carry_1=0, {R}={B}

    SUBI  r18,  low(reg_Kr)     ;1bc
    SBCI  r19, high(reg_Kr)     ;1bc
; {D2} = r19:r18 = {D1} - {Kr} = r19:r18 - {Kr}

    BRPL  DIV_ret               ;2b|1c, +7b=[36b]
; if {D2} positive, {R}={B}

    SUBI  r20,  low(-1)         ;1c
    SBCI  r21, high(-1)         ;1c, +8c=[37c]
; {R}={B}+1

DIV_ret:
    RET                         ;4
; {R} = r21:r20 = {B} or {B}+1 [ {A}/{N} rounded ]
 
Last edited:
Status
Not open for further replies.
Cookies are required to use this site. You must accept them to continue using the site. Learn more…