2015-10-18 50 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函数对你的程序进行了基准测试,这真的很重要吗? –