我突然意识到我完全走错了路(作为一名模型铁路工人,这真是令人发指的罪行)双关语的意思:-)。
但说实话,我一直在努力解决这个问题。
相反,我应该采取简单的方法:我应该回到 1950 年代的文法学校和我第一次长除法的冒险。
我们不要对 EDX:EAX 除以 ECX 感到困惑,而是考虑将两位数(无符号)数除以一位数(无符号)数。
现在,两位数就是被除数,它有一个个位和一个十位。所以它可以在 0 到 99 之间变化。
并且,一位数就是除数,并且只有一位数。因此,它可以在 1 到 9 之间变化(因为不允许除以零)。
例如,考虑 77 除以 2:
3 8
_____
2 | 7 7
6
_
1 7
1 6
___
1
所以,结果是:商是38,余数是1。
但是,在这里,就像被除数一样,我们允许商也有两位数:十位数字和个位数字。如果我们将商限制为只有个位,会发生什么情况。
然后我们可以调用任何除法,这会导致商在十位数字字段中具有除零之外的任何数字,溢出!
但是,那么,产生这种溢出所需的条件是什么:任何小于或等于被除数的十位数字的除数!
类似地,在 EDX:EAX 除以 ECX 时,如果 ECX
这就是我们对溢出的简单测试:
ECX <= EDX
这适用于无符号除法。
预检查有符号除法溢出要复杂得多。我认为这会起作用,但我仍在测试。
从 EDX:EAX 中的 64 位有符号除数开始,从 ECX 中的 32 位有符号除数开始。然后:
# Temporarily save the dividend
movl %edx, _dividendHigh # Most-significant 32b
movl %eax, _dividendLow # Least-significant 32b
# Check the divisor for zero
testl %ecx, %ecx # Is divisor = 0 ?
jz _DivideByZero # Go if Yes
# Check the divisor for +/- 1
cmpl $1, %ecx
je _dChkA # Go if divisor = 1
cmpl $-1, %ecx
je _dChkA # Go if divisor = -1
jmp _dChkC # Else continue
_dChkA:
# If dividendHigh < -1 or > 0 and divisor = +/- 1
# then overflow will occur.
cmpl $-1, %edx
jl _DivideOverflow # Go if divHigh < -1
cmpl $0, %edx
jg _DivideOverflow # Go if divHigh > 0
# If dividendHigh = -1 and bit 31 of dividendLow = 0
# and divisor = +/- 1 then overflow will occur.
cmpl $-1, %edx
jne _dChkB # Go if divHigh <> -1
bt $31, %eax
jnc _DivideOverflow # Go if divLow b31 = 0
_dChkB:
# If dividendHigh = 0 and bit 31 of dividendLow = 1
# and divisor = +/- 1 then overflow will occur.
cmpl $0, %edx
jne _dChkC # Go if divHigh <> 0
bt $31, %eax
jc _DivideOverflow # Go if divLow b31 = 1
# Check for non-unary overflow
# Overflow will occur if the
# most-significant 33b can be
# divided by the divisor. NOTE:
# that's 33 bits, not 32,
# because all numbers are signed.
# Do dividend shift and manual sign extension
# Check bit 63 to determine if dividend is positive or negative
_dChkC:
bt $31, %edx
jc _dChkD # Go if negative
# Do the 64-bit shift # Positive
# First, shift the Least-significant
# 32b left 1 bit (bit 32 --> CF).
shll $1, %eax
# Then, rotate the Most-significant
# 32b left, through the carry, 1 bit
# (CF --> bit 1 then bit 32 --> CF).
rcll $1, %edx
# Move it to %eax and manually positive-sign extend it
movl %edx, %eax
jmp _dChkE
_dChkD: # Negative
# Do the 64-bit shift
# First, shift the Least-significant
# 32b left 1 bit (bit 32 --> CF).
shll $1, %eax
# Then, rotate the Most-significant
# 32b left, through the carry, 1 bit
# (CF --> bit 1 then bit 32 --> CF).
rcll $1, %edx
# Move it to %eax and manually negative-sign extend it
movl %edx, %eax
movl $-1, %edx
# Do the Test Divide of the
# Most-Significant 33b
_dChkE:
idivl %ecx # EDX:EAX / ECX
# EAX = test quotient
# EDX = test remainder
testl %eax, %eax
jnz _DivideOverflow # Go if Quotient <> 0
# Get the full dividend
movl _dividendHigh, %edx # Most-significant 32b
movl _dividendLow, %eax # Least-significant 32b
# Perform the 64b by 32b division
idivl ecx # EDX:EAX / ECX
# EAX = quotient
# EDX = remainder