Commit ba624944 authored by Laurent Vivier's avatar Laurent Vivier
Browse files

target-m68k: add FPCR and FPSR



Signed-off-by: default avatarLaurent Vivier <laurent@vivier.eu>
Reviewed-by: default avatarRichard Henderson <rth@twiddle.net>
Message-Id: <20170620205121.26515-6-laurent@vivier.eu>
parent 5a4526b2
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -62,7 +62,7 @@ static void m68k_cpu_reset(CPUState *s)
    for (i = 0; i < 8; i++) {
        env->fregs[i].d = nan;
    }
    env->fpcr = 0;
    cpu_m68k_set_fpcr(env, 0);
    env->fpsr = 0;

    cpu_m68k_set_ccr(env, 0);
+39 −4
Original line number Diff line number Diff line
@@ -171,6 +171,7 @@ int cpu_m68k_signal_handler(int host_signum, void *pinfo,
                           void *puc);
uint32_t cpu_m68k_get_ccr(CPUM68KState *env);
void cpu_m68k_set_ccr(CPUM68KState *env, uint32_t);
void cpu_m68k_set_fpcr(CPUM68KState *env, uint32_t val);


/* Instead of computing the condition codes after each m68k instruction,
@@ -215,6 +216,43 @@ typedef enum {
#define M68K_SSP    0
#define M68K_USP    1

#define M68K_FPIAR_SHIFT  0
#define M68K_FPIAR        (1 << M68K_FPIAR_SHIFT)
#define M68K_FPSR_SHIFT   1
#define M68K_FPSR         (1 << M68K_FPSR_SHIFT)
#define M68K_FPCR_SHIFT   2
#define M68K_FPCR         (1 << M68K_FPCR_SHIFT)

/* Floating-Point Status Register */

/* Condition Code */
#define FPSR_CC_MASK  0x0f000000
#define FPSR_CC_A     0x01000000 /* Not-A-Number */
#define FPSR_CC_I     0x02000000 /* Infinity */
#define FPSR_CC_Z     0x04000000 /* Zero */
#define FPSR_CC_N     0x08000000 /* Negative */

/* Quotient */

#define FPSR_QT_MASK  0x00ff0000

/* Floating-Point Control Register */
/* Rounding mode */
#define FPCR_RND_MASK   0x0030
#define FPCR_RND_N      0x0000
#define FPCR_RND_Z      0x0010
#define FPCR_RND_M      0x0020
#define FPCR_RND_P      0x0030

/* Rounding precision */
#define FPCR_PREC_MASK  0x00c0
#define FPCR_PREC_X     0x0000
#define FPCR_PREC_S     0x0040
#define FPCR_PREC_D     0x0080
#define FPCR_PREC_U     0x00c0

#define FPCR_EXCP_MASK 0xff00

/* CACR fields are implementation defined, but some bits are common.  */
#define M68K_CACR_EUSP  0x10

@@ -231,8 +269,6 @@ typedef enum {
void m68k_set_irq_level(M68kCPU *cpu, int level, uint8_t vector);
void m68k_switch_sp(CPUM68KState *env);

#define M68K_FPCR_PREC (1 << 6)

void do_m68k_semihosting(CPUM68KState *env, int nr);

/* There are 4 ColdFire core ISA revisions: A, A+, B and C.
@@ -310,8 +346,7 @@ static inline void cpu_get_tb_cpu_state(CPUM68KState *env, target_ulong *pc,
{
    *pc = env->pc;
    *cs_base = 0;
    *flags = (env->fpcr & M68K_FPCR_PREC)       /* Bit  6 */
            | (env->sr & SR_S)                  /* Bit  13 */
    *flags = (env->sr & SR_S)                   /* Bit  13 */
            | ((env->macsr >> 4) & 0xf);        /* Bits 0-3 */
}

+101 −16
Original line number Diff line number Diff line
@@ -58,9 +58,74 @@ void HELPER(firound)(CPUM68KState *env, FPReg *res, FPReg *val)
    res->d = floatx80_round_to_int(val->d, &env->fp_status);
}

