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 */
30fenv_t __fe_dfl_env = 0;
31
32/*
33 * The feclearexcept() function clears the supported floating-point exceptions
34 * represented by `excepts'.
35 */
36int
37feclearexcept(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}
54DEF_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 */
61int
62fegetexceptflag(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 */
81int
82feraiseexcept(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}
98DEF_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 */
105int
106fesetexceptflag(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}
124DEF_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 */
131int
132fetestexcept(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}
143DEF_STD(fetestexcept);
144
145/*
146 * The fegetround() function gets the current rounding direction.
147 */
148int
149fegetround(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}
158DEF_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 */
165int
166fesetround(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}
186DEF_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 */
192int
193fegetenv(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}
200DEF_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 */
208int
209feholdexcept(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}
226DEF_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 */
236int
237fesetenv(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}
244DEF_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 */
254int
255feupdateenv(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}
270DEF_STD(feupdateenv);
271
272/*
273 * The following functions are extensions to the standard
274 */
275int
276feenableexcept(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
295int
296fedisableexcept(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
314int
315fegetexcept(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