DOSBox上的8086汇编:带有idiv指令的Bug?

时间:2017-04-23 18:28:50

标签: assembly x86 x86-16 tasm dosbox

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

.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行,无限再次执行。

有没有人对此有任何解释?

1 个答案:

答案 0 :(得分:7)

此问题是其他部门相关故障的变体。 x86 tag wiki还有一些其他链接:

  

您的调试器似乎跳转到的明显随机代码是Arithmetic Exception处理程序(也与Divide by Zero相同)。发生的事情是您的代码遇到Division Overflow。你正在做一个16位/ 8位IDIV。来自文档:

  

将AX除以r / m8,结果存储在:AL←商,AH←剩余。

enter image description here

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

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

  

符号划分DX:AX乘以r / m16,结果存储在AX←商,DX←剩余。

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

  

DX:AX←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