首页 > 解决方案 > 为什么没有实现 DIV 指令来设置 CF 而不是引发异常

问题描述

我知道在组装时必须非常小心,即这样做:

          mov ah, 10h
          mov al, 00h ; dividend = 1000h
          mov bl, 10h ; divisor = 10h
          div bl      ; Integer overflow exception, /result 100h cannot fit into al

我已经编写了一些可能无法验证的逻辑来为除法创建一个更友好的环境:

          mov ah, 10h
          mov al, 00h
          mov bl, 10h 
TryDivide:
          cmp bl,ah
          jna CatchClause
          div bl
          clc
          jmp TryEnd
CatchClause:
          stc
TryEnd:
     

有没有人知道这样的事情没有实现的技术原因,我们有异常而不是标志集/寄存器被截断?

标签: assemblyx86cpu-architectureinteger-divisioninstructions

解决方案


要获得明确的答案,您必须询问8086 指令集的设计者Stephen Morse 。

其他英特尔工程师致力于实际实施,但显然 ISA 最初是在纸上设计的,几乎完全由一个人设计。他还被认为是 8086 的首席架构师 。PC World 在 2008 年采访了他,纪念 8086 的 30 周年,更重要的是,他写了一本书,8086/8088 Primer (1982)。我还没有读过它,但显然他讨论了一些设计决策以及如何对其进行编程。如果你幸运的话,也许他写了一些关于选择 div/idiv 陷阱的东西。


没有理由必须这样。设置 CF 和/或 OF 和截断将是有效的设计。但是您仍然需要选择一些值来放入除零情况1中的商/余数输出寄存器。(我认为对于具有硬件除法的 ISA 来说,至少除以零时出现除错异常是很常见的,但是在哪些平台上整数除以零会触发浮点异常?不幸的是,只提到 x86 作为 ISA陷阱。如果除法确实陷阱并且 POSIX 操作系统完全传递信号,则它必须是 SIGFPE 算术异常。)

请注意,其他 ISA 确实做出了不同的选择。例如,ARM 除法永远不会出错,也不会设置标志。(虽然它不提供双倍宽度的被除数,所以只有INT_MIN / -1符号的溢出和除以 0 的情况是特殊的。)

IDK 如果构建一个硬件除法单元(或微码),可以为溢出情况(当精确商大于 16 位)获得正确截断的商,这将比简单地检测溢出和跳出更难。如果是这样,那将是一个很好的理由。

(将垃圾留在输出寄存器中并设置 FLAGS 是可能的,但不是很好;如果要避免使用垃圾的可能性,每个除法都需要事后检查结果。)

注 1:在某些方面除以 0 是这种情况的一种特殊情况:对于任何high_half < divisor除数的除数 = 0 为假。但是没有明确定义的数学结果可以截断。IEEE FP 除法通过将除数接近 0(即 +-无穷大)视为极限来解决此问题。但是整数 0 应该被假设为正好是 0,而不是一些很小的数字,并且无论如何都没有带内 NaN 或 Inf 值可以使用,只有有限的 0xFFFF ...


如果您考虑 FLAGS,则无需截断其他 8086 数学指令

请注意,8086mul包括and的单操作数形式imul,它们确实扩大了乘法:DX:AX = AX * src. (如果高半部分非零(对于mul),或者如果高半部分不是低半部分的符号扩展(对于 ),则设置 CF 和 OF imul)。只有后来的 CPU 引入了截断形式,例如imul r, r/m, imm( 186 ) 和imul r, r/m(386),它们不会浪费时间在任何地方写入高半部分,尽管仍然设置 FLAGS,以便您可以根据需要检测签名包装。(大多数用途没有,所以后来的 CPU 只提供了 imul,mul 的版本除了 FLAGS 之外是相同的。)

add/sub可以进位/借位,但加法的完整结果CF : reg与进位标志中的额外位一样可用。

如果您将 sar / shr /shl reg, cl视为按位逻辑运算,而不是数学,那么即使它可以移出多个位而不将它们留在任何地方,它也不算数。(最后一位留在 CF 中,因此可以使用循环进位来撤消移位 1。)

这留下了 DIV / IDIV,因为我认为唯一的算术指令可能会产生更广泛的结果并且无处可放。可能是选择让他们犯错的部分动机。


high_half < divisor 无符号除法的证明

这就是操作数大小中商拟合的确切条件。 1:0(例如0x0100对于 8 位操作数大小)是不适合的最小商因此0x0100 * divisor会产生不适合 8 位的商的最小被除数也是如此。

该股息被divisor:0分成与股息宽度相同的 hi:lo 两半。

任何小于此的数字都必须从高半部分“借用”,使其严格小于divisor.

(有符号除法也有INT_MIN / -1溢出极端情况,高半部分检查可能必须在绝对值上。)


推荐阅读