_processor Detection Schemes_
by Richard C. leinecker
[LISTING One]
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Detect the Processor Type-by Richard C. leinecker;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
_ptext SEGMENT PARA Public ' CODE '
Assume Cs:_ptext, Ds:_ptext
Public _processor
; This routine returns the processor type as an integer value.
; C Prototype
; int Processor (void);
; returns:0 = 8088, 1 = 8086, 2 = 80286, 3 = = 80386, 4 = = 80486
; Code assumes es, AX, BX, CX, and DX can be altered. If their contents
; Must is preserved, then you'll have to push and pop them.
_processor proc Far
Push BP; Preserve bp
MOV bp,sp; bp = sp
Push DS; Preserve DS
Push di; and Di
MOV Ax,cs; Point DS
MOV ds,ax; To _ptext
Call IsItAn8088; Returns 0 or 2
CMP al,2; 2 = 286 or better
Jge AtLeast286; Go to 286 and above code
Call IsItAn8086; Returns 0 or 1
JMP short exitprocessor; Jump to exit code
AtLeast286:
Call IsItA286; If it ' s a 286
CMP al,3; 4 and above for 386 and up
JL Short Exitprocessor; Jump to exit code
AtLeast386:
Call IsItA386; If it ' s a 386
Exitprocessor:
Pop di; Restore di,
Pop ds; Ds
Pop bp; and BP
RET; Return to caller
_processor ENDP
; Is It a 8088?
; Returns ax==0 for 8088/8086, ax==2 for 80286 and above
IsItAn8088 proc
PUSHF; Preserve the Flags
PUSHF; Push the flags So
Pop bx; We can pop them into BX
and BX,00FFFH; Mask off Bits 12-15
Push BX; And put it back on the stack
Popf; Pop value back into the flags
PUSHF; Push it back. 8088/8086 would
; Have bits 12-15 set
Pop bx; Get value into BX
and bx,0f000h; Mask all but bits 12-15
Sub Ax,ax; Set Ax to 8088 value
CMP bx,0f000h; If the bits are still set
Je Not286; Jump if not
MOV al,2; Set for 286 and above
Not286:
Popf; Restore the Flags
RET; Return to caller
IsItAn8088 ENDP
; Is It a 8086?
; Returns ax==0 for 8088, ax==1 for 8086
; Code takes advantage of the 8088 ' s 4-byte prefetch queues and 8086 ' s
; 6-byte prefetch queues. By self-modifying the code at a location exactly 5
; Bytes away from IP, and determining if the modification took effect,
; You can differentiate between 8088s and 8086s.
IsItAn8086 proc
MOV Ax,cs; Es = = Code segment
MOV Es,ax
STD; Cause STOSB to count backwards
MOV dx,1; DX is flag and we ' ll start at 1
mov Di,offset Endlabel; Di==offset of code tail to modify
MOV al,090h; al==nop instruction OpCode
MOV cx,3; Set for 3 repetitions
REP STOSB; Store the bytes
CLD; Clear the direction flag
NOP; Three Nops in a row
NOP; Provide dummy instructions
Nop
Dec DX; Decrement flag (only with 8088)
NOP; Dummy instruction--6 bytes
Endlabel:
Nop
MOV ax,dx; Store the flag in AX
RET; Back to Caller
IsItAn8086 ENDP
; Is It a 80286?
; Determines whether processor is a 286 or higher. Going into subroutine ax = 2
; If The processor is a 386 or higher, Ax would be 3 before returning. The
; Method are to set AX to 7000h which represent the 386/486 NT and IOPL bits
; This is pushed onto the stack and popped to the flags (with Popf).
; The flags are then pushed back onto the stack (with PUSHF). Only a 386 or 486
; Would keep the 7000h bits set. If it ' s a 286, those bits aren ' t defined and
; When the flags are pushed onto stack, these bits would be 0. Now, when the ax is
; Popped these bits can be checked. If they ' re set, we have a 386 or 486.
IsItA286 proc
PUSHF; Preserve the Flags
MOV ax,7000h; Set the NT and IOPL flag
; BITS only available for
; 386 processors and above
Push ax; Push Ax so we can pop 7000h
; into the flag register
Popf; Pop 7000h off of the stack
PUSHF; Push the flags back on
Pop ax; Get the pushed flags
; into ax
and ah,70h; If the NT and IOPL
; Flags are still set
MOV ax,2; Set AX to the 286 value
JZ YesItIsA286; If NT and IOPL not set
; It ' s a 286
Inc. AX; Ax now are 4 to indicate
; 386 or higher
YesItIsA286:
Popf; Restore the Flags
RET; Return to caller
IsItA286 ENDP
; Is It an 80386 or 80486?
; Determines whether processor is a 386 or higher. Going into subroutine ax=3
; If The processor is a 486, Ax would be 4 before leaving. The method was to set
; The AC bit of the flags via EAX and the stack. If It stays set, it ' s a 486.
IsItA386 proc
MOV di,ax; Store the processor value
MOV bx,sp; Save SP
and sp,0fffch; Prevent alignment fault
.386
PUSHFD; Preserve the Flags
PUSHFD; Push so we can get flags
Pop eax; Get flags into EAX
or eax,40000h; Set the AC bit
push eax; Push back on the stack
POPFD; Get the value into flags
PUSHFD; Put the value back on stack
Pop eax; Get value into EAX
Test eax,40000h; If the bit is set
JZ YesItIsA386; If not we have a 386
Add di,2; Increment to indicate 486
YesItIsA386:
POPFD; Restore the Flags
.8086
MOV sp,bx; Restore SP
MOV ax,di; Put processor value into ax
RET; Back to Caller
IsItA386 ENDP
_ptext ENDS
End
[LISTING Two]
.586
; Pentium detect routine. Call only after verifying processor be an i486 or
; Later. Returns 4 If i486, 5 if Pentium, 6 or greater for future
; Intel processors.
ef_idequ200000h; ID bit in EFlags register
Pentium proc Near
; Check for Pentium or later by attempting to toggle the ID bit in EFlags reg;
; If we can ' t, it ' s an i486.
PUSHFD; Get Current Flags
Popeax;
Movebx,eax;
xoreax,ef_id; Attempt to toggle ID bit
Pusheax;
POPFD;
PUSHFD; Get new EFlags
Popeax;
PUSHEBX; Restore Original Flags
POPFD;
andeax,ef_id; If we couldn ' t toggle ID, it ' s an i486
andebx,ef_id;
CMPEAX,EBX;
Jeshort Is486;
; It ' s a Pentium or later. Use CPUID to get processor family.
moveax,1; Get processor Info form of CPUID
CPUID;
shrax,8; Get Family field; 5 means Pentium.
ANDAX,0FH;
Ret
Is486:
movax,4; Return 4 for i486
Ret
Pentium ENDP
[LISTING THREE]
#pragma inline
Main ()
{
Long Start1;
Long End1;
Long Start2;
Long End2;
Start1 = Clock ();
ASM P386
ASM mov eax,10000000
ASM Lea EBX,LOOP1
ASM LOOP1:
ASM Dec EAX
ASM JZ LOOP1E
ASM JMP EBX
LOOP1E:
Start2 = End1 = Clock ();
ASM mov eax,10000000
ASM Lea EBX,LOOP2
ASM NOP
ASM LOOP2:
ASM Dec EAX
ASM JZ LOOP2E
ASM JMP EBX
LOOP2E:
End2 = Clock ();
printf ("misaligned loop time =%d, aligned loop time =%d
",
(int) (END1-START1), (int) (END2-START2));
Return
}