summary refs log tree commit diff stats
path: root/target
diff options
context:
space:
mode:
Diffstat (limited to 'target')
-rw-r--r--target/alpha/cpu.c15
-rw-r--r--target/hppa/cpu.c15
-rw-r--r--target/i386/Makefile.objs2
-rw-r--r--target/i386/cpu.c14
-rw-r--r--target/i386/cpu.h3
-rw-r--r--target/i386/kvm.c13
-rw-r--r--target/i386/machine.c20
-rw-r--r--target/i386/monitor.c66
-rw-r--r--target/i386/sev-stub.c51
-rw-r--r--target/i386/sev.c811
-rw-r--r--target/i386/sev_i386.h88
-rw-r--r--target/i386/trace-events10
-rw-r--r--target/lm32/cpu.c15
-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
-rw-r--r--target/sh4/cpu.c15
-rw-r--r--target/tricore/helper.c2
21 files changed, 3108 insertions, 57 deletions
diff --git a/target/alpha/cpu.c b/target/alpha/cpu.c
index 55675ce419..b08078e7fc 100644
--- a/target/alpha/cpu.c
+++ b/target/alpha/cpu.c
@@ -71,18 +71,6 @@ static void alpha_cpu_realizefn(DeviceState *dev, Error **errp)
     acc->parent_realize(dev, errp);
 }
 
-/* Sort alphabetically by type name. */
-static gint alpha_cpu_list_compare(gconstpointer a, gconstpointer b)
-{
-    ObjectClass *class_a = (ObjectClass *)a;
-    ObjectClass *class_b = (ObjectClass *)b;
-    const char *name_a, *name_b;
-
-    name_a = object_class_get_name(class_a);
-    name_b = object_class_get_name(class_b);
-    return strcmp(name_a, name_b);
-}
-
 static void alpha_cpu_list_entry(gpointer data, gpointer user_data)
 {
     ObjectClass *oc = data;
@@ -100,8 +88,7 @@ void alpha_cpu_list(FILE *f, fprintf_function cpu_fprintf)
     };
     GSList *list;
 
-    list = object_class_get_list(TYPE_ALPHA_CPU, false);
-    list = g_slist_sort(list, alpha_cpu_list_compare);
+    list = object_class_get_list_sorted(TYPE_ALPHA_CPU, false);
     (*cpu_fprintf)(f, "Available CPUs:\n");
     g_slist_foreach(list, alpha_cpu_list_entry, &s);
     g_slist_free(list);
diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c
index 969f628f0a..c261b6b090 100644
--- a/target/hppa/cpu.c
+++ b/target/hppa/cpu.c
@@ -110,18 +110,6 @@ static void hppa_cpu_realizefn(DeviceState *dev, Error **errp)
 #endif
 }
 
-/* Sort hppabetically by type name. */
-static gint hppa_cpu_list_compare(gconstpointer a, gconstpointer b)
-{
-    ObjectClass *class_a = (ObjectClass *)a;
-    ObjectClass *class_b = (ObjectClass *)b;
-    const char *name_a, *name_b;
-
-    name_a = object_class_get_name(class_a);
-    name_b = object_class_get_name(class_b);
-    return strcmp(name_a, name_b);
-}
-
 static void hppa_cpu_list_entry(gpointer data, gpointer user_data)
 {
     ObjectClass *oc = data;
@@ -138,8 +126,7 @@ void hppa_cpu_list(FILE *f, fprintf_function cpu_fprintf)
     };
     GSList *list;
 
-    list = object_class_get_list(TYPE_HPPA_CPU, false);
-    list = g_slist_sort(list, hppa_cpu_list_compare);
+    list = object_class_get_list_sorted(TYPE_HPPA_CPU, false);
     (*cpu_fprintf)(f, "Available CPUs:\n");
     g_slist_foreach(list, hppa_cpu_list_entry, &s);
     g_slist_free(list);
diff --git a/target/i386/Makefile.objs b/target/i386/Makefile.objs
index f5c6ef20a7..04678f5503 100644
--- a/target/i386/Makefile.objs
+++ b/target/i386/Makefile.objs
@@ -5,7 +5,9 @@ obj-$(CONFIG_TCG) += int_helper.o mem_helper.o misc_helper.o mpx_helper.o
 obj-$(CONFIG_TCG) += seg_helper.o smm_helper.o svm_helper.o
 obj-$(CONFIG_SOFTMMU) += machine.o arch_memory_mapping.o arch_dump.o monitor.o
 obj-$(CONFIG_KVM) += kvm.o hyperv.o
+obj-$(CONFIG_SEV) += sev.o
 obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o
+obj-$(call lnot,$(CONFIG_SEV)) += sev-stub.o
 # HAX support
 ifdef CONFIG_WIN32
 obj-$(CONFIG_HAX) += hax-all.o hax-mem.o hax-windows.o
diff --git a/target/i386/cpu.c b/target/i386/cpu.c
index ec1efd3a3c..6bb4ce8719 100644
--- a/target/i386/cpu.c
+++ b/target/i386/cpu.c
@@ -26,6 +26,7 @@
 #include "sysemu/hvf.h"
 #include "sysemu/cpus.h"
 #include "kvm_i386.h"
+#include "sev_i386.h"
 
 #include "qemu/error-report.h"
 #include "qemu/option.h"
@@ -3672,6 +3673,13 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
         *ecx = 0;
         *edx = 0;
         break;
+    case 0x8000001F:
+        *eax = sev_enabled() ? 0x2 : 0;
+        *ebx = sev_get_cbit_position();
+        *ebx |= sev_get_reduced_phys_bits() << 6;
+        *ecx = 0;
+        *edx = 0;
+        break;
     default:
         /* reserved values: zero */
         *eax = 0;
@@ -3705,6 +3713,7 @@ static void x86_cpu_reset(CPUState *s)
     cpu_x86_update_cr0(env, 0x60000010);
     env->a20_mask = ~0x0;
     env->smbase = 0x30000;
+    env->msr_smi_count = 0;
 
     env->idt.limit = 0xffff;
     env->gdt.limit = 0xffff;
