;*  Procdi.asm - Processor Identification routines
;*
;*      (C) Copyright Microsoft Corp., 1995
;*
;*      Processor ID
;*
;*  Origin:
;*
;*  Change history:
;*
;*  Date       Who        Description
;*  ---------  ---------  -------------------------------------------------
;*  12-Oct-95  MikeG      Created
;*

.386p
.387

.model  flat


;************************** Include Files ************************

;    include winbase.inc
;    include winerror.inc
;    include kernel32.inc
;    include segs.inc
;    include regstr.inc
;******************** Data Declerations **************************

.data
_DATA SEGMENT
CPU_ID  macro
        db      0fh, 0a2h
        endm

NONE            equ     0
PRESENT         equ     1
Nx586           equ     5
UNKNOWN         equ     0


_nxcpu          db      NONE                    ;default to none
_cputype        db      UNKNOWN                 ;default to unknown
_cpuid_flag     db      NONE                    ;default to no CPUID
_vendor_id      db      "************"
_cpu_signature  dd      0
_features_ecx   dd      0
_features_edx   dd      0
_features_ebx   dd      0
NexGen_id       db      "NexGenDriven"



;*********************** Prototypes ******************************


;************************** Code *********************************

.code

_TEXT	SEGMENT
;==========================================================================
; _get_nxcpu_type
;       This routine identifies NexGen's processor type in following steps:
;
;       if (no AC flag) {       //current Nx586 does not support AC flag
;               set ZF=1;
;               execute DIV to result a none zero value;
;               if (ZF=0) {     //ZF is changed
;                       not a NexGen processor;
;                       exit;
;               } else {        //Nx586 does not change ZF on DIV instruction
;                       if (ID bit not writeable) {
;                               CPU is Nx586 with no CPUID support
;                       } else {                //Nx586 with CPUID support
;                               execute CPUID instruction;
;                               save CPU information;
;                       }
;               }
;       } else {
;               if (ID bit not writeable) {
;                       not a NexGen processor;
;               } else {        //NexGen future processors support CPUID
;                       execute CPUID instruction;
;                       save CPU information;
;               }
;       }
;
;==========================================================================
get_nxcpu_type proc  C cdecl:DWORD
        push    ebx
        push    esi
        push    edi
        mov     byte ptr _nxcpu,PRESENT ; default to present

; test AC bit on EFLAGS register
        mov     bx,sp           ; save the current stack pointer
        and     sp,not 3        ; align the stack to avoid AC fault
        pushfd                  ;
        pop     eax             ; get the original EFLAGS
        mov     ecx,eax         ; save original flag
        xor     eax,40000h      ; flip AC bit in EFLAGS
        push    eax             ; save for EFLAGS
        popfd                   ; copy it to EFLAGS
        pushfd                  ;
        pop     eax             ; get the new EFLAGS value
        mov     sp,bx           ; restore stack pointer
        xor     eax,ecx         ; if the AC bit is unchanged
        je      test_zf         ;       goto second step
        jmp     nx_future_cpu

test_zf:
; test ZF on DIV instruction
        mov     ax,5555h        ; init AX with a non-zero value
        xor     dx,dx           ; set ZF=1
        mov     cx,2
        div     cx              ; Nx586 processor does not modify ZF on DIV
        jnz     not_nx_cpu      ; not a NexGen processor if ZF=0 (modified)

test_cpuid:
; test if CPUID instruction is available
; new Nx586 or future CPU supports CPUID instruction
        pushfd                  ; get EFLAGs
        pop     eax
        mov     ecx,eax         ; save it
        xor     eax,200000h     ; modify ID bit
        push    eax
        popfd                   ; save it in new EFLAGS
        pushfd                  ; get new EFLAGS
        pop     eax             ;
        xor     eax,ecx         ; is ID bit changed?
        jnz     cpuid_present   ; yes

        mov     byte ptr _cputype,Nx586 ; no, current Nx586
	mov	eax,1		; set return code == true
        jz      cpuid_exit      ; stop testing

nx_future_cpu:
; all NexGen's future processors feature a CPUID instruction
        mov     eax,ecx         ; get original EFLAGS
        xor     eax,200000h     ; modify ID bit
        push    eax
        popfd                   ; save it in new EFLAGS
        pushfd                  ; get new EFLAGS
        pop     eax             ;
        xor     eax,ecx         ; is ID bit changed?
        jz      not_nx_cpu      ; no, not a NexGen processor

cpuid_present:
; execute CPUID instruction to get vendor name, stepping and feature info
        xor     eax,eax
        CPU_ID
        mov     dword ptr _vendor_id,ebx
        mov     dword ptr _vendor_id[+4],edx
        mov     dword ptr _vendor_id[+8],ecx

        mov     bx,ds
        mov     es,bx
        mov     esi,offset _vendor_id
        mov     edi,offset NexGen_id
        mov     cx,12
        cld
        repe    cmpsb           ; compare vendor ID string
        jne     not_nx_cpu

        mov     byte ptr _cpuid_flag,PRESENT
        cmp     eax,1           ; check highest level
        jl      cpuid_exit

        mov     eax,1
        CPU_ID
        mov     _cpu_signature,eax
        mov     _features_ecx,ecx
        mov     _features_edx,edx
        mov     _features_ebx,ebx
        shr     eax,8
        and     al,0fh
        mov     _cputype,al
        jmp     cpuid_exit
not_nx_cpu:
        mov     byte ptr _nxcpu,NONE
        xor     eax,eax
cpuid_exit:
        pop     edi
        pop     esi
        pop     ebx
        ret
get_nxcpu_type endp

;**************************************************************************
;       Function:       int is_cyrix ()
;
;       Purpose:        Determine if Cyrix CPU is present
;       Technique:      Cyrix CPUs do not change flags where flags change
;                        in an undefined manner on other CPUs
;       Inputs:         none
;       Output:         ax == 1 Cyrix present, 0 if not
;**************************************************************************
is_cyrix proc C __cdecl:WORD
           .486
           push  bx
           xor   ax, ax         ; clear ax
           sahf                 ; clear flags, bit 1 is always 1 in flags
           mov   ax, 5
           mov   bx, 2
           div   bl             ; do an operation that does not change flags
           lahf                 ; get flags
           cmp   ah, 2          ; check for change in flags
           jne   not_cyrix      ; flags changed not Cyrix
           mov   ax, 1          ; TRUE Cyrix CPU
           jmp   done

not_cyrix:
           mov  ax, 0           ; FALSE NON-Cyrix CPU
done:
           pop  bx
           ret
is_cyrix   endp
_TEXT ends
      end