aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoraurel32 <aurel32@c046a42c-6fe2-441c-8c8c-71466251a162>2008-12-07 22:46:31 +0000
committeraurel32 <aurel32@c046a42c-6fe2-441c-8c8c-71466251a162>2008-12-07 22:46:31 +0000
commitd8299bccf265b72db967a064e8151660b78cdfae (patch)
treed4333d881e01ccea7680165495345d8b05aa14e9
parent54604f74ae618aca3715cf0d8cb2ede923c0d6c0 (diff)
SH4: Implement FD bit
SH4 manual say that if a floating point instruction is executed while FD bit in the status register is 1, an exception should be raised. QEMU presently does not do that, so the kernel does not initialize FP state for any thread, nor does it save/restore FP state. The most apparent consequence is that while recent gcc/libc expect double-precision mode to be set by kernel, they run in single-precision mode, and all FP code produces wrong values. This patch fixes this. It also fixes a couple of places where PC was not updated before handling an exception, although both those places deal with invalid instruction and don't lead to any user-visible bugs. (Vladimir Prus) git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@5937 c046a42c-6fe2-441c-8c8c-71466251a162
-rw-r--r--target-sh4/cpu.h3
-rw-r--r--target-sh4/helper.h2
-rw-r--r--target-sh4/op_helper.c12
-rw-r--r--target-sh4/translate.c30
4 files changed, 42 insertions, 5 deletions
diff --git a/target-sh4/cpu.h b/target-sh4/cpu.h
index 23ecded55f..02ee24198b 100644
--- a/target-sh4/cpu.h
+++ b/target-sh4/cpu.h
@@ -279,7 +279,8 @@ static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc,
*flags = (env->flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL
| DELAY_SLOT_TRUE | DELAY_SLOT_CLEARME)) /* Bits 0- 3 */
| (env->fpscr & (FPSCR_FR | FPSCR_SZ | FPSCR_PR)) /* Bits 19-21 */
- | (env->sr & (SR_MD | SR_RB)); /* Bits 29-30 */
+ | (env->sr & (SR_MD | SR_RB)) /* Bits 29-30 */
+ | (env->sr & SR_FD); /* Bit 15 */
}
#endif /* _CPU_SH4_H */
diff --git a/target-sh4/helper.h b/target-sh4/helper.h
index e8fd050efe..631e7e1968 100644
--- a/target-sh4/helper.h
+++ b/target-sh4/helper.h
@@ -3,6 +3,8 @@
DEF_HELPER_0(ldtlb, void)
DEF_HELPER_0(raise_illegal_instruction, void)
DEF_HELPER_0(raise_slot_illegal_instruction, void)
+DEF_HELPER_0(raise_fpu_disable, void)
+DEF_HELPER_0(raise_slot_fpu_disable, void)
DEF_HELPER_0(debug, void)
DEF_HELPER_1(sleep, void, i32)
DEF_HELPER_1(trapa, void, i32)
diff --git a/target-sh4/op_helper.c b/target-sh4/op_helper.c
index f3e73004a7..63522194f7 100644
--- a/target-sh4/op_helper.c
+++ b/target-sh4/op_helper.c
@@ -89,6 +89,18 @@ void helper_raise_slot_illegal_instruction(void)
cpu_loop_exit();
}
+void helper_raise_fpu_disable(void)
+{
+ env->exception_index = 0x800;
+ cpu_loop_exit();
+}
+
+void helper_raise_slot_fpu_disable(void)
+{
+ env->exception_index = 0x820;
+ cpu_loop_exit();
+}
+
void helper_debug(void)
{
env->exception_index = EXCP_DEBUG;
diff --git a/target-sh4/translate.c b/target-sh4/translate.c
index 505b19690f..50b82e080c 100644
--- a/target-sh4/translate.c
+++ b/target-sh4/translate.c
@@ -447,17 +447,35 @@ static inline void gen_store_fpr64 (TCGv_i64 t, int reg)
#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)) \
- {gen_helper_raise_slot_illegal_instruction(); ctx->bstate = BS_EXCP; \
- return;}
+ if (ctx->flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL)) \
+ { \
+ tcg_gen_movi_i32(cpu_pc, ctx->pc-2); \
+ gen_helper_raise_slot_illegal_instruction(); \
+ ctx->bstate = BS_EXCP; \
+ return; \
+ }
#define CHECK_PRIVILEGED \
if (IS_USER(ctx)) { \
+ tcg_gen_movi_i32(cpu_pc, ctx->pc); \
gen_helper_raise_illegal_instruction(); \
ctx->bstate = BS_EXCP; \
return; \
}
+#define CHECK_FPU_ENABLED \
+ if (ctx->flags & SR_FD) { \
+ if (ctx->flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL)) { \
+ tcg_gen_movi_i32(cpu_pc, ctx->pc-2); \
+ gen_helper_raise_slot_fpu_disable(); \
+ } else { \
+ tcg_gen_movi_i32(cpu_pc, ctx->pc); \
+ gen_helper_raise_fpu_disable(); \
+ } \
+ ctx->bstate = BS_EXCP; \
+ return; \
+ }
+
static void _decode_opc(DisasContext * ctx)
{
#if 0
@@ -1454,12 +1472,14 @@ static void _decode_opc(DisasContext * ctx)
LDST(mach, 0x400a, 0x4006, 0x000a, 0x4002, {})
LDST(macl, 0x401a, 0x4016, 0x001a, 0x4012, {})
LDST(pr, 0x402a, 0x4026, 0x002a, 0x4022, {})
- LDST(fpul, 0x405a, 0x4056, 0x005a, 0x4052, {})
+ LDST(fpul, 0x405a, 0x4056, 0x005a, 0x4052, {CHECK_FPU_ENABLED})
case 0x406a: /* lds Rm,FPSCR */
+ CHECK_FPU_ENABLED
gen_helper_ld_fpscr(REG(B11_8));
ctx->bstate = BS_STOP;
return;
case 0x4066: /* lds.l @Rm+,FPSCR */
+ CHECK_FPU_ENABLED
{
TCGv addr = tcg_temp_new();
tcg_gen_qemu_ld32s(addr, REG(B11_8), ctx->memidx);
@@ -1470,9 +1490,11 @@ static void _decode_opc(DisasContext * ctx)
}
return;
case 0x006a: /* sts FPSCR,Rn */
+ CHECK_FPU_ENABLED
tcg_gen_andi_i32(REG(B11_8), cpu_fpscr, 0x003fffff);
return;
case 0x4062: /* sts FPSCR,@-Rn */
+ CHECK_FPU_ENABLED
{
TCGv addr, val;
val = tcg_temp_new();