在使用DIV指令之前,为什么EDX应为0?

问题描述 投票:-2回答:2

我注意到EDX包含一些随机默认值,如00401000,然后我使用这样的DIV指令:

mov eax,10
mov ebx,5
div ebx

它会导致INTEGER OVERFLOW ERROR。但是,如果我将edx设置为0并执行相同的操作。我相信使用div将导致覆盖eax的商和其余覆盖edx

获得此INTEGER OVERFLOW ERROR确实让我感到困惑。

assembly x86 integer-division
2个回答
7
投票

What to do

对于32位/ 32位=> 32位除法:将32位被从EAX转换为零或符号扩展为64位EDX:EAX。 对于16位,AX进入DX:带有cwd或xor-zeroing的AX。

  • 无符号:XOR EDX,EDX然后DIV divisor
  • 签署:CDQ然后IDIV divisor

另见When and why do we sign extend and use cdq with mul/div?


Why (TL;DR)

对于DIV,寄存器EDXEAX形成一个单独的64位值(通常显示为EDX:EAX),然后在这种情况下由EBX划分。

因此,如果EAX = 10或hex AEDX,比如20或hex 14,那么他们一起形成64位值hex 14 0000 000A或decimal 85899345930。如果将其除以5,则结果为17179869186或hex 4 0000 0002,这是一个不适合32位的值。

这就是你得到整数溢出的原因。

但是,如果EDX只是1,你可以用1 0000 000A划分hex 5,这会产生十六进制 3333 3335。这不是您想要的值,但它不会导致整数溢出。

要真正将32位寄存器EAX除以另一个32位寄存器,请注意由EDX:EAX形成的64位值的顶部是0

所以,在单一分裂之前,你通常应该将EDX设置为0

(或者签名师,cdq签署在EAX之前将EDX:EAX扩展到idiv


EDX并不总是必须是0。结果导致溢出可能不会那么大。

我的BigInteger代码中的一个例子:

DIV分裂后,商在EAX,其余在EDX。要通过BigInteger(例如将值转换为十进制字符串)来划分类似DWORDS(由许多10的数组组成)之类的内容,您可以执行以下操作:

    ; ECX contains number of "limbs" (DWORDs) to divide by 10
    XOR     EDX,EDX      ; before start of loop, set EDX to 0
    MOV     EBX,10
    LEA     ESI,[EDI + 4*ECX - 4] ; now points to top element of array
@DivLoop:
    MOV     EAX,[ESI]
    DIV     EBX          ; divide EDX:EAX by EBX. After that,
                         ; quotient in EAX, remainder in EDX
    MOV     [ESI],EAX
    SUB     ESI,4        ; remainder in EDX is re-used as top DWORD... 
    DEC     ECX          ; ... for the next iteration, and is NOT set to 0.
    JNE     @DivLoop

在该循环之后,由整个数组表示的值(即由BigInteger)除以10,并且EDX包含该除法的余数。

FWIW,在我使用的汇编程序(Delphi的内置汇编程序)中,以@开头的标签是函数的本地标签,即它们不会干扰其他函数中同名的标签。


1
投票

DIV指令通过DIV指令后面的r / m32划分EDX:EAX。因此,如果您未能将EDX设置为零,则您使用的值将变得非常大。

相信有帮助

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