Search code examples
assemblyx86x86-16dostasm

DIV instruction jumping to random location?


So I am having this exact problem.

The solution given is to zero out DX, but in my case it already is!
My program is to simply divide a 16 bit number by an 8 bit number.

My code is:

data segment
num1 dw 0204h
num2 db 02h
quotient db ?
remainder db ?
data ends
code segment
assume cs:code,ds:data
start: mov ax,data
mov ds,ax
mov ax,num1
div num2
mov quotient,al
mov remainder,ah
mov ah,4ch
int 21h
code ends
end start

Any solution?


Solution

  • You badly need to start using whitespace to separate your assembly code. This code is doing something extremely simple (dividing one number by another), yet it is extremely difficult to read. That should obviously not be the case: simple code should be simple to read! Why is it hard to read? Because you've got all of your boilerplate code jammed up against the code that's actually doing the division operation, and my eyes just glaze over. I'm not able to pick out the important bits from the boilerplate. Whitespace is free; don't be afraid of it. Your assembler doesn't mind.

    Write it like this:

    data segment
    
    num1      dw 0204h
    num2      db 02h
    quotient  db ?
    remainder db ?
    
    data ends
    
    
    code segment
    assume cs:code, ds:data
    
    start:
        ; Initialize Data Segment (DS)
        mov ax, data
        mov ds, ax
    
        ; Do the division and save the results
        mov ax, num1
        div num2
        mov quotient, al
        mov remainder, ah
    
        ; Terminate process
        ; (Note that you should also be setting AL to a result code here!)
        mov ah, 4ch
        int 21h
    end start
    
    code ends
    

    Now, isn't it substantially more clear what's what? Also, while MASM/TASM will let you get away with being sloppy, don't get into bad habits. That's another way to make your code unreadable and get the wrong results. There are two different ways that you can use symbols in your code: one way is to use the address/offset of that symbol, and the other way is to use the contents/value of that symbol. In MASM/TASM, when you want the address/offset, you need to use the OFFSET keyword. When you use the contents/value, you don't technically need to, but you really should wrap the symbol in brackets to indicate that it is being dereferenced. In other words, instead of:

    mov ax, num1
    

    write it:

    mov  ax, [num1]
    

    With that rant off my chest, let's look at what's wrong with your code. Michael Petch has already pointed out that this is another case where MASM/TASM's "do what I mean, not what I write" style is not doing you any favors. It is doing an 8-bit division because you've used an 8-bit operand (num2) with the DIV instruction. That means it's actually doing:

    AX / [num2]
    

    with the quotient in AL and the remainder in AH. If the quotient is larger than 8 bits, it won't fit in AL and the division will overflow.

    The workaround is to do a 16-bit division, in which case the quotient will be placed in AX and the remainder will be placed in DX.

    To get that, write the code thusly:

    mov  ax, [num1]      ; AX = [num1]
    xor  dx, dx          ; DX = 0
    
    xor  bx, bx          ; BX = 0
    mov  bl, [num2]      ; BL = [num2], BH = 0
    
    div  bx              ; DX:AX / BX
    
    mov  [quotient],  ax
    mov  [remainder], dx
    

    (Since this is also clobbering BX, you may want to save its original value by doing a push bx at the top and a pop bx at the end.)


    The documentation for the DIV instruction contains a handy table summarizing how 8-bit, 16-bit, and 32-bit divisions work:

    Operand Size        | Dividend  | Divisor   | Quotient  | Remainder | Maximum Quotient
    --------------------------------------------------------------------------------------
    Word/byte           | AX        | r/m8      | AL        | AH        | 2^8 - 1
    Doubleword/word     | DX:AX     | r/m16     | AX        | DX        | 2^16 - 1
    Quadword/doubleword | EDX:EAX   | r/m32     | EAX       | EDX       | 2^32 - 1
    

    The "divisor" is the sole operand for the DIV instruction. The "dividend" is implicit. Note that "r/m" means either a register or memory operand.