sh4: Emulate more fpu opcodes This patch adds support for more fpu opcodes. Emulation is not complete yet - FPU flags and exceptions are not fully supported. The FPSCR register does not yet contain correct flag information. But it is a good start. =) Signed-off-by: Magnus Damm --- 0001/target-sh4/cpu.h +++ work/target-sh4/cpu.h 2007-05-07 17:40:32.000000000 +0900 @@ -99,6 +99,7 @@ typedef struct CPUSH4State { /* temporary float registers */ float32 ft0, ft1; float64 dt0, dt1; + float_status fp_status; /* Those belong to the specific unit (SH7750) but are handled here */ uint32_t mmucr; /* MMU control register */ --- 0001/target-sh4/op.c +++ work/target-sh4/op.c 2007-05-07 17:40:32.000000000 +0900 @@ -509,6 +509,9 @@ void OPPROTO op_##store##_##target##_T0 void OPPROTO op_lds_T0_fpscr(void) { env->fpscr = T0 & 0x003fffff; + env->fp_status.float_rounding_mode = T0 & 0x01 ? + float_round_to_zero : float_round_nearest_even; + RETURN(); } @@ -705,6 +708,18 @@ void OPPROTO op_fmov_drN_DT0(void) RETURN(); } +void OPPROTO op_fmov_frN_FT1(void) +{ + FT1 = *(float32 *)&env->fregs[PARAM1]; + RETURN(); +} + +void OPPROTO op_fmov_drN_DT1(void) +{ + DT1 = *(float64 *)&env->fregs[PARAM1]; + RETURN(); +} + void OPPROTO op_fmov_FT0_frN(void) { *(float32 *)&env->fregs[PARAM1] = FT0; @@ -717,6 +732,84 @@ void OPPROTO op_fmov_DT0_drN(void) RETURN(); } +void OPPROTO op_fadd_FT(void) +{ + FT0 = float32_add(FT0, FT1, &env->fp_status); + RETURN(); +} + +void OPPROTO op_fadd_DT(void) +{ + DT0 = float64_add(DT0, DT1, &env->fp_status); + RETURN(); +} + +void OPPROTO op_fsub_FT(void) +{ + FT0 = float32_sub(FT0, FT1, &env->fp_status); + RETURN(); +} + +void OPPROTO op_fsub_DT(void) +{ + DT0 = float64_sub(DT0, DT1, &env->fp_status); + RETURN(); +} + +void OPPROTO op_fmul_FT(void) +{ + FT0 = float32_mul(FT0, FT1, &env->fp_status); + RETURN(); +} + +void OPPROTO op_fmul_DT(void) +{ + DT0 = float64_mul(DT0, DT1, &env->fp_status); + RETURN(); +} + +void OPPROTO op_fdiv_FT(void) +{ + FT0 = float32_div(FT0, FT1, &env->fp_status); + RETURN(); +} + +void OPPROTO op_fdiv_DT(void) +{ + DT0 = float64_div(DT0, DT1, &env->fp_status); + RETURN(); +} + +void OPPROTO op_float_FT(void) +{ + FT0 = int32_to_float32(env->fpul, &env->fp_status); + RETURN(); +} + +void OPPROTO op_float_DT(void) +{ + DT0 = int32_to_float64(env->fpul, &env->fp_status); + RETURN(); +} + +void OPPROTO op_ftrc_FT(void) +{ + env->fpul = float32_to_int32(FT0, &env->fp_status); + RETURN(); +} + +void OPPROTO op_ftrc_DT(void) +{ + env->fpul = float64_to_int32(DT0, &env->fp_status); + RETURN(); +} + +void OPPROTO op_movl_T0_FT0(void) +{ + FT0 = T0; + RETURN(); +} + void OPPROTO op_dec1_rN(void) { env->gregs[PARAM1] -= 1; --- 0002/target-sh4/translate.c +++ work/target-sh4/translate.c 2007-05-09 16:47:55.000000000 +0900 @@ -131,7 +131,13 @@ void cpu_sh4_reset(CPUSH4State * env) #endif env->vbr = 0; env->pc = 0xA0000000; - env->fpscr = 0x00040001; +#if defined(CONFIG_USER_ONLY) + env->fpscr = FPSCR_PR; /* value for userspace according to the kernel */ + env->fp_status.float_rounding_mode = float_round_nearest_even; /* ?! */ +#else + env->fpscr = 0x00040001; /* CPU reset value according to SH4 manual */ + env->fp_status.float_rounding_mode = float_round_to_zero; +#endif env->mmucr = 0; } @@ -238,6 +244,7 @@ static void gen_delayed_conditional_jump #define FREG(x) (ctx->fpscr & FPSCR_FR ? (x) ^ 0x10 : (x)) #define XHACK(x) ((((x) & 1 ) << 4) | ((x) & 0xe)) #define XREG(x) (ctx->fpscr & FPSCR_FR ? XHACK(x) ^ 0x10 : XHACK(x)) +#define DREG(x) FREG(x) /* Assumes lsb of (x) is always 0 */ #define CHECK_NOT_DELAY_SLOT \ if (ctx->flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL)) \ @@ -768,6 +775,49 @@ void decode_opc(DisasContext * ctx) gen_op_stfl_FT0_T1(ctx); } return; + case 0xf000: /* fadd Rm,Rn */ + case 0xf001: /* fsub Rm,Rn */ + case 0xf002: /* fmul Rm,Rn */ + case 0xf003: /* fdiv Rm,Rn */ + case 0xf004: /* fcmp/eq Rm,Rn */ + case 0xf005: /* fcmp/gt Rm,Rn */ + if (ctx->fpscr & FPSCR_PR) { + if (ctx->opcode & 0x0110) + break; /* illegal instruction */ + gen_op_fmov_drN_DT1(DREG(B7_4)); + gen_op_fmov_drN_DT0(DREG(B11_8)); + } + else { + gen_op_fmov_frN_FT1(FREG(B7_4)); + gen_op_fmov_frN_FT0(FREG(B11_8)); + } + + switch (ctx->opcode & 0xf00f) { + case 0xf000: /* fadd Rm,Rn */ + ctx->fpscr & FPSCR_PR ? gen_op_fadd_DT() : gen_op_fadd_FT(); + break; + case 0xf001: /* fsub Rm,Rn */ + ctx->fpscr & FPSCR_PR ? gen_op_fsub_DT() : gen_op_fsub_FT(); + break; + case 0xf002: /* fmul Rm,Rn */ + ctx->fpscr & FPSCR_PR ? gen_op_fmul_DT() : gen_op_fmul_FT(); + break; + case 0xf003: /* fdiv Rm,Rn */ + ctx->fpscr & FPSCR_PR ? gen_op_fdiv_DT() : gen_op_fdiv_FT(); + break; + case 0xf004: /* fcmp/eq Rm,Rn */ + return; + case 0xf005: /* fcmp/gt Rm,Rn */ + return; + } + + if (ctx->fpscr & FPSCR_PR) { + gen_op_fmov_DT0_drN(DREG(B11_8)); + } + else { + gen_op_fmov_FT0_frN(FREG(B11_8)); + } + return; } switch (ctx->opcode & 0xff00) { @@ -1079,6 +1129,46 @@ void decode_opc(DisasContext * ctx) gen_op_fmov_frN_FT0(FREG(B11_8)); gen_op_movl_FT0_fpul(); return; + case 0xf02d: /* float FPUL,FRn/DRn */ + if (ctx->fpscr & FPSCR_PR) { + if (ctx->opcode & 0x0100) + break; /* illegal instruction */ + gen_op_float_DT(); + gen_op_fmov_DT0_drN(DREG(B11_8)); + } + else { + gen_op_float_FT(); + gen_op_fmov_FT0_frN(FREG(B11_8)); + } + return; + case 0xf03d: /* ftrc FRm/DRm,FPUL */ + if (ctx->fpscr & FPSCR_PR) { + if (ctx->opcode & 0x0100) + break; /* illegal instruction */ + gen_op_fmov_drN_DT0(DREG(B11_8)); + gen_op_ftrc_DT(); + } + else { + gen_op_fmov_frN_FT0(FREG(B11_8)); + gen_op_ftrc_FT(); + } + return; + case 0xf08d: /* fldi0 FRn */ + if (!(ctx->fpscr & FPSCR_PR)) { + gen_op_movl_imm_T0(0); + gen_op_movl_T0_FT0(); + gen_op_fmov_FT0_frN(FREG(B11_8)); + return; + } + break; + case 0xf09d: /* fldi1 FRn */ + if (!(ctx->fpscr & FPSCR_PR)) { + gen_op_movl_imm_T0(0x3f800000); + gen_op_movl_T0_FT0(); + gen_op_fmov_FT0_frN(FREG(B11_8)); + return; + } + break; } fprintf(stderr, "unknown instruction 0x%04x at pc 0x%08x\n",