Мне нужен простой способ разделить целые числа без знака 64b на ассемблере для x86. Мой номер хранится в двух 32-битных регистрах EDX:EAX, и мне нужно поместить результат обратно в EDX:EAX. Фактор находится в 32b целом числе. Какой-нибудь код, пожалуйста?
Сборщик 64б отдела
Ответы (3)
Если я правильно интерпретирую ваш вопрос (особенно часть Factor is in 32b integer
), вы хотите разделить 64-битное делимое на 32-битный делитель и получить 64-битное частное.
Если эта интерпретация верна, то это действительно легко сделать в 32-битном коде.
Идея состоит в том, что вы делите обе «половины» делимого на делитель и повторно используете остаток от первого деления для второго деления.
Код C, иллюстрирующий, как это сделать:
#include <stdio.h>
#include <limits.h>
#define C_ASSERT(expr) extern char CAssertExtern[(expr)?1:-1]
#if UINT_MAX >= 0xFFFFFFFF
typedef unsigned int uint32;
#else
typedef unsigned long uint32;
#endif
typedef unsigned long long uint64;
typedef unsigned long ulong;
// Make sure uint32=32 bits and uint64=64 bits
C_ASSERT(sizeof(uint32) * CHAR_BIT == 32);
C_ASSERT(sizeof(uint64) * CHAR_BIT == 64);
int div64by32eq64(uint64* dividend, uint32 divisor)
{
uint32 dividendHi = (uint32)(*dividend >> 32);
uint32 dividendLo = (uint32)*dividend;
uint32 quotientHi;
uint32 quotientLo;
if (divisor == 0)
return 0;
// This can be done as one 32-bit DIV, e.g. "div ecx"
quotientHi = dividendHi / divisor;
dividendHi = dividendHi % divisor;
// This can be done as another 32-bit DIV, e.g. "div ecx"
quotientLo = (uint32)((((uint64)dividendHi << 32) + dividendLo) / divisor);
*dividend = ((uint64)quotientHi << 32) + quotientLo;
return 1;
}
int main(void)
{
static const struct
{
uint64 dividend;
uint32 divisor;
} testData[] =
{
{ 1 , 0 },
{ 0xFFFFFFFFFFFFFFFFULL, 1 },
{ 0xFFFFFFFFFFFFFFFFULL, 2 },
{ 0xFFFFFFFF00000000ULL, 0xFFFFFFFFUL },
{ 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFUL },
};
int i;
for (i = 0; i < sizeof(testData)/sizeof(testData[0]); i++)
{
uint64 dividend = testData[i].dividend;
uint32 divisor = testData[i].divisor;
printf("0x%016llX / 0x%08lX = ", dividend, (ulong)divisor);
if (div64by32eq64(÷nd, divisor))
printf("0x%016llX\n", dividend);
else
printf("division by 0 error\n");
}
return 0;
}
Вывод (ideone):
0x0000000000000001 / 0x00000000 = division by 0 error
0xFFFFFFFFFFFFFFFF / 0x00000001 = 0xFFFFFFFFFFFFFFFF
0xFFFFFFFFFFFFFFFF / 0x00000002 = 0x7FFFFFFFFFFFFFFF
0xFFFFFFFF00000000 / 0xFFFFFFFF = 0x0000000100000000
0xFFFFFFFFFFFFFFFF / 0xFFFFFFFF = 0x0000000100000001
А теперь эквивалентный код деления на ассемблере (синтаксис NASM) без проверки деления на 0:
; 64-bit dividend
mov edx, 0xFFFFFFFF
mov eax, 0xFFFFFFFF
; 32-bit divisor
mov ecx, 0xFFFFFFFF
push eax
mov eax, edx
xor edx, edx
div ecx ; get high 32 bits of quotient
xchg eax, [esp] ; store them on stack, get low 32 bits of dividend
div ecx ; get low 32 bits of quotient
pop edx ; 64-bit quotient in edx:eax now
; edx:eax should now be equal 0x0000000100000001
xchg
с операндом памяти, если вам действительно не нужна блокировка шины.
- person fuz; 05.10.2017
Как насчет использования div а> инструкция?
Краткий обзор терминологии: числитель/делитель = результат + остаток/делитель
Сначала проверьте, равен ли делитель нулю (прекратите, если это так).
test eax,eax
jne .ok
test edx,edx
je .divisionByZero
.ok:
Сдвиньте делитель влево, пока не будет установлен старший бит, отслеживая, сколько сдвигов вы сделали:
xor ebp,ebp ;ebp = bits shifted so far
test edx,(1 << 31)
jne .l2
.l1:
shld edx,eax,1
shl eax,1
inc ebp
test edx,(1 << 31)
jne .l1
.l2:
Установите текущий результат на ноль:
xor esi,esi
xor edi,edi
Теперь переместите делитель обратно в исходное положение; при вычитании текущего делителя из оставшегося числителя и установке бита в результате всякий раз, когда текущий делитель меньше текущего числителя:
.nextBit:
shld edi,esi,1
shl esi,1
cmp ecx,edx
jb .doneBit
ja .subtract
cmp ebx,eax
jb .doneBit
.subtract:
sub ecx,edx
sbb ebx,eax
or esi,1
.doneBit:
sub ebp,1
jnc .nextBit
В этот момент EDX:EAX — то же значение, что и было, EDI:ESI — результат, а ECX:EBX — остаток.
ВНИМАНИЕ: Все вышеперечисленное полностью не проверено. Это просто пример/описание.
ПРИМЕЧАНИЕ. Если числа подписаны, вам необходимо сначала удалить бит знака из числителя и делителя; затем установите бит знака в результате и остаток позже (sign = numerator_sign XOR divisor_sign
).
div
в 64-битный режим. - person Alexey Frunze   schedule 19.10.2012