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