{
Here is some code that identifies the CPU and FPU of your computer.  It
demonstrates coordination of INLINE, ASM and standard Pascal code.  This
routine uses INTEL recommended CPU identification techniques.  It also
demontrates two techniques that may be used to address the extended 
registers of the 386 and 486.

Donated to the public domain.  John D. Leonard II
}

unit cpuid;

interface


Procedure CPUFPUID(var CPU,FPU:integer);

   {- This routine determine the type of CPU and FPU (Floating Point Unit)
      using INTEL recommended techniques as posted in CPUID.ASM on the
      PCEO Intel Forum.  It has been supplemented with a check for the
      386SX }

   {-Returns 1: 8088/8086
             2: 80286
             3: 80386   -3: 80386SX
             4: 80486 -}

   {-This routine does not require external OBJ files, all assembler
     calls are implemented using ASM directive or INLINE code. -}

   {-Use of INLINE was required, as the TP6 ASM processor does not
     recognize 386/486 specific register addressing -}

   {-The procedure first determines the CPU type by filtering through
     a series of tests.  Then, the FPU check is performed. -}

   {-This procedure demonstrates the use of ASM directives and INLINE
     code, in conjunction with regular TP assignments and labels. -}

   {-This code as been tested with 8088, 80286, 80386, 80386sx and 80486,
     (Yes, Virginia, we have them all sitting around the place.) }

{ ==================================================================== }

implementation


Procedure CPUFPUID(var CPU,FPU:integer);
   label check_fpu,stop,restore_eflags;
   var fp_status:word;
   begin

{- Begin a series of tests to determine CPU type ... -}

{- On an 8086/8088 Flag Bits 12-15 are always set -}
      cpu := 1;
      asm
         PUSHF
         POP   BX
         MOV   AX,0FFFH
         AND   AX,BX
         PUSH  AX
         POPF
         PUSHF
         POP   AX
         AND   AX,0F000H
         CMP   AX,0F000H
         JE    CHECK_FPU
      end;

{- On a 80286 Flag Bits 12-15 are always clear -}

      cpu := 2;
      asm
         OR    BX,0F000H
         PUSH  BX
         POPF
         PUSHF
         POP   AX
         AND   AX,0F000H
         JZ    CHECK_FPU
      end;

{- On a 80486, bit 18 of EFLAGS can be set.  It can't be set on 386.
   (This bit relates to generation of alignment faults. )
   Note the use of DB 66H in this code for reference to the
   386 extended registers.  It acts like a SHIFT key, allowing direct
   translation of some addressing commands between the standard
   registers and the 386/486 extended registers. }

      cpu := 4;
      asm

      {- Align Stack to prevent a fault -}

         MOV   DX,SP            { MOV    DX, SP     }
         AND   SP, NOT 3        { AND    SP, NOT 3  }

      {- Get EFLAGS and store in ECX for later comparison and restoration }

         DB    66H
         PUSHF                  { PUSHFD            }
         DB    66H
         POP   AX               { POP    EAX        }
         DB    66H
         MOV   CX,AX            { MOV    ECX,EAX    }


      {- Set Bit #18.  Note use of DW to get large integer -}

         DB    66H
         XOR   AX,0
         DW    4H               { XOR    EAX,40000H }

      {- Stick the result into EFLAGS, and get it back.  Keep in EAX -}

         DB    66H
         PUSH  AX               { PUSH   EAX        }
         DB    66H
         POPF                   { POPFD             }
         DB    66H
         PUSHF                  { PUSHFD            }
         DB    66H
         POP   AX               { POP    EAX        }

      {- Restore Flags and Stack before making comparison -}

         DB    66H
         PUSH  CX               { PUSH   ECX        }
         DB    66H
         POPF                   { POPFD             }
         MOV   SP,DX            { MOV    SP,DX      }

         DB    66H
         XOR   AX,CX            { XOR    EAX,ECX    }
         JNZ   CHECK_FPU

      end;


{- On a 386, the coprocessor type bit in CR0 can be set.  On a 386Sx,
   it can not be set.  NOTE: this test is not a part of the recommended
   INTEL CPU check code.  ALSO NOTE: this code can not determine early
   versions of the 386sx, as they allow this bit to be set.  INLINE was
   required, as there is no way to "shift" ala "DB 66H" to access CR0. }


      cpu := -3;
      inline( $0f/$20/$c0 );         { MOV EAX,CR0 }
      inline( $66/$8b/$c8 );         { MOV ECX,EAX }
      inline( $66/$83/$F0/$10);      { MOV EAX,10H }
      inline( $0f/$22/$c0 );         { MOV CR0,EAX }
      inline( $0f/$20/$c0 );         { MOV EAX,CR0 }
      inline( $0f/$22/$c1 );         { MOV CR0,ECX }
      inline( $66/$33/$c1 );         { XOR EAX,ECX }
      asm
         JZ CHECK_FPU
      end;

{- The default CPU type, if all else fails. -}

      cpu := 3;


{- Begin CoProcessor Check.  We first check if the FPU can be initialized.
   If not, there is no FPU.  If we have got one, it will, in general
   match the CPU type, i.e. 8087 goes with 8086/88, 287 goes with 286, etc.
   The only exception is the 386, which may use either the 387 or 287. }


   CHECK_FPU:

{- Can we initialize the CoProcessor? -}

      FPU := 0;
      asm
         FNINIT
         MOV    FP_STATUS,5A5AH
         FNSTSW FP_STATUS
         MOV    AX,FP_STATUS
         JNE    STOP
         FNSTCW FP_STATUS
         MOV    AX,FP_STATUS
         AND    AX,103FH
         CMP    AX,3FH
         JNE    STOP
      end;

{- Yes!  Assign the corresponding FPU based on CPU -}

      FPU := abs(CPU);

{- However, if we have a 386/386SX, we must determine 387 or 287.  A 387
   differentiates between positive and negative infinity, a 287 does not. }

      if FPU=3 then begin

         FPU := 2;
         asm
            FLD1
            FLDZ
            FDIV
            FLD    ST
            FCHS
            FCOMPP
            FSTSW  FP_STATUS
            MOV    AX,FP_STATUS
            SAHF
            JZ     STOP
         end;

         fpu := 3;

      end;


   stop:


   end;




end.
