JurassicParkTrespasser/jp2_pc/Source/Tools/Processor Detect/Detect.cpp
2018-01-01 23:07:24 +01:00

1466 lines
37 KiB
C++

/***********************************************************************************************
*
* Copyright © DreamWorks Interactive. 1997
*
* Contents:
* Processor detect DLL
*
* Bugs:
*
* To do:
*
* Notes:
*
***********************************************************************************************
*
* $Log:: /JP2_PC/Source/Tools/Processor Detect/Detect.cpp $
*
* 3 9/23/98 12:11p Rwyatt
*
* 2 9/23/98 12:11p Rwyatt
* Added support for K6-3and K7. Detection code is now future proof with respect to new models.
*
* 1 9/01/98 12:31a Rwyatt
* Added support for IDT WinChip and 3d now instructions are identified by feature and not
* maker.
*
* 3 10/24/97 1:51a Rwyatt
* Changed the 3DX/Cedar ID string to make it different from the standard K6.
*
* 2 10/24/97 1:17a Rwyatt
* Now detects K6 3DX/Cedar extentions
*
* 1 10/24/97 1:16a Rwyatt
* Initial implementation
*
***********************************************************************************************/
#include "Windows.h"
#ifdef _BUILD_EXTERNAL_
#include "..\DWITypes.h"
#include "..\Processor.hpp"
#else
#include "Lib/Std/UTypes.hpp"
#include "Lib/Sys/Processor.hpp"
#endif
//*********************************************************************************************
// This ststic is filled and then copied onto the one passed in, this is so we get another
// free register within the code.
//
static CPUInfo cpu;
static char strIntel[] = "GenuineIntel";
static char strAMD[] = "AuthenticAMD";
static char strCyrix[] = "CyrixInstead";
static char strIDT[] = "CentaurHauls";
static void SetGenericFlags(void);
static void DetectProcessor(void);
static void DetectFPU(void);
static uint32 u4CPUSpeed(int clocks);
static void MakeName(void);
static uint32 u4QueryCPUIDName(void);
//*********************************************************************************************
// Start of detection code
//*********************************************************************************************
//*********************************************************************************************
//
bool WINAPI DllMain(HINSTANCE hDLL, DWORD dwReason, LPVOID lpReserved)
{
return TRUE;
}
//*********************************************************************************************
// Declare the function below as extern "C" so it does not get its name mangled. This is the
// function that will be used in the call to GetProcAddress.
//
extern "C"
{
__declspec(dllexport) bool _cdecl bGetProcessorInfo(CPUInfo* pcpuProcessorInfo);
}
//*********************************************************************************************
//
__declspec(dllexport) bool _cdecl bGetProcessorInfo(CPUInfo* pcpuProcessorInfo)
{
// clear the destination structure.
memset(&cpu,0,sizeof(CPUInfo));
// we don't know anything about the chip
cpu.cpufamProcessorFamily = cpufamUNKNOWN;
cpu.cpufamFPUFamily = cpufamUNKNOWN;
cpu.cpumanProcessorManufacture = cpumanUNKNOWN;
DetectProcessor();
if (cpu.cpufamProcessorFamily == cpufamUNKNOWN)
return false;
// we need to do some adjusting for the non-Intel processors.
if (cpu.cpumanProcessorManufacture == cpumanAMD)
{
if (cpu.cpufamProcessorFamily == 4)
{
// a model 4 AMD is a AM486
cpu.cpufamProcessorFamily = cpufamAM486;
}
else if (cpu.cpufamProcessorFamily == 5)
{
// a model 5 AMD is either a K5 or K6, we need to check the stepping
switch ((cpu.u4Model & (0x0f<<4))>>4)
{
case 0:
case 1:
case 2:
case 3:
cpu.cpufamProcessorFamily = cpufamK5;
break;
case 4:
case 5:
cpu.cpufamProcessorFamily = cpufamUNKNOWN;
break;
case 6:
case 7:
cpu.cpufamProcessorFamily = cpufamK6;
break;
case 8:
cpu.cpufamProcessorFamily = cpufamK62;
break;
case 9:
case 10:
case 11:
case 12:
case 13:
case 14:
case 15:
cpu.cpufamProcessorFamily = cpufamK63;
break;
}
}
else if (cpu.cpufamProcessorFamily >= 6)
{
// Family model 6 on AMD are at least a K7
cpu.cpufamProcessorFamily = cpufamK7;
}
}
if (cpu.cpumanProcessorManufacture == cpumanCYRIX)
{
if (cpu.cpufamProcessorFamily == 5)
{
// model 5 Cyrix is the 686
cpu.cpufamProcessorFamily = cpufam686;
}
else if (cpu.cpufamProcessorFamily == 6)
{
// model 6 Cyrix is the M2
cpu.cpufamProcessorFamily = cpufamM2;
}
else if (cpu.cpufamProcessorFamily > 6)
{
// Future family is at least an M2
cpu.cpufamProcessorFamily = cpufamM2;
}
else
{
// If it is cyrix of model 4 or less the we do not know what
// it is.
cpu.cpufamProcessorFamily = cpufamUNKNOWN;
}
}
if (cpu.cpumanProcessorManufacture == cpumanIDT)
{
if (cpu.cpufamProcessorFamily == 5)
{
// a pentium class IDT chip is either a C6 or a WinChip
if ( (cpu.u4Model & (0x0f<<4))>>4 >= 8 )
{
cpu.cpufamProcessorFamily = cpufamWINCHIP2;
}
else if ( (cpu.u4Model & (0x0f<<4))>>4 == 4 )
{
cpu.cpufamProcessorFamily = cpufamWINCHIPC6;
}
else
{
cpu.cpufamProcessorFamily = cpufamUNKNOWN;
}
}
else if (cpu.cpufamProcessorFamily > 5)
{
// Future winchips muct be compatible with the WinChip2 so return it
cpu.cpufamProcessorFamily = cpufamWINCHIP2;
}
else
{
// IDT will never make a processor that has a family type less than 5.
// If they do then we have no idea what it is or what it is compatible
// with.
cpu.cpufamProcessorFamily = cpufamUNKNOWN;
}
}
// now we have identified the processor lets go and look for an FPU
// if CPUID is supported then this is simple we just chekc the bit and
// we can assume the FPU version is the same as the processor. The only
// device that varies is the 80386 which can use the FPU of the 80286.
if (cpu.u4CPUFlags & CPU_CPUID)
{
// if CPUID says that there is a FPU then it must be the same family
// as the processor
if (cpu.u4Feature & 1)
{
cpu.u4CPUFlags |= CPU_FPUPRESENT;
cpu.cpufamFPUFamily = cpu.cpufamProcessorFamily;
}
}
else
{
// this will detect a FPU on other processors without the CPUID instrucion
// The 386 is a special case because it can have a 287 attached to it,
// the DetectFPU function handles this and sets the result so only copy
// the family type into the FPU type if we are not a 386.
// this will also set the bit for an FPU present
DetectFPU();
if (cpu.u4CPUFlags & CPU_FPUPRESENT)
{
// if an FPU is present then set the FPU family
if (cpu.cpufamProcessorFamily != 3)
{
cpu.cpufamFPUFamily = cpu.cpufamProcessorFamily;
}
}
}
// Now lets derive the cache information in this machine
if (cpu.cpumanProcessorManufacture == cpumanINTEL)
{
// with an intel processor we much check if we can call CPUID with an input of 2
if (cpu.u4MaxCPUID>=2)
{
char cache[16];
int i;
// now call CPUID with an input of 2 to get the descriptors
cpu.u4CPUFlags |= CPU_CACHEINFO;
_asm
{
mov eax,2
cpuid
mov cl,al
dec cl
je short GOT_INTEL_CACHE
// ECX is the number of times we need to call CPUID
NEXT_ID:
mov eax,2
cpuid
dec cl
jnz short NEXT_ID
GOT_INTEL_CACHE:
// the registers are now set with the results of the CPUID[2]
mov DWORD PTR [cpu.au4ProcessorSpecific+0], eax
mov DWORD PTR [cpu.au4ProcessorSpecific+4], ebx
mov DWORD PTR [cpu.au4ProcessorSpecific+8], ecx
mov DWORD PTR [cpu.au4ProcessorSpecific+12], edx
mov al,0
mov DWORD PTR [cache+0], eax
mov DWORD PTR [cache+4], ebx
mov DWORD PTR [cache+8], ecx
mov DWORD PTR [cache+12], edx
}
for (i=0; i<16 ; i++)
{
// if the top bit of the cahce descriptors is clear then it is valid
if ((cache[i] & 0x80) == 0)
{
switch (cache[i])
{
case 0x06:
cpu.u4CodeCacheSize = 8;
cpu.u4CodeCacheLineSize = 32;
cpu.u4CodeCacheAssoc = 4;
break;
case 0x08:
cpu.u4CodeCacheSize = 16;
cpu.u4CodeCacheLineSize = 32;
cpu.u4CodeCacheAssoc = 4;
break;
case 0x0A:
cpu.u4DataCacheSize = 8;
cpu.u4DataCacheLineSize = 32;
cpu.u4DataCacheAssoc = 2;
break;
case 0x0C:
cpu.u4DataCacheSize = 16;
cpu.u4DataCacheLineSize = 32;
cpu.u4DataCacheAssoc = 2;
break;
case 0x40:
cpu.u4L2CacheSize = 0;
cpu.u4L2CacheLineSize = 0;
cpu.u4L2CacheAssoc = 0;
break;
case 0x41:
cpu.u4L2CacheSize = 128;
cpu.u4L2CacheLineSize = 32;
cpu.u4L2CacheAssoc = 4;
break;
case 0x42:
cpu.u4L2CacheSize = 256;
cpu.u4L2CacheLineSize = 32;
cpu.u4L2CacheAssoc = 4;
break;
case 0x43:
cpu.u4L2CacheSize = 512;
cpu.u4L2CacheLineSize = 32;
cpu.u4L2CacheAssoc = 4;
break;
}
}
}
}
}
else if ((cpu.cpumanProcessorManufacture == cpumanAMD) || (cpu.cpumanProcessorManufacture == cpumanIDT))
{
uint32 u4_level2 = 0;
// AMD and IDT are different to obtain the cache information, both of these manufactures use
// CPUID operation 0x80000005
_asm
{
mov eax,0x80000000 // extended AMD CPUID function
cpuid
cmp eax,0x80000005
jb short EXIT_AMD_CACHE // functions upto 0x80000005 must be present
mov eax,0x80000005 // extended AMD CPUID function
cpuid
mov DWORD PTR [cpu.au4ProcessorSpecific+56], eax
mov DWORD PTR [cpu.au4ProcessorSpecific+60], ebx
mov DWORD PTR [cpu.au4ProcessorSpecific+64], ecx
mov DWORD PTR [cpu.au4ProcessorSpecific+68], edx
or DWORD PTR [cpu.u4CPUFlags],CPU_CACHEINFO
mov eax,0x80000000 // extended AMD CPUID function
cpuid
cmp eax,0x80000006
jb short EXIT_AMD_CACHE // functions upto 0x80000006 must be present
mov eax,0x80000006 // extended AMD CPUID function for level 2 cache info
cpuid
mov DWORD PTR [cpu.au4ProcessorSpecific+72], eax
mov DWORD PTR [cpu.au4ProcessorSpecific+76], ebx
mov DWORD PTR [cpu.au4ProcessorSpecific+80], ecx
mov DWORD PTR [cpu.au4ProcessorSpecific+84], edx
// set the local flag saying we got level 2 cache info
mov DWORD PTR [u4_level2],0xffffffff
EXIT_AMD_CACHE:
}
if (u4_level2!=0)
{
// this device can get the size of the L2 cache
cpu.u4L2CacheSize = (cpu.au4ProcessorSpecific[20] & 0xffff0000)>>16;
cpu.u4L2CacheAssoc = (cpu.au4ProcessorSpecific[20] & 0x0000f000)>>12;
cpu.u4L2CacheLineSize = (cpu.au4ProcessorSpecific[20] & 0xf);
}
else
{
// this device cannot get the size of the L2 cache because it does
// not support CPUID 0x80000006
cpu.u4L2CacheSize = 0xffffffff;
}
cpu.u4DataCacheSize = (cpu.au4ProcessorSpecific[16] & 0xff000000)>>24;
cpu.u4DataCacheAssoc = (cpu.au4ProcessorSpecific[16] & 0x00ff0000)>>16;
cpu.u4DataCacheLineSize = (cpu.au4ProcessorSpecific[16] & 0xff);
cpu.u4CodeCacheSize = (cpu.au4ProcessorSpecific[17] & 0xff000000)>>24;
cpu.u4CodeCacheAssoc = (cpu.au4ProcessorSpecific[17] & 0x00ff0000)>>16;
cpu.u4CodeCacheLineSize = (cpu.au4ProcessorSpecific[17] & 0xff);
}
else
{
// unknown processor, cannot get cache info
cpu.u4CPUFlags &= ~CPU_CACHEINFO;
}
// measure the speed of this processor
cpu.u4CPUSpeed = u4CPUSpeed(0);
// finish off by doing general feature determination
switch(cpu.cpumanProcessorManufacture)
{
case cpumanINTEL:
SetGenericFlags();
// Some Pentium Pro's incorrectly set bit 11 in the feature flags when they could not do the
// fast system call operation. This code will sort it out.
if (cpu.u4Feature & (1<<11))
{
if ( (cpu.cpufamProcessorFamily == 6) && ( ((cpu.u4Model & (0x0f<<4))>>4) < 3) && ((cpu.u4Model & 0x04) < 3) )
{
cpu.u4CPUFlags &= ~CPU_SEP;
}
else
{
cpu.u4CPUFlags |= CPU_SEP;
}
}
break;
case cpumanAMD:
_asm
{
mov eax,0x80000000 // extended CPUID function
cpuid
mov [cpu.au4ProcessorSpecific+0], eax
cmp eax,0x80000001
jb short EXIT_AMD_EXT // functions upto 0x80000001 must be present
mov eax,0x80000001 // extended AMD CPUID function
cpuid
mov DWORD PTR [cpu.au4ProcessorSpecific+4], edx
}
// SEP
cpu.u4CPUFlags &= ~CPU_SEP;
if (cpu.au4ProcessorSpecific[1] & (1<<11) )
{
cpu.u4CPUFlags |= CPU_SEP;
}
// PAT
cpu.u4CPUFlags &= ~CPU_PAT;
if (cpu.au4ProcessorSpecific[1] & (1<<16) )
{
cpu.u4CPUFlags |= CPU_PAT;
}
// 3DX/CEDAR extentions..
if (cpu.au4ProcessorSpecific[1] & (1<<31) )
{
cpu.u4CPUFlags |= CPU_3DNOW;
}
EXIT_AMD_EXT:
SetGenericFlags();
if ((cpu.cpufamProcessorFamily >= cpufamK5) && (cpu.cpufamProcessorFamily < cpufamK7))
{
//These flags are reserved on K5 and all K6 processors so the feature cannot be
//present
cpu.u4CPUFlags &= ~(CPU_PAE|CPU_MCA);
}
if (cpu.cpufamProcessorFamily == cpufamK5)
{
// On the K5 model 0, bit 13 (global paging extentions) is reserved and bit 9
// is used to indicate global page extentions, which is usually used for APIC
// support.
// K5 Cannotsupport an APIC
cpu.u4CPUFlags &= ~CPU_APIC;
cpu.u4CPUFlags &= ~CPU_GPE;
if (cpu.u4Feature & (1<<9))
{
cpu.u4CPUFlags |= CPU_GPE;
}
}
break;
default:
//
// This is either an IDT WinChip, a cyrix or a completely unknown processor
//
if (cpu.u4CPUFlags & CPU_CPUID)
{
//
// We have a CPUID instruction, lets check some extened features that
// this clone may support.
//
_asm
{
mov eax,0x80000000 // extended CPUID function
cpuid
mov [cpu.au4ProcessorSpecific+0], eax
cmp eax,0x80000001
jb short NO_EXT // functions upto 0x80000001 must be present
mov edx,0
mov eax,0x80000001 // extended CPUID function
cpuid
cmp eax,0
je short NO_EXT // the feature word cannot be zero
mov DWORD PTR [cpu.au4ProcessorSpecific+4], edx
}
// 3DX/CEDAR extentions..
if (cpu.au4ProcessorSpecific[1] & (1<<31) )
{
cpu.u4CPUFlags |= CPU_3DNOW;
}
NO_EXT:;
}
//
// Parse the flags supported by the normal CPUID feature word
//
SetGenericFlags();
break;
}
// make a friendly string for this processor
MakeName();
// copy the final results into the structure passed in.
memcpy(pcpuProcessorInfo,&cpu,sizeof(CPUInfo));
return true;
}
//*********************************************************************************************
//
static void SetGenericFlags(void)
{
//
// We have no idea who made this chip so lets just check for some general features,
// if the CPUID instruction is present.
//
if (cpu.u4CPUFlags & CPU_CPUID)
{
// check for VME
if (cpu.u4Feature & (1<<1))
{
cpu.u4CPUFlags |= CPU_VME;
}
// check for extened DEBUG registers
if (cpu.u4Feature & (1<<2))
{
cpu.u4CPUFlags |= CPU_DEBUG;
}
// check for PSE
if (cpu.u4Feature & (1<<3))
{
cpu.u4CPUFlags |= CPU_PSE;
}
// check for RDTSC
if (cpu.u4Feature & (1<<4))
{
cpu.u4CPUFlags |= CPU_RDTSC;
}
// check for MSR
if (cpu.u4Feature & (1<<5))
{
cpu.u4CPUFlags |= CPU_MSR;
}
// check for PAE (physical address extentions)
if (cpu.u4Feature & (1<<6))
{
cpu.u4CPUFlags |= CPU_PAE;
}
// Machine Check exception
if (cpu.u4Feature & (1<<7))
{
cpu.u4CPUFlags |= CPU_MCE;
}
// check for the CMPXCHG8B instruction
if (cpu.u4Feature & (1<<8))
{
cpu.u4CPUFlags |= CPU_CMPXCHG8B;
}
// check for an APIC
if (cpu.u4Feature & (1<<9))
{
cpu.u4CPUFlags |= CPU_APIC;
}
// check for Fast system calls
if (cpu.u4Feature & (1<<11))
{
cpu.u4CPUFlags |= CPU_SEP;
}
// check for memory type range registers
if (cpu.u4Feature & (1<<12))
{
cpu.u4CPUFlags |= CPU_MTRR;
}
// check for Global paging extentions
if (cpu.u4Feature & (1<<13))
{
cpu.u4CPUFlags |= CPU_GPE;
}
// check for Machine check architecture
if (cpu.u4Feature & (1<<14))
{
cpu.u4CPUFlags |= CPU_MCA;
}
// check for CMOV instructions
if (cpu.u4Feature & (1<<15))
{
cpu.u4CPUFlags |= CPU_CMOV;
}
// check for Page Attribute Table
if (cpu.u4Feature & (1<<16))
{
cpu.u4CPUFlags |= CPU_PAT;
}
// check for MMX
if (cpu.u4Feature & (1<<23))
{
cpu.u4CPUFlags |= CPU_MMX;
}
}
if ( ((cpu.u4Model>>8) & 0xf) >= 5)
{
//Model is 5 or above so we say we can say we execute pentium code.
cpu.u4CPUFlags |= CPU_PENTIUM;
if (cpu.u4CPUFlags & CPU_CMOV)
{
// We have the Pentium pro conditional instructions so we say we
// can execute Pentium pro code.
cpu.u4CPUFlags |= CPU_PENTIUMPRO;
}
}
}
//*********************************************************************************************
//
static void DetectProcessor(void)
{
_asm
{
// CHECK 8086 : Bits 12-15 are always set on the 8086 processor
pushad
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 its a 8086
cmp ax,0f000h // test if its an 8086
mov cpu.cpufamProcessorFamily,cpufam8086
je short END_CPUID // if CPU is 8086/8088 check for 8087 FPU
// Check for 80286
// Bits 12-15 are always clear on the 80286 processor
pushf // Keep a copy of the flags
pushf
pop cx
mov bx, cx
or cx, 0f000h // Try to set bits 12-15
push cx // Save new FLAGS value on stack
popf // Replace current FLAGS value
pushf // Get new FLAGS
pop ax // Store new FLAGS in AX
popf // restore original flags to prevent task switch
and ax, 0f000h // If bits 12-15 are clear
mov cpu.cpufamProcessorFamily,cpufam80286
je short END_CPUID
// Intel386 CPU check
// The AC bit, bit #18, is a new bit introduced in the EFLAGS
// register on the Intel386 DX CPU to generate alignment faults.
// This bit can not be set on the Intel386 CPU
pushfd
pop eax // get original EFLAGS
mov ecx,eax // save origninal EFLAGS
xor eax,40000h // flip AC bit in EFLAGS
push eax // save for EFLAGS
popfd // copy to EFLAGS
pushfd // push EFLAGS
pop eax // get new EFLAGS value
xor eax,ecx // cant toggle AC bit, CPU=Intel386
mov cpu.cpufamProcessorFamily,cpufam80386
je short END_CPUID
// Intel486 DX CPU, Intel487 SX MCP, and Intel486 SX CPU checking
// Checking for ability to set/clear ID flag (bit 21) in EFLAGS
// which differentiates between a Pentium CPU or other processor
// with the ability to use the CPUID instruction. If this bit
// cannot be set, CPU=Intel486.
pushfd // Get original EFLAGS
pop eax
mov ecx, eax
xor eax, 200000h // Flip ID bit in EFLAGS
push eax // Save new EFLAGS value on stack
popfd // Replace current EFLAGS value
pushfd // Get new EFLAGS
pop eax // Store new EFLAGS in EAX
xor eax, ecx // Can not toggle ID bit,
jz short END_CPUID // Processor=80486
// Otherwise, execute CPUID instruction
// AMD devices K5 and K6 will all get to this stage, earlier devices will not but will
// recognized as intel devices
or cpu.u4CPUFlags,CPU_CPUID // set the bit for CPUID instruction
xor eax,eax // set up for CPUID instruction
cpuid
mov cpu.u4MaxCPUID, eax // maximum input function for CPUID
mov DWORD PTR [cpu.strManufactureID],ebx
mov DWORD PTR [cpu.strManufactureID+4],edx
mov DWORD PTR [cpu.strManufactureID+8],ecx
mov esi,OFFSET [cpu.strManufactureID]
mov edi,OFFSET strIntel
mov ecx,12
// compare for intel
repe cmpsb
cmp ecx,0 // must be GenuineIntel if ecx = 0
jne short NOT_INTEL
mov cpu.cpumanProcessorManufacture, cpumanINTEL
jmp short CPU_FEATURE
NOT_INTEL:
mov esi,OFFSET [cpu.strManufactureID]
mov edi,OFFSET strAMD
mov ecx,12
// compare for amd
repe cmpsb
cmp ecx,0 // must be AuthenticAMD if ecx = 0
jne short NOT_AMD
mov cpu.cpumanProcessorManufacture, cpumanAMD
jmp short CPU_FEATURE
NOT_AMD:
mov esi,OFFSET [cpu.strManufactureID]
mov edi,OFFSET strCyrix
mov ecx,12
// compare for cyrix
repe cmpsb
cmp ecx,0 // must be cyrix if ecx = 0
jne short NOT_CYRIX
mov cpu.cpumanProcessorManufacture, cpumanCYRIX
NOT_CYRIX:
mov esi,OFFSET [cpu.strManufactureID]
mov edi,OFFSET strIDT
mov ecx,12
// compare for Win Chip
repe cmpsb
cmp ecx,0 // must be cyrix if ecx = 0
jne short NOT_IDT
mov cpu.cpumanProcessorManufacture, cpumanIDT
NOT_IDT:
CPU_FEATURE:
// feature determination
mov eax,1
cpuid
// store the results of the instruction
mov cpu.u4Model, eax
mov cpu.u4Feature, edx
// get the model number
mov ebx,eax
shr ebx,8
and ebx,0x0f
// not the processor just the instruction family, for Intel these are the same
// but for the other devices they are not and this needs to be adjusted.
mov cpu.cpufamProcessorFamily, ebx
END_CPUID:
popad
}
}
//*********************************************************************************************
// Co-processor cheching begins here for the 8086,
// Intel 286, and Intel 386 CPUS, The algoritm is to
// detemine whether or not the floating-point status
// and control words can be written to.
// If they are not, no coprocessor exists. If the status
// and control words can be written to, the correct processor
// is then determined depending on the processor id. Coprocessor
// checks are first performed for an 8086, Intel 286 and an
// Intel486 DX CPU. If the coprocessor id is still undetermined, the
// system must contain an Intel386 CPU. The Intel386 CPU may work with
// either an Intel287 or an Intel387 math coprocessor. The infinity of the
// coprocessor must be checked to determine the correct coprocessor id.
//
static void DetectFPU(void)
{
WORD fp_status;
_asm
{
pushad // check for 8087, Intel287, or
// Intel387 math coprocessor
fninit // reset FP status word
mov [fp_status],5a5ah // initialise temp word to non-zero value
fnstsw [fp_status] // save FP status word
mov ax,[fp_status] // check FP status word
cmp al,0 // see if correct status with written
jne short END_CHECK_FPU
fnstcw [fp_status] // save FP control word
mov ax,[fp_status] // check FP control word
and ax,103fh // see if seleced parts look OK
cmp ax,3fh // check that 1s & 0s correctly read
jne END_CHECK_FPU
or cpu.u4CPUFlags, CPU_FPUPRESENT // set the bit for an FPU present
//
// Intel287 and Intel387 math coprocessor check for the Intel386 CPU
//
cmp cpu.cpufamProcessorFamily,3
jne short END_CHECK_FPU
fld1 // must use default control from FNINIT
fldz // form infinity
fdiv // 8087 and Intel287 MCP says +inf = -inf
fld st // form negative infinity
fchs // Intel387 MCP says +inf <>-inf
fcompp // see if they are the same and remove them
fstsw [fp_status] // look at the status from FCOMPP
mov ax,[fp_status]
mov cpu.cpufamFPUFamily,2 // store Intel287
sahf // see if infinities matched
jz short END_CHECK_FPU // jump if 8087 or Intel287 MCP is present
mov cpu.cpufamFPUFamily,3 // store Intel387 MCP for fpu IntelFPUType
END_CHECK_FPU:
popad
}
}
//*********************************************************************************************
// cycles*10 to execute a BSF instruciton on each processor
//
static uint32 IntelProcessorCycles[] =
{
000, 000, 000, 1150, 470, 430,
33, 33, 33, 33, 33, 33,
};
static uint32 AMDProcessorCycles[] =
{
470,430,302
};
static uint32 CyrixProcessorCycles[] =
{
330,330
};
//*********************************************************************************************
//
#define MAXCLOCKS 150 // Maximum number of cycles per BSF instruction
#define ITERATIONS 4000 // Number of times to repeat BSF instruction in samplings.
#define INITIAL_DELAY 3 // Number of ticks to wait through before starting test sampling.
#define SAMPLING_DELAY 60 // Number of ticks to allow to elapse during sampling.
#define TOLERANCE 1 // Number of MHz to allow samplings to deviate from average of samplings.
#define MAX_TRIES 40 // Maximum number of samplings to allow before giving up and returning current average
#define SAMPLINGS 10 // Number of BSF sequence samplings to make.
//*********************************************************************************************
//
static uint32 u4CPUSpeed(int clocks)
{
uint32 ticks; // Microseconds elapsed during test
uint32 cycles; // Clock cycles elapsed during test
uint32 stamp0, stamp1; // Time Stamp Variable for beginning and end of test
LARGE_INTEGER t0,t1; // Variables for High-Resolution Performance Counter reads
uint32 freq =0; // Most current frequ. calculation
uint32 freq2 =0; // 2nd most current frequ. calc.
uint32 freq3 =0; // 3rd most current frequ. calc.
uint32 total; // Sum of previous three frequency calculations
// Specifies whether the user manually entered the number of cycles for the BSF instruction.
int manual=0;
// Number of times a calculation has been made on this call to cpuspeed
int tries=0;
LARGE_INTEGER count_freq; // High Resolution Performance Counter frequency
// Check for manual BSF instruction clock count
if (clocks == 0)
{
switch (cpu.cpumanProcessorManufacture)
{
case cpumanINTEL:
cycles = ITERATIONS * IntelProcessorCycles[cpu.cpufamProcessorFamily];
break;
case cpumanAMD:
cycles = ITERATIONS * AMDProcessorCycles[cpu.cpufamProcessorFamily - cpufamAM486];
break;
case cpumanCYRIX:
cycles = ITERATIONS * CyrixProcessorCycles[cpu.cpufamProcessorFamily - cpufam686];
break;
}
}
else if (0 < clocks && clocks <= MAXCLOCKS)
{
// Toggle manual control flag. Note that this mode will not work properly with processors
// which can process multiple BSF instructions at a time. For example, manual mode
// will not work on a PentiumPro(R)
cycles = ITERATIONS * clocks;
manual = 1;
}
else
{
return 0;
}
// Checks whether the high-resolution counter exists and returns an error if it does not exist.
if ( !QueryPerformanceFrequency ( &count_freq ) )
{
return 0;
}
if ( ( cpu.u4Feature & 0x00000010 ) && !(manual) )
{
// On processors supporting the Read Time Stamp opcode, compare elapsed time on the
// High-Resolution Counter with elapsed cycles on the Time Stamp Register.
do
{
// This do loop runs up to 20 times or until the average of the previous three calculated
// frequencies is within 1 MHz of each of the individual calculated frequencies.
// This resampling increases the accuracy of the results since outside factors could affect
// this calculation
tries++; // Increment number of times sampled on this call to cpuspeed
freq3 = freq2; // Shift frequencies back to make
freq2 = freq; // room for new frequency measurement
//Get high-resolution performance counter time
QueryPerformanceCounter(&t0);
t1.LowPart = t0.LowPart; // Set Initial time
t1.HighPart = t0.HighPart;
// Loop until 50 ticks have passed since last read of hi-res counter.
// This accounts for overhead later.
while ( (uint32)t1.LowPart - (uint32)t0.LowPart<50)
{
QueryPerformanceCounter(&t1);
}
_asm
{
rdtsc
mov stamp0, EAX
}
t0.LowPart = t1.LowPart; // Reset Initial
t0.HighPart = t1.HighPart; // Time
// Loop until 1000 ticks have passed since last read of hi-res counter.
// This allows for elapsed time for sampling.
while ((uint32)t1.LowPart-(uint32)t0.LowPart<1000 )
{
QueryPerformanceCounter(&t1);
}
_asm
{
rdtsc
mov stamp1, EAX
}
cycles = stamp1 - stamp0;
ticks = (uint32) t1.LowPart - (uint32) t0.LowPart;
// Note that some seemingly arbitrary mulitplies and divides are done below.
// This is to maintain a high level of precision without truncating the most
// significant data. According to what value ITERATIIONS is set to, these
// multiplies and divides might need to be shifted for optimal precision.
ticks = ticks * 100000;
ticks = ticks / ( count_freq.LowPart/10 );
if ( ticks%count_freq.LowPart > count_freq.LowPart/2 )
{
ticks++; // Round up if necessary
}
freq = cycles/ticks; // Cycles / us = MHz
if ( cycles%ticks > ticks/2 )
{
freq++; // Round up if necessary
}
total = ( freq + freq2 + freq3 );
} while ( (tries < 3 ) || (tries < 20) && ((abs(3 * freq -total) > 3*TOLERANCE ) ||
(abs(3 * freq2-total) > 3*TOLERANCE ) || (abs(3 * freq3-total) > 3*TOLERANCE )));
if ( total / 3 != ( total + 1 ) / 3 )
{
total ++; // Round up if necessary
}
freq = total / 3;
}
else if ( cpu.cpufamProcessorFamily >= 3 )
{
// If processor does not support time stamp reading, but is at least a 386 or above,
// utilize method of timing a loop of BSF instructions which take a known number of
// cycles to run on i386(tm), i486(tm), and Pentium(R) processors.
int i; // Temporary Variable
uint32 current = 0;
uint32 lowest = 0xffffffff;
for ( i = 0; i < SAMPLINGS; i++ )
{
QueryPerformanceCounter(&t0); // Get start time
_asm
{
mov eax, 80000000h
mov bx, ITERATIONS
LOOP1: bsf ecx,eax
dec bx
jnz LOOP1
}
QueryPerformanceCounter(&t1); // Get end time
current = (uint32) t1.LowPart - (uint32) t0.LowPart;
if ( current < lowest )
lowest = current;
}
ticks = lowest;
ticks = ticks * 100000;
ticks = ticks / ( count_freq.LowPart/10 );
if ( ticks%count_freq.LowPart > count_freq.LowPart/2 )
{
ticks++; // Round up if necessary
}
freq = cycles/ticks; // Cycles / us = MHz
if ( cycles%ticks > ticks/2 )
freq++; // Round up if necessary
freq/=10;
}
else
{
return 0;
}
return freq;
}
//*********************************************************************************************
// Make the correct textual name for the processor
// AMD has some instructions to get this from the device itself!!
//
void MakeName(void)
{
uint32 u4_name;
if (cpu.u4CPUFlags & CPU_CPUID)
{
switch (cpu.cpumanProcessorManufacture)
{
case cpumanINTEL:
switch ((cpu.u4Model>>4) & 0xff)
{
case 0x040:
case 0x041:
strcat(&cpu.strProcessor[0],"Intel 486 DX");
break;
case 0x042:
strcat(&cpu.strProcessor[0],"Intel 486 SX");
break;
case 0x043:
strcat(&cpu.strProcessor[0],"Intel 486 DX2");
break;
case 0x044:
strcat(&cpu.strProcessor[0],"Intel 486 SL");
break;
case 0x045:
strcat(&cpu.strProcessor[0],"Intel 486 SX2");
break;
case 0x047:
strcat(&cpu.strProcessor[0],"Intel 486 DX2 Write-Back Enhanced");
break;
case 0x048:
strcat(&cpu.strProcessor[0],"Intel 486 DX4");
break;
case 0x050:
case 0x051:
case 0x052:
case 0x053:
strcat(&cpu.strProcessor[0],"Intel Pentium Processor");
break;
case 0x054:
case 0x055:
case 0x056:
case 0x057:
case 0x058:
case 0x059:
case 0x05A:
case 0x05B:
case 0x05C:
case 0x05D:
case 0x05E:
case 0x05F:
strcat(&cpu.strProcessor[0],"Intel Pentium Processor with MMX Technology");
break;
case 0x060:
case 0x061:
case 0x062:
strcat(&cpu.strProcessor[0],"Intel Pentium Pro Processor");
break;
case 0x063:
case 0x064:
case 0x065:
case 0x066:
case 0x067:
case 0x068:
case 0x069:
case 0x06A:
case 0x06B:
case 0x06C:
case 0x06D:
case 0x06E:
case 0x06F:
strcat(&cpu.strProcessor[0],"Intel Pentium II Processor with MMX Technology");
break;
}
break;
case cpumanAMD:
u4_name = u4QueryCPUIDName();
if (u4_name == 0)
{
// we failed to get a name from the CPUID instruction..
switch (cpu.cpufamProcessorFamily)
{
case cpufamAM486:
strcpy(&cpu.strProcessor[0],"AMD-AM486");
break;
case cpufamK5:
strcpy(&cpu.strProcessor[0],"AMD-K5");
break;
case cpufamK6:
strcpy(&cpu.strProcessor[0],"AMD-K6");
break;
}
if (cpu.u4Feature & (1<<23))
{
strcat(&cpu.strProcessor[0]," with multimedia extentions");
}
}
break;
case cpumanIDT:
u4_name = u4QueryCPUIDName();
if (u4_name == 0)
{
// we failed to get a name from the CPUID instruction..
switch (cpu.cpufamProcessorFamily)
{
case cpufamWINCHIPC6:
strcpy(&cpu.strProcessor[0],"IDT WinChip C6");
break;
case cpufamWINCHIP2:
strcpy(&cpu.strProcessor[0],"IDT WinChip2");
break;
}
}
break;
case cpumanCYRIX:
if (cpu.cpufamProcessorFamily == 5)
{
strcat(&cpu.strProcessor[0],"Cyrix 686");
}
else if (cpu.cpufamProcessorFamily == 6)
{
strcat(&cpu.strProcessor[0],"Cyrix M2");
}
break;
default:
// We do not know what processor is present but we can look for
// the extended CPUID functions that give the name on some
// processors.
u4_name = u4QueryCPUIDName();
if (u4_name == 0)
{
// Processor does not support the exteneded CPUID so we do
// not know what it is
strcpy(&cpu.strProcessor[0],"Cannot Determine");
}
else
{
// Processor returned something for the extended CPUID functions
// so we will return that with an unsure comment.
strcat(&cpu.strProcessor[0]," (Uncertain)");
}
break;
}
// check for an overdrive
if ((cpu.u4Model & 0x1000) == 0x1000 )
{
strcat(&cpu.strProcessor[0]," [OverDrive]");
}
if ((cpu.u4Model & 0x2000) == 0x2000 )
{
strcat(&cpu.strProcessor[0]," [Slave]");
}
}
else
{
// NO CPUID - we need to just do the best we can with whta we have
switch (cpu.cpufamProcessorFamily)
{
case cpufam8086:
strcpy(&cpu.strProcessor[0],"8086");
if (cpu.u4CPUFlags & CPU_FPUPRESENT)
{
strcat(&cpu.strProcessor[0]," with an 8087");
}
break;
case cpufam80286:
strcpy(&cpu.strProcessor[0],"80286");
if (cpu.u4CPUFlags & CPU_FPUPRESENT)
{
strcat(&cpu.strProcessor[0]," with an 80287");
}
break;
case cpufam80386:
strcpy(&cpu.strProcessor[0],"80386");
if (cpu.u4CPUFlags & CPU_FPUPRESENT)
{
if (cpu.cpufamFPUFamily == 2)
{
strcat(&cpu.strProcessor[0]," with an 80287");
}
else
{
strcat(&cpu.strProcessor[0]," with an 80387");
}
}
break;
case cpufam80486:
strcpy(&cpu.strProcessor[0],"80386");
if (cpu.u4CPUFlags & CPU_FPUPRESENT)
{
strcat(&cpu.strProcessor[0]," DX");
}
else
{
strcat(&cpu.strProcessor[0]," SX");
}
break;
default:
strcat(&cpu.strProcessor[0],"Cannot Determine");
break;
}
}
}
//*********************************************************************************************
// Look for the CPUID name functions and use if they are present.
//
uint32 u4QueryCPUIDName(void)
{
uint32 u4_name;
// No CPUID instruction then there are no extended names.
if ((cpu.u4CPUFlags & CPU_CPUID) == 0)
return 0;
_asm
{
mov eax,0x80000000 // extended CPUID function,AMD and IDT support it
//CPUID
_emit 0x0f
_emit 0xa2
cmp eax,0x80000004
jb short EXIT_AMD_NAME // functions upto 0x80000004 must be present
mov eax,0x80000002 // extended CPUID function
//CPUID
_emit 0x0f
_emit 0xa2
mov DWORD PTR [cpu.strProcessor+0],eax
mov DWORD PTR [cpu.au4ProcessorSpecific+8], eax
mov DWORD PTR [cpu.strProcessor+4],ebx
mov DWORD PTR [cpu.au4ProcessorSpecific+12], ebx
mov DWORD PTR [cpu.strProcessor+8],ecx
mov DWORD PTR [cpu.au4ProcessorSpecific+16], ecx
mov DWORD PTR [cpu.strProcessor+12],edx
mov DWORD PTR [cpu.au4ProcessorSpecific+20], edx
mov eax,0x80000003 // extended CPUID function
//CPUID
_emit 0x0f
_emit 0xa2
mov DWORD PTR [cpu.strProcessor+16],eax
mov DWORD PTR [cpu.au4ProcessorSpecific+24], eax
mov DWORD PTR [cpu.strProcessor+20],ebx
mov DWORD PTR [cpu.au4ProcessorSpecific+28], ebx
mov DWORD PTR [cpu.strProcessor+24],ecx
mov DWORD PTR [cpu.au4ProcessorSpecific+32], ecx
mov DWORD PTR [cpu.strProcessor+28],edx
mov DWORD PTR [cpu.au4ProcessorSpecific+36], edx
mov eax,0x80000004 // extended CPUID function
//CPUID
_emit 0x0f
_emit 0xa2
mov DWORD PTR [cpu.strProcessor+32],eax
mov DWORD PTR [cpu.au4ProcessorSpecific+40], eax
mov DWORD PTR [cpu.strProcessor+36],ebx
mov DWORD PTR [cpu.au4ProcessorSpecific+44], ebx
mov DWORD PTR [cpu.strProcessor+40],ecx
mov DWORD PTR [cpu.au4ProcessorSpecific+48], ecx
mov DWORD PTR [cpu.strProcessor+44],edx
mov DWORD PTR [cpu.au4ProcessorSpecific+52], edx
mov u4_name, 1 // we have got the name
jmp short DONE
EXIT_AMD_NAME:
mov u4_name, 0 // there is no name function, must get make the name
DONE:
}
return u4_name;
}