articles » current » assembly » x86 » cpuid-library

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. 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.


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;
}

This site uses cookies. Cookies are simple text files stored on the user's computer. They are used for adding features and security to this site. Read the privacy policy.
CLOSE