1*2c53affbSjmc /* $OpenBSD: fenv.c,v 1.2 2022/12/27 17:10:07 jmc Exp $ */
2c931458eSkettenis
3c931458eSkettenis /*
4c931458eSkettenis * Copyright (c) 2011 Martynas Venckus <martynas@openbsd.org>
5c931458eSkettenis *
6c931458eSkettenis * Permission to use, copy, modify, and distribute this software for any
7c931458eSkettenis * purpose with or without fee is hereby granted, provided that the above
8c931458eSkettenis * copyright notice and this permission notice appear in all copies.
9c931458eSkettenis *
10c931458eSkettenis * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11c931458eSkettenis * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12c931458eSkettenis * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13c931458eSkettenis * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14c931458eSkettenis * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15c931458eSkettenis * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16c931458eSkettenis * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17c931458eSkettenis */
18c931458eSkettenis
19c931458eSkettenis #include <fenv.h>
20c931458eSkettenis
21c931458eSkettenis union u {
22c931458eSkettenis unsigned long long fpscr;
23c931458eSkettenis unsigned int bits[2];
24c931458eSkettenis };
25c931458eSkettenis
26c931458eSkettenis /*
27c931458eSkettenis * The following constant represents the default floating-point environment
28c931458eSkettenis * (that is, the one installed at program startup) and has type pointer to
29c931458eSkettenis * const-qualified fenv_t.
30c931458eSkettenis *
31c931458eSkettenis * It can be used as an argument to the functions within the <fenv.h> header
32c931458eSkettenis * that manage the floating-point environment, namely fesetenv() and
33c931458eSkettenis * feupdateenv().
34c931458eSkettenis */
35c931458eSkettenis fenv_t __fe_dfl_env = 0;
36c931458eSkettenis
37c931458eSkettenis /*
38c931458eSkettenis * The feclearexcept() function clears the supported floating-point exceptions
39c931458eSkettenis * represented by `excepts'.
40c931458eSkettenis */
41c931458eSkettenis int
feclearexcept(int excepts)42c931458eSkettenis feclearexcept(int excepts)
43c931458eSkettenis {
44c931458eSkettenis union u u;
45c931458eSkettenis excepts &= FE_ALL_EXCEPT;
46c931458eSkettenis
47c931458eSkettenis /* Store the current floating-point status and control register */
48c931458eSkettenis __asm__ volatile ("mffs %0" : "=f" (u.fpscr));
49c931458eSkettenis
50c931458eSkettenis /* Clear the requested floating-point exceptions */
51c931458eSkettenis u.bits[1] &= ~excepts;
52c931458eSkettenis if (excepts & FE_INVALID)
53c931458eSkettenis u.bits[1] &= ~_FE_INVALID_ALL;
54c931458eSkettenis
55c931458eSkettenis /* Load the floating-point status and control register */
56c931458eSkettenis __asm__ volatile ("mtfsf 0xff,%0" :: "f" (u.fpscr));
57c931458eSkettenis
58c931458eSkettenis return (0);
59c931458eSkettenis }
60c931458eSkettenis DEF_STD(feclearexcept);
61c931458eSkettenis
62c931458eSkettenis /*
63c931458eSkettenis * The fegetexceptflag() function stores an implementation-defined
64c931458eSkettenis * representation of the states of the floating-point status flags indicated by
65c931458eSkettenis * the argument excepts in the object pointed to by the argument flagp.
66c931458eSkettenis */
67c931458eSkettenis int
fegetexceptflag(fexcept_t * flagp,int excepts)68c931458eSkettenis fegetexceptflag(fexcept_t *flagp, int excepts)
69c931458eSkettenis {
70c931458eSkettenis union u u;
71c931458eSkettenis
72c931458eSkettenis excepts &= FE_ALL_EXCEPT;
73c931458eSkettenis
74c931458eSkettenis /* Store the current floating-point status and control register */
75c931458eSkettenis __asm__ volatile ("mffs %0" : "=f" (u.fpscr));
76c931458eSkettenis
77c931458eSkettenis /* Store the results in flagp */
78c931458eSkettenis *flagp = u.bits[1] & excepts;
79c931458eSkettenis
80c931458eSkettenis return (0);
81c931458eSkettenis }
82c931458eSkettenis
83c931458eSkettenis /*
84c931458eSkettenis * The feraiseexcept() function raises the supported floating-point exceptions
85c931458eSkettenis * represented by the argument `excepts'.
86c931458eSkettenis */
87c931458eSkettenis int
feraiseexcept(int excepts)88c931458eSkettenis feraiseexcept(int excepts)
89c931458eSkettenis {
90c931458eSkettenis excepts &= FE_ALL_EXCEPT;
91c931458eSkettenis
92c931458eSkettenis fesetexceptflag((fexcept_t *)&excepts, excepts);
93c931458eSkettenis
94c931458eSkettenis return (0);
95c931458eSkettenis }
96c931458eSkettenis DEF_STD(feraiseexcept);
97c931458eSkettenis
98c931458eSkettenis /*
99c931458eSkettenis * This function sets the floating-point status flags indicated by the argument
100c931458eSkettenis * `excepts' to the states stored in the object pointed to by `flagp'. It does
101c931458eSkettenis * NOT raise any floating-point exceptions, but only sets the state of the flags.
102c931458eSkettenis */
103c931458eSkettenis int
fesetexceptflag(const fexcept_t * flagp,int excepts)104c931458eSkettenis fesetexceptflag(const fexcept_t *flagp, int excepts)
105c931458eSkettenis {
106c931458eSkettenis union u u;
107c931458eSkettenis
108c931458eSkettenis excepts &= FE_ALL_EXCEPT;
109c931458eSkettenis
110c931458eSkettenis /* Store the current floating-point status and control register */
111c931458eSkettenis __asm__ volatile ("mffs %0" : "=f" (u.fpscr));
112c931458eSkettenis
113c931458eSkettenis /* Set the requested status flags */
114c931458eSkettenis u.bits[1] &= ~excepts;
115c931458eSkettenis u.bits[1] |= *flagp & excepts;
116c931458eSkettenis if (excepts & FE_INVALID) {
117c931458eSkettenis if (*flagp & FE_INVALID)
118c931458eSkettenis u.bits[1] |= _FE_INVALID_SOFT;
119c931458eSkettenis else
120c931458eSkettenis u.bits[1] &= ~_FE_INVALID_ALL;
121c931458eSkettenis }
122c931458eSkettenis
123c931458eSkettenis /* Load the floating-point status and control register */
124c931458eSkettenis __asm__ volatile ("mtfsf 0xff,%0" :: "f" (u.fpscr));
125c931458eSkettenis
126c931458eSkettenis return (0);
127c931458eSkettenis }
128c931458eSkettenis DEF_STD(fesetexceptflag);
129c931458eSkettenis
130c931458eSkettenis /*
131c931458eSkettenis * The fetestexcept() function determines which of a specified subset of the
132c931458eSkettenis * floating-point exception flags are currently set. The `excepts' argument
133c931458eSkettenis * specifies the floating-point status flags to be queried.
134c931458eSkettenis */
135c931458eSkettenis int
fetestexcept(int excepts)136c931458eSkettenis fetestexcept(int excepts)
137c931458eSkettenis {
138c931458eSkettenis union u u;
139c931458eSkettenis
140c931458eSkettenis excepts &= FE_ALL_EXCEPT;
141c931458eSkettenis
142c931458eSkettenis /* Store the current floating-point status and control register */
143c931458eSkettenis __asm__ volatile ("mffs %0" : "=f" (u.fpscr));
144c931458eSkettenis
145c931458eSkettenis return (u.bits[1] & excepts);
146c931458eSkettenis }
147c931458eSkettenis DEF_STD(fetestexcept);
148c931458eSkettenis
149c931458eSkettenis /*
150c931458eSkettenis * The fegetround() function gets the current rounding direction.
151c931458eSkettenis */
152c931458eSkettenis int
fegetround(void)153c931458eSkettenis fegetround(void)
154c931458eSkettenis {
155c931458eSkettenis union u u;
156c931458eSkettenis
157c931458eSkettenis /* Store the current floating-point status and control register */
158c931458eSkettenis __asm__ volatile ("mffs %0" : "=f" (u.fpscr));
159c931458eSkettenis
160c931458eSkettenis return (u.bits[1] & _ROUND_MASK);
161c931458eSkettenis }
162c931458eSkettenis DEF_STD(fegetround);
163c931458eSkettenis
164c931458eSkettenis /*
165c931458eSkettenis * The fesetround() function establishes the rounding direction represented by
166c931458eSkettenis * its argument `round'. If the argument is not equal to the value of a rounding
167c931458eSkettenis * direction macro, the rounding direction is not changed.
168c931458eSkettenis */
169c931458eSkettenis int
fesetround(int round)170c931458eSkettenis fesetround(int round)
171c931458eSkettenis {
172c931458eSkettenis union u u;
173c931458eSkettenis
174c931458eSkettenis /* Check whether requested rounding direction is supported */
175c931458eSkettenis if (round & ~_ROUND_MASK)
176c931458eSkettenis return (-1);
177c931458eSkettenis
178c931458eSkettenis /* Store the current floating-point status and control register */
179c931458eSkettenis __asm__ volatile ("mffs %0" : "=f" (u.fpscr));
180c931458eSkettenis
181c931458eSkettenis /* Set the rounding direction */
182c931458eSkettenis u.bits[1] &= ~_ROUND_MASK;
183c931458eSkettenis u.bits[1] |= round;
184c931458eSkettenis
185c931458eSkettenis /* Load the floating-point status and control register */
186c931458eSkettenis __asm__ volatile ("mtfsf 0xff,%0" :: "f" (u.fpscr));
187c931458eSkettenis
188c931458eSkettenis return (0);
189c931458eSkettenis }
190c931458eSkettenis DEF_STD(fesetround);
191c931458eSkettenis
192c931458eSkettenis /*
193c931458eSkettenis * The fegetenv() function attempts to store the current floating-point
194c931458eSkettenis * environment in the object pointed to by envp.
195c931458eSkettenis */
196c931458eSkettenis int
fegetenv(fenv_t * envp)197c931458eSkettenis fegetenv(fenv_t *envp)
198c931458eSkettenis {
199c931458eSkettenis union u u;
200c931458eSkettenis
201c931458eSkettenis /* Store the current floating-point status and control register */
202c931458eSkettenis __asm__ volatile ("mffs %0" : "=f" (u.fpscr));
203c931458eSkettenis
204c931458eSkettenis *envp = u.bits[1];
205c931458eSkettenis
206c931458eSkettenis return (0);
207c931458eSkettenis }
208c931458eSkettenis DEF_STD(fegetenv);
209c931458eSkettenis
210c931458eSkettenis /*
211c931458eSkettenis * The feholdexcept() function saves the current floating-point environment
212c931458eSkettenis * in the object pointed to by envp, clears the floating-point status flags, and
213c931458eSkettenis * then installs a non-stop (continue on floating-point exceptions) mode, if
214c931458eSkettenis * available, for all floating-point exceptions.
215c931458eSkettenis */
216c931458eSkettenis int
feholdexcept(fenv_t * envp)217c931458eSkettenis feholdexcept(fenv_t *envp)
218c931458eSkettenis {
219c931458eSkettenis union u u;
220c931458eSkettenis
221c931458eSkettenis /* Store the current floating-point status and control register */
222c931458eSkettenis __asm__ volatile ("mffs %0" : "=f" (u.fpscr));
223c931458eSkettenis
224c931458eSkettenis *envp = u.bits[1];
225c931458eSkettenis
226c931458eSkettenis /* Clear exception flags in FPSCR */
227c931458eSkettenis u.bits[1] &= ~(FE_ALL_EXCEPT | _FE_INVALID_ALL);
228c931458eSkettenis
229c931458eSkettenis /* Mask all exceptions */
230c931458eSkettenis u.bits[1] &= ~(FE_ALL_EXCEPT >> _MASK_SHIFT);
231c931458eSkettenis __asm__ volatile ("mtfsf 0xff,%0" :: "f" (u.fpscr));
232c931458eSkettenis
233c931458eSkettenis return (0);
234c931458eSkettenis }
235c931458eSkettenis DEF_STD(feholdexcept);
236c931458eSkettenis
237c931458eSkettenis /*
238c931458eSkettenis * The fesetenv() function attempts to establish the floating-point environment
239c931458eSkettenis * represented by the object pointed to by envp. The argument `envp' points
240c931458eSkettenis * to an object set by a call to fegetenv() or feholdexcept(), or equal a
241c931458eSkettenis * floating-point environment macro. The fesetenv() function does not raise
242c931458eSkettenis * floating-point exceptions, but only installs the state of the floating-point
243c931458eSkettenis * status flags represented through its argument.
244c931458eSkettenis */
245c931458eSkettenis int
fesetenv(const fenv_t * envp)246c931458eSkettenis fesetenv(const fenv_t *envp)
247c931458eSkettenis {
248c931458eSkettenis union u u;
249c931458eSkettenis
250c931458eSkettenis u.bits[0] = 0;
251c931458eSkettenis u.bits[1] = *envp;
252c931458eSkettenis
253c931458eSkettenis /* Load the floating-point status and control register */
254c931458eSkettenis __asm__ volatile ("mtfsf 0xff,%0" :: "f" (u.fpscr));
255c931458eSkettenis
256c931458eSkettenis return (0);
257c931458eSkettenis }
258c931458eSkettenis DEF_STD(fesetenv);
259c931458eSkettenis
260c931458eSkettenis /*
261c931458eSkettenis * The feupdateenv() function saves the currently raised floating-point
262c931458eSkettenis * exceptions in its automatic storage, installs the floating-point environment
263c931458eSkettenis * represented by the object pointed to by `envp', and then raises the saved
264c931458eSkettenis * floating-point exceptions. The argument `envp' shall point to an object set
265c931458eSkettenis * by a call to feholdexcept() or fegetenv(), or equal a floating-point
266c931458eSkettenis * environment macro.
267c931458eSkettenis */
268c931458eSkettenis int
feupdateenv(const fenv_t * envp)269c931458eSkettenis feupdateenv(const fenv_t *envp)
270c931458eSkettenis {
271c931458eSkettenis union u u;
272c931458eSkettenis
273c931458eSkettenis /* Store the current floating-point status and control register */
274c931458eSkettenis __asm__ volatile ("mffs %0" : "=f" (u.fpscr));
275c931458eSkettenis
276c931458eSkettenis /* Install new floating-point environment */
277c931458eSkettenis fesetenv(envp);
278c931458eSkettenis
279c931458eSkettenis /* Raise any previously accumulated exceptions */
280c931458eSkettenis feraiseexcept(u.bits[1]);
281c931458eSkettenis
282c931458eSkettenis return (0);
283c931458eSkettenis }
284c931458eSkettenis DEF_STD(feupdateenv);
285c931458eSkettenis
286c931458eSkettenis /*
287*2c53affbSjmc * The following functions are extensions to the standard
288c931458eSkettenis */
289c931458eSkettenis int
feenableexcept(int mask)290c931458eSkettenis feenableexcept(int mask)
291c931458eSkettenis {
292c931458eSkettenis union u u;
293c931458eSkettenis unsigned int omask;
294c931458eSkettenis
295c931458eSkettenis mask &= FE_ALL_EXCEPT;
296c931458eSkettenis
297c931458eSkettenis /* Store the current floating-point status and control register */
298c931458eSkettenis __asm__ volatile ("mffs %0" : "=f" (u.fpscr));
299c931458eSkettenis
300c931458eSkettenis omask = (u.bits[1] << _MASK_SHIFT) & FE_ALL_EXCEPT;
301c931458eSkettenis u.bits[1] |= mask >> _MASK_SHIFT;
302c931458eSkettenis
303c931458eSkettenis /* Load the floating-point status and control register */
304c931458eSkettenis __asm__ volatile ("mtfsf 0xff,%0" :: "f" (u.fpscr));
305c931458eSkettenis
306c931458eSkettenis return (omask);
307c931458eSkettenis
308c931458eSkettenis }
309c931458eSkettenis
310c931458eSkettenis int
fedisableexcept(int mask)311c931458eSkettenis fedisableexcept(int mask)
312c931458eSkettenis {
313c931458eSkettenis union u u;
314c931458eSkettenis unsigned int omask;
315c931458eSkettenis
316c931458eSkettenis mask &= FE_ALL_EXCEPT;
317c931458eSkettenis
318c931458eSkettenis /* Store the current floating-point status and control register */
319c931458eSkettenis __asm__ volatile ("mffs %0" : "=f" (u.fpscr));
320c931458eSkettenis
321c931458eSkettenis omask = (u.bits[1] << _MASK_SHIFT) & FE_ALL_EXCEPT;
322c931458eSkettenis u.bits[1] &= ~(mask >> _MASK_SHIFT);
323c931458eSkettenis
324c931458eSkettenis /* Load the floating-point status and control register */
325c931458eSkettenis __asm__ volatile ("mtfsf 0xff,%0" :: "f" (u.fpscr));
326c931458eSkettenis
327c931458eSkettenis return (omask);
328c931458eSkettenis }
329c931458eSkettenis
330c931458eSkettenis int
fegetexcept(void)331c931458eSkettenis fegetexcept(void)
332c931458eSkettenis {
333c931458eSkettenis union u u;
334c931458eSkettenis
335c931458eSkettenis /* Store the current floating-point status and control register */
336c931458eSkettenis __asm__ volatile ("mffs %0" : "=f" (u.fpscr));
337c931458eSkettenis
338c931458eSkettenis return ((u.bits[1] << _MASK_SHIFT) & FE_ALL_EXCEPT);
339c931458eSkettenis }
340