2012-10-18 87 views
3

我需要一些簡單的方法來在x86彙編程序中劃分64b無符號整數。我的號碼保存在兩個32b寄存器EDX:EAX中,我需要將結果返回給EDX:EAX。因子是32b整數。請問一些代碼?彙編程序64b部門

+0

只是爲了澄清,你的意思是否使用x64指令?也就是說,這只是將數據轉化爲64位寄存器(例如RAX),進行64位分割,然後將它們分割回32位寄存器,或者您是否想要在32位處理器上模擬64位分割? – WeirdlyCheezy

+0

沒有64b reg - 我試圖在32b上模擬64b分區。 – Nick

+0

如果是這樣的話,這聽起來像你基本上從幾乎從零開始執行二進制分割。對於SO問題,這看起來有點寬泛。你試過什麼了?有沒有一個更具體的部分你堅持? – WeirdlyCheezy

回答

5

如果我正確解釋你的問題(特別是部分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(&dividend, 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 
+0

[已解決]哇!加工!謝謝,這是我需要:)謝謝你們所有人! – Nick

+1

呃......請不要使用帶內存操作數的'xchg',除非你真的想要一個總線鎖。 – fuz

0

如何使用div指令?

+0

當然,但是如果你有一個長號碼保存在兩個寄存器中,你不能只使用一個或兩個「div」。您需要某種方式,如何分割數字的一部分(EDX),數字的第二部分(EAX),合併結果並將最終結果保存回EDX:EAX。我正在尋找這種方式,如何做到這一點。 – Nick

+0

你的除數也是64位嗎? –

0

快速術語概括:分子/除數=結果+餘數/除數

首先檢查如果除數爲零(中止如果它是)。

 test eax,eax 
    jne .ok 
    test edx,edx 
    je .divisionByZero 
.ok: 

移位除數留到MSB,同時保持跟蹤有多少班次你沒有設置:

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)。