1*2c53affbSjmc /* $OpenBSD: fenv.c,v 1.5 2022/12/27 17:10:07 jmc Exp $ */
2499accfeSmartynas
3499accfeSmartynas /*
4499accfeSmartynas * Copyright (c) 2011 Martynas Venckus <martynas@openbsd.org>
5499accfeSmartynas *
6499accfeSmartynas * Permission to use, copy, modify, and distribute this software for any
7499accfeSmartynas * purpose with or without fee is hereby granted, provided that the above
8499accfeSmartynas * copyright notice and this permission notice appear in all copies.
9499accfeSmartynas *
10499accfeSmartynas * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11499accfeSmartynas * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12499accfeSmartynas * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13499accfeSmartynas * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14499accfeSmartynas * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15499accfeSmartynas * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16499accfeSmartynas * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17499accfeSmartynas */
18499accfeSmartynas
19499accfeSmartynas #include <sys/types.h>
20499accfeSmartynas #include <machine/sysarch.h>
21499accfeSmartynas
22499accfeSmartynas #include <fenv.h>
23499accfeSmartynas
24499accfeSmartynas /*
252f2c0062Sguenther * Call sysarch(2) via its alias in the reserved namespace:
262f2c0062Sguenther * _thread_sys_sysarch()
272f2c0062Sguenther */
282f2c0062Sguenther typeof(sysarch) sysarch asm("_thread_sys_sysarch");
292f2c0062Sguenther
302f2c0062Sguenther
312f2c0062Sguenther /*
32499accfeSmartynas * The following constant represents the default floating-point environment
33499accfeSmartynas * (that is, the one installed at program startup) and has type pointer to
34499accfeSmartynas * const-qualified fenv_t.
35499accfeSmartynas *
36499accfeSmartynas * It can be used as an argument to the functions within the <fenv.h> header
37499accfeSmartynas * that manage the floating-point environment, namely fesetenv() and
38499accfeSmartynas * feupdateenv().
39499accfeSmartynas */
40499accfeSmartynas fenv_t __fe_dfl_env = {
41499accfeSmartynas 0,
42499accfeSmartynas 0,
43499accfeSmartynas FE_TONEAREST
44499accfeSmartynas };
45499accfeSmartynas
46499accfeSmartynas /*
47499accfeSmartynas * The feclearexcept() function clears the supported floating-point exceptions
48499accfeSmartynas * represented by `excepts'.
49499accfeSmartynas */
50499accfeSmartynas int
feclearexcept(int excepts)51499accfeSmartynas feclearexcept(int excepts)
52499accfeSmartynas {
53499accfeSmartynas unsigned int fpsticky;
54499accfeSmartynas struct alpha_fp_except_args a;
55499accfeSmartynas
56499accfeSmartynas excepts &= FE_ALL_EXCEPT;
57499accfeSmartynas
58499accfeSmartynas /* Store the current floating-point sticky flags */
59499accfeSmartynas fpsticky = sysarch(ALPHA_FPGETSTICKY, 0L);
60499accfeSmartynas
61499accfeSmartynas /* Clear the requested floating-point exceptions */
62499accfeSmartynas fpsticky &= ~excepts;
63499accfeSmartynas
64499accfeSmartynas /* Load the floating-point sticky flags */
65499accfeSmartynas a.mask = fpsticky;
66499accfeSmartynas sysarch(ALPHA_FPSETSTICKY, &a);
67499accfeSmartynas
68499accfeSmartynas return (0);
69499accfeSmartynas }
702f2c0062Sguenther DEF_STD(feclearexcept);
71499accfeSmartynas
72499accfeSmartynas /*
73499accfeSmartynas * The fegetexceptflag() function stores an implementation-defined
74499accfeSmartynas * representation of the states of the floating-point status flags indicated by
75499accfeSmartynas * the argument excepts in the object pointed to by the argument flagp.
76499accfeSmartynas */
77499accfeSmartynas int
fegetexceptflag(fexcept_t * flagp,int excepts)78499accfeSmartynas fegetexceptflag(fexcept_t *flagp, int excepts)
79499accfeSmartynas {
80499accfeSmartynas unsigned int fpsticky;
81499accfeSmartynas
82499accfeSmartynas excepts &= FE_ALL_EXCEPT;
83499accfeSmartynas
84499accfeSmartynas /* Store the current floating-point sticky flags */
85499accfeSmartynas fpsticky = sysarch(ALPHA_FPGETSTICKY, 0L);
86499accfeSmartynas
87499accfeSmartynas /* Store the results in flagp */
88499accfeSmartynas *flagp = fpsticky & excepts;
89499accfeSmartynas
90499accfeSmartynas return (0);
91499accfeSmartynas }
92499accfeSmartynas
93499accfeSmartynas /*
94499accfeSmartynas * The feraiseexcept() function raises the supported floating-point exceptions
95499accfeSmartynas * represented by the argument `excepts'.
96499accfeSmartynas */
97499accfeSmartynas int
feraiseexcept(int excepts)98499accfeSmartynas feraiseexcept(int excepts)
99499accfeSmartynas {
100499accfeSmartynas excepts &= FE_ALL_EXCEPT;
101499accfeSmartynas
102499accfeSmartynas fesetexceptflag((fexcept_t *)&excepts, excepts);
103499accfeSmartynas
104499accfeSmartynas return (0);
105499accfeSmartynas }
1062f2c0062Sguenther DEF_STD(feraiseexcept);
107499accfeSmartynas
108499accfeSmartynas /*
109499accfeSmartynas * This function sets the floating-point status flags indicated by the argument
110499accfeSmartynas * `excepts' to the states stored in the object pointed to by `flagp'. It does
111499accfeSmartynas * NOT raise any floating-point exceptions, but only sets the state of the flags.
112499accfeSmartynas */
113499accfeSmartynas int
fesetexceptflag(const fexcept_t * flagp,int excepts)114499accfeSmartynas fesetexceptflag(const fexcept_t *flagp, int excepts)
115499accfeSmartynas {
116499accfeSmartynas unsigned int fpsticky;
117499accfeSmartynas struct alpha_fp_except_args a;
118499accfeSmartynas
119499accfeSmartynas excepts &= FE_ALL_EXCEPT;
120499accfeSmartynas
121499accfeSmartynas /* Store the current floating-point sticky flags */
122499accfeSmartynas fpsticky = sysarch(ALPHA_FPGETSTICKY, 0L);
123499accfeSmartynas
124499accfeSmartynas /* Set the requested status flags */
125499accfeSmartynas fpsticky &= ~excepts;
126499accfeSmartynas fpsticky |= *flagp & excepts;
127499accfeSmartynas
128499accfeSmartynas /* Load the floating-point sticky flags */
129499accfeSmartynas a.mask = fpsticky;
130499accfeSmartynas sysarch(ALPHA_FPSETSTICKY, &a);
131499accfeSmartynas
132499accfeSmartynas return (0);
133499accfeSmartynas }
1342f2c0062Sguenther DEF_STD(fesetexceptflag);
135499accfeSmartynas
136499accfeSmartynas /*
137499accfeSmartynas * The fetestexcept() function determines which of a specified subset of the
138499accfeSmartynas * floating-point exception flags are currently set. The `excepts' argument
139499accfeSmartynas * specifies the floating-point status flags to be queried.
140499accfeSmartynas */
141499accfeSmartynas int
fetestexcept(int excepts)142499accfeSmartynas fetestexcept(int excepts)
143499accfeSmartynas {
144499accfeSmartynas unsigned int fpsticky;
145499accfeSmartynas
146499accfeSmartynas excepts &= FE_ALL_EXCEPT;
147499accfeSmartynas
148499accfeSmartynas /* Store the current floating-point sticky flags */
149499accfeSmartynas fpsticky = sysarch(ALPHA_FPGETSTICKY, 0L);
150499accfeSmartynas
151499accfeSmartynas return (fpsticky & excepts);
152499accfeSmartynas }
1532f2c0062Sguenther DEF_STD(fetestexcept);
154499accfeSmartynas
155499accfeSmartynas /*
156499accfeSmartynas * The fegetround() function gets the current rounding direction.
157499accfeSmartynas */
158499accfeSmartynas int
fegetround(void)159499accfeSmartynas fegetround(void)
160499accfeSmartynas {
161499accfeSmartynas unsigned long fpcr;
162499accfeSmartynas
163499accfeSmartynas /* Store the current floating-point control register */
164b5aa3b33Sguenther __asm__ volatile ("trapb");
165b5aa3b33Sguenther __asm__ volatile ("mf_fpcr %0" : "=f" (fpcr));
166b5aa3b33Sguenther __asm__ volatile ("trapb");
167499accfeSmartynas
168499accfeSmartynas return ((fpcr >> _ROUND_SHIFT) & _ROUND_MASK);
169499accfeSmartynas }
1702f2c0062Sguenther DEF_STD(fegetround);
171499accfeSmartynas
172499accfeSmartynas /*
173499accfeSmartynas * The fesetround() function establishes the rounding direction represented by
174499accfeSmartynas * its argument `round'. If the argument is not equal to the value of a rounding
175499accfeSmartynas * direction macro, the rounding direction is not changed.
176499accfeSmartynas */
177499accfeSmartynas int
fesetround(int round)178499accfeSmartynas fesetround(int round)
179499accfeSmartynas {
180499accfeSmartynas unsigned long fpcr;
181499accfeSmartynas
182499accfeSmartynas /* Check whether requested rounding direction is supported */
183499accfeSmartynas if (round & ~_ROUND_MASK)
184499accfeSmartynas return (-1);
185499accfeSmartynas
186499accfeSmartynas /* Store the current floating-point control register */
187b5aa3b33Sguenther __asm__ volatile ("trapb");
188b5aa3b33Sguenther __asm__ volatile ("mf_fpcr %0" : "=f" (fpcr));
189b5aa3b33Sguenther __asm__ volatile ("trapb");
190499accfeSmartynas
191d6f349c8Smartynas /* Set the rounding direction */
192499accfeSmartynas fpcr &= ~((unsigned long)_ROUND_MASK << _ROUND_SHIFT);
193499accfeSmartynas fpcr |= (unsigned long)round << _ROUND_SHIFT;
194499accfeSmartynas
195499accfeSmartynas /* Load the floating-point control register */
196b5aa3b33Sguenther __asm__ volatile ("trapb");
197b5aa3b33Sguenther __asm__ volatile ("mt_fpcr %0" : : "f" (fpcr));
198b5aa3b33Sguenther __asm__ volatile ("trapb");
199499accfeSmartynas
200499accfeSmartynas return (0);
201499accfeSmartynas }
2022f2c0062Sguenther DEF_STD(fesetround);
203499accfeSmartynas
204499accfeSmartynas /*
205499accfeSmartynas * The fegetenv() function attempts to store the current floating-point
206499accfeSmartynas * environment in the object pointed to by envp.
207499accfeSmartynas */
208499accfeSmartynas int
fegetenv(fenv_t * envp)209499accfeSmartynas fegetenv(fenv_t *envp)
210499accfeSmartynas {
211499accfeSmartynas unsigned long fpcr;
212499accfeSmartynas
213499accfeSmartynas /* Store the current floating-point sticky flags */
214d6f349c8Smartynas envp->__sticky = sysarch(ALPHA_FPGETSTICKY, 0L) & FE_ALL_EXCEPT;
215499accfeSmartynas
216499accfeSmartynas /* Store the current floating-point masks */
217d6f349c8Smartynas envp->__mask = sysarch(ALPHA_FPGETMASK, 0L) & FE_ALL_EXCEPT;
218499accfeSmartynas
219499accfeSmartynas /* Store the current floating-point control register */
220b5aa3b33Sguenther __asm__ volatile ("trapb");
221b5aa3b33Sguenther __asm__ volatile ("mf_fpcr %0" : "=f" (fpcr));
222b5aa3b33Sguenther __asm__ volatile ("trapb");
223499accfeSmartynas envp->__round = (fpcr >> _ROUND_SHIFT) & _ROUND_MASK;
224499accfeSmartynas
225499accfeSmartynas return (0);
226499accfeSmartynas }
2272f2c0062Sguenther DEF_STD(fegetenv);
228499accfeSmartynas
229499accfeSmartynas /*
230499accfeSmartynas * The feholdexcept() function saves the current floating-point environment
231499accfeSmartynas * in the object pointed to by envp, clears the floating-point status flags, and
232499accfeSmartynas * then installs a non-stop (continue on floating-point exceptions) mode, if
233499accfeSmartynas * available, for all floating-point exceptions.
234499accfeSmartynas */
235499accfeSmartynas int
feholdexcept(fenv_t * envp)236499accfeSmartynas feholdexcept(fenv_t *envp)
237499accfeSmartynas {
238499accfeSmartynas struct alpha_fp_except_args a;
239499accfeSmartynas
240499accfeSmartynas /* Store the current floating-point environment */
241499accfeSmartynas fegetenv(envp);
242499accfeSmartynas
243499accfeSmartynas /* Clear exception flags */
244d6f349c8Smartynas a.mask = 0;
245499accfeSmartynas sysarch(ALPHA_FPSETSTICKY, &a);
246499accfeSmartynas
247499accfeSmartynas /* Mask all exceptions */
248d6f349c8Smartynas a.mask = 0;
249499accfeSmartynas sysarch(ALPHA_FPSETMASK, &a);
250499accfeSmartynas
251499accfeSmartynas return (0);
252499accfeSmartynas }
2532f2c0062Sguenther DEF_STD(feholdexcept);
254499accfeSmartynas
255499accfeSmartynas /*
256499accfeSmartynas * The fesetenv() function attempts to establish the floating-point environment
257499accfeSmartynas * represented by the object pointed to by envp. The argument `envp' points
258499accfeSmartynas * to an object set by a call to fegetenv() or feholdexcept(), or equal a
259499accfeSmartynas * floating-point environment macro. The fesetenv() function does not raise
260499accfeSmartynas * floating-point exceptions, but only installs the state of the floating-point
261499accfeSmartynas * status flags represented through its argument.
262499accfeSmartynas */
263499accfeSmartynas int
fesetenv(const fenv_t * envp)264499accfeSmartynas fesetenv(const fenv_t *envp)
265499accfeSmartynas {
266499accfeSmartynas unsigned long fpcr;
267499accfeSmartynas struct alpha_fp_except_args a;
268499accfeSmartynas
269499accfeSmartynas /* Load the floating-point sticky flags */
270d6f349c8Smartynas a.mask = envp->__sticky & FE_ALL_EXCEPT;
271499accfeSmartynas sysarch(ALPHA_FPSETSTICKY, &a);
272499accfeSmartynas
273499accfeSmartynas /* Load the floating-point masks */
274499accfeSmartynas a.mask = envp->__mask & FE_ALL_EXCEPT;
275499accfeSmartynas sysarch(ALPHA_FPSETMASK, &a);
276499accfeSmartynas
277499accfeSmartynas /* Store the current floating-point control register */
278b5aa3b33Sguenther __asm__ volatile ("trapb");
279b5aa3b33Sguenther __asm__ volatile ("mf_fpcr %0" : "=f" (fpcr));
280b5aa3b33Sguenther __asm__ volatile ("trapb");
281499accfeSmartynas
282d6f349c8Smartynas /* Set the requested flags */
283499accfeSmartynas fpcr &= ~((unsigned long)_ROUND_MASK << _ROUND_SHIFT);
284499accfeSmartynas fpcr |= ((unsigned long)envp->__round & _ROUND_MASK) << _ROUND_SHIFT;
285499accfeSmartynas
286499accfeSmartynas /* Load the floating-point control register */
287b5aa3b33Sguenther __asm__ volatile ("trapb");
288b5aa3b33Sguenther __asm__ volatile ("mt_fpcr %0" : : "f" (fpcr));
289b5aa3b33Sguenther __asm__ volatile ("trapb");
290499accfeSmartynas
291499accfeSmartynas return (0);
292499accfeSmartynas }
2932f2c0062Sguenther DEF_STD(fesetenv);
294499accfeSmartynas
295499accfeSmartynas /*
296499accfeSmartynas * The feupdateenv() function saves the currently raised floating-point
297499accfeSmartynas * exceptions in its automatic storage, installs the floating-point environment
298499accfeSmartynas * represented by the object pointed to by `envp', and then raises the saved
299499accfeSmartynas * floating-point exceptions. The argument `envp' shall point to an object set
300499accfeSmartynas * by a call to feholdexcept() or fegetenv(), or equal a floating-point
301499accfeSmartynas * environment macro.
302499accfeSmartynas */
303499accfeSmartynas int
feupdateenv(const fenv_t * envp)304499accfeSmartynas feupdateenv(const fenv_t *envp)
305499accfeSmartynas {
306499accfeSmartynas unsigned int fpsticky;
307499accfeSmartynas
308499accfeSmartynas /* Store the current floating-point sticky flags */
309499accfeSmartynas fpsticky = sysarch(ALPHA_FPGETSTICKY, 0L);
310499accfeSmartynas
311499accfeSmartynas /* Install new floating-point environment */
312499accfeSmartynas fesetenv(envp);
313499accfeSmartynas
314499accfeSmartynas /* Raise any previously accumulated exceptions */
315499accfeSmartynas feraiseexcept(fpsticky);
316499accfeSmartynas
317499accfeSmartynas return (0);
318499accfeSmartynas }
3192f2c0062Sguenther DEF_STD(feupdateenv);
320499accfeSmartynas
321499accfeSmartynas /*
322*2c53affbSjmc * The following functions are extensions to the standard
323499accfeSmartynas */
324499accfeSmartynas int
feenableexcept(int mask)325499accfeSmartynas feenableexcept(int mask)
326499accfeSmartynas {
327499accfeSmartynas unsigned int fpmask, omask;
328499accfeSmartynas struct alpha_fp_except_args a;
329499accfeSmartynas
330499accfeSmartynas mask &= FE_ALL_EXCEPT;
331499accfeSmartynas
332499accfeSmartynas /* Store the current floating-point masks */
333499accfeSmartynas fpmask = sysarch(ALPHA_FPGETMASK, 0L);
334499accfeSmartynas
335499accfeSmartynas omask = fpmask & FE_ALL_EXCEPT;
336499accfeSmartynas fpmask |= mask;
337499accfeSmartynas
338499accfeSmartynas /* Load the floating-point masks */
339499accfeSmartynas a.mask = fpmask;
340499accfeSmartynas sysarch(ALPHA_FPSETMASK, &a);
341499accfeSmartynas
342499accfeSmartynas return (omask);
343499accfeSmartynas
344499accfeSmartynas }
345499accfeSmartynas
346499accfeSmartynas int
fedisableexcept(int mask)347499accfeSmartynas fedisableexcept(int mask)
348499accfeSmartynas {
349499accfeSmartynas unsigned int fpmask, omask;
350499accfeSmartynas struct alpha_fp_except_args a;
351499accfeSmartynas
352499accfeSmartynas mask &= FE_ALL_EXCEPT;
353499accfeSmartynas
354499accfeSmartynas /* Store the current floating-point masks */
355499accfeSmartynas fpmask = sysarch(ALPHA_FPGETMASK, 0L);
356499accfeSmartynas
357499accfeSmartynas omask = fpmask & FE_ALL_EXCEPT;
358499accfeSmartynas fpmask &= ~mask;
359499accfeSmartynas
360499accfeSmartynas /* Load the floating-point masks */
361499accfeSmartynas a.mask = fpmask;
362499accfeSmartynas sysarch(ALPHA_FPSETMASK, &a);
363499accfeSmartynas
364499accfeSmartynas return (omask);
365499accfeSmartynas }
366499accfeSmartynas
367499accfeSmartynas int
fegetexcept(void)368499accfeSmartynas fegetexcept(void)
369499accfeSmartynas {
370499accfeSmartynas unsigned int fpmask;
371499accfeSmartynas
372499accfeSmartynas /* Store the current floating-point masks */
373499accfeSmartynas fpmask = sysarch(ALPHA_FPGETMASK, 0L);
374499accfeSmartynas
375499accfeSmartynas return (fpmask & FE_ALL_EXCEPT);
376499accfeSmartynas }
377