Calling the Intel CPUID instruction and reading processor information
Posted: (EET/GMT+2)
In my last post, I promised to show some code that you could use to detect processor features. So here goes.
The Intel x86 ("IA32") processors since Pentium I support the CPUID instruction, which can be used to read processor information. For example, you can use this instruction to detect if your processor supports MMX, SSE, SSE2, SSE3, IA64 or HyperThreading, and most importantly, EMT64, which is Intel's name for 64-bit extensions to their 32-bit Pentium processors. By the way, Microsoft calls EMT64 simply "x64", since they also support AMD's 64-bit extensions that are very similar to EMT64.
Now, I won't be going too much into the details, since you can download a document called the "Intel Processor Identification and the CPUID Instruction" from Intel's developer web site. Instead, I want to show you some example code written with Delphi 2006. You cannot do the same directly from C#, but you could easily write an unmanaged DLL from the Delphi code, and then call that DLL from C#.
Here's my sample application named ShowCPUID:
program ShowCPUID;
{$APPTYPE CONSOLE}
{$OPTIMIZATION OFF}
{$ALIGN 4}
{
Source:
"Intel Processor Identification and the CPUID Instruction"
- Application Note 485, January 2006.
http://developer.intel.com/
}
type
ProcessorInfo = record
MaximumBasicFunction : cardinal;
MaximumExtendedFunction : cardinal;
VendorID : array[0..12] of Char;
Signature : cardinal;
SupportsMMX : boolean;
SupportsSSE : boolean;
SupportsSSE2 : boolean;
SupportsSSE3 : boolean;
SupportsHyperThreading : boolean;
SupportsIA64 : boolean;
SupportsXDBit : boolean;
SupportsEMT64 : boolean;
procedure Clear;
procedure Print;
end;
var
{ globals used by the ReadProcessorInfo function }
MBF,MEF : cardinal;
Sig : cardinal;
A,B,C : cardinal;
FF1,FF2,FF3 : cardinal;
function ReadProcessorInfo : ProcessorInfo; register;
begin
Result.Clear();
{$REGION 'cpuid'}
asm
{ save registers used by the register calling convention }
push eax
push ebx
push ecx
push edx
{ call CPUID }
xor eax,eax
cpuid
mov MBF,eax
{ vendor ID }
mov A,ebx
mov B,edx
mov C,ecx
{ signature & feature flags 1-2 }
mov eax,1
cpuid
mov Sig,eax
mov FF1,edx
mov FF2,ecx
{ extended functions }
mov eax,$8000000
cpuid
mov MEF,eax
{ feature flags 3 }
mov eax,$8000001
cpuid
mov FF3,edx
{ restore }
pop edx
pop ecx
pop ebx
pop eax
end;
{$ENDREGION}
{$REGION 'parse results'}
Result.MaximumBasicFunction := MBF;
Result.MaximumExtendedFunction := MEF;
Result.Signature := Sig;
Result.VendorID[0] := Chr((A and $000000FF) shr 0);
Result.VendorID[1] := Chr((A and $0000FF00) shr 8);
Result.VendorID[2] := Chr((A and $00FF0000) shr 16);
Result.VendorID[3] := Chr((A and $FF000000) shr 24);
Result.VendorID[4] := Chr((B and $000000FF) shr 0);
Result.VendorID[5] := Chr((B and $0000FF00) shr 8);
Result.VendorID[6] := Chr((B and $00FF0000) shr 16);
Result.VendorID[7] := Chr((B and $FF000000) shr 24);
Result.VendorID[8] := Chr((C and $000000FF) shr 0);
Result.VendorID[9] := Chr((C and $0000FF00) shr 8);
Result.VendorID[10] := Chr((C and $00FF0000) shr 16);
Result.VendorID[11] := Chr((C and $FF000000) shr 24);
Result.VendorID[12] := #0;
{$ENDREGION}
{$REGION 'flags'}
Result.SupportsMMX := (FF1 and (1 shl 23)) <> 0;
Result.SupportsSSE := (FF1 and (1 shl 25)) <> 0;
Result.SupportsSSE2 := (FF1 and (1 shl 26)) <> 0;
Result.SupportsSSE3 := (FF2 and (1 shl 0)) <> 0;
Result.SupportsHyperThreading := (FF1 and (1 shl 28)) <> 0;
Result.SupportsIA64 := (FF1 and (1 shl 30)) <> 0;
Result.SupportsXDBit := (FF3 and (1 shl 20)) <> 0;
Result.SupportsEMT64 := (FF3 and (1 shl 29)) <> 0;
{$ENDREGION}
end;
{ ProcessorInfo }
procedure ProcessorInfo.Clear;
begin
MaximumBasicFunction := 0;
MaximumExtendedFunction := $80000000;
VendorID := '';
Signature := 0;
SupportsMMX := False;
SupportsSSE := False;
SupportsSSE2 := False;
SupportsSSE3 := False;
SupportsHyperThreading := False;
SupportsIA64 := False;
SupportsXDBit := False;
SupportsEMT64 := False;
end;
var
PI : ProcessorInfo;
procedure ProcessorInfo.Print;
begin
WriteLn('MaximumBasicFunction = ',MaximumBasicFunction);
WriteLn('MaximumExtendedFunction = ',MaximumExtendedFunction);
WriteLn('VendorID = "',VendorID,'"');
WriteLn('Signature = ',Signature);
WriteLn('SupportsMMX = ',SupportsMMX);
WriteLn('SupportsSSE = ',SupportsSSE);
WriteLn('SupportsSSE2 = ',SupportsSSE2);
WriteLn('SupportsSSE3 = ',SupportsSSE3);
WriteLn('SupportsHyperThreading = ',SupportsHyperThreading);
WriteLn('SupportsIA64 = ',SupportsIA64);
WriteLn('SupportsXDBit = ',SupportsXDBit);
WriteLn('SupportsEMT64 = ',SupportsEMT64);
end;
begin
WriteLn('ShowCPUID application version 1.00.');
Write('Reading processor information...');
PI := ReadProcessorInfo();
WriteLn('');
WriteLn('');
WriteLn('Results');
WriteLn('-------');
PI.Print();
WriteLn('');
Write('Press Enter to exit...');
ReadLn;
end.
If you run this program, it will display something similar to the following:
ShowCPUID application version 1.00. Reading processor information... Results ------- MaximumBasicFunction = 5 MaximumExtendedFunction = 64 VendorID = "GenuineIntel" Signature = 3892 SupportsMMX = TRUE SupportsSSE = TRUE SupportsSSE2 = TRUE SupportsSSE3 = TRUE SupportsHyperThreading = TRUE SupportsIA64 = FALSE SupportsXDBit = FALSE SupportsEMT64 = FALSE Press Enter to exit...
Note that my processor is a Pentium IV, but it unluckily doesn't support EMT64. My bad.