I tweaked these for an application I am working on, the main different is each procedure that calls CPUID is double protected by preserving all of the flags as well as reegisters as I found that Win2000 was fussy about the return state after CPUID is called. These are all testing up OK on the range of hardware and OS versions that I can test with. They are not pointed at pre Win2000 Windows versions including WinME but may in fact work on them if the instruction sets are enabled.
IF 0 ; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
----------------------------------------------
Put these prototypes and the structure in your
include file to use the procedures below
----------------------------------------------
is_cpuid PROTO ; test if CPUID is available
vendorstring PROTO :DWORD ; get the vendor string
cpu_string PROTO :DWORD ; get the CPU string
getmmi PROTO :DWORD ; get the instruction sets
mmistring PROTO :DWORD,:DWORD ; format instruction set string
; If the software that uses any of these procedures will have to work on very old
; hardware you need to run "is_cpuid" first to test if the processor supports CPUID.
; The following procedures cannot be run if the processor does not support CPUID.
NOTE that these procedures are not aimed at Win9x, NT4 or WinME Windows versions and have
not been tested on these versions. They are aimed at Win2000 upwards and test reliably on
Win2000, WinXP and Win7 64 bit.
They may work on earlier Win9x and similar 32 bit versions but the OS may not support or
allow the usage of later multimedia instruction sets.
"vendorstring" provide the "GenuineIntel" or "AuthenticAMD" string from the processor.
"cpu_string" provides the model identifier string if it is available.
"getmmi" tests for multimedia instruction sets and returns the information in the structure below.
"mmistring" formats the data from "getmmi" for display purposes.
X86ST STRUCT
sse4a dd ?
sse42 dd ?
sse41 dd ?
ssse3 dd ?
sse3 dd ?
sse2 dd ?
sse dd ?
mmx dd ?
mmxx dd ?
amd3D dd ?
amd3x dd ?
X86ST ENDS
ENDIF ; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
vendorstring proc pBuffer:DWORD
IF 0 ; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
pBuffer needs to be at least 16 bytes in length
vendorstring PROTO :DWORD
ENDIF ; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
pushad
pushfd
mov eax, 0 ; set ID string for Intel
cpuid
mov eax, pBuffer ; load buffer address into ESI
mov [eax], ebx ; write results to buffer
mov [eax+4], edx
mov [eax+8], ecx
mov [eax+12], DWORD PTR 0 ; terminate result
popfd
popad
ret
vendorstring endp
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
is_cpuid proc
IF 0 ; ¤¤¤¤¤¤¤¤¤¤¤¤
is_cpuid PROTO
ENDIF ; ¤¤¤¤¤¤¤¤¤¤¤¤
LOCAL rval :DWORD
pushad
pushfd
; -------------------------------------------------
; test for CPUID so it does not crash on old timers
; -------------------------------------------------
pushfd
pop eax
mov ecx, eax
xor eax, 00000000001000000000000000000000b ; set bit 21 of eflags
push eax
popfd
pushfd
pop eax
xor eax, ecx ; test if its changed
jnz exists ; if changed then CPUID
mov rval, 0 ; returns 0 if no CPUID
jmp quit
; -------------------------------------------------
exists:
mov rval, 1
quit:
popfd
popad
mov eax, rval
ret
is_cpuid endp
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
cpu_string proc pBuffer:DWORD
IF 0 ; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
cpu_string PROTO :DWORD
ENDIF ; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
LOCAL basicinf :DWORD
LOCAL extended :DWORD
pushad
pushfd
mov eax, 0 ; call basic info function
cpuid
mov basicinf, eax
mov eax, 80000000h ; call extended info function
cpuid
mov extended, eax
.if extended == 80000004h || extended == 80000008h
jmp getID
.endif
.if basicinf == 00000001h
cst pBuffer, "Early Unspecified x86 Processor"
jmp quit
.elseif basicinf == 00000002h
cst pBuffer, "Pentium Pro, II or Celeron Processor"
jmp quit
.elseif basicinf == 00000003h
cst pBuffer, "Pentium III Processor"
jmp quit
.else
cst pBuffer, "Cannot Identify x86 Processor"
jmp quit
.endif
getID:
mov esi, pBuffer ; load buffer address into ESI
mov eax, 80000002h ; extended function 2
cpuid
mov [esi], eax ; write results to buffer
mov [esi+4], ebx
mov [esi+8], ecx
mov [esi+12], edx
mov eax, 80000003h ; extended function 3
cpuid
mov [esi+16], eax ; write results to buffer
mov [esi+20], ebx
mov [esi+24], ecx
mov [esi+28], edx
mov eax, 80000004h ; extended function 4
cpuid
mov [esi+32], eax ; write results to buffer
mov [esi+36], ebx
mov [esi+40], ecx
mov [esi+44], edx
mov [esi+48], DWORD PTR 0 ; terminate result
quit:
popfd
popad
ret
cpu_string endp
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
getmmi proc pStruct:DWORD
IF 0 ; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
X86ST STRUCT
sse4a dd ?
sse42 dd ?
sse41 dd ?
ssse3 dd ?
sse3 dd ?
sse2 dd ?
sse dd ?
mmx dd ?
mmxx dd ?
amd3D dd ?
amd3x dd ?
X86ST ENDS
--------------------------------------
pass the address of an X86ST structure
results are written to the members
--------------------------------------
getmmi PROTO :DWORD
ENDIF ; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
pushad
pushfd
mov ecx, SIZEOF X86ST / 4
mov eax, pStruct
@@:
mov DWORD PTR [eax], 0
add eax, 4
sub ecx, 1
jnz @B
; INTEL
mov eax, 1
cpuid
mov eax, pStruct
bt ecx, 20 ; sse4.2
setc BYTE PTR (X86ST PTR [eax]).sse42
bt ecx, 19 ; sse4.1
setc BYTE PTR (X86ST PTR [eax]).sse41
bt ecx, 9 ; ssse3
setc BYTE PTR (X86ST PTR [eax]).ssse3
bt ecx, 0 ; sse3
setc BYTE PTR (X86ST PTR [eax]).sse3
bt edx, 26 ; sse2
setc BYTE PTR (X86ST PTR [eax]).sse2
bt edx, 25 ; sse
setc BYTE PTR (X86ST PTR [eax]).sse
bt edx, 23 ; mmx
setc BYTE PTR (X86ST PTR [eax]).mmx
; AMD
mov eax, 80000001h
cpuid
mov eax, pStruct
bt edx, 22 ; AMD mmx extended
setc BYTE PTR (X86ST PTR [eax]).mmxx
bt ecx, 6 ; AMD sse4a
setc BYTE PTR (X86ST PTR [eax]).sse4a
bt edx, 31 ; AMD 3DNow
setc BYTE PTR (X86ST PTR [eax]).amd3D
bt edx, 30 ; AMD 3DNowExt
setc BYTE PTR (X86ST PTR [eax]).amd3x
popfd
popad
ret
getmmi endp
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
mmistring proc pStruct:DWORD,pbuffer:DWORD
IF 0 ; ----------------------------------------------------------------------
Arguments are,
pStruct = address of a X86ST structure that has been written with getmmi
pbuffer = address of buffer large enough to accept the string results
128 BYTE buffer is OK.
getmmi PROTO :DWORD
mmistring PROTO :DWORD,:DWORD
ENDIF ; ----------------------------------------------------------------------
LOCAL cloc :DWORD
push esi
push edi
mov edi, pStruct
mov esi, pbuffer
mov cloc, 0
mov cloc, rv(szappend,esi,"Processor supports",cloc)
; INTEL
.if BYTE PTR (X86ST PTR [edi]).mmx == 1
mov cloc, rv(szappend,esi," mmx",cloc)
.endif
.if BYTE PTR (X86ST PTR [edi]).sse == 1
mov cloc, rv(szappend,esi," sse",cloc)
.endif
.if BYTE PTR (X86ST PTR [edi]).sse2 == 1
mov cloc, rv(szappend,esi," sse2",cloc)
.endif
.if BYTE PTR (X86ST PTR [edi]).sse3 == 1
mov cloc, rv(szappend,esi," sse3",cloc)
.endif
.if BYTE PTR (X86ST PTR [edi]).ssse3 == 1
mov cloc, rv(szappend,esi," ssse3",cloc)
.endif
.if BYTE PTR (X86ST PTR [edi]).sse41 == 1
mov cloc, rv(szappend,esi," sse4.1",cloc)
.endif
.if BYTE PTR (X86ST PTR [edi]).sse42 == 1
mov cloc, rv(szappend,esi," sse4.2",cloc)
.endif
; AMD
.if BYTE PTR (X86ST PTR [edi]).mmxx == 1
mov cloc, rv(szappend,esi," mmx ext",cloc)
.endif
.if BYTE PTR (X86ST PTR [edi]).sse4a == 1
mov cloc, rv(szappend,esi," sse4a",cloc)
.endif
.if BYTE PTR (X86ST PTR [edi]).amd3D == 1
mov cloc, rv(szappend,esi," 3DNow",cloc)
.endif
.if BYTE PTR (X86ST PTR [edi]).amd3x == 1
mov cloc, rv(szappend,esi," 3DNow ext",cloc)
.endif
pop edi
pop esi
ret
mmistring endp
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