8086 assembly on DOSBox: Bug with idiv instruction?
Asked Answered
W

1

6

I was helping a friend of mine debug his program, and we narrowed it down to an issue which occurs even here:

.MODEL small
.STACK 16
.CODE
start:
    mov ax, 044c0h
    mov bl, 85
    idiv bl
exit:
    mov ax, 4c00h
    int 21h

end start

After assembling it with tasm 4.1, and running it on DOSBox 0.74, it goes into an infinite loop. When inspecting it with turbo debugger one can see it happens after the idiv instruction, which for some reason modifies the cs and ip registers, and after two seemingly random instructions restores them to point to the idiv line, executing it again ad infinitum.

Does anyone have any explanation for this?

Weaver answered 23/4, 2017 at 18:28 Comment(8)
Because the signed quotient of your division can't fit in an 8-bit register (AL) so you are getting an arithmetic overflow. The range for the quotient on a r16/r8 IDIV is −128 to +127 . Your division yields a quotient of 207.Germanize
@MichaelPetch: Yep. I was just about to submit that as an answer, but you beat me!Paterfamilias
That would be the INT 00h interrupt handler. A bit surprising to me that it just jumps right back to the broken code, causing an infinite loop. I would have expected it to terminate the app, print an error message, or something a bit more obvious. You're the second person in as many days to be confused by this.Corticosterone
The divide overflow is the same exception as divide by zero. You should either set up exception handling or guard against overflows by using 16 bit operations.Paterfamilias
Ah yes, of course. I didn't think of that aspect of the operation. Still new to assembly. Simply making it a word division instead should fix it right?Weaver
See this answer, but skip the rant at the top. :-) Or just go straight to the documentation. DIV and IDIV are kind of funky in x86, with their implicit operands.Corticosterone
Yeah, the other question didn't use IDIV. Probably best not to overcomplicate things. Feel free to post an answer here, @michael.Corticosterone
A possible canonical duplicate (for other questions, not this) for 8-bit div without zeroing AH Assembly Divide by zeroTocsin
G
9

This question is a variation on other division related failures. The x86 tag wiki has some additional links:


The apparently random code your debugger seems to jump to is the Arithmetic Exception handler (also the same one as Divide by Zero). What is happening is that your code is experiencing a Division Overflow. You are doing a 16-bit/8-bit IDIV. From the documentation:

Signed divide AX by r/m8, with result stored in: AL ← Quotient, AH ← Remainder.

enter image description here

You will notice that for division with an 8-bit divisor (in your case BL) the range for the quotient is -128 to +127. 044c0h IDIV 85 is 207 (decimal). 207 doesn't fit in a signed 8-bit register so you get division overflow and the cause of your unexpected problem.

To resolve this you can move up to a 16-bit divisor. So you can place your divisor in BX (16-bit register). That would be mov bx, 85. Unfortunately it isn't so simple. When using a 16-bit divisor the processor assumes the dividend is 32-bits with high 16-bits in DX and lower 16-bits in AX.

Signed divide DX:AX by r/m16, with result stored in AX ← Quotient, DX ← Remainder.

To resolve this you have to sign extend the 16-bit value in AX. This is simple as you only need to use the CWD instruction after placing the value in AX. From the instruction set reference

DX:AX ← sign-extend of AX.

Effectively if the Most Significant Bit (MSB) of AX is 0 DX will become 0. If the MSB is 1 then DX would become 0ffffh (all bits set to one). The sign bit of a number is the MSB.

With all this in mind your division code could be adjusted to take a 16-bit divisor:

mov ax, 044c0h
cwd                ; Sign extend AX into DX (DX:AX = 32-bit dividend)
mov bx, 85         ; Divisor is 85
idiv bx            ; Signed divide of DX:AX by BX
Germanize answered 23/4, 2017 at 19:40 Comment(1)
Related: Problems dividing 64 bits in x86 Assembly has a version of the table of what goes where for all sizes from 8 to 64-bit, for unsigned div.Tocsin

© 2022 - 2024 — McMap. All rights reserved.