PROWAREtech
x64 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 x86 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. It official use if 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.
_TEXT SEGMENT
cpu_id_supported PROC
push rbx ; save rbx for the caller
pushfq ; push rflags on the stack
pop rax ; pop them into rax
mov rbx, rax ; save to rbx for restoring afterwards
xor rax, 200000h ; toggle bit 21
push rax ; push the toggled rflags
popfq ; pop them back into rflags
pushfq ; push rflags
pop rax ; pop them back into rax
cmp rax, rbx ; see if bit 21 was reset
jz not_supported
mov eax, 1
jmp exit
not_supported:
xor eax, eax
exit:
pop rbx
ret
cpu_id_supported ENDP
cpu_processor_features_edx PROC
call cpu_id_supported
cmp eax, 0
je exit
push rbx
mov eax, 1
cpuid
mov eax, edx
pop rbx
exit:
ret
cpu_processor_features_edx ENDP
cpu_processor_features_ecx PROC
call cpu_id_supported
cmp eax, 0
je exit
push rbx
mov eax, 1
cpuid
mov eax, ecx
pop rbx
exit:
ret
cpu_processor_features_ecx ENDP
cpu_processor_features_ebx PROC
call cpu_id_supported
cmp eax, 0
je exit
push rbx
mov eax, 1
cpuid
mov eax, ebx
pop rbx
exit:
ret
cpu_processor_features_ebx ENDP
cpu_processor_features_eax PROC
call cpu_id_supported
cmp eax, 0
je exit
push rbx
mov eax, 1
cpuid
pop rbx
exit:
ret
cpu_processor_features_eax ENDP
cpu_brand_part0 PROC
push rbx
call cpu_id_supported
cmp eax, 0
je exit
mov eax, 80000000h
cpuid
cmp eax, 80000004h
jnge not_supported
mov eax, 80000002h
cpuid
xchg rbx, rax
shl rax, 32
shl rbx, 32
shr rbx, 32
or rax, rbx
jmp exit
not_supported:
xor rax, rax
exit:
pop rbx
ret
cpu_brand_part0 ENDP
cpu_brand_part1 PROC
push rbx
call cpu_id_supported
cmp eax, 0
je exit
mov eax, 80000000h
cpuid
cmp eax, 80000004h
jnge not_supported
mov eax, 80000002h
cpuid
mov rax, rdx
shl rax, 32
shl rcx, 32
shr rcx, 32
or rax, rcx
jmp exit
not_supported:
xor eax, eax
exit:
pop rbx
ret
cpu_brand_part1 ENDP
cpu_brand_part2 PROC
push rbx
call cpu_id_supported
cmp eax, 0
je exit
mov eax, 80000000h
cpuid
cmp eax, 80000004h
jnge not_supported
mov eax, 80000003h
cpuid
xchg rbx, rax
shl rax, 32
shl rbx, 32
shr rbx, 32
or rax, rbx
jmp exit
not_supported:
xor eax, eax
exit:
pop rbx
ret
cpu_brand_part2 ENDP
cpu_brand_part3 PROC
push rbx
call cpu_id_supported
cmp eax, 0
je exit
mov eax, 80000000h
cpuid
cmp eax, 80000004h
jnge not_supported
mov eax, 80000003h
cpuid
mov rax, rdx
shl rax, 32
shl rcx, 32
shr rcx, 32
or rax, rcx
jmp exit
not_supported:
xor eax, eax
exit:
pop rbx
ret
cpu_brand_part3 ENDP
cpu_brand_part4 PROC
push rbx
call cpu_id_supported
cmp eax, 0
je exit
mov eax, 80000000h
cpuid
cmp eax, 80000004h
jnge not_supported
mov eax, 80000004h
cpuid
xchg rbx, rax
shl rax, 32
shl rbx, 32
shr rbx, 32
or rax, rbx
jmp exit
not_supported:
xor eax, eax
exit:
pop rbx
ret
cpu_brand_part4 ENDP
cpu_brand_part5 PROC
push rbx
call cpu_id_supported
cmp eax, 0
je exit
mov eax, 80000000h
cpuid
cmp eax, 80000004h
jnge not_supported
mov eax, 80000004h
cpuid
mov rax, rdx
shl rax, 32
shl rcx, 32
shr rcx, 32
or rax, rcx
jmp exit
not_supported:
xor eax, eax
exit:
pop rbx
ret
cpu_brand_part5 ENDP
cpu_avx PROC
call cpu_id_supported
cmp eax, 0
je exit
push rbx
mov eax, 1
cpuid
shr ecx, 28
and ecx, 1
mov eax, ecx
pop rbx
exit:
ret
cpu_avx ENDP
cpu_avx2 PROC
call cpu_id_supported
cmp eax, 0
je exit
push rbx
xor rbx, rbx
mov eax, 7
cpuid
shr ebx, 5
and ebx, 1
mov eax, ebx
pop rbx
exit:
ret
cpu_avx2 ENDP
cpu_mmx PROC
call cpu_id_supported
cmp eax, 0
je exit
push rbx
mov eax, 1
cpuid
shr edx, 23
and edx, 1
mov eax, edx
pop rbx
exit:
ret
cpu_mmx ENDP
cpu_sse PROC
call cpu_id_supported
cmp eax, 0
je exit
push rbx
mov eax, 1
cpuid
shr edx, 25
and edx, 1
mov eax, edx
pop rbx
exit:
ret
cpu_sse ENDP
cpu_sse2 PROC
call cpu_id_supported
cmp eax, 0
je exit
push rbx
mov eax, 1
cpuid
shr edx, 26
and edx, 1
mov eax, edx
pop rbx
exit:
ret
cpu_sse2 ENDP
cpu_sse3 PROC
call cpu_id_supported
cmp eax, 0
je exit
push rbx
mov eax, 1
cpuid
and ecx, 1
mov eax, ecx
pop rbx
exit:
ret
cpu_sse3 ENDP
cpu_ssse3 PROC
call cpu_id_supported
cmp eax, 0
je exit
push rbx
mov eax, 1
cpuid
shr ecx, 9
and ecx, 1
mov eax, ecx
pop rbx
exit:
ret
cpu_ssse3 ENDP
cpu_sse41 PROC
call cpu_id_supported
cmp eax, 0
je exit
push rbx
mov eax, 1
cpuid
shr ecx, 19
and ecx, 1
mov eax, ecx
pop rbx
exit:
ret
cpu_sse41 ENDP
cpu_sse42 PROC
call cpu_id_supported
cmp eax, 0
je exit
push rbx
mov eax, 1
cpuid
shr ecx, 20
and ecx, 1
mov eax, ecx
pop rbx
exit:
ret
cpu_sse42 ENDP
cpu_logical_processor_count PROC
call cpu_id_supported
cmp eax, 0
je exit
call cpu_hyperthreading
cmp eax, 0
je exit
push rbx
mov eax, 1
cpuid
and ebx, 00FF0000h
mov eax, ebx
shr eax, 16
pop rbx
exit:
ret
cpu_logical_processor_count ENDP
cpu_hyperthreading PROC
call cpu_id_supported
cmp eax, 0
je exit
push rbx
mov eax, 1
cpuid
shr edx, 28
and edx, 1
mov eax, edx
pop rbx
exit:
ret
cpu_hyperthreading ENDP
_TEXT ENDS
END
The header file:
#ifndef CPUIDLIB64_H
#define CPUIDLIB64_H
extern "C" int cpu_id_supported(); // returns true if CPUID is supported
extern "C" int cpu_processor_features_edx(); // returns 0 if not supported, otherwise, returns edx
extern "C" int cpu_processor_features_ecx(); // returns 0 if not supported, otherwise, returns ecx
extern "C" int cpu_processor_features_ebx(); // returns 0 if not supported, otherwise, returns ebx
extern "C" int cpu_processor_features_eax(); // returns 0 if not supported, otherwise, returns eax
extern "C" long long cpu_brand_part0(); // returns 8 byte string if brand is supported, or 0 if not supported
extern "C" long long cpu_brand_part1(); // returns 8 byte string if brand is supported, or 0 if not supported
extern "C" long long cpu_brand_part2(); // returns 8 byte string if brand is supported, or 0 if not supported
extern "C" long long cpu_brand_part3(); // returns 8 byte string if brand is supported, or 0 if not supported
extern "C" long long cpu_brand_part4(); // returns 8 byte string if brand is supported, or 0 if not supported
extern "C" long long cpu_brand_part5(); // returns 8 byte string if brand is supported, or 0 if not supported
extern "C" int cpu_avx(); // returns true if AVX is supported
extern "C" int cpu_avx2(); // returns true if AVX2 is supported
extern "C" int cpu_mmx(); // returns true if MMX is supported
extern "C" int cpu_sse(); // returns true if SSE is supported
extern "C" int cpu_sse2(); // returns true if SSE2 is supported
extern "C" int cpu_sse3(); // returns true if SSE3 is supported
extern "C" int cpu_ssse3(); // returns true if SSSE3 is supported
extern "C" int cpu_sse41(); // returns true if SSE41 is supported
extern "C" int cpu_sse42(); // returns true if SSE42 is supported
extern "C" int cpu_logical_processor_count(); // returns the number of logical processors
extern "C" int cpu_hyperthreading(); // returns true if HT is a feature
#endif
The driver code:
#include <iostream>
#include "CPUIDLIB64.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
union {
char brand[48 + 1];
long long buffer[6];
};
brand[48] = 0;
buffer[0] = cpu_brand_part0();
buffer[1] = cpu_brand_part1();
buffer[2] = cpu_brand_part2();
buffer[3] = cpu_brand_part3();
buffer[4] = cpu_brand_part4();
buffer[5] = cpu_brand_part5();
cout << "Brand = " << brand << endl;
cout << endl << "Ctrl+C to quit" << endl;
}
else
{
cout << "CPUID = no" << endl;
}
cin.get();
return 0;
}
Comment