#define cmp_FPU(name, optype, a_size, load_var, rw, use_var, is_nan, cycle_postfix)                                                                \
    static int sf_FCOM##name##_a##a_size(uint32_t fetchdat)                                                                                        \
    {                                                                                                                                              \
        floatx80                  a;                                                                                                               \
        int                       rc;                                                                                                              \
        struct softfloat_status_t status;                                                                                                          \
        optype                    temp;                                                                                                            \
        FP_ENTER();                                                                                                                                \
        FPU_check_pending_exceptions();                                                                                                            \
        fetch_ea_##a_size(fetchdat);                                                                                                               \
        SEG_CHECK_READ(cpu_state.ea_seg);                                                                                                          \
        load_var = rw;                                                                                                                             \
        if (cpu_state.abrt)                                                                                                                        \
            return 1;                                                                                                                              \
        clear_C1();                                                                                                                                \
        if (IS_TAG_EMPTY(0)) {                                                                                                                     \
            FPU_exception(fetchdat, FPU_EX_Stack_Underflow, 0);                                                                                    \
            setcc(FPU_SW_C0 | FPU_SW_C2 | FPU_SW_C3);                                                                                              \
            goto next_ins;                                                                                                                         \
        }                                                                                                                                          \
        status = i387cw_to_softfloat_status_word(i387_get_control_word());                                                                         \
        a      = FPU_read_regi(0);                                                                                                                 \
        if (is_nan) {                                                                                                                              \
            rc = softfloat_relation_unordered;                                                                                                     \
            softfloat_raiseFlags(&status, softfloat_flag_invalid);                                                                                 \
        } else {                                                                                                                                   \
            rc = extF80_compare_normal(a, use_var, &status);                                                                                       \
        }                                                                                                                                          \
        setcc(FPU_status_word_flags_fpu_compare(rc));                                                                                              \
        FPU_exception(fetchdat, status.softfloat_exceptionFlags, 0);                                                                               \
                                                                                                                                                   \
next_ins:                                                                                                                                          \
        CLOCK_CYCLES_FPU((fpu_type >= FPU_487SX) ? (x87_timings.fcom##cycle_postfix) : ((x87_timings.fcom##cycle_postfix) * cpu_multi));           \
        CONCURRENCY_CYCLES((fpu_type >= FPU_487SX) ? (x87_concurrency.fcom##cycle_postfix) : ((x87_concurrency.fcom##cycle_postfix) * cpu_multi)); \
        return 0;                                                                                                                                  \
    }                                                                                                                                              \
    static int sf_FCOMP##name##_a##a_size(uint32_t fetchdat)                                                                                       \
    {                                                                                                                                              \
        floatx80                  a;                                                                                                               \
        int                       rc;                                                                                                              \
        struct softfloat_status_t status;                                                                                                          \
        optype                    temp;                                                                                                            \
        FP_ENTER();                                                                                                                                \
        FPU_check_pending_exceptions();                                                                                                            \
        fetch_ea_##a_size(fetchdat);                                                                                                               \
        SEG_CHECK_READ(cpu_state.ea_seg);                                                                                                          \
        load_var = rw;                                                                                                                             \
        if (cpu_state.abrt)                                                                                                                        \
            return 1;                                                                                                                              \
        clear_C1();                                                                                                                                \
        if (IS_TAG_EMPTY(0)) {                                                                                                                     \
            FPU_exception(fetchdat, FPU_EX_Stack_Underflow, 0);                                                                                    \
            setcc(FPU_SW_C0 | FPU_SW_C2 | FPU_SW_C3);                                                                                              \
            if (is_IA_masked())                                                                                                                    \
                FPU_pop();                                                                                                                         \
                                                                                                                                                   \
            goto next_ins;                                                                                                                         \
        }                                                                                                                                          \
        status = i387cw_to_softfloat_status_word(i387_get_control_word());                                                                         \
        a      = FPU_read_regi(0);                                                                                                                 \
        if (is_nan) {                                                                                                                              \
            rc = softfloat_relation_unordered;                                                                                                     \
            softfloat_raiseFlags(&status, softfloat_flag_invalid);                                                                                 \
        } else {                                                                                                                                   \
            rc = extF80_compare_normal(a, use_var, &status);                                                                                       \
        }                                                                                                                                          \
        setcc(FPU_status_word_flags_fpu_compare(rc));                                                                                              \
        if (!FPU_exception(fetchdat, status.softfloat_exceptionFlags, 0))                                                                          \
            FPU_pop();                                                                                                                             \
                                                                                                                                                   \
next_ins:                                                                                                                                          \
        CLOCK_CYCLES_FPU((fpu_type >= FPU_487SX) ? (x87_timings.fcom##cycle_postfix) : ((x87_timings.fcom##cycle_postfix) * cpu_multi));           \
        CONCURRENCY_CYCLES((fpu_type >= FPU_487SX) ? (x87_concurrency.fcom##cycle_postfix) : ((x87_concurrency.fcom##cycle_postfix) * cpu_multi)); \
        return 0;                                                                                                                                  \
    }

// clang-format off
cmp_FPU(s, float32, 16, temp, geteal(), f32_to_extF80(temp, &status), extF80_isNaN(a) || extF80_isUnsupported(a) || f32_isNaN(temp), _32)
#ifndef FPU_8087
cmp_FPU(s, float32, 32, temp, geteal(), f32_to_extF80(temp, &status), extF80_isNaN(a) || extF80_isUnsupported(a) || f32_isNaN(temp), _32)
#endif
cmp_FPU(d, float64, 16, temp, geteaq(), f64_to_extF80(temp, &status), extF80_isNaN(a) || extF80_isUnsupported(a) || f64_isNaN(temp), _64)
#ifndef FPU_8087
cmp_FPU(d, float64, 32, temp, geteaq(), f64_to_extF80(temp, &status), extF80_isNaN(a) || extF80_isUnsupported(a) || f64_isNaN(temp), _64)
#endif

cmp_FPU(iw, int16_t, 16, temp, (int16_t)geteaw(), i32_to_extF80((int32_t)temp), 0, _i16)
#ifndef FPU_8087
cmp_FPU(iw, int16_t, 32, temp, (int16_t)geteaw(), i32_to_extF80((int32_t)temp), 0, _i16)
#endif
cmp_FPU(il, int32_t, 16, temp, (int32_t)geteal(), i32_to_extF80(temp), 0, _i32)
#ifndef FPU_8087
cmp_FPU(il, int32_t, 32, temp, (int32_t)geteal(), i32_to_extF80(temp), 0, _i32)
#endif
// clang-format on

static int
sf_FCOM_sti(uint32_t fetchdat)
{
    floatx80                  a;
    floatx80                  b;
    struct softfloat_status_t status;
    int                       rc;

    FP_ENTER();
    FPU_check_pending_exceptions();
    cpu_state.pc++;
    clear_C1();
    if (IS_TAG_EMPTY(0) || IS_TAG_EMPTY(fetchdat & 7)) {
        FPU_exception(fetchdat, FPU_EX_Stack_Underflow, 0);
        setcc(FPU_SW_C0 | FPU_SW_C2 | FPU_SW_C3);
        goto next_ins;
    }
    status = i387cw_to_softfloat_status_word(i387_get_control_word());
    a      = FPU_read_regi(0);
    b      = FPU_read_regi(fetchdat & 7);
    rc     = extF80_compare_normal(a, b, &status);
    setcc(FPU_status_word_flags_fpu_compare(rc));
    FPU_exception(fetchdat, status.softfloat_exceptionFlags, 0);

next_ins:
    CLOCK_CYCLES_FPU((fpu_type >= FPU_487SX) ? (x87_timings.fcom) : (x87_timings.fcom * cpu_multi));
    CONCURRENCY_CYCLES((fpu_type >= FPU_487SX) ? (x87_concurrency.fcom) : (x87_concurrency.fcom * cpu_multi));
    return 0;
}

static int
sf_FCOMP_sti(uint32_t fetchdat)
{
    floatx80                  a;
    floatx80                  b;
    struct softfloat_status_t status;
    int                       rc;

    FP_ENTER();
    FPU_check_pending_exceptions();
    cpu_state.pc++;
    clear_C1();
    if (IS_TAG_EMPTY(0) || IS_TAG_EMPTY(fetchdat & 7)) {
        FPU_exception(fetchdat, FPU_EX_Stack_Underflow, 0);
        setcc(FPU_SW_C0 | FPU_SW_C2 | FPU_SW_C3);
        if (is_IA_masked()) {
            FPU_pop();
        }
        goto next_ins;
    }
    status = i387cw_to_softfloat_status_word(i387_get_control_word());
    a      = FPU_read_regi(0);
    b      = FPU_read_regi(fetchdat & 7);
    rc     = extF80_compare_normal(a, b, &status);
    setcc(FPU_status_word_flags_fpu_compare(rc));
    if (!FPU_exception(fetchdat, status.softfloat_exceptionFlags, 0)) {
        FPU_pop();
    }

next_ins:
    CLOCK_CYCLES_FPU((fpu_type >= FPU_487SX) ? (x87_timings.fcom) : (x87_timings.fcom * cpu_multi));
    CONCURRENCY_CYCLES((fpu_type >= FPU_487SX) ? (x87_concurrency.fcom) : (x87_concurrency.fcom * cpu_multi));
    return 0;
}

static int
sf_FCOMPP(uint32_t fetchdat)
{
    floatx80                  a;
    floatx80                  b;
    struct softfloat_status_t status;
    int                       rc;

    FP_ENTER();
    FPU_check_pending_exceptions();
    cpu_state.pc++;
    clear_C1();
    if (IS_TAG_EMPTY(0) || IS_TAG_EMPTY(1)) {
        FPU_exception(fetchdat, FPU_EX_Stack_Underflow, 0);
        setcc(FPU_SW_C0 | FPU_SW_C2 | FPU_SW_C3);
        if (is_IA_masked()) {
            FPU_pop();
            FPU_pop();
        }
        goto next_ins;
    }
    status = i387cw_to_softfloat_status_word(i387_get_control_word());
    a      = FPU_read_regi(0);
    b      = FPU_read_regi(1);
    rc     = extF80_compare_normal(a, b, &status);
    setcc(FPU_status_word_flags_fpu_compare(rc));
    if (!FPU_exception(fetchdat, status.softfloat_exceptionFlags, 0)) {
        FPU_pop();
        FPU_pop();
    }

next_ins:
    CLOCK_CYCLES_FPU((fpu_type >= FPU_487SX) ? (x87_timings.fcom) : (x87_timings.fcom * cpu_multi));
    CONCURRENCY_CYCLES((fpu_type >= FPU_487SX) ? (x87_concurrency.fcom) : (x87_concurrency.fcom * cpu_multi));
    return 0;
}

#ifndef FPU_8087
static int
sf_FUCOMPP(uint32_t fetchdat)
{
    floatx80                  a;
    floatx80                  b;
    struct softfloat_status_t status;
    int                       rc;

    FP_ENTER();
    FPU_check_pending_exceptions();
    cpu_state.pc++;
    clear_C1();
    if (IS_TAG_EMPTY(0) || IS_TAG_EMPTY(1)) {
        FPU_exception(fetchdat, FPU_EX_Stack_Underflow, 0);
        setcc(FPU_SW_C0 | FPU_SW_C2 | FPU_SW_C3);
        if (is_IA_masked()) {
            FPU_pop();
            FPU_pop();
        }
        goto next_ins;
    }
    status = i387cw_to_softfloat_status_word(i387_get_control_word());
    a      = FPU_read_regi(0);
    b      = FPU_read_regi(1);
    rc     = extF80_compare_quiet(a, b, &status);
    setcc(FPU_status_word_flags_fpu_compare(rc));
    if (!FPU_exception(fetchdat, status.softfloat_exceptionFlags, 0)) {
        FPU_pop();
        FPU_pop();
    }

next_ins:
    CLOCK_CYCLES_FPU((fpu_type >= FPU_487SX) ? (x87_timings.fucom) : (x87_timings.fucom * cpu_multi));
    CONCURRENCY_CYCLES((fpu_type >= FPU_487SX) ? (x87_concurrency.fucom) : (x87_concurrency.fucom * cpu_multi));
    return 0;
}

#ifndef OPS_286_386
static int
sf_FCOMI_st0_stj(uint32_t fetchdat)
{
    floatx80                  a;
    floatx80                  b;
    struct softfloat_status_t status;
    int                       rc;

    FP_ENTER();
    FPU_check_pending_exceptions();
    cpu_state.pc++;
    flags_rebuild();
    clear_C1();
    if (IS_TAG_EMPTY(0) || IS_TAG_EMPTY(fetchdat & 7)) {
        FPU_exception(fetchdat, FPU_EX_Stack_Underflow, 0);
        cpu_state.flags |= (Z_FLAG | P_FLAG | C_FLAG);
        goto next_ins;
    }
    status = i387cw_to_softfloat_status_word(i387_get_control_word());
    a      = FPU_read_regi(0);
    b      = FPU_read_regi(fetchdat & 7);
    rc     = extF80_compare_normal(a, b, &status);
    FPU_write_eflags_fpu_compare(rc);
    FPU_exception(fetchdat, status.softfloat_exceptionFlags, 0);

next_ins:
    CLOCK_CYCLES_FPU((fpu_type >= FPU_487SX) ? (x87_timings.fcom) : (x87_timings.fcom * cpu_multi));
    CONCURRENCY_CYCLES((fpu_type >= FPU_487SX) ? (x87_concurrency.fcom) : (x87_concurrency.fcom * cpu_multi));
    return 0;
}
static int
sf_FCOMIP_st0_stj(uint32_t fetchdat)
{
    floatx80                  a;
    floatx80                  b;
    struct softfloat_status_t status;
    int                       rc;

    FP_ENTER();
    FPU_check_pending_exceptions();
    cpu_state.pc++;
    flags_rebuild();
    clear_C1();
    if (IS_TAG_EMPTY(0) || IS_TAG_EMPTY(fetchdat & 7)) {
        FPU_exception(fetchdat, FPU_EX_Stack_Underflow, 0);
        cpu_state.flags |= (Z_FLAG | P_FLAG | C_FLAG);
        if (is_IA_masked()) {
            FPU_pop();
        }
        goto next_ins;
    }
    status = i387cw_to_softfloat_status_word(i387_get_control_word());
    a      = FPU_read_regi(0);
    b      = FPU_read_regi(fetchdat & 7);
    rc     = extF80_compare_normal(a, b, &status);
    FPU_write_eflags_fpu_compare(rc);
    if (!FPU_exception(fetchdat, status.softfloat_exceptionFlags, 0)) {
        FPU_pop();
    }

next_ins:
    CLOCK_CYCLES_FPU((fpu_type >= FPU_487SX) ? (x87_timings.fcom) : (x87_timings.fcom * cpu_multi));
    CONCURRENCY_CYCLES((fpu_type >= FPU_487SX) ? (x87_concurrency.fcom) : (x87_concurrency.fcom * cpu_multi));
    return 0;
}
#endif

static int
sf_FUCOM_sti(uint32_t fetchdat)
{
    floatx80                  a;
    floatx80                  b;
    struct softfloat_status_t status;
    int                       rc;

    FP_ENTER();
    FPU_check_pending_exceptions();
    cpu_state.pc++;
    clear_C1();
    if (IS_TAG_EMPTY(0) || IS_TAG_EMPTY(fetchdat & 7)) {
        FPU_exception(fetchdat, FPU_EX_Stack_Underflow, 0);
        setcc(FPU_SW_C0 | FPU_SW_C2 | FPU_SW_C3);
        goto next_ins;
    }
    status = i387cw_to_softfloat_status_word(i387_get_control_word());
    a      = FPU_read_regi(0);
    b      = FPU_read_regi(fetchdat & 7);
    rc     = extF80_compare_quiet(a, b, &status);
    setcc(FPU_status_word_flags_fpu_compare(rc));
    FPU_exception(fetchdat, status.softfloat_exceptionFlags, 0);

next_ins:
    CLOCK_CYCLES_FPU((fpu_type >= FPU_487SX) ? (x87_timings.fucom) : (x87_timings.fucom * cpu_multi));
    CONCURRENCY_CYCLES((fpu_type >= FPU_487SX) ? (x87_concurrency.fucom) : (x87_concurrency.fucom * cpu_multi));
    return 0;
}

static int
sf_FUCOMP_sti(uint32_t fetchdat)
{
    floatx80                  a;
    floatx80                  b;
    struct softfloat_status_t status;
    int                       rc;

    FP_ENTER();
    FPU_check_pending_exceptions();
    cpu_state.pc++;
    clear_C1();
    if (IS_TAG_EMPTY(0) || IS_TAG_EMPTY(fetchdat & 7)) {
        FPU_exception(fetchdat, FPU_EX_Stack_Underflow, 0);
        setcc(FPU_SW_C0 | FPU_SW_C2 | FPU_SW_C3);
        if (is_IA_masked())
            FPU_pop();

        goto next_ins;
    }
    status = i387cw_to_softfloat_status_word(i387_get_control_word());
    a      = FPU_read_regi(0);
    b      = FPU_read_regi(fetchdat & 7);
    rc     = extF80_compare_quiet(a, b, &status);
    setcc(FPU_status_word_flags_fpu_compare(rc));
    if (!FPU_exception(fetchdat, status.softfloat_exceptionFlags, 0))
        FPU_pop();

next_ins:
    CLOCK_CYCLES_FPU((fpu_type >= FPU_487SX) ? (x87_timings.fucom) : (x87_timings.fucom * cpu_multi));
    CONCURRENCY_CYCLES((fpu_type >= FPU_487SX) ? (x87_concurrency.fucom) : (x87_concurrency.fucom * cpu_multi));
    return 0;
}

#    ifndef OPS_286_386
static int
sf_FUCOMI_st0_stj(uint32_t fetchdat)
{
    floatx80                  a;
    floatx80                  b;
    struct softfloat_status_t status;
    int                       rc;

    FP_ENTER();
    FPU_check_pending_exceptions();
    cpu_state.pc++;
    flags_rebuild();
    clear_C1();
    if (IS_TAG_EMPTY(0) || IS_TAG_EMPTY(fetchdat & 7)) {
        FPU_exception(fetchdat, FPU_EX_Stack_Underflow, 0);
        cpu_state.flags |= (Z_FLAG | P_FLAG | C_FLAG);
        goto next_ins;
    }
    status = i387cw_to_softfloat_status_word(i387_get_control_word());
    a      = FPU_read_regi(0);
    b      = FPU_read_regi(fetchdat & 7);
    rc     = extF80_compare_quiet(a, b, &status);
    FPU_write_eflags_fpu_compare(rc);
    FPU_exception(fetchdat, status.softfloat_exceptionFlags, 0);

next_ins:
    CLOCK_CYCLES_FPU((fpu_type >= FPU_487SX) ? (x87_timings.fucom) : (x87_timings.fucom * cpu_multi));
    CONCURRENCY_CYCLES((fpu_type >= FPU_487SX) ? (x87_concurrency.fucom) : (x87_concurrency.fucom * cpu_multi));
    return 0;
}
static int
sf_FUCOMIP_st0_stj(uint32_t fetchdat)
{
    floatx80                  a;
    floatx80                  b;
    struct softfloat_status_t status;
    int                       rc;

    FP_ENTER();
    FPU_check_pending_exceptions();
    cpu_state.pc++;
    flags_rebuild();
    clear_C1();
    if (IS_TAG_EMPTY(0) || IS_TAG_EMPTY(fetchdat & 7)) {
        FPU_exception(fetchdat, FPU_EX_Stack_Underflow, 0);
        cpu_state.flags |= (Z_FLAG | P_FLAG | C_FLAG);
        if (is_IA_masked())
            FPU_pop();

        goto next_ins;
    }
    status = i387cw_to_softfloat_status_word(i387_get_control_word());
    a      = FPU_read_regi(0);
    b      = FPU_read_regi(fetchdat & 7);
    rc     = extF80_compare_quiet(a, b, &status);
    FPU_write_eflags_fpu_compare(rc);
    if (!FPU_exception(fetchdat, status.softfloat_exceptionFlags, 0))
        FPU_pop();

next_ins:
    CLOCK_CYCLES_FPU((fpu_type >= FPU_487SX) ? (x87_timings.fucom) : (x87_timings.fucom * cpu_multi));
    CONCURRENCY_CYCLES((fpu_type >= FPU_487SX) ? (x87_concurrency.fucom) : (x87_concurrency.fucom * cpu_multi));
    return 0;
}
#endif
#endif

static int
sf_FTST(uint32_t fetchdat)
{
    const floatx80            Const_Z = packFloatx80(0, 0x0000, 0);
    struct softfloat_status_t status;
    int                       rc;

    FP_ENTER();
    FPU_check_pending_exceptions();
    cpu_state.pc++;
    clear_C1();
    if (IS_TAG_EMPTY(0)) {
        FPU_exception(fetchdat, FPU_EX_Stack_Underflow, 0);
        setcc(FPU_SW_C0 | FPU_SW_C2 | FPU_SW_C3);
    } else {
        status = i387cw_to_softfloat_status_word(i387_get_control_word());
        rc     = extF80_compare_normal(FPU_read_regi(0), Const_Z, &status);
        setcc(FPU_status_word_flags_fpu_compare(rc));
        FPU_exception(fetchdat, status.softfloat_exceptionFlags, 0);
    }
    CLOCK_CYCLES_FPU((fpu_type >= FPU_487SX) ? (x87_timings.ftst) : (x87_timings.ftst * cpu_multi));
    CONCURRENCY_CYCLES((fpu_type >= FPU_487SX) ? (x87_concurrency.ftst) : (x87_concurrency.ftst * cpu_multi));
    return 0;
}

#ifndef FPU_8087
static int
sf_FTSTP(uint32_t fetchdat)
{
    const floatx80            Const_Z = packFloatx80(0, 0x0000, 0);
    struct softfloat_status_t status;
    int                       rc;

    FP_ENTER();
    FPU_check_pending_exceptions();
    cpu_state.pc++;
    clear_C1();
    if (IS_TAG_EMPTY(0)) {
        FPU_exception(fetchdat, FPU_EX_Stack_Underflow, 0);
        setcc(FPU_SW_C0 | FPU_SW_C2 | FPU_SW_C3);
    } else {
        status = i387cw_to_softfloat_status_word(i387_get_control_word());
        rc     = extF80_compare_normal(FPU_read_regi(0), Const_Z, &status);
        setcc(FPU_status_word_flags_fpu_compare(rc));
        FPU_exception(fetchdat, status.softfloat_exceptionFlags, 0);
        FPU_pop();
    }
    CLOCK_CYCLES_FPU((fpu_type >= FPU_487SX) ? (x87_timings.ftst) : (x87_timings.ftst * cpu_multi));
    CONCURRENCY_CYCLES((fpu_type >= FPU_487SX) ? (x87_concurrency.ftst) : (x87_concurrency.ftst * cpu_multi));
    return 0;
}
#endif

static int
sf_FXAM(UNUSED(uint32_t fetchdat))
{
    floatx80            reg;
    int                 sign;
    softfloat_class_t   aClass;

    FP_ENTER();
    FPU_check_pending_exceptions();
    cpu_state.pc++;
    reg  = FPU_read_regi(0);
    sign = extF80_sign(reg);
    /*
     * Examine the contents of the ST(0) register and sets the condition
     * code flags C0, C2 and C3 in the FPU status word to indicate the
     * class of value or number in the register.
     */
    if (IS_TAG_EMPTY(0)) {
        setcc(FPU_SW_C0 | FPU_SW_C1 | FPU_SW_C3);
    } else {
        aClass = extF80_class(reg);
        switch (aClass) {
            case softfloat_zero:
                setcc(FPU_SW_C1 | FPU_SW_C3);
                break;
            case softfloat_SNaN:
            case softfloat_QNaN:
                // unsupported handled as NaNs
                if (extF80_isUnsupported(reg))
                    setcc(FPU_SW_C1);
                else
                    setcc(FPU_SW_C0 | FPU_SW_C1);
                break;
            case softfloat_negative_inf:
            case softfloat_positive_inf:
                setcc(FPU_SW_C0 | FPU_SW_C1 | FPU_SW_C2);
                break;
            case softfloat_denormal:
                setcc(FPU_SW_C1 | FPU_SW_C2 | FPU_SW_C3);
                break;
            case softfloat_normalized:
                setcc(FPU_SW_C1 | FPU_SW_C2);
                break;
        }
    }
    /*
     * The C1 flag is set to the sign of the value in ST(0), regardless
     * of whether the register is empty or full.
     */
    if (!sign)
        clear_C1();

    CLOCK_CYCLES_FPU((fpu_type >= FPU_487SX) ? (x87_timings.fxam) : (x87_timings.fxam * cpu_multi));
    CONCURRENCY_CYCLES((fpu_type >= FPU_487SX) ? (x87_concurrency.fxam) : (x87_concurrency.fxam * cpu_multi));
    return 0;
}
