;****************************************************************************
; WHATCPU identifies the type of CPU that it is run on.
; From PC Magazine
;****************************************************************************

CPUID           MACRO
                db      0Fh,0A2h                ;Macro for CPUID instruction
ENDM

code            segment
                assume  cs:code,ds:code
                org     100h
begin:          jmp     short main

msg_8086        db      "It's an 8086 or 8088!",13,10,"$"
msg_286         db      "It's a 286!",13,10,"$"
msg_386         db      "It's a 386!",13,10,"$"
msg_486         db      "It's a 486!",13,10,"$"
msg_586         db      "It's a Pentium!",13,10,"$"
msg_other       db      "It's a ?86!",13,10,"$"

main            proc    near
;
; Check for an 8086 or 8088 by attempting to clear bits 12-15 of the
; FLAGS register. On an 8086 or 8088, these bits are always set.
;
                mov     dx,offset msg_8086      ;Assume 8086/88
                cli                             ;Interrupts off
                pushf                           ;Save FLAGS
                pushf                           ;Push FLAGS on stack
                pop     ax                      ;Pop FLAGS into AX
                and     ax,0FFFh                ;Clear bits 12-15
                or      ax,5000h                ;Set bits 12 and 14
                push    ax                      ;Push AX on stack
                popf                            ;Pop AX into FLAGS
                pushf                           ;Push FLAGS on stack
                pop     ax                      ;Pop FLAGS into AX
                popf                            ;Restore FLAGS
                sti                             ;Interrupts on
                and     ax,0F000h               ;Clear bits 0-11
                cmp     ax,0F000h               ;Are bits 12-15 set?
                jne     check_286               ;No, then it's a 286 or higher
                jmp     done                    ;Yes, then it's an 8086/88
;
; Check for a 286 by seeing if bits 12-15 are clear after the previous
; operation. On a 286 running in real mode, these bits are always clear.
;
check_286:      mov     dx,offset msg_286       ;Assume 286
                or      ax,ax                   ;Are bits 12-15 clear?
                jne     check_386               ;No, then it's a 386 or higher
                jmp     done                    ;Yes, then it's a 286
;
; Check for a 386 by attempting to toggle the EFLAGS register's Alignment
; Check (AC) bit. This bit cannot be changed on a 386.
;
                .386

check_386:      mov     dx,offset msg_386       ;Assume 386
                cli                             ;Interrupts off
                pushfd                          ;Save EFLAGS
                pushfd                          ;Push EFLAGS on stack
                pop     eax                     ;Pop EFLAGS into EAX
                mov     ebx,eax                 ;Store EAX in EBX
                xor     eax,40000h              ;Toggle bit 18
                push    eax                     ;Push EAX on stack
                popfd                           ;Pop EAX into EFLAGS
                pushfd                          ;Push EFLAGS on stack
                pop     eax                     ;Pop EFLAGS into EAX
                popfd                           ;Restore EFLAGS
                sti                             ;Interrupts on
                and     eax,40000h              ;Clear all but bit 18
                and     ebx,40000h              ;in EAX and EBX
                cmp     eax,ebx                 ;Compare EAX and EBX
                je      done                    ;If equal, then it's a 386
;
; Check for a 486 by attempting to toggle the EFLAGS register's ID bit.
; This bit cannot be changed on *most* 486s.
;
                mov     dx,offset msg_486       ;Assume 486
                cli                             ;Interrupts off
                pushfd                          ;Save EFLAGS
                pushfd                          ;Push EFLAGS on stack
                pop     eax                     ;Pop EFLAGS into EAX
                mov     ebx,eax                 ;Store EAX in EBX
                xor     eax,200000h             ;Toggle bit 21
                push    eax                     ;Push EAX on stack
                popfd                           ;Pop EAX into EFLAGS
                pushfd                          ;Push EFLAGS on stack
                pop     eax                     ;Pop EFLAGS into EAX
                popfd                           ;Restore EFLAGS
                sti                             ;Interrupts on
                and     eax,200000h             ;Clear all but bit 21 in
                and     ebx,200000h             ;EAX and EBX
                cmp     eax,ebx                 ;Compare EAX and EBX
                je      done                    ;If equal, then it's a 486
;
; We succeeded in toggling the processor's ID bit, so it's probably a
; Pentium or higher. But it *could* be a 486, so be careful. To identify
; the processor, use the family ID code returned by the CPUID instruction.
;
                mov     eax,1                   ;Get CPU ID information
                CPUID                           ;via the CPUID instruction
                and     eax,0F00h               ;Clear all but bits 8-11
                shr     eax,8                   ;Shift right 8 places
                                                ;(EAX = Family ID)

                mov     dx,offset msg_586       ;Assume Pentium
                cmp     eax,5                   ;Is the family ID 5?
                je      done                    ;Yes, then it's a Pentium

                add     al,30h                          ;It's not a Pentium,
                mov     byte ptr msg_other[7],al        ;so report the family
                mov     dx,offset msg_other             ;ID directly
;
; Display the processor type and terminate.
;
done:           mov     ah,09h                  ;Display "It's a ..."
                int     21h                     ;message
                mov     ah,4Ch                  ;Terminate via DOS
                int     21h                     ;function 4Ch
main            endp

code            ends
                end     begin
