/*-
 * Copyright (c) 2004 David Schultz <das@FreeBSD.ORG>
 * Copyright (c) 2013 Andrew Turner <andrew@FreeBSD.ORG>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $FreeBSD: releng/11.1/lib/msun/arm/fenv.c 269956 2014-08-14 04:20:13Z imp $
 */
#define	__fenv_static
#include "fenv.h"
#include <machine/acle-compat.h>
#if __ARM_ARCH >= 6
#define FENV_ARMv6
#endif
/* When SOFTFP_ABI is defined we are using the softfp ABI. */
#if defined(__VFP_FP__) && !defined(__ARM_PCS_VFP)
#define SOFTFP_ABI
#endif
#ifndef FENV_MANGLE
/*
 * Hopefully the system ID byte is immutable, so it's valid to use
 * this as a default environment.
 */
const fenv_t __fe_dfl_env = 0;
#endif
/* If this is a non-mangled softfp version special processing is required */
#if defined(FENV_MANGLE) || !defined(SOFTFP_ABI) || !defined(FENV_ARMv6)
/*
 * The following macros map between the softfloat emulator's flags and
 * the hardware's FPSR.  The hardware this file was written for doesn't
 * have rounding control bits, so we stick those in the system ID byte.
 */
#ifndef __ARM_PCS_VFP
#define	__set_env(env, flags, mask, rnd) env = ((flags)			\
						| (mask)<<_FPUSW_SHIFT	\
						| (rnd) << 24)
#define	__env_flags(env)		((env) & FE_ALL_EXCEPT)
#define	__env_mask(env)			(((env) >> _FPUSW_SHIFT)	\
						& FE_ALL_EXCEPT)