@@ -4101,6 +4110,11 @@ static void x86_cpu_expand_features(X86CPU *cpu, Error **errp)
         if (env->features[FEAT_8000_0001_ECX] & CPUID_EXT3_SVM) {
             x86_cpu_adjust_level(cpu, &env->cpuid_min_xlevel, 0x8000000A);
         }
+
+        /* SEV requires CPUID[0x8000001F] */
+        if (sev_enabled()) {
+            x86_cpu_adjust_level(cpu, &env->cpuid_min_xlevel, 0x8000001F);
+        }
     }
 
     /* Set cpuid_*level* based on cpuid_min_*level, if not explicitly set */
diff --git a/target/i386/cpu.h b/target/i386/cpu.h
index 0c3f51445e..2e2bab5ff3 100644
--- a/target/i386/cpu.h
+++ b/target/i386/cpu.h
@@ -1,3 +1,4 @@
+
 /*
  * i386 virtual CPU header
  *
@@ -359,6 +360,7 @@ typedef enum X86Seg {
 #define MSR_P6_PERFCTR0                 0xc1
 
 #define MSR_IA32_SMBASE                 0x9e
+#define MSR_SMI_COUNT                   0x34
 #define MSR_MTRRcap                     0xfe
 #define MSR_MTRRcap_VCNT                8
 #define MSR_MTRRcap_FIXRANGE_SUPPORT    (1 << 8)
@@ -1142,6 +1144,7 @@ typedef struct CPUX86State {
 
     uint64_t pat;
     uint32_t smbase;
+    uint64_t msr_smi_count;
 
     uint32_t pkru;
 
diff --git a/target/i386/kvm.c b/target/i386/kvm.c
index d996cca68b..d23fff12f5 100644
--- a/target/i386/kvm.c
+++ b/target/i386/kvm.c
@@ -92,6 +92,7 @@ static bool has_msr_hv_stimer;
 static bool has_msr_hv_frequencies;
 static bool has_msr_xss;
 static bool has_msr_spec_ctrl;
+static bool has_msr_smi_count;
 
 static uint32_t has_architectural_pmu_version;
 static uint32_t num_architectural_pmu_gp_counters;
@@ -1151,6 +1152,9 @@ static int kvm_get_supported_msrs(KVMState *s)
                 case MSR_IA32_SMBASE:
                     has_msr_smbase = true;
                     break;
+                case MSR_SMI_COUNT:
+                    has_msr_smi_count = true;
+                    break;
                 case MSR_IA32_MISC_ENABLE:
                     has_msr_misc_enable = true;
                     break;
@@ -1660,6 +1664,9 @@ static int kvm_put_msrs(X86CPU *cpu, int level)
     if (has_msr_smbase) {
         kvm_msr_entry_add(cpu, MSR_IA32_SMBASE, env->smbase);
     }
+    if (has_msr_smi_count) {
+        kvm_msr_entry_add(cpu, MSR_SMI_COUNT, env->msr_smi_count);
+    }
     if (has_msr_bndcfgs) {
         kvm_msr_entry_add(cpu, MSR_IA32_BNDCFGS, env->msr_bndcfgs);
     }
@@ -2025,6 +2032,9 @@ static int kvm_get_msrs(X86CPU *cpu)
     if (has_msr_smbase) {
         kvm_msr_entry_add(cpu, MSR_IA32_SMBASE, 0);
     }
+    if (has_msr_smi_count) {
+        kvm_msr_entry_add(cpu, MSR_SMI_COUNT, 0);
+    }
     if (has_msr_feature_control) {
         kvm_msr_entry_add(cpu, MSR_IA32_FEATURE_CONTROL, 0);
     }
@@ -2265,6 +2275,9 @@ static int kvm_get_msrs(X86CPU *cpu)
         case MSR_IA32_SMBASE:
             env->smbase = msrs[i].data;
             break;
+        case MSR_SMI_COUNT:
+            env->msr_smi_count = msrs[i].data;
+            break;
         case MSR_IA32_FEATURE_CONTROL:
             env->msr_ia32_feature_control = msrs[i].data;
             break;
diff --git a/target/i386/machine.c b/target/i386/machine.c
index c05fe6fb1a..bd2d82e91b 100644
--- a/target/i386/machine.c
+++ b/target/i386/machine.c
@@ -395,6 +395,25 @@ static const VMStateDescription vmstate_msr_tsc_adjust = {
     }
 };
 
+static bool msr_smi_count_needed(void *opaque)
+{
+    X86CPU *cpu = opaque;
+    CPUX86State *env = &cpu->env;
+
+    return env->msr_smi_count != 0;
+}
+
+static const VMStateDescription vmstate_msr_smi_count = {
+    .name = "cpu/msr_smi_count",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .needed = msr_smi_count_needed,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT64(env.msr_smi_count, X86CPU),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
 static bool tscdeadline_needed(void *opaque)
 {
     X86CPU *cpu = opaque;
@@ -989,6 +1008,7 @@ VMStateDescription vmstate_x86_cpu = {
         &vmstate_avx512,
         &vmstate_xss,
         &vmstate_tsc_khz,
+        &vmstate_msr_smi_count,
 #ifdef TARGET_X86_64
         &vmstate_pkru,
 #endif
diff --git a/target/i386/monitor.c b/target/i386/monitor.c
index 75429129fd..011419eba2 100644
--- a/target/i386/monitor.c
+++ b/target/i386/monitor.c
@@ -29,7 +29,11 @@
 #include "qapi/qmp/qdict.h"
 #include "hw/i386/pc.h"
 #include "sysemu/kvm.h"
+#include "sysemu/sev.h"
 #include "hmp.h"
+#include "qapi/error.h"
+#include "sev_i386.h"
+#include "qapi/qapi-commands-misc.h"
 
 
 static void print_pte(Monitor *mon, CPUArchState *env, hwaddr addr,
@@ -661,3 +665,65 @@ void hmp_info_io_apic(Monitor *mon, const QDict *qdict)
         ioapic_dump_state(mon, qdict);
     }
 }
+
+SevInfo *qmp_query_sev(Error **errp)
+{
+    SevInfo *info;
+
+    info = sev_get_info();
+    if (!info) {
+        error_setg(errp, "SEV feature is not available");
+        return NULL;
+    }
+
+    return info;
+}
+
+void hmp_info_sev(Monitor *mon, const QDict *qdict)
+{
+    SevInfo *info = sev_get_info();
+
+    if (info && info->enabled) {
+        monitor_printf(mon, "handle: %d\n", info->handle);
+        monitor_printf(mon, "state: %s\n", SevState_str(info->state));
+        monitor_printf(mon, "build: %d\n", info->build_id);
+        monitor_printf(mon, "api version: %d.%d\n",
+                       info->api_major, info->api_minor);
+        monitor_printf(mon, "debug: %s\n",
+                       info->policy & SEV_POLICY_NODBG ? "off" : "on");
+        monitor_printf(mon, "key-sharing: %s\n",
+                       info->policy & SEV_POLICY_NOKS ? "off" : "on");
+    } else {
+        monitor_printf(mon, "SEV is not enabled\n");
+    }
+}
+
+SevLaunchMeasureInfo *qmp_query_sev_launch_measure(Error **errp)
+{
+    char *data;
+    SevLaunchMeasureInfo *info;
+
+    data = sev_get_launch_measurement();
+    if (!data) {
+        error_setg(errp, "Measurement is not available");
+        return NULL;
+    }
+
+    info = g_malloc0(sizeof(*info));
+    info->data = data;
+
+    return info;
+}
+
+SevCapability *qmp_query_sev_capabilities(Error **errp)
+{
+    SevCapability *data;
+
+    data = sev_get_capabilities();
+    if (!data) {
+        error_setg(errp, "SEV feature is not available");
+        return NULL;
+    }
+
+    return data;
+}
diff --git a/target/i386/sev-stub.c b/target/i386/sev-stub.c
new file mode 100644
index 0000000000..59a003a4eb
--- /dev/null
+++ b/target/i386/sev-stub.c
@@ -0,0 +1,51 @@
+/*
+ * QEMU SEV stub
+ *
+ * Copyright Advanced Micro Devices 2018
+ *
+ * Authors:
+ *      Brijesh Singh <brijesh.singh@amd.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "sev_i386.h"
+
+SevInfo *sev_get_info(void)
+{
+    return NULL;
+}
+
+bool sev_enabled(void)
+{
+    return false;
+}
+
+uint64_t sev_get_me_mask(void)
+{
+    return ~0;
+}
+
+uint32_t sev_get_cbit_position(void)
+{
+    return 0;
+}
+
+uint32_t sev_get_reduced_phys_bits(void)
+{
+    return 0;
+}
+
+char *sev_get_launch_measurement(void)
+{
+    return NULL;
+}
+
+SevCapability *sev_get_capabilities(void)
+{
+    return NULL;
+}
diff --git a/target/i386/sev.c b/target/i386/sev.c
new file mode 100644
index 0000000000..019d84cef2
--- /dev/null
+++ b/target/i386/sev.c
@@ -0,0 +1,811 @@
+/*
+ * QEMU SEV support
+ *
+ * Copyright Advanced Micro Devices 2016-2018
+ *
+ * Author:
+ *      Brijesh Singh <brijesh.singh@amd.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include <linux/kvm.h>
+#include <linux/psp-sev.h>
+
+#include <sys/ioctl.h>
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qom/object_interfaces.h"
+#include "qemu/base64.h"
+#include "sysemu/kvm.h"
+#include "sev_i386.h"
+#include "sysemu/sysemu.h"
+#include "trace.h"
+#include "migration/blocker.h"
+
+#define DEFAULT_GUEST_POLICY    0x1 /* disable debug */
+#define DEFAULT_SEV_DEVICE      "/dev/sev"
+
+static SEVState *sev_state;
+static Error *sev_mig_blocker;
+
+static const char *const sev_fw_errlist[] = {
+    "",
+    "Platform state is invalid",
+    "Guest state is invalid",
+    "Platform configuration is invalid",
+    "Buffer too small",
+    "Platform is already owned",
+    "Certificate is invalid",
+    "Policy is not allowed",
+    "Guest is not active",
+    "Invalid address",
+    "Bad signature",
+    "Bad measurement",
+    "Asid is already owned",
+    "Invalid ASID",
+    "WBINVD is required",
+    "DF_FLUSH is required",
+    "Guest handle is invalid",
+    "Invalid command",
+    "Guest is active",
+    "Hardware error",
+    "Hardware unsafe",
+    "Feature not supported",
+    "Invalid parameter"
+};
+
+#define SEV_FW_MAX_ERROR      ARRAY_SIZE(sev_fw_errlist)
+
+static int
+sev_ioctl(int fd, int cmd, void *data, int *error)
+{
+    int r;
+    struct kvm_sev_cmd input;
+
+    memset(&input, 0x0, sizeof(input));
+
+    input.id = cmd;
+    input.sev_fd = fd;
+    input.data = (__u64)(unsigned long)data;
+
+    r = kvm_vm_ioctl(kvm_state, KVM_MEMORY_ENCRYPT_OP, &input);
+
+    if (error) {
+        *error = input.error;
+    }
+
+    return r;
+}
+
+static int
+sev_platform_ioctl(int fd, int cmd, void *data, int *error)
+{
+    int r;
+    struct sev_issue_cmd arg;
+
+    arg.cmd = cmd;
+    arg.data = (unsigned long)data;
+    r = ioctl(fd, SEV_ISSUE_CMD, &arg);
+    if (error) {
+        *error = arg.error;
+    }
+
+    return r;
+}
+
+static const char *
+fw_error_to_str(int code)
+{
+    if (code < 0 || code >= SEV_FW_MAX_ERROR) {
+        return "unknown error";
+    }
+
+    return sev_fw_errlist[code];
+}
+
+static bool
+sev_check_state(SevState state)
+{
+    assert(sev_state);
+    return sev_state->state == state ? true : false;
+}
+
+static void
+sev_set_guest_state(SevState new_state)
+{
+    assert(new_state < SEV_STATE__MAX);
+    assert(sev_state);
+
+    trace_kvm_sev_change_state(SevState_str(sev_state->state),
+                               SevState_str(new_state));
+    sev_state->state = new_state;
+}
+
+static void
+sev_ram_block_added(RAMBlockNotifier *n, void *host, size_t size)
+{
+    int r;
+    struct kvm_enc_region range;
+
+    range.addr = (__u64)(unsigned long)host;
+    range.size = size;
+
+    trace_kvm_memcrypt_register_region(host, size);
+    r = kvm_vm_ioctl(kvm_state, KVM_MEMORY_ENCRYPT_REG_REGION, &range);
+    if (r) {
+        error_report("%s: failed to register region (%p+%#zx) error '%s'",
+                     __func__, host, size, strerror(errno));
+        exit(1);
+    }
+}
+
+static void
+sev_ram_block_removed(RAMBlockNotifier *n, void *host, size_t size)
+{
+    int r;
+    struct kvm_enc_region range;
+
+    range.addr = (__u64)(unsigned long)host;
+    range.size = size;
+
+    trace_kvm_memcrypt_unregister_region(host, size);
+    r = kvm_vm_ioctl(kvm_state, KVM_MEMORY_ENCRYPT_UNREG_REGION, &range);
+    if (r) {
+        error_report("%s: failed to unregister region (%p+%#zx)",
+                     __func__, host, size);
+    }
+}
+
+static struct RAMBlockNotifier sev_ram_notifier = {
+    .ram_block_added = sev_ram_block_added,
+    .ram_block_removed = sev_ram_block_removed,
+};
+
+static void
+qsev_guest_finalize(Object *obj)
+{
+}
+
+static char *
+qsev_guest_get_session_file(Object *obj, Error **errp)
+{
+    QSevGuestInfo *s = QSEV_GUEST_INFO(obj);
+
+    return s->session_file ? g_strdup(s->session_file) : NULL;
+}
+
+static void
+qsev_guest_set_session_file(Object *obj, const char *value, Error **errp)
+{
+    QSevGuestInfo *s = QSEV_GUEST_INFO(obj);
+
+    s->session_file = g_strdup(value);
+}
+
+static char *
+qsev_guest_get_dh_cert_file(Object *obj, Error **errp)
+{
+    QSevGuestInfo *s = QSEV_GUEST_INFO(obj);
+
+    return g_strdup(s->dh_cert_file);
+}
+
+static void
+qsev_guest_set_dh_cert_file(Object *obj, const char *value, Error **errp)
+{
+    QSevGuestInfo *s = QSEV_GUEST_INFO(obj);
+
+    s->dh_cert_file = g_strdup(value);
+}
+
+static char *
+qsev_guest_get_sev_device(Object *obj, Error **errp)
+{
+    QSevGuestInfo *sev = QSEV_GUEST_INFO(obj);
+
+    return g_strdup(sev->sev_device);
+}
+
+static void
+qsev_guest_set_sev_device(Object *obj, const char *value, Error **errp)
+{
+    QSevGuestInfo *sev = QSEV_GUEST_INFO(obj);
+
+    sev->sev_device = g_strdup(value);
+}
+
+static void
+qsev_guest_class_init(ObjectClass *oc, void *data)
+{
+    object_class_property_add_str(oc, "sev-device",
+                                  qsev_guest_get_sev_device,
+                                  qsev_guest_set_sev_device,
+                                  NULL);
+    object_class_property_set_description(oc, "sev-device",
+            "SEV device to use", NULL);
+    object_class_property_add_str(oc, "dh-cert-file",
+                                  qsev_guest_get_dh_cert_file,
+                                  qsev_guest_set_dh_cert_file,
+                                  NULL);
+    object_class_property_set_description(oc, "dh-cert-file",
+            "guest owners DH certificate (encoded with base64)", NULL);
+    object_class_property_add_str(oc, "session-file",
+                                  qsev_guest_get_session_file,
+                                  qsev_guest_set_session_file,
+                                  NULL);
+    object_class_property_set_description(oc, "session-file",
+            "guest owners session parameters (encoded with base64)", NULL);
+}
+
+static void
+qsev_guest_set_handle(Object *obj, Visitor *v, const char *name,
+                      void *opaque, Error **errp)
+{
+    QSevGuestInfo *sev = QSEV_GUEST_INFO(obj);
+    uint32_t value;
+
+    visit_type_uint32(v, name, &value, errp);
+    sev->handle = value;
+}
+
+static void
+qsev_guest_set_policy(Object *obj, Visitor *v, const char *name,
+                      void *opaque, Error **errp)
+{
+    QSevGuestInfo *sev = QSEV_GUEST_INFO(obj);
+    uint32_t value;
+
+    visit_type_uint32(v, name, &value, errp);
+    sev->policy = value;
+}
+
+static void
+qsev_guest_set_cbitpos(Object *obj, Visitor *v, const char *name,
+                       void *opaque, Error **errp)
+{
+    QSevGuestInfo *sev = QSEV_GUEST_INFO(obj);
+    uint32_t value;
+
+    visit_type_uint32(v, name, &value, errp);
+    sev->cbitpos = value;
+}
+
+static void
+qsev_guest_set_reduced_phys_bits(Object *obj, Visitor *v, const char *name,
+                                   void *opaque, Error **errp)
+{
+    QSevGuestInfo *sev = QSEV_GUEST_INFO(obj);
+    uint32_t value;
+
+    visit_type_uint32(v, name, &value, errp);
+    sev->reduced_phys_bits = value;
+}
+
+static void
+qsev_guest_get_policy(Object *obj, Visitor *v, const char *name,
+                      void *opaque, Error **errp)
+{
+    uint32_t value;
+    QSevGuestInfo *sev = QSEV_GUEST_INFO(obj);
+
+    value = sev->policy;
+    visit_type_uint32(v, name, &value, errp);
+}
+
+static void
+qsev_guest_get_handle(Object *obj, Visitor *v, const char *name,
+                      void *opaque, Error **errp)
+{
+    uint32_t value;
+    QSevGuestInfo *sev = QSEV_GUEST_INFO(obj);
+
+    value = sev->handle;
+    visit_type_uint32(v, name, &value, errp);
+}
+
+static void
+qsev_guest_get_cbitpos(Object *obj, Visitor *v, const char *name,
+                       void *opaque, Error **errp)
+{
+    uint32_t value;
+    QSevGuestInfo *sev = QSEV_GUEST_INFO(obj);
+
+    value = sev->cbitpos;
+    visit_type_uint32(v, name, &value, errp);
+}
+
+static void
+qsev_guest_get_reduced_phys_bits(Object *obj, Visitor *v, const char *name,
+                                   void *opaque, Error **errp)
+{
+    uint32_t value;
+    QSevGuestInfo *sev = QSEV_GUEST_INFO(obj);
+
+    value = sev->reduced_phys_bits;
+    visit_type_uint32(v, name, &value, errp);
+}
+
+static void
+qsev_guest_init(Object *obj)
+{
+    QSevGuestInfo *sev = QSEV_GUEST_INFO(obj);
+
+    sev->sev_device = g_strdup(DEFAULT_SEV_DEVICE);
+    sev->policy = DEFAULT_GUEST_POLICY;
+    object_property_add(obj, "policy", "uint32", qsev_guest_get_policy,
+                        qsev_guest_set_policy, NULL, NULL, NULL);
+    object_property_add(obj, "handle", "uint32", qsev_guest_get_handle,
+                        qsev_guest_set_handle, NULL, NULL, NULL);
+    object_property_add(obj, "cbitpos", "uint32", qsev_guest_get_cbitpos,
+                        qsev_guest_set_cbitpos, NULL, NULL, NULL);
+    object_property_add(obj, "reduced-phys-bits", "uint32",
+                        qsev_guest_get_reduced_phys_bits,
+                        qsev_guest_set_reduced_phys_bits, NULL, NULL, NULL);
+}
+
+/* sev guest info */
+static const TypeInfo qsev_guest_info = {
+    .parent = TYPE_OBJECT,
+    .name = TYPE_QSEV_GUEST_INFO,
+    .instance_size = sizeof(QSevGuestInfo),
+    .instance_finalize = qsev_guest_finalize,
+    .class_size = sizeof(QSevGuestInfoClass),
+    .class_init = qsev_guest_class_init,
+    .instance_init = qsev_guest_init,
+    .interfaces = (InterfaceInfo[]) {
+        { TYPE_USER_CREATABLE },
+        { }
+    }
+};
+
+static QSevGuestInfo *
+lookup_sev_guest_info(const char *id)
+{
+    Object *obj;
+    QSevGuestInfo *info;
+
+    obj = object_resolve_path_component(object_get_objects_root(), id);
+    if (!obj) {
+        return NULL;
+    }
+
+    info = (QSevGuestInfo *)
+            object_dynamic_cast(obj, TYPE_QSEV_GUEST_INFO);
+    if (!info) {
+        return NULL;
+    }
+
+    return info;
+}
+
+bool
+sev_enabled(void)
+{
+    return sev_state ? true : false;
+}
+
+uint64_t
+sev_get_me_mask(void)
+{
+    return sev_state ? sev_state->me_mask : ~0;
+}
+
+uint32_t
+sev_get_cbit_position(void)
+{
+    return sev_state ? sev_state->cbitpos : 0;
+}
+
+uint32_t
+sev_get_reduced_phys_bits(void)
+{
+    return sev_state ? sev_state->reduced_phys_bits : 0;
+}
+
+SevInfo *
+sev_get_info(void)
+{
+    SevInfo *info;
+
+    info = g_new0(SevInfo, 1);
+    info->enabled = sev_state ? true : false;
+
+    if (info->enabled) {
+        info->api_major = sev_state->api_major;
+        info->api_minor = sev_state->api_minor;
+        info->build_id = sev_state->build_id;
+        info->policy = sev_state->policy;
+        info->state = sev_state->state;
+        info->handle = sev_state->handle;
+    }
+
+    return info;
+}
+
+static int
+sev_get_pdh_info(int fd, guchar **pdh, size_t *pdh_len, guchar **cert_chain,
+                 size_t *cert_chain_len)
+{
+    guchar *pdh_data, *cert_chain_data;
+    struct sev_user_data_pdh_cert_export export = {};
+    int err, r;
+
+    /* query the certificate length */
+    r = sev_platform_ioctl(fd, SEV_PDH_CERT_EXPORT, &export, &err);
+    if (r < 0) {
+        if (err != SEV_RET_INVALID_LEN) {
+            error_report("failed to export PDH cert ret=%d fw_err=%d (%s)",
+                         r, err, fw_error_to_str(err));
+            return 1;
+        }
+    }
+
+    pdh_data = g_new(guchar, export.pdh_cert_len);
+    cert_chain_data = g_new(guchar, export.cert_chain_len);
+    export.pdh_cert_address = (unsigned long)pdh_data;
+    export.cert_chain_address = (unsigned long)cert_chain_data;
+
+    r = sev_platform_ioctl(fd, SEV_PDH_CERT_EXPORT, &export, &err);
+    if (r < 0) {
+        error_report("failed to export PDH cert ret=%d fw_err=%d (%s)",
+                     r, err, fw_error_to_str(err));
+        goto e_free;
+    }
+
+    *pdh = pdh_data;
+    *pdh_len = export.pdh_cert_len;
+    *cert_chain = cert_chain_data;
+    *cert_chain_len = export.cert_chain_len;
+    return 0;
+
+e_free:
+    g_free(pdh_data);
+    g_free(cert_chain_data);
+    return 1;
+}
+
+SevCapability *
+sev_get_capabilities(void)
+{
+    SevCapability *cap;
+    guchar *pdh_data, *cert_chain_data;
+    size_t pdh_len = 0, cert_chain_len = 0;
+    uint32_t ebx;
+    int fd;
+
+    fd = open(DEFAULT_SEV_DEVICE, O_RDWR);
+    if (fd < 0) {
+        error_report("%s: Failed to open %s '%s'", __func__,
+                     DEFAULT_SEV_DEVICE, strerror(errno));
+        return NULL;
+    }
+
+    if (sev_get_pdh_info(fd, &pdh_data, &pdh_len,
+                         &cert_chain_data, &cert_chain_len)) {
+        return NULL;
+    }
+
+    cap = g_new0(SevCapability, 1);
+    cap->pdh = g_base64_encode(pdh_data, pdh_len);
+    cap->cert_chain = g_base64_encode(cert_chain_data, cert_chain_len);
+
+    host_cpuid(0x8000001F, 0, NULL, &ebx, NULL, NULL);
+    cap->cbitpos = ebx & 0x3f;
+
+    /*
+     * When SEV feature is enabled, we loose one bit in guest physical
+     * addressing.
+     */
+    cap->reduced_phys_bits = 1;
+
+    g_free(pdh_data);
+    g_free(cert_chain_data);
+
+    close(fd);
+    return cap;
+}
+
+static int
+sev_read_file_base64(const char *filename, guchar **data, gsize *len)
+{
+    gsize sz;
+    gchar *base64;
+    GError *error = NULL;
+
+    if (!g_file_get_contents(filename, &base64, &sz, &error)) {
+        error_report("failed to read '%s' (%s)", filename, error->message);
+        return -1;
+    }
+
+    *data = g_base64_decode(base64, len);
+    return 0;
+}
+
+static int
+sev_launch_start(SEVState *s)
+{
+    gsize sz;
+    int ret = 1;
+    int fw_error;
+    QSevGuestInfo *sev = s->sev_info;
+    struct kvm_sev_launch_start *start;
+    guchar *session = NULL, *dh_cert = NULL;
+
+    start = g_new0(struct kvm_sev_launch_start, 1);
+
+    start->handle = object_property_get_int(OBJECT(sev), "handle",
+                                            &error_abort);
+    start->policy = object_property_get_int(OBJECT(sev), "policy",
+                                            &error_abort);
+    if (sev->session_file) {
+        if (sev_read_file_base64(sev->session_file, &session, &sz) < 0) {
+            return 1;
+        }
+        start->session_uaddr = (unsigned long)session;
+        start->session_len = sz;
+    }
+
+    if (sev->dh_cert_file) {
+        if (sev_read_file_base64(sev->dh_cert_file, &dh_cert, &sz) < 0) {
+            return 1;
+        }
+        start->dh_uaddr = (unsigned long)dh_cert;
+        start->dh_len = sz;
+    }
+
+    trace_kvm_sev_launch_start(start->policy, session, dh_cert);
+    ret = sev_ioctl(s->sev_fd, KVM_SEV_LAUNCH_START, start, &fw_error);
+    if (ret < 0) {
+        error_report("%s: LAUNCH_START ret=%d fw_error=%d '%s'",
+                __func__, ret, fw_error, fw_error_to_str(fw_error));
+        return 1;
+    }
+
+    object_property_set_int(OBJECT(sev), start->handle, "handle",
+                            &error_abort);
+    sev_set_guest_state(SEV_STATE_LAUNCH_UPDATE);
+    s->handle = start->handle;
+    s->policy = start->policy;
+
+    g_free(start);
+    g_free(session);
+    g_free(dh_cert);
+
+    return 0;
+}
+
+static int
+sev_launch_update_data(uint8_t *addr, uint64_t len)
+{
+    int ret, fw_error;
+    struct kvm_sev_launch_update_data update;
+
+    if (!addr || !len) {
+        return 1;
+    }
+
+    update.uaddr = (__u64)(unsigned long)addr;
+    update.len = len;
+    trace_kvm_sev_launch_update_data(addr, len);
+    ret = sev_ioctl(sev_state->sev_fd, KVM_SEV_LAUNCH_UPDATE_DATA,
+                    &update, &fw_error);
+    if (ret) {
+        error_report("%s: LAUNCH_UPDATE ret=%d fw_error=%d '%s'",
+                __func__, ret, fw_error, fw_error_to_str(fw_error));
+    }
+
+    return ret;
+}
+
+static void
+sev_launch_get_measure(Notifier *notifier, void *unused)
+{
+    int ret, error;
+    guchar *data;
+    SEVState *s = sev_state;
+    struct kvm_sev_launch_measure *measurement;
+
+    if (!sev_check_state(SEV_STATE_LAUNCH_UPDATE)) {
+        return;
+    }
+
+    measurement = g_new0(struct kvm_sev_launch_measure, 1);
+
+    /* query the measurement blob length */
+    ret = sev_ioctl(sev_state->sev_fd, KVM_SEV_LAUNCH_MEASURE,
+                    measurement, &error);
+    if (!measurement->len) {
+        error_report("%s: LAUNCH_MEASURE ret=%d fw_error=%d '%s'",
+                     __func__, ret, error, fw_error_to_str(errno));
+        goto free_measurement;
+    }
+
+    data = g_new0(guchar, measurement->len);
+    measurement->uaddr = (unsigned long)data;
+
+    /* get the measurement blob */
+    ret = sev_ioctl(sev_state->sev_fd, KVM_SEV_LAUNCH_MEASURE,
+                    measurement, &error);
+    if (ret) {
+        error_report("%s: LAUNCH_MEASURE ret=%d fw_error=%d '%s'",
+                     __func__, ret, error, fw_error_to_str(errno));
+        goto free_data;
+    }
+
+    sev_set_guest_state(SEV_STATE_LAUNCH_SECRET);
+
+    /* encode the measurement value and emit the event */
+    s->measurement = g_base64_encode(data, measurement->len);
+    trace_kvm_sev_launch_measurement(s->measurement);
+
+free_data:
+    g_free(data);
+free_measurement:
+    g_free(measurement);
+}
+
+char *
+sev_get_launch_measurement(void)
+{
+    if (sev_state &&
+        sev_state->state >= SEV_STATE_LAUNCH_SECRET) {
+        return g_strdup(sev_state->measurement);
+    }
+
+    return NULL;
+}
+
+static Notifier sev_machine_done_notify = {
+    .notify = sev_launch_get_measure,
+};
+
+static void
+sev_launch_finish(SEVState *s)
+{
+    int ret, error;
+    Error *local_err = NULL;
+
+    trace_kvm_sev_launch_finish();
+    ret = sev_ioctl(sev_state->sev_fd, KVM_SEV_LAUNCH_FINISH, 0, &error);
+    if (ret) {
+        error_report("%s: LAUNCH_FINISH ret=%d fw_error=%d '%s'",
+                     __func__, ret, error, fw_error_to_str(error));
+        exit(1);
+    }
+
+    sev_set_guest_state(SEV_STATE_RUNNING);
+
+    /* add migration blocker */
+    error_setg(&sev_mig_blocker,
+               "SEV: Migration is not implemented");
+    ret = migrate_add_blocker(sev_mig_blocker, &local_err);
+    if (local_err) {
+        error_report_err(local_err);
+        error_free(sev_mig_blocker);
+        exit(1);
+    }
+}
+
+static void
+sev_vm_state_change(void *opaque, int running, RunState state)
+{
+    SEVState *s = opaque;
+
+    if (running) {
+        if (!sev_check_state(SEV_STATE_RUNNING)) {
+            sev_launch_finish(s);
+        }
+    }
+}
+
+void *
+sev_guest_init(const char *id)
+{
+    SEVState *s;
+    char *devname;
+    int ret, fw_error;
+    uint32_t ebx;
+    uint32_t host_cbitpos;
+    struct sev_user_data_status status = {};
+
+    s = g_new0(SEVState, 1);
+    s->sev_info = lookup_sev_guest_info(id);
+    if (!s->sev_info) {
+        error_report("%s: '%s' is not a valid '%s' object",
+                     __func__, id, TYPE_QSEV_GUEST_INFO);
+        goto err;
+    }
+
+    sev_state = s;
+    s->state = SEV_STATE_UNINIT;
+
+    host_cpuid(0x8000001F, 0, NULL, &ebx, NULL, NULL);
+    host_cbitpos = ebx & 0x3f;
+
+    s->cbitpos = object_property_get_int(OBJECT(s->sev_info), "cbitpos", NULL);
+    if (host_cbitpos != s->cbitpos) {
+        error_report("%s: cbitpos check failed, host '%d' requested '%d'",
+                     __func__, host_cbitpos, s->cbitpos);
+        goto err;
+    }
+
+    s->reduced_phys_bits = object_property_get_int(OBJECT(s->sev_info),
+                                        "reduced-phys-bits", NULL);
+    if (s->reduced_phys_bits < 1) {
+        error_report("%s: reduced_phys_bits check failed, it should be >=1,"
+                     "' requested '%d'", __func__, s->reduced_phys_bits);
+        goto err;
+    }
+
+    s->me_mask = ~(1UL << s->cbitpos);
+
+    devname = object_property_get_str(OBJECT(s->sev_info), "sev-device", NULL);
+    s->sev_fd = open(devname, O_RDWR);
+    if (s->sev_fd < 0) {
+        error_report("%s: Failed to open %s '%s'", __func__,
+                     devname, strerror(errno));
+        goto err;
+    }
+    g_free(devname);
+
+    ret = sev_platform_ioctl(s->sev_fd, SEV_PLATFORM_STATUS, &status,
+                             &fw_error);
+    if (ret) {
+        error_report("%s: failed to get platform status ret=%d"
+                     "fw_error='%d: %s'", __func__, ret, fw_error,
+                     fw_error_to_str(fw_error));
+        goto err;
+    }
+    s->build_id = status.build;
+    s->api_major = status.api_major;
+    s->api_minor = status.api_minor;
+
+    trace_kvm_sev_init();
+    ret = sev_ioctl(s->sev_fd, KVM_SEV_INIT, NULL, &fw_error);
+    if (ret) {
+        error_report("%s: failed to initialize ret=%d fw_error=%d '%s'",
+                     __func__, ret, fw_error, fw_error_to_str(fw_error));
+        goto err;
+    }
+
+    ret = sev_launch_start(s);
+    if (ret) {
+        error_report("%s: failed to create encryption context", __func__);
+        goto err;
+    }
+
+    ram_block_notifier_add(&sev_ram_notifier);
+    qemu_add_machine_init_done_notifier(&sev_machine_done_notify);
+    qemu_add_vm_change_state_handler(sev_vm_state_change, s);
+
+    return s;
+err:
+    g_free(sev_state);
+    sev_state = NULL;
+    return NULL;
+}
+
+int
+sev_encrypt_data(void *handle, uint8_t *ptr, uint64_t len)
+{
+    assert(handle);
+
+    /* if SEV is in update state then encrypt the data else do nothing */
+    if (sev_check_state(SEV_STATE_LAUNCH_UPDATE)) {
+        return sev_launch_update_data(ptr, len);
+    }
+
+    return 0;
+}
+
+static void
+sev_register_types(void)
+{
+    type_register_static(&qsev_guest_info);
+}
+
+type_init(sev_register_types);
diff --git a/target/i386/sev_i386.h b/target/i386/sev_i386.h
new file mode 100644
index 0000000000..b8622dfb1e
--- /dev/null
+++ b/target/i386/sev_i386.h
@@ -0,0 +1,88 @@
+/*
+ * QEMU Secure Encrypted Virutualization (SEV) support
+ *
+ * Copyright: Advanced Micro Devices, 2016-2018
+ *
+ * Authors:
+ *  Brijesh Singh <brijesh.singh@amd.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef QEMU_SEV_I386_H
+#define QEMU_SEV_I386_H
+
+#include "qom/object.h"
+#include "qapi/error.h"
+#include "sysemu/kvm.h"
+#include "sysemu/sev.h"
+#include "qemu/error-report.h"
+#include "qapi/qapi-commands-misc.h"
+
+#define SEV_POLICY_NODBG        0x1
+#define SEV_POLICY_NOKS         0x2
+#define SEV_POLICY_ES           0x4
+#define SEV_POLICY_NOSEND       0x8
+#define SEV_POLICY_DOMAIN       0x10
+#define SEV_POLICY_SEV          0x20
+
+#define TYPE_QSEV_GUEST_INFO "sev-guest"
+#define QSEV_GUEST_INFO(obj)                  \
+    OBJECT_CHECK(QSevGuestInfo, (obj), TYPE_QSEV_GUEST_INFO)
+
+extern bool sev_enabled(void);
+extern uint64_t sev_get_me_mask(void);
+extern SevInfo *sev_get_info(void);
+extern uint32_t sev_get_cbit_position(void);
+extern uint32_t sev_get_reduced_phys_bits(void);
+extern char *sev_get_launch_measurement(void);
+extern SevCapability *sev_get_capabilities(void);
+
+typedef struct QSevGuestInfo QSevGuestInfo;
+typedef struct QSevGuestInfoClass QSevGuestInfoClass;
+
+/**
+ * QSevGuestInfo:
+ *
+ * The QSevGuestInfo object is used for creating a SEV guest.
+ *
+ * # $QEMU \
+ *         -object sev-guest,id=sev0 \
+ *         -machine ...,memory-encryption=sev0
+ */
+struct QSevGuestInfo {
+    Object parent_obj;
+
+    char *sev_device;
+    uint32_t policy;
+    uint32_t handle;
+    char *dh_cert_file;
+    char *session_file;
+    uint32_t cbitpos;
+    uint32_t reduced_phys_bits;
+};
+
+struct QSevGuestInfoClass {
+    ObjectClass parent_class;
+};
+
+struct SEVState {
+    QSevGuestInfo *sev_info;
+    uint8_t api_major;
+    uint8_t api_minor;
+    uint8_t build_id;
+    uint32_t policy;
+    uint64_t me_mask;
+    uint32_t cbitpos;
+    uint32_t reduced_phys_bits;
+    uint32_t handle;
+    int sev_fd;
+    SevState state;
+    gchar *measurement;
+};
+
+typedef struct SEVState SEVState;
+
+#endif
diff --git a/target/i386/trace-events b/target/i386/trace-events
index 3153fd4454..6a19a69af5 100644
--- a/target/i386/trace-events
+++ b/target/i386/trace-events
@@ -5,3 +5,13 @@ kvm_x86_fixup_msi_error(uint32_t gsi) "VT-d failed to remap interrupt for GSI %"
 kvm_x86_add_msi_route(int virq) "Adding route entry for virq %d"
 kvm_x86_remove_msi_route(int virq) "Removing route entry for virq %d"
 kvm_x86_update_msi_routes(int num) "Updated %d MSI routes"
