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.
Unsigned divide AX by r/m8, with result stored in AL ← Quotient, AH ← Remainder.
. This is because MASM/TASM quietly assumednum1
was an 8 bit memory operand because of the definitionnum2 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). – Naturalismnum2
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 isUnsigned divide DX:AX by r/m16, with result stored in AX ← Quotient, DX ← Remainder.
. When the division is complete 0x102 should be in AX – Naturalism