CPUTYPE.TXT

                   ┌────────────────────────────┐
                   │ Testing the Intel CPU Type │
                   └────────────────────────────┘

            Written for the PC-GPE by Mark Feldman
            e-mail address : u914097@student.canberra.edu.au
                             myndale@cairo.anu.edu.au

           ┌───────────────────────────────────────────┐
           │      THIS FILE MAY NOT BE DISTRIBUTED     │
           │ SEPERATE TO THE ENTIRE PC-GPE COLLECTION. │
           └───────────────────────────────────────────┘


┌────────────┬───────────────────────────────────────────────────────────────
│ Disclaimer │
└────────────┘

I assume no responsibility whatsoever for any effect that this file, the
information contained therein or the use thereof has on you, your sanity,
computer, spouse, children, pets or anything else related to you or your
existance. No warranty is provided nor implied with this information.

┌──────────────┬─────────────────────────────────────────────────────────────
│ Introduction │
└──────────────┘

Believe it or not there are people in the world who still own 8088s! Heck I
only just upgraded my 286 to a 486SUX33 a couple of months ago.

As we all know, 286's and below just don't make the cut anymore, and even
386's are becoming a thing of the past. Personally I think a program should
politely tell someone they are a phleb rather than unceremoniously hanging
their machine for them.

This text file will show one mothed of detecting the CPU type. Unfortunately
I don't have a Pentium op code list so it'll only detect up to a 486.

Most of the information in this file came from a well documented assembly
program available on various ftp sites called 80486.asm, written by Robert
Collins. I tried calling Robert for permission to use his original file, but
he appears to have moved house.

┌────────┬───────────────────────────────────────────────────────────────────
│ Method │
└────────┘

80186 chips and higher generate an interrupt 6 when they come across an
instruction they don't support, this provides us with a real simple method
of determining the cpu type (coupled with the trap interrupt it would
conceivably also allow us to write a Pentium emulator for the 80286, but
that's another story). We simply have to try execting a 486 command, if it
causes an interrupt 6 then we know the machine is a 386 or lower.

The op codes used in the program below all modify the dx register.

     ┌───────────────────────────────────────────────────────────────┐
     │  Op Code           Machine Language Bytes   Supported by      │
     ├───────────────────────────────────────────────────────────────┤
     │  shl  dx, 5               C1 E2 05           80186 and higher │
     │  smsw dx                  0F 01 E2           80286 and higher │
     │  mov  edx, cr0            0F 20 C2           80386 and higher │
     │  xadd dx, dx              0F C1 D2           80486 and higher │
     └───────────────────────────────────────────────────────────────┘

When an interrupt 6 is generated you have to modify the value of the IP
register which was pushed onto the stack when the interrupt occurred,
otherwise the CPU will go back to it after the interrupt and keep trying
to execute it. Each of the instructions in the table above are 3 bytes long
so our interrupt handler can simply add 3 to the IP value on the stack.

Identifying an 8088 chip is even simpler. If you push the SP register onto
the stack the 8088 increments the SP value before it pushes it, the other
chips all increment it afterwards. So to test for the presence of an 8088
push SP onto the stack and pop it off into another variable, say AX. If AX
and SP are not equal then the chip is an 8088.

Keep in mind that you *MUST* check for the presence of an 8088 before
doing anything else. Attempting to execute an invalid op code on an 8088
will cause it to hang. Each of the functions in the unit below check for
an 8088 first to prevent this happening.

┌────────────────────────────────────┬───────────────────────────────────────
│ A Pascal Unit to Test the CPU Type │
└────────────────────────────────────┘

The following pascal unit contains some functions your program can use to
make sure it's running on the right kind of machine. If your program will
only work on a 386 and higher (for example) then put this unit first in your
Uses clause and modify the unit's initialization code to terminate the
program if the wrong CPU type is detected. The unit's current initialization
simply test the CPU type and store it in the 'cpu' variable.

{

  CPUTYPE - A Pascal Unit to Test the CPU Type
            By Mark Feldman u914097@student.canberra.edu
                            myndale@cairo.anu.edu.au

            Based on an original assembly program by Robert Collins.


}


Unit CPUTYPE;


Interface

const CPU_8088    =  0;
      CPU_80186   =  1;
      CPU_80286   =  2;
      CPU_80386   =  3;
      CPU_80486   =  4;
      CPU_UNKNOWN = -1;

{ The cpu variable is initialised to the cpu type }
var cpu : integer;

{ Isa8088 returns true only if cpu is an 8088 or 8086 }
function Isa8088 : boolean;

{ Isa80186 returns true if cpu is an 80186 or higher }
function Isa80186 : boolean;

{ Isa80286 returns true if cpu is an 80286 or higher }
function Isa80286 : boolean;

{ Isa80386 returns true if cpu is an 80386 or higher }
function Isa80386 : boolean;

{ Isa80486 returns true if cpu is an 80486 or higher }
function Isa80486 : boolean;




Implementation

Uses Dos;

var OldIntr6Handler : procedure;
      valid_op_code : boolean;

procedure Intr6Handler;
interrupt;
begin
  valid_op_code := false;

  { Stoopid TP7 won't let me modify IP directly }
  asm
    add word ptr ss:[bp + 18], 3
  end;
end;

function Isa8088 : boolean;
var sp1, sp2 : word;
begin
  asm
    mov sp1, sp
    push sp
    pop sp2
  end;
  if sp1 <> sp2 then
    Isa8088 := true
  else
    Isa8088 := false;
end;

function Isa80186 : boolean;
begin
  if Isa8088 then
    Isa80186 := false
  else
    begin
      valid_op_code := true;
      GetIntVec(6, @OldIntr6Handler);
      SetIntVec(6, Addr(Intr6Handler));
      inline($C1/$E2/$05);  { shl dx, 5 }
      SetIntVec(6, @OldIntr6Handler);
      Isa80186 := valid_op_code;
    end;
end;

function Isa80286 : boolean;
begin
  if Isa8088 then
    Isa80286 := false
  else
    begin
      valid_op_code := true;
      GetIntVec(6, @OldIntr6Handler);
      SetIntVec(6, Addr(Intr6Handler));
      inline($0F/$01/$E2);  { smsw dx }
      SetIntVec(6, @OldIntr6Handler);
      Isa80286 := valid_op_code;
    end;
end;

function Isa80386 : boolean;
begin
  if Isa8088 then
    Isa80386 := false
  else
    begin
      valid_op_code := true;
      GetIntVec(6, @OldIntr6Handler);
      SetIntVec(6, Addr(Intr6Handler));
      inline($0F/$20/$C2);  { mov edx, cr0 }
      SetIntVec(6, @OldIntr6Handler);
      Isa80386 := valid_op_code;
    end;
end;

function Isa80486 : boolean;
begin
  if Isa8088 then
    Isa80486 := false
  else
    begin
      valid_op_code := true;
      GetIntVec(6, @OldIntr6Handler);
      SetIntVec(6, Addr(Intr6Handler));
      inline($0F/$C1/$D2);  { xadd dx, dx }
      SetIntVec(6, @OldIntr6Handler);
      Isa80486 := valid_op_code;
    end;
end;


begin
  if Isa8088 then
    cpu := CPU_8088
  else if Isa80486 then
    cpu := CPU_80486
  else if Isa80386 then
    cpu := CPU_80386
  else if Isa80286 then
    cpu := CPU_80286
  else if Isa80186 then
    cpu := CPU_80186
  else
    cpu := CPU_UNKNOWN;
end.