DOSBox 上的 8086 程序集:idiv 指令有错误?

8086 assembly on DOSBox: Bug with idiv instruction?

我正在帮助我的一个朋友调试他的程序,我们将其缩小到甚至在这里发生的问题:

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

end start

用tasm 4.1组装后,运行在DOSBox 0.74上组装后,进入死循环。当使用 Turbo 调试器检查它时,可以看到它发生在 idiv 指令之后,由于某种原因修改了 csip 寄存器,并且在两条看似随机的指令之后将它们恢复为指向idiv 行,无限次执行它。

有人对此有任何解释吗?

这个问题是其他部门相关失败的变体。 x86 tag wiki 有一些额外的链接:

  • idiv / div problems: . 32bit div faults if the 64b/32b => 32b quotient doesn't actually fit in 32b.

您的调试器似乎跳转到的明显随机代码是算术异常处理程序(也与除以零相同)。发生的事情是您的代码正在经历 Division Overflow。你正在做一个 16-bit/8-bitIDIV。来自文档:

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

您会注意到,对于 8 位除数的除法(在您的例子中 BL),商的范围是 -128 到 +127。 044c0h IDIV 85 是 207(十进制)。 207 不适合带符号的 8 位寄存器,因此您会遇到除法溢出和意外问题的原因。

要解决此问题,您可以使用 16 位除数。因此,您可以将除数放在 BX(16 位寄存器)中。那将是 mov bx, 85。不幸的是,事情并非如此简单。当使用 16 位除数时,处理器假定被除数为 32 位,高 16 位在 DX 中,低 16 位在 AX 中.

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

要解决此问题,您必须对 AX 中的 16 位值进行符号扩展。这很简单,因为您只需要在将值放入 AX 后使用 CWD 指令。来自指令集参考

DX:AX ← sign-extend of AX.

如果 AX 的最高有效位 (MSB) 为 0,则有效 DX 将变为 0。如果 MSB 为 1,则 DX 将变为 0ffffh(所有位设置为 1)。数字的符号位是 MSB。

考虑到所有这些,您的除法代码可以调整为采用 16 位除数:

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