1130142Sdas/*-
2143708Sdas * Copyright (c) 2004-2005 David Schultz <das@FreeBSD.ORG>
3130142Sdas * All rights reserved.
4130142Sdas *
5130142Sdas * Redistribution and use in source and binary forms, with or without
6130142Sdas * modification, are permitted provided that the following conditions
7130142Sdas * are met:
8130142Sdas * 1. Redistributions of source code must retain the above copyright
9130142Sdas *    notice, this list of conditions and the following disclaimer.
10130142Sdas * 2. Redistributions in binary form must reproduce the above copyright
11130142Sdas *    notice, this list of conditions and the following disclaimer in the
12130142Sdas *    documentation and/or other materials provided with the distribution.
13130142Sdas *
14130142Sdas * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15130142Sdas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16130142Sdas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17130142Sdas * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18130142Sdas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19130142Sdas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20130142Sdas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21130142Sdas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22130142Sdas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23130142Sdas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24130142Sdas * SUCH DAMAGE.
25130142Sdas *
26130142Sdas * $FreeBSD$
27130142Sdas */
28130142Sdas
29130142Sdas#include <sys/cdefs.h>
30130142Sdas#include <sys/types.h>
31130142Sdas#include <machine/fpu.h>
32226218Sdas
33226218Sdas#define	__fenv_static
34226415Sdas#include "fenv.h"
35130142Sdas
36226218Sdas#ifdef __GNUC_GNU_INLINE__
37226218Sdas#error "This file must be compiled with C99 'inline' semantics"
38226218Sdas#endif
39226218Sdas
40130142Sdasconst fenv_t __fe_dfl_env = {
41130142Sdas	{ 0xffff0000 | __INITIAL_FPUCW__,
42130142Sdas	  0xffff0000,
43130142Sdas	  0xffffffff,
44130142Sdas	  { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
45130142Sdas	    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff }
46130142Sdas	},
47130142Sdas	__INITIAL_MXCSR__
48130142Sdas};
49130142Sdas
50226218Sdasextern inline int feclearexcept(int __excepts);
51226218Sdasextern inline int fegetexceptflag(fexcept_t *__flagp, int __excepts);
52226218Sdas
53130142Sdasint
54130142Sdasfesetexceptflag(const fexcept_t *flagp, int excepts)
55130142Sdas{
56130142Sdas	fenv_t env;
57130142Sdas
58130142Sdas	__fnstenv(&env.__x87);
59130142Sdas	env.__x87.__status &= ~excepts;
60130142Sdas	env.__x87.__status |= *flagp & excepts;
61130142Sdas	__fldenv(env.__x87);
62130142Sdas
63130142Sdas	__stmxcsr(&env.__mxcsr);
64130142Sdas	env.__mxcsr &= ~excepts;
65130142Sdas	env.__mxcsr |= *flagp & excepts;
66130142Sdas	__ldmxcsr(env.__mxcsr);
67130142Sdas
68130142Sdas	return (0);
69130142Sdas}
70130142Sdas
71130142Sdasint
72130142Sdasferaiseexcept(int excepts)
73130142Sdas{
74130142Sdas	fexcept_t ex = excepts;
75130142Sdas
76130142Sdas	fesetexceptflag(&ex, excepts);
77130142Sdas	__fwait();
78130142Sdas	return (0);
79130142Sdas}
80130142Sdas
81226218Sdasextern inline int fetestexcept(int __excepts);
82226218Sdasextern inline int fegetround(void);
83226218Sdasextern inline int fesetround(int __round);
84226218Sdas
85130142Sdasint
86130142Sdasfegetenv(fenv_t *envp)
87130142Sdas{
88130142Sdas
89165795Sdas	__fnstenv(&envp->__x87);
90165795Sdas	__stmxcsr(&envp->__mxcsr);
91130142Sdas	/*
92165795Sdas	 * fnstenv masks all exceptions, so we need to restore the
93165795Sdas	 * control word to avoid this side effect.
94130142Sdas	 */
95165795Sdas	__fldcw(envp->__x87.__control);
96130142Sdas	return (0);
97130142Sdas}
98130142Sdas
99130142Sdasint
100130142Sdasfeholdexcept(fenv_t *envp)
101130142Sdas{
102203441Skib	__uint32_t mxcsr;
103130142Sdas
104130142Sdas	__stmxcsr(&mxcsr);
105130142Sdas	__fnstenv(&envp->__x87);
106130142Sdas	__fnclex();
107130142Sdas	envp->__mxcsr = mxcsr;
108130328Sdas	mxcsr &= ~FE_ALL_EXCEPT;
109130142Sdas	mxcsr |= FE_ALL_EXCEPT << _SSE_EMASK_SHIFT;
110130142Sdas	__ldmxcsr(mxcsr);
111130142Sdas	return (0);
112130142Sdas}
113130142Sdas
114226218Sdasextern inline int fesetenv(const fenv_t *__envp);
115226218Sdas
116130142Sdasint
117130142Sdasfeupdateenv(const fenv_t *envp)
118130142Sdas{
119203441Skib	__uint32_t mxcsr;
120203441Skib	__uint16_t status;
121130142Sdas
122130142Sdas	__fnstsw(&status);
123130142Sdas	__stmxcsr(&mxcsr);
124130142Sdas	fesetenv(envp);
125130142Sdas	feraiseexcept((mxcsr | status) & FE_ALL_EXCEPT);
126130142Sdas	return (0);
127130142Sdas}
128130142Sdas
129130142Sdasint
130143708Sdas__feenableexcept(int mask)
131130142Sdas{
132203441Skib	__uint32_t mxcsr, omask;
133203441Skib	__uint16_t control;
134130142Sdas
135143708Sdas	mask &= FE_ALL_EXCEPT;
136130142Sdas	__fnstcw(&control);
137130142Sdas	__stmxcsr(&mxcsr);
138226594Sdas	omask = ~(control | mxcsr >> _SSE_EMASK_SHIFT) & FE_ALL_EXCEPT;
139143708Sdas	control &= ~mask;
140130142Sdas	__fldcw(control);
141130142Sdas	mxcsr &= ~(mask << _SSE_EMASK_SHIFT);
142130142Sdas	__ldmxcsr(mxcsr);
143226594Sdas	return (omask);
144130142Sdas}
145130142Sdas
146143708Sdasint
147143708Sdas__fedisableexcept(int mask)
148143708Sdas{
149203441Skib	__uint32_t mxcsr, omask;
150203441Skib	__uint16_t control;
151143708Sdas
152143708Sdas	mask &= FE_ALL_EXCEPT;
153143708Sdas	__fnstcw(&control);
154143708Sdas	__stmxcsr(&mxcsr);
155226594Sdas	omask = ~(control | mxcsr >> _SSE_EMASK_SHIFT) & FE_ALL_EXCEPT;
156143708Sdas	control |= mask;
157143708Sdas	__fldcw(control);
158143708Sdas	mxcsr |= mask << _SSE_EMASK_SHIFT;
159143708Sdas	__ldmxcsr(mxcsr);
160226594Sdas	return (omask);
161143708Sdas}
162143708Sdas
163143708Sdas__weak_reference(__feenableexcept, feenableexcept);
164143708Sdas__weak_reference(__fedisableexcept, fedisableexcept);
165