Search code examples
assemblyx86stack-pointer

Long division in ASM x86


I am writing an ASM program that divides two numbers and calculates 20 decimal places. My strategy was to calculate the next digits with a long division sort of process and push them to the stack. Then get the stack pointer, subtract 20 digits * 8 bits from it and write the digit from that address, then add 8 to that adress, write that digit etc. But when I try that it writes out only zeros. The digits are on the stack because I tried just "popping" them and that works fine. I just need them to be popped in the same order that they were pushed, not reversed.

my code:

dane        SEGMENT ;segment danych
text        db 0dh, 0ah,"Give b (a/b)", 0dh, 0ah, "$"
text2       db 0dh, 0ah,"Give a (a/b)", 0dh, 0ah, "$"
text3       db 0dh, 0ah,"a/b:", 0dh, 0ah, "$"
quot        db 0h
rem         db 0h
counter     db 0h
dane        ENDS
; C:\ml /Fl /Zm /Zi /c lab3.asm
; E:\link /CODEVIEW lab3.obj

rozkazy     SEGMENT 'CODE' use16 ;segment rozkazu
        ASSUME cs:rozkazy, ds:dane
    start:  

        mov ax, SEG dane
        mov ds, ax

        mov cl, 03h
        mov ch, 0Ah

        mov dx, offset text
        mov ah, 09h
        int 21h                 ; "input divisor"

        mov ah, 01h
        int 21h                 ; input into al

        mov cl, al              
        sub cl, 30h             ; move to cl and subtract 30h to get value instead of ascii


        mov dx, offset text2
        mov ah, 09h
        int 21h                 ; "input dividend"

        mov ah, 01h
        int 21h 
        sub al, 30h             ; input into al and get number instead of ascii

        jmp divide





    divide: 
        cbw
        div cl                  ; convert al to ax and divide by cl
        mov dl, ah      

        cbw                     ; convert al -> ax

        push ax                 ; push ax to stack

        mov al, dl              ; move remainder from division to al
        xor ah, ah              
        cbw
        mul ch                  ; clear ah and al - > ax, multiply remainder times 10
        inc counter             ; increase counter by 1

        cmp counter, 14h        ; if the division was preformed 20 times, jump to show 
            jz show



        jmp divide

    show: 

        mov dx, offset text3
        mov ah, 09h
        int 21h             ; "your number" 

        mov bx, sp
        sub bx, 160         ; get stack pointer address and subtract by 8*20 (to get address of the number that is 20 positions down from sp) 

        jmp show2

    show2: 

        mov dx, [bx]        ; move value to dx from address that is stored in bx
        add dl, 30h         ; add 30h to get ascii
        mov ah, 02h
        int 21h             ; write dl out

        dec counter         ; decrease counter

        add bx, 8h          ; move our memory pointer 8 up (to next number)

        cmp counter, 0h
            jz finish       ; if counter = 0 jump to finish

        jmp show2

    finish:     
        mov ah, 4CH
        int 21H

rozkazy ENDS


stosik SEGMENT stack
    dw  128 dup(?)
stosik ENDS

END start

Solution

  • The push ax stores 2 bytes into memory and you do that 20 times = 40 bytes. I don't see how you managed to find 160 for sub bx.

    Also the stack grows downward, i.e. if ahead of your loop the sp is 1234, then after first push ax it will be adjusted to 1232 (-2), and the al value is right there at [1232] and ah at [1233].

    So you should do instead add bx,19*2 to calculate address of the first stored digit, and then sub bx,2 to move to the next one.

    BTW, in this case it would be more efficient to not even store the values, but just output them immediately, so I guess you are exercising stack memory manipulation.

    Also use some debugger to check the memory and addresses yourself, your mistake should be obvious if you mark down the sp after each push ax, where did you store particular digit. There was some kind of "cv.exe" (code view) from Microsoft DOS, or you may try to search for other popular DOS debuggers, although most of them are probably difficult to obtain in legal way.

    You may also consider to use some modern tools like DOS emulators with built-in debuggers (some dosbox builds have it, or BOCHS, etc...), which are usually more powerful than the SW debugger running in DOS.


    Oh, now I see...

    subtract 20 digits * 8 bits from it

    Memory is addressable by bytes, not bits, so that would suggest you may want to adjust by 20 (20 digits * 1 byte). But you are doing push ax, storing 16 bits, not just 8 (because there is no push 8 bits instruction on x86-16 CPUs), and 16 bits are 2 bytes, so 20 * 2 = 40 is needed and in opposite direction.