我有问题。我遵循用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;
我从未在汇编中编程,有没有人知道端口是什么或者我将如何改变它。
答案 0 :(得分:10)
我不是Win64汇编程序大师,但下一个翻译对我有用(在64位免费pascal上测试):
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.
答案 1 :(得分: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 is nonvolatile以来,我们保留其价值。我们修改的所有其他寄存器都是易失的,因此我们不需要保留它们。我想不出有任何方法可以进一步简化这一点。
这可以很容易地扩展到允许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;
答案 2 :(得分:-1)
我从未使用过CPUID,因此我不确定它的作用。 但是从常识和维基百科(如果这些来源就足够了),我的建议是:
尝试
1)删除“汇编程序”;关键字 - 过时的
1.1)可选择删除“注册;” - 默认情况下,无参数功能的价值很小。维基百科也表示在Win64中没有效果。
2)如果可能 - 将其改为procedure GetCID (out data: TCPUID);
。
如果需要函数 - 我宁愿在Pascal中创建inline
包装器 - 只是为了保持定义简单明了。这对作者来说是一个很好的建议 - 保持非自动化的东西简单化,并将语法 - 糖自动化留给Pascal,特别是当你没有经验时,任何不简单的技巧都会让你迷惑并导致你输入破碎的代码。 KISS原则。
3)删除push ebx / pop ebx
3.1)我认为推edi / popedi也被删除了。但为了安全起见 - 我已将其更改为push rdi
和pop 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
命令,作为额外的安全措施。但它应该过于谨慎和多余。
其余部分应该是相同的似乎x64具有与x86模式相同的CPUID约定 - 在http://en.wikipedia.org/wiki/CPUID#EAX.3D1:_Processor_Info_and_Feature_Bits没有mentio nof 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;