CPUID for ASM and C backend

Share your advanced PureBasic knowledge/code with the community.
User avatar
luis
Addict
Addict
Posts: 3876
Joined: Wed Aug 31, 2005 11:09 pm
Location: Italy

CPUID for ASM and C backend

Post by luis »

Wrapping of the CPUID instruction, should work in x86/x64 with both ASM and C backend.
Tested on Win 10.

Code: Select all

DeclareModule CPUID
 Declare.i    IsSupported () ; Check if CPUID is supported by the CPU
 Declare      CPUID (leaf.l, *eax, *ebx, *ecx, *edx) ; This wraps the CPUID instruction.
 Declare      GetHighestLeaf (Extended.l, *HighestLeaf, *Manufacturer = #Null) ; Get the 'highest' or 'highest extended' leaf level supported by CPUID and optionally the manufacturer ID.
EndDeclareModule

Module CPUID

EnableExplicit

Procedure.i IsSupported ()
 ;> Check if CPUID is supported by the CPU 
 ; It was introduced in 1993 at the time of the Pentium, so ... but you may do a check before calling the other functions.
 
CompilerIf (#PB_Compiler_Processor = #PB_Processor_x64)
 ProcedureReturn 1 ; CPUID is always present on 64 bit CPUs
CompilerEndIf
  
CompilerIf (#PB_Compiler_Processor = #PB_Processor_x86)  

 CompilerIf #PB_Compiler_Backend = #PB_Backend_Asm
  !pushfd
  !pop eax
  !mov edx, eax
  !xor eax, 0x00200000
  !push eax
  !popfd
  !pushfd
  !pop eax
  !xor eax, edx
  !jne IsCpuid_OK
  !xor eax, eax
  !ret
  !IsCpuid_OK:
  !mov eax, 1
  !ret
 CompilerEndIf

 CompilerIf #PB_Compiler_Backend = #PB_Backend_C
  !asm volatile (".intel_syntax noprefix;"
  !"pushfd;"
  !"pop eax;"
  !"mov edx, eax;"
  !"xor eax, 0x00200000;"  
  !"push eax;"
  !"popfd;"
  !"pushfd;"
  !"pop eax;"
  !"xor eax, edx;"
  
  !"jne IsCpuid_OK;"
  !"mov %[retval], 0;"
  !"jmp IsCpuid_EXIT;"
  
  !"IsCpuid_OK:"
  !"mov %[retval], 1;"
  
  !"IsCpuid_EXIT:"
      
  !".att_syntax;"
  !: [retval] "=r" (r)
  !);

  !return r;
 CompilerEndIf

CompilerEndIf

EndProcedure

Procedure GetHighestLeaf (Extended.l, *HighestLeaf, *Manufacturer = #Null)
;> Get the 'highest' or 'highest extended' leaf level supported by CPUID and optionally the manufacturer ID.

; If Extended is = 0 the long pointed by *HighestLeaf will contain the highest leaf level supported.
; If Extended is = $80000000 the long pointed by *HighestLeaf will contain the highest extended leaf level supported.

; *HighestLeaf must be a pointer to a 32 bit integer (PB long)

; If *Manufacturer is not null it must point to a string of at least 12 * SizeOf(Character) + null termination.
; It will contain the manufacturer string ID of the CPU (AuthenticAMD, Genuineintel, etc.)

; Before calling CPUID() you should verify if the leaf level you are going to request is supported using this function.

Protected.l mf1, mf2, mf3

CompilerIf #PB_Compiler_Backend = #PB_Backend_Asm
  !mov eax, dword [p.v_Extended]
  !CPUID
 CompilerIf (#PB_Compiler_Processor = #PB_Processor_x86)  
  !mov ebp, dword [p.p_HighestLeaf]
  !mov dword [ebp], eax 
 CompilerElse   
  !mov rbp, qword [p.p_HighestLeaf]
  !mov dword [rbp], eax
 CompilerEndIf 
  !mov dword [p.v_mf1], ebx
  !mov dword [p.v_mf2], edx
  !mov dword [p.v_mf3], ecx
CompilerEndIf

CompilerIf #PB_Compiler_Backend = #PB_Backend_C
  !unsigned int reg_a, reg_b, reg_c, reg_d;
  
  !asm volatile ("cpuid;"  
  !: "=a" (reg_a), "=b" (reg_b), "=c" (reg_c), "=d" (reg_d)	
  !: "0" (v_extended)
  !);
  
  ! * (unsigned int *) p_highestleaf = reg_a;
  ! v_mf1 = reg_b;
  ! v_mf2 = reg_d;
  ! v_mf3 = reg_c;
CompilerEndIf

If *Manufacturer
    PokeS(*Manufacturer, PeekS(@mf1, 4, #PB_Ascii) + PeekS(@mf2, 4, #PB_Ascii) + PeekS(@mf3, 4, #PB_Ascii))       
EndIf
EndProcedure

Procedure CPUID (leaf.l, *eax, *ebx, *ecx, *edx)
;> This wraps the CPUID instruction.

; leaf must be a 32 bit integer (PB long)
; It must contain the numerical id of the requested level of information and it will be loaded in the EAX register.

; *eax, *ebx, *ecx, *edx must be pointers to 32 bit integers (PB long)
; They will contain a copy of the values stored in the cpu registers after CPUID has been called.

CompilerIf #PB_Compiler_Backend = #PB_Backend_Asm
  !mov eax, dword [p.v_leaf]
  !CPUID
 CompilerIf (#PB_Compiler_Processor = #PB_Processor_x86)  
  !mov ebp, dword [p.p_eax]
  !mov dword [ebp], eax
  !mov ebp, dword [p.p_ebx]
  !mov dword [ebp], ebx
  !mov ebp, dword [p.p_ecx]
  !mov dword [ebp], ecx
  !mov ebp, dword [p.p_edx]
  !mov dword [ebp], edx  
 CompilerElse   
  !mov rbp, qword [p.p_eax]
  !mov dword [rbp], eax
  !mov rbp, qword [p.p_ebx]
  !mov dword [rbp], ebx
  !mov rbp, qword [p.p_ecx]
  !mov dword [rbp], ecx
  !mov rbp, qword [p.p_edx]
  !mov dword [rbp], edx
 CompilerEndIf  
CompilerEndIf

CompilerIf #PB_Compiler_Backend = #PB_Backend_C
  !unsigned int reg_a, reg_b, reg_c, reg_d;
  
  !asm volatile ("cpuid;"  
  !: "=a" (reg_a), "=b" (reg_b), "=c" (reg_c), "=d" (reg_d)	
  !: "0" (v_leaf)
  !);
  
  ! * (unsigned int *) p_eax = reg_a;
  ! * (unsigned int *) p_ebx = reg_b;
  ! * (unsigned int *) p_ecx = reg_c;
  ! * (unsigned int *) p_edx = reg_d;
CompilerEndIf

EndProcedure

EndModule
Example of use:

Code: Select all

EnableExplicit

Define text$, backend$, leaf
Define.l eax, ebx, ecx, edx, Highest, HighestExt
Define Manufacturer$ = Space(12)

CompilerIf #PB_Compiler_Backend = #PB_Backend_Asm
 backend$ = "ASM"
CompilerEndIf

CompilerIf #PB_Compiler_Backend = #PB_Backend_C
 backend$ = "C"
CompilerEndIf

CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
 backend$ + " x86"
CompilerElse   
 backend$ + " x64"
CompilerEndIf


backend$ + " " + #PB_Compiler_Version

text$ = "Backend : " + backend$ 

If CPUID::IsSupported()

 text$ + #CRLF$ + "CPUID is supported."
 
 CPUID::GetHighestLeaf (0, @Highest, @Manufacturer$)
 text$ + #CRLF$ + "Manufacturer ID : " + Manufacturer$
 text$ + #CRLF$ + "Highest leaf : " + Highest
 
 CPUID::GetHighestLeaf ($80000000, @HighestExt)
 text$ + #CRLF$ + "Highest extended leaf : " + "0x" + Hex(HighestExt, #PB_Long)
 
 If ($80000004 & $7FFFFFFF) <= (HighestExt & $7FFFFFFF) ; checks if the required leaves are supported
    text$ + #CRLF$ + "Processor string : [" 
    For leaf = $80000002 To $80000004
        CPUID::CPUID (leaf, @eax, @ebx, @ecx, @edx)
        text$ + PeekS(@eax, 4, #PB_Ascii) + PeekS(@ebx, 4, #PB_Ascii) + PeekS(@ecx, 4, #PB_Ascii) + PeekS(@edx, 4, #PB_Ascii)         
    Next
    text$ + "]"
 Else
     text$ + #CRLF$ + "The extended leaves $80000002 - $80000004 are not supported." 
 EndIf
 
Else
 text$ = "CPUID is not supported."
EndIf

MessageRequester("CPUID", text$)
Example output:

Code: Select all

Backend : C x86 601

CPUID is supported.

Manufacturer ID : AuthenticAMD

Highest leaf : 13

Highest extended leaf : 0x8000001E

Processor string : [AMD Ryzen 5 3600 6-Core Processor              ]
"Have you tried turning it off and on again ?"
A little PureBasic review
User avatar
idle
Always Here
Always Here
Posts: 5042
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: CPUID for ASM and C backend

Post by idle »

Also one here viewtopic.php?p=456687#p456687
needs a fix for x86 c backend though.
Post Reply