xref: /openbsd/lib/libm/arch/m88k/fenv.c (revision cca36db2)
1 /*	$OpenBSD: fenv.c,v 1.3 2011/04/29 21:37:40 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 /*
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  */
32 fenv_t __fe_dfl_env = {
33 	0x00000000,				/* Control register */
34 	0x00000000				/* Status register */
35 };
36 
37 /*
38  * The feclearexcept() function clears the supported floating-point exceptions
39  * represented by `excepts'.
40  */
41 int
42 feclearexcept(int excepts)
43 {
44 	unsigned int fpsr;
45 
46 	excepts &= FE_ALL_EXCEPT;
47 
48 	/* Store the current floating-point status register */
49 	__asm__ __volatile__ ("fldcr %0, fcr62" : "=r" (fpsr));
50 
51 	/* Clear the requested floating-point exceptions */
52 	fpsr &= ~excepts;
53 
54 	/* Load the floating-point status register */
55 	__asm__ __volatile__ ("fstcr %0, fcr62" : : "r" (fpsr));
56 
57 	return (0);
58 }
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  */
65 int
66 fegetexceptflag(fexcept_t *flagp, int excepts)
67 {
68 	unsigned int fpsr;
69 
70 	excepts &= FE_ALL_EXCEPT;
71 
72 	/* Store the current floating-point status register */
73 	__asm__ __volatile__ ("fldcr %0, fcr62" : "=r" (fpsr));
74 
75 	/* Store the results in flagp */
76 	*flagp = fpsr & 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  */
85 int
86 feraiseexcept(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 = 0x1p1023;
113 		d = 0x1p-1022 / d;
114 	}
115 	if (excepts & FE_INEXACT) {
116 		d = 0x1p-1022;
117 		d += 1.0;
118 	}
119 	return (0);
120 }
121 
122 /*
123  * This function sets the floating-point status flags indicated by the argument
124  * `excepts' to the states stored in the object pointed to by `flagp'. It does
125  * NOT raise any floating-point exceptions, but only sets the state of the flags.
126  */
127 int
128 fesetexceptflag(const fexcept_t *flagp, int excepts)
129 {
130 	unsigned int fpsr;
131 
132 	excepts &= FE_ALL_EXCEPT;
133 
134 	/* Store the current floating-point status register */
135 	__asm__ __volatile__ ("fldcr %0, fcr62" : "=r" (fpsr));
136 
137 	/* Set the requested status flags */
138 	fpsr &= ~excepts;
139 	fpsr |= *flagp & excepts;
140 
141 	/* Load the floating-point status register */
142 	__asm__ __volatile__ ("fstcr %0, fcr62" : : "r" (fpsr));
143 
144 	return (0);
145 }
146 
147 /*
148  * The fetestexcept() function determines which of a specified subset of the
149  * floating-point exception flags are currently set. The `excepts' argument
150  * specifies the floating-point status flags to be queried.
151  */
152 int
153 fetestexcept(int excepts)
154 {
155 	unsigned int fpsr;
156 
157 	excepts &= FE_ALL_EXCEPT;
158 
159 	/* Store the current floating-point status register */
160 	__asm__ __volatile__ ("fldcr %0, fcr62" : "=r" (fpsr));
161 
162 	return (fpsr & excepts);
163 }
164 
165 /*
166  * The fegetround() function gets the current rounding direction.
167  */
168 int
169 fegetround(void)
170 {
171 	unsigned int fpcr;
172 
173 	/* Store the current floating-point control register */
174 	__asm__ __volatile__ ("fldcr %0, fcr63" : "=r" (fpcr));
175 
176 	return (fpcr & _ROUND_MASK);
177 }
178 
179 /*
180  * The fesetround() function establishes the rounding direction represented by
181  * its argument `round'. If the argument is not equal to the value of a rounding
182  * direction macro, the rounding direction is not changed.
183  */
184 int
185 fesetround(int round)
186 {
187 	unsigned int fpcr;
188 
189 	/* Check whether requested rounding direction is supported */
190 	if (round & ~_ROUND_MASK)
191 		return (-1);
192 
193 	/* Store the current floating-point control register */
194 	__asm__ __volatile__ ("fldcr %0, fcr63" : "=r" (fpcr));
195 
196 	/* Set the rounding direction */
197 	fpcr &= ~_ROUND_MASK;
198 	fpcr |= round;
199 
200 	/* Load the floating-point control register */
201 	__asm__ __volatile__ ("fstcr %0, fcr63" : : "r" (fpcr));
202 
203 	return (0);
204 }
205 
206 /*
207  * The fegetenv() function attempts to store the current floating-point
208  * environment in the object pointed to by envp.
209  */
210 int
211 fegetenv(fenv_t *envp)
212 {
213 	/* Store the current floating-point control and status registers */
214 	__asm__ __volatile__ ("fldcr %0, fcr63" : "=r" (envp->__control));
215 	__asm__ __volatile__ ("fldcr %0, fcr62" : "=r" (envp->__status));
216 
217 	return (0);
218 }
219 
220 /*
221  * The feholdexcept() function saves the current floating-point environment
222  * in the object pointed to by envp, clears the floating-point status flags, and
223  * then installs a non-stop (continue on floating-point exceptions) mode, if
224  * available, for all floating-point exceptions.
225  */
226 int
227 feholdexcept(fenv_t *envp)
228 {
229 	unsigned int fpsr, fpcr;
230 
231 	/* Store the current floating-point control and status registers */
232 	__asm__ __volatile__ ("fldcr %0, fcr63" : "=r" (envp->__control));
233 	__asm__ __volatile__ ("fldcr %0, fcr62" : "=r" (envp->__status));
234 
235 	/* Clear exception flags in FPSR */
236 	fpsr = envp->__status;
237 	fpsr &= ~FE_ALL_EXCEPT;
238 	__asm__ __volatile__ ("fstcr %0, fcr62" : : "r" (fpsr));
239 
240 	/* Mask all exceptions */
241 	fpcr = envp->__control;
242 	fpcr &= ~FE_ALL_EXCEPT;
243 	__asm__ __volatile__ ("fstcr %0, fcr63" : : "r" (fpcr));
244 
245 	return (0);
246 }
247 
248 /*
249  * The fesetenv() function attempts to establish the floating-point environment
250  * represented by the object pointed to by envp. The argument `envp' points
251  * to an object set by a call to fegetenv() or feholdexcept(), or equal a
252  * floating-point environment macro. The fesetenv() function does not raise
253  * floating-point exceptions, but only installs the state of the floating-point
254  * status flags represented through its argument.
255  */
256 int
257 fesetenv(const fenv_t *envp)
258 {
259 	fenv_t fenv;
260 
261 	/* Store the current floating-point control and status registers */
262 	__asm__ __volatile__ ("fldcr %0, fcr63" : "=r" (fenv.__control));
263 	__asm__ __volatile__ ("fldcr %0, fcr62" : "=r" (fenv.__status));
264 
265 	/* Set the requested control flags */
266 	fenv.__control &= ~(FE_ALL_EXCEPT | _ROUND_MASK);
267 	fenv.__control |= envp->__control & (FE_ALL_EXCEPT | _ROUND_MASK);
268 
269 	/* Set the requested status flags */
270 	fenv.__status &= ~FE_ALL_EXCEPT;
271 	fenv.__status |= envp->__status & FE_ALL_EXCEPT;
272 
273 	/* Load the floating-point control and status registers */
274 	__asm__ __volatile__ ("fstcr %0, fcr63" : : "r" (fenv.__control));
275 	__asm__ __volatile__ ("fstcr %0, fcr62" : : "r" (fenv.__status));
276 
277 	return (0);
278 }
279 
280 /*
281  * The feupdateenv() function saves the currently raised floating-point
282  * exceptions in its automatic storage, installs the floating-point environment
283  * represented by the object pointed to by `envp', and then raises the saved
284  * floating-point exceptions. The argument `envp' shall point to an object set
285  * by a call to feholdexcept() or fegetenv(), or equal a floating-point
286  * environment macro.
287  */
288 int
289 feupdateenv(const fenv_t *envp)
290 {
291 	unsigned int fpsr;
292 
293 	/* Store the current floating-point status register */
294 	__asm__ __volatile__ ("fldcr %0, fcr62" : "=r" (fpsr));
295 
296 	/* Install new floating-point environment */
297 	fesetenv(envp);
298 
299 	/* Raise any previously accumulated exceptions */
300 	feraiseexcept(fpsr);
301 
302 	return (0);
303 }
304 
305 /*
306  * The following functions are extentions to the standard
307  */
308 int
309 feenableexcept(int mask)
310 {
311 	unsigned int fpcr, omask;
312 
313 	mask &= FE_ALL_EXCEPT;
314 
315 	/* Store the current floating-point control register */
316 	__asm__ __volatile__ ("fldcr %0, fcr63" : "=r" (fpcr));
317 
318 	omask = fpcr & FE_ALL_EXCEPT;
319 	fpcr |= mask;
320 
321 	/* Load the floating-point control register */
322 	__asm__ __volatile__ ("fstcr %0, fcr63" : : "r" (fpcr));
323 
324 	return (omask);
325 
326 }
327 
328 int
329 fedisableexcept(int mask)
330 {
331 	unsigned int fpcr, omask;
332 
333 	mask &= FE_ALL_EXCEPT;
334 
335 	/* Store the current floating-point control register */
336 	__asm__ __volatile__ ("fldcr %0, fcr63" : "=r" (fpcr));
337 
338 	omask = fpcr & FE_ALL_EXCEPT;
339 	fpcr &= ~mask;
340 
341 	/* Load the floating-point control register */
342 	__asm__ __volatile__ ("fstcr %0, fcr63" : : "r" (fpcr));
343 
344 	return (omask);
345 }
346 
347 int
348 fegetexcept(void)
349 {
350 	unsigned int fpcr;
351 
352 	/* Store the current floating-point control register */
353 	__asm__ __volatile__ ("fldcr %0, fcr63" : "=r" (fpcr));
354 
355 	return (fpcr & FE_ALL_EXCEPT);
356 }
357