为什么没有实现 DIV 指令来设置 CF 而不是引发异常

问题描述 投票:0回答:1

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

          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:
     

有谁知道这样的技术原因没有实现,并且我们有例外而不是设置标志/寄存器被截断?

assembly x86 cpu-architecture integer-division instructions
1个回答
8
投票

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

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


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

请注意,其他 ISA 实际上会做出不同的选择。 例如 ARM 除法永远不会出错,也不会设置标志。 (虽然它不提供双角除数,所以只有

INT_MIN / -1
有符号溢出和除以0的情况是特殊的。)

IDK 如果构建一个硬件除法单元(或微代码)来获取溢出情况下正确截断的商(当精确商比 16 位宽时)会比简单地检测溢出和跳出更困难。如果是这样,那将是一个相当好的理由。

(在输出寄存器中留下垃圾并设置标志是可能的,但不是很好;如果想要避免使用垃圾的可能性,每个除法都需要事后检查结果。除非结果已知适合,例如因为它是无符号的,被除数的高半部分为零,并且除数已知为非零。)

注 1:在某些方面除以 0 是一种特殊情况:对于

any
被除数,对于 divisor=0,high_half < divisor 为假。但没有明确定义的数学结果可以截断。 IEEE FP 除法通过将除数接近 0(即 +- 无穷大)视为极限来解决此问题。但整数 0 应该被假设为恰好 0,而不是某个微小的数字,并且无论如何都没有带内 NaN 或 Inf 值可供使用,只有有限的 0xFFFF...


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

请注意,8086 only 包括单操作数形式

mul
imul
,它们进行加宽乘法:
DX:AX = AX * src
。 (如果高半部分非零(对于
mul
),或者如果高半部分不是低半部分的符号扩展(对于
imul
),则设置CF和OF)。只是后来的 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 位的商的最小被除数。

当分成与股息宽度相同的 hi:lo 半部分时,该股息为

divisor:0

任何小于该值的数字都必须从上半部分“借用”,使其严格小于

divisor

(有符号除法也有

INT_MIN / -1
溢出的极端情况,并且高半检查可能必须针对绝对值。)

© www.soinside.com 2019 - 2024. All rights reserved.