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