+
+# target/i386/sev.c
+kvm_sev_init(void) ""
+kvm_memcrypt_register_region(void *addr, size_t len) "addr %p len 0x%zu"
+kvm_memcrypt_unregister_region(void *addr, size_t len) "addr %p len 0x%zu"
+kvm_sev_change_state(const char *old, const char *new) "%s -> %s"
+kvm_sev_launch_start(int policy, void *session, void *pdh) "policy 0x%x session %p pdh %p"
+kvm_sev_launch_update_data(void *addr, uint64_t len) "addr %p len 0x%" PRIu64
+kvm_sev_launch_measurement(const char *value) "data %s"
+kvm_sev_launch_finish(void) ""
diff --git a/target/lm32/cpu.c b/target/lm32/cpu.c
index 96c2499d0b..0003152469 100644
--- a/target/lm32/cpu.c
+++ b/target/lm32/cpu.c
@@ -32,18 +32,6 @@ static void lm32_cpu_set_pc(CPUState *cs, vaddr value)
     cpu->env.pc = value;
 }
 
-/* Sort alphabetically by type name. */
-static gint lm32_cpu_list_compare(gconstpointer a, gconstpointer b)
-{
-    ObjectClass *class_a = (ObjectClass *)a;
-    ObjectClass *class_b = (ObjectClass *)b;
-    const char *name_a, *name_b;
-
-    name_a = object_class_get_name(class_a);
-    name_b = object_class_get_name(class_b);
-    return strcmp(name_a, name_b);
-}
-
 static void lm32_cpu_list_entry(gpointer data, gpointer user_data)
 {
     ObjectClass *oc = data;
@@ -65,8 +53,7 @@ void lm32_cpu_list(FILE *f, fprintf_function cpu_fprintf)
     };
     GSList *list;
 
