Example 5-1 is the Intel recommended method of determing the processor type as well as the presence and type of NPX or integrated FPU. This code has been modified from previous versions of Intel's recommended CPU identification code by modularizing the printing functions so that applications not running in a DOS environment can remove or change the print function to conform to the appropriate environment. Example 5-1. CPU Identification and FPU Detection ; Filename: cpuid32.asm ; This program is modularized in two parts: ; Part 1: Identifies CPU type in cpu_type: ; 0=8086 processor ; 2=80286 processor ; 3=Intel386TM processor ; 4=Intel486TM processor ; 5=P5TM processor ; The presence of a floating-point unit is ; indicated in fp_flag (1=present). ; ; The variable infinity is used to determine if ; an 80287 (2) is being used with an Intel386 cpu ; or an Intel387 (3) is being used. ; ; Part 2: Prints out the appropriate message. This part can ; be removed if this program is not used in a DOS-based ; system by removing the print_data segment and the ; print procedure in the code segment. ; ; This program uses 32-bit instructions and operands. ; For use on 16-bit assemblers, replace 32-bit instructions ; with 16-bit and use the operand-size override prefix 66H, ; for example: ; ; Instead of: POPFD EAX ; MOV ECX, EAX ; ; Use: DB 66H ; POPF AX ; DB 66H ; MOV CX, AX TITLE CPUID DOSSEG .model small .stack 100h .486 CPUID MACRO db 0Fh ; Hardcoded opcode for CPUID instruction db 0a2h ENDM .data fp_status dw ? saved_cpuid dd ? vendor_id db 12 dup (?) cpu_type db ? model db ? stepping db ? id_flag db 0 fpu_present db 0 intel_proc db 0 infinity db 0 ; ; remove the remaining data declarations if not using the DOS-based ; print procedure ; id_msg db "This system has a$" fp_8087 db " and an 8087 math coprocessor$" fp_80287 db " and an i287tm math coprocessor$" fp_80387 db " and an i387tm math coprocessor$" c8086 db "n 8086/8088 microprocessor$" c286 db "n 80286 microprocessor$" c386 db " i386tm microprocessor$" c486 db " i486tm DX microprocessor or i487tm SX math coprocessor$" c486nfp db " i486tm SX microprocessor$" P5 db " P5 microprocessor",13,10,"$" intel db " This system contains a Genuine Intel processor",13,10,"$" modelmsg db "Model: $" steppingmsg db "Stepping: $" familymsg db "Processor Family: $" period db ".",13,10,"$" dataCR db ?,13,10,"$" intel_id db "GenuineIntel" ; ; The purpose of this code is to allow the user the ; ability to identify the processor and coprocessor ; that is currently in the system. The algorithm of ; the program is to first determine the processor ; id. When that is accomplished, the program continues ; to then identify whether a coprocessor exists ; in the system. If a coprocessor or integrated ; coprocessor exists, the program will identify ; the coprocessor id. The program then prints out ; the CPU and floating point presence and type. ; .code start: mov ax,@data mov ds, ax ; set segment register mov es, ax ; set segment register mov ebx, esp ; save current stack pointer to align and esp, not 3 ; align stack to avoid AC fault pushfd ; save for restoration at end call get_cpuid call check_fpu call print popfd mov esp,ebx ; restore original stack pointer mov ax,4c00h ; terminate program int 21h get_cpuid proc ; ; This procedure determines the type of CPU in a system ; and sets the cpu_type variable with the appropriate ; value. ; ; First check for an 8086 CPU ; Bits 12-15 of the FLAGS register are always set on the ; 8086 processor. ; check_8086: push ebx push ecx pushf ; save EFLAGS pop bx ; store EFLAGS in BX mov ax,0fffh ; clear bits 12-15 and ax,bx ; in EFLAGS push ax ; store new EFLAGS value on stack popf ; replace current EFLAGS value pushf ; set new EFLAGS pop ax ; store new EFLAGS in AX and ax,0f000h ; if bits 12-15 are set, then CPU cmp ax,0f000h ; is an 8086/8088 mov cpu_type, 0 ; turn on 8086/8088 flag je end_get_cpuid ; if CPU is 8086/8088, check for 8087 ; ; 80286 CPU check ; Bits 12-15 of the FLAGS register are always clear on the ; 80286 processor. ; check_80286: or bx,0f000h ; try to set bits 12-15 push bx popf pushf pop ax and ax,0f000h ; if bits 12-15 are cleared, CPU=80286 mov cpu_type, 2 ; turn on 80286 flag jz end_get_cpuid ; if CPU is 80286, ; check for 80287 ; i386 CPU check ; The AC bit, bit #18, is a new bit introduced in the EFLAGS ; register on the i486 DX CPU to generate alignment faults. ; This bit can not be set on the i386 CPU. ; check_Intel386: pushfd pop eax ; get original EFLAGS mov ecx,eax ; save original EFLAGS xor eax,40000h ; flip AC bit in EFLAGS push eax ; save for EFLAGS popfd ; copy to EFLAGS pushfd ; push EFLAGS pop eax ; get new EFLAGS value xor eax,ecx ; can't toggle AC bit, CPU=Intel386 mov cpu_type, 3 ; turn on 386 flag je end_get_cpuid ; if CPU is i386, now check ; for 80287/80387 MCP ; i486 DX CPU / i487 SX MCP and i486 SX CPU checking ; ; Checking for ability to set/clear ID flag (Bit 21) in EFLAGS ; which indicates the presence of a processor ; with the ability to use the CPUID instruction. ; check_Intel486: mov cpu_type, 4 ; turn on i486 flag pushfd ; push original EFLAGS pop eax ; get original EFLAGS in eax mov ecx,eax ; save original EFLAGS in ecx xor eax,200000h ; flip ID bit in EFLAGS push eax ; save for EFLAGS popfd ; copy to EFLAGS pushfd ; push EFLAGS pop eax ; get new EFLAGS value xor eax, ecx je end_get_cpuid ; if ID bit cannot be changed, CPU=486 ; without CPUID instruction functionality ; Execute CPUID instruction to determine vendor, family, ; model and stepping. The use of the CPUID instruction used ; in this program can be used for B0 and later steppings ; of the P5 processor. check_vendor: mov id_flag, 1 ; set flag indicating use of CPUID mov eax, 0 ; set up for CPUID instruction CPUID ; macro for CPUID instruction mov dword ptr vendor_id, ebx ; Test for "GenuineIntel" mov dword ptr vendor_id[+4], edx mov dword ptr vendor_id[+8], ecx mov esi, offset vendor_id mov edi, offset intel_id mov ecx, length intel_id compare: repe cmpsb jne cpuid_data ; if not equal, cpu is not GenuineIntel intel_processor: mov intel_proc, 1 cpuid_data: mov eax, 1 CPUID mov saved_cpuid, eax ; save for future use and eax, 0F00H ; mask everything but family shr eax, 8 mov cpu_type, al ; set cpu_type with family mov eax, saved_cpuid ; restore data mov stepping, al and stepping, 0FH ; isolate stepping info mov eax, saved_cpuid mov model, al and model, 0F0H ; isolate model info shr model, 4 end_get_cpuid: pop ecx pop ebx ret get_cpuid endp ;****************************************************************** check_fpu proc ; ; Co-processor checking begins here for the ; 8086/80286/i386 CPUs. The algorithm is to ; determine whether or not the floating-point ; status and control words can be written to. ; If they are not, no coprocessor exists. If ; the status and control words can be written ; to, the correct coprocessor is then determined ; depending on the processor id. The i386 CPU may ; work with either an 80287 or an 80387. ; The infinity of the coprocessor must be ; checked to determine the correct coprocessor id. ; push eax ; check for 8087/80287/80387 fninit ; reset FP status word mov fp_status,5a5ah ; initialize temp word to ; non-zero value fnstsw fp_status ; save FP status word mov ax,fp_status ; check FP status word cmp al,0 ; see if correct status with ; written je check_control_word mov fpu_present, 0 ; no fpu present jmp end_check_fpu check_control_word: fnstcw fp_status ; save FP control word mov ax,fp_status ; check FP control word and ax,103fh ; see if selected parts looks OK cmp ax,3fh ; check that 1's & 0's ; correctly read je set_fpu_present mov fpu_present, 0 jmp end_check_fpu set_fpu_present: mov fpu_present, 1 ; ; 80287/80387 check for the i386 CPU ; check_infinity: cmp cpu_type, 3 jne end_check_fpu fld1 ; must use default control from FNINIT fldz ; form infinity fdiv ; 8087/80287 says +inf = -inf fld st ; form negative infinity fchs ; 80387 says +inf <> -inf fcompp ; see if they are the same and remove them fstsw fp_status ; look at status from FCOMPP mov ax,fp_status mov infinity, 2 ; store 80287 for fpu infinity sahf ; see if infinities matched jz end_check_fpu ; jump if 8087/80287 is present mov infinity, 3 ; store 80387 for fpu infinity end_check_fpu: pop eax ret check_fpu endp ;******************************************************************* ; ; This procedure prints the appropriate cpuid string and ; numeric processor presence status. If the CPUID instruction ; was supported, it prints out cpuid info. print proc push eax push ebx push ecx push edx cmp id_flag, 1 ; if set to 1, cpu supported ; CPUID instruction ; print detailed CPUID information je print_cpuid_data mov dx, offset id_msg ;print initial message mov ah, 9h int 21h print_86: cmp cpu_type, 0 jne print_286 mov dx, offset c8086 mov ah, 9h int 21h cmp fpu_present, 0 je end_print mov dx, offset fp_8087 mov ah, 9h int 21h jmp end_print print_286: cmp cpu_type, 2 jne print_386 mov dx, offset c286 mov ah, 9h int 21h cmp fpu_present, 0 je end_print mov dx, offset fp_80287 mov ah, 9h int 21h jmp end_print print_386: cmp cpu_type, 3 jne print_486 mov dx, offset c386 mov ah, 9h int 21h cmp fpu_present, 0 je end_print cmp infinity, 2 jne print_387 mov dx, offset fp_80287 mov ah, 9h int 21h jmp end_print print_387: mov dx, offset fp_80387 mov ah, 9h int 21h jmp end_print print_486: cmp fpu_present, 0 je print_Intel486sx mov dx, offset c486 mov ah,9h int 21h jmp end_print print_Intel486sx: mov dx, offset c486nfp mov ah,9h int 21h jmp end_print print_cpuid_data: mov edx, offset familymsg ; print family msg mov ah, 9h int 21h mov al, cpu_type mov byte ptr dataCR, al add byte ptr dataCR, 30H ; convert to ASCII mov edx, offset dataCR ; print family info mov ah, 9h int 21h mov edx, offset steppingmsg ; print stepping msg mov ah, 9h int 21h mov al, stepping mov byte ptr dataCR, al add byte ptr dataCR, 30H ; convert to ASCII mov edx, offset dataCR ; print stepping info mov ah, 9h int 21h mov edx, offset modelmsg ; print model msg mov ah, 9h int 21h mov al, model mov byte ptr dataCR, al add byte ptr dataCR, 30H ; convert to ASCII mov edx, offset dataCR ; print model info mov ah, 9h int 21h end_print: pop edx pop ecx pop ebx pop eax ret print endp end start