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