-    list = object_class_get_list(TYPE_LM32_CPU, false);
-    list = g_slist_sort(list, lm32_cpu_list_compare);
+    list = object_class_get_list_sorted(TYPE_LM32_CPU, false);
     (*cpu_fprintf)(f, "Available CPUs:\n");
     g_slist_foreach(list, lm32_cpu_list_entry, &s);
     g_slist_free(list);
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;
diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c
index 6302cfda3a..541ffc2d97 100644
--- a/target/sh4/cpu.c
+++ b/target/sh4/cpu.c
@@ -85,18 +85,6 @@ typedef struct SuperHCPUListState {
     FILE *file;
 } SuperHCPUListState;
 
-/* Sort alphabetically by type name. */
-static gint superh_cpu_list_compare(gconstpointer a, gconstpointer b)
-{
-    ObjectClass *class_a = (ObjectClass *)a;
-    ObjectClass *class_b = (ObjectClass *)b;
-    const char *name_a, *name_b;
-
-    name_a = object_class_get_name(class_a);
-    name_b = object_class_get_name(class_b);
-    return strcmp(name_a, name_b);
-}
-
 static void superh_cpu_list_entry(gpointer data, gpointer user_data)
 {
     SuperHCPUListState *s = user_data;
@@ -114,8 +102,7 @@ void sh4_cpu_list(FILE *f, fprintf_function cpu_fprintf)
     };
     GSList *list;
 
-    list = object_class_get_list(TYPE_SUPERH_CPU, false);
-    list = g_slist_sort(list, superh_cpu_list_compare);
+    list = object_class_get_list_sorted(TYPE_SUPERH_CPU, false);
     g_slist_foreach(list, superh_cpu_list_entry, &s);
     g_slist_free(list);
 }
diff --git a/target/tricore/helper.c b/target/tricore/helper.c
index 45276d3782..dad7eea085 100644
--- a/target/tricore/helper.c
+++ b/target/tricore/helper.c
@@ -101,7 +101,7 @@ void tricore_cpu_list(FILE *f, fprintf_function cpu_fprintf)
     };
     GSList *list;
 
-    list = object_class_get_list(TYPE_TRICORE_CPU, false);
+    list = object_class_get_list_sorted(TYPE_TRICORE_CPU, false);
     (*cpu_fprintf)(f, "Available CPUs:\n");
     g_slist_foreach(list, tricore_cpu_list_entry, &s);
     g_slist_free(list);