PROWAREtech
x86 Assembly: CPUID/CPU Capabilities Library
A C/C++ library written in assembly that wraps the functionality of CPUID.
This code was compiled and linked using the Microsoft Macro Assembler for Visual Studio 2022.
This library is available in 64-bit x64 Assembly.
This library uses the CPUID instruction to check a CPU for AVX, AVX2, MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2 and Hyper-threading. It also retrieves the CPU brand and the logical processor count. Note: the logical processor count is only valid on older CPU's as this function is deprecated. Its official use is for "Maximum number of addressable IDs for logical processors in this physical package," which means the number will be greater than or equal to the number of logical processors of the CPU.
TITLE 'SEE HEADER FILE FOR DECLARATIONS'
.686P
.model FLAT
PUBLIC _cpu_id_supported@0
PUBLIC _cpu_id@16
PUBLIC _cpu_brand@4
PUBLIC _cpu_avx@0
PUBLIC _cpu_avx2@0
PUBLIC _cpu_mmx@0
PUBLIC _cpu_sse@0
PUBLIC _cpu_sse2@0
PUBLIC _cpu_sse3@0
PUBLIC _cpu_ssse3@0
PUBLIC _cpu_sse41@0
PUBLIC _cpu_sse42@0
PUBLIC _cpu_logical_processor_count@0
PUBLIC _cpu_hyperthreading@0
_TEXT SEGMENT
_cpu_id_supported@0 PROC NEAR
push ebx ; save ebx for the caller
pushfd ; push eflags on the stack
pop eax ; pop them into eax
mov ebx, eax ; save to ebx for restoring afterwards
xor eax, 200000h ; toggle bit 21
push eax ; push the toggled eflags
popfd ; pop them back into eflags
pushfd ; push eflags
pop eax ; pop them back into eax
cmp eax, ebx ; see if bit 21 was reset
jz not_supported
mov eax, 1
jmp exit
not_supported:
xor eax, eax
exit:
pop ebx
ret 0
_cpu_id_supported@0 ENDP
_cpu_id@16 PROC NEAR
call _cpu_id_supported@0
cmp eax, 0
je exit
push ebx
push esi
mov esi, [esp+(1+2)*4] ; parameter: eax
mov eax, [esi]
mov esi, [esp+(2+2)*4] ; parameter: ebx
mov ebx, [esi]
mov esi, [esp+(3+2)*4] ; parameter: ecx
mov ecx, [esi]
mov esi, [esp+(4+2)*4] ; parameter: edx
mov edx, [esi]
cpuid
mov [esi], edx
mov esi, [esp+(3+2)*4] ; parameter: ecx
mov [esi], ecx
mov esi, [esp+(2+2)*4] ; parameter: ebx
mov [esi], ebx
mov esi, [esp+(1+2)*4] ; parameter: eax
mov [esi], eax
pop esi
pop ebx
exit:
ret 16
_cpu_id@16 ENDP
_cpu_brand@4 PROC NEAR
push ebx
push esi
call _cpu_id_supported@0
cmp eax, 0
je exit
mov eax, 80000000h
cpuid
cmp eax, 80000004h
jnge not_supported
mov esi, [esp+(1+2)*4]
mov eax, 80000002h
cpuid
mov [esi], eax
mov [esi+4], ebx
mov [esi+8], ecx
mov [esi+12], edx
mov eax, 80000003h
cpuid
mov [esi+16], eax
mov [esi+20], ebx
mov [esi+24], ecx
mov [esi+28], edx
mov eax, 80000004h
cpuid
mov [esi+32], eax
mov [esi+36], ebx
mov [esi+40], ecx
mov [esi+44], edx
mov eax, 1
jmp exit
not_supported:
xor eax, eax
exit:
pop esi
pop ebx
ret 4
_cpu_brand@4 ENDP
_cpu_avx@0 PROC NEAR
call _cpu_id_supported@0
cmp eax, 0
je exit
push ebx
mov eax, 1
cpuid
shr ecx, 28
and ecx, 1
mov eax, ecx
pop ebx
exit:
ret 0
_cpu_avx@0 ENDP
_cpu_avx2@0 PROC NEAR
call _cpu_id_supported@0
cmp eax, 0
je exit
push ebx
mov eax, 7
xor ecx, ecx
cpuid
shr ebx, 5
and ebx, 1
mov eax, ebx
pop ebx
exit:
ret 0
_cpu_avx2@0 ENDP
_cpu_mmx@0 PROC NEAR
call _cpu_id_supported@0
cmp eax, 0
je exit
push ebx
mov eax, 1
cpuid
shr edx, 23
and edx, 1
mov eax, edx
pop ebx
exit:
ret 0
_cpu_mmx@0 ENDP
_cpu_sse@0 PROC NEAR
call _cpu_id_supported@0
cmp eax, 0
je exit
push ebx
mov eax, 1
cpuid
shr edx, 25
and edx, 1
mov eax, edx
pop ebx
exit:
ret 0
_cpu_sse@0 ENDP
_cpu_sse2@0 PROC NEAR
call _cpu_id_supported@0
cmp eax, 0
je exit
push ebx
mov eax, 1
cpuid
shr edx, 26
and edx, 1
mov eax, edx
pop ebx
exit:
ret 0
_cpu_sse2@0 ENDP
_cpu_sse3@0 PROC NEAR
call _cpu_id_supported@0
cmp eax, 0
je exit
push ebx
mov eax, 1
cpuid
and ecx, 1
mov eax, ecx
pop ebx
exit:
ret 0
_cpu_sse3@0 ENDP
_cpu_ssse3@0 PROC NEAR
call _cpu_id_supported@0
cmp eax, 0
je exit
push ebx
mov eax, 1
cpuid
shr ecx, 9
and ecx, 1
mov eax, ecx
pop ebx
exit:
ret 0
_cpu_ssse3@0 ENDP
_cpu_sse41@0 PROC NEAR
call _cpu_id_supported@0
cmp eax, 0
je exit
push ebx
mov eax, 1
cpuid
shr ecx, 19
and ecx, 1
mov eax, ecx
pop ebx
exit:
ret 0
_cpu_sse41@0 ENDP
_cpu_sse42@0 PROC NEAR
call _cpu_id_supported@0
cmp eax, 0
je exit
push ebx
mov eax, 1
cpuid
shr ecx, 20
and ecx, 1
mov eax, ecx
pop ebx
exit:
ret 0
_cpu_sse42@0 ENDP
_cpu_logical_processor_count@0 PROC NEAR
call _cpu_id_supported@0
cmp eax, 0
je exit
call _cpu_hyperthreading@0
cmp eax, 0
je exit
push ebx
mov eax, 1
cpuid
shr ebx, 16
and ebx, 0FFh
mov eax, ebx
pop ebx
exit:
ret 0
_cpu_logical_processor_count@0 ENDP
_cpu_hyperthreading@0 PROC NEAR
call _cpu_id_supported@0
cmp eax, 0
je exit
push ebx
mov eax, 1
cpuid
shr edx, 28 ; bit 28
and edx, 1
mov eax, edx
pop ebx
exit:
ret 0
_cpu_hyperthreading@0 ENDP
_TEXT ENDS
END
The header file:
#ifndef CPUIDLIB32_H
#define CPUIDLIB32_H
extern "C" int __stdcall cpu_id_supported(); // returns true if CPUID is supported
extern "C" void __stdcall cpu_id(int *eax, int *ebx, int *ecx, int *edx); // eax, ebx, ecx and edx are [in/out] parameters
extern "C" int __stdcall cpu_brand(char brand[]); // returns true if the brand was copied to the parameter; [brand] should be at least 48 bytes
extern "C" int __stdcall cpu_avx(); // returns true if AVX is supported
extern "C" int __stdcall cpu_avx2(); // returns true if AVX2 is supported
extern "C" int __stdcall cpu_mmx(); // returns true if MMX is supported
extern "C" int __stdcall cpu_sse(); // returns true if SSE is supported
extern "C" int __stdcall cpu_sse2(); // returns true if SSE2 is supported
extern "C" int __stdcall cpu_sse3(); // returns true if SSE3 is supported
extern "C" int __stdcall cpu_ssse3(); // returns true if SSSE3 is supported
extern "C" int __stdcall cpu_sse41(); // returns true if SSE41 is supported
extern "C" int __stdcall cpu_sse42(); // returns true if SSE42 is supported
extern "C" int __stdcall cpu_logical_processor_count(); // returns the number of logical processors if HT is a feature
extern "C" int __stdcall cpu_hyperthreading(); // returns true if HT is a feature
#endif
The driver code:
#include <iostream>
#include "CPUIDLIB32.h"
using namespace std;
int main()
{
if (cpu_id_supported())
{
cout << "CPUID = yes" << endl;
cout << "AVX = " << (cpu_avx() ? "yes" : "no") << endl;
cout << "AVX2 = " << (cpu_avx2() ? "yes" : "no") << endl;
cout << "MMX = " << (cpu_mmx() ? "yes" : "no") << endl;
cout << "SSE = " << (cpu_sse() ? "yes" : "no") << endl;
cout << "SSE2 = " << (cpu_sse2() ? "yes" : "no") << endl;
cout << "SSE3 = " << (cpu_sse3() ? "yes" : "no") << endl;
cout << "SSSE3 = " << (cpu_ssse3() ? "yes" : "no") << endl;
cout << "SSE41 = " << (cpu_sse41() ? "yes" : "no") << endl;
cout << "SSE42 = " << (cpu_sse42() ? "yes" : "no") << endl;
cout << "HT = " << (cpu_hyperthreading() ? "yes" : "no") << endl;
cout << "ThreadCount = " << cpu_logical_processor_count() << endl;
// retrieve the processor brand
char brand[48 + 1];
brand[48] = 0;
if (cpu_brand(brand))
{
cout << "Brand = " << brand << endl;
}
cout << endl << "Ctrl+C to quit" << endl;
}
else
{
cout << "CPUID = no" << endl;
}
cin.get();
return 0;
}
Comment