1 /* $OpenBSD: fenv.c,v 1.6 2022/12/27 17:10:07 jmc Exp $ */
2
3 /*
4 * Copyright (c) 2011 Martynas Venckus <martynas@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <fenv.h>
20
21 /*
22 * The following constant represents the default floating-point environment
23 * (that is, the one installed at program startup) and has type pointer to
24 * const-qualified fenv_t.
25 *
26 * It can be used as an argument to the functions within the <fenv.h> header
27 * that manage the floating-point environment, namely fesetenv() and
28 * feupdateenv().
29 */
30 fenv_t __fe_dfl_env = 0;
31
32 /*
33 * The feclearexcept() function clears the supported floating-point exceptions
34 * represented by `excepts'.
35 */
36 int
feclearexcept(int excepts)37 feclearexcept(int excepts)
38 {
39 unsigned int fcsr;
40
41 excepts &= FE_ALL_EXCEPT;
42
43 /* Store the current floating-point control and status register */
44 __asm__ volatile ("cfc1 %0, $31" : "=r" (fcsr));
45
46 /* Clear the requested floating-point exceptions */
47 fcsr &= ~excepts;
48
49 /* Load the floating-point control and status register */
50 __asm__ volatile ("ctc1 %0, $31" : : "r" (fcsr));
51
52 return (0);
53 }
54 DEF_STD(feclearexcept);
55
56 /*
57 * The fegetexceptflag() function stores an implementation-defined
58 * representation of the states of the floating-point status flags indicated by
59 * the argument excepts in the object pointed to by the argument flagp.
60 */
61 int
fegetexceptflag(fexcept_t * flagp,int excepts)62 fegetexceptflag(fexcept_t *flagp, int excepts)
63 {
64 unsigned int fcsr;
65
66 excepts &= FE_ALL_EXCEPT;
67
68 /* Store the current floating-point control and status register */
69 __asm__ volatile ("cfc1 %0, $31" : "=r" (fcsr));
70
71 /* Store the results in flagp */
72 *flagp = fcsr & excepts;
73
74 return (0);
75 }
76
77 /*
78 * The feraiseexcept() function raises the supported floating-point exceptions
79 * represented by the argument `excepts'.
80 */
81 int
feraiseexcept(int excepts)82 feraiseexcept(int excepts)
83 {
84 unsigned int fcsr;
85
86 excepts &= FE_ALL_EXCEPT;
87
88 /* Store the current floating-point control and status register */
89 __asm__ volatile ("cfc1 %0, $31" : "=r" (fcsr));
90
91 fcsr |= excepts | (excepts << 10);
92
93 /* Load the floating-point control and status register */
94 __asm__ volatile ("ctc1 %0, $31" : : "r" (fcsr));
95
96 return (0);
97 }
98 DEF_STD(feraiseexcept);
99
100 /*
101 * This function sets the floating-point status flags indicated by the argument
102 * `excepts' to the states stored in the object pointed to by `flagp'. It does
103 * NOT raise any floating-point exceptions, but only sets the state of the flags.
104 */
105 int
fesetexceptflag(const fexcept_t * flagp,int excepts)106 fesetexceptflag(const fexcept_t *flagp, int excepts)
107 {
108 unsigned int fcsr;
109
110 excepts &= FE_ALL_EXCEPT;
111
112 /* Store the current floating-point control and status register */
113 __asm__ volatile ("cfc1 %0, $31" : "=r" (fcsr));
114
115 /* Set the requested status flags */
116 fcsr &= ~excepts;
117 fcsr |= *flagp & excepts;
118
119 /* Load the floating-point control and status register */
120 __asm__ volatile ("ctc1 %0, $31" : : "r" (fcsr));
121
122 return (0);
123 }
124 DEF_STD(fesetexceptflag);
125
126 /*
127 * The fetestexcept() function determines which of a specified subset of the
128 * floating-point exception flags are currently set. The `excepts' argument
129 * specifies the floating-point status flags to be queried.
130 */
131 int
fetestexcept(int excepts)132 fetestexcept(int excepts)
133 {
134 unsigned int fcsr;
135
136 excepts &= FE_ALL_EXCEPT;
137
138 /* Store the current floating-point control and status register */
139 __asm__ volatile ("cfc1 %0, $31" : "=r" (fcsr));
140
141 return (fcsr & excepts);
142 }
143 DEF_STD(fetestexcept);
144
145 /*
146 * The fegetround() function gets the current rounding direction.
147 */
148 int
fegetround(void)149 fegetround(void)
150 {
151 unsigned int fcsr;
152
153 /* Store the current floating-point control and status register */
154 __asm__ volatile ("cfc1 %0, $31" : "=r" (fcsr));
155
156 return (fcsr & _ROUND_MASK);
157 }
158 DEF_STD(fegetround);
159
160 /*
161 * The fesetround() function establishes the rounding direction represented by
162 * its argument `round'. If the argument is not equal to the value of a rounding
163 * direction macro, the rounding direction is not changed.
164 */
165 int
fesetround(int round)166 fesetround(int round)
167 {
168 unsigned int fcsr;
169
170 /* Check whether requested rounding direction is supported */
171 if (round & ~_ROUND_MASK)
172 return (-1);
173
174 /* Store the current floating-point control and status register */
175 __asm__ volatile ("cfc1 %0, $31" : "=r" (fcsr));
176
177 /* Set the rounding direction */
178 fcsr &= ~_ROUND_MASK;
179 fcsr |= round;
180
181 /* Load the floating-point control and status register */
182 __asm__ volatile ("ctc1 %0, $31" : : "r" (fcsr));
183
184 return (0);
185 }
186 DEF_STD(fesetround);
187
188 /*
189 * The fegetenv() function attempts to store the current floating-point
190 * environment in the object pointed to by envp.
191 */
192 int
fegetenv(fenv_t * envp)193 fegetenv(fenv_t *envp)
194 {
195 /* Store the current floating-point control and status register */
196 __asm__ volatile ("cfc1 %0, $31" : "=r" (*envp));
197
198 return (0);
199 }
200 DEF_STD(fegetenv);
201
202 /*
203 * The feholdexcept() function saves the current floating-point environment
204 * in the object pointed to by envp, clears the floating-point status flags, and
205 * then installs a non-stop (continue on floating-point exceptions) mode, if
206 * available, for all floating-point exceptions.
207 */
208 int
feholdexcept(fenv_t * envp)209 feholdexcept(fenv_t *envp)
210 {
211 unsigned int fcsr;
212
213 /* Store the current floating-point control and status register */
214 __asm__ volatile ("cfc1 %0, $31" : "=r" (*envp));
215
216 /* Clear exception flags in FCSR */
217 fcsr = *envp;
218 fcsr &= ~FE_ALL_EXCEPT;
219
220 /* Mask all exceptions */
221 fcsr &= ~(FE_ALL_EXCEPT << _MASK_SHIFT);
222 __asm__ volatile ("ctc1 %0, $31" : : "r" (fcsr));
223
224 return (0);
225 }
226 DEF_STD(feholdexcept);
227
228 /*
229 * The fesetenv() function attempts to establish the floating-point environment
230 * represented by the object pointed to by envp. The argument `envp' points
231 * to an object set by a call to fegetenv() or feholdexcept(), or equal a
232 * floating-point environment macro. The fesetenv() function does not raise
233 * floating-point exceptions, but only installs the state of the floating-point
234 * status flags represented through its argument.
235 */
236 int
fesetenv(const fenv_t * envp)237 fesetenv(const fenv_t *envp)
238 {
239 /* Load the floating-point control and status register */
240 __asm__ volatile ("ctc1 %0, $31" : : "r" (*envp));
241
242 return (0);
243 }
244 DEF_STD(fesetenv);
245
246 /*
247 * The feupdateenv() function saves the currently raised floating-point
248 * exceptions in its automatic storage, installs the floating-point environment
249 * represented by the object pointed to by `envp', and then raises the saved
250 * floating-point exceptions. The argument `envp' shall point to an object set
251 * by a call to feholdexcept() or fegetenv(), or equal a floating-point
252 * environment macro.
253 */
254 int
feupdateenv(const fenv_t * envp)255 feupdateenv(const fenv_t *envp)
256 {
257 unsigned int fcsr;
258
259 /* Store the current floating-point control and status register */
260 __asm__ volatile ("cfc1 %0, $31" : "=r" (fcsr));
261
262 /* Install new floating-point environment */
263 fesetenv(envp);
264
265 /* Raise any previously accumulated exceptions */
266 feraiseexcept(fcsr);
267
268 return (0);
269 }
270 DEF_STD(feupdateenv);
271
272 /*
273 * The following functions are extensions to the standard
274 */
275 int
feenableexcept(int mask)276 feenableexcept(int mask)
277 {
278 unsigned int fcsr, omask;
279
280 mask &= FE_ALL_EXCEPT;
281
282 /* Store the current floating-point control and status register */
283 __asm__ volatile ("cfc1 %0, $31" : "=r" (fcsr));
284
285 omask = (fcsr >> _MASK_SHIFT) & FE_ALL_EXCEPT;
286 fcsr |= mask << _MASK_SHIFT;
287
288 /* Load the floating-point control and status register */
289 __asm__ volatile ("ctc1 %0, $31" : : "r" (fcsr));
290
291 return (omask);
292
293 }
294
295 int
fedisableexcept(int mask)296 fedisableexcept(int mask)
297 {
298 unsigned int fcsr, omask;
299
300 mask &= FE_ALL_EXCEPT;
301
302 /* Store the current floating-point control and status register */
303 __asm__ volatile ("cfc1 %0, $31" : "=r" (fcsr));
304
305 omask = (fcsr >> _MASK_SHIFT) & FE_ALL_EXCEPT;
306 fcsr &= ~(mask << _MASK_SHIFT);
307
308 /* Load the floating-point control and status register */
309 __asm__ volatile ("ctc1 %0, $31" : : "r" (fcsr));
310
311 return (omask);
312 }
313
314 int
fegetexcept(void)315 fegetexcept(void)
316 {
317 unsigned int fcsr;
318
319 /* Store the current floating-point control and status register */
320 __asm__ volatile ("cfc1 %0, $31" : "=r" (fcsr));
321
322 return ((fcsr >> _MASK_SHIFT) & FE_ALL_EXCEPT);
323 }
324