;****************** CPU.ASM -- Check CPU type, recognizes all CPUs

Ideal

Model Tiny
P586
CodeSeg
Org 100h

Proc        Prog

            mov ah,9                ;Print first part
            mov dx,offset CPUStr
            int 21h

            call CheckCPU           ;Check CPU

            test ax,ax              ;Zero, it's an 8086
            je Prt8086

            add al,'0'              ;Otherwise it's an 80x86 (x = 1+)
            mov [CPUStrA],al        ;Set byte... ASCII

            mov ah,9                ;Print second part
            mov dx,offset CPUStrA
            int 21h

            jmp Continue

Prt8086:    mov ah,9                ;Print second part
            mov dx,offset CPUStrA+1
            int 21h

Continue:   call CheckFPU           ;Check CPU

            cmp ax,-1               ;Zero, FPU not present
            je PrtNope

            add al,'0'              ;Otherwise it's an 80x87 (x = 1+)
            mov [FPUStrA],al        ;Set byte... ASCII

            mov ah,9                ;Print first part
            mov dx,offset FPUStr
            int 21h

            mov ah,9                ;Print second part
            mov dx,offset FPUStrA
            cmp al,'0'
            jne $+3
            inc dx
            int 21h

            jmp Done

PrtNope:    mov ah,9                ;Print other string
            mov dx,offset FPUNope
            int 21h

Done:       ret                     ;Return

EndP        Prog

CPUStr      db 'The CPU is an 80$'  ;Strings
CPUStrA     db ?, '86.',13,10,'$'
FPUStr      db 'The FPU is an 80$'
FPUStrA     db ?, '87.',13,10,'$'
FPUNope     db 'The FPU is not present$'

;**************************** CheckCPU -- Returns CPU level in AX

Proc        CheckCPU

            push es            ;Save ES
            pusha              ;Save gen. regs

            call GetCPU        ;Call main procedure

            shr ax,8           ;AL = AH, AH = 0
            mov es,ax          ;ES = AX
            popa               ;Restore gen. regs
            mov ax,es          ;Return value in AX
            pop es             ;Restore ES
            ret                ;Return


GetCPU:     mov al,1           ;If AL can be shifted
            mov cl,21h         ;by 21h, then it's an
            shl al,cl          ;8086, because a 186+
            jz Is8086          ;limits shift counts.

            push sp            ;If SP is pushed as its
            pop ax             ;original value, then
            cmp ax,sp          ;it's a 286+.
            jne Is186

            pushf              ;Save flags
            cli                ;No interrupts
            pushf              ;AX = flags
            pop ax
            mov bx,ax          ;BX = flags
            xor ax,7000h       ;Toggle IOPL bit
            push ax            ;Flags = AX
            popf
            pushf              ;AX = flags
            pop ax
            popf               ;Restore flags
            cmp ax,bx          ;If the bit was not
            je Is286           ;reset, it's a 386+

            push bp            ;Align stack to dword
            mov bp,sp
            and sp,0FFFCh
            pushfd             ;Save eflags
            cli                ;No interrupts
            pushfd             ;EAX = eflags
            pop eax
            mov ebx,eax        ;EBX = eflags
            xor eax,40000h     ;Toggle AC bit
            push eax           ;Eflags = EAX
            popfd
            pushfd             ;EAX = eflags
            pop eax
            popfd              ;Restore eflags
            mov sp,bp          ;Restore stack
            pop bp
            cmp eax,ebx        ;If the bit was not
            je Is386           ;reset, it's a 486+

            pushfd             ;Save eflags
            cli                ;No interrupts
            pushfd             ;EAX = eflags
            pop eax
            mov ebx,eax        ;EBX = eflags
            xor eax,200000h    ;Toggle ID bit
            push eax           ;Eflags = EAX
            popfd
            pushfd             ;EAX = eflags
            pop eax
            popfd              ;Restore eflags
            cmp eax,ebx        ;If the bit was not
            je Is486           ;reset, it's a 586+

            xor eax,eax        ;EAX = 1
            inc ax
            cpuid              ;Get CPU type
            ret                ;Return

Is486:      mov ah,4           ;486, return 4
            ret

Is386:      mov ah,3           ;386, return 3
            ret

Is286:      mov ah,2           ;286, return 2
            ret

Is186:      mov ah,1           ;186, return 1
            ret

Is8086:     mov ah,0           ;8086, return 0
            ret

EndP        CheckCPU

;**************************** CheckFPU -- Returns FPU level in AX
;                                         AX = -1 means no FPU

Proc        CheckFPU

            push es            ;Save ES
            pusha              ;Save gen. regs

            call GetFPU        ;Get FPU type

            mov es,ax          ;ES = AX
            popa               ;Restore gen. regs
            mov ax,es          ;Return value in AX
            pop es             ;Restore ES
            ret                ;Return

P8086                          ;8087 instructions
P8087

GetFPU:     fninit             ;Initialize FPU
            mov [Junk],55AAh   ;Set junk value
            fnstsw [Junk]      ;Store status word
            cmp [byte Junk],0  ;If it's not 0, no FPU
            jne NoFPU
            fnstcw [Junk]      ;Store control word
            mov ax,[Junk]      ;If the bits are not the way
            and ax,103Fh       ;they should be, no FPU
            cmp ax,3Fh
            jne NoFPU

            and [Junk],0FF7Fh  ;Clear interrupt bit
            fldcw [Junk]       ;Load control word
            fdisi              ;Disable interrupts
            fstcw [Junk]       ;Store control word
            test [Junk],80h    ;If it changed, it's an 8087
            jnz Is8087
P286                           ;Now 287 instructions
P287

            finit              ;Re-initialize
            fld1               ;Divide 1 by 0 to get
            fldz               ;a positive infinity
            fdiv
            fld st             ;Get a negative infinity
            fchs
            fcompp             ;Compare them
            fstsw ax           ;Store status word
            sahf               ;If the FPU thought that they
            je Is287           ;were equal, it's a 287

            mov ax,3           ;387, return 3
            finit              ;Init processor
            ret

Is287:      mov ax,2           ;287, return 2
            finit              ;Init processor
            ret
P8087                          ;Back to 8087
Is8087:     xor ax,ax          ;8087, return 0
            finit              ;Init processor
            ret

NoFPU:      mov ax,-1          ;No FPU, return -1
            ret

Junk        dw 0

EndP        CheckFPU

End Prog
