mirror of
https://github.com/id-Software/DOOM-3-BFG.git
synced 2024-12-11 21:21:27 +00:00
1125 lines
24 KiB
C++
1125 lines
24 KiB
C++
/*
|
|
===========================================================================
|
|
|
|
Doom 3 BFG Edition GPL Source Code
|
|
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
|
|
|
|
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
|
|
|
|
Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
|
|
|
|
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
|
|
|
===========================================================================
|
|
*/
|
|
|
|
#pragma hdrstop
|
|
#include "../../idlib/precompiled.h"
|
|
|
|
#include "win_local.h"
|
|
|
|
#pragma warning(disable:4740) // warning C4740: flow in or out of inline asm code suppresses global optimization
|
|
#pragma warning(disable:4731) // warning C4731: 'XXX' : frame pointer register 'ebx' modified by inline assembly code
|
|
|
|
/*
|
|
==============================================================
|
|
|
|
Clock ticks
|
|
|
|
==============================================================
|
|
*/
|
|
|
|
/*
|
|
================
|
|
Sys_GetClockTicks
|
|
================
|
|
*/
|
|
double Sys_GetClockTicks() {
|
|
#if 0
|
|
|
|
LARGE_INTEGER li;
|
|
|
|
QueryPerformanceCounter( &li );
|
|
return = (double ) li.LowPart + (double) 0xFFFFFFFF * li.HighPart;
|
|
|
|
#else
|
|
|
|
// RB begin
|
|
#if defined(_MSC_VER)
|
|
unsigned long lo, hi;
|
|
|
|
__asm {
|
|
push ebx
|
|
xor eax, eax
|
|
cpuid
|
|
rdtsc
|
|
mov lo, eax
|
|
mov hi, edx
|
|
pop ebx
|
|
}
|
|
return (double ) lo + (double) 0xFFFFFFFF * hi;
|
|
|
|
#elif defined(__GNUC__) && defined( __i386__ )
|
|
unsigned long lo, hi;
|
|
|
|
__asm__ __volatile__(
|
|
"push %%ebx\n" \
|
|
"xor %%eax,%%eax\n" \
|
|
"cpuid\n" \
|
|
"rdtsc\n" \
|
|
"mov %%eax,%0\n" \
|
|
"mov %%edx,%1\n" \
|
|
"pop %%ebx\n"
|
|
: "=r"( lo ), "=r"( hi ) );
|
|
return ( double ) lo + ( double ) 0xFFFFFFFF * hi;
|
|
#else
|
|
#error unsupported CPU
|
|
#endif
|
|
// RB end
|
|
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
================
|
|
Sys_ClockTicksPerSecond
|
|
================
|
|
*/
|
|
double Sys_ClockTicksPerSecond() {
|
|
static double ticks = 0;
|
|
#if 0
|
|
|
|
if ( !ticks ) {
|
|
LARGE_INTEGER li;
|
|
QueryPerformanceFrequency( &li );
|
|
ticks = li.QuadPart;
|
|
}
|
|
|
|
#else
|
|
|
|
if ( !ticks ) {
|
|
HKEY hKey;
|
|
LPBYTE ProcSpeed;
|
|
DWORD buflen, ret;
|
|
|
|
if ( !RegOpenKeyEx( HKEY_LOCAL_MACHINE, "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", 0, KEY_READ, &hKey ) ) {
|
|
ProcSpeed = 0;
|
|
buflen = sizeof( ProcSpeed );
|
|
ret = RegQueryValueEx( hKey, "~MHz", NULL, NULL, (LPBYTE) &ProcSpeed, &buflen );
|
|
// If we don't succeed, try some other spellings.
|
|
if ( ret != ERROR_SUCCESS ) {
|
|
ret = RegQueryValueEx( hKey, "~Mhz", NULL, NULL, (LPBYTE) &ProcSpeed, &buflen );
|
|
}
|
|
if ( ret != ERROR_SUCCESS ) {
|
|
ret = RegQueryValueEx( hKey, "~mhz", NULL, NULL, (LPBYTE) &ProcSpeed, &buflen );
|
|
}
|
|
RegCloseKey( hKey );
|
|
if ( ret == ERROR_SUCCESS ) {
|
|
ticks = (double) ((unsigned long)ProcSpeed) * 1000000;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
return ticks;
|
|
}
|
|
|
|
|
|
/*
|
|
==============================================================
|
|
|
|
CPU
|
|
|
|
==============================================================
|
|
*/
|
|
|
|
/*
|
|
================
|
|
HasCPUID
|
|
================
|
|
*/
|
|
static bool HasCPUID() {
|
|
__asm
|
|
{
|
|
pushfd // save eflags
|
|
pop eax
|
|
test eax, 0x00200000 // check ID bit
|
|
jz set21 // bit 21 is not set, so jump to set_21
|
|
and eax, 0xffdfffff // clear bit 21
|
|
push eax // save new value in register
|
|
popfd // store new value in flags
|
|
pushfd
|
|
pop eax
|
|
test eax, 0x00200000 // check ID bit
|
|
jz good
|
|
jmp err // cpuid not supported
|
|
set21:
|
|
or eax, 0x00200000 // set ID bit
|
|
push eax // store new value
|
|
popfd // store new value in EFLAGS
|
|
pushfd
|
|
pop eax
|
|
test eax, 0x00200000 // if bit 21 is on
|
|
jnz good
|
|
jmp err
|
|
}
|
|
|
|
err:
|
|
return false;
|
|
good:
|
|
return true;
|
|
}
|
|
|
|
#define _REG_EAX 0
|
|
#define _REG_EBX 1
|
|
#define _REG_ECX 2
|
|
#define _REG_EDX 3
|
|
|
|
/*
|
|
================
|
|
CPUID
|
|
================
|
|
*/
|
|
static void CPUID( int func, unsigned regs[4] ) {
|
|
unsigned regEAX, regEBX, regECX, regEDX;
|
|
|
|
__asm pusha
|
|
__asm mov eax, func
|
|
__asm __emit 00fh
|
|
__asm __emit 0a2h
|
|
__asm mov regEAX, eax
|
|
__asm mov regEBX, ebx
|
|
__asm mov regECX, ecx
|
|
__asm mov regEDX, edx
|
|
__asm popa
|
|
|
|
regs[_REG_EAX] = regEAX;
|
|
regs[_REG_EBX] = regEBX;
|
|
regs[_REG_ECX] = regECX;
|
|
regs[_REG_EDX] = regEDX;
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
IsAMD
|
|
================
|
|
*/
|
|
static bool IsAMD() {
|
|
char pstring[16];
|
|
char processorString[13];
|
|
|
|
// get name of processor
|
|
CPUID( 0, ( unsigned int * ) pstring );
|
|
processorString[0] = pstring[4];
|
|
processorString[1] = pstring[5];
|
|
processorString[2] = pstring[6];
|
|
processorString[3] = pstring[7];
|
|
processorString[4] = pstring[12];
|
|
processorString[5] = pstring[13];
|
|
processorString[6] = pstring[14];
|
|
processorString[7] = pstring[15];
|
|
processorString[8] = pstring[8];
|
|
processorString[9] = pstring[9];
|
|
processorString[10] = pstring[10];
|
|
processorString[11] = pstring[11];
|
|
processorString[12] = 0;
|
|
|
|
if ( strcmp( processorString, "AuthenticAMD" ) == 0 ) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
================
|
|
HasCMOV
|
|
================
|
|
*/
|
|
static bool HasCMOV() {
|
|
unsigned regs[4];
|
|
|
|
// get CPU feature bits
|
|
CPUID( 1, regs );
|
|
|
|
// bit 15 of EDX denotes CMOV existence
|
|
if ( regs[_REG_EDX] & ( 1 << 15 ) ) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
================
|
|
Has3DNow
|
|
================
|
|
*/
|
|
static bool Has3DNow() {
|
|
unsigned regs[4];
|
|
|
|
// check AMD-specific functions
|
|
CPUID( 0x80000000, regs );
|
|
if ( regs[_REG_EAX] < 0x80000000 ) {
|
|
return false;
|
|
}
|
|
|
|
// bit 31 of EDX denotes 3DNow! support
|
|
CPUID( 0x80000001, regs );
|
|
if ( regs[_REG_EDX] & ( 1 << 31 ) ) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
================
|
|
HasMMX
|
|
================
|
|
*/
|
|
static bool HasMMX() {
|
|
unsigned regs[4];
|
|
|
|
// get CPU feature bits
|
|
CPUID( 1, regs );
|
|
|
|
// bit 23 of EDX denotes MMX existence
|
|
if ( regs[_REG_EDX] & ( 1 << 23 ) ) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
================
|
|
HasSSE
|
|
================
|
|
*/
|
|
static bool HasSSE() {
|
|
unsigned regs[4];
|
|
|
|
// get CPU feature bits
|
|
CPUID( 1, regs );
|
|
|
|
// bit 25 of EDX denotes SSE existence
|
|
if ( regs[_REG_EDX] & ( 1 << 25 ) ) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
================
|
|
HasSSE2
|
|
================
|
|
*/
|
|
static bool HasSSE2() {
|
|
unsigned regs[4];
|
|
|
|
// get CPU feature bits
|
|
CPUID( 1, regs );
|
|
|
|
// bit 26 of EDX denotes SSE2 existence
|
|
if ( regs[_REG_EDX] & ( 1 << 26 ) ) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
================
|
|
HasSSE3
|
|
================
|
|
*/
|
|
static bool HasSSE3() {
|
|
unsigned regs[4];
|
|
|
|
// get CPU feature bits
|
|
CPUID( 1, regs );
|
|
|
|
// bit 0 of ECX denotes SSE3 existence
|
|
if ( regs[_REG_ECX] & ( 1 << 0 ) ) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
================
|
|
LogicalProcPerPhysicalProc
|
|
================
|
|
*/
|
|
#define NUM_LOGICAL_BITS 0x00FF0000 // EBX[23:16] Bit 16-23 in ebx contains the number of logical
|
|
// processors per physical processor when execute cpuid with
|
|
// eax set to 1
|
|
static unsigned char LogicalProcPerPhysicalProc() {
|
|
unsigned int regebx = 0;
|
|
__asm {
|
|
mov eax, 1
|
|
cpuid
|
|
mov regebx, ebx
|
|
}
|
|
return (unsigned char) ((regebx & NUM_LOGICAL_BITS) >> 16);
|
|
}
|
|
|
|
/*
|
|
================
|
|
GetAPIC_ID
|
|
================
|
|
*/
|
|
#define INITIAL_APIC_ID_BITS 0xFF000000 // EBX[31:24] Bits 24-31 (8 bits) return the 8-bit unique
|
|
// initial APIC ID for the processor this code is running on.
|
|
// Default value = 0xff if HT is not supported
|
|
static unsigned char GetAPIC_ID() {
|
|
unsigned int regebx = 0;
|
|
__asm {
|
|
mov eax, 1
|
|
cpuid
|
|
mov regebx, ebx
|
|
}
|
|
return (unsigned char) ((regebx & INITIAL_APIC_ID_BITS) >> 24);
|
|
}
|
|
|
|
/*
|
|
================
|
|
CPUCount
|
|
|
|
logicalNum is the number of logical CPU per physical CPU
|
|
physicalNum is the total number of physical processor
|
|
returns one of the HT_* flags
|
|
================
|
|
*/
|
|
#define HT_NOT_CAPABLE 0
|
|
#define HT_ENABLED 1
|
|
#define HT_DISABLED 2
|
|
#define HT_SUPPORTED_NOT_ENABLED 3
|
|
#define HT_CANNOT_DETECT 4
|
|
|
|
int CPUCount( int &logicalNum, int &physicalNum ) {
|
|
int statusFlag;
|
|
SYSTEM_INFO info;
|
|
|
|
physicalNum = 1;
|
|
logicalNum = 1;
|
|
statusFlag = HT_NOT_CAPABLE;
|
|
|
|
info.dwNumberOfProcessors = 0;
|
|
GetSystemInfo (&info);
|
|
|
|
// Number of physical processors in a non-Intel system
|
|
// or in a 32-bit Intel system with Hyper-Threading technology disabled
|
|
physicalNum = info.dwNumberOfProcessors;
|
|
|
|
unsigned char HT_Enabled = 0;
|
|
|
|
logicalNum = LogicalProcPerPhysicalProc();
|
|
|
|
if ( logicalNum >= 1 ) { // > 1 doesn't mean HT is enabled in the BIOS
|
|
HANDLE hCurrentProcessHandle;
|
|
DWORD dwProcessAffinity;
|
|
DWORD dwSystemAffinity;
|
|
DWORD dwAffinityMask;
|
|
|
|
// Calculate the appropriate shifts and mask based on the
|
|
// number of logical processors.
|
|
|
|
unsigned char i = 1, PHY_ID_MASK = 0xFF, PHY_ID_SHIFT = 0;
|
|
|
|
while( i < logicalNum ) {
|
|
i *= 2;
|
|
PHY_ID_MASK <<= 1;
|
|
PHY_ID_SHIFT++;
|
|
}
|
|
|
|
hCurrentProcessHandle = GetCurrentProcess();
|
|
GetProcessAffinityMask( hCurrentProcessHandle, &dwProcessAffinity, &dwSystemAffinity );
|
|
|
|
// Check if available process affinity mask is equal to the
|
|
// available system affinity mask
|
|
if ( dwProcessAffinity != dwSystemAffinity ) {
|
|
statusFlag = HT_CANNOT_DETECT;
|
|
physicalNum = -1;
|
|
return statusFlag;
|
|
}
|
|
|
|
dwAffinityMask = 1;
|
|
while ( dwAffinityMask != 0 && dwAffinityMask <= dwProcessAffinity ) {
|
|
// Check if this CPU is available
|
|
if ( dwAffinityMask & dwProcessAffinity ) {
|
|
if ( SetProcessAffinityMask( hCurrentProcessHandle, dwAffinityMask ) ) {
|
|
unsigned char APIC_ID, LOG_ID, PHY_ID;
|
|
|
|
Sleep( 0 ); // Give OS time to switch CPU
|
|
|
|
APIC_ID = GetAPIC_ID();
|
|
LOG_ID = APIC_ID & ~PHY_ID_MASK;
|
|
PHY_ID = APIC_ID >> PHY_ID_SHIFT;
|
|
|
|
if ( LOG_ID != 0 ) {
|
|
HT_Enabled = 1;
|
|
}
|
|
}
|
|
}
|
|
dwAffinityMask = dwAffinityMask << 1;
|
|
}
|
|
|
|
// Reset the processor affinity
|
|
SetProcessAffinityMask( hCurrentProcessHandle, dwProcessAffinity );
|
|
|
|
if ( logicalNum == 1 ) { // Normal P4 : HT is disabled in hardware
|
|
statusFlag = HT_DISABLED;
|
|
} else {
|
|
if ( HT_Enabled ) {
|
|
// Total physical processors in a Hyper-Threading enabled system.
|
|
physicalNum /= logicalNum;
|
|
statusFlag = HT_ENABLED;
|
|
} else {
|
|
statusFlag = HT_SUPPORTED_NOT_ENABLED;
|
|
}
|
|
}
|
|
}
|
|
return statusFlag;
|
|
}
|
|
|
|
/*
|
|
================
|
|
HasHTT
|
|
================
|
|
*/
|
|
static bool HasHTT() {
|
|
unsigned regs[4];
|
|
int logicalNum, physicalNum, HTStatusFlag;
|
|
|
|
// get CPU feature bits
|
|
CPUID( 1, regs );
|
|
|
|
// bit 28 of EDX denotes HTT existence
|
|
if ( !( regs[_REG_EDX] & ( 1 << 28 ) ) ) {
|
|
return false;
|
|
}
|
|
|
|
HTStatusFlag = CPUCount( logicalNum, physicalNum );
|
|
if ( HTStatusFlag != HT_ENABLED ) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
HasHTT
|
|
================
|
|
*/
|
|
static bool HasDAZ() {
|
|
__declspec(align(16)) unsigned char FXSaveArea[512];
|
|
unsigned char *FXArea = FXSaveArea;
|
|
DWORD dwMask = 0;
|
|
unsigned regs[4];
|
|
|
|
// get CPU feature bits
|
|
CPUID( 1, regs );
|
|
|
|
// bit 24 of EDX denotes support for FXSAVE
|
|
if ( !( regs[_REG_EDX] & ( 1 << 24 ) ) ) {
|
|
return false;
|
|
}
|
|
|
|
memset( FXArea, 0, sizeof( FXSaveArea ) );
|
|
|
|
__asm {
|
|
mov eax, FXArea
|
|
FXSAVE [eax]
|
|
}
|
|
|
|
dwMask = *(DWORD *)&FXArea[28]; // Read the MXCSR Mask
|
|
return ( ( dwMask & ( 1 << 6 ) ) == ( 1 << 6 ) ); // Return if the DAZ bit is set
|
|
}
|
|
|
|
/*
|
|
================================================================================================
|
|
|
|
CPU
|
|
|
|
================================================================================================
|
|
*/
|
|
|
|
/*
|
|
========================
|
|
CountSetBits
|
|
Helper function to count set bits in the processor mask.
|
|
========================
|
|
*/
|
|
DWORD CountSetBits( ULONG_PTR bitMask ) {
|
|
DWORD LSHIFT = sizeof( ULONG_PTR ) * 8 - 1;
|
|
DWORD bitSetCount = 0;
|
|
ULONG_PTR bitTest = (ULONG_PTR)1 << LSHIFT;
|
|
|
|
for ( DWORD i = 0; i <= LSHIFT; i++ ) {
|
|
bitSetCount += ( ( bitMask & bitTest ) ? 1 : 0 );
|
|
bitTest /= 2;
|
|
}
|
|
|
|
return bitSetCount;
|
|
}
|
|
|
|
typedef BOOL (WINAPI *LPFN_GLPI)( PSYSTEM_LOGICAL_PROCESSOR_INFORMATION, PDWORD );
|
|
|
|
enum LOGICAL_PROCESSOR_RELATIONSHIP_LOCAL {
|
|
localRelationProcessorCore,
|
|
localRelationNumaNode,
|
|
localRelationCache,
|
|
localRelationProcessorPackage
|
|
};
|
|
|
|
struct cpuInfo_t {
|
|
int processorPackageCount;
|
|
int processorCoreCount;
|
|
int logicalProcessorCount;
|
|
int numaNodeCount;
|
|
struct cacheInfo_t {
|
|
int count;
|
|
int associativity;
|
|
int lineSize;
|
|
int size;
|
|
} cacheLevel[3];
|
|
};
|
|
|
|
/*
|
|
========================
|
|
GetCPUInfo
|
|
========================
|
|
*/
|
|
bool GetCPUInfo( cpuInfo_t & cpuInfo ) {
|
|
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION buffer = NULL;
|
|
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION ptr = NULL;
|
|
PCACHE_DESCRIPTOR Cache;
|
|
LPFN_GLPI glpi;
|
|
BOOL done = FALSE;
|
|
DWORD returnLength = 0;
|
|
DWORD byteOffset = 0;
|
|
|
|
memset( & cpuInfo, 0, sizeof( cpuInfo ) );
|
|
|
|
glpi = (LPFN_GLPI)GetProcAddress( GetModuleHandle(TEXT("kernel32")), "GetLogicalProcessorInformation" );
|
|
if ( NULL == glpi ) {
|
|
idLib::Printf( "\nGetLogicalProcessorInformation is not supported.\n" );
|
|
return 0;
|
|
}
|
|
|
|
while ( !done ) {
|
|
DWORD rc = glpi( buffer, &returnLength );
|
|
|
|
if ( FALSE == rc ) {
|
|
if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER ) {
|
|
if ( buffer ) {
|
|
free( buffer );
|
|
}
|
|
|
|
buffer = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION)malloc( returnLength );
|
|
} else {
|
|
idLib::Printf( "Sys_CPUCount error: %d\n", GetLastError() );
|
|
return false;
|
|
}
|
|
} else {
|
|
done = TRUE;
|
|
}
|
|
}
|
|
|
|
ptr = buffer;
|
|
|
|
while ( byteOffset + sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION) <= returnLength ) {
|
|
switch ( (LOGICAL_PROCESSOR_RELATIONSHIP_LOCAL) ptr->Relationship ) {
|
|
case localRelationProcessorCore:
|
|
cpuInfo.processorCoreCount++;
|
|
|
|
// A hyperthreaded core supplies more than one logical processor.
|
|
cpuInfo.logicalProcessorCount += CountSetBits( ptr->ProcessorMask );
|
|
break;
|
|
|
|
case localRelationNumaNode:
|
|
// Non-NUMA systems report a single record of this type.
|
|
cpuInfo.numaNodeCount++;
|
|
break;
|
|
|
|
case localRelationCache:
|
|
// Cache data is in ptr->Cache, one CACHE_DESCRIPTOR structure for each cache.
|
|
Cache = &ptr->Cache;
|
|
if ( Cache->Level >= 1 && Cache->Level <= 3 ) {
|
|
int level = Cache->Level - 1;
|
|
if ( cpuInfo.cacheLevel[level].count > 0 ) {
|
|
cpuInfo.cacheLevel[level].count++;
|
|
} else {
|
|
cpuInfo.cacheLevel[level].associativity = Cache->Associativity;
|
|
cpuInfo.cacheLevel[level].lineSize = Cache->LineSize;
|
|
cpuInfo.cacheLevel[level].size = Cache->Size;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case localRelationProcessorPackage:
|
|
// Logical processors share a physical package.
|
|
cpuInfo.processorPackageCount++;
|
|
break;
|
|
|
|
default:
|
|
idLib::Printf( "Error: Unsupported LOGICAL_PROCESSOR_RELATIONSHIP value.\n" );
|
|
break;
|
|
}
|
|
byteOffset += sizeof( SYSTEM_LOGICAL_PROCESSOR_INFORMATION );
|
|
ptr++;
|
|
}
|
|
|
|
free( buffer );
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
========================
|
|
Sys_GetCPUCacheSize
|
|
========================
|
|
*/
|
|
void Sys_GetCPUCacheSize( int level, int & count, int & size, int & lineSize ) {
|
|
assert( level >= 1 && level <= 3 );
|
|
cpuInfo_t cpuInfo;
|
|
|
|
GetCPUInfo( cpuInfo );
|
|
|
|
count = cpuInfo.cacheLevel[level - 1].count;
|
|
size = cpuInfo.cacheLevel[level - 1].size;
|
|
lineSize = cpuInfo.cacheLevel[level - 1].lineSize;
|
|
}
|
|
|
|
/*
|
|
========================
|
|
Sys_CPUCount
|
|
|
|
numLogicalCPUCores - the number of logical CPU per core
|
|
numPhysicalCPUCores - the total number of cores per package
|
|
numCPUPackages - the total number of packages (physical processors)
|
|
========================
|
|
*/
|
|
void Sys_CPUCount( int & numLogicalCPUCores, int & numPhysicalCPUCores, int & numCPUPackages ) {
|
|
cpuInfo_t cpuInfo;
|
|
GetCPUInfo( cpuInfo );
|
|
|
|
numPhysicalCPUCores = cpuInfo.processorCoreCount;
|
|
numLogicalCPUCores = cpuInfo.logicalProcessorCount;
|
|
numCPUPackages = cpuInfo.processorPackageCount;
|
|
}
|
|
|
|
/*
|
|
================
|
|
Sys_GetCPUId
|
|
================
|
|
*/
|
|
cpuid_t Sys_GetCPUId() {
|
|
int flags;
|
|
|
|
// verify we're at least a Pentium or 486 with CPUID support
|
|
if ( !HasCPUID() ) {
|
|
return CPUID_UNSUPPORTED;
|
|
}
|
|
|
|
// check for an AMD
|
|
if ( IsAMD() ) {
|
|
flags = CPUID_AMD;
|
|
} else {
|
|
flags = CPUID_INTEL;
|
|
}
|
|
|
|
// check for Multi Media Extensions
|
|
if ( HasMMX() ) {
|
|
flags |= CPUID_MMX;
|
|
}
|
|
|
|
// check for 3DNow!
|
|
if ( Has3DNow() ) {
|
|
flags |= CPUID_3DNOW;
|
|
}
|
|
|
|
// check for Streaming SIMD Extensions
|
|
if ( HasSSE() ) {
|
|
flags |= CPUID_SSE | CPUID_FTZ;
|
|
}
|
|
|
|
// check for Streaming SIMD Extensions 2
|
|
if ( HasSSE2() ) {
|
|
flags |= CPUID_SSE2;
|
|
}
|
|
|
|
// check for Streaming SIMD Extensions 3 aka Prescott's New Instructions
|
|
if ( HasSSE3() ) {
|
|
flags |= CPUID_SSE3;
|
|
}
|
|
|
|
// check for Hyper-Threading Technology
|
|
if ( HasHTT() ) {
|
|
flags |= CPUID_HTT;
|
|
}
|
|
|
|
// check for Conditional Move (CMOV) and fast floating point comparison (FCOMI) instructions
|
|
if ( HasCMOV() ) {
|
|
flags |= CPUID_CMOV;
|
|
}
|
|
|
|
// check for Denormals-Are-Zero mode
|
|
if ( HasDAZ() ) {
|
|
flags |= CPUID_DAZ;
|
|
}
|
|
|
|
return (cpuid_t)flags;
|
|
}
|
|
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
FPU
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
typedef struct bitFlag_s {
|
|
char * name;
|
|
int bit;
|
|
} bitFlag_t;
|
|
|
|
static byte fpuState[128], *statePtr = fpuState;
|
|
static char fpuString[2048];
|
|
static bitFlag_t controlWordFlags[] = {
|
|
{ "Invalid operation", 0 },
|
|
{ "Denormalized operand", 1 },
|
|
{ "Divide-by-zero", 2 },
|
|
{ "Numeric overflow", 3 },
|
|
{ "Numeric underflow", 4 },
|
|
{ "Inexact result (precision)", 5 },
|
|
{ "Infinity control", 12 },
|
|
{ "", 0 }
|
|
};
|
|
static char *precisionControlField[] = {
|
|
"Single Precision (24-bits)",
|
|
"Reserved",
|
|
"Double Precision (53-bits)",
|
|
"Double Extended Precision (64-bits)"
|
|
};
|
|
static char *roundingControlField[] = {
|
|
"Round to nearest",
|
|
"Round down",
|
|
"Round up",
|
|
"Round toward zero"
|
|
};
|
|
static bitFlag_t statusWordFlags[] = {
|
|
{ "Invalid operation", 0 },
|
|
{ "Denormalized operand", 1 },
|
|
{ "Divide-by-zero", 2 },
|
|
{ "Numeric overflow", 3 },
|
|
{ "Numeric underflow", 4 },
|
|
{ "Inexact result (precision)", 5 },
|
|
{ "Stack fault", 6 },
|
|
{ "Error summary status", 7 },
|
|
{ "FPU busy", 15 },
|
|
{ "", 0 }
|
|
};
|
|
|
|
/*
|
|
===============
|
|
Sys_FPU_PrintStateFlags
|
|
===============
|
|
*/
|
|
int Sys_FPU_PrintStateFlags( char *ptr, int ctrl, int stat, int tags, int inof, int inse, int opof, int opse ) {
|
|
int i, length = 0;
|
|
|
|
length += sprintf( ptr+length, "CTRL = %08x\n"
|
|
"STAT = %08x\n"
|
|
"TAGS = %08x\n"
|
|
"INOF = %08x\n"
|
|
"INSE = %08x\n"
|
|
"OPOF = %08x\n"
|
|
"OPSE = %08x\n"
|
|
"\n",
|
|
ctrl, stat, tags, inof, inse, opof, opse );
|
|
|
|
length += sprintf( ptr+length, "Control Word:\n" );
|
|
for ( i = 0; controlWordFlags[i].name[0]; i++ ) {
|
|
length += sprintf( ptr+length, " %-30s = %s\n", controlWordFlags[i].name, ( ctrl & ( 1 << controlWordFlags[i].bit ) ) ? "true" : "false" );
|
|
}
|
|
length += sprintf( ptr+length, " %-30s = %s\n", "Precision control", precisionControlField[(ctrl>>8)&3] );
|
|
length += sprintf( ptr+length, " %-30s = %s\n", "Rounding control", roundingControlField[(ctrl>>10)&3] );
|
|
|
|
length += sprintf( ptr+length, "Status Word:\n" );
|
|
for ( i = 0; statusWordFlags[i].name[0]; i++ ) {
|
|
ptr += sprintf( ptr+length, " %-30s = %s\n", statusWordFlags[i].name, ( stat & ( 1 << statusWordFlags[i].bit ) ) ? "true" : "false" );
|
|
}
|
|
length += sprintf( ptr+length, " %-30s = %d%d%d%d\n", "Condition code", (stat>>8)&1, (stat>>9)&1, (stat>>10)&1, (stat>>14)&1 );
|
|
length += sprintf( ptr+length, " %-30s = %d\n", "Top of stack pointer", (stat>>11)&7 );
|
|
|
|
return length;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
Sys_FPU_StackIsEmpty
|
|
===============
|
|
*/
|
|
bool Sys_FPU_StackIsEmpty() {
|
|
__asm {
|
|
mov eax, statePtr
|
|
fnstenv [eax]
|
|
mov eax, [eax+8]
|
|
xor eax, 0xFFFFFFFF
|
|
and eax, 0x0000FFFF
|
|
jz empty
|
|
}
|
|
return false;
|
|
empty:
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
Sys_FPU_ClearStack
|
|
===============
|
|
*/
|
|
void Sys_FPU_ClearStack() {
|
|
__asm {
|
|
mov eax, statePtr
|
|
fnstenv [eax]
|
|
mov eax, [eax+8]
|
|
xor eax, 0xFFFFFFFF
|
|
mov edx, (3<<14)
|
|
emptyStack:
|
|
mov ecx, eax
|
|
and ecx, edx
|
|
jz done
|
|
fstp st
|
|
shr edx, 2
|
|
jmp emptyStack
|
|
done:
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
Sys_FPU_GetState
|
|
|
|
gets the FPU state without changing the state
|
|
===============
|
|
*/
|
|
const char *Sys_FPU_GetState() {
|
|
double fpuStack[8] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
|
|
double *fpuStackPtr = fpuStack;
|
|
int i, numValues;
|
|
char *ptr;
|
|
|
|
__asm {
|
|
mov esi, statePtr
|
|
mov edi, fpuStackPtr
|
|
fnstenv [esi]
|
|
mov esi, [esi+8]
|
|
xor esi, 0xFFFFFFFF
|
|
mov edx, (3<<14)
|
|
xor eax, eax
|
|
mov ecx, esi
|
|
and ecx, edx
|
|
jz done
|
|
fst qword ptr [edi+0]
|
|
inc eax
|
|
shr edx, 2
|
|
mov ecx, esi
|
|
and ecx, edx
|
|
jz done
|
|
fxch st(1)
|
|
fst qword ptr [edi+8]
|
|
inc eax
|
|
fxch st(1)
|
|
shr edx, 2
|
|
mov ecx, esi
|
|
and ecx, edx
|
|
jz done
|
|
fxch st(2)
|
|
fst qword ptr [edi+16]
|
|
inc eax
|
|
fxch st(2)
|
|
shr edx, 2
|
|
mov ecx, esi
|
|
and ecx, edx
|
|
jz done
|
|
fxch st(3)
|
|
fst qword ptr [edi+24]
|
|
inc eax
|
|
fxch st(3)
|
|
shr edx, 2
|
|
mov ecx, esi
|
|
and ecx, edx
|
|
jz done
|
|
fxch st(4)
|
|
fst qword ptr [edi+32]
|
|
inc eax
|
|
fxch st(4)
|
|
shr edx, 2
|
|
mov ecx, esi
|
|
and ecx, edx
|
|
jz done
|
|
fxch st(5)
|
|
fst qword ptr [edi+40]
|
|
inc eax
|
|
fxch st(5)
|
|
shr edx, 2
|
|
mov ecx, esi
|
|
and ecx, edx
|
|
jz done
|
|
fxch st(6)
|
|
fst qword ptr [edi+48]
|
|
inc eax
|
|
fxch st(6)
|
|
shr edx, 2
|
|
mov ecx, esi
|
|
and ecx, edx
|
|
jz done
|
|
fxch st(7)
|
|
fst qword ptr [edi+56]
|
|
inc eax
|
|
fxch st(7)
|
|
done:
|
|
mov numValues, eax
|
|
}
|
|
|
|
int ctrl = *(int *)&fpuState[0];
|
|
int stat = *(int *)&fpuState[4];
|
|
int tags = *(int *)&fpuState[8];
|
|
int inof = *(int *)&fpuState[12];
|
|
int inse = *(int *)&fpuState[16];
|
|
int opof = *(int *)&fpuState[20];
|
|
int opse = *(int *)&fpuState[24];
|
|
|
|
ptr = fpuString;
|
|
ptr += sprintf( ptr,"FPU State:\n"
|
|
"num values on stack = %d\n", numValues );
|
|
for ( i = 0; i < 8; i++ ) {
|
|
ptr += sprintf( ptr, "ST%d = %1.10e\n", i, fpuStack[i] );
|
|
}
|
|
|
|
Sys_FPU_PrintStateFlags( ptr, ctrl, stat, tags, inof, inse, opof, opse );
|
|
|
|
return fpuString;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
Sys_FPU_EnableExceptions
|
|
===============
|
|
*/
|
|
void Sys_FPU_EnableExceptions( int exceptions ) {
|
|
__asm {
|
|
mov eax, statePtr
|
|
mov ecx, exceptions
|
|
and cx, 63
|
|
not cx
|
|
fnstcw word ptr [eax]
|
|
mov bx, word ptr [eax]
|
|
or bx, 63
|
|
and bx, cx
|
|
mov word ptr [eax], bx
|
|
fldcw word ptr [eax]
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
Sys_FPU_SetPrecision
|
|
===============
|
|
*/
|
|
void Sys_FPU_SetPrecision( int precision ) {
|
|
short precisionBitTable[4] = { 0, 1, 3, 0 };
|
|
short precisionBits = precisionBitTable[precision & 3] << 8;
|
|
short precisionMask = ~( ( 1 << 9 ) | ( 1 << 8 ) );
|
|
|
|
__asm {
|
|
mov eax, statePtr
|
|
mov cx, precisionBits
|
|
fnstcw word ptr [eax]
|
|
mov bx, word ptr [eax]
|
|
and bx, precisionMask
|
|
or bx, cx
|
|
mov word ptr [eax], bx
|
|
fldcw word ptr [eax]
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
Sys_FPU_SetRounding
|
|
================
|
|
*/
|
|
void Sys_FPU_SetRounding( int rounding ) {
|
|
short roundingBitTable[4] = { 0, 1, 2, 3 };
|
|
short roundingBits = roundingBitTable[rounding & 3] << 10;
|
|
short roundingMask = ~( ( 1 << 11 ) | ( 1 << 10 ) );
|
|
|
|
__asm {
|
|
mov eax, statePtr
|
|
mov cx, roundingBits
|
|
fnstcw word ptr [eax]
|
|
mov bx, word ptr [eax]
|
|
and bx, roundingMask
|
|
or bx, cx
|
|
mov word ptr [eax], bx
|
|
fldcw word ptr [eax]
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
Sys_FPU_SetDAZ
|
|
================
|
|
*/
|
|
void Sys_FPU_SetDAZ( bool enable ) {
|
|
DWORD dwData;
|
|
|
|
_asm {
|
|
movzx ecx, byte ptr enable
|
|
and ecx, 1
|
|
shl ecx, 6
|
|
STMXCSR dword ptr dwData
|
|
mov eax, dwData
|
|
and eax, ~(1<<6) // clear DAX bit
|
|
or eax, ecx // set the DAZ bit
|
|
mov dwData, eax
|
|
LDMXCSR dword ptr dwData
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
Sys_FPU_SetFTZ
|
|
================
|
|
*/
|
|
void Sys_FPU_SetFTZ( bool enable ) {
|
|
DWORD dwData;
|
|
|
|
_asm {
|
|
movzx ecx, byte ptr enable
|
|
and ecx, 1
|
|
shl ecx, 15
|
|
STMXCSR dword ptr dwData
|
|
mov eax, dwData
|
|
and eax, ~(1<<15) // clear FTZ bit
|
|
or eax, ecx // set the FTZ bit
|
|
mov dwData, eax
|
|
LDMXCSR dword ptr dwData
|
|
}
|
|
}
|