DIV instruction jumping to random location?
Asked Answered
P

1

2

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?

Pa answered 20/4, 2017 at 14:22 Comment(2)
The form of DIV instruction you are using is Unsigned divide AX by r/m8, with result stored in AL ← Quotient, AH ← Remainder. . This is because MASM/TASM quietly assumed num1 was an 8 bit memory operand because of the definition num2 db 02h. In this case DX isn't used (so its value doesn't matter). What is wrong is that 0x204 divided by 2 is 0x102. 0x102 can't fit in a byte (byte is 0x00 to 0xff) so it causes a division exception (overflow) because the quotient can't fit in an 8 byte register (AL).Naturalism
To get this to work you'll have to 0 out a 16-bit register, mov num2 into the lower 8 bits of that register, set DX to zero and do a 16 bit division (rather than 8). From the link in my first comment, the form of instruction is Unsigned divide DX:AX by r/m16, with result stored in AX ← Quotient, DX ← Remainder.. When the division is complete 0x102 should be in AXNaturalism
M
3

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.

Milone answered 21/4, 2017 at 10:53 Comment(1)
Perhaps a good idea to also indicate that the definitions for quotient and remainder need to change from db ? to dw ? The proposed solution does write words!Cordoba

© 2022 - 2024 — McMap. All rights reserved.