6

我有个问题。我有以下用 ASM 编写的 x86 delphi 代码。我需要将它移植到 AMD64 吗?

type
 TCPUID = array[1..4] of Longint;

function GetCID : TCPUID; assembler; register;
asm
  push  ebx
  push  edi
  mov   edi, eax
  mov   eax, 1
  dw    $A20F
  stosd
  mov   eax, ebx
  stosd
  mov   eax, ecx
  stosd
  mov   eax, edx
  stosd
  pop   edi
  pop   ebx
end;

我从来没有在汇编中编程过,有人知道端口是什么或者我将如何改变它。

4

3 回答 3

10

我不是 Win64 汇编大师,但下一个翻译对我有用(在 64 位自由帕斯卡上测试):

program project1;

{$mode delphi}
{$asmmode intel}

type
 TCPUID = array[1..4] of Longint;

function GetCID: TCPUID;
asm
  push  rbx
  push  rdi
  mov   rdi, rcx
  mov   eax, 1
  cpuid
  mov   [rdi],eax
  add   rdi,4
  mov   [rdi],ebx
  add   rdi,4
  mov   [rdi],ecx
  add   rdi,4
  mov   [rdi],edx
  pop   rdi
  pop   rbx
end;

var ID: TCPUID;

begin
  ID:= GetCID;
  Writeln(ID[1], '-', ID[2], '-', ID[3], '-', ID[4]);
  Readln;
end.
于 2012-12-14T10:18:10.777 回答
6

这是我的 x86 和 x64 版本:

function GetCPUID: TCPUID;
asm
{$IF Defined(CPUX86)}
  push  ebx
  push  edi
  mov   edi, eax
  mov   eax, 1
  xor   ecx,ecx
  cpuid
  mov   [edi+$0], eax
  mov   [edi+$4], ebx
  mov   [edi+$8], ecx
  mov   [edi+$c], edx
  pop   edi
  pop   ebx
{$ELSEIF Defined(CPUX64)}
  mov   r8, rbx
  mov   r9, rcx
  mov   eax, 1
  cpuid
  mov   [r9+$0], eax
  mov   [r9+$4], ebx
  mov   [r9+$8], ecx
  mov   [r9+$c], edx
  mov   rbx, r8
{$IFEND}
end;

x64 的优点之一是有更多可用的寄存器,其中许多是易失的。所以我们可以利用这个暂存空间,完全避免接触主存。很明显,我们必须触摸主内存才能返回结果。

由于RBX 是非易失性的,我们保留了它的价值。我们修改的所有其他寄存器都是易失的,因此我们不需要保留它们。我想不出任何方法来进一步简化这一点。

这可以很容易地扩展以允许输入CPUID作为参数传递:

function GetCPUID(ID: Integer): TCPUID;
asm
{$IF Defined(CPUX86)}
  push  ebx
  push  edi
  mov   edi, edx
  xor   ecx,ecx
  cpuid
  mov   [edi+$0], eax
  mov   [edi+$4], ebx
  mov   [edi+$8], ecx
  mov   [edi+$c], edx
  pop   edi
  pop   ebx
{$ELSEIF Defined(CPUX64)}
  mov   r8, rbx
  mov   r9, rcx
  mov   eax, edx
  cpuid
  mov   [r9+$0], eax
  mov   [r9+$4], ebx
  mov   [r9+$8], ecx
  mov   [r9+$c], edx
  mov   rbx, r8
{$ELSE}
  {$Message Fatal 'GetCPUID has not been implemented for this architecture.'}
{$IFEND}
end;

这假定在 ECX 中传递的子叶值为 0。同样,如果您想通过它,这很容易:

function GetCPUID(Leaf, Subleaf: Integer): TCPUID;
asm
{$IF Defined(CPUX86)}
  push  ebx
  push  edi
  mov   edi, ecx
  mov   ecx, edx
  cpuid
  mov   [edi+$0], eax
  mov   [edi+$4], ebx
  mov   [edi+$8], ecx
  mov   [edi+$c], edx
  pop   edi
  pop   ebx
{$ELSEIF Defined(CPUX64)}
  mov   r9,rcx
  mov   ecx,r8d
  mov   r8,rbx
  mov   eax,edx
  cpuid
  mov   [r9+$0], eax
  mov   [r9+$4], ebx
  mov   [r9+$8], ecx
  mov   [r9+$c], edx
  mov   rbx, r8
{$ELSE}
  {$Message Fatal 'GetCPUID has not been implemented for this architecture.'}
{$IFEND}
end;
于 2012-12-14T14:41:14.983 回答
-1

我从未使用过 CPUID,所以我不确定它的作用。但从常识和维基百科(如果这些来源足够的话)我的建议是:

尝试
1)删除“汇编器”;关键字 - 已过时
1.1) 可选择删除“注册;” - 默认情况下,对于无参数功能几乎没有价值。维基百科也告诉这在 Win64 中没有影响。

2) 如果可能 - 将其改写为procedure GetCID (out data: TCPUID);. 如果需要功能 - 我宁愿inline在 Pascal 中制作包装器 - 只是为了保持定义简单明了。这对作者来说是一个很好的建议——保持非自动化的东西简单化,把语法糖自动化留给 Pascal,特别是当你没有经验并且任何不简单的技巧都会让你感到困惑并导致你输入损坏的代码时。亲吻原则。

3) 删除 push ebx/pop ebx
3.1) 我认为 push edi/popedi 也将被删除。但是为了安全起见-我将它们更改为push rdipop rdi

这篇文章没有说明应该保存或保留一些寄存器: http ://en.wikipedia.org/wiki/X86_calling_conventions#x86-64_calling_conventions http://docwiki.embarcadero.com/RADStudio/XE3
也没有说明这一要求/en/Assembly_Procedures_and_Functions

4) mov edi, eax-> mov rdi, rcx
4.1) 您可以在其后添加cld命令 next,作为额外的安全措施。但它应该过于谨慎和多余。

其余部分应该与 x64 与 x86 模式具有相同的 CPUID 约定相同 - 在http://en.wikipedia.org/wiki/CPUID#EAX.3D1:_Processor_Info_and_Feature_Bits没有提及 x64 模式

总结起来应该是这样的

type
 TCPUID = packed array[1..4] of INT32;

function GetCID : TCPUID; inline;
begin
  GetCID_Implementation(Result);
end;

procedure GetCID_Implementation (out buffer: TCPUID);
asm
  mov   rdi, rcx  // mov edi, eax
  // RCX/EAX is Delphi/Windows pointer to 1st parameter
  // RDI/EDI is CPU pointer to output buffer

//  cld  - optionally, should not be needed
//  state of cld should be preserved by all other functions

  xor   eax, eax  // shorter way for eax := 1
  inc   eax

  dw    $A20F     // call CPUID(eax==1),
// output (according to wikipedia) is in eax,edx,ecx,ebx  32-bit registers

  stosd           // *( (INT32*)RDI )++ := eax
  mov   eax, ebx  // xchg eax, ebx would be shorter,on that
  stosd
  mov   eax, ecx  // but Delphi XE2 is broken with xchg eax
  stosd
  mov   eax, edx
  stosd
end;
于 2012-12-14T09:26:31.680 回答