Commit 9937b029 authored by Laurent Vivier's avatar Laurent Vivier
Browse files

target/m68k: implement ftanh



Using local m68k floatx80_tanh() and floatx80_etoxm1()
[copied from previous:
Written by Andreas Grabher for Previous, NeXT Computer Emulator.]

Signed-off-by: default avatarLaurent Vivier <laurent@vivier.eu>
Message-Id: <20180312202728.23790-10-laurent@vivier.eu>
parent e3655afa
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -638,3 +638,8 @@ 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);
}
+1 −0
Original line number Diff line number Diff line
@@ -83,6 +83,7 @@ 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(mac_move, void, env, i32, i32)
DEF_HELPER_3(macmulf, i64, env, i32, i32)
+366 −0
Original line number Diff line number Diff line
@@ -2367,3 +2367,369 @@ floatx80 floatx80_atanh(floatx80 a, float_status *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;
    }
}
+2 −0
Original line number Diff line number Diff line
@@ -41,4 +41,6 @@ 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);
#endif
+3 −0
Original line number Diff line number Diff line
@@ -5057,6 +5057,9 @@ 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;