Last active
August 29, 2015 14:06
-
-
Save TheRayTracer/564a35828680364dcef9 to your computer and use it in GitHub Desktop.
Utility of functions that help to determine specific CPU features, including determining the vendor of the CPU.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef CPUID_H | |
#define CPUID_H | |
#include <memory.h> | |
#define FAMILY_ID 0x0000F00 | |
#define EXT_FAMILY_ID 0x0F00000 | |
#define PENTIUM4_ID 0x0000F00 | |
#define CMPXCHG8_FLAG 0x00000100 | |
#define MMX_FLAG 0x00800000 | |
#define SSE_FLAG 0x02000000 | |
#define SSE2_FLAG 0x04000000 | |
#define SSE3_FLAG 0x00000001 | |
#define HT_FLAG 0x10000000 | |
#define TYPE_MASK 0x00003000 | |
#define FAMILY_MASK 0x00000F00 | |
#define MAJORSTEPPING_MASK 0x000000F0 | |
#define MINORSTEPPING_MASK 0x0000000F | |
#define NUM_LOGICAL_MASK 0x00FF0000 /* EBX bits 23 thu 16 indicate logical processor count. */ | |
union | |
{ | |
struct | |
{ | |
unsigned int a, d; | |
}; | |
long long ad; | |
} ads; | |
inline long long GetCycleNumber() | |
{ | |
#ifdef GCC | |
asm volatile ("rdtsc" : "=a" (ads.a), "=d" (ads.d)); | |
#else | |
_asm RDTSC | |
_asm mov ads.a, eax | |
_asm mov ads.d, edx | |
#endif | |
return ads.ad; | |
} | |
#ifndef GCC | |
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers. | |
#include <windows.h> | |
long long GetChipHz() | |
{ | |
LARGE_INTEGER frequency, count_before, count_after, cycle_before, cycle_after; | |
QueryPerformanceFrequency(&frequency); | |
QueryPerformanceCounter(&count_before); | |
cycle_before.QuadPart = GetCycleNumber(); | |
/* Some spin-wait. */ | |
for (volatile int i = 0; i < 1000000; i++); | |
QueryPerformanceCounter(&count_after); | |
cycle_after.QuadPart = GetCycleNumber(); | |
return ((cycle_after.QuadPart - cycle_before.QuadPart) * frequency.QuadPart / (count_after.QuadPart - count_before.QuadPart)); | |
} | |
#endif | |
/* Returns false on MIPS, PA-RISC, SPARC | |
Returns true on Intel x86 */ | |
bool IsLittleEndian() | |
{ | |
typedef union | |
{ | |
int integer; | |
char byte[4]; | |
} endian_test; | |
endian_test etest; | |
etest.integer = 1; | |
return etest.byte[3] == 0; | |
} | |
/* Pass Desired CPUID input (to be used for EAX) as function parameter. | |
NOTE: If function is zero (00h) then out_eax will return the maximum standard input to be used. | |
Exceeding the maximum standard input will result in an Exception. | |
If function is 80000000h then out_eax will return the maximum extended input to be used. | |
Exceeding the maximum extended input may result in an Exception. An Intel CPU will return 80000002h information. | |
Returns true if successful. */ | |
bool CPUID(unsigned long function, unsigned long& out_eax, unsigned long& out_ebx, unsigned long& out_ecx, unsigned long& out_edx) | |
{ | |
bool result = true; | |
#ifdef GCC | |
/* GCC uses AT&T Assembly Syntax. | |
Extended inline assembly must be used to reference local memory. */ | |
try | |
{ | |
asm ("cpuid" | |
: "=a" (out_eax), /* Statements. */ | |
"=b" (out_ebx), /* Output. */ | |
"=c" (out_ecx), | |
"=d" (out_edx) | |
: "a" (function)); /* Input. And No clobbered hints. */ | |
} catch (...) { result = FALSE; } | |
#else | |
unsigned long local_eax, local_ebx, local_ecx, local_edx; | |
_asm pushad; | |
__try | |
{ | |
_asm xor edx, edx /* Hint to compiler that EDX is about to be used. */ | |
_asm mov eax, function | |
_asm cpuid | |
_asm mov local_eax, eax | |
_asm mov local_ebx, ebx | |
_asm mov local_ecx, ecx | |
_asm mov local_edx, edx | |
} __except(EXCEPTION_EXECUTE_HANDLER) { result = false; } | |
out_eax = local_eax; | |
out_ebx = local_ebx; | |
out_ecx = local_ecx; | |
out_edx = local_edx; | |
_asm popad | |
#endif | |
return result; | |
} | |
/* Returns the Vendor of the CPU. */ | |
void GetProcessorVendor(char* vendor) | |
{ | |
unsigned long unused, vendor_registers[3]; | |
memcpy(vendor, "UnknownCPUID", 13); | |
if (CPUID(0, unused, vendor_registers[0], vendor_registers[2], vendor_registers[1]) != FALSE) | |
{ | |
memcpy(vendor + 0, &(vendor_registers[0]), 4); | |
memcpy(vendor + 4, &(vendor_registers[1]), 4); | |
memcpy(vendor + 8, &(vendor_registers[2]), 4); | |
} | |
return; | |
} | |
/* Returns CPU type (Intel Only, else type is zeroed), CPU family, CPU major stepping and CPU minor stepping. */ | |
unsigned long GetProcessorSignature(unsigned int& type, unsigned int& family, unsigned int& major_stepping, unsigned int& minor_stepping) | |
{ | |
unsigned long unused, eax = 0; | |
if (CPUID(1, eax, unused, unused, unused) == false) | |
{ | |
return 0; | |
} | |
type = 0; | |
char vendor[13] = {0}; | |
GetProcessorVendor(vendor); | |
if (memcmp("GenuineIntel", vendor, 12) == 0) | |
{ | |
type = (eax & TYPE_MASK); | |
type = type >> 12; | |
} | |
family = (eax & FAMILY_MASK); | |
family = family >> 8; | |
major_stepping = (eax & MAJORSTEPPING_MASK); | |
major_stepping = major_stepping >> 4; | |
minor_stepping = (eax & MINORSTEPPING_MASK); | |
/* minor_stepping = minor_stepping >> 0; */ | |
return eax; | |
} | |
/* Returns true if CPU supports the CMPXCHG8 instruction. */ | |
bool CheckCMPXCHG8() | |
{ | |
unsigned long unused, edx = 0; | |
if (CPUID(1, unused, unused, unused, edx) == false) | |
{ | |
return false; | |
} | |
return (edx & CMPXCHG8_FLAG) != 0; /* Test Bit 8. */ | |
} | |
/* Returns true if CPU supports MMX Technology. */ | |
bool CheckMMXTechnology() | |
{ | |
unsigned long unused, edx = 0; | |
if (CPUID(1, unused, unused, unused, edx) == false) | |
{ | |
return false; | |
} | |
return (edx & MMX_FLAG) != 0; /* Test Bit 23. */ | |
} | |
/* Returns true if CPU supports SEE Technology. */ | |
bool CheckSSETechnology() | |
{ | |
unsigned long unused, edx = 0; | |
if (CPUID(1, unused, unused, unused, edx) == false) | |
{ | |
return false; | |
} | |
return (edx & SSE_FLAG) != 0; /* Test Bit 25. */ | |
} | |
/* Returns TRUE if CPU supports SSE2 Technology. */ | |
bool CheckSSE2Technology() | |
{ | |
unsigned long unused, edx = 0; | |
if (CPUID(1, unused, unused, unused, edx) == false) | |
{ | |
return false; | |
} | |
return (edx & SSE2_FLAG) != 0; /* Test Bit 26. */ | |
} | |
/* Returns true if CPU supports SSE3 Technology. */ | |
bool CheckSSE3Technology() | |
{ | |
unsigned long unused, ecx = 0; | |
if (CPUID(1, unused, unused, ecx, unused) == false) | |
{ | |
return false; | |
} | |
return (ecx & SSE3_FLAG) != 0; /* Test Bit 1. */ | |
} | |
/* Returns true if CPU supports HT Technology. */ | |
bool CheckHTTechnology() | |
{ | |
unsigned long unused, eax = 0, edx = 0; | |
if (CPUID(1, eax, unused, unused, edx) == false) | |
{ | |
return false; | |
} | |
if ((eax & FAMILY_ID) != PENTIUM4_ID && (eax & EXT_FAMILY_ID) == 0) | |
{ | |
edx = 0; | |
} | |
return (edx & HT_FLAG) != 0; /* Test Bit 28. */ | |
} | |
/* Returns number of Logical Processors Per Package. */ | |
unsigned int GetLogicalProcessors() | |
{ | |
unsigned long unused, ebx = 0; | |
if (CPUID(1, unused, ebx, unused, unused) == false) | |
{ | |
return 1; | |
} | |
return (ebx & NUM_LOGICAL_MASK) >> 16; | |
} | |
/* Returns true if CPU supports 3DNow Technology. */ | |
bool Check3DNowTechnology() | |
{ | |
unsigned long eax = 0, edx = 0, unused; | |
if (CPUID(0x80000000, eax, unused, unused, unused) == false) | |
{ | |
return false; | |
} | |
if (eax > 0x80000000) | |
{ | |
if (CPUID(0x80000001, unused, unused, unused, edx) == false) | |
{ | |
return false; | |
} | |
return (edx & 0x80000000) != 0; /* Test Bit 31. */ | |
} | |
return false; | |
} | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment