Contributor: BRUCE LACKLORE           

Unit CPUInfo;

{        Version 1.1.0.P

        Requires Borland Turbo Pascal version 6.0 or later to compile.
        Author:  Bruce J. Lackore.  Created Saturday, October 9, 1993.
}

{$IFDEF Test}
        {$A+,B-,D+,F-,G-,I+,L+,O-,R+,S+,V-,X+}
{$ELSE}
        {$A+,B-,D-,F-,G-,I-,L-,O-,R-,S-,V-,X+}
{$ENDIF}

{        This unit contains a handy gadget for determining the CPU speed.  It is NOT
        coded for the Pentium family (if anyone wants to take a shot at it, please
        let me know the results)!
}

Interface

Const
        Cpu8086                                                                          = 1;
        Cpu80286                                                                         = 2;
        Cpu80386                                                                         = 3;
        Cpu80486                                                                         = 4;

Function WhatCPU:  Word;

{        This function examines the CPU and returns a number corresponding to the
        CPU type;  1 for 8086, 3 for 80386, etc.  This procedure came right out of
        Neil Rubenking's Turbo Pascal 6.0 Techniques and Utilities (thanx Neil!).
}

Procedure CPUSpd(Var MHz, KHz:  Word);

{        This procedure is a ROUGH estimation of how fast the CPU is running in
        MegaHertz.  It was adapted from a C program found in the Intel forum of
        CIS written by Glenn Dill.  I had to do some finagling of the original code
        because C allows for a 32-bit UNSIGNED integer, whereas Pascal allows for a
        32-bit SIGNED integer (the LongInt), therefore, I was forced to reduce all
        calculations by 10 in order to get it to fit properly.
}

{ ************************************************************************** }

Implementation

Function WhatCPU;  Assembler;

        Asm  { Function WhatCPU }
                        MOV            DX,Cpu8086
                        PUSH           SP
                        POP            AX
                        CMP            SP,AX
                        JNE                 @OUT
                        MOV                 DX,Cpu80286
                        PUSHF
                        POP            AX
                        OR             AX,4000h
                        PUSH           AX
                        POPF
                        PUSHF
                        POP            AX
                        TEST           AX,4000h
                        JE             @OUT
                        MOV                 DX,Cpu80386                        {        "DB 66h" makes '386 extended instruction }
                        DB 66h; MOV BX,SP              {        MOV EBX,ESP }
                        DB 66h, 83h, 0E4h, 0FCh {        AND ESP,FFFC        }
                        DB 66h; PUSHF           {        PUSHFD }
                        DB 66h; POP AX          {        POP EAX        }
                        DB 66h; MOV CX, AX      {        MOV ECX,EAX }
                        DB 66h, 35h, 00h
                        DB 00h, 04h, 00         {        XOR EAX,00040000        }
                        DB 66h; PUSH AX         {        PUSH EAX }
                        DB 66h; POPF            {        POPFD }
                        DB 66h; PUSHF           {        PUSHFD }
                        DB 66h; POP AX          {        POP EAX }
                        DB 66h, 25h,00h
                        DB 00h, 04h,00h                {        AND EAX,00040000 }
                        DB 66h, 81h,0E1h,00h
                        DB 00h, 04h,00h                {        AND ECX,00040000 }
                        DB 66h; CMP AX,CX              {        CMP EAX,ECX }
                        JE @Not486
                        MOV DX, Cpu80486
                @Not486:
                        DB 66h; PUSH CX         {        PUSH ECX }
                        DB 66h; POPF            {        POPFD }
                        DB 66h; MOV SP, BX      {        MOV ESP,EBX }
                @Out:
                        MOV AX, DX
        End;        { Function WhatCPU }

Procedure CPUSpd;

        Const
                Processor_cycles:                                Array[0..4] of Byte = (165, 165, 25, 103, 42);
                                                                                                                {        Cycle times of 8086, 80186, 80286, 80386, 80486}

                {        Notice that here I have defined the 8086 as a Processor type of 0 vice
                        the returned value of 1 from WhatCPU.  Since the original code did not
                        distinguish between the 8086 and the 80186, I can get away with this.
                }

        Var
                Ticks,
                Cycles,
                CPS:                                                                                LongInt;
                Which_CPU:                                                        Word;

        Function i86_to_i286:  Word;  Assembler;

                Asm  { Function i86_to_i286 }
                        CLI
                        MOV                CX,1234
                        XOR                DX,DX
                        XOR                AX,AX
                        MOV                AL,$B8
                        OUT                $43,AL
                        IN                AL,$61
                        OR                AL,1
                        OUT                $61,AL
                        XOR                AL,AL
                        OUT                $42,AL
                        OUT                $42,AL
                        XOR                AX,AX
                        IDIV        CX
                        IDIV        CX
                        IDIV        CX
                        IDIV        CX
                        IDIV        CX
                        IDIV        CX
                        IDIV        CX
                        IDIV        CX
                        IDIV        CX
                        IDIV        CX
                        IDIV        CX
                        IDIV        CX
                        IDIV        CX
                        IDIV        CX
                        IDIV        CX
                        IDIV        CX
                        IDIV        CX
                        IDIV        CX
                        IDIV        CX
                        IDIV        CX
                        IN                AL,$42
                        MOV                AH,AL
                        IN                AL,$42
                        XCHG        AL,AH
                        NEG                AX
                        STI
                End;  { Function i86_to_i286 }

        Function i386_to_i486:  Word;        Assembler;

                Asm  { Function i386_to_i486 }
                        CLI
                        MOV                AL,$B8
                        OUT                $43,AL
                        IN                AL,$61
                        OR                AL,1
                        OUT                $61,AL
                        XOR                AL,AL
                        OUT                $42,AL
                        OUT                $42,AL
                        DB 66H,$B8,00h,00h,00h,80h;
                        DB 66H,0FH,$BC,$C8;                                {        BSF        ECX,EAX }
                        DB 66H,0FH,$BC,$C8;                                {        BSF        ECX,EAX }
                        DB 66H,0FH,$BC,$C8;                                {        BSF        ECX,EAX }
                        DB 66H,0FH,$BC,$C8;                                {        BSF        ECX,EAX }
                        DB 66H,0FH,$BC,$C8;                                {        BSF        ECX,EAX }
                        DB 66H,0FH,$BC,$C8;                                {        BSF        ECX,EAX }
                        DB 66H,0FH,$BC,$C8;                                {        BSF        ECX,EAX }
                        DB 66H,0FH,$BC,$C8;                                {        BSF        ECX,EAX }
                        DB 66H,0FH,$BC,$C8;                                {        BSF        ECX,EAX }
                        DB 66H,0FH,$BC,$C8;                                {        BSF        ECX,EAX }
                        DB 66H,0FH,$BC,$C8;                                {        BSF        ECX,EAX }
                        DB 66H,0FH,$BC,$C8;                                {        BSF        ECX,EAX }
                        DB 66H,0FH,$BC,$C8;                                {        BSF        ECX,EAX }
                        DB 66H,0FH,$BC,$C8;                                {        BSF        ECX,EAX }
                        DB 66H,0FH,$BC,$C8;                                {        BSF        ECX,EAX }
                        DB 66H,0FH,$BC,$C8;                                {        BSF        ECX,EAX }
                        DB 66H,0FH,$BC,$C8;                                {        BSF        ECX,EAX }
                        DB 66H,0FH,$BC,$C8;                                {        BSF        ECX,EAX }
                        DB 66H,0FH,$BC,$C8;                                {        BSF        ECX,EAX }
                        DB 66H,0FH,$BC,$C8;                                {        BSF        ECX,EAX }
                        IN                AL,42H
                        MOV   AH,AL
                        IN                AL,42H
                        XCHG        AL,AH
                        NEG                AX
                        STI
                End;  { Function i386_to_486 }

        Begin  { Procedure CPUSpd }
                Which_CPU := WhatCPU;
                If Which_cpu < 3 Then
                        Ticks := i86_to_i286
                Else
                        Ticks := i386_to_i486;
                Cycles := 20 * Processor_cycles[Which_CPU];
                CPS := (Cycles * 119318) Div Ticks;
                MHz := CPS Div 100000;
                KHz := (CPS Mod 100000 + 500) Div 1000
        End;  { Procedure CPUSpd }

End.  { Unit CPUInfo }

{ ---------------------   TEST PROGRAM --------------------------}

Program CPUSpeed;

{        Version 1.0.0.T.

        Requires Borland Turbo Pascal version 6.0 or later to compile.

        Author:  Bruce J. Lackore.  Created Saturday, October 9, 1993.
}

{$IFDEF Test}
        {$A+,B-,D+,E+,F-,G-,I+,L+,N-,R+,S+,V-,X+}
{$ELSE}
        {$A+,B-,D-,E+,F-,G-,I-,L-,N-,R-,S-,V-,X+}
{$ENDIF}

{$M 1024, 0, 0}

Uses CPUInfo;

Var
        MHz,
        KHz:                                                                                        Word;

Begin  { Program: Cpuspeed }
        CpuSpd(MHz, KHz);
        Writeln('The CPU speed is ', MHz, '.', KHz, ' MHz.')
End.  { Program:  Cpuspeed }