/*
 * fenv.h
 *
 * Structures and constants defining, and functions for management of,
 * the floating point environment.
 *
 * $Id$
 *
 * Written by Danny Smith <dannysmith@users.sourceforge.net>
 * Copyright (C) 2002, 2003, 2005-2007, 2017, MinGW.org Project
 *
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice, this permission notice, and the following
 * disclaimer shall be included in all copies or substantial portions of
 * the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OF OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */
#ifndef _FENV_H
#pragma  GCC system_header
#define _FENV_H

/* All MinGW headers are required to include <_mingw.h>
 */
#include <_mingw.h>

/* FPU status word flags indicating exceptions.  Each is represented
 * by a single bit, sequentially and contiguously assigned to the low
 * order bits of the status word, enumerated from lowest:
 */
enum
{ __FE_INVALID_EXCEPT_FLAG_SHIFT,
  __FE_DENORMAL_EXCEPT_FLAG_SHIFT,
  __FE_DIVBYZERO_EXCEPT_FLAG_SHIFT,
  __FE_OVERFLOW_EXCEPT_FLAG_SHIFT,
  __FE_UNDERFLOW_EXCEPT_FLAG_SHIFT,
  __FE_INEXACT_EXCEPT_FLAG_SHIFT,

  /* The final entry in the shift enumeration represents the order
   * of the rightmost bit which does NOT represent an exception flag;
   * we use it as a high water mark, for generation of the aggregate
   * mask for ALL exception flags; arithmetically, this becomes the
   * numeric value of a pseudo-flag placed at the high water mark,
   * less one.
   */
  __FE_ALL_EXCEPT_HWM_SHIFT
# define FE_ALL_EXCEPT		((0x01 << __FE_ALL_EXCEPT_HWM_SHIFT) - 1)
};

#define __FE_EXCEPT(__NAME__)	 (0x01 << __FE_##__NAME__##_EXCEPT_FLAG_SHIFT)

#define FE_INVALID		  __FE_EXCEPT(INVALID)
#define FE_DENORMAL		  __FE_EXCEPT(DENORMAL)
#define FE_DIVBYZERO		  __FE_EXCEPT(DIVBYZERO)
#define FE_OVERFLOW		  __FE_EXCEPT(OVERFLOW)
#define FE_UNDERFLOW		  __FE_EXCEPT(UNDERFLOW)
#define FE_INEXACT		  __FE_EXCEPT(INEXACT)

/* FPU control word flags to specify rounding mode; this may be
 * represented as a selection from a four-way enumeration...
 */
enum
{ __FE_ROUND_TONEAREST,
  __FE_ROUND_DOWNWARD,
  __FE_ROUND_UPWARD,
  __FE_ROUND_TOWARDZERO
};
/* ...with the actual flag bits offset 10-bits from the rightmost
 * bit in the control word; hence, the rounding mode macros become:
 */
#define __FE_ROUND(__MODE__)	(__FE_ROUND_##__MODE__ << 10)

#define FE_TONEAREST		 __FE_ROUND(TONEAREST)
#define FE_DOWNWARD		 __FE_ROUND(DOWNWARD)
#define FE_UPWARD		 __FE_ROUND(UPWARD)
#define FE_TOWARDZERO		 __FE_ROUND(TOWARDZERO)

/* The MXCSR exception flags are the same as those for the FPU...
 */
#define __MXCSR_EXCEPT_FLAG_SHIFT  0

/* ...and the corresponding mask bits are offset by a further 7-bit
 * shift to the left...
 */
#define __MXCSR_EXCEPT_MASK_SHIFT  7

/* ...while the MXCSR rounding mode flags adopt the same enumeration,
 * but are offset a further 3-bits to the left of those representing
 * the FPU rounding mode selection flags.
 */
#define __MXCSR_ROUND_FLAG_SHIFT   3

#ifndef RC_INVOKED
/* For now, support only for the basic abstraction of flags that are
 * either set or clear; fexcept_t could be a structure that holds more
 * info about the fp environment.
 */
