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