[Back to DELPHI SWAG index]  [Back to Main SWAG index]  [Original]


{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). }

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.

[Back to DELPHI SWAG index]  [Back to Main SWAG index]  [Original]