summaryrefslogtreecommitdiff
path: root/target
diff options
context:
space:
mode:
Diffstat (limited to 'target')
-rw-r--r--target/i386/cpu.c92
-rw-r--r--target/i386/cpu.h26
-rw-r--r--target/i386/kvm.c78
-rw-r--r--target/i386/machine.c38
-rw-r--r--target/m68k/fpu_helper.c61
-rw-r--r--target/m68k/helper.h11
-rw-r--r--target/m68k/softfloat.c1637
-rw-r--r--target/m68k/softfloat.h11
-rw-r--r--target/m68k/softfloat_fpsp_tables.h267
-rw-r--r--target/m68k/translate.c38
10 files changed, 2257 insertions, 2 deletions
diff --git a/target/i386/cpu.c b/target/i386/cpu.c
index 01607dd046..6bb4ce8719 100644
--- a/target/i386/cpu.c
+++ b/target/i386/cpu.c
@@ -174,7 +174,32 @@
#define L2_ITLB_4K_ASSOC 4
#define L2_ITLB_4K_ENTRIES 512
-
+/* CPUID Leaf 0x14 constants: */
+#define INTEL_PT_MAX_SUBLEAF 0x1
+/*
+ * bit[00]: IA32_RTIT_CTL.CR3 filter can be set to 1 and IA32_RTIT_CR3_MATCH
+ * MSR can be accessed;
+ * bit[01]: Support Configurable PSB and Cycle-Accurate Mode;
+ * bit[02]: Support IP Filtering, TraceStop filtering, and preservation
+ * of Intel PT MSRs across warm reset;
+ * bit[03]: Support MTC timing packet and suppression of COFI-based packets;
+ */
+#define INTEL_PT_MINIMAL_EBX 0xf
+/*
+ * bit[00]: Tracing can be enabled with IA32_RTIT_CTL.ToPA = 1 and
+ * IA32_RTIT_OUTPUT_BASE and IA32_RTIT_OUTPUT_MASK_PTRS MSRs can be
+ * accessed;
+ * bit[01]: ToPA tables can hold any number of output entries, up to the
+ * maximum allowed by the MaskOrTableOffset field of
+ * IA32_RTIT_OUTPUT_MASK_PTRS;
+ * bit[02]: Support Single-Range Output scheme;
+ */
+#define INTEL_PT_MINIMAL_ECX 0x7
+#define INTEL_PT_ADDR_RANGES_NUM 0x2 /* Number of configurable address ranges */
+#define INTEL_PT_ADDR_RANGES_NUM_MASK 0x3
+#define INTEL_PT_MTC_BITMAP (0x0249 << 16) /* Support ART(0,3,6,9) */
+#define INTEL_PT_CYCLE_BITMAP 0x1fff /* Support 0,2^(0~11) */
+#define INTEL_PT_PSB_BITMAP (0x003f << 16) /* Support 2K,4K,8K,16K,32K,64K */
static void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1,
uint32_t vendor2, uint32_t vendor3)
@@ -360,6 +385,20 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = {
.cpuid_eax = KVM_CPUID_FEATURES, .cpuid_reg = R_EAX,
.tcg_features = TCG_KVM_FEATURES,
},
+ [FEAT_KVM_HINTS] = {
+ .feat_names = {
+ "kvm-hint-dedicated", NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ },
+ .cpuid_eax = KVM_CPUID_FEATURES, .cpuid_reg = R_EDX,
+ .tcg_features = TCG_KVM_FEATURES,
+ },
[FEAT_HYPERV_EAX] = {
.feat_names = {
NULL /* hv_msr_vp_runtime_access */, NULL /* hv_msr_time_refcount_access */,
@@ -429,7 +468,7 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = {
NULL, NULL, "mpx", NULL,
"avx512f", "avx512dq", "rdseed", "adx",
"smap", "avx512ifma", "pcommit", "clflushopt",
- "clwb", NULL, "avx512pf", "avx512er",
+ "clwb", "intel-pt", "avx512pf", "avx512er",
"avx512cd", "sha-ni", "avx512bw", "avx512vl",
},
.cpuid_eax = 7,
@@ -3454,6 +3493,27 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
}
break;
}
+ case 0x14: {
+ /* Intel Processor Trace Enumeration */
+ *eax = 0;
+ *ebx = 0;
+ *ecx = 0;
+ *edx = 0;
+ if (!(env->features[FEAT_7_0_EBX] & CPUID_7_0_EBX_INTEL_PT) ||
+ !kvm_enabled()) {
+ break;
+ }
+
+ if (count == 0) {
+ *eax = INTEL_PT_MAX_SUBLEAF;
+ *ebx = INTEL_PT_MINIMAL_EBX;
+ *ecx = INTEL_PT_MINIMAL_ECX;
+ } else if (count == 1) {
+ *eax = INTEL_PT_MTC_BITMAP | INTEL_PT_ADDR_RANGES_NUM;
+ *ebx = INTEL_PT_PSB_BITMAP | INTEL_PT_CYCLE_BITMAP;
+ }
+ break;
+ }
case 0x40000000:
/*
* CPUID code in kvm_arch_init_vcpu() ignores stuff
@@ -4097,6 +4157,34 @@ static int x86_cpu_filter_features(X86CPU *cpu)
}
}
+ if ((env->features[FEAT_7_0_EBX] & CPUID_7_0_EBX_INTEL_PT) &&
+ kvm_enabled()) {
+ KVMState *s = CPU(cpu)->kvm_state;
+ uint32_t eax_0 = kvm_arch_get_supported_cpuid(s, 0x14, 0, R_EAX);
+ uint32_t ebx_0 = kvm_arch_get_supported_cpuid(s, 0x14, 0, R_EBX);
+ uint32_t ecx_0 = kvm_arch_get_supported_cpuid(s, 0x14, 0, R_ECX);
+ uint32_t eax_1 = kvm_arch_get_supported_cpuid(s, 0x14, 1, R_EAX);
+ uint32_t ebx_1 = kvm_arch_get_supported_cpuid(s, 0x14, 1, R_EBX);
+
+ if (!eax_0 ||
+ ((ebx_0 & INTEL_PT_MINIMAL_EBX) != INTEL_PT_MINIMAL_EBX) ||
+ ((ecx_0 & INTEL_PT_MINIMAL_ECX) != INTEL_PT_MINIMAL_ECX) ||
+ ((eax_1 & INTEL_PT_MTC_BITMAP) != INTEL_PT_MTC_BITMAP) ||
+ ((eax_1 & INTEL_PT_ADDR_RANGES_NUM_MASK) <
+ INTEL_PT_ADDR_RANGES_NUM) ||
+ ((ebx_1 & (INTEL_PT_PSB_BITMAP | INTEL_PT_CYCLE_BITMAP)) !=
+ (INTEL_PT_PSB_BITMAP | INTEL_PT_CYCLE_BITMAP))) {
+ /*
+ * Processor Trace capabilities aren't configurable, so if the
+ * host can't emulate the capabilities we report on
+ * cpu_x86_cpuid(), intel-pt can't be enabled on the current host.
+ */
+ env->features[FEAT_7_0_EBX] &= ~CPUID_7_0_EBX_INTEL_PT;
+ cpu->filtered_features[FEAT_7_0_EBX] |= CPUID_7_0_EBX_INTEL_PT;
+ rv = 1;
+ }
+ }
+
return rv;
}
diff --git a/target/i386/cpu.h b/target/i386/cpu.h
index 254e557bb8..2e2bab5ff3 100644
--- a/target/i386/cpu.h
+++ b/target/i386/cpu.h
@@ -417,6 +417,21 @@ typedef enum X86Seg {
#define MSR_MC0_ADDR 0x402
#define MSR_MC0_MISC 0x403
+#define MSR_IA32_RTIT_OUTPUT_BASE 0x560
+#define MSR_IA32_RTIT_OUTPUT_MASK 0x561
+#define MSR_IA32_RTIT_CTL 0x570
+#define MSR_IA32_RTIT_STATUS 0x571
+#define MSR_IA32_RTIT_CR3_MATCH 0x572
+#define MSR_IA32_RTIT_ADDR0_A 0x580
+#define MSR_IA32_RTIT_ADDR0_B 0x581
+#define MSR_IA32_RTIT_ADDR1_A 0x582
+#define MSR_IA32_RTIT_ADDR1_B 0x583
+#define MSR_IA32_RTIT_ADDR2_A 0x584
+#define MSR_IA32_RTIT_ADDR2_B 0x585
+#define MSR_IA32_RTIT_ADDR3_A 0x586
+#define MSR_IA32_RTIT_ADDR3_B 0x587
+#define MAX_RTIT_ADDRS 8
+
#define MSR_EFER 0xc0000080
#define MSR_EFER_SCE (1 << 0)
@@ -473,6 +488,7 @@ typedef enum FeatureWord {
FEAT_8000_0008_EBX, /* CPUID[8000_0008].EBX */
FEAT_C000_0001_EDX, /* CPUID[C000_0001].EDX */
FEAT_KVM, /* CPUID[4000_0001].EAX (KVM_CPUID_FEATURES) */
+ FEAT_KVM_HINTS, /* CPUID[4000_0001].EDX */
FEAT_HYPERV_EAX, /* CPUID[4000_0003].EAX */
FEAT_HYPERV_EBX, /* CPUID[4000_0003].EBX */
FEAT_HYPERV_EDX, /* CPUID[4000_0003].EDX */
@@ -642,6 +658,7 @@ typedef uint32_t FeatureWordArray[FEATURE_WORDS];
#define CPUID_7_0_EBX_PCOMMIT (1U << 22) /* Persistent Commit */
#define CPUID_7_0_EBX_CLFLUSHOPT (1U << 23) /* Flush a Cache Line Optimized */
#define CPUID_7_0_EBX_CLWB (1U << 24) /* Cache Line Write Back */
+#define CPUID_7_0_EBX_INTEL_PT (1U << 25) /* Intel Processor Trace */
#define CPUID_7_0_EBX_AVX512PF (1U << 26) /* AVX-512 Prefetch */
#define CPUID_7_0_EBX_AVX512ER (1U << 27) /* AVX-512 Exponential and Reciprocal */
#define CPUID_7_0_EBX_AVX512CD (1U << 28) /* AVX-512 Conflict Detection */
@@ -668,6 +685,8 @@ typedef uint32_t FeatureWordArray[FEATURE_WORDS];
#define CPUID_7_0_EDX_AVX512_4FMAPS (1U << 3) /* AVX512 Multiply Accumulation Single Precision */
#define CPUID_7_0_EDX_SPEC_CTRL (1U << 26) /* Speculation Control */
+#define KVM_HINTS_DEDICATED (1U << 0)
+
#define CPUID_8000_0008_EBX_IBPB (1U << 12) /* Indirect Branch Prediction Barrier */
#define CPUID_XSAVE_XSAVEOPT (1U << 0)
@@ -1156,6 +1175,13 @@ typedef struct CPUX86State {
uint64_t msr_hv_stimer_config[HV_STIMER_COUNT];
uint64_t msr_hv_stimer_count[HV_STIMER_COUNT];
+ uint64_t msr_rtit_ctrl;
+ uint64_t msr_rtit_status;
+ uint64_t msr_rtit_output_base;
+ uint64_t msr_rtit_output_mask;
+ uint64_t msr_rtit_cr3_match;
+ uint64_t msr_rtit_addrs[MAX_RTIT_ADDRS];
+
/* exception/interrupt handling */
int error_code;
int exception_is_int;
diff --git a/target/i386/kvm.c b/target/i386/kvm.c
index a53735f266..d23fff12f5 100644
--- a/target/i386/kvm.c
+++ b/target/i386/kvm.c
@@ -384,6 +384,9 @@ uint32_t kvm_arch_get_supported_cpuid(KVMState *s, uint32_t function,
if (!kvm_irqchip_in_kernel()) {
ret &= ~(1U << KVM_FEATURE_PV_UNHALT);
}
+ } else if (function == KVM_CPUID_FEATURES && reg == R_EDX) {
+ ret |= KVM_HINTS_DEDICATED;
+ found = 1;
}
/* fallback for older kernels */
@@ -802,6 +805,7 @@ int kvm_arch_init_vcpu(CPUState *cs)
c = &cpuid_data.entries[cpuid_i++];
c->function = KVM_CPUID_FEATURES | kvm_base;
c->eax = env->features[FEAT_KVM];
+ c->edx = env->features[FEAT_KVM_HINTS];
}
cpu_x86_cpuid(env, 0, 0, &limit, &unused, &unused, &unused);
@@ -866,6 +870,29 @@ int kvm_arch_init_vcpu(CPUState *cs)
c = &cpuid_data.entries[cpuid_i++];
}
break;
+ case 0x14: {
+ uint32_t times;
+
+ c->function = i;
+ c->index = 0;
+ c->flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX;
+ cpu_x86_cpuid(env, i, 0, &c->eax, &c->ebx, &c->ecx, &c->edx);
+ times = c->eax;
+
+ for (j = 1; j <= times; ++j) {
+ if (cpuid_i == KVM_MAX_CPUID_ENTRIES) {
+ fprintf(stderr, "cpuid_data is full, no space for "
+ "cpuid(eax:0x14,ecx:0x%x)\n", j);
+ abort();
+ }
+ c = &cpuid_data.entries[cpuid_i++];
+ c->function = i;
+ c->index = j;
+ c->flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX;
+ cpu_x86_cpuid(env, i, j, &c->eax, &c->ebx, &c->ecx, &c->edx);
+ }
+ break;
+ }
default:
c->function = i;
c->flags = 0;
@@ -1795,6 +1822,25 @@ static int kvm_put_msrs(X86CPU *cpu, int level)
kvm_msr_entry_add(cpu, MSR_MTRRphysMask(i), mask);
}
}
+ if (env->features[FEAT_7_0_EBX] & CPUID_7_0_EBX_INTEL_PT) {
+ int addr_num = kvm_arch_get_supported_cpuid(kvm_state,
+ 0x14, 1, R_EAX) & 0x7;
+
+ kvm_msr_entry_add(cpu, MSR_IA32_RTIT_CTL,
+ env->msr_rtit_ctrl);
+ kvm_msr_entry_add(cpu, MSR_IA32_RTIT_STATUS,
+ env->msr_rtit_status);
+ kvm_msr_entry_add(cpu, MSR_IA32_RTIT_OUTPUT_BASE,
+ env->msr_rtit_output_base);
+ kvm_msr_entry_add(cpu, MSR_IA32_RTIT_OUTPUT_MASK,
+ env->msr_rtit_output_mask);
+ kvm_msr_entry_add(cpu, MSR_IA32_RTIT_CR3_MATCH,
+ env->msr_rtit_cr3_match);
+ for (i = 0; i < addr_num; i++) {
+ kvm_msr_entry_add(cpu, MSR_IA32_RTIT_ADDR0_A + i,
+ env->msr_rtit_addrs[i]);
+ }
+ }
/* Note: MSR_IA32_FEATURE_CONTROL is written separately, see
* kvm_put_msr_feature_control. */
@@ -2111,6 +2157,20 @@ static int kvm_get_msrs(X86CPU *cpu)
}
}
+ if (env->features[FEAT_7_0_EBX] & CPUID_7_0_EBX_INTEL_PT) {
+ int addr_num =
+ kvm_arch_get_supported_cpuid(kvm_state, 0x14, 1, R_EAX) & 0x7;
+
+ kvm_msr_entry_add(cpu, MSR_IA32_RTIT_CTL, 0);
+ kvm_msr_entry_add(cpu, MSR_IA32_RTIT_STATUS, 0);
+ kvm_msr_entry_add(cpu, MSR_IA32_RTIT_OUTPUT_BASE, 0);
+ kvm_msr_entry_add(cpu, MSR_IA32_RTIT_OUTPUT_MASK, 0);
+ kvm_msr_entry_add(cpu, MSR_IA32_RTIT_CR3_MATCH, 0);
+ for (i = 0; i < addr_num; i++) {
+ kvm_msr_entry_add(cpu, MSR_IA32_RTIT_ADDR0_A + i, 0);
+ }
+ }
+
ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_MSRS, cpu->kvm_msr_buf);
if (ret < 0) {
return ret;
@@ -2354,6 +2414,24 @@ static int kvm_get_msrs(X86CPU *cpu)
case MSR_IA32_SPEC_CTRL:
env->spec_ctrl = msrs[i].data;
break;
+ case MSR_IA32_RTIT_CTL:
+ env->msr_rtit_ctrl = msrs[i].data;
+ break;
+ case MSR_IA32_RTIT_STATUS:
+ env->msr_rtit_status = msrs[i].data;
+ break;
+ case MSR_IA32_RTIT_OUTPUT_BASE:
+ env->msr_rtit_output_base = msrs[i].data;
+ break;
+ case MSR_IA32_RTIT_OUTPUT_MASK:
+ env->msr_rtit_output_mask = msrs[i].data;
+ break;
+ case MSR_IA32_RTIT_CR3_MATCH:
+ env->msr_rtit_cr3_match = msrs[i].data;
+ break;
+ case MSR_IA32_RTIT_ADDR0_A ... MSR_IA32_RTIT_ADDR3_B:
+ env->msr_rtit_addrs[index - MSR_IA32_RTIT_ADDR0_A] = msrs[i].data;
+ break;
}
}
diff --git a/target/i386/machine.c b/target/i386/machine.c
index 9432496cbd..bd2d82e91b 100644
--- a/target/i386/machine.c
+++ b/target/i386/machine.c
@@ -856,6 +856,43 @@ static const VMStateDescription vmstate_spec_ctrl = {
}
};
+static bool intel_pt_enable_needed(void *opaque)
+{
+ X86CPU *cpu = opaque;
+ CPUX86State *env = &cpu->env;
+ int i;
+
+ if (env->msr_rtit_ctrl || env->msr_rtit_status ||
+ env->msr_rtit_output_base || env->msr_rtit_output_mask ||
+ env->msr_rtit_cr3_match) {
+ return true;
+ }
+
+ for (i = 0; i < MAX_RTIT_ADDRS; i++) {
+ if (env->msr_rtit_addrs[i]) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static const VMStateDescription vmstate_msr_intel_pt = {
+ .name = "cpu/intel_pt",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = intel_pt_enable_needed,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64(env.msr_rtit_ctrl, X86CPU),
+ VMSTATE_UINT64(env.msr_rtit_status, X86CPU),
+ VMSTATE_UINT64(env.msr_rtit_output_base, X86CPU),
+ VMSTATE_UINT64(env.msr_rtit_output_mask, X86CPU),
+ VMSTATE_UINT64(env.msr_rtit_cr3_match, X86CPU),
+ VMSTATE_UINT64_ARRAY(env.msr_rtit_addrs, X86CPU, MAX_RTIT_ADDRS),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
VMStateDescription vmstate_x86_cpu = {
.name = "cpu",
.version_id = 12,
@@ -977,6 +1014,7 @@ VMStateDescription vmstate_x86_cpu = {
#endif
&vmstate_spec_ctrl,
&vmstate_mcg_ext_ctl,
+ &vmstate_msr_intel_pt,
NULL
}
};
diff --git a/target/m68k/fpu_helper.c b/target/m68k/fpu_helper.c
index 62cbb0dff1..6eeffdf9bb 100644
--- a/target/m68k/fpu_helper.c
+++ b/target/m68k/fpu_helper.c
@@ -592,3 +592,64 @@ void HELPER(ftentox)(CPUM68KState *env, FPReg *res, FPReg *val)
{
res->d = floatx80_tentox(val->d, &env->fp_status);
}
+
+void HELPER(ftan)(CPUM68KState *env, FPReg *res, FPReg *val)
+{
+ res->d = floatx80_tan(val->d, &env->fp_status);
+}
+
+void HELPER(fsin)(CPUM68KState *env, FPReg *res, FPReg *val)
+{
+ res->d = floatx80_sin(val->d, &env->fp_status);
+}
+
+void HELPER(fcos)(CPUM68KState *env, FPReg *res, FPReg *val)
+{
+ res->d = floatx80_cos(val->d, &env->fp_status);
+}
+
+void HELPER(fsincos)(CPUM68KState *env, FPReg *res0, FPReg *res1, FPReg *val)
+{
+ floatx80 a = val->d;
+ /* If res0 and res1 specify the same floating-point data register,
+ * the sine result is stored in the register, and the cosine
+ * result is discarded.
+ */
+ res1->d = floatx80_cos(a, &env->fp_status);
+ res0->d = floatx80_sin(a, &env->fp_status);
+}
+
+void HELPER(fatan)(CPUM68KState *env, FPReg *res, FPReg *val)
+{
+ res->d = floatx80_atan(val->d, &env->fp_status);
+}
+
+void HELPER(fasin)(CPUM68KState *env, FPReg *res, FPReg *val)
+{
+ res->d = floatx80_asin(val->d, &env->fp_status);
+}
+
+void HELPER(facos)(CPUM68KState *env, FPReg *res, FPReg *val)
+{
+ res->d = floatx80_acos(val->d, &env->fp_status);
+}
+
+void HELPER(fatanh)(CPUM68KState *env, FPReg *res, FPReg *val)
+{
+ res->d = floatx80_atanh(val->d, &env->fp_status);
+}
+
+void HELPER(ftanh)(CPUM68KState *env, FPReg *res, FPReg *val)
+{
+ res->d = floatx80_tanh(val->d, &env->fp_status);
+}
+
+void HELPER(fsinh)(CPUM68KState *env, FPReg *res, FPReg *val)
+{
+ res->d = floatx80_sinh(val->d, &env->fp_status);
+}
+
+void HELPER(fcosh)(CPUM68KState *env, FPReg *res, FPReg *val)
+{
+ res->d = floatx80_cosh(val->d, &env->fp_status);
+}
diff --git a/target/m68k/helper.h b/target/m68k/helper.h
index 9a9734c196..feee7be626 100644
--- a/target/m68k/helper.h
+++ b/target/m68k/helper.h
@@ -75,6 +75,17 @@ DEF_HELPER_3(flog2, void, env, fp, fp)
DEF_HELPER_3(fetox, void, env, fp, fp)
DEF_HELPER_3(ftwotox, void, env, fp, fp)
DEF_HELPER_3(ftentox, void, env, fp, fp)
+DEF_HELPER_3(ftan, void, env, fp, fp)
+DEF_HELPER_3(fsin, void, env, fp, fp)
+DEF_HELPER_3(fcos, void, env, fp, fp)
+DEF_HELPER_4(fsincos, void, env, fp, fp, fp)
+DEF_HELPER_3(fatan, void, env, fp, fp)
+DEF_HELPER_3(fasin, void, env, fp, fp)
+DEF_HELPER_3(facos, void, env, fp, fp)
+DEF_HELPER_3(fatanh, void, env, fp, fp)
+DEF_HELPER_3(ftanh, void, env, fp, fp)
+DEF_HELPER_3(fsinh, void, env, fp, fp)
+DEF_HELPER_3(fcosh, void, env, fp, fp)
DEF_HELPER_3(mac_move, void, env, i32, i32)
DEF_HELPER_3(macmulf, i64, env, i32, i32)
diff --git a/target/m68k/softfloat.c b/target/m68k/softfloat.c
index 4bd5b9e6b7..dffb371c71 100644
--- a/target/m68k/softfloat.c
+++ b/target/m68k/softfloat.c
@@ -23,6 +23,10 @@
#include "fpu/softfloat-macros.h"
#include "softfloat_fpsp_tables.h"
+#define pi_exp 0x4000
+#define piby2_exp 0x3FFF
+#define pi_sig LIT64(0xc90fdaa22168c235)
+
static floatx80 propagateFloatx80NaNOneArg(floatx80 a, float_status *status)
{
if (floatx80_is_signaling_nan(a, status)) {
@@ -1266,3 +1270,1636 @@ floatx80 floatx80_tentox(floatx80 a, float_status *status)
return a;
}
}
+
+/*----------------------------------------------------------------------------
+ | Tangent
+ *----------------------------------------------------------------------------*/
+
+floatx80 floatx80_tan(floatx80 a, float_status *status)
+{
+ flag aSign, xSign;
+ int32_t aExp, xExp;
+ uint64_t aSig, xSig;
+
+ int8_t user_rnd_mode, user_rnd_prec;
+
+ int32_t compact, l, n, j;
+ floatx80 fp0, fp1, fp2, fp3, fp4, fp5, invtwopi, twopi1, twopi2;
+ float32 twoto63;
+ flag endflag;
+
+ aSig = extractFloatx80Frac(a);
+ aExp = extractFloatx80Exp(a);
+ aSign = extractFloatx80Sign(a);
+
+ if (aExp == 0x7FFF) {
+ if ((uint64_t) (aSig << 1)) {
+ return propagateFloatx80NaNOneArg(a, status);
+ }
+ float_raise(float_flag_invalid, status);
+ return floatx80_default_nan(status);
+ }
+
+ if (aExp == 0 && aSig == 0) {
+ return packFloatx80(aSign, 0, 0);
+ }
+
+ user_rnd_mode = status->float_rounding_mode;
+ user_rnd_prec = status->floatx80_rounding_precision;
+ status->float_rounding_mode = float_round_nearest_even;
+ status->floatx80_rounding_precision = 80;
+
+ compact = floatx80_make_compact(aExp, aSig);
+
+ fp0 = a;
+
+ if (compact < 0x3FD78000 || compact > 0x4004BC7E) {
+ /* 2^(-40) > |X| > 15 PI */
+ if (compact > 0x3FFF8000) { /* |X| >= 15 PI */
+ /* REDUCEX */
+ fp1 = packFloatx80(0, 0, 0);
+ if (compact == 0x7FFEFFFF) {
+ twopi1 = packFloatx80(aSign ^ 1, 0x7FFE,
+ LIT64(0xC90FDAA200000000));
+ twopi2 = packFloatx80(aSign ^ 1, 0x7FDC,
+ LIT64(0x85A308D300000000));
+ fp0 = floatx80_add(fp0, twopi1, status);
+ fp1 = fp0;
+ fp0 = floatx80_add(fp0, twopi2, status);
+ fp1 = floatx80_sub(fp1, fp0, status);
+ fp1 = floatx80_add(fp1, twopi2, status);
+ }
+ loop:
+ xSign = extractFloatx80Sign(fp0);
+ xExp = extractFloatx80Exp(fp0);
+ xExp -= 0x3FFF;
+ if (xExp <= 28) {
+ l = 0;
+ endflag = 1;
+ } else {
+ l = xExp - 27;
+ endflag = 0;
+ }
+ invtwopi = packFloatx80(0, 0x3FFE - l,
+ LIT64(0xA2F9836E4E44152A)); /* INVTWOPI */
+ twopi1 = packFloatx80(0, 0x3FFF + l, LIT64(0xC90FDAA200000000));
+ twopi2 = packFloatx80(0, 0x3FDD + l, LIT64(0x85A308D300000000));
+
+ /* SIGN(INARG)*2^63 IN SGL */
+ twoto63 = packFloat32(xSign, 0xBE, 0);
+
+ fp2 = floatx80_mul(fp0, invtwopi, status);
+ fp2 = floatx80_add(fp2, float32_to_floatx80(twoto63, status),
+ status); /* THE FRACT PART OF FP2 IS ROUNDED */
+ fp2 = floatx80_sub(fp2, float32_to_floatx80(twoto63, status),
+ status); /* FP2 is N */
+ fp4 = floatx80_mul(twopi1, fp2, status); /* W = N*P1 */
+ fp5 = floatx80_mul(twopi2, fp2, status); /* w = N*P2 */
+ fp3 = floatx80_add(fp4, fp5, status); /* FP3 is P */
+ fp4 = floatx80_sub(fp4, fp3, status); /* W-P */
+ fp0 = floatx80_sub(fp0, fp3, status); /* FP0 is A := R - P */
+ fp4 = floatx80_add(fp4, fp5, status); /* FP4 is p = (W-P)+w */
+ fp3 = fp0; /* FP3 is A */
+ fp1 = floatx80_sub(fp1, fp4, status); /* FP1 is a := r - p */
+ fp0 = floatx80_add(fp0, fp1, status); /* FP0 is R := A+a */
+
+ if (endflag > 0) {
+ n = floatx80_to_int32(fp2, status);
+ goto tancont;
+ }
+ fp3 = floatx80_sub(fp3, fp0, status); /* A-R */
+ fp1 = floatx80_add(fp1, fp3, status); /* FP1 is r := (A-R)+a */
+ goto loop;
+ } else {
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_move(a, status);
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ }
+ } else {
+ fp1 = floatx80_mul(fp0, float64_to_floatx80(
+ make_float64(0x3FE45F306DC9C883), status),
+ status); /* X*2/PI */
+
+ n = floatx80_to_int32(fp1, status);
+ j = 32 + n;
+
+ fp0 = floatx80_sub(fp0, pi_tbl[j], status); /* X-Y1 */
+ fp0 = floatx80_sub(fp0, float32_to_floatx80(pi_tbl2[j], status),
+ status); /* FP0 IS R = (X-Y1)-Y2 */
+
+ tancont:
+ if (n & 1) {
+ /* NODD */
+ fp1 = fp0; /* R */
+ fp0 = floatx80_mul(fp0, fp0, status); /* S = R*R */
+ fp3 = float64_to_floatx80(make_float64(0x3EA0B759F50F8688),
+ status); /* Q4 */
+ fp2 = float64_to_floatx80(make_float64(0xBEF2BAA5A8924F04),
+ status); /* P3 */
+ fp3 = floatx80_mul(fp3, fp0, status); /* SQ4 */
+ fp2 = floatx80_mul(fp2, fp0, status); /* SP3 */
+ fp3 = floatx80_add(fp3, float64_to_floatx80(
+ make_float64(0xBF346F59B39BA65F), status),
+ status); /* Q3+SQ4 */
+ fp4 = packFloatx80(0, 0x3FF6, LIT64(0xE073D3FC199C4A00));
+ fp2 = floatx80_add(fp2, fp4, status); /* P2+SP3 */
+ fp3 = floatx80_mul(fp3, fp0, status); /* S(Q3+SQ4) */
+ fp2 = floatx80_mul(fp2, fp0, status); /* S(P2+SP3) */
+ fp4 = packFloatx80(0, 0x3FF9, LIT64(0xD23CD68415D95FA1));
+ fp3 = floatx80_add(fp3, fp4, status); /* Q2+S(Q3+SQ4) */
+ fp4 = packFloatx80(1, 0x3FFC, LIT64(0x8895A6C5FB423BCA));
+ fp2 = floatx80_add(fp2, fp4, status); /* P1+S(P2+SP3) */
+ fp3 = floatx80_mul(fp3, fp0, status); /* S(Q2+S(Q3+SQ4)) */
+ fp2 = floatx80_mul(fp2, fp0, status); /* S(P1+S(P2+SP3)) */
+ fp4 = packFloatx80(1, 0x3FFD, LIT64(0xEEF57E0DA84BC8CE));
+ fp3 = floatx80_add(fp3, fp4, status); /* Q1+S(Q2+S(Q3+SQ4)) */
+ fp2 = floatx80_mul(fp2, fp1, status); /* RS(P1+S(P2+SP3)) */
+ fp0 = floatx80_mul(fp0, fp3, status); /* S(Q1+S(Q2+S(Q3+SQ4))) */
+ fp1 = floatx80_add(fp1, fp2, status); /* R+RS(P1+S(P2+SP3)) */
+ fp0 = floatx80_add(fp0, float32_to_floatx80(
+ make_float32(0x3F800000), status),
+ status); /* 1+S(Q1+S(Q2+S(Q3+SQ4))) */
+
+ xSign = extractFloatx80Sign(fp1);
+ xExp = extractFloatx80Exp(fp1);
+ xSig = extractFloatx80Frac(fp1);
+ xSign ^= 1;
+ fp1 = packFloatx80(xSign, xExp, xSig);
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_div(fp0, fp1, status);
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ } else {
+ fp1 = floatx80_mul(fp0, fp0, status); /* S = R*R */
+ fp3 = float64_to_floatx80(make_float64(0x3EA0B759F50F8688),
+ status); /* Q4 */
+ fp2 = float64_to_floatx80(make_float64(0xBEF2BAA5A8924F04),
+ status); /* P3 */
+ fp3 = floatx80_mul(fp3, fp1, status); /* SQ4 */
+ fp2 = floatx80_mul(fp2, fp1, status); /* SP3 */
+ fp3 = floatx80_add(fp3, float64_to_floatx80(
+ make_float64(0xBF346F59B39BA65F), status),
+ status); /* Q3+SQ4 */
+ fp4 = packFloatx80(0, 0x3FF6, LIT64(0xE073D3FC199C4A00));
+ fp2 = floatx80_add(fp2, fp4, status); /* P2+SP3 */
+ fp3 = floatx80_mul(fp3, fp1, status); /* S(Q3+SQ4) */
+ fp2 = floatx80_mul(fp2, fp1, status); /* S(P2+SP3) */
+ fp4 = packFloatx80(0, 0x3FF9, LIT64(0xD23CD68415D95FA1));
+ fp3 = floatx80_add(fp3, fp4, status); /* Q2+S(Q3+SQ4) */
+ fp4 = packFloatx80(1, 0x3FFC, LIT64(0x8895A6C5FB423BCA));
+ fp2 = floatx80_add(fp2, fp4, status); /* P1+S(P2+SP3) */
+ fp3 = floatx80_mul(fp3, fp1, status); /* S(Q2+S(Q3+SQ4)) */
+ fp2 = floatx80_mul(fp2, fp1, status); /* S(P1+S(P2+SP3)) */
+ fp4 = packFloatx80(1, 0x3FFD, LIT64(0xEEF57E0DA84BC8CE));
+ fp3 = floatx80_add(fp3, fp4, status); /* Q1+S(Q2+S(Q3+SQ4)) */
+ fp2 = floatx80_mul(fp2, fp0, status); /* RS(P1+S(P2+SP3)) */
+ fp1 = floatx80_mul(fp1, fp3, status); /* S(Q1+S(Q2+S(Q3+SQ4))) */
+ fp0 = floatx80_add(fp0, fp2, status); /* R+RS(P1+S(P2+SP3)) */
+ fp1 = floatx80_add(fp1, float32_to_floatx80(
+ make_float32(0x3F800000), status),
+ status); /* 1+S(Q1+S(Q2+S(Q3+SQ4))) */
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_div(fp0, fp1, status);
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ }
+ }
+}
+
+/*----------------------------------------------------------------------------
+ | Sine
+ *----------------------------------------------------------------------------*/
+
+floatx80 floatx80_sin(floatx80 a, float_status *status)
+{
+ flag aSign, xSign;
+ int32_t aExp, xExp;
+ uint64_t aSig, xSig;
+
+ int8_t user_rnd_mode, user_rnd_prec;
+
+ int32_t compact, l, n, j;
+ floatx80 fp0, fp1, fp2, fp3, fp4, fp5, x, invtwopi, twopi1, twopi2;
+ float32 posneg1, twoto63;
+ flag adjn, endflag;
+
+ aSig = extractFloatx80Frac(a);
+ aExp = extractFloatx80Exp(a);
+ aSign = extractFloatx80Sign(a);
+
+ if (aExp == 0x7FFF) {
+ if ((uint64_t) (aSig << 1)) {
+ return propagateFloatx80NaNOneArg(a, status);
+ }
+ float_raise(float_flag_invalid, status);
+ return floatx80_default_nan(status);
+ }
+
+ if (aExp == 0 && aSig == 0) {
+ return packFloatx80(aSign, 0, 0);
+ }
+
+ adjn = 0;
+
+ user_rnd_mode = status->float_rounding_mode;
+ user_rnd_prec = status->floatx80_rounding_precision;
+ status->float_rounding_mode = float_round_nearest_even;
+ status->floatx80_rounding_precision = 80;
+
+ compact = floatx80_make_compact(aExp, aSig);
+
+ fp0 = a;
+
+ if (compact < 0x3FD78000 || compact > 0x4004BC7E) {
+ /* 2^(-40) > |X| > 15 PI */
+ if (compact > 0x3FFF8000) { /* |X| >= 15 PI */
+ /* REDUCEX */
+ fp1 = packFloatx80(0, 0, 0);
+ if (compact == 0x7FFEFFFF) {
+ twopi1 = packFloatx80(aSign ^ 1, 0x7FFE,
+ LIT64(0xC90FDAA200000000));
+ twopi2 = packFloatx80(aSign ^ 1, 0x7FDC,
+ LIT64(0x85A308D300000000));
+ fp0 = floatx80_add(fp0, twopi1, status);
+ fp1 = fp0;
+ fp0 = floatx80_add(fp0, twopi2, status);
+ fp1 = floatx80_sub(fp1, fp0, status);
+ fp1 = floatx80_add(fp1, twopi2, status);
+ }
+ loop:
+ xSign = extractFloatx80Sign(fp0);
+ xExp = extractFloatx80Exp(fp0);
+ xExp -= 0x3FFF;
+ if (xExp <= 28) {
+ l = 0;
+ endflag = 1;
+ } else {
+ l = xExp - 27;
+ endflag = 0;
+ }
+ invtwopi = packFloatx80(0, 0x3FFE - l,
+ LIT64(0xA2F9836E4E44152A)); /* INVTWOPI */
+ twopi1 = packFloatx80(0, 0x3FFF + l, LIT64(0xC90FDAA200000000));
+ twopi2 = packFloatx80(0, 0x3FDD + l, LIT64(0x85A308D300000000));
+
+ /* SIGN(INARG)*2^63 IN SGL */
+ twoto63 = packFloat32(xSign, 0xBE, 0);
+
+ fp2 = floatx80_mul(fp0, invtwopi, status);
+ fp2 = floatx80_add(fp2, float32_to_floatx80(twoto63, status),
+ status); /* THE FRACT PART OF FP2 IS ROUNDED */
+ fp2 = floatx80_sub(fp2, float32_to_floatx80(twoto63, status),
+ status); /* FP2 is N */
+ fp4 = floatx80_mul(twopi1, fp2, status); /* W = N*P1 */
+ fp5 = floatx80_mul(twopi2, fp2, status); /* w = N*P2 */
+ fp3 = floatx80_add(fp4, fp5, status); /* FP3 is P */
+ fp4 = floatx80_sub(fp4, fp3, status); /* W-P */
+ fp0 = floatx80_sub(fp0, fp3, status); /* FP0 is A := R - P */
+ fp4 = floatx80_add(fp4, fp5, status); /* FP4 is p = (W-P)+w */
+ fp3 = fp0; /* FP3 is A */
+ fp1 = floatx80_sub(fp1, fp4, status); /* FP1 is a := r - p */
+ fp0 = floatx80_add(fp0, fp1, status); /* FP0 is R := A+a */
+
+ if (endflag > 0) {
+ n = floatx80_to_int32(fp2, status);
+ goto sincont;
+ }
+ fp3 = floatx80_sub(fp3, fp0, status); /* A-R */
+ fp1 = floatx80_add(fp1, fp3, status); /* FP1 is r := (A-R)+a */
+ goto loop;
+ } else {
+ /* SINSM */
+ fp0 = float32_to_floatx80(make_float32(0x3F800000),
+ status); /* 1 */
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ if (adjn) {
+ /* COSTINY */
+ a = floatx80_sub(fp0, float32_to_floatx80(
+ make_float32(0x00800000), status), status);
+ } else {
+ /* SINTINY */
+ a = floatx80_move(a, status);
+ }
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ }
+ } else {
+ fp1 = floatx80_mul(fp0, float64_to_floatx80(
+ make_float64(0x3FE45F306DC9C883), status),
+ status); /* X*2/PI */
+
+ n = floatx80_to_int32(fp1, status);
+ j = 32 + n;
+
+ fp0 = floatx80_sub(fp0, pi_tbl[j], status); /* X-Y1 */
+ fp0 = floatx80_sub(fp0, float32_to_floatx80(pi_tbl2[j], status),
+ status); /* FP0 IS R = (X-Y1)-Y2 */
+
+ sincont:
+ if ((n + adjn) & 1) {
+ /* COSPOLY */
+ fp0 = floatx80_mul(fp0, fp0, status); /* FP0 IS S */
+ fp1 = floatx80_mul(fp0, fp0, status); /* FP1 IS T */
+ fp2 = float64_to_floatx80(make_float64(0x3D2AC4D0D6011EE3),
+ status); /* B8 */
+ fp3 = float64_to_floatx80(make_float64(0xBDA9396F9F45AC19),
+ status); /* B7 */
+
+ xSign = extractFloatx80Sign(fp0); /* X IS S */
+ xExp = extractFloatx80Exp(fp0);
+ xSig = extractFloatx80Frac(fp0);
+
+ if (((n + adjn) >> 1) & 1) {
+ xSign ^= 1;
+ posneg1 = make_float32(0xBF800000); /* -1 */
+ } else {
+ xSign ^= 0;
+ posneg1 = make_float32(0x3F800000); /* 1 */
+ } /* X IS NOW R'= SGN*R */
+
+ fp2 = floatx80_mul(fp2, fp1, status); /* TB8 */
+ fp3 = floatx80_mul(fp3, fp1, status); /* TB7 */
+ fp2 = floatx80_add(fp2, float64_to_floatx80(
+ make_float64(0x3E21EED90612C972), status),
+ status); /* B6+TB8 */
+ fp3 = floatx80_add(fp3, float64_to_floatx80(
+ make_float64(0xBE927E4FB79D9FCF), status),
+ status); /* B5+TB7 */
+ fp2 = floatx80_mul(fp2, fp1, status); /* T(B6+TB8) */
+ fp3 = floatx80_mul(fp3, fp1, status); /* T(B5+TB7) */
+ fp2 = floatx80_add(fp2, float64_to_floatx80(
+ make_float64(0x3EFA01A01A01D423), status),
+ status); /* B4+T(B6+TB8) */
+ fp4 = packFloatx80(1, 0x3FF5, LIT64(0xB60B60B60B61D438));
+ fp3 = floatx80_add(fp3, fp4, status); /* B3+T(B5+TB7) */
+ fp2 = floatx80_mul(fp2, fp1, status); /* T(B4+T(B6+TB8)) */
+ fp1 = floatx80_mul(fp1, fp3, status); /* T(B3+T(B5+TB7)) */
+ fp4 = packFloatx80(0, 0x3FFA, LIT64(0xAAAAAAAAAAAAAB5E));
+ fp2 = floatx80_add(fp2, fp4, status); /* B2+T(B4+T(B6+TB8)) */
+ fp1 = floatx80_add(fp1, float32_to_floatx80(
+ make_float32(0xBF000000), status),
+ status); /* B1+T(B3+T(B5+TB7)) */
+ fp0 = floatx80_mul(fp0, fp2, status); /* S(B2+T(B4+T(B6+TB8))) */
+ fp0 = floatx80_add(fp0, fp1, status); /* [B1+T(B3+T(B5+TB7))]+
+ * [S(B2+T(B4+T(B6+TB8)))]
+ */
+
+ x = packFloatx80(xSign, xExp, xSig);
+ fp0 = floatx80_mul(fp0, x, status);
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_add(fp0, float32_to_floatx80(posneg1, status), status);
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ } else {
+ /* SINPOLY */
+ xSign = extractFloatx80Sign(fp0); /* X IS R */
+ xExp = extractFloatx80Exp(fp0);
+ xSig = extractFloatx80Frac(fp0);
+
+ xSign ^= ((n + adjn) >> 1) & 1; /* X IS NOW R'= SGN*R */
+
+ fp0 = floatx80_mul(fp0, fp0, status); /* FP0 IS S */
+ fp1 = floatx80_mul(fp0, fp0, status); /* FP1 IS T */
+ fp3 = float64_to_floatx80(make_float64(0xBD6AAA77CCC994F5),
+ status); /* A7 */
+ fp2 = float64_to_floatx80(make_float64(0x3DE612097AAE8DA1),
+ status); /* A6 */
+ fp3 = floatx80_mul(fp3, fp1, status); /* T*A7 */
+ fp2 = floatx80_mul(fp2, fp1, status); /* T*A6 */
+ fp3 = floatx80_add(fp3, float64_to_floatx80(
+ make_float64(0xBE5AE6452A118AE4), status),
+ status); /* A5+T*A7 */
+ fp2 = floatx80_add(fp2, float64_to_floatx80(
+ make_float64(0x3EC71DE3A5341531), status),
+ status); /* A4+T*A6 */
+ fp3 = floatx80_mul(fp3, fp1, status); /* T(A5+TA7) */
+ fp2 = floatx80_mul(fp2, fp1, status); /* T(A4+TA6) */
+ fp3 = floatx80_add(fp3, float64_to_floatx80(
+ make_float64(0xBF2A01A01A018B59), status),
+ status); /* A3+T(A5+TA7) */
+ fp4 = packFloatx80(0, 0x3FF8, LIT64(0x88888888888859AF));
+ fp2 = floatx80_add(fp2, fp4, status); /* A2+T(A4+TA6) */
+ fp1 = floatx80_mul(fp1, fp3, status); /* T(A3+T(A5+TA7)) */
+ fp2 = floatx80_mul(fp2, fp0, status); /* S(A2+T(A4+TA6)) */
+ fp4 = packFloatx80(1, 0x3FFC, LIT64(0xAAAAAAAAAAAAAA99));
+ fp1 = floatx80_add(fp1, fp4, status); /* A1+T(A3+T(A5+TA7)) */
+ fp1 = floatx80_add(fp1, fp2,
+ status); /* [A1+T(A3+T(A5+TA7))]+
+ * [S(A2+T(A4+TA6))]
+ */
+
+ x = packFloatx80(xSign, xExp, xSig);
+ fp0 = floatx80_mul(fp0, x, status); /* R'*S */
+ fp0 = floatx80_mul(fp0, fp1, status); /* SIN(R')-R' */
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_add(fp0, x, status);
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ }
+ }
+}
+
+/*----------------------------------------------------------------------------
+ | Cosine
+ *----------------------------------------------------------------------------*/
+
+floatx80 floatx80_cos(floatx80 a, float_status *status)
+{
+ flag aSign, xSign;
+ int32_t aExp, xExp;
+ uint64_t aSig, xSig;
+
+ int8_t user_rnd_mode, user_rnd_prec;
+
+ int32_t compact, l, n, j;
+ floatx80 fp0, fp1, fp2, fp3, fp4, fp5, x, invtwopi, twopi1, twopi2;
+ float32 posneg1, twoto63;
+ flag adjn, endflag;
+
+ aSig = extractFloatx80Frac(a);
+ aExp = extractFloatx80Exp(a);
+ aSign = extractFloatx80Sign(a);
+
+ if (aExp == 0x7FFF) {
+ if ((uint64_t) (aSig << 1)) {
+ return propagateFloatx80NaNOneArg(a, status);
+ }
+ float_raise(float_flag_invalid, status);
+ return floatx80_default_nan(status);
+ }
+
+ if (aExp == 0 && aSig == 0) {
+ return packFloatx80(0, one_exp, one_sig);
+ }
+
+ adjn = 1;
+
+ user_rnd_mode = status->float_rounding_mode;
+ user_rnd_prec = status->floatx80_rounding_precision;
+ status->float_rounding_mode = float_round_nearest_even;
+ status->floatx80_rounding_precision = 80;
+
+ compact = floatx80_make_compact(aExp, aSig);
+
+ fp0 = a;
+
+ if (compact < 0x3FD78000 || compact > 0x4004BC7E) {
+ /* 2^(-40) > |X| > 15 PI */
+ if (compact > 0x3FFF8000) { /* |X| >= 15 PI */
+ /* REDUCEX */
+ fp1 = packFloatx80(0, 0, 0);
+ if (compact == 0x7FFEFFFF) {
+ twopi1 = packFloatx80(aSign ^ 1, 0x7FFE,
+ LIT64(0xC90FDAA200000000));
+ twopi2 = packFloatx80(aSign ^ 1, 0x7FDC,
+ LIT64(0x85A308D300000000));
+ fp0 = floatx80_add(fp0, twopi1, status);
+ fp1 = fp0;
+ fp0 = floatx80_add(fp0, twopi2, status);
+ fp1 = floatx80_sub(fp1, fp0, status);
+ fp1 = floatx80_add(fp1, twopi2, status);
+ }
+ loop:
+ xSign = extractFloatx80Sign(fp0);
+ xExp = extractFloatx80Exp(fp0);
+ xExp -= 0x3FFF;
+ if (xExp <= 28) {
+ l = 0;
+ endflag = 1;
+ } else {
+ l = xExp - 27;
+ endflag = 0;
+ }
+ invtwopi = packFloatx80(0, 0x3FFE - l,
+ LIT64(0xA2F9836E4E44152A)); /* INVTWOPI */
+ twopi1 = packFloatx80(0, 0x3FFF + l, LIT64(0xC90FDAA200000000));
+ twopi2 = packFloatx80(0, 0x3FDD + l, LIT64(0x85A308D300000000));
+
+ /* SIGN(INARG)*2^63 IN SGL */
+ twoto63 = packFloat32(xSign, 0xBE, 0);
+
+ fp2 = floatx80_mul(fp0, invtwopi, status);
+ fp2 = floatx80_add(fp2, float32_to_floatx80(twoto63, status),
+ status); /* THE FRACT PART OF FP2 IS ROUNDED */
+ fp2 = floatx80_sub(fp2, float32_to_floatx80(twoto63, status),
+ status); /* FP2 is N */
+ fp4 = floatx80_mul(twopi1, fp2, status); /* W = N*P1 */
+ fp5 = floatx80_mul(twopi2, fp2, status); /* w = N*P2 */
+ fp3 = floatx80_add(fp4, fp5, status); /* FP3 is P */
+ fp4 = floatx80_sub(fp4, fp3, status); /* W-P */
+ fp0 = floatx80_sub(fp0, fp3, status); /* FP0 is A := R - P */
+ fp4 = floatx80_add(fp4, fp5, status); /* FP4 is p = (W-P)+w */
+ fp3 = fp0; /* FP3 is A */
+ fp1 = floatx80_sub(fp1, fp4, status); /* FP1 is a := r - p */
+ fp0 = floatx80_add(fp0, fp1, status); /* FP0 is R := A+a */
+
+ if (endflag > 0) {
+ n = floatx80_to_int32(fp2, status);
+ goto sincont;
+ }
+ fp3 = floatx80_sub(fp3, fp0, status); /* A-R */
+ fp1 = floatx80_add(fp1, fp3, status); /* FP1 is r := (A-R)+a */
+ goto loop;
+ } else {
+ /* SINSM */
+ fp0 = float32_to_floatx80(make_float32(0x3F800000), status); /* 1 */
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ if (adjn) {
+ /* COSTINY */
+ a = floatx80_sub(fp0, float32_to_floatx80(
+ make_float32(0x00800000), status),
+ status);
+ } else {
+ /* SINTINY */
+ a = floatx80_move(a, status);
+ }
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ }
+ } else {
+ fp1 = floatx80_mul(fp0, float64_to_floatx80(
+ make_float64(0x3FE45F306DC9C883), status),
+ status); /* X*2/PI */
+
+ n = floatx80_to_int32(fp1, status);
+ j = 32 + n;
+
+ fp0 = floatx80_sub(fp0, pi_tbl[j], status); /* X-Y1 */
+ fp0 = floatx80_sub(fp0, float32_to_floatx80(pi_tbl2[j], status),
+ status); /* FP0 IS R = (X-Y1)-Y2 */
+
+ sincont:
+ if ((n + adjn) & 1) {
+ /* COSPOLY */
+ fp0 = floatx80_mul(fp0, fp0, status); /* FP0 IS S */
+ fp1 = floatx80_mul(fp0, fp0, status); /* FP1 IS T */
+ fp2 = float64_to_floatx80(make_float64(0x3D2AC4D0D6011EE3),
+ status); /* B8 */
+ fp3 = float64_to_floatx80(make_float64(0xBDA9396F9F45AC19),
+ status); /* B7 */
+
+ xSign = extractFloatx80Sign(fp0); /* X IS S */
+ xExp = extractFloatx80Exp(fp0);
+ xSig = extractFloatx80Frac(fp0);
+
+ if (((n + adjn) >> 1) & 1) {
+ xSign ^= 1;
+ posneg1 = make_float32(0xBF800000); /* -1 */
+ } else {
+ xSign ^= 0;
+ posneg1 = make_float32(0x3F800000); /* 1 */
+ } /* X IS NOW R'= SGN*R */
+
+ fp2 = floatx80_mul(fp2, fp1, status); /* TB8 */
+ fp3 = floatx80_mul(fp3, fp1, status); /* TB7 */
+ fp2 = floatx80_add(fp2, float64_to_floatx80(
+ make_float64(0x3E21EED90612C972), status),
+ status); /* B6+TB8 */
+ fp3 = floatx80_add(fp3, float64_to_floatx80(
+ make_float64(0xBE927E4FB79D9FCF), status),
+ status); /* B5+TB7 */
+ fp2 = floatx80_mul(fp2, fp1, status); /* T(B6+TB8) */
+ fp3 = floatx80_mul(fp3, fp1, status); /* T(B5+TB7) */
+ fp2 = floatx80_add(fp2, float64_to_floatx80(
+ make_float64(0x3EFA01A01A01D423), status),
+ status); /* B4+T(B6+TB8) */
+ fp4 = packFloatx80(1, 0x3FF5, LIT64(0xB60B60B60B61D438));
+ fp3 = floatx80_add(fp3, fp4, status); /* B3+T(B5+TB7) */
+ fp2 = floatx80_mul(fp2, fp1, status); /* T(B4+T(B6+TB8)) */
+ fp1 = floatx80_mul(fp1, fp3, status); /* T(B3+T(B5+TB7)) */
+ fp4 = packFloatx80(0, 0x3FFA, LIT64(0xAAAAAAAAAAAAAB5E));
+ fp2 = floatx80_add(fp2, fp4, status); /* B2+T(B4+T(B6+TB8)) */
+ fp1 = floatx80_add(fp1, float32_to_floatx80(
+ make_float32(0xBF000000), status),
+ status); /* B1+T(B3+T(B5+TB7)) */
+ fp0 = floatx80_mul(fp0, fp2, status); /* S(B2+T(B4+T(B6+TB8))) */
+ fp0 = floatx80_add(fp0, fp1, status);
+ /* [B1+T(B3+T(B5+TB7))]+[S(B2+T(B4+T(B6+TB8)))] */
+
+ x = packFloatx80(xSign, xExp, xSig);
+ fp0 = floatx80_mul(fp0, x, status);
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_add(fp0, float32_to_floatx80(posneg1, status), status);
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ } else {
+ /* SINPOLY */
+ xSign = extractFloatx80Sign(fp0); /* X IS R */
+ xExp = extractFloatx80Exp(fp0);
+ xSig = extractFloatx80Frac(fp0);
+
+ xSign ^= ((n + adjn) >> 1) & 1; /* X IS NOW R'= SGN*R */
+
+ fp0 = floatx80_mul(fp0, fp0, status); /* FP0 IS S */
+ fp1 = floatx80_mul(fp0, fp0, status); /* FP1 IS T */
+ fp3 = float64_to_floatx80(make_float64(0xBD6AAA77CCC994F5),
+ status); /* A7 */
+ fp2 = float64_to_floatx80(make_float64(0x3DE612097AAE8DA1),
+ status); /* A6 */
+ fp3 = floatx80_mul(fp3, fp1, status); /* T*A7 */
+ fp2 = floatx80_mul(fp2, fp1, status); /* T*A6 */
+ fp3 = floatx80_add(fp3, float64_to_floatx80(
+ make_float64(0xBE5AE6452A118AE4), status),
+ status); /* A5+T*A7 */
+ fp2 = floatx80_add(fp2, float64_to_floatx80(
+ make_float64(0x3EC71DE3A5341531), status),
+ status); /* A4+T*A6 */
+ fp3 = floatx80_mul(fp3, fp1, status); /* T(A5+TA7) */
+ fp2 = floatx80_mul(fp2, fp1, status); /* T(A4+TA6) */
+ fp3 = floatx80_add(fp3, float64_to_floatx80(
+ make_float64(0xBF2A01A01A018B59), status),
+ status); /* A3+T(A5+TA7) */
+ fp4 = packFloatx80(0, 0x3FF8, LIT64(0x88888888888859AF));
+ fp2 = floatx80_add(fp2, fp4, status); /* A2+T(A4+TA6) */
+ fp1 = floatx80_mul(fp1, fp3, status); /* T(A3+T(A5+TA7)) */
+ fp2 = floatx80_mul(fp2, fp0, status); /* S(A2+T(A4+TA6)) */
+ fp4 = packFloatx80(1, 0x3FFC, LIT64(0xAAAAAAAAAAAAAA99));
+ fp1 = floatx80_add(fp1, fp4, status); /* A1+T(A3+T(A5+TA7)) */
+ fp1 = floatx80_add(fp1, fp2, status);
+ /* [A1+T(A3+T(A5+TA7))]+[S(A2+T(A4+TA6))] */
+
+ x = packFloatx80(xSign, xExp, xSig);
+ fp0 = floatx80_mul(fp0, x, status); /* R'*S */
+ fp0 = floatx80_mul(fp0, fp1, status); /* SIN(R')-R' */
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_add(fp0, x, status);
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ }
+ }
+}
+
+/*----------------------------------------------------------------------------
+ | Arc tangent
+ *----------------------------------------------------------------------------*/
+
+floatx80 floatx80_atan(floatx80 a, float_status *status)
+{
+ flag aSign;
+ int32_t aExp;
+ uint64_t aSig;
+
+ int8_t user_rnd_mode, user_rnd_prec;
+
+ int32_t compact, tbl_index;
+ floatx80 fp0, fp1, fp2, fp3, xsave;
+
+ aSig = extractFloatx80Frac(a);
+ aExp = extractFloatx80Exp(a);
+ aSign = extractFloatx80Sign(a);
+
+ if (aExp == 0x7FFF) {
+ if ((uint64_t) (aSig << 1)) {
+ return propagateFloatx80NaNOneArg(a, status);
+ }
+ a = packFloatx80(aSign, piby2_exp, pi_sig);
+ float_raise(float_flag_inexact, status);
+ return floatx80_move(a, status);
+ }
+
+ if (aExp == 0 && aSig == 0) {
+ return packFloatx80(aSign, 0, 0);
+ }
+
+ compact = floatx80_make_compact(aExp, aSig);
+
+ user_rnd_mode = status->float_rounding_mode;
+ user_rnd_prec = status->floatx80_rounding_precision;
+ status->float_rounding_mode = float_round_nearest_even;
+ status->floatx80_rounding_precision = 80;
+
+ if (compact < 0x3FFB8000 || compact > 0x4002FFFF) {
+ /* |X| >= 16 or |X| < 1/16 */
+ if (compact > 0x3FFF8000) { /* |X| >= 16 */
+ if (compact > 0x40638000) { /* |X| > 2^(100) */
+ fp0 = packFloatx80(aSign, piby2_exp, pi_sig);
+ fp1 = packFloatx80(aSign, 0x0001, one_sig);
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_sub(fp0, fp1, status);
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ } else {
+ fp0 = a;
+ fp1 = packFloatx80(1, one_exp, one_sig); /* -1 */
+ fp1 = floatx80_div(fp1, fp0, status); /* X' = -1/X */
+ xsave = fp1;
+ fp0 = floatx80_mul(fp1, fp1, status); /* Y = X'*X' */
+ fp1 = floatx80_mul(fp0, fp0, status); /* Z = Y*Y */
+ fp3 = float64_to_floatx80(make_float64(0xBFB70BF398539E6A),
+ status); /* C5 */
+ fp2 = float64_to_floatx80(make_float64(0x3FBC7187962D1D7D),
+ status); /* C4 */
+ fp3 = floatx80_mul(fp3, fp1, status); /* Z*C5 */
+ fp2 = floatx80_mul(fp2, fp1, status); /* Z*C4 */
+ fp3 = floatx80_add(fp3, float64_to_floatx80(
+ make_float64(0xBFC24924827107B8), status),
+ status); /* C3+Z*C5 */
+ fp2 = floatx80_add(fp2, float64_to_floatx80(
+ make_float64(0x3FC999999996263E), status),
+ status); /* C2+Z*C4 */
+ fp1 = floatx80_mul(fp1, fp3, status); /* Z*(C3+Z*C5) */
+ fp2 = floatx80_mul(fp2, fp0, status); /* Y*(C2+Z*C4) */
+ fp1 = floatx80_add(fp1, float64_to_floatx80(
+ make_float64(0xBFD5555555555536), status),
+ status); /* C1+Z*(C3+Z*C5) */
+ fp0 = floatx80_mul(fp0, xsave, status); /* X'*Y */
+ /* [Y*(C2+Z*C4)]+[C1+Z*(C3+Z*C5)] */
+ fp1 = floatx80_add(fp1, fp2, status);
+ /* X'*Y*([B1+Z*(B3+Z*B5)]+[Y*(B2+Z*(B4+Z*B6))]) ?? */
+ fp0 = floatx80_mul(fp0, fp1, status);
+ fp0 = floatx80_add(fp0, xsave, status);
+ fp1 = packFloatx80(aSign, piby2_exp, pi_sig);
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_add(fp0, fp1, status);
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ }
+ } else { /* |X| < 1/16 */
+ if (compact < 0x3FD78000) { /* |X| < 2^(-40) */
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_move(a, status);
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ } else {
+ fp0 = a;
+ xsave = a;
+ fp0 = floatx80_mul(fp0, fp0, status); /* Y = X*X */
+ fp1 = floatx80_mul(fp0, fp0, status); /* Z = Y*Y */
+ fp2 = float64_to_floatx80(make_float64(0x3FB344447F876989),
+ status); /* B6 */
+ fp3 = float64_to_floatx80(make_float64(0xBFB744EE7FAF45DB),
+ status); /* B5 */
+ fp2 = floatx80_mul(fp2, fp1, status); /* Z*B6 */
+ fp3 = floatx80_mul(fp3, fp1, status); /* Z*B5 */
+ fp2 = floatx80_add(fp2, float64_to_floatx80(
+ make_float64(0x3FBC71C646940220), status),
+ status); /* B4+Z*B6 */
+ fp3 = floatx80_add(fp3, float64_to_floatx80(
+ make_float64(0xBFC24924921872F9),
+ status), status); /* B3+Z*B5 */
+ fp2 = floatx80_mul(fp2, fp1, status); /* Z*(B4+Z*B6) */
+ fp1 = floatx80_mul(fp1, fp3, status); /* Z*(B3+Z*B5) */
+ fp2 = floatx80_add(fp2, float64_to_floatx80(
+ make_float64(0x3FC9999999998FA9), status),
+ status); /* B2+Z*(B4+Z*B6) */
+ fp1 = floatx80_add(fp1, float64_to_floatx80(
+ make_float64(0xBFD5555555555555), status),
+ status); /* B1+Z*(B3+Z*B5) */
+ fp2 = floatx80_mul(fp2, fp0, status); /* Y*(B2+Z*(B4+Z*B6)) */
+ fp0 = floatx80_mul(fp0, xsave, status); /* X*Y */
+ /* [B1+Z*(B3+Z*B5)]+[Y*(B2+Z*(B4+Z*B6))] */
+ fp1 = floatx80_add(fp1, fp2, status);
+ /* X*Y*([B1+Z*(B3+Z*B5)]+[Y*(B2+Z*(B4+Z*B6))]) */
+ fp0 = floatx80_mul(fp0, fp1, status);
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_add(fp0, xsave, status);
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ }
+ }
+ } else {
+ aSig &= LIT64(0xF800000000000000);
+ aSig |= LIT64(0x0400000000000000);
+ xsave = packFloatx80(aSign, aExp, aSig); /* F */
+ fp0 = a;
+ fp1 = a; /* X */
+ fp2 = packFloatx80(0, one_exp, one_sig); /* 1 */
+ fp1 = floatx80_mul(fp1, xsave, status); /* X*F */
+ fp0 = floatx80_sub(fp0, xsave, status); /* X-F */
+ fp1 = floatx80_add(fp1, fp2, status); /* 1 + X*F */
+ fp0 = floatx80_div(fp0, fp1, status); /* U = (X-F)/(1+X*F) */
+
+ tbl_index = compact;
+
+ tbl_index &= 0x7FFF0000;
+ tbl_index -= 0x3FFB0000;
+ tbl_index >>= 1;
+ tbl_index += compact & 0x00007800;
+ tbl_index >>= 11;
+
+ fp3 = atan_tbl[tbl_index];
+
+ fp3.high |= aSign ? 0x8000 : 0; /* ATAN(F) */
+
+ fp1 = floatx80_mul(fp0, fp0, status); /* V = U*U */
+ fp2 = float64_to_floatx80(make_float64(0xBFF6687E314987D8),
+ status); /* A3 */
+ fp2 = floatx80_add(fp2, fp1, status); /* A3+V */
+ fp2 = floatx80_mul(fp2, fp1, status); /* V*(A3+V) */
+ fp1 = floatx80_mul(fp1, fp0, status); /* U*V */
+ fp2 = floatx80_add(fp2, float64_to_floatx80(
+ make_float64(0x4002AC6934A26DB3), status),
+ status); /* A2+V*(A3+V) */
+ fp1 = floatx80_mul(fp1, float64_to_floatx80(
+ make_float64(0xBFC2476F4E1DA28E), status),
+ status); /* A1+U*V */
+ fp1 = floatx80_mul(fp1, fp2, status); /* A1*U*V*(A2+V*(A3+V)) */
+ fp0 = floatx80_add(fp0, fp1, status); /* ATAN(U) */
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_add(fp0, fp3, status); /* ATAN(X) */
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ }
+}
+
+/*----------------------------------------------------------------------------
+ | Arc sine
+ *----------------------------------------------------------------------------*/
+
+floatx80 floatx80_asin(floatx80 a, float_status *status)
+{
+ flag aSign;
+ int32_t aExp;
+ uint64_t aSig;
+
+ int8_t user_rnd_mode, user_rnd_prec;
+
+ int32_t compact;
+ floatx80 fp0, fp1, fp2, one;
+
+ aSig = extractFloatx80Frac(a);
+ aExp = extractFloatx80Exp(a);
+ aSign = extractFloatx80Sign(a);
+
+ if (aExp == 0x7FFF && (uint64_t) (aSig << 1)) {
+ return propagateFloatx80NaNOneArg(a, status);
+ }
+
+ if (aExp == 0 && aSig == 0) {
+ return packFloatx80(aSign, 0, 0);
+ }
+
+ compact = floatx80_make_compact(aExp, aSig);
+
+ if (compact >= 0x3FFF8000) { /* |X| >= 1 */
+ if (aExp == one_exp && aSig == one_sig) { /* |X| == 1 */
+ float_raise(float_flag_inexact, status);
+ a = packFloatx80(aSign, piby2_exp, pi_sig);
+ return floatx80_move(a, status);
+ } else { /* |X| > 1 */
+ float_raise(float_flag_invalid, status);
+ return floatx80_default_nan(status);
+ }
+
+ } /* |X| < 1 */
+
+ user_rnd_mode = status->float_rounding_mode;
+ user_rnd_prec = status->floatx80_rounding_precision;
+ status->float_rounding_mode = float_round_nearest_even;
+ status->floatx80_rounding_precision = 80;
+
+ one = packFloatx80(0, one_exp, one_sig);
+ fp0 = a;
+
+ fp1 = floatx80_sub(one, fp0, status); /* 1 - X */
+ fp2 = floatx80_add(one, fp0, status); /* 1 + X */
+ fp1 = floatx80_mul(fp2, fp1, status); /* (1+X)*(1-X) */
+ fp1 = floatx80_sqrt(fp1, status); /* SQRT((1+X)*(1-X)) */
+ fp0 = floatx80_div(fp0, fp1, status); /* X/SQRT((1+X)*(1-X)) */
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_atan(fp0, status); /* ATAN(X/SQRT((1+X)*(1-X))) */
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+}
+
+/*----------------------------------------------------------------------------
+ | Arc cosine
+ *----------------------------------------------------------------------------*/
+
+floatx80 floatx80_acos(floatx80 a, float_status *status)
+{
+ flag aSign;
+ int32_t aExp;
+ uint64_t aSig;
+
+ int8_t user_rnd_mode, user_rnd_prec;
+
+ int32_t compact;
+ floatx80 fp0, fp1, one;
+
+ aSig = extractFloatx80Frac(a);
+ aExp = extractFloatx80Exp(a);
+ aSign = extractFloatx80Sign(a);
+
+ if (aExp == 0x7FFF && (uint64_t) (aSig << 1)) {
+ return propagateFloatx80NaNOneArg(a, status);
+ }
+ if (aExp == 0 && aSig == 0) {
+ float_raise(float_flag_inexact, status);
+ return roundAndPackFloatx80(status->floatx80_rounding_precision, 0,
+ piby2_exp, pi_sig, 0, status);
+ }
+
+ compact = floatx80_make_compact(aExp, aSig);
+
+ if (compact >= 0x3FFF8000) { /* |X| >= 1 */
+ if (aExp == one_exp && aSig == one_sig) { /* |X| == 1 */
+ if (aSign) { /* X == -1 */
+ a = packFloatx80(0, pi_exp, pi_sig);
+ float_raise(float_flag_inexact, status);
+ return floatx80_move(a, status);
+ } else { /* X == +1 */
+ return packFloatx80(0, 0, 0);
+ }
+ } else { /* |X| > 1 */
+ float_raise(float_flag_invalid, status);
+ return floatx80_default_nan(status);
+ }
+ } /* |X| < 1 */
+
+ user_rnd_mode = status->float_rounding_mode;
+ user_rnd_prec = status->floatx80_rounding_precision;
+ status->float_rounding_mode = float_round_nearest_even;
+ status->floatx80_rounding_precision = 80;
+
+ one = packFloatx80(0, one_exp, one_sig);
+ fp0 = a;
+
+ fp1 = floatx80_add(one, fp0, status); /* 1 + X */
+ fp0 = floatx80_sub(one, fp0, status); /* 1 - X */
+ fp0 = floatx80_div(fp0, fp1, status); /* (1-X)/(1+X) */
+ fp0 = floatx80_sqrt(fp0, status); /* SQRT((1-X)/(1+X)) */
+ fp0 = floatx80_atan(fp0, status); /* ATAN(SQRT((1-X)/(1+X))) */
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_add(fp0, fp0, status); /* 2 * ATAN(SQRT((1-X)/(1+X))) */
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+}
+
+/*----------------------------------------------------------------------------
+ | Hyperbolic arc tangent
+ *----------------------------------------------------------------------------*/
+
+floatx80 floatx80_atanh(floatx80 a, float_status *status)
+{
+ flag aSign;
+ int32_t aExp;
+ uint64_t aSig;
+
+ int8_t user_rnd_mode, user_rnd_prec;
+
+ int32_t compact;
+ floatx80 fp0, fp1, fp2, one;
+
+ aSig = extractFloatx80Frac(a);
+ aExp = extractFloatx80Exp(a);
+ aSign = extractFloatx80Sign(a);
+
+ if (aExp == 0x7FFF && (uint64_t) (aSig << 1)) {
+ return propagateFloatx80NaNOneArg(a, status);
+ }
+
+ if (aExp == 0 && aSig == 0) {
+ return packFloatx80(aSign, 0, 0);
+ }
+
+ compact = floatx80_make_compact(aExp, aSig);
+
+ if (compact >= 0x3FFF8000) { /* |X| >= 1 */
+ if (aExp == one_exp && aSig == one_sig) { /* |X| == 1 */
+ float_raise(float_flag_divbyzero, status);
+ return packFloatx80(aSign, floatx80_infinity.high,
+ floatx80_infinity.low);
+ } else { /* |X| > 1 */
+ float_raise(float_flag_invalid, status);
+ return floatx80_default_nan(status);
+ }
+ } /* |X| < 1 */
+
+ user_rnd_mode = status->float_rounding_mode;
+ user_rnd_prec = status->floatx80_rounding_precision;
+ status->float_rounding_mode = float_round_nearest_even;
+ status->floatx80_rounding_precision = 80;
+
+ one = packFloatx80(0, one_exp, one_sig);
+ fp2 = packFloatx80(aSign, 0x3FFE, one_sig); /* SIGN(X) * (1/2) */
+ fp0 = packFloatx80(0, aExp, aSig); /* Y = |X| */
+ fp1 = packFloatx80(1, aExp, aSig); /* -Y */
+ fp0 = floatx80_add(fp0, fp0, status); /* 2Y */
+ fp1 = floatx80_add(fp1, one, status); /* 1-Y */
+ fp0 = floatx80_div(fp0, fp1, status); /* Z = 2Y/(1-Y) */
+ fp0 = floatx80_lognp1(fp0, status); /* LOG1P(Z) */
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_mul(fp0, fp2,
+ status); /* ATANH(X) = SIGN(X) * (1/2) * LOG1P(Z) */
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+}
+
+/*----------------------------------------------------------------------------
+ | e to x minus 1
+ *----------------------------------------------------------------------------*/
+
+floatx80 floatx80_etoxm1(floatx80 a, float_status *status)
+{
+ flag aSign;
+ int32_t aExp;
+ uint64_t aSig;
+
+ int8_t user_rnd_mode, user_rnd_prec;
+
+ int32_t compact, n, j, m, m1;
+ floatx80 fp0, fp1, fp2, fp3, l2, sc, onebysc;
+
+ aSig = extractFloatx80Frac(a);
+ aExp = extractFloatx80Exp(a);
+ aSign = extractFloatx80Sign(a);
+
+ if (aExp == 0x7FFF) {
+ if ((uint64_t) (aSig << 1)) {
+ return propagateFloatx80NaNOneArg(a, status);
+ }
+ if (aSign) {
+ return packFloatx80(aSign, one_exp, one_sig);
+ }
+ return packFloatx80(0, floatx80_infinity.high,
+ floatx80_infinity.low);
+ }
+
+ if (aExp == 0 && aSig == 0) {
+ return packFloatx80(aSign, 0, 0);
+ }
+
+ user_rnd_mode = status->float_rounding_mode;
+ user_rnd_prec = status->floatx80_rounding_precision;
+ status->float_rounding_mode = float_round_nearest_even;
+ status->floatx80_rounding_precision = 80;
+
+ if (aExp >= 0x3FFD) { /* |X| >= 1/4 */
+ compact = floatx80_make_compact(aExp, aSig);
+
+ if (compact <= 0x4004C215) { /* |X| <= 70 log2 */
+ fp0 = a;
+ fp1 = a;
+ fp0 = floatx80_mul(fp0, float32_to_floatx80(
+ make_float32(0x42B8AA3B), status),
+ status); /* 64/log2 * X */
+ n = floatx80_to_int32(fp0, status); /* int(64/log2*X) */
+ fp0 = int32_to_floatx80(n, status);
+
+ j = n & 0x3F; /* J = N mod 64 */
+ m = n / 64; /* NOTE: this is really arithmetic right shift by 6 */
+ if (n < 0 && j) {
+ /* arithmetic right shift is division and
+ * round towards minus infinity
+ */
+ m--;
+ }
+ m1 = -m;
+ /*m += 0x3FFF; // biased exponent of 2^(M) */
+ /*m1 += 0x3FFF; // biased exponent of -2^(-M) */
+
+ fp2 = fp0; /* N */
+ fp0 = floatx80_mul(fp0, float32_to_floatx80(
+ make_float32(0xBC317218), status),
+ status); /* N * L1, L1 = lead(-log2/64) */
+ l2 = packFloatx80(0, 0x3FDC, LIT64(0x82E308654361C4C6));
+ fp2 = floatx80_mul(fp2, l2, status); /* N * L2, L1+L2 = -log2/64 */
+ fp0 = floatx80_add(fp0, fp1, status); /* X + N*L1 */
+ fp0 = floatx80_add(fp0, fp2, status); /* R */
+
+ fp1 = floatx80_mul(fp0, fp0, status); /* S = R*R */
+ fp2 = float32_to_floatx80(make_float32(0x3950097B),
+ status); /* A6 */
+ fp2 = floatx80_mul(fp2, fp1, status); /* fp2 is S*A6 */
+ fp3 = floatx80_mul(float32_to_floatx80(make_float32(0x3AB60B6A),
+ status), fp1, status); /* fp3 is S*A5 */
+ fp2 = floatx80_add(fp2, float64_to_floatx80(
+ make_float64(0x3F81111111174385), status),
+ status); /* fp2 IS A4+S*A6 */
+ fp3 = floatx80_add(fp3, float64_to_floatx80(
+ make_float64(0x3FA5555555554F5A), status),
+ status); /* fp3 is A3+S*A5 */
+ fp2 = floatx80_mul(fp2, fp1, status); /* fp2 IS S*(A4+S*A6) */
+ fp3 = floatx80_mul(fp3, fp1, status); /* fp3 IS S*(A3+S*A5) */
+ fp2 = floatx80_add(fp2, float64_to_floatx80(
+ make_float64(0x3FC5555555555555), status),
+ status); /* fp2 IS A2+S*(A4+S*A6) */
+ fp3 = floatx80_add(fp3, float32_to_floatx80(
+ make_float32(0x3F000000), status),
+ status); /* fp3 IS A1+S*(A3+S*A5) */
+ fp2 = floatx80_mul(fp2, fp1,
+ status); /* fp2 IS S*(A2+S*(A4+S*A6)) */
+ fp1 = floatx80_mul(fp1, fp3,
+ status); /* fp1 IS S*(A1+S*(A3+S*A5)) */
+ fp2 = floatx80_mul(fp2, fp0,
+ status); /* fp2 IS R*S*(A2+S*(A4+S*A6)) */
+ fp0 = floatx80_add(fp0, fp1,
+ status); /* fp0 IS R+S*(A1+S*(A3+S*A5)) */
+ fp0 = floatx80_add(fp0, fp2, status); /* fp0 IS EXP(R) - 1 */
+
+ fp0 = floatx80_mul(fp0, exp_tbl[j],
+ status); /* 2^(J/64)*(Exp(R)-1) */
+
+ if (m >= 64) {
+ fp1 = float32_to_floatx80(exp_tbl2[j], status);
+ onebysc = packFloatx80(1, m1 + 0x3FFF, one_sig); /* -2^(-M) */
+ fp1 = floatx80_add(fp1, onebysc, status);
+ fp0 = floatx80_add(fp0, fp1, status);
+ fp0 = floatx80_add(fp0, exp_tbl[j], status);
+ } else if (m < -3) {
+ fp0 = floatx80_add(fp0, float32_to_floatx80(exp_tbl2[j],
+ status), status);
+ fp0 = floatx80_add(fp0, exp_tbl[j], status);
+ onebysc = packFloatx80(1, m1 + 0x3FFF, one_sig); /* -2^(-M) */
+ fp0 = floatx80_add(fp0, onebysc, status);
+ } else { /* -3 <= m <= 63 */
+ fp1 = exp_tbl[j];
+ fp0 = floatx80_add(fp0, float32_to_floatx80(exp_tbl2[j],
+ status), status);
+ onebysc = packFloatx80(1, m1 + 0x3FFF, one_sig); /* -2^(-M) */
+ fp1 = floatx80_add(fp1, onebysc, status);
+ fp0 = floatx80_add(fp0, fp1, status);
+ }
+
+ sc = packFloatx80(0, m + 0x3FFF, one_sig);
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_mul(fp0, sc, status);
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ } else { /* |X| > 70 log2 */
+ if (aSign) {
+ fp0 = float32_to_floatx80(make_float32(0xBF800000),
+ status); /* -1 */
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_add(fp0, float32_to_floatx80(
+ make_float32(0x00800000), status),
+ status); /* -1 + 2^(-126) */
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ } else {
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ return floatx80_etox(a, status);
+ }
+ }
+ } else { /* |X| < 1/4 */
+ if (aExp >= 0x3FBE) {
+ fp0 = a;
+ fp0 = floatx80_mul(fp0, fp0, status); /* S = X*X */
+ fp1 = float32_to_floatx80(make_float32(0x2F30CAA8),
+ status); /* B12 */
+ fp1 = floatx80_mul(fp1, fp0, status); /* S * B12 */
+ fp2 = float32_to_floatx80(make_float32(0x310F8290),
+ status); /* B11 */
+ fp1 = floatx80_add(fp1, float32_to_floatx80(
+ make_float32(0x32D73220), status),
+ status); /* B10 */
+ fp2 = floatx80_mul(fp2, fp0, status);
+ fp1 = floatx80_mul(fp1, fp0, status);
+ fp2 = floatx80_add(fp2, float32_to_floatx80(
+ make_float32(0x3493F281), status),
+ status); /* B9 */
+ fp1 = floatx80_add(fp1, float64_to_floatx80(
+ make_float64(0x3EC71DE3A5774682), status),
+ status); /* B8 */
+ fp2 = floatx80_mul(fp2, fp0, status);
+ fp1 = floatx80_mul(fp1, fp0, status);
+ fp2 = floatx80_add(fp2, float64_to_floatx80(
+ make_float64(0x3EFA01A019D7CB68), status),
+ status); /* B7 */
+ fp1 = floatx80_add(fp1, float64_to_floatx80(
+ make_float64(0x3F2A01A01A019DF3), status),
+ status); /* B6 */
+ fp2 = floatx80_mul(fp2, fp0, status);
+ fp1 = floatx80_mul(fp1, fp0, status);
+ fp2 = floatx80_add(fp2, float64_to_floatx80(
+ make_float64(0x3F56C16C16C170E2), status),
+ status); /* B5 */
+ fp1 = floatx80_add(fp1, float64_to_floatx80(
+ make_float64(0x3F81111111111111), status),
+ status); /* B4 */
+ fp2 = floatx80_mul(fp2, fp0, status);
+ fp1 = floatx80_mul(fp1, fp0, status);
+ fp2 = floatx80_add(fp2, float64_to_floatx80(
+ make_float64(0x3FA5555555555555), status),
+ status); /* B3 */
+ fp3 = packFloatx80(0, 0x3FFC, LIT64(0xAAAAAAAAAAAAAAAB));
+ fp1 = floatx80_add(fp1, fp3, status); /* B2 */
+ fp2 = floatx80_mul(fp2, fp0, status);
+ fp1 = floatx80_mul(fp1, fp0, status);
+
+ fp2 = floatx80_mul(fp2, fp0, status);
+ fp1 = floatx80_mul(fp1, a, status);
+
+ fp0 = floatx80_mul(fp0, float32_to_floatx80(
+ make_float32(0x3F000000), status),
+ status); /* S*B1 */
+ fp1 = floatx80_add(fp1, fp2, status); /* Q */
+ fp0 = floatx80_add(fp0, fp1, status); /* S*B1+Q */
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_add(fp0, a, status);
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ } else { /* |X| < 2^(-65) */
+ sc = packFloatx80(1, 1, one_sig);
+ fp0 = a;
+
+ if (aExp < 0x0033) { /* |X| < 2^(-16382) */
+ fp0 = floatx80_mul(fp0, float64_to_floatx80(
+ make_float64(0x48B0000000000000), status),
+ status);
+ fp0 = floatx80_add(fp0, sc, status);
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_mul(fp0, float64_to_floatx80(
+ make_float64(0x3730000000000000), status),
+ status);
+ } else {
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_add(fp0, sc, status);
+ }
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ }
+ }
+}
+
+/*----------------------------------------------------------------------------
+ | Hyperbolic tangent
+ *----------------------------------------------------------------------------*/
+
+floatx80 floatx80_tanh(floatx80 a, float_status *status)
+{
+ flag aSign, vSign;
+ int32_t aExp, vExp;
+ uint64_t aSig, vSig;
+
+ int8_t user_rnd_mode, user_rnd_prec;
+
+ int32_t compact;
+ floatx80 fp0, fp1;
+ uint32_t sign;
+
+ aSig = extractFloatx80Frac(a);
+ aExp = extractFloatx80Exp(a);
+ aSign = extractFloatx80Sign(a);
+
+ if (aExp == 0x7FFF) {
+ if ((uint64_t) (aSig << 1)) {
+ return propagateFloatx80NaNOneArg(a, status);
+ }
+ return packFloatx80(aSign, one_exp, one_sig);
+ }
+
+ if (aExp == 0 && aSig == 0) {
+ return packFloatx80(aSign, 0, 0);
+ }
+
+ user_rnd_mode = status->float_rounding_mode;
+ user_rnd_prec = status->floatx80_rounding_precision;
+ status->float_rounding_mode = float_round_nearest_even;
+ status->floatx80_rounding_precision = 80;
+
+ compact = floatx80_make_compact(aExp, aSig);
+
+ if (compact < 0x3FD78000 || compact > 0x3FFFDDCE) {
+ /* TANHBORS */
+ if (compact < 0x3FFF8000) {
+ /* TANHSM */
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_move(a, status);
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ } else {
+ if (compact > 0x40048AA1) {
+ /* TANHHUGE */
+ sign = 0x3F800000;
+ sign |= aSign ? 0x80000000 : 0x00000000;
+ fp0 = float32_to_floatx80(make_float32(sign), status);
+ sign &= 0x80000000;
+ sign ^= 0x80800000; /* -SIGN(X)*EPS */
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_add(fp0, float32_to_floatx80(make_float32(sign),
+ status), status);
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ } else {
+ fp0 = packFloatx80(0, aExp + 1, aSig); /* Y = 2|X| */
+ fp0 = floatx80_etox(fp0, status); /* FP0 IS EXP(Y) */
+ fp0 = floatx80_add(fp0, float32_to_floatx80(
+ make_float32(0x3F800000),
+ status), status); /* EXP(Y)+1 */
+ sign = aSign ? 0x80000000 : 0x00000000;
+ fp1 = floatx80_div(float32_to_floatx80(make_float32(
+ sign ^ 0xC0000000), status), fp0,
+ status); /* -SIGN(X)*2 / [EXP(Y)+1] */
+ fp0 = float32_to_floatx80(make_float32(sign | 0x3F800000),
+ status); /* SIGN */
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_add(fp1, fp0, status);
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ }
+ }
+ } else { /* 2**(-40) < |X| < (5/2)LOG2 */
+ fp0 = packFloatx80(0, aExp + 1, aSig); /* Y = 2|X| */
+ fp0 = floatx80_etoxm1(fp0, status); /* FP0 IS Z = EXPM1(Y) */
+ fp1 = floatx80_add(fp0, float32_to_floatx80(make_float32(0x40000000),
+ status),
+ status); /* Z+2 */
+
+ vSign = extractFloatx80Sign(fp1);
+ vExp = extractFloatx80Exp(fp1);
+ vSig = extractFloatx80Frac(fp1);
+
+ fp1 = packFloatx80(vSign ^ aSign, vExp, vSig);
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_div(fp0, fp1, status);
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ }
+}
+
+/*----------------------------------------------------------------------------
+ | Hyperbolic sine
+ *----------------------------------------------------------------------------*/
+
+floatx80 floatx80_sinh(floatx80 a, float_status *status)
+{
+ flag aSign;
+ int32_t aExp;
+ uint64_t aSig;
+
+ int8_t user_rnd_mode, user_rnd_prec;
+
+ int32_t compact;
+ floatx80 fp0, fp1, fp2;
+ float32 fact;
+
+ aSig = extractFloatx80Frac(a);
+ aExp = extractFloatx80Exp(a);
+ aSign = extractFloatx80Sign(a);
+
+ if (aExp == 0x7FFF) {
+ if ((uint64_t) (aSig << 1)) {
+ return propagateFloatx80NaNOneArg(a, status);
+ }
+ return packFloatx80(aSign, floatx80_infinity.high,
+ floatx80_infinity.low);
+ }
+
+ if (aExp == 0 && aSig == 0) {
+ return packFloatx80(aSign, 0, 0);
+ }
+
+ user_rnd_mode = status->float_rounding_mode;
+ user_rnd_prec = status->floatx80_rounding_precision;
+ status->float_rounding_mode = float_round_nearest_even;
+ status->floatx80_rounding_precision = 80;
+
+ compact = floatx80_make_compact(aExp, aSig);
+
+ if (compact > 0x400CB167) {
+ /* SINHBIG */
+ if (compact > 0x400CB2B3) {
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ return roundAndPackFloatx80(status->floatx80_rounding_precision,
+ aSign, 0x8000, aSig, 0, status);
+ } else {
+ fp0 = floatx80_abs(a); /* Y = |X| */
+ fp0 = floatx80_sub(fp0, float64_to_floatx80(
+ make_float64(0x40C62D38D3D64634), status),
+ status); /* (|X|-16381LOG2_LEAD) */
+ fp0 = floatx80_sub(fp0, float64_to_floatx80(
+ make_float64(0x3D6F90AEB1E75CC7), status),
+ status); /* |X| - 16381 LOG2, ACCURATE */
+ fp0 = floatx80_etox(fp0, status);
+ fp2 = packFloatx80(aSign, 0x7FFB, one_sig);
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_mul(fp0, fp2, status);
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ }
+ } else { /* |X| < 16380 LOG2 */
+ fp0 = floatx80_abs(a); /* Y = |X| */
+ fp0 = floatx80_etoxm1(fp0, status); /* FP0 IS Z = EXPM1(Y) */
+ fp1 = floatx80_add(fp0, float32_to_floatx80(make_float32(0x3F800000),
+ status), status); /* 1+Z */
+ fp2 = fp0;
+ fp0 = floatx80_div(fp0, fp1, status); /* Z/(1+Z) */
+ fp0 = floatx80_add(fp0, fp2, status);
+
+ fact = packFloat32(aSign, 0x7E, 0);
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_mul(fp0, float32_to_floatx80(fact, status), status);
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ }
+}
+
+/*----------------------------------------------------------------------------
+ | Hyperbolic cosine
+ *----------------------------------------------------------------------------*/
+
+floatx80 floatx80_cosh(floatx80 a, float_status *status)
+{
+ int32_t aExp;
+ uint64_t aSig;
+
+ int8_t user_rnd_mode, user_rnd_prec;
+
+ int32_t compact;
+ floatx80 fp0, fp1;
+
+ aSig = extractFloatx80Frac(a);
+ aExp = extractFloatx80Exp(a);
+
+ if (aExp == 0x7FFF) {
+ if ((uint64_t) (aSig << 1)) {
+ return propagateFloatx80NaNOneArg(a, status);
+ }
+ return packFloatx80(0, floatx80_infinity.high,
+ floatx80_infinity.low);
+ }
+
+ if (aExp == 0 && aSig == 0) {
+ return packFloatx80(0, one_exp, one_sig);
+ }
+
+ user_rnd_mode = status->float_rounding_mode;
+ user_rnd_prec = status->floatx80_rounding_precision;
+ status->float_rounding_mode = float_round_nearest_even;
+ status->floatx80_rounding_precision = 80;
+
+ compact = floatx80_make_compact(aExp, aSig);
+
+ if (compact > 0x400CB167) {
+ if (compact > 0x400CB2B3) {
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+ return roundAndPackFloatx80(status->floatx80_rounding_precision, 0,
+ 0x8000, one_sig, 0, status);
+ } else {
+ fp0 = packFloatx80(0, aExp, aSig);
+ fp0 = floatx80_sub(fp0, float64_to_floatx80(
+ make_float64(0x40C62D38D3D64634), status),
+ status);
+ fp0 = floatx80_sub(fp0, float64_to_floatx80(
+ make_float64(0x3D6F90AEB1E75CC7), status),
+ status);
+ fp0 = floatx80_etox(fp0, status);
+ fp1 = packFloatx80(0, 0x7FFB, one_sig);
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_mul(fp0, fp1, status);
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+ }
+ }
+
+ fp0 = packFloatx80(0, aExp, aSig); /* |X| */
+ fp0 = floatx80_etox(fp0, status); /* EXP(|X|) */
+ fp0 = floatx80_mul(fp0, float32_to_floatx80(make_float32(0x3F000000),
+ status), status); /* (1/2)*EXP(|X|) */
+ fp1 = float32_to_floatx80(make_float32(0x3E800000), status); /* 1/4 */
+ fp1 = floatx80_div(fp1, fp0, status); /* 1/(2*EXP(|X|)) */
+
+ status->float_rounding_mode = user_rnd_mode;
+ status->floatx80_rounding_precision = user_rnd_prec;
+
+ a = floatx80_add(fp0, fp1, status);
+
+ float_raise(float_flag_inexact, status);
+
+ return a;
+}
diff --git a/target/m68k/softfloat.h b/target/m68k/softfloat.h
index d28e49fe9f..602661d5a8 100644
--- a/target/m68k/softfloat.h
+++ b/target/m68k/softfloat.h
@@ -34,4 +34,15 @@ floatx80 floatx80_log2(floatx80 a, float_status *status);
floatx80 floatx80_etox(floatx80 a, float_status *status);
floatx80 floatx80_twotox(floatx80 a, float_status *status);
floatx80 floatx80_tentox(floatx80 a, float_status *status);
+floatx80 floatx80_tan(floatx80 a, float_status *status);
+floatx80 floatx80_sin(floatx80 a, float_status *status);
+floatx80 floatx80_cos(floatx80 a, float_status *status);
+floatx80 floatx80_atan(floatx80 a, float_status *status);
+floatx80 floatx80_asin(floatx80 a, float_status *status);
+floatx80 floatx80_acos(floatx80 a, float_status *status);
+floatx80 floatx80_atanh(floatx80 a, float_status *status);
+floatx80 floatx80_etoxm1(floatx80 a, float_status *status);
+floatx80 floatx80_tanh(floatx80 a, float_status *status);
+floatx80 floatx80_sinh(floatx80 a, float_status *status);
+floatx80 floatx80_cosh(floatx80 a, float_status *status);
#endif
diff --git a/target/m68k/softfloat_fpsp_tables.h b/target/m68k/softfloat_fpsp_tables.h
index dd76dc0373..3f1419ee6e 100644
--- a/target/m68k/softfloat_fpsp_tables.h
+++ b/target/m68k/softfloat_fpsp_tables.h
@@ -371,4 +371,271 @@ static const uint32_t exp2_tbl2[64] = {
0xBFBDBF4A, 0x3FBEC01A, 0x3FBE8CAC, 0xBFBCBB3F,
0x3FBEF73A, 0xBFB8B795, 0x3FBEF84B, 0xBFBEF581
};
+
+static const floatx80 pi_tbl[65] = {
+ make_floatx80_init(0xC004, 0xC90FDAA22168C235),
+ make_floatx80_init(0xC004, 0xC2C75BCD105D7C23),
+ make_floatx80_init(0xC004, 0xBC7EDCF7FF523611),
+ make_floatx80_init(0xC004, 0xB6365E22EE46F000),
+ make_floatx80_init(0xC004, 0xAFEDDF4DDD3BA9EE),
+ make_floatx80_init(0xC004, 0xA9A56078CC3063DD),
+ make_floatx80_init(0xC004, 0xA35CE1A3BB251DCB),
+ make_floatx80_init(0xC004, 0x9D1462CEAA19D7B9),
+ make_floatx80_init(0xC004, 0x96CBE3F9990E91A8),
+ make_floatx80_init(0xC004, 0x9083652488034B96),
+ make_floatx80_init(0xC004, 0x8A3AE64F76F80584),
+ make_floatx80_init(0xC004, 0x83F2677A65ECBF73),
+ make_floatx80_init(0xC003, 0xFB53D14AA9C2F2C2),
+ make_floatx80_init(0xC003, 0xEEC2D3A087AC669F),
+ make_floatx80_init(0xC003, 0xE231D5F66595DA7B),
+ make_floatx80_init(0xC003, 0xD5A0D84C437F4E58),
+ make_floatx80_init(0xC003, 0xC90FDAA22168C235),
+ make_floatx80_init(0xC003, 0xBC7EDCF7FF523611),
+ make_floatx80_init(0xC003, 0xAFEDDF4DDD3BA9EE),
+ make_floatx80_init(0xC003, 0xA35CE1A3BB251DCB),
+ make_floatx80_init(0xC003, 0x96CBE3F9990E91A8),
+ make_floatx80_init(0xC003, 0x8A3AE64F76F80584),
+ make_floatx80_init(0xC002, 0xFB53D14AA9C2F2C2),
+ make_floatx80_init(0xC002, 0xE231D5F66595DA7B),
+ make_floatx80_init(0xC002, 0xC90FDAA22168C235),
+ make_floatx80_init(0xC002, 0xAFEDDF4DDD3BA9EE),
+ make_floatx80_init(0xC002, 0x96CBE3F9990E91A8),
+ make_floatx80_init(0xC001, 0xFB53D14AA9C2F2C2),
+ make_floatx80_init(0xC001, 0xC90FDAA22168C235),
+ make_floatx80_init(0xC001, 0x96CBE3F9990E91A8),
+ make_floatx80_init(0xC000, 0xC90FDAA22168C235),
+ make_floatx80_init(0xBFFF, 0xC90FDAA22168C235),
+ make_floatx80_init(0x0000, 0x0000000000000000),
+ make_floatx80_init(0x3FFF, 0xC90FDAA22168C235),
+ make_floatx80_init(0x4000, 0xC90FDAA22168C235),
+ make_floatx80_init(0x4001, 0x96CBE3F9990E91A8),
+ make_floatx80_init(0x4001, 0xC90FDAA22168C235),
+ make_floatx80_init(0x4001, 0xFB53D14AA9C2F2C2),
+ make_floatx80_init(0x4002, 0x96CBE3F9990E91A8),
+ make_floatx80_init(0x4002, 0xAFEDDF4DDD3BA9EE),
+ make_floatx80_init(0x4002, 0xC90FDAA22168C235),
+ make_floatx80_init(0x4002, 0xE231D5F66595DA7B),
+ make_floatx80_init(0x4002, 0xFB53D14AA9C2F2C2),
+ make_floatx80_init(0x4003, 0x8A3AE64F76F80584),
+ make_floatx80_init(0x4003, 0x96CBE3F9990E91A8),
+ make_floatx80_init(0x4003, 0xA35CE1A3BB251DCB),
+ make_floatx80_init(0x4003, 0xAFEDDF4DDD3BA9EE),
+ make_floatx80_init(0x4003, 0xBC7EDCF7FF523611),
+ make_floatx80_init(0x4003, 0xC90FDAA22168C235),
+ make_floatx80_init(0x4003, 0xD5A0D84C437F4E58),
+ make_floatx80_init(0x4003, 0xE231D5F66595DA7B),
+ make_floatx80_init(0x4003, 0xEEC2D3A087AC669F),
+ make_floatx80_init(0x4003, 0xFB53D14AA9C2F2C2),
+ make_floatx80_init(0x4004, 0x83F2677A65ECBF73),
+ make_floatx80_init(0x4004, 0x8A3AE64F76F80584),
+ make_floatx80_init(0x4004, 0x9083652488034B96),
+ make_floatx80_init(0x4004, 0x96CBE3F9990E91A8),
+ make_floatx80_init(0x4004, 0x9D1462CEAA19D7B9),
+ make_floatx80_init(0x4004, 0xA35CE1A3BB251DCB),
+ make_floatx80_init(0x4004, 0xA9A56078CC3063DD),
+ make_floatx80_init(0x4004, 0xAFEDDF4DDD3BA9EE),
+ make_floatx80_init(0x4004, 0xB6365E22EE46F000),
+ make_floatx80_init(0x4004, 0xBC7EDCF7FF523611),
+ make_floatx80_init(0x4004, 0xC2C75BCD105D7C23),
+ make_floatx80_init(0x4004, 0xC90FDAA22168C235)
+};
+
+static const float32 pi_tbl2[65] = {
+ const_float32(0x21800000),
+ const_float32(0xA0D00000),
+ const_float32(0xA1E80000),
+ const_float32(0x21480000),
+ const_float32(0xA1200000),
+ const_float32(0x21FC0000),
+ const_float32(0x21100000),
+ const_float32(0xA1580000),
+ const_float32(0x21E00000),
+ const_float32(0x20B00000),
+ const_float32(0xA1880000),
+ const_float32(0x21C40000),
+ const_float32(0x20000000),
+ const_float32(0x21380000),
+ const_float32(0xA1300000),
+ const_float32(0x9FC00000),
+ const_float32(0x21000000),
+ const_float32(0xA1680000),
+ const_float32(0xA0A00000),
+ const_float32(0x20900000),
+ const_float32(0x21600000),
+ const_float32(0xA1080000),
+ const_float32(0x1F800000),
+ const_float32(0xA0B00000),
+ const_float32(0x20800000),
+ const_float32(0xA0200000),
+ const_float32(0x20E00000),
+ const_float32(0x1F000000),
+ const_float32(0x20000000),
+ const_float32(0x20600000),
+ const_float32(0x1F800000),
+ const_float32(0x1F000000),
+ const_float32(0x00000000),
+ const_float32(0x9F000000),
+ const_float32(0x9F800000),
+ const_float32(0xA0600000),
+ const_float32(0xA0000000),
+ const_float32(0x9F000000),
+ const_float32(0xA0E00000),
+ const_float32(0x20200000),
+ const_float32(0xA0800000),
+ const_float32(0x20B00000),
+ const_float32(0x9F800000),
+ const_float32(0x21080000),
+ const_float32(0xA1600000),
+ const_float32(0xA0900000),
+ const_float32(0x20A00000),
+ const_float32(0x21680000),
+ const_float32(0xA1000000),
+ const_float32(0x1FC00000),
+ const_float32(0x21300000),
+ const_float32(0xA1380000),
+ const_float32(0xA0000000),
+ const_float32(0xA1C40000),
+ const_float32(0x21880000),
+ const_float32(0xA0B00000),
+ const_float32(0xA1E00000),
+ const_float32(0x21580000),
+ const_float32(0xA1100000),
+ const_float32(0xA1FC0000),
+ const_float32(0x21200000),
+ const_float32(0xA1480000),
+ const_float32(0x21E80000),
+ const_float32(0x20D00000),
+ const_float32(0xA1800000),
+};
+
+static const floatx80 atan_tbl[128] = {
+ make_floatx80_init(0x3FFB, 0x83D152C5060B7A51),
+ make_floatx80_init(0x3FFB, 0x8BC8544565498B8B),
+ make_floatx80_init(0x3FFB, 0x93BE406017626B0D),
+ make_floatx80_init(0x3FFB, 0x9BB3078D35AEC202),
+ make_floatx80_init(0x3FFB, 0xA3A69A525DDCE7DE),
+ make_floatx80_init(0x3FFB, 0xAB98E94362765619),
+ make_floatx80_init(0x3FFB, 0xB389E502F9C59862),
+ make_floatx80_init(0x3FFB, 0xBB797E436B09E6FB),
+ make_floatx80_init(0x3FFB, 0xC367A5C739E5F446),
+ make_floatx80_init(0x3FFB, 0xCB544C61CFF7D5C6),
+ make_floatx80_init(0x3FFB, 0xD33F62F82488533E),
+ make_floatx80_init(0x3FFB, 0xDB28DA8162404C77),
+ make_floatx80_init(0x3FFB, 0xE310A4078AD34F18),
+ make_floatx80_init(0x3FFB, 0xEAF6B0A8188EE1EB),
+ make_floatx80_init(0x3FFB, 0xF2DAF1949DBE79D5),
+ make_floatx80_init(0x3FFB, 0xFABD581361D47E3E),
+ make_floatx80_init(0x3FFC, 0x8346AC210959ECC4),
+ make_floatx80_init(0x3FFC, 0x8B232A08304282D8),
+ make_floatx80_init(0x3FFC, 0x92FB70B8D29AE2F9),
+ make_floatx80_init(0x3FFC, 0x9ACF476F5CCD1CB4),
+ make_floatx80_init(0x3FFC, 0xA29E76304954F23F),
+ make_floatx80_init(0x3FFC, 0xAA68C5D08AB85230),
+ make_floatx80_init(0x3FFC, 0xB22DFFFD9D539F83),
+ make_floatx80_init(0x3FFC, 0xB9EDEF453E900EA5),
+ make_floatx80_init(0x3FFC, 0xC1A85F1CC75E3EA5),
+ make_floatx80_init(0x3FFC, 0xC95D1BE828138DE6),
+ make_floatx80_init(0x3FFC, 0xD10BF300840D2DE4),
+ make_floatx80_init(0x3FFC, 0xD8B4B2BA6BC05E7A),
+ make_floatx80_init(0x3FFC, 0xE0572A6BB42335F6),
+ make_floatx80_init(0x3FFC, 0xE7F32A70EA9CAA8F),
+ make_floatx80_init(0x3FFC, 0xEF88843264ECEFAA),
+ make_floatx80_init(0x3FFC, 0xF7170A28ECC06666),
+ make_floatx80_init(0x3FFD, 0x812FD288332DAD32),
+ make_floatx80_init(0x3FFD, 0x88A8D1B1218E4D64),
+ make_floatx80_init(0x3FFD, 0x9012AB3F23E4AEE8),
+ make_floatx80_init(0x3FFD, 0x976CC3D411E7F1B9),
+ make_floatx80_init(0x3FFD, 0x9EB689493889A227),
+ make_floatx80_init(0x3FFD, 0xA5EF72C34487361B),
+ make_floatx80_init(0x3FFD, 0xAD1700BAF07A7227),
+ make_floatx80_init(0x3FFD, 0xB42CBCFAFD37EFB7),
+ make_floatx80_init(0x3FFD, 0xBB303A940BA80F89),
+ make_floatx80_init(0x3FFD, 0xC22115C6FCAEBBAF),
+ make_floatx80_init(0x3FFD, 0xC8FEF3E686331221),
+ make_floatx80_init(0x3FFD, 0xCFC98330B4000C70),
+ make_floatx80_init(0x3FFD, 0xD6807AA1102C5BF9),
+ make_floatx80_init(0x3FFD, 0xDD2399BC31252AA3),
+ make_floatx80_init(0x3FFD, 0xE3B2A8556B8FC517),
+ make_floatx80_init(0x3FFD, 0xEA2D764F64315989),
+ make_floatx80_init(0x3FFD, 0xF3BF5BF8BAD1A21D),
+ make_floatx80_init(0x3FFE, 0x801CE39E0D205C9A),
+ make_floatx80_init(0x3FFE, 0x8630A2DADA1ED066),
+ make_floatx80_init(0x3FFE, 0x8C1AD445F3E09B8C),
+ make_floatx80_init(0x3FFE, 0x91DB8F1664F350E2),
+ make_floatx80_init(0x3FFE, 0x97731420365E538C),
+ make_floatx80_init(0x3FFE, 0x9CE1C8E6A0B8CDBA),
+ make_floatx80_init(0x3FFE, 0xA22832DBCADAAE09),
+ make_floatx80_init(0x3FFE, 0xA746F2DDB7602294),
+ make_floatx80_init(0x3FFE, 0xAC3EC0FB997DD6A2),
+ make_floatx80_init(0x3FFE, 0xB110688AEBDC6F6A),
+ make_floatx80_init(0x3FFE, 0xB5BCC49059ECC4B0),
+ make_floatx80_init(0x3FFE, 0xBA44BC7DD470782F),
+ make_floatx80_init(0x3FFE, 0xBEA94144FD049AAC),
+ make_floatx80_init(0x3FFE, 0xC2EB4ABB661628B6),
+ make_floatx80_init(0x3FFE, 0xC70BD54CE602EE14),
+ make_floatx80_init(0x3FFE, 0xCD000549ADEC7159),
+ make_floatx80_init(0x3FFE, 0xD48457D2D8EA4EA3),
+ make_floatx80_init(0x3FFE, 0xDB948DA712DECE3B),
+ make_floatx80_init(0x3FFE, 0xE23855F969E8096A),
+ make_floatx80_init(0x3FFE, 0xE8771129C4353259),
+ make_floatx80_init(0x3FFE, 0xEE57C16E0D379C0D),
+ make_floatx80_init(0x3FFE, 0xF3E10211A87C3779),
+ make_floatx80_init(0x3FFE, 0xF919039D758B8D41),
+ make_floatx80_init(0x3FFE, 0xFE058B8F64935FB3),
+ make_floatx80_init(0x3FFF, 0x8155FB497B685D04),
+ make_floatx80_init(0x3FFF, 0x83889E3549D108E1),
+ make_floatx80_init(0x3FFF, 0x859CFA76511D724B),
+ make_floatx80_init(0x3FFF, 0x87952ECFFF8131E7),
+ make_floatx80_init(0x3FFF, 0x89732FD19557641B),
+ make_floatx80_init(0x3FFF, 0x8B38CAD101932A35),
+ make_floatx80_init(0x3FFF, 0x8CE7A8D8301EE6B5),
+ make_floatx80_init(0x3FFF, 0x8F46A39E2EAE5281),
+ make_floatx80_init(0x3FFF, 0x922DA7D791888487),
+ make_floatx80_init(0x3FFF, 0x94D19FCBDEDF5241),
+ make_floatx80_init(0x3FFF, 0x973AB94419D2A08B),
+ make_floatx80_init(0x3FFF, 0x996FF00E08E10B96),
+ make_floatx80_init(0x3FFF, 0x9B773F9512321DA7),
+ make_floatx80_init(0x3FFF, 0x9D55CC320F935624),
+ make_floatx80_init(0x3FFF, 0x9F100575006CC571),
+ make_floatx80_init(0x3FFF, 0xA0A9C290D97CC06C),
+ make_floatx80_init(0x3FFF, 0xA22659EBEBC0630A),
+ make_floatx80_init(0x3FFF, 0xA388B4AFF6EF0EC9),
+ make_floatx80_init(0x3FFF, 0xA4D35F1061D292C4),
+ make_floatx80_init(0x3FFF, 0xA60895DCFBE3187E),
+ make_floatx80_init(0x3FFF, 0xA72A51DC7367BEAC),
+ make_floatx80_init(0x3FFF, 0xA83A51530956168F),
+ make_floatx80_init(0x3FFF, 0xA93A20077539546E),
+ make_floatx80_init(0x3FFF, 0xAA9E7245023B2605),
+ make_floatx80_init(0x3FFF, 0xAC4C84BA6FE4D58F),
+ make_floatx80_init(0x3FFF, 0xADCE4A4A606B9712),
+ make_floatx80_init(0x3FFF, 0xAF2A2DCD8D263C9C),
+ make_floatx80_init(0x3FFF, 0xB0656F81F22265C7),
+ make_floatx80_init(0x3FFF, 0xB18465150F71496A),
+ make_floatx80_init(0x3FFF, 0xB28AAA156F9ADA35),
+ make_floatx80_init(0x3FFF, 0xB37B44FF3766B895),
+ make_floatx80_init(0x3FFF, 0xB458C3DCE9630433),
+ make_floatx80_init(0x3FFF, 0xB525529D562246BD),
+ make_floatx80_init(0x3FFF, 0xB5E2CCA95F9D88CC),
+ make_floatx80_init(0x3FFF, 0xB692CADA7ACA1ADA),
+ make_floatx80_init(0x3FFF, 0xB736AEA7A6925838),
+ make_floatx80_init(0x3FFF, 0xB7CFAB287E9F7B36),
+ make_floatx80_init(0x3FFF, 0xB85ECC66CB219835),
+ make_floatx80_init(0x3FFF, 0xB8E4FD5A20A593DA),
+ make_floatx80_init(0x3FFF, 0xB99F41F64AFF9BB5),
+ make_floatx80_init(0x3FFF, 0xBA7F1E17842BBE7B),
+ make_floatx80_init(0x3FFF, 0xBB4712857637E17D),
+ make_floatx80_init(0x3FFF, 0xBBFABE8A4788DF6F),
+ make_floatx80_init(0x3FFF, 0xBC9D0FAD2B689D79),
+ make_floatx80_init(0x3FFF, 0xBD306A39471ECD86),
+ make_floatx80_init(0x3FFF, 0xBDB6C731856AF18A),
+ make_floatx80_init(0x3FFF, 0xBE31CAC502E80D70),
+ make_floatx80_init(0x3FFF, 0xBEA2D55CE33194E2),
+ make_floatx80_init(0x3FFF, 0xBF0B10B7C03128F0),
+ make_floatx80_init(0x3FFF, 0xBF6B7A18DACB778D),
+ make_floatx80_init(0x3FFF, 0xBFC4EA4663FA18F6),
+ make_floatx80_init(0x3FFF, 0xC0181BDE8B89A454),
+ make_floatx80_init(0x3FFF, 0xC065B066CFBF6439),
+ make_floatx80_init(0x3FFF, 0xC0AE345F56340AE6),
+ make_floatx80_init(0x3FFF, 0xC0F222919CB9E6A7)
+};
#endif
diff --git a/target/m68k/translate.c b/target/m68k/translate.c
index 6d5bde0777..cef6f663ad 100644
--- a/target/m68k/translate.c
+++ b/target/m68k/translate.c
@@ -5042,6 +5042,9 @@ DISAS_INSN(fpu)
case 1: /* fint */
gen_helper_firound(cpu_env, cpu_dest, cpu_src);
break;
+ case 2: /* fsinh */
+ gen_helper_fsinh(cpu_env, cpu_dest, cpu_src);
+ break;
case 3: /* fintrz */
gen_helper_fitrunc(cpu_env, cpu_dest, cpu_src);
break;
@@ -5057,6 +5060,24 @@ DISAS_INSN(fpu)
case 0x06: /* flognp1 */
gen_helper_flognp1(cpu_env, cpu_dest, cpu_src);
break;
+ case 0x09: /* ftanh */
+ gen_helper_ftanh(cpu_env, cpu_dest, cpu_src);
+ break;
+ case 0x0a: /* fatan */
+ gen_helper_fatan(cpu_env, cpu_dest, cpu_src);
+ break;
+ case 0x0c: /* fasin */
+ gen_helper_fasin(cpu_env, cpu_dest, cpu_src);
+ break;
+ case 0x0d: /* fatanh */
+ gen_helper_fatanh(cpu_env, cpu_dest, cpu_src);
+ break;
+ case 0x0e: /* fsin */
+ gen_helper_fsin(cpu_env, cpu_dest, cpu_src);
+ break;
+ case 0x0f: /* ftan */
+ gen_helper_ftan(cpu_env, cpu_dest, cpu_src);
+ break;
case 0x10: /* fetox */
gen_helper_fetox(cpu_env, cpu_dest, cpu_src);
break;
@@ -5084,6 +5105,9 @@ DISAS_INSN(fpu)
case 0x5c: /* fdabs */
gen_helper_fdabs(cpu_env, cpu_dest, cpu_src);
break;
+ case 0x19: /* fcosh */
+ gen_helper_fcosh(cpu_env, cpu_dest, cpu_src);
+ break;
case 0x1a: /* fneg */
gen_helper_fneg(cpu_env, cpu_dest, cpu_src);
break;
@@ -5093,6 +5117,12 @@ DISAS_INSN(fpu)
case 0x5e: /* fdneg */
gen_helper_fdneg(cpu_env, cpu_dest, cpu_src);
break;
+ case 0x1c: /* facos */
+ gen_helper_facos(cpu_env, cpu_dest, cpu_src);
+ break;
+ case 0x1d: /* fcos */
+ gen_helper_fcos(cpu_env, cpu_dest, cpu_src);
+ break;
case 0x1e: /* fgetexp */
gen_helper_fgetexp(cpu_env, cpu_dest, cpu_src);
break;
@@ -5150,6 +5180,14 @@ DISAS_INSN(fpu)
case 0x6c: /* fdsub */
gen_helper_fdsub(cpu_env, cpu_dest, cpu_src, cpu_dest);
break;
+ case 0x30: case 0x31: case 0x32:
+ case 0x33: case 0x34: case 0x35:
+ case 0x36: case 0x37: {
+ TCGv_ptr cpu_dest2 = gen_fp_ptr(REG(ext, 0));
+ gen_helper_fsincos(cpu_env, cpu_dest, cpu_dest2, cpu_src);
+ tcg_temp_free_ptr(cpu_dest2);
+ }
+ break;
case 0x38: /* fcmp */
gen_helper_fcmp(cpu_env, cpu_src, cpu_dest);
return;