xref: /openbsd/lib/libm/arch/alpha/fenv.c (revision 5af055cd)
1 /*	$OpenBSD: fenv.c,v 1.3 2014/04/18 15:09:52 guenther 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/types.h>
20 #include <machine/sysarch.h>
21 
22 #include <fenv.h>
23 
24 /*
25  * The following constant represents the default floating-point environment
26  * (that is, the one installed at program startup) and has type pointer to
27  * const-qualified fenv_t.
28  *
29  * It can be used as an argument to the functions within the <fenv.h> header
30  * that manage the floating-point environment, namely fesetenv() and
31  * feupdateenv().
32  */
33 fenv_t __fe_dfl_env = {
34 	0,
35 	0,
36 	FE_TONEAREST
37 };
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 	unsigned int fpsticky;
47 	struct alpha_fp_except_args a;
48 
49 	excepts &= FE_ALL_EXCEPT;
50 
51 	/* Store the current floating-point sticky flags */
52 	fpsticky = sysarch(ALPHA_FPGETSTICKY, 0L);
53 
54 	/* Clear the requested floating-point exceptions */
55 	fpsticky &= ~excepts;
56 
57 	/* Load the floating-point sticky flags */
58 	a.mask = fpsticky;
59 	sysarch(ALPHA_FPSETSTICKY, &a);
60 
61 	return (0);
62 }
63 
64 /*
65  * The fegetexceptflag() function stores an implementation-defined
66  * representation of the states of the floating-point status flags indicated by
67  * the argument excepts in the object pointed to by the argument flagp.
68  */
69 int
70 fegetexceptflag(fexcept_t *flagp, int excepts)
71 {
72 	unsigned int fpsticky;
73 
74 	excepts &= FE_ALL_EXCEPT;
75 
76 	/* Store the current floating-point sticky flags */
77 	fpsticky = sysarch(ALPHA_FPGETSTICKY, 0L);
78 
79 	/* Store the results in flagp */
80 	*flagp = fpsticky & excepts;
81 
82 	return (0);
83 }
84 
85 /*
86  * The feraiseexcept() function raises the supported floating-point exceptions
87  * represented by the argument `excepts'.
88  */
89 int
90 feraiseexcept(int excepts)
91 {
92 	excepts &= FE_ALL_EXCEPT;
93 
94 	fesetexceptflag((fexcept_t *)&excepts, excepts);
95 
96 	return (0);
97 }
98 
99 /*
100  * This function sets the floating-point status flags indicated by the argument
101  * `excepts' to the states stored in the object pointed to by `flagp'. It does
102  * NOT raise any floating-point exceptions, but only sets the state of the flags.
103  */
104 int
105 fesetexceptflag(const fexcept_t *flagp, int excepts)
106 {
107 	unsigned int fpsticky;
108 	struct alpha_fp_except_args a;
109 
110 	excepts &= FE_ALL_EXCEPT;
111 
112 	/* Store the current floating-point sticky flags */
113 	fpsticky = sysarch(ALPHA_FPGETSTICKY, 0L);
114 
115 	/* Set the requested status flags */
116 	fpsticky &= ~excepts;
117 	fpsticky |= *flagp & excepts;
118 
119 	/* Load the floating-point sticky flags */
120 	a.mask = fpsticky;
121 	sysarch(ALPHA_FPSETSTICKY, &a);
122 
123 	return (0);
124 }
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  */
131 int
132 fetestexcept(int excepts)
133 {
134 	unsigned int fpsticky;
135 
136 	excepts &= FE_ALL_EXCEPT;
137 
138 	/* Store the current floating-point sticky flags */
139 	fpsticky = sysarch(ALPHA_FPGETSTICKY, 0L);
140 
141 	return (fpsticky & excepts);
142 }
143 
144 /*
145  * The fegetround() function gets the current rounding direction.
146  */
147 int
148 fegetround(void)
149 {
150 	unsigned long fpcr;
151 
152 	/* Store the current floating-point control register */
153 	__asm__ volatile ("trapb");
154 	__asm__ volatile ("mf_fpcr %0" : "=f" (fpcr));
155 	__asm__ volatile ("trapb");
156 
157 	return ((fpcr >> _ROUND_SHIFT) & _ROUND_MASK);
158 }
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  */
165 int
166 fesetround(int round)
167 {
168 	unsigned long fpcr;
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 register */
175 	__asm__ volatile ("trapb");
176 	__asm__ volatile ("mf_fpcr %0" : "=f" (fpcr));
177 	__asm__ volatile ("trapb");
178 
179 	/* Set the rounding direction */
180 	fpcr &= ~((unsigned long)_ROUND_MASK << _ROUND_SHIFT);
181 	fpcr |= (unsigned long)round << _ROUND_SHIFT;
182 
183 	/* Load the floating-point control register */
184 	__asm__ volatile ("trapb");
185 	__asm__ volatile ("mt_fpcr %0" : : "f" (fpcr));
186 	__asm__ volatile ("trapb");
187 
188 	return (0);
189 }
190 
191 /*
192  * The fegetenv() function attempts to store the current floating-point
193  * environment in the object pointed to by envp.
194  */
195 int
196 fegetenv(fenv_t *envp)
197 {
198 	unsigned long fpcr;
199 
200 	/* Store the current floating-point sticky flags */
201 	envp->__sticky = sysarch(ALPHA_FPGETSTICKY, 0L) & FE_ALL_EXCEPT;
202 
203 	/* Store the current floating-point masks */
204 	envp->__mask = sysarch(ALPHA_FPGETMASK, 0L) & FE_ALL_EXCEPT;
205 
206 	/* Store the current floating-point control register */
207 	__asm__ volatile ("trapb");
208 	__asm__ volatile ("mf_fpcr %0" : "=f" (fpcr));
209 	__asm__ volatile ("trapb");
210 	envp->__round = (fpcr >> _ROUND_SHIFT) & _ROUND_MASK;
211 
212 	return (0);
213 }
214 
215 /*
216  * The feholdexcept() function saves the current floating-point environment
217  * in the object pointed to by envp, clears the floating-point status flags, and
218  * then installs a non-stop (continue on floating-point exceptions) mode, if
219  * available, for all floating-point exceptions.
220  */
221 int
222 feholdexcept(fenv_t *envp)
223 {
224 	struct alpha_fp_except_args a;
225 
226 	/* Store the current floating-point environment */
227 	fegetenv(envp);
228 
229 	/* Clear exception flags */
230 	a.mask = 0;
231 	sysarch(ALPHA_FPSETSTICKY, &a);
232 
233 	/* Mask all exceptions */
234 	a.mask = 0;
235 	sysarch(ALPHA_FPSETMASK, &a);
236 
237 	return (0);
238 }
239 
240 /*
241  * The fesetenv() function attempts to establish the floating-point environment
242  * represented by the object pointed to by envp. The argument `envp' points
243  * to an object set by a call to fegetenv() or feholdexcept(), or equal a
244  * floating-point environment macro. The fesetenv() function does not raise
245  * floating-point exceptions, but only installs the state of the floating-point
246  * status flags represented through its argument.
247  */
248 int
249 fesetenv(const fenv_t *envp)
250 {
251 	unsigned long fpcr;
252 	struct alpha_fp_except_args a;
253 
254 	/* Load the floating-point sticky flags */
255 	a.mask = envp->__sticky & FE_ALL_EXCEPT;
256 	sysarch(ALPHA_FPSETSTICKY, &a);
257 
258 	/* Load the floating-point masks */
259 	a.mask = envp->__mask & FE_ALL_EXCEPT;
260 	sysarch(ALPHA_FPSETMASK, &a);
261 
262 	/* Store the current floating-point control register */
263 	__asm__ volatile ("trapb");
264 	__asm__ volatile ("mf_fpcr %0" : "=f" (fpcr));
265 	__asm__ volatile ("trapb");
266 
267 	/* Set the requested flags */
268 	fpcr &= ~((unsigned long)_ROUND_MASK << _ROUND_SHIFT);
269 	fpcr |= ((unsigned long)envp->__round & _ROUND_MASK) << _ROUND_SHIFT;
270 
271 	/* Load the floating-point control register */
272 	__asm__ volatile ("trapb");
273 	__asm__ volatile ("mt_fpcr %0" : : "f" (fpcr));
274 	__asm__ volatile ("trapb");
275 
276 	return (0);
277 }
278 
279 /*
280  * The feupdateenv() function saves the currently raised floating-point
281  * exceptions in its automatic storage, installs the floating-point environment
282  * represented by the object pointed to by `envp', and then raises the saved
283  * floating-point exceptions. The argument `envp' shall point to an object set
284  * by a call to feholdexcept() or fegetenv(), or equal a floating-point
285  * environment macro.
286  */
287 int
288 feupdateenv(const fenv_t *envp)
289 {
290 	unsigned int fpsticky;
291 
292 	/* Store the current floating-point sticky flags */
293 	fpsticky = sysarch(ALPHA_FPGETSTICKY, 0L);
294 
295 	/* Install new floating-point environment */
296 	fesetenv(envp);
297 
298 	/* Raise any previously accumulated exceptions */
299 	feraiseexcept(fpsticky);
300 
301 	return (0);
302 }
303 
304 /*
305  * The following functions are extentions to the standard
306  */
307 int
308 feenableexcept(int mask)
309 {
310 	unsigned int fpmask, omask;
311 	struct alpha_fp_except_args a;
312 
313 	mask &= FE_ALL_EXCEPT;
314 
315 	/* Store the current floating-point masks */
316 	fpmask = sysarch(ALPHA_FPGETMASK, 0L);
317 
318 	omask = fpmask & FE_ALL_EXCEPT;
319 	fpmask |= mask;
320 
321 	/* Load the floating-point masks */
322 	a.mask = fpmask;
323 	sysarch(ALPHA_FPSETMASK, &a);
324 
325 	return (omask);
326 
327 }
328 
329 int
330 fedisableexcept(int mask)
331 {
332 	unsigned int fpmask, omask;
333 	struct alpha_fp_except_args a;
334 
335 	mask &= FE_ALL_EXCEPT;
336 
337 	/* Store the current floating-point masks */
338 	fpmask = sysarch(ALPHA_FPGETMASK, 0L);
339 
340 	omask = fpmask & FE_ALL_EXCEPT;
341 	fpmask &= ~mask;
342 
343 	/* Load the floating-point masks */
344 	a.mask = fpmask;
345 	sysarch(ALPHA_FPSETMASK, &a);
346 
347 	return (omask);
348 }
349 
350 int
351 fegetexcept(void)
352 {
353 	unsigned int fpmask;
354 
355 	/* Store the current floating-point masks */
356 	fpmask = sysarch(ALPHA_FPGETMASK, 0L);
357 
358 	return (fpmask & FE_ALL_EXCEPT);
359 }
360