xref: /reactos/sdk/lib/crt/float/i386/cntrlfp.c (revision cce399e7)
1 /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
2 
3 #include <precomp.h>
4 #include <float.h>
5 
6 #define X87_CW_IM   (1<<0)      /* Invalid operation mask */
7 #define X87_CW_DM   (1<<1)      /* Denormal operand mask */
8 #define X87_CW_ZM   (1<<2)      /* Zero divide mask */
9 #define X87_CW_OM   (1<<3)      /* Overflow mask */
10 #define X87_CW_UM   (1<<4)      /* Underflow mask */
11 #define X87_CW_PM   (1<<5)      /* Precision mask */
12 
13 #define X87_CW_PC_MASK     (3<<8)   /* precision control mask */
14 #define X87_CW_PC24        (0<<8)   /* 24 bit precision */
15 #define X87_CW_PC53        (2<<8)   /* 53 bit precision */
16 #define X87_CW_PC64        (3<<8)   /* 64 bit precision */
17 
18 #define X87_CW_RC_MASK     (3<<10)  /* rounding control mask */
19 #define X87_CW_RC_NEAREST  (0<<10)  /* round to nearest */
20 #define X87_CW_RC_DOWN     (1<<10)  /* round down */
21 #define X87_CW_RC_UP       (2<<10)  /* round up */
22 #define X87_CW_RC_ZERO     (3<<10)  /* round toward zero (chop) */
23 
24 #define X87_CW_IC          (1<<12)  /* infinity control flag */
25 
26 #ifdef _M_AMD64
27 unsigned int __getfpcw87(void);
28 void __setfpcw87(unsigned int);
29 #endif
30 
31 /*
32  * @implemented
33  */
34 
35 unsigned int CDECL _controlfp(unsigned int newval, unsigned int mask)
36 {
37   return _control87( newval, mask & ~_EM_DENORMAL );
38 }
39 
40 /*********************************************************************
41  *		_control87 (MSVCRT.@)
42  */
43 unsigned int CDECL _control87(unsigned int newval, unsigned int mask)
44 {
45   unsigned short fpword = 0;
46   unsigned int flags = 0;
47 
48   TRACE("(%08x, %08x): Called\n", newval, mask);
49 
50   /* Get fp control word */
51 #ifdef _M_AMD64
52   fpword = __getfpcw87();
53 #elif defined(__GNUC__)
54   __asm__ __volatile__( "fstcw %0" : "=m" (fpword) : );
55 #else
56   __asm fstcw [fpword];
57 #endif
58 
59   TRACE("Control word before : %08x\n", fpword);
60 
61   /* Convert into mask constants */
62   if (fpword & 0x1)  flags |= _EM_INVALID;
63   if (fpword & 0x2)  flags |= _EM_DENORMAL;
64   if (fpword & 0x4)  flags |= _EM_ZERODIVIDE;
65   if (fpword & 0x8)  flags |= _EM_OVERFLOW;
66   if (fpword & 0x10) flags |= _EM_UNDERFLOW;
67   if (fpword & 0x20) flags |= _EM_INEXACT;
68   switch(fpword & 0xC00) {
69   case 0xC00: flags |= _RC_UP|_RC_DOWN; break;
70   case 0x800: flags |= _RC_UP; break;
71   case 0x400: flags |= _RC_DOWN; break;
72   }
73   switch(fpword & 0x300) {
74   case 0x0:   flags |= _PC_24; break;
75   case 0x200: flags |= _PC_53; break;
76   case 0x300: flags |= _PC_64; break;
77   }
78   if (fpword & 0x1000) flags |= _IC_AFFINE;
79 
80   /* Mask with parameters */
81   flags = (flags & ~mask) | (newval & mask);
82 
83   /* Convert (masked) value back to fp word */
84   fpword = 0;
85   if (flags & _EM_INVALID)    fpword |= 0x1;
86   if (flags & _EM_DENORMAL)   fpword |= 0x2;
87   if (flags & _EM_ZERODIVIDE) fpword |= 0x4;
88   if (flags & _EM_OVERFLOW)   fpword |= 0x8;
89   if (flags & _EM_UNDERFLOW)  fpword |= 0x10;
90   if (flags & _EM_INEXACT)    fpword |= 0x20;
91   switch(flags & (_RC_UP | _RC_DOWN)) {
92   case _RC_UP|_RC_DOWN: fpword |= 0xC00; break;
93   case _RC_UP:          fpword |= 0x800; break;
94   case _RC_DOWN:        fpword |= 0x400; break;
95   }
96   switch (flags & (_PC_24 | _PC_53)) {
97   case _PC_64: fpword |= 0x300; break;
98   case _PC_53: fpword |= 0x200; break;
99   case _PC_24: fpword |= 0x0; break;
100   }
101   if (flags & _IC_AFFINE) fpword |= 0x1000;
102 
103   TRACE("Control word after  : %08x\n", fpword);
104 
105   /* Put fp control word */
106 #ifdef _M_AMD64
107   __setfpcw87(fpword);
108 #elif defined(__GNUC__)
109   __asm__ __volatile__( "fldcw %0" : : "m" (fpword) );
110 #else
111   __asm fldcw [fpword];
112 #endif
113 
114   return flags;
115 }
116