Skip to content

Instantly share code, notes, and snippets.

@TheRayTracer
Last active August 29, 2015 14:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save TheRayTracer/564a35828680364dcef9 to your computer and use it in GitHub Desktop.
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.
#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