812 lines
20 KiB
C
812 lines
20 KiB
C
/* + mmtimers.h
|
|
*
|
|
* Accurate timers using pentium cpu clock, QueryPerformanceCounter
|
|
* or GetTickCount depending on what system the code is run on
|
|
*
|
|
* Copyright (C) 1995, Microsoft Corporation, all rights reserved
|
|
*
|
|
*-========================================================================*/
|
|
|
|
#if !defined _INC_MMTIMERS_
|
|
#define _INC_MMTIMERS_
|
|
|
|
typedef struct {
|
|
DWORD dwlo;
|
|
DWORD dwhi;
|
|
} PCTIMER, NEAR * PPCTIMER;
|
|
|
|
struct _pctimer_global {
|
|
DWORD dwRawHz;
|
|
DWORD dwMicroAdjust;
|
|
union {
|
|
DWORD dwRawKhz;
|
|
WORD wRawKhz;
|
|
};
|
|
union {
|
|
DWORD dwRawMhz;
|
|
WORD wRawMhz;
|
|
};
|
|
DWORD dwTimerKhz;
|
|
PCTIMER base;
|
|
DWORD (WINAPI * DifTicks )(PCTIMER *);
|
|
DWORD (WINAPI * DifMicrosec )(PCTIMER *);
|
|
DWORD (WINAPI * DifMillisec )(PCTIMER *);
|
|
DWORD (WINAPI * DeltaTicks )(PCTIMER *);
|
|
DWORD (WINAPI * DeltaMicrosec)(PCTIMER *);
|
|
DWORD (WINAPI * DeltaMillisec)(PCTIMER *);
|
|
UINT uTimerType;
|
|
};
|
|
extern struct _pctimer_global pc;
|
|
|
|
extern VOID WINAPI InitPerformanceCounters ();
|
|
|
|
#define pcBegin() pc.DeltaTicks(&pc.base)
|
|
#define pcGetTime() pc.DifMillisec(&pc.base)
|
|
#define pcGetTicks() pc.DifMicrosec(&pc.base)
|
|
#define pcGetTickRate() (pc.dwTimerKhz * 1000)
|
|
#define pcBeginTimer(ppt) (pc.DeltaMicrosec(ppt), 0)
|
|
#define pcDeltaTicks(ppt) pc.DeltaMicrosec(ppt)
|
|
|
|
#endif //_INC_MMTIMERS_
|
|
|
|
// =============================================================================
|
|
|
|
//
|
|
// include this in only one module in a DLL or APP
|
|
//
|
|
#if (defined _INC_MMTIMERS_CODE_) && (_INC_MMTIMERS_CODE_ != FALSE)
|
|
#undef _INC_MMTIMERS_CODE_
|
|
#define _INC_MMTIMERS_CODE_ FALSE
|
|
|
|
static DWORD WINAPI tgtDeltaTime (PCTIMER *pctimer)
|
|
{
|
|
DWORD dwTime = timeGetTime();
|
|
DWORD dwDelta = dwTime - pctimer->dwlo;
|
|
pctimer->dwlo = dwTime;
|
|
return dwDelta;
|
|
}
|
|
|
|
static DWORD WINAPI tgtDiffTime (PCTIMER *pctimer)
|
|
{
|
|
return timeGetTime() - pctimer->dwlo;
|
|
}
|
|
|
|
struct _pctimer_global pc = {1000, 0, 1, 0, 1,
|
|
0, 0,
|
|
(LPVOID)tgtDiffTime,
|
|
(LPVOID)tgtDiffTime,
|
|
(LPVOID)tgtDiffTime,
|
|
(LPVOID)tgtDeltaTime,
|
|
(LPVOID)tgtDeltaTime,
|
|
(LPVOID)tgtDeltaTime,
|
|
0,
|
|
};
|
|
|
|
#if defined WIN32 || defined _WIN32
|
|
|
|
#if !defined _X86_
|
|
#define Scale(value,scalar) (DWORD)((value).QuadPart / (scalar))
|
|
#else
|
|
//
|
|
// c9 wants to do LARGE_INTEGER division by calling a library
|
|
// routine. We get a link error for projects that are not
|
|
// already using the C-runtime, so to avoid that, we do the division
|
|
// using x86 assembler
|
|
//
|
|
#pragma warning(disable:4704)
|
|
#pragma warning(disable:4035)
|
|
DWORD _inline Scale(
|
|
LARGE_INTEGER value,
|
|
DWORD scalar)
|
|
{
|
|
_asm {
|
|
mov ecx, scalar
|
|
mov eax, value.LowPart
|
|
mov edx, value.HighPart
|
|
jecxz bail
|
|
cmp edx, ecx
|
|
jb ok_to_divide
|
|
push eax
|
|
mov eax, edx
|
|
xor edx, edx
|
|
div ecx
|
|
pop eax
|
|
ok_to_divide:
|
|
div ecx
|
|
bail:
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static VOID WINAPI qpcInitTimer (PCTIMER * pbase)
|
|
{
|
|
QueryPerformanceCounter ((LPVOID)pbase);
|
|
}
|
|
|
|
static DWORD WINAPI qpcDiffTicks (PCTIMER * pbase)
|
|
{
|
|
LARGE_INTEGER *plarge = (LPVOID)pbase;
|
|
LARGE_INTEGER ticks;
|
|
|
|
QueryPerformanceCounter (&ticks);
|
|
ticks.QuadPart -= plarge->QuadPart;
|
|
return ticks.LowPart;
|
|
}
|
|
|
|
static DWORD WINAPI qpcDiffMicrosec (PCTIMER * pbase)
|
|
{
|
|
LARGE_INTEGER *plarge = (LPVOID)pbase;
|
|
LARGE_INTEGER ticks;
|
|
|
|
QueryPerformanceCounter (&ticks);
|
|
ticks.QuadPart -= plarge->QuadPart;
|
|
ticks.LowPart = Scale(ticks, pc.dwRawMhz);
|
|
if (pc.dwMicroAdjust)
|
|
return MulDiv (ticks.LowPart, 1000000, pc.dwMicroAdjust);
|
|
return ticks.LowPart;
|
|
}
|
|
|
|
static DWORD WINAPI qpcDiffMillisec (PCTIMER * pbase)
|
|
{
|
|
LARGE_INTEGER *plarge = (LPVOID)pbase;
|
|
LARGE_INTEGER ticks;
|
|
|
|
QueryPerformanceCounter (&ticks);
|
|
ticks.QuadPart -= plarge->QuadPart;
|
|
return Scale(ticks, pc.dwRawKhz);
|
|
}
|
|
|
|
static DWORD WINAPI qpcDeltaTicks (PCTIMER * pbase)
|
|
{
|
|
LARGE_INTEGER *plarge = (LPVOID)pbase;
|
|
LARGE_INTEGER ticks = *plarge;
|
|
|
|
QueryPerformanceCounter (plarge);
|
|
ticks.QuadPart = plarge->QuadPart - ticks.QuadPart;
|
|
return ticks.LowPart;
|
|
}
|
|
|
|
static DWORD WINAPI qpcDeltaMicrosec (PCTIMER * pbase)
|
|
{
|
|
LARGE_INTEGER *plarge = (LPVOID)pbase;
|
|
LARGE_INTEGER ticks = *plarge;
|
|
|
|
QueryPerformanceCounter (plarge);
|
|
ticks.QuadPart = plarge->QuadPart - ticks.QuadPart;
|
|
ticks.LowPart = Scale(ticks, pc.dwRawMhz);
|
|
if (pc.dwMicroAdjust)
|
|
return MulDiv (ticks.LowPart, 1000000, pc.dwMicroAdjust);
|
|
return ticks.LowPart;
|
|
}
|
|
|
|
static DWORD WINAPI qpcDeltaMillisec (PCTIMER * pbase)
|
|
{
|
|
LARGE_INTEGER *plarge = (LPVOID)pbase;
|
|
LARGE_INTEGER ticks = *plarge;
|
|
|
|
QueryPerformanceCounter (plarge);
|
|
ticks.QuadPart = plarge->QuadPart - ticks.QuadPart;
|
|
return Scale(ticks, pc.dwRawKhz);
|
|
}
|
|
|
|
static DWORD WINAPI qpcTimerFreq ()
|
|
{
|
|
LARGE_INTEGER freq;
|
|
if (QueryPerformanceFrequency (&freq))
|
|
return freq.LowPart;
|
|
return 0;
|
|
}
|
|
|
|
#ifdef _X86_
|
|
|
|
#pragma warning(disable:4704)
|
|
#pragma warning(disable:4035)
|
|
|
|
static VOID WINAPI p5InitTimer (PCTIMER * pBase)
|
|
{
|
|
_asm {
|
|
_emit 0x0f
|
|
_emit 0x31
|
|
|
|
mov ebx, pBase
|
|
mov [ebx], eax
|
|
mov [ebx+4], edx
|
|
}
|
|
}
|
|
|
|
static DWORD WINAPI p5DiffTicks (PCTIMER * pBase)
|
|
{
|
|
_asm {
|
|
_emit 0x0f
|
|
_emit 0x31
|
|
|
|
mov ebx, pBase
|
|
sub eax, [ebx]
|
|
sbb edx, [ebx+4]
|
|
}
|
|
}
|
|
|
|
static DWORD WINAPI p5DiffMicrosec (PCTIMER * pBase)
|
|
{
|
|
_asm {
|
|
_emit 0x0f
|
|
_emit 0x31
|
|
|
|
mov ebx, pBase
|
|
sub eax, [ebx]
|
|
sbb edx, [ebx+4]
|
|
|
|
mov ecx, pc.dwRawMhz
|
|
jecxz bail
|
|
cmp edx, ecx
|
|
jb ok_to_divide
|
|
push eax
|
|
mov eax, edx
|
|
xor edx, edx
|
|
div ecx
|
|
pop eax
|
|
ok_to_divide:
|
|
div ecx
|
|
bail:
|
|
}
|
|
}
|
|
|
|
static DWORD WINAPI p5DiffMillisec (PCTIMER * pBase)
|
|
{
|
|
_asm {
|
|
_emit 0x0f
|
|
_emit 0x31
|
|
|
|
mov ebx, pBase
|
|
sub eax, [ebx]
|
|
sbb edx, [ebx+4]
|
|
|
|
mov ecx, pc.dwRawKhz
|
|
jecxz bail
|
|
cmp edx, ecx
|
|
jb ok_to_divide
|
|
push eax
|
|
mov eax, edx
|
|
xor edx, edx
|
|
div ecx
|
|
pop eax
|
|
ok_to_divide:
|
|
div ecx
|
|
bail:
|
|
}
|
|
}
|
|
|
|
static DWORD WINAPI p5DeltaTicks (PCTIMER * pBase)
|
|
{
|
|
_asm {
|
|
_emit 0x0f
|
|
_emit 0x31
|
|
|
|
mov ebx, pBase
|
|
mov ecx, eax
|
|
sub eax, [ebx]
|
|
mov [ebx], ecx
|
|
mov ecx, edx
|
|
sbb edx, [ebx+4]
|
|
mov [ebx+4], ecx
|
|
}
|
|
}
|
|
static DWORD WINAPI p5DeltaMicrosec (PCTIMER * pBase)
|
|
{
|
|
_asm {
|
|
_emit 0x0f
|
|
_emit 0x31
|
|
|
|
mov ebx, pBase
|
|
mov ecx, eax
|
|
sub eax, [ebx]
|
|
mov [ebx], ecx
|
|
mov ecx, edx
|
|
sbb edx, [ebx+4]
|
|
mov [ebx+4], ecx
|
|
|
|
mov ecx, pc.dwRawMhz
|
|
jecxz bail
|
|
cmp edx, ecx
|
|
jb ok_to_divide
|
|
push eax
|
|
mov eax, edx
|
|
xor edx, edx
|
|
div ecx
|
|
pop eax
|
|
ok_to_divide:
|
|
div ecx
|
|
bail:
|
|
}
|
|
}
|
|
|
|
static DWORD WINAPI p5DeltaMillisec (PCTIMER * pBase)
|
|
{
|
|
_asm {
|
|
_emit 0x0f
|
|
_emit 0x31
|
|
|
|
mov ebx, pBase
|
|
mov ecx, eax
|
|
sub eax, [ebx]
|
|
mov [ebx], ecx
|
|
mov ecx, edx
|
|
sbb edx, [ebx+4]
|
|
mov [ebx+4], ecx
|
|
|
|
mov ecx, pc.dwRawKhz
|
|
jecxz bail
|
|
cmp edx, ecx
|
|
jb ok_to_divide
|
|
push eax
|
|
mov eax, edx
|
|
xor edx, edx
|
|
div ecx
|
|
pop eax
|
|
ok_to_divide:
|
|
div ecx
|
|
bail:
|
|
}
|
|
}
|
|
|
|
static DWORD WINAPI p5TimerFreq ()
|
|
{
|
|
SYSTEM_INFO si;
|
|
|
|
GetSystemInfo(&si);
|
|
if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL &&
|
|
si.wProcessorLevel == 5
|
|
)
|
|
{
|
|
PCTIMER timer;
|
|
LARGE_INTEGER qpc1, qpc2;
|
|
DWORD dwTime;
|
|
DWORD dwTicks;
|
|
OSVERSIONINFO osv;
|
|
#define MS_INTERVAL 500
|
|
|
|
// pentium timers dont work correctly on NT so
|
|
// dont use them
|
|
//
|
|
{
|
|
osv.dwOSVersionInfoSize = sizeof(osv);
|
|
GetVersionEx (&osv);
|
|
}
|
|
|
|
// dont use pentium timers if they take more
|
|
// than about 12 microsec to execute
|
|
//
|
|
p5InitTimer (&timer);
|
|
if (p5DeltaTicks (&timer) > (60 * 12) &&
|
|
p5DeltaTicks (&timer) > (60 * 12))
|
|
{
|
|
// pentium timers are too slow to try and use them.
|
|
// just go with QueryPerformanceCounter instead
|
|
//
|
|
return 0;
|
|
}
|
|
|
|
// for some reason, if you use timeBeginPeriod
|
|
// on NT. it decides that my 90mhz pentium is an 88mhz
|
|
// pentium.
|
|
//
|
|
//if (osv.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
|
|
// timeBeginPeriod (1);
|
|
|
|
p5InitTimer (&timer);
|
|
QueryPerformanceCounter (&qpc1);
|
|
Sleep(MS_INTERVAL);
|
|
QueryPerformanceCounter (&qpc2);
|
|
dwTicks = p5DiffTicks(&timer);
|
|
|
|
//if (osv.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
|
|
// timeEndPeriod (1);
|
|
|
|
dwTime = (DWORD)(qpc2.QuadPart - qpc1.QuadPart);
|
|
QueryPerformanceFrequency (&qpc1);
|
|
dwTime = MulDiv(dwTime, 1000, qpc1.LowPart);
|
|
|
|
if (dwTime < MS_INTERVAL * 9 / 10)
|
|
return 0;
|
|
|
|
pc.dwRawMhz = (dwTicks + dwTime * 1000/2) /dwTime /1000;
|
|
pc.dwRawKhz = pc.dwRawMhz * 1000;
|
|
pc.dwRawHz = pc.dwRawKhz * 1000;
|
|
pc.dwMicroAdjust = 0;
|
|
pc.dwTimerKhz = 1000;
|
|
|
|
return pc.dwRawHz;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
VOID WINAPI InitPerformanceCounters (void)
|
|
{
|
|
DWORD dwFreq;
|
|
|
|
#ifdef _X86_
|
|
if (p5TimerFreq())
|
|
{
|
|
pc.DifTicks = p5DiffTicks;
|
|
pc.DifMicrosec = p5DiffMicrosec;
|
|
pc.DifMillisec = p5DiffMillisec;
|
|
pc.DeltaTicks = p5DeltaTicks;
|
|
pc.DeltaMicrosec = p5DeltaMicrosec;
|
|
pc.DeltaMillisec = p5DeltaMillisec;
|
|
pc.uTimerType = 5;
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
if (dwFreq = qpcTimerFreq())
|
|
{
|
|
pc.dwRawKhz = dwFreq / 1000;
|
|
pc.dwRawMhz = pc.dwRawKhz / 1000;
|
|
pc.dwMicroAdjust = dwFreq / pc.dwRawMhz;
|
|
if (pc.dwMicroAdjust == 1000000)
|
|
pc.dwMicroAdjust = 0;
|
|
pc.dwTimerKhz = 1000;
|
|
|
|
pc.DifTicks = qpcDiffTicks;
|
|
pc.DifMicrosec = qpcDiffMicrosec;
|
|
pc.DifMillisec = qpcDiffMillisec;
|
|
pc.DeltaTicks = qpcDeltaTicks;
|
|
pc.DeltaMicrosec = qpcDeltaMicrosec;
|
|
pc.DeltaMillisec = qpcDeltaMillisec;
|
|
pc.uTimerType = 1;
|
|
}
|
|
}
|
|
|
|
#else // win16
|
|
|
|
#pragma warning(disable:4704)
|
|
#pragma warning(disable:4035)
|
|
|
|
static VOID WINAPI p5InitTimer (PCTIMER * pBase)
|
|
{
|
|
_asm {
|
|
_emit 0x0f
|
|
_emit 0x31
|
|
|
|
_emit 0x66
|
|
xor bx, bx
|
|
mov bx, pBase
|
|
_emit 0x66
|
|
mov [bx], ax
|
|
_emit 0x66
|
|
mov [bx+4], dx
|
|
}
|
|
}
|
|
|
|
static DWORD WINAPI p5DiffTicks (PCTIMER * pBase)
|
|
{
|
|
_asm {
|
|
_emit 0x0f
|
|
_emit 0x31
|
|
|
|
_emit 0x66
|
|
xor bx, bx
|
|
mov bx, pBase
|
|
_emit 0x66
|
|
sub ax, [bx]
|
|
_emit 0x66
|
|
sbb dx, [bx+4]
|
|
}
|
|
}
|
|
|
|
static DWORD WINAPI p5DiffMicrosec (PCTIMER * pBase)
|
|
{
|
|
_asm {
|
|
_emit 0x0f
|
|
_emit 0x31
|
|
|
|
_emit 0x66
|
|
xor bx, bx
|
|
mov bx, pBase
|
|
_emit 0x66
|
|
sub ax, [bx]
|
|
_emit 0x66
|
|
sbb dx, [bx+4]
|
|
|
|
//_emit 0x66
|
|
mov cx, pc.wRawMhz
|
|
_emit 0x66
|
|
jcxz bail
|
|
_emit 0x66
|
|
cmp dx, cx
|
|
jb ok_to_divide
|
|
_emit 0x66
|
|
push ax
|
|
_emit 0x66
|
|
mov ax, dx
|
|
_emit 0x66
|
|
xor dx, dx
|
|
_emit 0x66
|
|
div cx
|
|
_emit 0x66
|
|
pop ax
|
|
ok_to_divide:
|
|
_emit 0x66
|
|
div cx
|
|
bail:
|
|
}
|
|
}
|
|
|
|
static DWORD WINAPI p5DiffMillisec (PCTIMER * pBase)
|
|
{
|
|
_asm {
|
|
_emit 0x0f
|
|
_emit 0x31
|
|
|
|
_emit 0x66
|
|
xor bx, bx
|
|
mov bx, pBase
|
|
_emit 0x66
|
|
sub ax, [bx]
|
|
_emit 0x66
|
|
sbb dx, [bx+4]
|
|
|
|
_emit 0x66
|
|
mov cx, pc.wRawKhz
|
|
_emit 0x66
|
|
jcxz bail
|
|
_emit 0x66
|
|
cmp dx, cx
|
|
jb ok_to_divide
|
|
_emit 0x66
|
|
push ax
|
|
_emit 0x66
|
|
mov ax, dx
|
|
_emit 0x66
|
|
xor dx, dx
|
|
_emit 0x66
|
|
div cx
|
|
_emit 0x66
|
|
pop ax
|
|
ok_to_divide:
|
|
_emit 0x66
|
|
div cx
|
|
bail:
|
|
}
|
|
}
|
|
|
|
static DWORD WINAPI p5DeltaTicks (PCTIMER * pBase)
|
|
{
|
|
_asm {
|
|
_emit 0x0f
|
|
_emit 0x31
|
|
|
|
_emit 0x66
|
|
mov bx, pBase
|
|
_emit 0x66
|
|
mov cx, ax
|
|
_emit 0x66
|
|
sub ax, [bx]
|
|
_emit 0x66
|
|
mov [bx], cx
|
|
_emit 0x66
|
|
mov cx, dx
|
|
_emit 0x66
|
|
sbb dx, [bx+4]
|
|
_emit 0x66
|
|
mov [bx+4], cx
|
|
}
|
|
}
|
|
static DWORD WINAPI p5DeltaMicrosec (PCTIMER * pBase)
|
|
{
|
|
_asm {
|
|
_emit 0x0f
|
|
_emit 0x31
|
|
|
|
_emit 0x66
|
|
mov bx, pBase
|
|
_emit 0x66
|
|
mov cx, ax
|
|
_emit 0x66
|
|
sub ax, [bx]
|
|
_emit 0x66
|
|
mov [bx], cx
|
|
_emit 0x66
|
|
mov cx, dx
|
|
_emit 0x66
|
|
sbb dx, [bx+4]
|
|
_emit 0x66
|
|
mov [bx+4], cx
|
|
|
|
_emit 0x66
|
|
mov cx, pc.wRawMhz
|
|
_emit 0x66
|
|
jcxz bail
|
|
_emit 0x66
|
|
cmp dx, cx
|
|
jb ok_to_divide
|
|
_emit 0x66
|
|
push ax
|
|
_emit 0x66
|
|
mov ax, dx
|
|
_emit 0x66
|
|
xor dx, dx
|
|
_emit 0x66
|
|
div cx
|
|
_emit 0x66
|
|
pop ax
|
|
ok_to_divide:
|
|
_emit 0x66
|
|
div cx
|
|
bail:
|
|
}
|
|
}
|
|
|
|
static DWORD WINAPI p5DeltaMillisec (PCTIMER * pBase)
|
|
{
|
|
_asm {
|
|
_emit 0x0f
|
|
_emit 0x31
|
|
|
|
_emit 0x66
|
|
mov bx, pBase
|
|
_emit 0x66
|
|
mov cx, ax
|
|
_emit 0x66
|
|
sub ax, [bx]
|
|
_emit 0x66
|
|
mov [bx], cx
|
|
_emit 0x66
|
|
mov cx, dx
|
|
_emit 0x66
|
|
sbb dx, [bx+4]
|
|
_emit 0x66
|
|
mov [bx+4], cx
|
|
|
|
//_emit 0x66
|
|
mov cx, pc.wRawKhz
|
|
_emit 0x66
|
|
jcxz bail
|
|
_emit 0x66
|
|
cmp dx, cx
|
|
jb ok_to_divide
|
|
_emit 0x66
|
|
push ax
|
|
_emit 0x66
|
|
mov ax, dx
|
|
_emit 0x66
|
|
xor dx, dx
|
|
_emit 0x66
|
|
div cx
|
|
_emit 0x66
|
|
pop ax
|
|
ok_to_divide:
|
|
_emit 0x66
|
|
div cx
|
|
bail:
|
|
|
|
}
|
|
}
|
|
|
|
// 16 bit code for detecting CPU type so we can decide
|
|
// whether or not it is ok to use the pentium timing stuff
|
|
//
|
|
int WINAPI pcGetCpuID ()
|
|
{
|
|
_asm {
|
|
_emit 0x66
|
|
pushf ; save eflags
|
|
|
|
// check for 486 by attempting to set the 0x40000 bit
|
|
// in eflags. if we can set it, the processor is 486 or better
|
|
//
|
|
_emit 0x66
|
|
pushf ; push eflags
|
|
pop ax ; move eflags to dx:ax
|
|
pop dx
|
|
or dx, 4 ; set 0x40000 bit in eflags
|
|
push dx ; put back onto stack
|
|
push ax
|
|
_emit 0x66
|
|
popf ; pop modified flags back into eflags
|
|
_emit 0x66
|
|
pushf ; push eflags back onto stack
|
|
pop ax ; move eflags in to dx:bx
|
|
pop dx
|
|
|
|
_emit 0x66
|
|
popf ; restore origonal eflags
|
|
|
|
mov bx, 3 ; assume 386
|
|
test dx, 4 ; 486 will preserve 0x40000 bit on push/pop of eflags
|
|
jz ret_procid
|
|
inc bx ; this is a 486 or higher
|
|
|
|
// if we get to here it is a 486 or greater
|
|
|
|
// check for pentium or higher by attempting to toggle the
|
|
// ID bit (0x200000) in eflags.
|
|
// on a pentium, this bit will toggle, on 486 it will not
|
|
//
|
|
_emit 0x66
|
|
pushf ; save eflags
|
|
_emit 0x66
|
|
pushf ; get eflags
|
|
pop ax ; put eflags into dx:ax
|
|
pop dx
|
|
xor dx, 0x20 ; toggle 0x200000 bit in eflags
|
|
push dx
|
|
push ax ; push modified eflags from dx:ax
|
|
_emit 0x66
|
|
popf ; load changed eflags
|
|
_emit 0x66
|
|
pushf ; get eflags again
|
|
pop ax ; discard eflags lo
|
|
pop ax ; get eflags hi
|
|
xor dx, ax ; did anything change?
|
|
_emit 0x66 ; restore old eflags
|
|
popf
|
|
|
|
test dx, 0x20 ; did we change the 20 bit?
|
|
jz ret_procid ; if not, bx already has 4, return that
|
|
|
|
// if we get to here, it is a pentium or greater
|
|
|
|
// use the pentium CPUID instruction to detect exact processor
|
|
// type
|
|
//
|
|
_emit 0x0F ; cpuid instruction
|
|
_emit 0xA2
|
|
shr ax, 8 ; extract family field
|
|
and ax, 0x0F
|
|
mov bx, ax ; 5 is pentium, others are higher
|
|
|
|
ret_procid:
|
|
mov ax, bx
|
|
}
|
|
}
|
|
|
|
static DWORD WINAPI p5TimerFreq ()
|
|
{
|
|
if (pcGetCpuID() >= 5)
|
|
{
|
|
DWORD dw;
|
|
DWORD dwTicks;
|
|
static PCTIMER timer;
|
|
|
|
p5InitTimer (&timer);
|
|
dw = timeGetTime() + 200;
|
|
while (timeGetTime() < dw)
|
|
;
|
|
dw = timeGetTime() - dw;
|
|
dwTicks = p5DiffTicks(&timer);
|
|
|
|
pc.dwRawMhz = (dwTicks + dw * 1000/2) /dw /1000;
|
|
pc.dwRawKhz = pc.dwRawMhz * 1000;
|
|
pc.dwRawHz = pc.dwRawKhz * 1000;
|
|
pc.dwMicroAdjust = 0;
|
|
pc.dwTimerKhz = 1000;
|
|
|
|
return pc.dwRawHz;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
VOID WINAPI InitPerformanceCounters (void)
|
|
{
|
|
if (p5TimerFreq() != 0l)
|
|
{
|
|
pc.DifTicks = p5DiffTicks;
|
|
pc.DifMicrosec = p5DiffMicrosec;
|
|
pc.DifMillisec = p5DiffMillisec;
|
|
pc.DeltaTicks = p5DeltaTicks;
|
|
pc.DeltaMicrosec = p5DeltaMicrosec;
|
|
pc.DeltaMillisec = p5DeltaMillisec;
|
|
pc.uTimerType = 5;
|
|
return;
|
|
}
|
|
}
|
|
|
|
#endif // WIN32
|
|
|
|
#endif // _INC_MMTIMERS_CODE_
|