#define	__env_round(env)		(((env) >> 24) & _ROUND_MASK)
#include "fenv-softfloat.h"
#endif
#ifdef __GNUC_GNU_INLINE__
#error "This file must be compiled with C99 'inline' semantics"
#endif
extern inline int feclearexcept(int __excepts);
extern inline int fegetexceptflag(fexcept_t *__flagp, int __excepts);
extern inline int fesetexceptflag(const fexcept_t *__flagp, int __excepts);
extern inline int feraiseexcept(int __excepts);
extern inline int fetestexcept(int __excepts);
extern inline int fegetround(void);
extern inline int fesetround(int __round);
extern inline int fegetenv(fenv_t *__envp);
extern inline int feholdexcept(fenv_t *__envp);
extern inline int fesetenv(const fenv_t *__envp);
extern inline int feupdateenv(const fenv_t *__envp);
extern inline int feenableexcept(int __mask);
extern inline int fedisableexcept(int __mask);
extern inline int fegetexcept(void);
#else /* !FENV_MANGLE && SOFTFP_ABI */
/* Set by libc when the VFP unit is enabled */
extern int _libc_arm_fpu_present;
int __softfp_feclearexcept(int __excepts);
int __softfp_fegetexceptflag(fexcept_t *__flagp, int __excepts);
int __softfp_fesetexceptflag(const fexcept_t *__flagp, int __excepts);
int __softfp_feraiseexcept(int __excepts);
int __softfp_fetestexcept(int __excepts);
int __softfp_fegetround(void);
int __softfp_fesetround(int __round);
int __softfp_fegetenv(fenv_t *__envp);
int __softfp_feholdexcept(fenv_t *__envp);
int __softfp_fesetenv(const fenv_t *__envp);
int __softfp_feupdateenv(const fenv_t *__envp);
int __softfp_feenableexcept(int __mask);
int __softfp_fedisableexcept(int __mask);
int __softfp_fegetexcept(void);
int __vfp_feclearexcept(int __excepts);
int __vfp_fegetexceptflag(fexcept_t *__flagp, int __excepts);
int __vfp_fesetexceptflag(const fexcept_t *__flagp, int __excepts);
int __vfp_feraiseexcept(int __excepts);
int __vfp_fetestexcept(int __excepts);
int __vfp_fegetround(void);
int __vfp_fesetround(int __round);
int __vfp_fegetenv(fenv_t *__envp);
int __vfp_feholdexcept(fenv_t *__envp);
int __vfp_fesetenv(const fenv_t *__envp);
int __vfp_feupdateenv(const fenv_t *__envp);
int __vfp_feenableexcept(int __mask);
int __vfp_fedisableexcept(int __mask);
int __vfp_fegetexcept(void);
static int
__softfp_round_to_vfp(int round)
{
	switch (round) {
	case FE_TONEAREST:
	default:
		return VFP_FE_TONEAREST;
	case FE_TOWARDZERO:
		return VFP_FE_TOWARDZERO;
	case FE_UPWARD:
		return VFP_FE_UPWARD;
	case FE_DOWNWARD:
		return VFP_FE_DOWNWARD;
	}
}
static int
__softfp_round_from_vfp(int round)
{
	switch (round) {
	case VFP_FE_TONEAREST:
	default:
		return FE_TONEAREST;
	case VFP_FE_TOWARDZERO:
		return FE_TOWARDZERO;
	case VFP_FE_UPWARD:
		return FE_UPWARD;
	case VFP_FE_DOWNWARD:
		return FE_DOWNWARD;
	}
}
int feclearexcept(int __excepts)
{
	if (_libc_arm_fpu_present)
		__vfp_feclearexcept(__excepts);
	__softfp_feclearexcept(__excepts);
	return (0);
}
int fegetexceptflag(fexcept_t *__flagp, int __excepts)
{
	fexcept_t __vfp_flagp;
	__vfp_flagp = 0;
	if (_libc_arm_fpu_present)
		__vfp_fegetexceptflag(&__vfp_flagp, __excepts);
	__softfp_fegetexceptflag(__flagp, __excepts);
	*__flagp |= __vfp_flagp;
	return (0);
}
int fesetexceptflag(const fexcept_t *__flagp, int __excepts)
{
	if (_libc_arm_fpu_present)
		__vfp_fesetexceptflag(__flagp, __excepts);
	__softfp_fesetexceptflag(__flagp, __excepts);
	return (0);
}
int feraiseexcept(int __excepts)
{
	if (_libc_arm_fpu_present)
		__vfp_feraiseexcept(__excepts);
	__softfp_feraiseexcept(__excepts);
	return (0);
}
int fetestexcept(int __excepts)
{
	int __got_excepts;
	__got_excepts = 0;
	if (_libc_arm_fpu_present)
		__got_excepts = __vfp_fetestexcept(__excepts);
	__got_excepts |= __softfp_fetestexcept(__excepts);
	return (__got_excepts);
}
int fegetround(void)
{
	if (_libc_arm_fpu_present)
		return __softfp_round_from_vfp(__vfp_fegetround());
	return __softfp_fegetround();
}
int fesetround(int __round)
{
	if (_libc_arm_fpu_present)
		__vfp_fesetround(__softfp_round_to_vfp(__round));
	__softfp_fesetround(__round);
	return (0);
}
int fegetenv(fenv_t *__envp)
{
	fenv_t __vfp_envp;
	__vfp_envp = 0;
	if (_libc_arm_fpu_present)
		__vfp_fegetenv(&__vfp_envp);
	__softfp_fegetenv(__envp);
	*__envp |= __vfp_envp;
	return (0);
}
int feholdexcept(fenv_t *__envp)
{
	fenv_t __vfp_envp;
	__vfp_envp = 0;
	if (_libc_arm_fpu_present)
		__vfp_feholdexcept(&__vfp_envp);
	__softfp_feholdexcept(__envp);
	*__envp |= __vfp_envp;
	return (0);
}
int fesetenv(const fenv_t *__envp)
{
	if (_libc_arm_fpu_present)
		__vfp_fesetenv(__envp);
	__softfp_fesetenv(__envp);
	return (0);
}
int feupdateenv(const fenv_t *__envp)
{
	if (_libc_arm_fpu_present)
		__vfp_feupdateenv(__envp);
	__softfp_feupdateenv(__envp);
	return (0);
}
int feenableexcept(int __mask)
{
	int __unmasked;
	__unmasked = 0;
	if (_libc_arm_fpu_present)
		__unmasked = __vfp_feenableexcept(__mask);
	__unmasked |= __softfp_feenableexcept(__mask);
	return (__unmasked);
}
int fedisableexcept(int __mask)
{
	int __unmasked;
	__unmasked = 0;
	if (_libc_arm_fpu_present)
		__unmasked = __vfp_fedisableexcept(__mask);
	__unmasked |= __softfp_fedisableexcept(__mask);
	return (__unmasked);
}
int fegetexcept(void)
{
	int __unmasked;
	__unmasked = 0;
	if (_libc_arm_fpu_present)
		__unmasked = __vfp_fegetexcept();
	__unmasked |= __softfp_fegetexcept();
	return (__unmasked);
}
#endif