Почему EDX должен быть 0, прежде чем использовать инструкцию DIV?

Я заметил, когда EDX содержит какое-то случайное значение по умолчанию, например 00401000, и затем я использую инструкцию DIV следующим образом:

mov eax,10
mov ebx,5
div ebx

он вызывает ошибку INTEGER OVERFLOW. Однако, если я устанавливаю edx в 0 и выполняю то же самое, что он работает. Я полагал, что использование div приведет к перезаписи частным образом eax, а остальная перезапись edx.

Получение этой ошибки INTEGER OVERFLOW ERROR действительно смущает меня.

Ответ 1

Что делать

Для 32-разрядного /32-разрядного => 32-разрядного деления: zero- или знаковое расширение 32-разрядного дивиденда от EAX до 64-разрядного EDX: EAX.
Для 16-битных AX в DX: AX с cwd или xor- cwd.

  • без знака: XOR EDX,EDX затем DIV divisor
  • подписано: CDQ затем IDIV divisor

Смотрите также Когда и почему мы подписываем расширение и используем cdq с mul/div?


Почему (TL; DR)

Для DIV регистры EDX и EAX образуют одно 64-битное значение (часто обозначаемое как EDX:EAX), которое затем делится, в данном случае, на EBX.

Таким образом, если EAX= 10 или шестнадцатеричный A а EDX равен, скажем, 20 или шестнадцатеричный 14, то вместе они образуют шестнадцатеричное 64-разрядное значение 14 0000 000A или десятичное 85899345930. Если это делится на 5, результат 17179869186 или гекс
4 0000 0002, это значение, которое не помещается в 32 бита.

Вот почему вы получаете целочисленное переполнение.

Если, однако, EDX был только 1, вы бы поделили гекс 1 0000 000A на 5, что 1 0000 000A к 1 0000 000A
3333 3335. Это не то значение, которое вы хотели, но оно не вызывает целочисленное переполнение.

Чтобы действительно разделить 32-разрядный регистр EAX на другой 32-разрядный регистр, позаботьтесь о том, чтобы верхняя часть 64-разрядного значения, сформированного EDX:EAX равна 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), метки, начинающиеся с @ являются локальными для функции, то есть они не мешают меткам с одинаковыми именами в других функциях.

Ответ 2

Инструкция DIV делит EDX: EAX на r/m32, который следует за инструкцией DIV. Таким образом, если вы не установите EDX на ноль, значение, которое вы используете, становится очень большим.

Доверяйте, что помогает