xref: /openbsd-src/lib/libm/arch/powerpc/fenv.c (revision 4c1e55dc91edd6e69ccc60ce855900fbc12cf34f)
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 
23 union 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  */
37 fenv_t __fe_dfl_env = 0;
38 
39 /*
40  * The feclearexcept() function clears the supported floating-point exceptions
41  * represented by `excepts'.
42  */
43 int
44 feclearexcept(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  */
68 int
69 fegetexceptflag(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  */
88 int
89 feraiseexcept(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  */
103 int
104 fesetexceptflag(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  */
134 int
135 fetestexcept(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  */
150 int
151 fegetround(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  */
166 int
167 fesetround(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  */
192 int
193 fegetenv(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  */
211 int
212 feholdexcept(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  */
239 int
240 fesetenv(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  */
261 int
262 feupdateenv(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  */
281 int
282 feenableexcept(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 
302 int
303 fedisableexcept(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 
322 int
323 fegetexcept(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