Search code examples
assemblydivisionsubtractionmasm32

How to fix this code in order to perform "Division by using Subtracting"


My program's supposed to perform division by using subtraction. However, I'm getting nonsense results as output. Could someone help me with my procedure and the loop within? Thank you.

div proc
pushad

mov ecx,firstInt
mov ebx,0
subtracting:
    sub ebx,secondInt
loop subtracting
mov divResult,ebx
popad
ret
divt endp

Solution

  • You continu subtracting for as long as there is no borrow. If a borrow happened, you have the count:

        mov     ecx, firstInt
        mov     ebx, -1
    subtracting:
        inc     ebx
        sub     ecx, secondInt
        jnb     subtracting
        mov     divResult, ebx
    

    EDIT

    What the above code still lacks is checking if the divider is zero. If we allow a zero-divider the code will run forever because subtracting zero will never produce a borrow!

    This simple solution gets the job done, but it is unacceptably slow for big quotients. This calls for a better solution without betraying of course the task of "Division by using subtracting" aka "Don't use div (or any similar instruction)".

    In order to mimic the div instruction:

    divide:
        mov     eax, firstInt
        xor     edx, edx
        div     secondInt       ; -> EDX is remainder, EAX is quotient
    

    we can write:

    simple:
        mov     edx, firstInt
        mov     eax, -1
    .A: inc     eax
        sub     edx, secondInt
        jnb     .A
        add     edx, secondInt  ; -> EDX is remainder, EAX is quotient
    

    The problem with this simple solution is that we could be subtracting a small number many, many times. What if we could find a way to subtract more at once?
    We can, if we subtract binary multiples of the divider (*1, *2, *4, *8, *16, ...). It will be the summing of these factors that produces the quotient.

    To calculate e.g. 503 / 20 we would subtract the following:

    503 - (20 * 16) = 183
    183 - (20 *  8) =  23
     23 - (20 *  1) =   3 <-- is remainder
                --
                25 <-- is quotient
    

    In code:

    complex:
        mov     edx, firstInt
        xor     eax, eax
        jmp     .C
    .A: rol     ecx, 1
        shl     ebx, 1
        jc      .B
        cmp     ebx, edx
        jbe     .A
    .B: rcr     ebx, 1
        add     eax, ecx
        sub     edx, ebx
    .C: mov     ebx, secondInt
        mov     ecx, 80000000h
        cmp     edx, ebx
        jnb     .A
    ; -> EDX is remainder, EAX is quotient
    

    To illustrate the importance of developing a better solution, I've timed a few divisions:

                           simple      complex    divide
                       --------------  --------  --------
    4294967295 /   1   8087730.0 µsec  3.0 µsec  0.3 µsec
    2147483648 /  10    405994.0 µsec  1.3 µsec  0.1 µsec
         47623 / 320         0.4 µsec  0.2 µsec  0.1 µsec
    
    • 4294967295 / 1 is the worst case division
    • 2147483648 / 10 is used to start displaying the number 2147483648
    • 47623 / 320 is used to convert a 320x200 256-color video mode offset address 47623 into (x,y) coordinates