2015-10-18 48 views
3

我需要處理來自舊式Mac時代(舊式Motorola CPU)的文件。字節是大端,所以我有一個功能,交換和INT64英特爾小端。該功能是ASM,適用於32位CPU,但不適用於64位。對於64位,我有一個不是ASM的功能。我想結合使用IFDEF的功能。我可以這樣做嗎?這會成爲一個問題嗎?將ASM與非asm編碼(或需要SwapInt64 ASM功能)相結合

interface 

function SwapInt64(Value: Int64): Int64; assembler; 

implementation 

{$IFDEF CPUx86}   
function SwapInt64(Value: Int64): Int64; assembler;  { Does not work on 64 bit }                  { 
asm 
MOV  EDX,[DWORD PTR EBP + 12] 
MOV  EAX,[DWORD PTR EBP + 8] 
BSWAP EAX 
XCHG EAX,EDX 
BSWAP EAX 
end; 

{$else} 

function SwapInt64 (Value: Int64): Int64; 
var P: PInteger; 
begin 
    Result: = (Value shl 32) or (Value shr 32); 
    P: = @Result; 
    P ^: = (Swap (P ^) shl 16) or (Swap (P^shr 16)); 
    Inc (P); 
    P ^: = (Swap (P ^) shl 16) or (Swap (P^shr 16)); 
end; 
{$ENDIF} 

我覺得編譯器會正確編譯/調用相應的功能不管一個是ASM,另一個是帕斯卡。

+0

Mac也使用小端。你實際上是在尋找網絡來主辦功能?你爲什麼覺得需要重新實現它們?你爲什麼想要使用asm?那不是你造成什麼問題嗎?如果你使用Pascal,你會回家並且乾燥? –

+0

@ DavidHeffernan-對不起。我的意思是'老蘋果'。大端(sparc)。 – Ampere

+0

@DavidHeffernan - 請參閱與數據來源有關的說明(舊mac) – Ampere

回答

6

你提出的是完全正確的。這是一個非常合理的方法。

如果你想在ASM 64位互換,針對x64,這是相當簡單:

function SwapInt64(Value: Int64): Int64; 
asm 
    MOV RAX,RCX 
    BSWAP RAX 
end; 

使用條件與32位版本結合這一點,因爲你在問題中所做的那樣。

function SwapInt64(Value: Int64): Int64; 
{$IF Defined(CPUX86)} 
asm 
MOV  EDX,[DWORD PTR EBP + 12] 
MOV  EAX,[DWORD PTR EBP + 8] 
BSWAP EAX 
XCHG EAX,EDX 
BSWAP EAX 
end; 
{$ELSEIF Defined(CPUX64)} 
asm 
    MOV RAX,RCX 
    BSWAP RAX 
end; 
{$ELSE} 
    {$Message Fatal 'Unsupported architecture'} 
{$ENDIF} 

或者在{$ELSE}塊中包含Pascal實現。

+0

謝謝大衛。這將做到! – Ampere

5

如果性能是您所追求的,那麼在不能內聯的單獨例程中交換字節的方法有點愚蠢。

一個更好的方法來假設你有一塊數據和它的所有雙字/ qword需要改變其字節順序。

這看起來像這樣。

對於雙字

function SwapDWords(var Data; size: cardinal): boolean; 
{ifdef CPUX64} 
asm 
    //Data in RCX, Size in EDX 
    xor EAX,EAX //failure 
    test EDX,3 
    jz @MultipleOf4 
@error: 
    ret 
@MultipleOf4 
    neg EDX //Count up instead of down 
    jz @done 
    ADD RCX,RDX  
@loop 
    mov R8d, [RCX+RDX] 
    bswap R8d 
    mov [RCX+RDX],R8d 
    add RDX,4 //add is faster than inc on modern processors 
    jnz @loop 
@done: 
    inc EAX //success 
    ret 
end; 

對於四字

function SwapQWords(var Data; size: cardinal): boolean; 
{ifdef CPUX64} 
asm 
    //Data in RCX, Size in EDX 
    xor EAX,EAX //failure 
    test EDX,7 
    jz @MultipleOf8 
@error: 
    ret 
@MultipleOf8 
    neg EDX //Count up instead of down 
    jz @done 
    ADD RCX,RDX  
@loop 
    mov R8, [RCX+RDX] 
    bswap R8 
    mov [RCX+RDX],R8 
    add RDX,8 //add is faster than inc on modern processors 
    jnz @loop 
@done: 
    inc EAX //success 
    ret 
end; 

如果你已經在64位,那麼你有SSE2,並且可以使用128位的SSE寄存器。
現在,您可以一次處理4個dword,並有效展開循環4次。 參見:http://www.asmcommunity.net/forums/topic/?id=29743

movntpd xmm5,[RCX+RDX] //non-temporal move to avoid polluting the cache 
    movdqu xmm0, xmm5 
    movdqu xmm1, xmm5 
    pxor xmm5, xmm5 
    punpckhbw xmm0, xmm5 ; interleave '0' with bytes of original 
    punpcklbw xmm1, xmm5 ; so they become words 
    pshuflw xmm0, xmm0, 27 ; swap the words by shuffling 
    pshufhw xmm0, xmm0, 27 ;//27 = B00_01_10_11 
    pshuflw xmm1, xmm1, 27 
    pshufhw xmm1, xmm1, 27 
    packuswb xmm1, xmm0 ; make the words back into bytes. 
    movntpd [RCX+RDX], xmm1 //non-temporal move to keep the cache clean. 
+0

通過計數或長度將允許您避免參數有效性檢查。在表現方面,作出判斷還爲時過早。如果緩衝區在磁盤上,那麼這就是重要的。沒有證據表明數據是同質的。很顯然,如果它是同質的並且在記憶中,那麼在asm中循環將是最好的。 –

+0

@Johan +1爲你的不錯的答案。不幸的是,在我的情況下,它不會工作。我讀了一個結構複雜的格式,所以數據是「混合」的。我沒有一個單一的dword塊。我有各種數據:整數,雙字,布爾值,單詞等。 – Ampere

+0

@Frosty在這種情況下,我懷疑asm vs Pascal在perf方面有很大的不同。正如它發生的那樣,asm版本實際上更容易編寫!儘管你需要編寫多個版本。無論如何,這些決定都是你的。 –

1

只需使用任一LEToN()或BEtoN()

使用如果數據是小端排序的LE變體(例如,32或64位的x86 MAC,現代臂),使用BE如果源數據(例如磁盤上的文件)是大端格式。

根據使用的體系結構,swap或「nothing」將被內聯,對於單次轉換通常是相當優化的。對於面向塊的解決方案,請參閱已發佈的SSE代碼(或Agner Fog's)。

+0

Leton/beton適用於Freepascal,不適用於Delphi。我不確定它會在德爾福工作。我將不得不檢查它。 – Ampere

+0

使用Delphi cpu定義和假設有關調用約定可能會失敗,特別是非Windows FPC目標。你是否使用asm函數和pascal函數對你的程序進行了基準測試,這真的很重要嗎? –