]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/sys/win32/win_cpu.cpp
hello world
[icculus/iodoom3.git] / neo / sys / win32 / win_cpu.cpp
1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. 
6
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).  
8
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.
13
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.
18
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/>.
21
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.
23
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.
25
26 ===========================================================================
27 */
28
29 #include "../../idlib/precompiled.h"
30 #pragma hdrstop
31
32 #include "win_local.h"
33
34
35 /*
36 ==============================================================
37
38         Clock ticks
39
40 ==============================================================
41 */
42
43 /*
44 ================
45 Sys_GetClockTicks
46 ================
47 */
48 double Sys_GetClockTicks( void ) {
49 #if 0
50
51         LARGE_INTEGER li;
52
53         QueryPerformanceCounter( &li );
54         return = (double ) li.LowPart + (double) 0xFFFFFFFF * li.HighPart;
55
56 #else
57
58         unsigned long lo, hi;
59
60         __asm {
61                 push ebx
62                 xor eax, eax
63                 cpuid
64                 rdtsc
65                 mov lo, eax
66                 mov hi, edx
67                 pop ebx
68         }
69         return (double ) lo + (double) 0xFFFFFFFF * hi;
70
71 #endif
72 }
73
74 /*
75 ================
76 Sys_ClockTicksPerSecond
77 ================
78 */
79 double Sys_ClockTicksPerSecond( void ) {
80         static double ticks = 0;
81 #if 0
82
83         if ( !ticks ) {
84                 LARGE_INTEGER li;
85                 QueryPerformanceFrequency( &li );
86                 ticks = li.QuadPart;
87         }
88
89 #else
90
91         if ( !ticks ) {
92                 HKEY hKey;
93                 LPBYTE ProcSpeed;
94                 DWORD buflen, ret;
95
96                 if ( !RegOpenKeyEx( HKEY_LOCAL_MACHINE, "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", 0, KEY_READ, &hKey ) ) {
97                         ProcSpeed = 0;
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 );
103                         }
104                         if ( ret != ERROR_SUCCESS ) {
105                                 ret = RegQueryValueEx( hKey, "~mhz", NULL, NULL, (LPBYTE) &ProcSpeed, &buflen );
106                         }
107                         RegCloseKey( hKey );
108                         if ( ret == ERROR_SUCCESS ) {
109                                 ticks = (double) ((unsigned long)ProcSpeed) * 1000000;
110                         }
111                 }
112         }
113
114 #endif
115         return ticks;
116 }
117
118
119 /*
120 ==============================================================
121
122         CPU
123
124 ==============================================================
125 */
126
127 /*
128 ================
129 HasCPUID
130 ================
131 */
132 static bool HasCPUID( void ) {
133         __asm 
134         {
135                 pushfd                                          // save eflags
136                 pop             eax
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
142                 pushfd
143                 pop             eax
144                 test    eax, 0x00200000         // check ID bit
145                 jz              good
146                 jmp             err                                     // cpuid not supported
147 set21:
148                 or              eax, 0x00200000         // set ID bit
149                 push    eax                                     // store new value
150                 popfd                                           // store new value in EFLAGS
151                 pushfd
152                 pop             eax
153                 test    eax, 0x00200000         // if bit 21 is on
154                 jnz             good
155                 jmp             err
156         }
157
158 err:
159         return false;
160 good:
161         return true;
162 }
163
164 #define _REG_EAX                0
165 #define _REG_EBX                1
166 #define _REG_ECX                2
167 #define _REG_EDX                3
168
169 /*
170 ================
171 CPUID
172 ================
173 */
174 static void CPUID( int func, unsigned regs[4] ) {
175         unsigned regEAX, regEBX, regECX, regEDX;
176
177         __asm pusha
178         __asm mov eax, func
179         __asm __emit 00fh
180         __asm __emit 0a2h
181         __asm mov regEAX, eax
182         __asm mov regEBX, ebx
183         __asm mov regECX, ecx
184         __asm mov regEDX, edx
185         __asm popa
186
187         regs[_REG_EAX] = regEAX;
188         regs[_REG_EBX] = regEBX;
189         regs[_REG_ECX] = regECX;
190         regs[_REG_EDX] = regEDX;
191 }
192
193
194 /*
195 ================
196 IsAMD
197 ================
198 */
199 static bool IsAMD( void ) {
200         char pstring[16];
201         char processorString[13];
202
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;
218
219         if ( strcmp( processorString, "AuthenticAMD" ) == 0 ) {
220                 return true;
221         }
222         return false;
223 }
224
225 /*
226 ================
227 HasCMOV
228 ================
229 */
230 static bool HasCMOV( void ) {
231         unsigned regs[4];
232
233         // get CPU feature bits
234         CPUID( 1, regs );
235
236         // bit 15 of EDX denotes CMOV existence
237         if ( regs[_REG_EDX] & ( 1 << 15 ) ) {
238                 return true;
239         }
240         return false;
241 }
242
243 /*
244 ================
245 Has3DNow
246 ================
247 */
248 static bool Has3DNow( void ) {
249         unsigned regs[4];
250
251         // check AMD-specific functions
252         CPUID( 0x80000000, regs );
253         if ( regs[_REG_EAX] < 0x80000000 ) {
254                 return false;
255         }
256
257         // bit 31 of EDX denotes 3DNow! support
258         CPUID( 0x80000001, regs );
259         if ( regs[_REG_EDX] & ( 1 << 31 ) ) {
260                 return true;
261         }
262
263         return false;
264 }
265
266 /*
267 ================
268 HasMMX
269 ================
270 */
271 static bool HasMMX( void ) {
272         unsigned regs[4];
273
274         // get CPU feature bits
275         CPUID( 1, regs );
276
277         // bit 23 of EDX denotes MMX existence
278         if ( regs[_REG_EDX] & ( 1 << 23 ) ) {
279                 return true;
280         }
281         return false;
282 }
283
284 /*
285 ================
286 HasSSE
287 ================
288 */
289 static bool HasSSE( void ) {
290         unsigned regs[4];
291
292         // get CPU feature bits
293         CPUID( 1, regs );
294
295         // bit 25 of EDX denotes SSE existence
296         if ( regs[_REG_EDX] & ( 1 << 25 ) ) {
297                 return true;
298         }
299         return false;
300 }
301
302 /*
303 ================
304 HasSSE2
305 ================
306 */
307 static bool HasSSE2( void ) {
308         unsigned regs[4];
309
310         // get CPU feature bits
311         CPUID( 1, regs );
312
313         // bit 26 of EDX denotes SSE2 existence
314         if ( regs[_REG_EDX] & ( 1 << 26 ) ) {
315                 return true;
316         }
317         return false;
318 }
319
320 /*
321 ================
322 HasSSE3
323 ================
324 */
325 static bool HasSSE3( void ) {
326         unsigned regs[4];
327
328         // get CPU feature bits
329         CPUID( 1, regs );
330
331         // bit 0 of ECX denotes SSE3 existence
332         if ( regs[_REG_ECX] & ( 1 << 0 ) ) {
333                 return true;
334         }
335         return false;
336 }
337
338 /*
339 ================
340 LogicalProcPerPhysicalProc
341 ================
342 */
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 
345                                           // eax set to 1
346 static unsigned char LogicalProcPerPhysicalProc( void ) {
347         unsigned int regebx = 0;
348         __asm {
349                 mov eax, 1
350                 cpuid
351                 mov regebx, ebx
352         }
353         return (unsigned char) ((regebx & NUM_LOGICAL_BITS) >> 16);
354 }
355
356 /*
357 ================
358 GetAPIC_ID
359 ================
360 */
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;
366         __asm {
367                 mov eax, 1
368                 cpuid
369                 mov regebx, ebx
370         }
371         return (unsigned char) ((regebx & INITIAL_APIC_ID_BITS) >> 24);
372 }
373
374 /*
375 ================
376 CPUCount
377
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
381 ================
382 */
383 #define HT_NOT_CAPABLE                          0
384 #define HT_ENABLED                                      1
385 #define HT_DISABLED                                     2
386 #define HT_SUPPORTED_NOT_ENABLED        3
387 #define HT_CANNOT_DETECT                        4
388
389 int CPUCount( int &logicalNum, int &physicalNum ) {
390         int statusFlag;
391         SYSTEM_INFO info;
392
393         physicalNum = 1;
394         logicalNum = 1;
395         statusFlag = HT_NOT_CAPABLE;
396
397         info.dwNumberOfProcessors = 0;
398         GetSystemInfo (&info);
399
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;  
403
404         unsigned char HT_Enabled = 0;
405
406         logicalNum = LogicalProcPerPhysicalProc();
407
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;
413
414                 // Calculate the appropriate  shifts and mask based on the 
415                 // number of logical processors.
416
417                 unsigned char i = 1, PHY_ID_MASK  = 0xFF, PHY_ID_SHIFT = 0;
418
419                 while( i < logicalNum ) {
420                         i *= 2;
421                         PHY_ID_MASK  <<= 1;
422                         PHY_ID_SHIFT++;
423                 }
424                 
425                 hCurrentProcessHandle = GetCurrentProcess();
426                 GetProcessAffinityMask( hCurrentProcessHandle, &dwProcessAffinity, &dwSystemAffinity );
427
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;
432                         physicalNum = -1;
433                         return statusFlag;
434                 }
435
436                 dwAffinityMask = 1;
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;
442
443                                         Sleep( 0 ); // Give OS time to switch CPU
444
445                                         APIC_ID = GetAPIC_ID();
446                                         LOG_ID  = APIC_ID & ~PHY_ID_MASK;
447                                         PHY_ID  = APIC_ID >> PHY_ID_SHIFT;
448
449                                         if ( LOG_ID != 0 ) {
450                                                 HT_Enabled = 1;
451                                         }
452                                 }
453                         }
454                         dwAffinityMask = dwAffinityMask << 1;
455                 }
456                 
457                 // Reset the processor affinity
458                 SetProcessAffinityMask( hCurrentProcessHandle, dwProcessAffinity );
459             
460                 if ( logicalNum == 1 ) {  // Normal P4 : HT is disabled in hardware
461                         statusFlag = HT_DISABLED;
462                 } else {
463                         if ( HT_Enabled ) {
464                                 // Total physical processors in a Hyper-Threading enabled system.
465                                 physicalNum /= logicalNum;
466                                 statusFlag = HT_ENABLED;
467                         } else {
468                                 statusFlag = HT_SUPPORTED_NOT_ENABLED;
469                         }
470                 }
471         }
472         return statusFlag;
473 }
474
475 /*
476 ================
477 HasHTT
478 ================
479 */
480 static bool HasHTT( void ) {
481         unsigned regs[4];
482         int logicalNum, physicalNum, HTStatusFlag;
483
484         // get CPU feature bits
485         CPUID( 1, regs );
486
487         // bit 28 of EDX denotes HTT existence
488         if ( !( regs[_REG_EDX] & ( 1 << 28 ) ) ) {
489                 return false;
490         }
491
492         HTStatusFlag = CPUCount( logicalNum, physicalNum );
493         if ( HTStatusFlag != HT_ENABLED ) {
494                 return false;
495         }
496         return true;
497 }
498
499 /*
500 ================
501 HasHTT
502 ================
503 */
504 static bool HasDAZ( void ) {
505         __declspec(align(16)) unsigned char FXSaveArea[512];
506         unsigned char *FXArea = FXSaveArea;
507         DWORD dwMask = 0;
508         unsigned regs[4];
509
510         // get CPU feature bits
511         CPUID( 1, regs );
512
513         // bit 24 of EDX denotes support for FXSAVE
514         if ( !( regs[_REG_EDX] & ( 1 << 24 ) ) ) {
515                 return false;
516         }
517
518         memset( FXArea, 0, sizeof( FXSaveArea ) );
519
520         __asm {
521                 mov             eax, FXArea
522                 FXSAVE  [eax]
523         }
524
525         dwMask = *(DWORD *)&FXArea[28];                                         // Read the MXCSR Mask
526         return ( ( dwMask & ( 1 << 6 ) ) == ( 1 << 6 ) );       // Return if the DAZ bit is set
527 }
528
529 /*
530 ================
531 Sys_GetCPUId
532 ================
533 */
534 cpuid_t Sys_GetCPUId( void ) {
535         int flags;
536
537         // verify we're at least a Pentium or 486 with CPUID support
538         if ( !HasCPUID() ) {
539                 return CPUID_UNSUPPORTED;
540         }
541
542         // check for an AMD
543         if ( IsAMD() ) {
544                 flags = CPUID_AMD;
545         } else {
546                 flags = CPUID_INTEL;
547         }
548
549         // check for Multi Media Extensions
550         if ( HasMMX() ) {
551                 flags |= CPUID_MMX;
552         }
553
554         // check for 3DNow!
555         if ( Has3DNow() ) {
556                 flags |= CPUID_3DNOW;
557         }
558
559         // check for Streaming SIMD Extensions
560         if ( HasSSE() ) {
561                 flags |= CPUID_SSE | CPUID_FTZ;
562         }
563
564         // check for Streaming SIMD Extensions 2
565         if ( HasSSE2() ) {
566                 flags |= CPUID_SSE2;
567         }
568
569         // check for Streaming SIMD Extensions 3 aka Prescott's New Instructions
570         if ( HasSSE3() ) {
571                 flags |= CPUID_SSE3;
572         }
573
574         // check for Hyper-Threading Technology
575         if ( HasHTT() ) {
576                 flags |= CPUID_HTT;
577         }
578
579         // check for Conditional Move (CMOV) and fast floating point comparison (FCOMI) instructions
580         if ( HasCMOV() ) {
581                 flags |= CPUID_CMOV;
582         }
583
584         // check for Denormals-Are-Zero mode
585         if ( HasDAZ() ) {
586                 flags |= CPUID_DAZ;
587         }
588
589         return (cpuid_t)flags;
590 }
591
592
593 /*
594 ===============================================================================
595
596         FPU
597
598 ===============================================================================
599 */
600
601 typedef struct bitFlag_s {
602         char *          name;
603         int                     bit;
604 } bitFlag_t;
605
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 },
616         { "", 0 }
617 };
618 static char *precisionControlField[] = {
619         "Single Precision (24-bits)",
620         "Reserved",
621         "Double Precision (53-bits)",
622         "Double Extended Precision (64-bits)"
623 };
624 static char *roundingControlField[] = {
625         "Round to nearest",
626         "Round down",
627         "Round up",
628         "Round toward zero"
629 };
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 },
639         { "FPU busy", 15 },
640         { "", 0 }
641 };
642
643 /*
644 ===============
645 Sys_FPU_PrintStateFlags
646 ===============
647 */
648 int Sys_FPU_PrintStateFlags( char *ptr, int ctrl, int stat, int tags, int inof, int inse, int opof, int opse ) {
649         int i, length = 0;
650
651         length += sprintf( ptr+length,  "CTRL = %08x\n"
652                                                                         "STAT = %08x\n"
653                                                                         "TAGS = %08x\n"
654                                                                         "INOF = %08x\n"
655                                                                         "INSE = %08x\n"
656                                                                         "OPOF = %08x\n"
657                                                                         "OPSE = %08x\n"
658                                                                         "\n",
659                                                                         ctrl, stat, tags, inof, inse, opof, opse );
660
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" );
664         }
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] );
667
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" );
671         }
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 );
674
675         return length;
676 }
677
678 /*
679 ===============
680 Sys_FPU_StackIsEmpty
681 ===============
682 */
683 bool Sys_FPU_StackIsEmpty( void ) {
684         __asm {
685                 mov                     eax, statePtr
686                 fnstenv         [eax]
687                 mov                     eax, [eax+8]
688                 xor                     eax, 0xFFFFFFFF
689                 and                     eax, 0x0000FFFF
690                 jz                      empty
691         }
692         return false;
693 empty:
694         return true;
695 }
696
697 /*
698 ===============
699 Sys_FPU_ClearStack
700 ===============
701 */
702 void Sys_FPU_ClearStack( void ) {
703         __asm {
704                 mov                     eax, statePtr
705                 fnstenv         [eax]
706                 mov                     eax, [eax+8]
707                 xor                     eax, 0xFFFFFFFF
708                 mov                     edx, (3<<14)
709         emptyStack:
710                 mov                     ecx, eax
711                 and                     ecx, edx
712                 jz                      done
713                 fstp            st
714                 shr                     edx, 2
715                 jmp                     emptyStack
716         done:
717         }
718 }
719
720 /*
721 ===============
722 Sys_FPU_GetState
723
724   gets the FPU state without changing the state
725 ===============
726 */
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;
730         int i, numValues;
731         char *ptr;
732
733         __asm {
734                 mov                     esi, statePtr
735                 mov                     edi, fpuStackPtr
736                 fnstenv         [esi]
737                 mov                     esi, [esi+8]
738                 xor                     esi, 0xFFFFFFFF
739                 mov                     edx, (3<<14)
740                 xor                     eax, eax
741                 mov                     ecx, esi
742                 and                     ecx, edx
743                 jz                      done
744                 fst                     qword ptr [edi+0]
745                 inc                     eax
746                 shr                     edx, 2
747                 mov                     ecx, esi
748                 and                     ecx, edx
749                 jz                      done
750                 fxch            st(1)
751                 fst                     qword ptr [edi+8]
752                 inc                     eax
753                 fxch            st(1)
754                 shr                     edx, 2
755                 mov                     ecx, esi
756                 and                     ecx, edx
757                 jz                      done
758                 fxch            st(2)
759                 fst                     qword ptr [edi+16]
760                 inc                     eax
761                 fxch            st(2)
762                 shr                     edx, 2
763                 mov                     ecx, esi
764                 and                     ecx, edx
765                 jz                      done
766                 fxch            st(3)
767                 fst                     qword ptr [edi+24]
768                 inc                     eax
769                 fxch            st(3)
770                 shr                     edx, 2
771                 mov                     ecx, esi
772                 and                     ecx, edx
773                 jz                      done
774                 fxch            st(4)
775                 fst                     qword ptr [edi+32]
776                 inc                     eax
777                 fxch            st(4)
778                 shr                     edx, 2
779                 mov                     ecx, esi
780                 and                     ecx, edx
781                 jz                      done
782                 fxch            st(5)
783                 fst                     qword ptr [edi+40]
784                 inc                     eax
785                 fxch            st(5)
786                 shr                     edx, 2
787                 mov                     ecx, esi
788                 and                     ecx, edx
789                 jz                      done
790                 fxch            st(6)
791                 fst                     qword ptr [edi+48]
792                 inc                     eax
793                 fxch            st(6)
794                 shr                     edx, 2
795                 mov                     ecx, esi
796                 and                     ecx, edx
797                 jz                      done
798                 fxch            st(7)
799                 fst                     qword ptr [edi+56]
800                 inc                     eax
801                 fxch            st(7)
802         done:
803                 mov                     numValues, eax
804         }
805
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];
813
814         ptr = fpuString;
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] );
819         }
820
821         Sys_FPU_PrintStateFlags( ptr, ctrl, stat, tags, inof, inse, opof, opse );
822
823         return fpuString;
824 }
825
826 /*
827 ===============
828 Sys_FPU_EnableExceptions
829 ===============
830 */
831 void Sys_FPU_EnableExceptions( int exceptions ) {
832         __asm {
833                 mov                     eax, statePtr
834                 mov                     ecx, exceptions
835                 and                     cx, 63
836                 not                     cx
837                 fnstcw          word ptr [eax]
838                 mov                     bx, word ptr [eax]
839                 or                      bx, 63
840                 and                     bx, cx
841                 mov                     word ptr [eax], bx
842                 fldcw           word ptr [eax]
843         }
844 }
845
846 /*
847 ===============
848 Sys_FPU_SetPrecision
849 ===============
850 */
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 ) );
855
856         __asm {
857                 mov                     eax, statePtr
858                 mov                     cx, precisionBits
859                 fnstcw          word ptr [eax]
860                 mov                     bx, word ptr [eax]
861                 and                     bx, precisionMask
862                 or                      bx, cx
863                 mov                     word ptr [eax], bx
864                 fldcw           word ptr [eax]
865         }
866 }
867
868 /*
869 ================
870 Sys_FPU_SetRounding
871 ================
872 */
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 ) );
877
878         __asm {
879                 mov                     eax, statePtr
880                 mov                     cx, roundingBits
881                 fnstcw          word ptr [eax]
882                 mov                     bx, word ptr [eax]
883                 and                     bx, roundingMask
884                 or                      bx, cx
885                 mov                     word ptr [eax], bx
886                 fldcw           word ptr [eax]
887         }
888 }
889
890 /*
891 ================
892 Sys_FPU_SetDAZ
893 ================
894 */
895 void Sys_FPU_SetDAZ( bool enable ) {
896         DWORD dwData;
897
898         _asm {
899                 movzx   ecx, byte ptr enable
900                 and             ecx, 1
901                 shl             ecx, 6
902                 STMXCSR dword ptr dwData
903                 mov             eax, dwData
904                 and             eax, ~(1<<6)    // clear DAX bit
905                 or              eax, ecx                // set the DAZ bit
906                 mov             dwData, eax
907                 LDMXCSR dword ptr dwData
908         }
909 }
910
911 /*
912 ================
913 Sys_FPU_SetFTZ
914 ================
915 */
916 void Sys_FPU_SetFTZ( bool enable ) {
917         DWORD dwData;
918
919         _asm {
920                 movzx   ecx, byte ptr enable
921                 and             ecx, 1
922                 shl             ecx, 15
923                 STMXCSR dword ptr dwData
924                 mov             eax, dwData
925                 and             eax, ~(1<<15)   // clear FTZ bit
926                 or              eax, ecx                // set the FTZ bit
927                 mov             dwData, eax
928                 LDMXCSR dword ptr dwData
929         }
930 }