1/* $NetBSD: fenv.h,v 1.10 2024/10/30 15:56:11 riastradh Exp $ */
2
3/*-
4 * Copyright (c) 2015 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Christos Zoulas.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#ifndef _M68K_FENV_H_
33#define _M68K_FENV_H_
34
35#include <sys/featuretest.h>
36#include <sys/stdint.h>
37
38#include <m68k/float.h>
39#include <m68k/fpreg.h>
40
41/* Exception bits, from FPSR */
42#define FE_INEXACT FPSR_AINEX
43#define FE_DIVBYZERO FPSR_ADZ
44#define FE_UNDERFLOW FPSR_AUNFL
45#define FE_OVERFLOW FPSR_AOVFL
46#define FE_INVALID FPSR_AIOP
47
48#define FE_ALL_EXCEPT \
49 (FE_INEXACT | FE_DIVBYZERO | FE_UNDERFLOW | FE_OVERFLOW | FE_INVALID)
50
51/* Rounding modes, from FPCR */
52#define FE_TONEAREST FPCR_NEAR
53#define FE_TOWARDZERO FPCR_ZERO
54#define FE_DOWNWARD FPCR_MINF
55#define FE_UPWARD FPCR_PINF
56
57#define _ROUND_MASK \
58 (FE_TONEAREST | FE_TOWARDZERO | FE_DOWNWARD | FE_UPWARD)
59
60#if defined(__HAVE_68881__)
61
62#ifndef __fenv_static
63#define __fenv_static static
64#endif
65
66typedef uint32_t fexcept_t;
67
68/* same layout as fmovem */
69typedef struct {
70 uint32_t fpcr;
71 uint32_t fpsr;
72 uint32_t fppc;
73} fenv_t;
74
75#define FE_DFL_ENV ((fenv_t *) -1)
76
77#define __get_fpcr(__fpcr) \
78 __asm__ __volatile__ ("fmove%.l %!,%0" : "=dm" (__fpcr))
79#define __set_fpcr(__fpcr) \
80 __asm__ __volatile__ ("fmove%.l %0,%!" : : "dm" (__fpcr))
81
82#define __get_fpsr(__fpsr) \
83 __asm__ __volatile__ ("fmove%.l %/fpsr,%0" : "=dm" (__fpsr))
84#define __set_fpsr(__fpsr) \
85 __asm__ __volatile__ ("fmove%.l %0,%/fpsr" : : "dm" (__fpsr))
86
87#define __fmul(__s, __t, __d) \
88 do { \
89 __t d = __d; \
90 __asm__ __volatile__ ("fmul" __s "; fnop" : "=f" (d) : "0" (d)); \
91 } while (/*CONSTCOND*/0)
92
93#define __fdiv(__s, __t, __d) \
94 do { \
95 __t d = __d; \
96 __asm__ __volatile__ ("fdiv" __s "; fnop" : "=f" (d) : "0" (d)); \
97 } while (/*CONSTCOND*/0)
98
99#define __fetox(__s, __t, __d) \
100 do { \
101 __t d = __d; \
102 __asm__ __volatile__ ("fetox" __s "; fnop" : "=f" (d) : "0" (d)); \
103 } while (/*CONSTCOND*/0)
104
105#define __fgetenv(__envp) \
106 __asm__ __volatile__ ("fmovem%.l %/fpcr/%/fpsr/%/fpiar,%0" : "=m" (__envp))
107
108#define __fsetenv(__envp) \
109 __asm__ __volatile__ ("fmovem%.l %0,%/fpcr/%/fpsr/%/fpiar" : : "m" (__envp))
110
111__BEGIN_DECLS
112
113#if __GNUC_PREREQ__(8, 0)
114#pragma GCC diagnostic push
115#pragma GCC diagnostic ignored "-Wshadow"
116#endif
117
118__fenv_static inline int
119feclearexcept(int __excepts)
120{
121 fexcept_t __fpsr;
122
123 __excepts &= FE_ALL_EXCEPT;
124
125 __get_fpsr(__fpsr);
126 __fpsr &= ~__excepts;
127 __set_fpsr(__fpsr);
128
129 return 0;
130}
131
132__fenv_static inline int
133fegetexceptflag(fexcept_t *__flagp, int __excepts)
134{
135 fexcept_t __fpsr;
136
137 __get_fpsr(__fpsr);
138
139 *__flagp = __fpsr & __excepts & FE_ALL_EXCEPT;
140
141 return 0;
142}
143
144__fenv_static inline int
145fesetexceptflag(const fexcept_t *__flagp, int __excepts)
146{
147 fexcept_t __fpsr;
148
149 __get_fpsr(__fpsr);
150
151 __fpsr &= ~(__excepts & FE_ALL_EXCEPT);
152 __fpsr |= *__flagp & __excepts & FE_ALL_EXCEPT;
153
154 __set_fpsr(__fpsr);
155
156 return 0;
157}
158
159__fenv_static inline int
160feraiseexcept(int __excepts)
161{
162 if (__excepts & FE_INVALID) /* Inf * 0 */
163 __fmul("%.s %#0r0,%0", double, __builtin_huge_val());
164
165 if (__excepts & FE_DIVBYZERO) /* 1.0 / 0 */
166 __fdiv("%.s %#0r0,%0", double, 1.0);
167
168 if (__excepts & FE_OVERFLOW) /* MAX * MAX */
169 __fmul("%.x %0,%0", long double, LDBL_MAX);
170
171 if (__excepts & FE_UNDERFLOW) /* e ^ -MAX */
172 __fetox("%.x %0", long double, -LDBL_MAX);
173
174 if (__excepts & FE_INEXACT) /* 1 / 3 */
175 __fdiv("%.s %#0r3,%0", long double, 1.0);
176
177 return 0;
178}
179
180__fenv_static inline int
181fetestexcept(int __excepts)
182{
183 fexcept_t __fpsr;
184
185 __get_fpsr(__fpsr);
186
187 return __fpsr & __excepts & FE_ALL_EXCEPT;
188}
189
190__fenv_static inline int
191fegetround(void)
192{
193 fexcept_t __fpcr;
194
195 __get_fpcr(__fpcr);
196 return __fpcr & _ROUND_MASK;
197}
198
199__fenv_static inline int
200fesetround(int __round)
201{
202 fexcept_t __fpcr;
203
204 if (__round & ~_ROUND_MASK)
205 return -1;
206
207 __get_fpcr(__fpcr);
208
209 __fpcr &= ~_ROUND_MASK;
210 __fpcr |= __round;
211
212 __set_fpcr(__fpcr);
213
214 return 0;
215}
216
217__fenv_static inline int
218fegetenv(fenv_t *__envp)
219{
220 __fgetenv(*__envp);
221
222 return 0;
223}
224
225__fenv_static inline int
226feholdexcept(fenv_t *__envp)
227{
228 fexcept_t __fpcr, __fpsr;
229
230 __fgetenv(*__envp);
231 __fpsr = __envp->fpsr & ~FE_ALL_EXCEPT;
232 __set_fpsr(__fpsr); /* clear all */
233 __fpcr = __envp->fpcr & ~(FE_ALL_EXCEPT << 6);
234 __set_fpcr(__fpcr); /* set non/stop */
235
236 return 0;
237}
238
239__fenv_static inline int
240fesetenv(const fenv_t *__envp)
241{
242 fenv_t __tenv;
243
244 __fgetenv(__tenv);
245
246 if (__envp == FE_DFL_ENV) {
247 __tenv.fpcr |=
248 __envp->fpcr & ((FE_ALL_EXCEPT << 6) | FE_UPWARD);
249 __tenv.fpsr |= __envp->fpsr & FE_ALL_EXCEPT;
250 }
251
252 __fsetenv(__tenv);
253
254 return 0;
255}
256
257__fenv_static inline int
258feupdateenv(const fenv_t *__envp)
259{
260 fexcept_t __fpsr;
261
262 __get_fpsr(__fpsr);
263 __fpsr &= FE_ALL_EXCEPT;
264 fesetenv(__envp);
265 feraiseexcept((int)__fpsr);
266 return 0;
267}
268
269#if __GNUC_PREREQ__(8, 0)
270#pragma GCC diagnostic pop
271#endif
272
273#if defined(_NETBSD_SOURCE) || defined(_GNU_SOURCE)
274
275__fenv_static inline int
276feenableexcept(int __mask)
277{
278 fexcept_t __fpcr, __oldmask;
279
280 __get_fpcr(__fpcr);
281 __oldmask = (__fpcr >> 6) & FE_ALL_EXCEPT;
282 __fpcr |= (__mask & FE_ALL_EXCEPT) << 6;
283 __set_fpcr(__fpcr);
284
285 return __oldmask;
286}
287
288__fenv_static inline int
289fedisableexcept(int __mask)
290{
291 fexcept_t __fpcr, __oldmask;
292
293 __get_fpcr(__fpcr);
294 __oldmask = (__fpcr >> 6) & FE_ALL_EXCEPT;
295 __fpcr &= ~((__mask & FE_ALL_EXCEPT) << 6);
296 __set_fpcr(__fpcr);
297
298 return __oldmask;
299}
300
301__fenv_static inline int
302fegetexcept(void)
303{
304 fexcept_t __fpcr;
305
306 __get_fpcr(__fpcr);
307
308 return (__fpcr >> 6) & FE_ALL_EXCEPT;
309}
310
311#endif /* _NETBSD_SOURCE || _GNU_SOURCE */
312
313__END_DECLS
314
315#endif /* __HAVE_68881__ */
316
317#endif /* _M68K_FENV_H_ */