2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
22 In addition, the Doom 3 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 Source Code. If not, please request a copy in writing from id Software at the address below.
24 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.
26 ===========================================================================
29 #include "../../idlib/precompiled.h"
32 #include "win_local.h"
36 ==============================================================
40 ==============================================================
48 double Sys_GetClockTicks( void ) {
53 QueryPerformanceCounter( &li );
54 return = (double ) li.LowPart + (double) 0xFFFFFFFF * li.HighPart;
69 return (double ) lo + (double) 0xFFFFFFFF * hi;
76 Sys_ClockTicksPerSecond
79 double Sys_ClockTicksPerSecond( void ) {
80 static double ticks = 0;
85 QueryPerformanceFrequency( &li );
96 if ( !RegOpenKeyEx( HKEY_LOCAL_MACHINE, "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", 0, KEY_READ, &hKey ) ) {
98 buflen = sizeof( ProcSpeed );
99 ret = RegQueryValueEx( hKey, "~MHz", NULL, NULL, (LPBYTE) &ProcSpeed, &buflen );
100 // If we don't succeed, try some other spellings.
101 if ( ret != ERROR_SUCCESS ) {
102 ret = RegQueryValueEx( hKey, "~Mhz", NULL, NULL, (LPBYTE) &ProcSpeed, &buflen );
104 if ( ret != ERROR_SUCCESS ) {
105 ret = RegQueryValueEx( hKey, "~mhz", NULL, NULL, (LPBYTE) &ProcSpeed, &buflen );
108 if ( ret == ERROR_SUCCESS ) {
109 ticks = (double) ((unsigned long)ProcSpeed) * 1000000;
120 ==============================================================
124 ==============================================================
132 static bool HasCPUID( void ) {
135 pushfd // save eflags
137 test eax, 0x00200000 // check ID bit
138 jz set21 // bit 21 is not set, so jump to set_21
139 and eax, 0xffdfffff // clear bit 21
140 push eax // save new value in register
141 popfd // store new value in flags
144 test eax, 0x00200000 // check ID bit
146 jmp err // cpuid not supported
148 or eax, 0x00200000 // set ID bit
149 push eax // store new value
150 popfd // store new value in EFLAGS
153 test eax, 0x00200000 // if bit 21 is on
174 static void CPUID( int func, unsigned regs[4] ) {
175 unsigned regEAX, regEBX, regECX, regEDX;
181 __asm mov regEAX, eax
182 __asm mov regEBX, ebx
183 __asm mov regECX, ecx
184 __asm mov regEDX, edx
187 regs[_REG_EAX] = regEAX;
188 regs[_REG_EBX] = regEBX;
189 regs[_REG_ECX] = regECX;
190 regs[_REG_EDX] = regEDX;
199 static bool IsAMD( void ) {
201 char processorString[13];
203 // get name of processor
204 CPUID( 0, ( unsigned int * ) pstring );
205 processorString[0] = pstring[4];
206 processorString[1] = pstring[5];
207 processorString[2] = pstring[6];
208 processorString[3] = pstring[7];
209 processorString[4] = pstring[12];
210 processorString[5] = pstring[13];
211 processorString[6] = pstring[14];
212 processorString[7] = pstring[15];
213 processorString[8] = pstring[8];
214 processorString[9] = pstring[9];
215 processorString[10] = pstring[10];
216 processorString[11] = pstring[11];
217 processorString[12] = 0;
219 if ( strcmp( processorString, "AuthenticAMD" ) == 0 ) {
230 static bool HasCMOV( void ) {
233 // get CPU feature bits
236 // bit 15 of EDX denotes CMOV existence
237 if ( regs[_REG_EDX] & ( 1 << 15 ) ) {
248 static bool Has3DNow( void ) {
251 // check AMD-specific functions
252 CPUID( 0x80000000, regs );
253 if ( regs[_REG_EAX] < 0x80000000 ) {
257 // bit 31 of EDX denotes 3DNow! support
258 CPUID( 0x80000001, regs );
259 if ( regs[_REG_EDX] & ( 1 << 31 ) ) {
271 static bool HasMMX( void ) {
274 // get CPU feature bits
277 // bit 23 of EDX denotes MMX existence
278 if ( regs[_REG_EDX] & ( 1 << 23 ) ) {
289 static bool HasSSE( void ) {
292 // get CPU feature bits
295 // bit 25 of EDX denotes SSE existence
296 if ( regs[_REG_EDX] & ( 1 << 25 ) ) {
307 static bool HasSSE2( void ) {
310 // get CPU feature bits
313 // bit 26 of EDX denotes SSE2 existence
314 if ( regs[_REG_EDX] & ( 1 << 26 ) ) {
325 static bool HasSSE3( void ) {
328 // get CPU feature bits
331 // bit 0 of ECX denotes SSE3 existence
332 if ( regs[_REG_ECX] & ( 1 << 0 ) ) {
340 LogicalProcPerPhysicalProc
343 #define NUM_LOGICAL_BITS 0x00FF0000 // EBX[23:16] Bit 16-23 in ebx contains the number of logical
344 // processors per physical processor when execute cpuid with
346 static unsigned char LogicalProcPerPhysicalProc( void ) {
347 unsigned int regebx = 0;
353 return (unsigned char) ((regebx & NUM_LOGICAL_BITS) >> 16);
361 #define INITIAL_APIC_ID_BITS 0xFF000000 // EBX[31:24] Bits 24-31 (8 bits) return the 8-bit unique
362 // initial APIC ID for the processor this code is running on.
363 // Default value = 0xff if HT is not supported
364 static unsigned char GetAPIC_ID( void ) {
365 unsigned int regebx = 0;
371 return (unsigned char) ((regebx & INITIAL_APIC_ID_BITS) >> 24);
378 logicalNum is the number of logical CPU per physical CPU
379 physicalNum is the total number of physical processor
380 returns one of the HT_* flags
383 #define HT_NOT_CAPABLE 0
385 #define HT_DISABLED 2
386 #define HT_SUPPORTED_NOT_ENABLED 3
387 #define HT_CANNOT_DETECT 4
389 int CPUCount( int &logicalNum, int &physicalNum ) {
395 statusFlag = HT_NOT_CAPABLE;
397 info.dwNumberOfProcessors = 0;
398 GetSystemInfo (&info);
400 // Number of physical processors in a non-Intel system
401 // or in a 32-bit Intel system with Hyper-Threading technology disabled
402 physicalNum = info.dwNumberOfProcessors;
404 unsigned char HT_Enabled = 0;
406 logicalNum = LogicalProcPerPhysicalProc();
408 if ( logicalNum >= 1 ) { // > 1 doesn't mean HT is enabled in the BIOS
409 HANDLE hCurrentProcessHandle;
410 DWORD dwProcessAffinity;
411 DWORD dwSystemAffinity;
412 DWORD dwAffinityMask;
414 // Calculate the appropriate shifts and mask based on the
415 // number of logical processors.
417 unsigned char i = 1, PHY_ID_MASK = 0xFF, PHY_ID_SHIFT = 0;
419 while( i < logicalNum ) {
425 hCurrentProcessHandle = GetCurrentProcess();
426 GetProcessAffinityMask( hCurrentProcessHandle, &dwProcessAffinity, &dwSystemAffinity );
428 // Check if available process affinity mask is equal to the
429 // available system affinity mask
430 if ( dwProcessAffinity != dwSystemAffinity ) {
431 statusFlag = HT_CANNOT_DETECT;
437 while ( dwAffinityMask != 0 && dwAffinityMask <= dwProcessAffinity ) {
438 // Check if this CPU is available
439 if ( dwAffinityMask & dwProcessAffinity ) {
440 if ( SetProcessAffinityMask( hCurrentProcessHandle, dwAffinityMask ) ) {
441 unsigned char APIC_ID, LOG_ID, PHY_ID;
443 Sleep( 0 ); // Give OS time to switch CPU
445 APIC_ID = GetAPIC_ID();
446 LOG_ID = APIC_ID & ~PHY_ID_MASK;
447 PHY_ID = APIC_ID >> PHY_ID_SHIFT;
454 dwAffinityMask = dwAffinityMask << 1;
457 // Reset the processor affinity
458 SetProcessAffinityMask( hCurrentProcessHandle, dwProcessAffinity );
460 if ( logicalNum == 1 ) { // Normal P4 : HT is disabled in hardware
461 statusFlag = HT_DISABLED;
464 // Total physical processors in a Hyper-Threading enabled system.
465 physicalNum /= logicalNum;
466 statusFlag = HT_ENABLED;
468 statusFlag = HT_SUPPORTED_NOT_ENABLED;
480 static bool HasHTT( void ) {
482 int logicalNum, physicalNum, HTStatusFlag;
484 // get CPU feature bits
487 // bit 28 of EDX denotes HTT existence
488 if ( !( regs[_REG_EDX] & ( 1 << 28 ) ) ) {
492 HTStatusFlag = CPUCount( logicalNum, physicalNum );
493 if ( HTStatusFlag != HT_ENABLED ) {
504 static bool HasDAZ( void ) {
505 __declspec(align(16)) unsigned char FXSaveArea[512];
506 unsigned char *FXArea = FXSaveArea;
510 // get CPU feature bits
513 // bit 24 of EDX denotes support for FXSAVE
514 if ( !( regs[_REG_EDX] & ( 1 << 24 ) ) ) {
518 memset( FXArea, 0, sizeof( FXSaveArea ) );
525 dwMask = *(DWORD *)&FXArea[28]; // Read the MXCSR Mask
526 return ( ( dwMask & ( 1 << 6 ) ) == ( 1 << 6 ) ); // Return if the DAZ bit is set
534 cpuid_t Sys_GetCPUId( void ) {
537 // verify we're at least a Pentium or 486 with CPUID support
539 return CPUID_UNSUPPORTED;
549 // check for Multi Media Extensions
556 flags |= CPUID_3DNOW;
559 // check for Streaming SIMD Extensions
561 flags |= CPUID_SSE | CPUID_FTZ;
564 // check for Streaming SIMD Extensions 2
569 // check for Streaming SIMD Extensions 3 aka Prescott's New Instructions
574 // check for Hyper-Threading Technology
579 // check for Conditional Move (CMOV) and fast floating point comparison (FCOMI) instructions
584 // check for Denormals-Are-Zero mode
589 return (cpuid_t)flags;
594 ===============================================================================
598 ===============================================================================
601 typedef struct bitFlag_s {
606 static byte fpuState[128], *statePtr = fpuState;
607 static char fpuString[2048];
608 static bitFlag_t controlWordFlags[] = {
609 { "Invalid operation", 0 },
610 { "Denormalized operand", 1 },
611 { "Divide-by-zero", 2 },
612 { "Numeric overflow", 3 },
613 { "Numeric underflow", 4 },
614 { "Inexact result (precision)", 5 },
615 { "Infinity control", 12 },
618 static char *precisionControlField[] = {
619 "Single Precision (24-bits)",
621 "Double Precision (53-bits)",
622 "Double Extended Precision (64-bits)"
624 static char *roundingControlField[] = {
630 static bitFlag_t statusWordFlags[] = {
631 { "Invalid operation", 0 },
632 { "Denormalized operand", 1 },
633 { "Divide-by-zero", 2 },
634 { "Numeric overflow", 3 },
635 { "Numeric underflow", 4 },
636 { "Inexact result (precision)", 5 },
637 { "Stack fault", 6 },
638 { "Error summary status", 7 },
645 Sys_FPU_PrintStateFlags
648 int Sys_FPU_PrintStateFlags( char *ptr, int ctrl, int stat, int tags, int inof, int inse, int opof, int opse ) {
651 length += sprintf( ptr+length, "CTRL = %08x\n"
659 ctrl, stat, tags, inof, inse, opof, opse );
661 length += sprintf( ptr+length, "Control Word:\n" );
662 for ( i = 0; controlWordFlags[i].name[0]; i++ ) {
663 length += sprintf( ptr+length, " %-30s = %s\n", controlWordFlags[i].name, ( ctrl & ( 1 << controlWordFlags[i].bit ) ) ? "true" : "false" );
665 length += sprintf( ptr+length, " %-30s = %s\n", "Precision control", precisionControlField[(ctrl>>8)&3] );
666 length += sprintf( ptr+length, " %-30s = %s\n", "Rounding control", roundingControlField[(ctrl>>10)&3] );
668 length += sprintf( ptr+length, "Status Word:\n" );
669 for ( i = 0; statusWordFlags[i].name[0]; i++ ) {
670 ptr += sprintf( ptr+length, " %-30s = %s\n", statusWordFlags[i].name, ( stat & ( 1 << statusWordFlags[i].bit ) ) ? "true" : "false" );
672 length += sprintf( ptr+length, " %-30s = %d%d%d%d\n", "Condition code", (stat>>8)&1, (stat>>9)&1, (stat>>10)&1, (stat>>14)&1 );
673 length += sprintf( ptr+length, " %-30s = %d\n", "Top of stack pointer", (stat>>11)&7 );
683 bool Sys_FPU_StackIsEmpty( void ) {
702 void Sys_FPU_ClearStack( void ) {
724 gets the FPU state without changing the state
727 const char *Sys_FPU_GetState( void ) {
728 double fpuStack[8] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
729 double *fpuStackPtr = fpuStack;
744 fst qword ptr [edi+0]
751 fst qword ptr [edi+8]
759 fst qword ptr [edi+16]
767 fst qword ptr [edi+24]
775 fst qword ptr [edi+32]
783 fst qword ptr [edi+40]
791 fst qword ptr [edi+48]
799 fst qword ptr [edi+56]
806 int ctrl = *(int *)&fpuState[0];
807 int stat = *(int *)&fpuState[4];
808 int tags = *(int *)&fpuState[8];
809 int inof = *(int *)&fpuState[12];
810 int inse = *(int *)&fpuState[16];
811 int opof = *(int *)&fpuState[20];
812 int opse = *(int *)&fpuState[24];
815 ptr += sprintf( ptr,"FPU State:\n"
816 "num values on stack = %d\n", numValues );
817 for ( i = 0; i < 8; i++ ) {
818 ptr += sprintf( ptr, "ST%d = %1.10e\n", i, fpuStack[i] );
821 Sys_FPU_PrintStateFlags( ptr, ctrl, stat, tags, inof, inse, opof, opse );
828 Sys_FPU_EnableExceptions
831 void Sys_FPU_EnableExceptions( int exceptions ) {
837 fnstcw word ptr [eax]
838 mov bx, word ptr [eax]
841 mov word ptr [eax], bx
851 void Sys_FPU_SetPrecision( int precision ) {
852 short precisionBitTable[4] = { 0, 1, 3, 0 };
853 short precisionBits = precisionBitTable[precision & 3] << 8;
854 short precisionMask = ~( ( 1 << 9 ) | ( 1 << 8 ) );
858 mov cx, precisionBits
859 fnstcw word ptr [eax]
860 mov bx, word ptr [eax]
861 and bx, precisionMask
863 mov word ptr [eax], bx
873 void Sys_FPU_SetRounding( int rounding ) {
874 short roundingBitTable[4] = { 0, 1, 2, 3 };
875 short roundingBits = roundingBitTable[rounding & 3] << 10;
876 short roundingMask = ~( ( 1 << 11 ) | ( 1 << 10 ) );
881 fnstcw word ptr [eax]
882 mov bx, word ptr [eax]
885 mov word ptr [eax], bx
895 void Sys_FPU_SetDAZ( bool enable ) {
899 movzx ecx, byte ptr enable
902 STMXCSR dword ptr dwData
904 and eax, ~(1<<6) // clear DAX bit
905 or eax, ecx // set the DAZ bit
907 LDMXCSR dword ptr dwData
916 void Sys_FPU_SetFTZ( bool enable ) {
920 movzx ecx, byte ptr enable
923 STMXCSR dword ptr dwData
925 and eax, ~(1<<15) // clear FTZ bit
926 or eax, ecx // set the FTZ bit
928 LDMXCSR dword ptr dwData