typedef unsigned short fexcept_t;

typedef struct __fenv_t
{ /* This 28-byte structure represents the entire floating point
   * environment of the FPU, as stored by either the "fnstenv", or
   * the "fstenv" instruction.
   */
  unsigned short	__control_word;
  unsigned short	__unused0;
  unsigned short	__status_word;
  unsigned short	__unused1;
  unsigned short	__tag_word;
  unsigned short	__unused2;
  unsigned int		__ip_offset;    /* instruction pointer offset */
  unsigned short	__ip_selector;
  unsigned short	__opcode;
  unsigned int		__data_offset;
  unsigned short	__data_selector;
  unsigned short	__unused3;

  /* The structure is extended, beyond the 28 byte requirement to
   * store the FPU state, by 4 additional bytes, which then makes it
   * sufficient to also store the contents of the MXCSR register, as
   * stored by the "stmxcsr" instruction, (if the CPU supports it).
   */
  unsigned int		__mxcsr;	/* content of the MXCSR register */
} fenv_t;

/* The ISO-C99 standard, section 7.6 paragraph 8 requires us to define
 * the FE_DFL_ENV macro; it specifies restoration of the FPU environment
 * to its default state, as established at application start-up.
 */
#define FE_DFL_ENV    ((const fenv_t *)(0))

/* Paragraph 9 of the same section of ISO-C99 provides for the definition
 * of implementation-specific macros to identify alternative predefined
 * FPU environment configuration; we exploit this licence to offer:
 *
 *   FP_PC64_ENV  --  Intel standard 80-bit (64-bit precision); this is
 *		      consistent with IEEE-754 extended precision, and
 *		      the configuration preferred by MinGW, because it
 *		      matches the format of GCC's 80-bit long doubles.
 *
 *   FP_PC53_ENV  --  IEEE-754 64-bit (53-bit precision); consistent
 *		      with Microsoft's preferred FPU configuration;
 *		      (they offer no better than 64-bit long doubles).
 */
#define FE_PC64_ENV   ((const fenv_t *)(-1))
#define FE_PC53_ENV   ((const fenv_t *)(-2))

/* The following pair of additional predefined environment macros serve
 * as aliases for the preceding pair, respectively, with the added side
 * effect that, when passed to fesetenv(), each causes its respective
 * alias to become associated with FE_DFL_ENV in subsequent calls.
 */
#define FE_PD64_ENV   ((const fenv_t *)(-3))
#define FE_PD53_ENV   ((const fenv_t *)(-4))

_BEGIN_C_DECLS

/*TODO: Some of these could be inlined */

/* ISO-C99 section 7.6.2 -- Floating Point Exception Handling
 */
extern __cdecl __MINGW_NOTHROW  int feclearexcept (int);
extern __cdecl __MINGW_NOTHROW  int fegetexceptflag (fexcept_t *, int);
extern __cdecl __MINGW_NOTHROW  int feraiseexcept (int);
extern __cdecl __MINGW_NOTHROW  int fesetexceptflag (const fexcept_t *, int);
extern __cdecl __MINGW_NOTHROW  int fetestexcept (int);

/* ISO-C99 section 7.6.3 -- Floating Point Rounding Mode Control
 */
extern __cdecl __MINGW_NOTHROW  int fegetround (void);
extern __cdecl __MINGW_NOTHROW  int fesetround (int);

/* ISO-C99 section 7.6.4 -- Floating Point Environment Configuration
 */
extern __cdecl __MINGW_NOTHROW  int fegetenv (fenv_t *);
extern __cdecl __MINGW_NOTHROW  int fesetenv (const fenv_t *);
extern __cdecl __MINGW_NOTHROW  int feupdateenv (const fenv_t *);
extern __cdecl __MINGW_NOTHROW  int feholdexcept (fenv_t *);

_END_C_DECLS

#endif	/* ! RC_INVOKED */
#endif	/* !_FENV_H: $RCSfile$: end of file */
