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