From: lisch@tempest-sw.com (Ray Lischner) 
Subject: Re: Delphi: Pentium 
Date: Mon, 19 Jun 1995 05:06:03 GMT 
 
>How can I detect a Pentium processor from within a Delphi program?   
 
Here is a Delphi unit to detect the CPU type, modified from Intel's 
code. Use should be fairly obvious.  If not, send me email, and I can 
send you an example program.  Because Delphi's assembler is 16-bit, 
the code looks a little wierd.  Try using a 32-bit disassembler to see 
the 32-bit instructions (or read the comments). 
 
-----cut here-----8<-------cut here------8<--------- 
unit CpuId; 
 
{ This code comes from Intel, and has been modified for Delphi's 
  inline assembler.  Since Intel made the original code freely 
  available, I am making my changes freely available. 
 
  Share and enjoy! 
 
  Ray Lischner 
  Tempest Software 
  6/18/95 
} 
 
interface 
 
type 
  { All the types currently known.  As new types are created, 
    add suitable names, and extend the case statement in 
    CpuTypeString. 
  } 
  TCpuType = (cpu8086, cpu80286, cpu386, cpu486, cpuPentium); 
 
{ Return the type of the current CPU } 
function CpuType: TCpuType; 
 
{ Return the type as a short string } 
function CpuTypeString: String; 
 
implementation 
 
uses SysUtils; 
 
function CpuType: TCpuType; assembler; 
asm 
  push DS 
 
{ First check for an 8086 CPU } 
{ Bits 12-15 of the FLAGS register are always set on the } 
{ 8086 processor. } 
  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 	ax, cpu8086     { turn on 8086/8088 flag } 
  je		@@End_CpuType 
 
  { 80286 CPU check } 
  { Bits 12-15 of the FLAGS register are always clear on the } 
  { 80286 processor. } 
  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 	ax, cpu80286      { turn on 80286 flag } 
  jz		@@End_CpuType 
 
  { To test for 386 or better, we need to use 32 bit instructions, 
    but the 16-bit Delphi assembler does not recognize the 32 bit 
opcodes 
    or operands.  Instead, use the 66H operand size prefix to change 
    each instruction to its 32-bit equivalent. For 32-bit immediate 
    operands, we also need to store the high word of the operand 
immediately 
    following the instruction.  The 32-bit instruction is shown in a 
comment 
    after the 66H instruction. 
  } 
 
  { 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. } 
 
  db 66h                    { pushfd } 
  pushf 
  db 66h                    { pop eax } 
  pop	ax		                { get original EFLAGS } 
  db 66h                    { mov ecx, eax } 
  mov	cx,ax		             { save original EFLAGS } 
  db 66h                    { xor eax,40000h } 
  xor	ax,0h	                { flip AC bit in EFLAGS } 
  dw 0004h 
  db 66h                    { push eax } 
  push ax			          { save for EFLAGS } 
  db 66h                    { popfd } 
  popf				          { copy to EFLAGS } 
  db 66h                    { pushfd } 
  pushf				          { push EFLAGS } 
  db 66h                    { pop eax } 
  pop	ax		                { get new EFLAGS value } 
  db 66h                    { xor eax,ecx } 
  xor	ax,cx		             { can't toggle AC bit, CPU=Intel386 } 
  mov ax, cpu386            { turn on 386 flag } 
  je @@End_CpuType 
 
{ 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. } 
  db 66h                    { pushfd } 
  pushf				          { push original EFLAGS } 
  db 66h                    { pop eax } 
  pop	ax		                { get original EFLAGS in eax } 
  db 66h                    { mov ecx, eax } 
  mov	cx,ax		             { save original EFLAGS in ecx } 
  db 66h                    { xor eax,200000h } 
  xor	ax,0h	                { flip ID bit in EFLAGS } 
  dw 0020h 
  db 66h                    { push eax } 
  push ax			          { save for EFLAGS } 
  db 66h                    { popfd } 
  popf				          { copy to EFLAGS } 
  db 66h                    { pushfd } 
  pushf                     { push EFLAGS } 
  db 66h                    { pop eax } 
  pop	ax		                { get new EFLAGS value } 
  db 66h                    { xor eax, ecx } 
  xor ax, cx 
  mov ax, cpu486            { turn on i486 flag } 
  je @@End_CpuType	       { 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. } 
   db 66h                  { mov eax, 1 } 
	mov ax, 1			      { set up for CPUID instruction } 
   dw 0 
   db 66h                  { cpuid } 
	db	0Fh	               { Hardcoded opcode for CPUID 
instruction } 
	db	0a2h 
   db 66h                  { and eax, 0F00H } 
	and ax, 0F00H	         { mask everything but family } 
   dw 0 
   db 66h                  { shr eax, 8 } 
	shr ax, 8               { shift the cpu type down to the low byte } 
   sub ax, 1               { subtract 1 to map to TCpuType } 
 
@@End_CpuType: 
   pop ds 
end; 
 
function CpuTypeString: String; 
var 
  kind: TCpuType; 
begin 
  kind := CpuType; 
  case kind of 
  cpu8086: 
    Result := '8086'; 
  cpu80286: 
    Result := '80286'; 
  cpu386: 
    Result := '386'; 
  cpu486: 
    Result := '486'; 
  cpuPentium: 
    Result := 'Pentium'; 
  else 
    { Try to be flexible for future cpu types, e.g., P6. } 
    Result := Format('P%d', [Ord(kind)]); 
  end; 
end; 
 
end. 
 
-- 
Ray Lischner       (lisch@tempest-sw.com) 
Tempest Software 
 
