Porting x86 Assembler Processor ID Code for AMD64

I have a problem. I have the following x86 delphi code that is written in ASM. Do I need to port this to 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; 

I have never programmed in the assembly, does anyone know what the port will be or how I will change it.

+6
source share
3 answers

I am not a Win64 assembler, but the following translation worked for me (tested on 64-bit free 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. 
+10
source

Here is my version for x86 and 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; 

One of the nice things about x64 is that there are much more registers available, many of which are unstable. Therefore, we can use this space for scratches and not touch the main memory at all. Well, obviously, we need to touch the main memory in order to return the result.

Since RBX is non-volatile , we retain its value. All other registers that we modify are volatile, so we do not need to save them. I can think of no way to simplify this further.

This can be easily extended to skip CPUID input as an argument:

 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; 

This assumes that the sublist value is 0, which is passed to ECX. Again, if you want to convey this, it's simple enough:

 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; 
+6
source

I have never worked with a CPUID, so I don’t know exactly what it does. But from common sense and Wikipedia (if these sources were enough), my advice:

Try 1) remove the "assembler"; keyword is obsolete
1.1) it is optional to delete "register"; - it is by default and has little value for a function without parameters. Wikipedia also reports that it has no effect in Win64.

2) if possible - rephrase it as procedure GetCID (out data: TCPUID); . If you need a function - I would prefer to do inline packaging in Pascal - just to make the definition simple and straightforward. This is good advice for the author - to keep simplified simplified things and leave automation of sugar syntax on Pascal, especially if you have no experience, and any simple way can not confuse you and force you to type code. KISS principle.

3) remove push ebx / pop ebx
3.1) I think push edi / popedi will also be deleted. But to be safe - I changed them to push rdi and pop rdi

The article does not say that some registers should be saved or saved: http://en.wikipedia.org/wiki/X86_calling_conventions#x86-64_calling_conventions
This requirement is not specified in http://docwiki.embarcadero.com/RADStudio/XE3/en/Assembly_Procedures_and_Functions

4) mov edi, eax β†’ mov rdi, rcx
4.1) you can add the following cld command after it, as an additional security measure. But this should be excessive and redundant.

The rest should be the same as x64 seems to have the same convention for CPUID as x86 mode - no mentio nof x64 mode at http://en.wikipedia.org/wiki/CPUID#EAX.3D1:_Processor_Info_and_Feature_Bits

To summarize, it should look like

 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; 
-1
source

All Articles