1

There follows some code I want to translate from Delphi inline assembly because when compiling for Win64 I get errors. The code works as expected when compiled for Win32.

Thank you for your help.

var
  a,b,c,d: LongWord;
  CPUID: string;
begin
  asm
    push EAX
    push EBX
    push ECX
    push EDX

    mov eax, 1
    db $0F, $A2
    mov a, EAX
    mov b, EBX
    mov c, ECX
    mov d, EDX

    pop EDX
    pop ECX
    pop EBX
    pop EAX

    {
    mov eax, 1
    db $0F, $A2
    mov a, EAX
    mov b, EBX
    mov c, ECX
    mov d, EDX
    }
  end;
  CPUID := IntToHex(a,8) + '-' + IntToHex(b,8) + '-' + IntToHex(c,8) + '-' + IntToHex(d,8);
  ShowMessage(CPUID);
end;
blong
  • 2,145
  • 13
  • 23
  • 1
    Code from [here](http://www.swissdelphicenter.ch/en/showcode.php?id=2044), getting cpuid information (stepping id, model number, family code and processor type). – LU RD Oct 19 '16 at 13:39
  • 1
    See [Porting Assembler x86 CPU ID code to AMD64](http://stackoverflow.com/q/13874152/576719). – LU RD Oct 19 '16 at 13:43

1 Answers1

2

This code executes a native hardware instruction on the x86 and x64 processors, CPUID. That instruction cannot be accessed by native Pascal code, so you will need to drop into assembler. The code in your question does not work because it mixes Pascal and assembler, which is not allowed in the 64 bit compiler, and a really bad idea in the 32 bit compiler. So, the way forward is to code this as a pure assembler routine.

There are a great many examples around of how to do this. For instance, Rodrigo Ruz has this unit: https://github.com/RRUZ/vcl-styles-plugins/blob/master/Common/delphi-detours-library/CPUID.pas which contains exactly what you need.

It's not terribly difficult to roll your own. It might go like this:

{$APPTYPE CONSOLE}

uses
  System.SysUtils;

type
  TRegisters = record
    EAX: UInt32;
    EBX: UInt32;
    ECX: UInt32;
    EDX: UInt32;
  end;

function GetCPUID(ID: Integer): TRegisters;
asm
{$IF Defined(CPUX86)}
  push  ebx
  push  edi
  mov   edi, 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   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;

var
  Registers: TRegisters;

begin
  Registers := GetCPUID(1);
  Writeln(IntToHex(Registers.EAX, 8) + '-' + IntToHex(Registers.EBX, 8) + '-' + IntToHex(Registers.ECX, 8) + '-' + IntToHex(Registers.EDX, 8));
  Readln;
end.

You need to understand the calling conventions, how the parameters map to registers, which registers must be preserved, and so on.

With some websearch you will be able to find countless more examples. For example, there is a Stack Overflow post here (Porting Assembler x86 CPU ID code to AMD64) which is arguably a duplicate of this question.

Community
  • 1
  • 1
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • That doesn't seem to answer the question of how to recode the CPUID call so it compiles OK in Delphi's Win64 compiler. As stated in the question, such Win32 assembly instructions cause compiler errors. Indeed specifically the question enquires whether this can be recoded in Pascal so the assembler issues with Win64 can be avoided. – blong Oct 19 '16 at 13:51
  • that's right @blong, thanks for the help. in all examples the assembly is present... the error that returns Delphi is where is 'asm'... – Jorge Williams Oct 19 '16 at 14:01
  • @DavidHeffernan Thanks for the help, this code is part of a component, the error of Delphi whenever it encounters the expression 'asm' ... – Jorge Williams Oct 19 '16 at 14:06
  • 1
    @JorgeWilliams, because mixing pascal and assembler in the same routine does not work in 64 bit compiler, like David explained. – LU RD Oct 19 '16 at 14:09
  • @LURD, then if it is separated as the unit that provided DavidHeffernan will not get error in Win64 build when going through 'asm'? – Jorge Williams Oct 19 '16 at 14:20
  • @JorgeWilliams, that is correct. The defines also make it work both on win32 and win64 compilers. – LU RD Oct 19 '16 at 14:22
  • You need the type declaration and the function, in the code in my answer, and they will work correctly for 32 and 64 bit Windows builds. – David Heffernan Oct 19 '16 at 14:22
  • Okay, I think I understood ... I will try and I will inform the result. Thank you for your attention. – Jorge Williams Oct 19 '16 at 14:30
  • Please elaborate why it is a bad idea in 32-bit assembler. it is a bad idea in win64 because the complicated win64 calling conventions. 32-bit isn't hampered by such restraints though, and while any assembler is evil, anything with cpuid will remain intrinsically assembler anyway. – Marco van de Voort Nov 11 '16 at 20:06
  • @marco it's a bad idea to mix Pascal and assembler because the compiler and the assembler writer can't communicate with each other about stack usage, register usage etc. – David Heffernan Nov 11 '16 at 20:09
  • That doesn't improve anything if you convert the Pascal to assembler to stretch the whole procedure. You can't convert it entirely to pascal since cpuid is asm no matter what. Hence my doubts about that statement. In Win64 the compiler needs complete control to create a proper compliant stackframe, which is the main reason for the Delphi 64-bit requirements, but in 32-bit this is not necessary – Marco van de Voort Nov 11 '16 at 20:22
  • @marco You just extract what you need into a pure asm function. I demonstrated how to do so here. It's in the answer. – David Heffernan Nov 11 '16 at 20:33
  • That is a pure assembler function indeed, but I still fail to see the benefit in the 32-bit case. – Marco van de Voort Nov 11 '16 at 20:46
  • OK, well that's fine. I far prefer a clean separation but if you think the code in the question is better that's up to you. – David Heffernan Nov 11 '16 at 20:55