For the past several days I have been working on rewriting several functions that get the CPU type and speed of the current system to use Win32API functions instead of being dependent upon MFC. I did this so that I could add CPU detection capabilities to an existing Win32API application I have been developing for the past several months. Tonight I was able to finally complete this task and to compile and run the program in debug mode. However, when I run the program after compiling it in release mode I get the following error:
Unhandled Exception in SysInfo.exe: 0xC0000005: Access Violation.
Then of course the program exits and I am taken to a program disassembly screen. Even though I do not understand the content of the program disassembly screen, I have been able to trace the cause of the problem to one of the assembly language statements in the file CpuId.cpp, the text of which I will include below.
As far as these particular lines of these functions go, I am way over my head. I do not understand any of them so I am totally unable to resolve the problem. All I know is that these same lines were taken from the MFC project they were origionally a part of and were not changed with the exception of some formatting improvements. I also know that the project they were origionally taken from works equally well in release and debug mode. So I am at a total loss on how to fix the problem at hand.
Well, enough chatter for now. Here is the text of the file that I feel is the cause of my problems. I would appreciate any ideas you may have.
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include "SysInfo.h"
// Define the CPUID instruction
#define CPU_ID __asm _emit 0x0f __asm _emit 0xa2
// Define the RDTSC instruction
#define RDTSC _asm _emit 0x0f _asm _emit 0x31
// CONSTANT DEFINITIONS
#define CLONE_MASK 0x8000 // Mask to be 'OR'ed with proc-
#define MAXCLOCKS 150 // Maximum number of cycles per BSF instruction
// ACCURACY AFFECTING CONSTANTS
// Number of times to repeat BSF instruction in samplings. Initially set to 4000.
#define ITERATIONS 4000
// Maximum number of samplings to allow before giving up and returning current
// average. Initially set to 20.
#define MAX_TRIES 20
// Number of MHz to allow samplings to deviate from average of samplings.
// Initially set to 2.
#define TOLERANCE 2
// Number of BSF sequence samplings to make.
// Initially set to 10.
#define SAMPLINGS 10
#define ROUND_THRESHOLD 6
struct FREQ_INFO
{
unsigned long in_cycles; // Internal clock cycles during test
unsigned long ex_ticks; // Microseconds elapsed during test
unsigned long raw_freq; // Raw frequency of CPU in MHz
unsigned long norm_freq; // Normalized frequency of CPU in MHz.
};
// Number of cycles needed to execute a single BSF instruction.
// Note that processors below i386(tm) are not supported.
static ulong processor_cycles[] = {
00, 00, 00, 115, 47, 43,
38, 38, 38, 38, 38, 38,
};
/*
**************************
**********
**********
**********
**********
**********
**********
****
Function:
bool CSysInfo::check_clone()
Author's Name:
Ben Key
Creation Date:
08/17/1999
Description:
Detects if the processor of the curent system is a clone.
Parameters
Globals
Used:
Modified:
External Functions:
Return Value:
1 if processor is clone (limited detection ability)
0 otherwise
Other Information:
**************************
**********
**********
**********
**********
**********
**********
****
MODIFICATION LOG
Date Author Description
--------------- ----------- --------------------------
----------
------
08/17/1999 Ben Key CPU Identification functions added to project.
**************************
**********
**********
**********
**********
**********
**********
****
*/
bool CSysInfo::check_clone()
{
short cpu_type=0;
_asm
{
MOV AX,5555h // Check to make sure this
XOR DX,DX // is a 32-bit processor
MOV CX,2h
DIV CX // Perform Division
CLC
JNZ no_clone
JMP clone
no_clone: STC
clone: PUSHF
POP AX // Get the flags
AND AL,1
XOR AL,1 // AL=0 is probably Intel,
// AL=1 is a Clone
MOV cpu_type, ax
}
cpu_type = cpu_type & 0x0001;
if(cpu_type)
return TRUE;
else
return FALSE;
}
/*
**************************
**********
**********
**********
**********
**********
**********
****
Function:
WORD CSysInfo::check_IDProc()
Author's Name:
Ben Key
Creation Date:
08/17/1999
Description:
Detects the system processor family. A support function for wincpuid().
Parameters
Globals
Used:
Modified:
External Functions:
Return Value:
CPU Family (i.e. 4 if Intel 486, 5 if Pentium(R) Processor)
Other Information:
**************************
**********
**********
**********
**********
**********
**********
****
MODIFICATION LOG
Date Author Description
--------------- ----------- --------------------------
----------
------
08/17/1999 Ben Key CPU Identification functions added to project.
**************************
**********
**********
**********
**********
**********
**********
****
*/
WORD CSysInfo::check_IDProc()
{
int i=0;
WORD cpu_type=0xffff;
BYTE stepping=0;
BYTE model=0;
BYTE vendor_id[]="------------"
;
BYTE intel_id[]="GenuineIntel";
__asm
{
xor eax, eax
CPU_ID
mov dword ptr vendor_id, ebx
mov dword ptr vendor_id[+4], edx
mov dword ptr vendor_id[+8], ecx
}
__asm
{
cmp eax, 1 // Make sure 1 is valid input for CPUID
jl end_IDProc // If not, jump to end
xor eax, eax
inc eax
CPU_ID // Get family/model/stepping/ features
mov stepping, al
and stepping, 0x0f //0fh
and al, 0f0h
shr al, 4
mov model, al
and eax, 0f00h
shr eax, 8 // Isolate family
and eax, 0fh
mov cpu_type, ax // Set _cpu_type with family
end_IDProc:
mov ax, cpu_type
}
return cpu_type;
} // Check_IDProc()
/*
**************************
**********
**********
**********
**********
**********
**********
****
Function:
int CSysInfo::wincpuidsupport(
)
Author's Name:
Ben Key
Creation Date:
08/17/1999
Description:
Detects the system processor family. A support function for wincpuid().
Parameters
Globals
Used:
Modified:
External Functions:
Return Value:
CPU Family (i.e. 4 if Intel 486, 5 if Pentium(R) Processor)
Other Information:
**************************
**********
**********
**********
**********
**********
**********
****
MODIFICATION LOG
Date Author Description
--------------- ----------- --------------------------
----------
------
08/17/1999 Ben Key CPU Identification functions added to project.
**************************
**********
**********
**********
**********
**********
**********
****
*/
int CSysInfo::wincpuidsupport(
)
{
int cpuid_support = 1;
_asm
{
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,
jnz support // Processor=80486
mov cpuid_support,0 // Clear support flag
support:
}
return cpuid_support;
}
/*
**************************
**********
**********
**********
**********
**********
**********
****
Function:
int CSysInfo::wincpuid()
Author's Name:
Ben Key
Creation Date:
08/17/1999
Description:
Detects the system processor family. Uses the functions check_clone(), check_IDProc(),
and wincpuidsupport() to obtain the required information.
Parameters
Globals
Used:
Modified:
External Functions:
Return Value:
An integer value representing the CPU family.
0 = Unknown
2 = 80286
3 = 80386
4 = 80486
5 = Pentium(R) Processor
6 = PentiumPro(R) Processor
7 or higher = Processor beyond the PentiumPro6(R) Processor
Other Information:
**************************
**********
**********
**********
**********
**********
**********
****
MODIFICATION LOG
Date Author Description
--------------- ----------- --------------------------
----------
------
08/17/1999 Ben Key CPU Identification functions added to project.
**************************
**********
**********
**********
**********
**********
**********
****
*/
int CSysInfo::wincpuid()
{
int cpuid;
bool clone_flag = FALSE;
if (wincpuidsupport()) // Determine whether CPUID opcode is supported.
cpuid=check_IDProc();
else
{
clone_flag=check_clone();
cpuid=0;
}
if (clone_flag)
cpuid = cpuid | CLONE_MASK; // Signify that a clone has been detected by setting MSB high.
return cpuid;
}
/*
**************************
**********
**********
**********
**********
**********
**********
****
Function:
LPCTSTR CSysInfo::GetCpuString(int
type)
Author's Name:
Ben Key
Creation Date:
08/17/1999
Description:
This function is used to convert a numeric identifier of the CPU family to a string
description of the CPU Type.
Parameters:
int type The numeric CPU type identifier.
Globals
Used:
Modified:
External Functions:
Return Value:
LPCTSTR A long pointer to a string that provides a description of the CPU type.
I.E. 386, 486, Pentium(R) Processor, PentiumPro(R) Processor
Other Information:
**************************
**********
**********
**********
**********
**********
**********
****
MODIFICATION LOG
Date Author Description
--------------- ----------- --------------------------
----------
------
08/17/1999 Ben Key CPU Identification functions added to project.
**************************
**********
**********
**********
**********
**********
**********
****
*/
LPCTSTR CSysInfo::GetCpuString(int
type)
{
LPTSTR szCPUType;
switch (type)
{
case 3:
szCPUType = new char [5];
strcpy (szCPUType, "386");
break;
case 4:
szCPUType = new char [5];
strcpy (szCPUType, "486");
break;
case 5:
szCPUType = new char [strlen ("Pentium(R) Processor") + 2];
strcpy (szCPUType, "Pentium(R) Processor");
break;
case 6:
szCPUType = new char [strlen ("PentiumPro(R) Processor") + 2];
strcpy (szCPUType, "PentiumPro(R) Processor");
break;
default:
szCPUType = new char [strlen ("Unknown Type: (7)") + 5];
sprintf (szCPUType, "Unknown Type: (%d)", type);
break;
}
return (LPCTSTR) szCPUType;
}
/*
**************************
**********
**********
**********
**********
**********
**********
****
Function:
LPCTSTR CSysInfo::GetCpuId()
Author's Name:
Ben Key
Creation Date:
08/17/1999
Description:
This function is used to obtain a full description of the current CPU type, including
the processor class and whether or not it is a Genuine Intel processor or an Intel Clone
processor.
Parameters:
Globals
Used:
Modified:
External Functions:
Return Value:
LPCTSTR A long pointer to a string that provides the complete description of the CPU type.
I.E. Intel CPU Family : Pentium(R) Processor
Other Information:
**************************
**********
**********
**********
**********
**********
**********
****
MODIFICATION LOG
Date Author Description
--------------- ----------- --------------------------
----------
------
08/17/1999 Ben Key CPU Identification functions added to project.
**************************
**********
**********
**********
**********
**********
**********
****
*/
LPCTSTR CSysInfo::GetCpuId()
{
int cpu_type;
int cpuid_support;
LPTSTR szStr;
LPTSTR szCpuIdStr;
szStr = new char [50];
szCpuIdStr = new char [100];
cpu_type = this->wincpuid();
cpuid_support = this->wincpuidsupport();
strcpy (szStr, this->GetCpuString(cpu_typ
e));
if (cpu_type & CLONE_MASK)
sprintf (szCpuIdStr, "Intel Clone CPU Family: %s", (LPCTSTR)szStr);
else
{
if (cpuid_support)
sprintf (szCpuIdStr, "Intel CPU Family: %s", (LPCTSTR)szStr);
else
sprintf (szCpuIdStr, "CPU Family: %s", (LPCTSTR)szStr);
}
return (LPCTSTR) szCpuIdStr;
}
/*
**************************
**********
**********
**********
**********
**********
**********
****
Function:
DWORD CSysInfo::wincpufeatures()
Author's Name:
Ben Key
Creation Date:
08/17/1999
Description:
This function is used to obtain the CPU feature flags that are supported by the system.
Parameters:
Globals
Used:
Modified:
External Functions:
Return Value:
DWORD The supported Feature Flags
0 = Processor which does not execute the CPUID instruction.
This includes 8086, 8088, 80286, 80386, and some older 80486 processors.
Else
Feature Flags (refer to App Note AP-485 for description).
This DWORD was put into EDX by the CPUID instruction.
Current flag assignment is as follows:
bit31..10 reserved (=0)
bit9=1 CPU contains a local APIC (iPentium-3V)
bit8=1 CMPXCHG8B instruction supported
bit7=1 machine check exception supported
bit6=0 reserved (36bit-addressing & 2MB-paging)
bit5=1 iPentium-style MSRs supported
bit4=1 time stamp counter TSC supported
bit3=1 page size extensions supported
bit2=1 I/O breakpoints supported
bit1=1 enhanced virtual 8086 mode supported
bit0=1 CPU contains a floating-point unit (FPU)
Note: New bits will be assigned on future processors... see processor data
books for updated information
Other Information:
**************************
**********
**********
**********
**********
**********
**********
****
MODIFICATION LOG
Date Author Description
--------------- ----------- --------------------------
----------
------
08/17/1999 Ben Key CPU Identification functions added to project.
**************************
**********
**********
**********
**********
**********
**********
****
*/
DWORD CSysInfo::wincpufeatures()
{
int i=0;
DWORD cpuff=0x00000000;
BYTE vendor_id[]="------------"
;
BYTE intel_id[]="GenuineIntel";
if (wincpuidsupport())
{
_asm
{
xor eax, eax // Set up for CPUID instruction
CPU_ID // Get and save vendor ID
mov dword ptr vendor_id, ebx
mov dword ptr vendor_id[+4], edx
mov dword ptr vendor_id[+8], ecx
}
_asm
{
cmp eax, 1 // Make sure 1 is valid input for CPUID
jl end_cpuff // If not, jump to end
xor eax, eax
inc eax
CPU_ID // Get family/model/stepping/ features
mov cpuff, edx
end_cpuff:
mov eax, cpuff
}
}
return cpuff;
}
/*
**************************
**********
**********
**********
**********
**********
**********
****
Function:
void CSysInfo::GetRDTSCCpuSpeed
(struct FREQ_INFO *cpu_speed)
Author's Name:
Ben Key
Creation Date:
08/17/1999
Description:
A supporting function for CSysInfo that is used to get the CPU speed. It is used to
fill the members of the FREQ_INFO structure defined above.
Parameters:
struct FREQ_INFO *cpu_speed
This parameter is the cpu_speed structure that is passed to the function by reference
so that the function may fill the values stored by the structure.
Globals
Used:
Modified:
External Functions:
Return Value:
Other Information:
**************************
**********
**********
**********
**********
**********
**********
****
MODIFICATION LOG
Date Author Description
--------------- ----------- --------------------------
----------
------
08/17/1999 Ben Key CPU Identification functions added to project.
**************************
**********
**********
**********
**********
**********
**********
****
*/
void CSysInfo::GetRDTSCCpuSpeed
(struct FREQ_INFO *cpu_speed)
{
LARGE_INTEGER t0,t1; // Variables for High-Resolution Performance
// Counter reads
ulong freq =0; // Most current frequ. calculation
ulong freq2 =0; // 2nd most current frequ. calc.
ulong freq3 =0; // 3rd most current frequ. calc.
ulong total; // Sum of previous three frequency calculations
int tries=0; // Number of times a calculation has been made on this call to cpuspeed
ulong total_cycles=0, cycles; // Clock cycles elapsed during test
ulong stamp0, stamp1; // Time Stamp Variable for beginning and end of test.
ulong total_ticks=0, ticks; // Microseconds elapsed during test.
LARGE_INTEGER count_freq; // High Resolution Performance Counter frequency.
#ifdef WIN32
int iPriority;
HANDLE hThread = GetCurrentThread();
#endif // WIN32;
memset(cpu_speed, 0x00, sizeof(cpu_speed));
if ( !QueryPerformanceFrequency
( &count_freq ) )
return;
// 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.
QueryPerformanceCounter(&t
0); // Get high-resolution performance counter time.
t1.LowPart = t0.LowPart; // Set Initial time
t1.HighPart = t0.HighPart;
#ifdef WIN32
iPriority = GetThreadPriority(hThread)
;
if ( iPriority != THREAD_PRIORITY_ERROR_RETU
RN )
{
SetThreadPriority(hThread,
THREAD_PRIORITY_TIME_CRITI
CAL);
}
#endif // WIN32
while ( (ulong)t1.LowPart - (ulong)t0.LowPart<50)
{
// Loop until 50 ticks have passed since last read of hi-res counter. This
// accounts for overhead later.
QueryPerformanceCounter(&t
1);
RDTSC; // Read Time Stamp
_asm
{
MOV stamp0, EAX
}
}
t0.LowPart = t1.LowPart; // Reset Initial
t0.HighPart = t1.HighPart; // Time
while ((ulong)t1.LowPart-(ulong)
t0.LowPart
<1000 )
{
// Loop until 1000 ticks have passed since last read of hi-res counter.
// This allows for elapsed time for sampling.
QueryPerformanceCounter(&t
1);
RDTSC; // Read Time Stamp
__asm
{
MOV stamp1, EAX
}
}
#ifdef WIN32
// Reset priority
if (iPriority != THREAD_PRIORITY_ERROR_RETU
RN)
{
SetThreadPriority(hThread,
iPriority);
}
#endif // WIN32
cycles = stamp1 - stamp0; // Number of internal clock cycles is
// difference between two time stamp readings.
ticks = (ulong) t1.LowPart - (ulong) t0.LowPart;
// Number of external ticks is difference between two hi-res counter reads.
// 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;
// Convert ticks to hundred thousandths of a tick
ticks = ticks / (count_freq.LowPart/10);
// Hundred Thousandths of a Ticks / (10 ticks/second) = microseconds (us)
total_ticks += ticks;
total_cycles += cycles;
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);
// Total last three frequency calculations
} while ((tries < 3 ) ||
(tries < 20)&&
((abs(3 * freq -total) > 3*TOLERANCE )||
(abs(3 * freq2-total) > 3*TOLERANCE )||
(abs(3 * freq3-total) > 3*TOLERANCE )));
// Compare last three calculations to average of last three calculations.
// Try one more significant digit.
freq3 = ( total_cycles * 10 ) / total_ticks;
freq2 = ( total_cycles * 100 ) / total_ticks;
if ( freq2 - (freq3 * 10) >= ROUND_THRESHOLD )
freq3++;
cpu_speed->raw_freq = total_cycles / total_ticks;
cpu_speed->norm_freq = cpu_speed->raw_freq;
freq = cpu_speed->raw_freq * 10;
if( (freq3 - freq) >= ROUND_THRESHOLD )
cpu_speed->norm_freq++;
cpu_speed->ex_ticks = total_ticks;
cpu_speed->in_cycles = total_cycles;
return;
}
/*
**************************
**********
**********
**********
**********
**********
**********
****
Function:
int CSysInfo::GetCmosTick()
Author's Name:
Ben Key
Creation Date:
08/17/1999
Description:
A supporting function for CSysInfo that is used to get the tick count from CMOS.
Parameters:
Globals
Used:
Modified:
External Functions:
Return Value:
Int The tick count as obtained from CMOS.
Other Information:
**************************
**********
**********
**********
**********
**********
**********
****
MODIFICATION LOG
Date Author Description
--------------- ----------- --------------------------
----------
------
08/17/1999 Ben Key CPU Identification functions added to project.
**************************
**********
**********
**********
**********
**********
**********
****
*/
int CSysInfo::GetCmosTick()
{
int tick = 0;
// __asm mov ah, 02h
// __asm int 1Ah
// __asm mov al, dh
// __asm and ax, 000Fh
__asm xor ax, ax
__asm out 070h, al
__asm xor ax, ax
__asm in al, 071h
// _outp( 0x70, offset );
// base = _inp( 0x71 );
// value returned in ax by function
__asm mov word ptr tick, ax
return tick;
}
/*
**************************
**********
**********
**********
**********
**********
**********
****
Function:
unsigned long CSysInfo::cpuTimeStamp(uns
igned long *hi, unsigned long *low)
Author's Name:
Ben Key
Creation Date:
08/17/1999
Description:
Returns the pentium cpu time stamp in 2 32 bit unsigned longs
Notes: maintains a flag to make sure the cpu supports the RDTSC instruction. There is
the overhead of checking the cpu the first time afterwhich the time consumed in
checking the flag is very minimal. You could adjust the count but then you would
have to do 64bit math. ugh.
Parameters:
unsigned long *hi
unsigned long *low
Globals
Used:
Modified:
External Functions:
Return Value:
Int The tick count as obtained from CMOS.
Other Information:
**************************
**********
**********
**********
**********
**********
**********
****
MODIFICATION LOG
Date Author Description
--------------- ----------- --------------------------
----------
------
08/17/1999 Ben Key CPU Identification functions added to project.
**************************
**********
**********
**********
**********
**********
**********
****
*/
unsigned long CSysInfo::cpuTimeStamp(uns
igned long *hi, unsigned long *low)
{
unsigned long ulHi = 0L;
unsigned long ulLow = 0L;
__asm
{
;RDTSC
_emit 0Fh
_emit 31h
mov ulLow, eax
mov ulHi, edx
}
*hi = ulHi;
*low = ulLow;
return ulLow;
}
/*
**************************
**********
**********
**********
**********
**********
**********
****
Function:
unsigned long CSysInfo::diffTime64(unsig
ned long t1Hi, unsigned long t1Low,
unsigned long t2Hi, unsigned long t2Low, unsigned long *tHi, unsigned long *tLow)
Author's Name:
Ben Key
Creation Date:
08/17/1999
Description:
Calculates the difference of a 64 bit time as represented by two 32 bit unsigned longs.
Parameters:
unsigned long t1Hi
unsigned long t1Low
unsigned long t2Hi
unsigned long t2Low
unsigned long *tHi
unsigned long *tLow
Globals
Used:
Modified:
External Functions:
Return Value:
Int The tick count as obtained from CMOS.
Other Information:
**************************
**********
**********
**********
**********
**********
**********
****
MODIFICATION LOG
Date Author Description
--------------- ----------- --------------------------
----------
------
08/17/1999 Ben Key CPU Identification functions added to project.
**************************
**********
**********
**********
**********
**********
**********
****
*/
unsigned long CSysInfo::diffTime64(unsig
ned long t1Hi, unsigned long t1Low,
unsigned long t2Hi, unsigned long t2Low, unsigned long *tHi, unsigned long *tLow)
{
unsigned long xl, xh;
/*
*tHi = t2Hi - t1Hi;
if( t1Low > t2Low )
{
*tLow = t1Low - t2Low;
*tLow = ULONG_MAX - *tLow;
*tHi -= 1;
} else
{
*tLow = t2Low - t1Low;
}
*/
__asm
{
mov eax, t2Low
mov ebx, t1Low
sub eax, ebx
mov xl, eax
mov eax, t2Hi
mov ebx, t1Hi
sbb eax, ebx
mov xh, eax
}
*tLow = xl;
*tHi = xh;
return *tLow;
}
#define ABS_TICK(a,b) (b<a)?b+10-a:b-a
/*
**************************
**********
**********
**********
**********
**********
**********
****
Function:
void CSysInfo::GetCmosCpuSpeed(
struct FREQ_INFO *cpu_speed)
Author's Name:
Ben Key
Creation Date:
08/17/1999
Description:
Gets the CPU Speed from CMOS
Parameters:
struct FREQ_INFO *cpu_speed A FREQ_INFO structure that is passed to the function by
reference.
Globals
Used:
Modified:
External Functions:
Return Value:
Int The tick count as obtained from CMOS.
Other Information:
**************************
**********
**********
**********
**********
**********
**********
****
MODIFICATION LOG
Date Author Description
--------------- ----------- --------------------------
----------
------
08/17/1999 Ben Key CPU Identification functions added to project.
**************************
**********
**********
**********
**********
**********
**********
****
*/
void CSysInfo::GetCmosCpuSpeed(
struct FREQ_INFO *cpu_speed)
{
unsigned long t1Low, t1High, t2Low, t2High, tResLow, tResHigh;
int timeStart, timeStop, lapseTime;
unsigned long temp;
unsigned long temp1;
unsigned long cpuSpeed = 0l;
#ifdef WIN32
HANDLE hThread = GetCurrentThread();
int iPriority;
#endif // WIN32
memset(cpu_speed, 0x00, sizeof(cpu_speed));
// This loop waits for the next tick so that we begin speed test on a tick edge
#ifdef WIN32
iPriority = GetThreadPriority(hThread)
;
if ( iPriority != THREAD_PRIORITY_ERROR_RETU
RN )
{
SetThreadPriority(hThread,
iPriority+1);
}
#endif // WIN32
timeStart = GetCmosTick();
for(;;)
{
timeStop = GetCmosTick();
if ( ABS_TICK(timeStart,timeSto
p) > 0 )
{
cpuTimeStamp(&t1High, &t1Low);
break;
}
}
timeStart = timeStop;
for(;;)
{
timeStop = GetCmosTick();
if ( ABS_TICK(timeStart,timeSto
p) > 0 )
{
cpuTimeStamp(&t2High, &t2Low);
break;
}
}
#ifdef WIN32
// Set thread priority back.
if ( iPriority != THREAD_PRIORITY_ERROR_RETU
RN )
{
SetThreadPriority(hThread,
iPriority);
}
#endif // WIN32
diffTime64(t1High, t1Low, t2High, t2Low, &tResHigh, &tResLow );
lapseTime = ABS_TICK(timeStart,timeSto
p);
cpuSpeed = tResLow; ///lapseTime;
cpu_speed->in_cycles = tResLow; // Cycles count since we in this routine
//round to nearest digit
temp = cpuSpeed/1000000;
temp1 = cpuSpeed/100000;
temp = temp * 10; // realign with last digit = zero
cpuSpeed = cpuSpeed/1000000; // cpuSpeed/1000000;
cpu_speed->raw_freq = cpuSpeed;
if( (temp1 - temp) >= ROUND_THRESHOLD )
cpuSpeed++;
cpu_speed->norm_freq = cpuSpeed;
cpu_speed->ex_ticks = (timeStop - timeStart) * 1000000;
return;
}
/*
**************************
**********
**********
**********
**********
**********
**********
****
Function:
void CSysInfo::GetBSFCpuSpeed(u
long cycles, struct FREQ_INFO *cpu_speed)
Author's Name:
Ben Key
Creation Date:
08/17/1999
Description:
Gets the CPU Speed from CMOS
Parameters:
struct FREQ_INFO *cpu_speed A FREQ_INFO structure that is passed to the function by
reference.
Globals
Used:
Modified:
External Functions:
Return Value:
Int The tick count as obtained from CMOS.
Other Information:
**************************
**********
**********
**********
**********
**********
**********
****
MODIFICATION LOG
Date Author Description
--------------- ----------- --------------------------
----------
------
08/17/1999 Ben Key CPU Identification functions added to project.
**************************
**********
**********
**********
**********
**********
**********
****
*/
void CSysInfo::GetBSFCpuSpeed(u
long cycles, struct FREQ_INFO *cpu_speed)
{
// 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.
LARGE_INTEGER t0,t1; // Variables for High-Resolution Performance
// Counter reads ulong freq =0; Most current frequ. calculation
ulong ticks; // Microseconds elapsed during test
LARGE_INTEGER count_freq; // High Resolution Performance Counter frequency
int i; // Temporary Variable
ulong current = 0; // Variable to store time elapsed during loop of of BSF instructions
ulong lowest = ULONG_MAX;
// Since algorithm finds the lowest value out of a set of samplings, this variable is
// set intially to the max unsigned long value). This guarantees that the initialized
// value is not later used as the least time through the loop.
ulong freq;
memset(cpu_speed, 0x00, sizeof(cpu_speed));
if ( !QueryPerformanceFrequency
( &count_freq ) )
return;
for ( i = 0; i < SAMPLINGS; i++ )
{
// Sample Ten times. Can be increased or decreased depending on accuracy vs.
// time requirements
QueryPerformanceCounter(&t
0); // Get start time
_asm
{
mov eax, 80000000h
mov bx, ITERATIONS
// Number of consecutive BSF instructions to execute. Set identical to
// nIterations constant in speed.h
loop1: bsf ecx,eax
dec bx
jnz loop1
}
QueryPerformanceCounter(&t
1); // Get end time
current = (ulong) t1.LowPart - (ulong) t0.LowPart;
// Number of external ticks is difference between two hi-res counter reads.
if ( current < lowest ) // Take lowest elapsed
lowest = current; // time to account
} // for some samplings being interrupted by other operations
ticks = lowest;
// 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;
// Convert ticks to hundred thousandths of a tick ticks = ticks / ( count_freq.LowPart/10 );
// Hundred Thousandths of a Ticks / (10 ticks/second) = microseconds (us)
if ( ticks%count_freq.LowPart > count_freq.LowPart/2 )
ticks++; // Round up if necessary
freq = cycles/ticks; // Cycles / us = MHz
cpu_speed->raw_freq = freq;
if ( cycles%ticks > ticks/2 )
freq++; // Round up if necessary
cpu_speed->in_cycles = cycles; // Return variable structure
cpu_speed->ex_ticks = ticks; // determined by one of
cpu_speed->norm_freq = freq;
return;
}
/*
**************************
**********
**********
**********
**********
**********
**********
****
Function:
struct FREQ_INFO CSysInfo::wincpuspeed(int clocks)
Author's Name:
Ben Key
Creation Date:
08/17/1999
Description:
Return the raw clock rate of the host CPU.
Parameters:
clocks:
0: Use default value for number of cycles per BSF instruction.
-1: Use CMos timer to get cpu speed.
Positive Integer: Use clocks value for number of cycles per BSF instruction.
Globals
Used:
Modified:
External Functions:
Return Value:
If error then return all zeroes in FREQ_INFO structure
Else return FREQ_INFO structure containing calculated
clock frequency, normalized clock frequency, number of
clock cycles during test sampling, and the number of
microseconds elapsed during the sampling.
Other Information:
**************************
**********
**********
**********
**********
**********
**********
****
MODIFICATION LOG
Date Author Description
--------------- ----------- --------------------------
----------
------
08/17/1999 Ben Key CPU Identification functions added to project.
**************************
**********
**********
**********
**********
**********
**********
****
*/
struct FREQ_INFO CSysInfo::wincpuspeed(int clocks)
{
ulong cycles; // Clock cycles elapsed during test
ushort processor = wincpuid(); // Family of processor
DWORD features = wincpufeatures(); // Features of Processor
int manual=0; // Specifies whether the user manually entered the number of
// cycles for the BSF instruction.
struct FREQ_INFO cpu_speed; // Return structure for cpuspeed
memset(&cpu_speed, 0x00, sizeof(cpu_speed));
if ( processor & CLONE_MASK )
return cpu_speed;
// Check for manual BSF instruction clock count
if (clocks <= 0)
{
cycles = ITERATIONS * processor_cycles[processor
];
}
else if (0 < clocks && clocks <= MAXCLOCKS)
{
cycles = ITERATIONS * clocks;
manual = 1; // 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)
}
if (( features&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.
if ( clocks == 0 )
GetRDTSCCpuSpeed(&cpu_spee
d);
else
GetCmosCpuSpeed(&cpu_speed
);
}
else if (processor >= 3)
{
GetBSFCpuSpeed(cycles, &cpu_speed);
}
return cpu_speed;
}
LPCTSTR CSysInfo::GetCpuSpeed()
{
LPTSTR szTemp;
LPTSTR szCPUSpeed;
szTemp = new char [20];
szCPUSpeed = new char [20];
struct FREQ_INFO cpu_speed;
cpu_speed = wincpuspeed(0);
sprintf (szCPUSpeed, "%i %s", cpu_speed.norm_freq, "MHz");
return (LPCTSTR) szCPUSpeed;
}