static void m68k_restore_precision_mode(CPUM68KState *env)
{
    switch (env->fpcr & FPCR_PREC_MASK) {
    case FPCR_PREC_X: /* extended */
        set_floatx80_rounding_precision(80, &env->fp_status);
        break;
    case FPCR_PREC_S: /* single */
        set_floatx80_rounding_precision(32, &env->fp_status);
        break;
    case FPCR_PREC_D: /* double */
        set_floatx80_rounding_precision(64, &env->fp_status);
        break;
    case FPCR_PREC_U: /* undefined */
    default:
        break;
    }
}

static void cf_restore_precision_mode(CPUM68KState *env)
{
    if (env->fpcr & FPCR_PREC_S) { /* single */
        set_floatx80_rounding_precision(32, &env->fp_status);
    } else { /* double */
        set_floatx80_rounding_precision(64, &env->fp_status);
    }
}

static void restore_rounding_mode(CPUM68KState *env)
{
    switch (env->fpcr & FPCR_RND_MASK) {
    case FPCR_RND_N: /* round to nearest */
        set_float_rounding_mode(float_round_nearest_even, &env->fp_status);
        break;
    case FPCR_RND_Z: /* round to zero */
        set_float_rounding_mode(float_round_to_zero, &env->fp_status);
        break;
    case FPCR_RND_M: /* round toward minus infinity */
        set_float_rounding_mode(float_round_down, &env->fp_status);
        break;
    case FPCR_RND_P: /* round toward positive infinity */
        set_float_rounding_mode(float_round_up, &env->fp_status);
        break;
    }
}

void cpu_m68k_set_fpcr(CPUM68KState *env, uint32_t val)
{
    env->fpcr = val & 0xffff;

    if (m68k_feature(env, M68K_FEATURE_CF_FPU)) {
        cf_restore_precision_mode(env);
    } else {
        m68k_restore_precision_mode(env);
    }
    restore_rounding_mode(env);
}

void HELPER(fitrunc)(CPUM68KState *env, FPReg *res, FPReg *val)
{
    int rounding_mode = get_float_rounding_mode(&env->fp_status);
    set_float_rounding_mode(float_round_to_zero, &env->fp_status);
    res->d = floatx80_round_to_int(val->d, &env->fp_status);
    set_float_rounding_mode(rounding_mode, &env->fp_status);
}

void HELPER(set_fpcr)(CPUM68KState *env, uint32_t val)
{
    cpu_m68k_set_fpcr(env, val);
}

void HELPER(fsqrt)(CPUM68KState *env, FPReg *res, FPReg *val)
@@ -98,24 +163,44 @@ void HELPER(fdiv)(CPUM68KState *env, FPReg *res, FPReg *val0, FPReg *val1)
    res->d = floatx80_div(val1->d, val0->d, &env->fp_status);
}

