1 /*- 2 * Copyright (c) 2013 The NetBSD Foundation, Inc. 3 * All rights reserved. 4 * 5 * This code is derived from software contributed to The NetBSD Foundation 6 * by Matt Thomas of 3am Software Foundry. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 * POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 #include <sys/cdefs.h> 31 __RCSID("$NetBSD: fenv.c,v 1.9 2017/05/08 09:25:03 martin Exp $"); 32 33 #include "namespace.h" 34 35 #include <sys/types.h> 36 #include <assert.h> 37 #include <fenv.h> 38 #include <string.h> 39 #include <unistd.h> 40 #include <inttypes.h> 41 42 #ifdef __SOFTFP__ 43 #error This fenv implementation is only for hardfloat. 44 #endif 45 46 #ifdef __weak_alias 47 __weak_alias(feclearexcept,_feclearexcept) 48 __weak_alias(fedisableexcept,_fedisableexcept) 49 __weak_alias(feenableexcept,_feenableexcept) 50 __weak_alias(fegetenv,_fegetenv) 51 __weak_alias(fegetexcept,_fegetexcept) 52 __weak_alias(fegetexceptflag,_fegetexceptflag) 53 __weak_alias(fegetround,_fegetround) 54 __weak_alias(feholdexcept,_feholdexcept) 55 __weak_alias(feraiseexcept,_feraiseexcept) 56 __weak_alias(fesetenv,_fesetenv) 57 __weak_alias(fesetexceptflag,_fesetexceptflag) 58 __weak_alias(fesetround,_fesetround) 59 __weak_alias(fetestexcept,_fetestexcept) 60 __weak_alias(feupdateenv,_feupdateenv) 61 #endif 62 63 #include <arm/armreg.h> 64 #include <arm/vfpreg.h> 65 66 const fenv_t __fe_dfl_env = VFP_FPSCR_FZ|VFP_FPSCR_DN|VFP_FPSCR_RN; 67 68 /* 69 * The feclearexcept() function shall attempt to clear the supported 70 * floating-point exceptions represented by excepts. 71 */ 72 int 73 feclearexcept(int excepts) 74 { 75 #ifndef lint 76 _DIAGASSERT((except & ~FE_ALL_EXCEPT) == 0); 77 #endif 78 int tmp = armreg_fpscr_read() & ~__SHIFTIN(excepts, VFP_FPSCR_CSUM); 79 armreg_fpscr_write(tmp); 80 return 0; 81 } 82 83 /* 84 * The fegetexceptflag() function shall attempt to store an 85 * implementation-defined representation of the states of the floating-point 86 * status flags indicated by the argument excepts in the object pointed to by 87 * the argument flagp. 88 */ 89 int 90 fegetexceptflag(fexcept_t *flagp, int excepts) 91 { 92 93 _DIAGASSERT((except & ~FE_ALL_EXCEPT) == 0); 94 *flagp = __SHIFTOUT(armreg_fpscr_read(), VFP_FPSCR_CSUM) & excepts; 95 return 0; 96 } 97 98 /* 99 * The feraiseexcept() function shall attempt to raise the supported 100 * floating-point exceptions represented by the argument excepts. The order 101 * in which these floating-point exceptions are raised is unspecified. 102 */ 103 int 104 feraiseexcept(int excepts) 105 { 106 #ifndef lint 107 _DIAGASSERT((except & ~FE_ALL_EXCEPT) == 0); 108 #endif 109 int fpscr = armreg_fpscr_read(); 110 fpscr |= __SHIFTIN(excepts, VFP_FPSCR_CSUM); 111 armreg_fpscr_write(fpscr); 112 return 0; 113 } 114 115 /* 116 * The fesetexceptflag() function shall attempt to set the floating-point 117 * status flags indicated by the argument excepts to the states stored in the 118 * object pointed to by flagp. The value pointed to by flagp shall have been 119 * set by a previous call to fegetexceptflag() whose second argument 120 * represented at least those floating-point exceptions represented by the 121 * argument excepts. This function does not raise floating-point exceptions, 122 * but only sets the state of the flags. 123 */ 124 int 125 fesetexceptflag(const fexcept_t *flagp, int excepts) 126 { 127 #ifndef lint 128 _DIAGASSERT((except & ~FE_ALL_EXCEPT) == 0); 129 #endif 130 int fpscr = armreg_fpscr_read(); 131 fpscr &= ~__SHIFTIN(excepts, VFP_FPSCR_CSUM); 132 fpscr |= __SHIFTIN((*flagp & excepts), VFP_FPSCR_CSUM); 133 armreg_fpscr_write(fpscr); 134 return 0; 135 } 136 137 int 138 feenableexcept(int excepts) 139 { 140 #ifndef lint 141 _DIAGASSERT((except & ~FE_ALL_EXCEPT) == 0); 142 #endif 143 int fpscr = armreg_fpscr_read(); 144 armreg_fpscr_write(fpscr | __SHIFTIN((excepts), VFP_FPSCR_ESUM)); 145 return __SHIFTOUT(fpscr, VFP_FPSCR_ESUM) & FE_ALL_EXCEPT; 146 } 147 148 int 149 fedisableexcept(int excepts) 150 { 151 #ifndef lint 152 _DIAGASSERT((except & ~FE_ALL_EXCEPT) == 0); 153 #endif 154 int fpscr = armreg_fpscr_read(); 155 armreg_fpscr_write(fpscr & ~__SHIFTIN((excepts), VFP_FPSCR_ESUM)); 156 return __SHIFTOUT(fpscr, VFP_FPSCR_ESUM) & FE_ALL_EXCEPT; 157 } 158 159 /* 160 * The fetestexcept() function shall determine which of a specified subset of 161 * the floating-point exception flags are currently set. The excepts argument 162 * specifies the floating-point status flags to be queried. 163 */ 164 int 165 fetestexcept(int excepts) 166 { 167 _DIAGASSERT((except & ~FE_ALL_EXCEPT) == 0); 168 return __SHIFTOUT(armreg_fpscr_read(), VFP_FPSCR_CSUM) & excepts; 169 } 170 171 int 172 fegetexcept(void) 173 { 174 return __SHIFTOUT(armreg_fpscr_read(), VFP_FPSCR_ESUM); 175 } 176 177 /* 178 * The fegetround() function shall get the current rounding direction. 179 */ 180 int 181 fegetround(void) 182 { 183 return __SHIFTOUT(armreg_fpscr_read(), VFP_FPSCR_RMODE); 184 } 185 186 /* 187 * The fesetround() function shall establish the rounding direction represented 188 * by its argument round. If the argument is not equal to the value of a 189 * rounding direction macro, the rounding direction is not changed. 190 */ 191 int 192 fesetround(int round) 193 { 194 #ifndef lint 195 _DIAGASSERT(!(round & ~__SHIFTOUT(VFP_FPSCR_RMODE, VFP_FPSCR_RMODE))); 196 #endif 197 int fpscr = armreg_fpscr_read() & ~VFP_FPSCR_RMODE; 198 fpscr |= __SHIFTIN(round, VFP_FPSCR_RMODE); 199 armreg_fpscr_write(fpscr); 200 return 0; 201 } 202 203 /* 204 * The fegetenv() function shall attempt to store the current floating-point 205 * environment in the object pointed to by envp. 206 */ 207 int 208 fegetenv(fenv_t *envp) 209 { 210 211 *envp = armreg_fpscr_read(); 212 return 0; 213 } 214 215 /* 216 * The feholdexcept() function shall save the current floating-point 217 * environment in the object pointed to by envp, clear the floating-point 218 * status flags, and then install a non-stop (continue on floating-point 219 * exceptions) mode, if available, for all floating-point exceptions. 220 */ 221 int 222 feholdexcept(fenv_t *envp) 223 { 224 225 *envp = armreg_fpscr_read(); 226 armreg_fpscr_write((*envp) & ~(VFP_FPSCR_ESUM|VFP_FPSCR_CSUM)); 227 return 0; 228 } 229 230 /* 231 * The fesetenv() function shall attempt to establish the floating-point 232 * environment represented by the object pointed to by envp. The fesetenv() 233 * function does not raise floating-point exceptions, but only installs the 234 * state of the floating-point status flags represented through its argument. 235 */ 236 int 237 fesetenv(const fenv_t *envp) 238 { 239 240 armreg_fpscr_write(*envp); 241 return 0; 242 } 243 244 /* 245 * The feupdateenv() function shall attempt to save the currently raised 246 * floating-point exceptions in its automatic storage, attempt to install the 247 * floating-point environment represented by the object pointed to by envp, 248 * and then attempt to raise the saved floating-point exceptions. 249 */ 250 int 251 feupdateenv(const fenv_t *envp) 252 { 253 #ifndef lint 254 _DIAGASSERT(envp != NULL); 255 #endif 256 int fpscr = armreg_fpscr_read(); 257 fpscr &= ~(VFP_FPSCR_ESUM|VFP_FPSCR_RMODE); 258 fpscr |= *envp; 259 armreg_fpscr_write(fpscr); 260 261 /* Success */ 262 return 0; 263 } 264