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

eeq*_*nox 5 x86 assembly tasm dosbox x86-16

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

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

end start
Run Code Online (Sandbox Code Playgroud)

用tasm 4.1组装它,然后在DOSBox 0.74上运行它,它进入一个无限循环.当涡轮调试器检查它人们可以看到它之后发生idiv的指令,这对于一些原因,修改csip登记,并经过两次看似随意指令恢复它们指向idiv线,再次执行它循环往复.

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

Mic*_*tch 8

这个问题是其他部门相关失败的变种.该x86标签的wiki有一些额外的链接:


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

符号除以AX/r8,结果存储在:AL←商,AH←余数.

在此输入图像描述

您会注意到,对于具有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
Run Code Online (Sandbox Code Playgroud)