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
21extern	unsigned int	__fpscr_values[2];
22
23/*
24 * The following constant represents the default floating-point environment
25 * (that is, the one installed at program startup) and has type pointer to
26 * const-qualified fenv_t.
27 *
28 * It can be used as an argument to the functions within the <fenv.h> header
29 * that manage the floating-point environment, namely fesetenv() and
30 * feupdateenv().
31 */
32fenv_t __fe_dfl_env = 0xc0000;
33
34/*
35 * The feclearexcept() function clears the supported floating-point exceptions
36 * represented by `excepts'.
37 */
38int
39feclearexcept(int excepts)
40{
41	unsigned int fpscr;
42
43	excepts &= FE_ALL_EXCEPT;
44
45	/* Store the current floating-point status and control register */
46	__asm__ volatile ("sts fpscr, %0" : "=r" (fpscr));
47
48	/* Clear the requested floating-point exceptions */
49	fpscr &= ~excepts;
50	__fpscr_values[0] &= ~excepts;
51	__fpscr_values[1] &= ~excepts;
52
53	/* Load the floating-point status and control register */
54	__asm__ volatile ("lds %0, fpscr" : : "r" (fpscr));
55
56	return (0);
57}
58DEF_STD(feclearexcept);
59
60/*
61 * The fegetexceptflag() function stores an implementation-defined
62 * representation of the states of the floating-point status flags indicated by
63 * the argument excepts in the object pointed to by the argument flagp.
64 */
65int
66fegetexceptflag(fexcept_t *flagp, int excepts)
67{
68	unsigned int fpscr;
69
70	excepts &= FE_ALL_EXCEPT;
71
72	/* Store the current floating-point status and control register */
73	__asm__ volatile ("sts fpscr, %0" : "=r" (fpscr));
74
75	/* Store the results in flagp */
76	*flagp = fpscr & excepts;
77
78	return (0);
79}
80
81/*
82 * The feraiseexcept() function raises the supported floating-point exceptions
83 * represented by the argument `excepts'.
84 */
85int
86feraiseexcept(int excepts)
87{
88	volatile double d;
89
90	excepts &= FE_ALL_EXCEPT;
91
92	/*
93	 * With a compiler that supports the FENV_ACCESS pragma
94	 * properly, simple expressions like '0.0 / 0.0' should
95	 * be sufficient to generate traps.  Unfortunately, we
96	 * need to bring a volatile variable into the equation
97	 * to prevent incorrect optimizations.
98	 */
99	if (excepts & FE_INVALID) {
100		d = 0.0;
101		d = 0.0 / d;
102	}
103	if (excepts & FE_DIVBYZERO) {
104		d = 0.0;
105		d = 1.0 / d;
106	}
107	if (excepts & FE_OVERFLOW) {
108		d = 0x1.ffp1023;
109		d *= 2.0;
110	}
111	if (excepts & FE_UNDERFLOW) {
112		d = 0x1p-1022;
113		d /= 0x1p1023;
114	}
115	if (excepts & FE_INEXACT) {
116		d = 0x1p-1022;
117		d += 1.0;
118	}
119
120	return (0);
121}
122DEF_STD(feraiseexcept);
123
124/*
125 * This function sets the floating-point status flags indicated by the argument
126 * `excepts' to the states stored in the object pointed to by `flagp'. It does
127 * NOT raise any floating-point exceptions, but only sets the state of the flags.
128 */
129int
130fesetexceptflag(const fexcept_t *flagp, int excepts)
131{
132	unsigned int fpscr;
133
134	excepts &= FE_ALL_EXCEPT;
135
136	/* Store the current floating-point status and control register */
137	__asm__ volatile ("sts fpscr, %0" : "=r" (fpscr));
138
139	/* Set the requested status flags */
140	fpscr &= ~excepts;
141	__fpscr_values[0] &= ~excepts;
142	__fpscr_values[1] &= ~excepts;
143
144	fpscr |= *flagp & excepts;
145	__fpscr_values[0] |= *flagp & excepts;
146	__fpscr_values[1] |= *flagp & excepts;
147
148	/* Load the floating-point status and control register */
149	__asm__ volatile ("lds %0, fpscr" : : "r" (fpscr));
150
151	return (0);
152}
153DEF_STD(fesetexceptflag);
154
155/*
156 * The fetestexcept() function determines which of a specified subset of the
157 * floating-point exception flags are currently set. The `excepts' argument
158 * specifies the floating-point status flags to be queried.
159 */
160int
161fetestexcept(int excepts)
162{
163	unsigned int fpscr;
164
165	excepts &= FE_ALL_EXCEPT;
166
167	/* Store the current floating-point status and control register */
168	__asm__ volatile ("sts fpscr, %0" : "=r" (fpscr));
169
170	return (fpscr & excepts);
171}
172DEF_STD(fetestexcept);
173
174/*
175 * The fegetround() function gets the current rounding direction.
176 */
177int
178fegetround(void)
179{
180	unsigned int fpscr;
181
182	/* Store the current floating-point status and control register */
183	__asm__ volatile ("sts fpscr, %0" : "=r" (fpscr));
184
185	return (fpscr & _ROUND_MASK);
186}
187DEF_STD(fegetround);
188
189/*
190 * The fesetround() function establishes the rounding direction represented by
191 * its argument `round'. If the argument is not equal to the value of a rounding
192 * direction macro, the rounding direction is not changed.
193 */
194int
195fesetround(int round)
196{
197	unsigned int fpscr;
198
199	/* Check whether requested rounding direction is supported */
200	if (round & ~_ROUND_MASK)
201		return (-1);
202
203	/* Store the current floating-point status and control register */
204	__asm__ volatile ("sts fpscr, %0" : "=r" (fpscr));
205
206	/* Set the rounding direction */
207	fpscr &= ~_ROUND_MASK;
208	__fpscr_values[0] &= ~_ROUND_MASK;
209	__fpscr_values[1] &= ~_ROUND_MASK;
210
211	fpscr |= round;
212	__fpscr_values[0] |= round;
213	__fpscr_values[1] |= round;
214
215	/* Load the floating-point status and control register */
216	__asm__ volatile ("lds %0, fpscr" : : "r" (fpscr));
217
218	return (0);
219}
220DEF_STD(fesetround);
221
222/*
223 * The fegetenv() function attempts to store the current floating-point
224 * environment in the object pointed to by envp.
225 */
226int
227fegetenv(fenv_t *envp)
228{
229	/* Store the current floating-point status and control register */
230	__asm__ volatile ("sts fpscr, %0" : "=r" (*envp));
231
232	return (0);
233}
234DEF_STD(fegetenv);
235
236/*
237 * The feholdexcept() function saves the current floating-point environment
238 * in the object pointed to by envp, clears the floating-point status flags, and
239 * then installs a non-stop (continue on floating-point exceptions) mode, if
240 * available, for all floating-point exceptions.
241 */
242int
243feholdexcept(fenv_t *envp)
244{
245	unsigned int fpscr;
246
247	/* Store the current floating-point status and control register */
248	__asm__ volatile ("sts fpscr, %0" : "=r" (fpscr));
249
250	*envp = fpscr;
251
252	/* Clear exception flags in FPSCR */
253	fpscr &= ~FE_ALL_EXCEPT;
254	__fpscr_values[0] &= ~FE_ALL_EXCEPT;
255	__fpscr_values[1] &= ~FE_ALL_EXCEPT;
256
257	/* Mask all exceptions */
258	fpscr &= ~(FE_ALL_EXCEPT << _MASK_SHIFT);
259	__fpscr_values[0] &= ~(FE_ALL_EXCEPT << _MASK_SHIFT);
260	__fpscr_values[1] &= ~(FE_ALL_EXCEPT << _MASK_SHIFT);
261
262	/* Load the floating-point status and control register */
263	__asm__ volatile ("lds %0, fpscr" : : "r" (fpscr));
264
265	return (0);
266}
267DEF_STD(feholdexcept);
268
269/*
270 * The fesetenv() function attempts to establish the floating-point environment
271 * represented by the object pointed to by envp. The argument `envp' points
272 * to an object set by a call to fegetenv() or feholdexcept(), or equal a
273 * floating-point environment macro. The fesetenv() function does not raise
274 * floating-point exceptions, but only installs the state of the floating-point
275 * status flags represented through its argument.
276 */
277int
278fesetenv(const fenv_t *envp)
279{
280	unsigned int fpscr;
281
282	/* Store the current floating-point status and control register */
283	__asm__ volatile ("sts fpscr, %0" : "=r" (fpscr));
284
285	/* Set the requested flags */
286	fpscr &= ~((FE_ALL_EXCEPT << _MASK_SHIFT) | _ROUND_MASK |
287	    FE_ALL_EXCEPT);
288	__fpscr_values[0] &= ~((FE_ALL_EXCEPT << _MASK_SHIFT) | _ROUND_MASK |
289	    FE_ALL_EXCEPT);
290	__fpscr_values[1] &= ~((FE_ALL_EXCEPT << _MASK_SHIFT) | _ROUND_MASK |
291	    FE_ALL_EXCEPT);
292
293	fpscr |= *envp & ((FE_ALL_EXCEPT << _MASK_SHIFT) | _ROUND_MASK |
294	    FE_ALL_EXCEPT);
295	__fpscr_values[0] |= *envp & ((FE_ALL_EXCEPT << _MASK_SHIFT) |
296	    _ROUND_MASK | FE_ALL_EXCEPT);
297	__fpscr_values[1] |= *envp & ((FE_ALL_EXCEPT << _MASK_SHIFT) |
298	    _ROUND_MASK | FE_ALL_EXCEPT);
299
300	/* Load the floating-point status and control register */
301	__asm__ volatile ("lds %0, fpscr" : : "r" (fpscr));
302
303	return (0);
304}
305DEF_STD(fesetenv);
306
307/*
308 * The feupdateenv() function saves the currently raised floating-point
309 * exceptions in its automatic storage, installs the floating-point environment
310 * represented by the object pointed to by `envp', and then raises the saved
311 * floating-point exceptions. The argument `envp' shall point to an object set
312 * by a call to feholdexcept() or fegetenv(), or equal a floating-point
313 * environment macro.
314 */
315int
316feupdateenv(const fenv_t *envp)
317{
318	unsigned int fpscr;
319
320	/* Store the current floating-point status and control register */
321	__asm__ volatile ("sts fpscr, %0" : "=r" (fpscr));
322
323	/* Install new floating-point environment */
324	fesetenv(envp);
325
326	/* Raise any previously accumulated exceptions */
327	feraiseexcept(fpscr);
328
329	return (0);
330}
331DEF_STD(feupdateenv);
332
333/*
334 * The following functions are extensions to the standard
335 */
336int
337feenableexcept(int mask)
338{
339	unsigned int fpscr, omask;
340
341	mask &= FE_ALL_EXCEPT;
342
343	/* Store the current floating-point status and control register */
344	__asm__ volatile ("sts fpscr, %0" : "=r" (fpscr));
345
346	omask = (fpscr >> _MASK_SHIFT) & FE_ALL_EXCEPT;
347	fpscr |= mask << _MASK_SHIFT;
348	__fpscr_values[0] |= mask << _MASK_SHIFT;
349	__fpscr_values[1] |= mask << _MASK_SHIFT;
350
351	/* Load the floating-point status and control register */
352	__asm__ volatile ("lds %0, fpscr" : : "r" (fpscr));
353
354	return (omask);
355
356}
357
358int
359fedisableexcept(int mask)
360{
361	unsigned int fpscr, omask;
362
363	mask &= FE_ALL_EXCEPT;
364
365	/* Store the current floating-point status and control register */
366	__asm__ volatile ("sts fpscr, %0" : "=r" (fpscr));
367
368	omask = (fpscr >> _MASK_SHIFT) & FE_ALL_EXCEPT;
369	fpscr &= ~(mask << _MASK_SHIFT);
370	__fpscr_values[0] &= ~(mask << _MASK_SHIFT);
371	__fpscr_values[1] &= ~(mask << _MASK_SHIFT);
372
373	/* Load the floating-point status and control register */
374	__asm__ volatile ("lds %0, fpscr" : : "r" (fpscr));
375
376	return (omask);
377}
378
379int
380fegetexcept(void)
381{
382	unsigned int fpscr;
383
384	/* Store the current floating-point status and control register */
385	__asm__ volatile ("sts fpscr, %0" : "=r" (fpscr));
386
387	return ((fpscr >> _MASK_SHIFT) & FE_ALL_EXCEPT);
388}
389