void HELPER(fsub_cmp)(CPUM68KState *env, FPReg *res, FPReg *val0, FPReg *val1)
static int float_comp_to_cc(int float_compare)
{
    /* ??? This may incorrectly raise exceptions.  */
    /* ??? Should flush denormals to zero.  */
    res->d = floatx80_sub(val0->d, val1->d, &env->fp_status);
    if (floatx80_is_quiet_nan(res->d, &env->fp_status)) {
        /* +/-inf compares equal against itself, but sub returns nan.  */
        if (!floatx80_is_quiet_nan(val0->d, &env->fp_status)
            && !floatx80_is_quiet_nan(val1->d, &env->fp_status)) {
            res->d = floatx80_zero;
            if (floatx80_lt_quiet(val0->d, res->d, &env->fp_status)) {
                res->d = floatx80_chs(res->d);
            }
    switch (float_compare) {
    case float_relation_equal:
        return FPSR_CC_Z;
    case float_relation_less:
        return FPSR_CC_N;
    case float_relation_unordered:
        return FPSR_CC_A;
    case float_relation_greater:
        return 0;
    default:
        g_assert_not_reached();
    }
}

void HELPER(fcmp)(CPUM68KState *env, FPReg *val0, FPReg *val1)
{
    int float_compare;

    float_compare = floatx80_compare(val1->d, val0->d, &env->fp_status);
    env->fpsr = (env->fpsr & ~FPSR_CC_MASK) | float_comp_to_cc(float_compare);
}

uint32_t HELPER(fcompare)(CPUM68KState *env, FPReg *val)
void HELPER(ftst)(CPUM68KState *env, FPReg *val)
{
    return floatx80_compare_quiet(val->d, floatx80_zero, &env->fp_status);
    uint32_t cc = 0;

    if (floatx80_is_neg(val->d)) {
        cc |= FPSR_CC_N;
    }

    if (floatx80_is_any_nan(val->d)) {
        cc |= FPSR_CC_A;
    } else if (floatx80_is_infinity(val->d)) {
        cc |= FPSR_CC_I;
    } else if (floatx80_is_zero(val->d)) {
        cc |= FPSR_CC_Z;
    }
    env->fpsr = (env->fpsr & ~FPSR_CC_MASK) | cc;
}
+17 −5
Original line number Diff line number Diff line
@@ -80,8 +80,14 @@ static int cf_fpu_gdb_get_reg(CPUM68KState *env, uint8_t *mem_buf, int n)
        stfq_p(mem_buf, floatx80_to_float64(env->fregs[n].d, &s));
        return 8;
    }
    if (n < 11) {
        /* FP control registers (not implemented)  */
    switch (n) {
    case 8: /* fpcontrol */
        stl_be_p(mem_buf, env->fpcr);
        return 4;
    case 9: /* fpstatus */
        stl_be_p(mem_buf, env->fpsr);
        return 4;
    case 10: /* fpiar, not implemented */
        memset(mem_buf, 0, 4);
        return 4;
    }
@@ -95,8 +101,14 @@ static int cf_fpu_gdb_set_reg(CPUM68KState *env, uint8_t *mem_buf, int n)
        env->fregs[n].d = float64_to_floatx80(ldfq_p(mem_buf), &s);
        return 8;
    }
    if (n < 11) {
        /* FP control registers (not implemented)  */
    switch (n) {
    case 8: /* fpcontrol */
        cpu_m68k_set_fpcr(env, ldl_p(mem_buf));
        return 4;
    case 9: /* fpstatus */
        env->fpsr = ldl_p(mem_buf);
        return 4;
    case 10: /* fpiar, not implemented */
        return 4;
    }
    return 0;
@@ -133,7 +145,7 @@ static int m68k_fpu_gdb_set_reg(CPUM68KState *env, uint8_t *mem_buf, int n)
    }
    switch (n) {
    case 8: /* fpcontrol */
        env->fpcr = ldl_p(mem_buf);
        cpu_m68k_set_fpcr(env, ldl_p(mem_buf));
        return 4;
    case 9: /* fpstatus */
        env->fpsr = ldl_p(mem_buf);
+3 −2
Original line number Diff line number Diff line
@@ -32,8 +32,9 @@ DEF_HELPER_4(fadd, void, env, fp, fp, fp)
DEF_HELPER_4(fsub, void, env, fp, fp, fp)
DEF_HELPER_4(fmul, void, env, fp, fp, fp)
DEF_HELPER_4(fdiv, void, env, fp, fp, fp)
DEF_HELPER_4(fsub_cmp, void, env, fp, fp, fp)
DEF_HELPER_2(fcompare, i32, env, fp)
DEF_HELPER_FLAGS_3(fcmp, TCG_CALL_NO_RWG, void, env, fp, fp)
DEF_HELPER_FLAGS_2(set_fpcr, TCG_CALL_NO_RWG, void, env, i32)
DEF_HELPER_FLAGS_2(ftst, TCG_CALL_NO_RWG, void, env, fp)

DEF_HELPER_3(mac_move, void, env, i32, i32)
DEF_HELPER_3(macmulf, i64, env, i32, i32)
Loading