/**************************************************************************/
/* mmglue: midipix architecture- and target-specific bits for musl libc */
/* Copyright (C) 2013--2023 SysDeer Technologies, LLC */
/* Released under the Standard MIT License; see COPYING.MMGLUE. */
/**************************************************************************/
.file "fenv.S"
/* common exception bits */
#define FE_INVALID (1 << 0)
#define FE_DENORM (1 << 1)
#define FE_DIVBYZERO (1 << 2)
#define FE_OVERFLOW (1 << 3)
#define FE_UNDERFLOW (1 << 4)
#define FE_INEXACT (1 << 5)
#define FE_ALL_EXCEPT (0x3F)
/* X87 FPU rounding control bits */
#define FPU_RC_TONEAREST (0x0 << 10)
#define FPU_RC_DOWNWARD (0x1 << 10)
#define FPU_RC_UPWARD (0x2 << 10)
#define FPU_RC_TOWARDZERO (0x3 << 10)
#define FPU_RC_MASK FPU_RC_TOWARDZERO
/* X87 FPU control word */
#define FPU_CW_MASK (0xFFFF)
#define FPU_CW_RC_MASK (FPU_CW_MASK ^ FPU_RC_MASK)
/* MXCSR rounding control bits */
#define MXCSR_RC_TONEAREST (0X0 << 13)
#define MXCSR_RC_DOWNWARD (0x1 << 13)
#define MXCSR_RC_UPWARD (0x2 << 13)
#define MXCSR_RC_TOWARDZERO (0x3 << 13)
#define MXCSR_RC_MASK MXCSR_RC_TOWARDZERO
/* MXCSR control word */
#define MXCSR_CW_MASK (0xFFFF)
#define MXCSR_CW_RC_MASK (MXCSR_CW_MASK ^ MXCSR_RC_MASK)
/* fenv_At struct definition */
#define FPUCW_MEMBER_OFFSET (0x0)
#define MXCSR_MEMBER_OFFSET (0x1C)
/* user rounding control flags */
#define FE_RC_TONEAREST FPU_RC_TONEAREST
#define FE_RC_DOWNWARD FPU_RC_DOWNWARD
#define FE_RC_UPWARD FPU_RC_UPWARD
#define FE_RC_TOWARDZERO FPU_RC_TOWARDZERO
#define FE_RC_MASK FPU_RC_MASK
/* user default float environment */
#define FE_DFL_ENV (-1)
/******************************************************/
/* */
/* FSTCW: check for pending floating-point */
/* exceptions, then store FPU control word. */
/* */
/* FNSTCW: store FPU control word without first */
/* checking for pending floating-point */
/* exceptions. */
/* */
/* FSTSW: check for pending floating-point */
/* exceptions, then store FPU status word. */
/* */
/* FNSTSW: store FPU status word without first */
/* checking for pending floating-point */
/* exceptions. */
/* */
/* FCLEX: check for pending floating-point */
/* exceptions, then clear FPU exception */
/* flags. */
/* */
/* FNCLEX: clear FPU exception flags without first */
/* checking for pending floating-point */
/* exceptions. */
/* */
/* FLDCW: set the FPU Control Word from memory. */
/* */
/* FLDENV: set the FPU Environment from memory. */
/* */
/* STMXCSR: store the state of the MXCSR register; */
/* the register's reserved bits are set to */
/* zero in the destination. */
/* */
/* LDMXCSR: set the MXCSR environment from memory. */
/* */
/******************************************************/
/**********************************************/
/* int fetestexcept(int exflags); */
/* */
/* query which exception flags, as indicated */
/* by the exflags mask, are currently set. */
/* */
/**********************************************/
.text
.def fetestexcept; .scl 2; .type 32; .endef
.global fetestexcept
.cfi_startproc;
fetestexcept:
and $FE_ALL_EXCEPT,%ecx /* normalize exflags */
push %r10 /* temporary state var */
stmxcsr (%rsp) /* store mxcsr state */
pop %rdx /* copy state to rdx */
fnstsw %ax /* store fpu state */
or %edx,%eax /* fpu+mxcsr state */
and %ecx,%eax /* desired flags only */
ret
.cfi_endproc
/**********************************************/
/* int feclearexcept(int exflags); */
/* */
/* clear specific fpu exception flags, as */
/* indicated by the exflags mask. */
/* */
/**********************************************/
.text
.def feclearexcept; .scl 2; .type 32; .endef
.global feclearexcept
.cfi_startproc;
feclearexcept:
fnstsw %ax /* store fpu state */
and $FE_ALL_EXCEPT,%ecx /* normalize exflags */
and $FE_ALL_EXCEPT,%eax /* normalize fpu state */
test %ecx,%eax /* test exflags,state */
jz 1f /* state already clear? */
fnclex /* clear fpu state */
1: push %rdx /* temporary state var */
stmxcsr (%rsp) /* store mxcsr state */
pop %rdx /* copy state to rdx */
or %eax,%edx /* fpu+mxcsr state */
test %ecx,%edx /* test exflags,state */
jz 1f /* state already clear? */
not %ecx /* clear desired flags- */
and %ecx,%edx /* -from mxcsr state */
push %rdx /* modified mxcsr var */
ldmxcsr (%rsp) /* update mxcsr */
pop %rdx /* dealloc mxcsr var */
1: xor %eax,%eax /* ret zero either way */
ret
.cfi_endproc
/**********************************************/
/* int feraiseexcept(int exflags); */
/* */
/* set specific fpu exception flags, as */
/* indicated by the exflags mask. */
/* */
/* it is enough to set the flags only in */
/* either mxcsr or the [legacy] x87 fpu, */
/* specifically since fetestexcept() does */
/* take both into account. */
/* */
/**********************************************/
.text
.def feraiseexcept; .scl 2; .type 32; .endef
.global feraiseexcept
.cfi_startproc;
feraiseexcept:
and $FE_ALL_EXCEPT,%ecx /* normalize exflags */
stmxcsr -0x8(%rsp) /* store mxcsr state */
or %ecx,-0x8(%rsp) /* set desired flags */
ldmxcsr -0x8(%rsp) /* update mxcsr */
xor %eax,%eax
ret
.cfi_endproc
/**********************************************/
/* int fegetround(void); */
/* */
/* obtain the current rounding mode, which */
/* should be one of: round-to-neareset, */
/* round-downwards, round-upwards, or */
/* round-toward-zero. */
/* */
/* when we set the rounding mode we set both */
/* mxcsr and the [legacy] x87 fpu, and it */
/* thus suffices to obtain the mode from */
/* either, which is mxcsr in our case. */
/**********************************************/
.text
.def fegetround; .scl 2; .type 32; .endef
.global fegetround
.cfi_startproc;
fegetround:
push %rdx /* temporary state var */
stmxcsr (%rsp) /* store mxcsr state */
pop %rax /* copy state to rax */
and $MXCSR_RC_MASK,%rax /* MXCSR RC bits only */
shr $0x3,%rax /* MXCSR --> FE bits */
ret
.cfi_endproc
/**********************************************/
/* int fesetround(int rcmode); */
/* */
/* set the current rounding mode, which */
/* should be one of: round-to-neareset, */
/* round-downwards, round-upwards, or */
/* round-toward-zero. */
/**********************************************/
.text
.def __fesetround; .scl 2; .type 32; .endef
.global __fesetround
.cfi_startproc;
__fesetround:
push %rdx /* alloc control var */
and $FE_RC_MASK,%ecx /* normalize rcarg */
fnstcw (%rsp) /* current control word */
andw $FPU_CW_RC_MASK,(%rsp) /* control word only */
or %ecx,(%rsp) /* set rc bits to rcarg */
fldcw (%rsp) /* set fpu control word */
shl $0x3,%ecx /* rcarg: FPU --> MXCSR */
stmxcsr (%rsp) /* current mxcsr reg */
andw $MXCSR_CW_RC_MASK,(%rsp) /* control word only */
or %ecx,(%rsp) /* set rc bits to rcarg */
ldmxcsr (%rsp) /* set mxcsr ctrl word */
pop %rdx /* dealloc control var */
xor %eax,%eax
ret
.cfi_endproc
/**********************************************/
/* int fegetenv(fenv_t *); */
/* */
/* popualte a structure of type fenv_t with */
/* the current X87 FPU environmtnet (fnstenv) */
/* as well as MXCSR environment (stmxcsr). */
/**********************************************/
.text
.def fegetenv; .scl 2; .type 32; .endef
.global fegetenv
.cfi_startproc;
fegetenv:
fnstenv FPUCW_MEMBER_OFFSET(%rcx)
stmxcsr MXCSR_MEMBER_OFFSET(%rcx)
xor %eax,%eax
ret
.cfi_endproc
/**********************************************/
/* int fesetenv(cont fenv_t *); */
/* */
/* (re)initialize the X87 FPU and MXCSR */
/* environments from a user structure of */
/* type fenv_t. */
/* */
/* if the pointer to the fenv_t structure is */
/* FE_DFL_ENV, reset the X87 FPU and MXCSR */
/* environments to theit default values as */
/* defined by the architecture. */
/* */
/* .__control_word = 0x037f, */
/* .__unused1 = N/A, */
/* .__status_word = 0x0, */
/* .__unused2 = N/A, */
/* .__tags = 0xffff, */
/* .__unused3 = N/A, */
/* .__eip = 0x0, */
/* .__cs_selector = 0x0, */
/* .__opcode = 0x0, */
/* .__unused4 = N/A, */
/* .__data_offset = 0x0, */
/* .__data_selector = 0x0, */
/* .__unused5 = N/A, */
/* .__mxcsr = 0x1f80 */
/* */
/**********************************************/
.text
.def fesetenv; .scl 2; .type 32; .endef
.global fesetenv
.cfi_startproc;
fesetenv:
/* prolog */
xor %eax,%eax
/* use fenv_t or reset to default values? */
cmpq $FE_DFL_ENV,%rcx
jz 1f
/* use the user-provided fenv_t */
fldenv FPUCW_MEMBER_OFFSET(%rcx)
ldmxcsr MXCSR_MEMBER_OFFSET(%rcx)
ret
/* reset environments to the architecture-defined defaults */
1: subq $0x20,%rsp
/* X87 FPU */
movq $0x037f,(0x0)(%rsp)
movq $0xffff,(0x8)(%rsp)
movq $0x0,(0x10)(%rsp)
movq $0x0,(0x18)(%rsp)
fldenv (%rsp)
/* MXCSR */
movq $0x1f80,(%rsp)
ldmxcsr (%rsp)
/* all done */
addq $0x20,%rsp
ret
.cfi_endproc
.section .got$fetestexcept
.global __imp_fetestexcept
__imp_fetestexcept:
.quad fetestexcept
.linkonce discard
.section .got$feclearexcept
.global __imp_feclearexcept
__imp_feclearexcept:
.quad feclearexcept
.linkonce discard
.section .got$feraiseexcept
.global __imp_feraiseexcept
__imp_feraiseexcept:
.quad feraiseexcept
.linkonce discard
.section .got$fegetround
.global __imp_fegetround
__imp_fegetround:
.quad fegetround
.linkonce discard
.section .got$__fesetround
.global __imp___fesetround
__imp___fesetround:
.quad __fesetround
.linkonce discard
.section .got$fegetenv
.global __imp_fegetenv
__imp_fegetenv:
.quad fegetenv
.linkonce discard
.section .got$fesetenv
.global __imp_fesetenv
__imp_fesetenv:
.quad fesetenv
.linkonce discard