From 2ee73ac3a855fb0cfba3db91fdd1ecebdbc6f971 Mon Sep 17 00:00:00 2001 From: bellard Date: Sat, 8 May 2004 21:08:41 +0000 Subject: [PATCH] division by zero FPU exception support git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@795 c046a42c-6fe2-441c-8c8c-71466251a162 --- target-i386/cpu.h | 3 +++ target-i386/exec.h | 14 ++++++++++++++ target-i386/helper.c | 31 +++++++++++++++++++++++++++++-- target-i386/op.c | 17 +++++++++++++---- target-i386/translate.c | 5 +++++ 5 files changed, 64 insertions(+), 6 deletions(-) diff --git a/target-i386/cpu.h b/target-i386/cpu.h index 6b2a89bb9a..6939a2c6e6 100644 --- a/target-i386/cpu.h +++ b/target-i386/cpu.h @@ -143,6 +143,7 @@ #define CR0_MP_MASK (1 << 1) #define CR0_EM_MASK (1 << 2) #define CR0_TS_MASK (1 << 3) +#define CR0_ET_MASK (1 << 4) #define CR0_NE_MASK (1 << 5) #define CR0_WP_MASK (1 << 16) #define CR0_AM_MASK (1 << 18) @@ -373,6 +374,8 @@ CPUX86State *cpu_x86_init(void); int cpu_x86_exec(CPUX86State *s); void cpu_x86_close(CPUX86State *s); int cpu_get_pic_interrupt(CPUX86State *s); +/* MSDOS compatibility mode FPU exception support */ +void cpu_set_ferr(CPUX86State *s); /* this function must always be used to load data in the segment cache: it synchronizes the hflags with the segment cache values */ diff --git a/target-i386/exec.h b/target-i386/exec.h index 63010f745d..fb9cc772f9 100644 --- a/target-i386/exec.h +++ b/target-i386/exec.h @@ -478,10 +478,24 @@ static inline void helper_fstt(CPU86_LDouble f, uint8_t *ptr) #endif /* USE_X86LDOUBLE */ +#define FPUS_IE (1 << 0) +#define FPUS_DE (1 << 1) +#define FPUS_ZE (1 << 2) +#define FPUS_OE (1 << 3) +#define FPUS_UE (1 << 4) +#define FPUS_PE (1 << 5) +#define FPUS_SF (1 << 6) +#define FPUS_SE (1 << 7) +#define FPUS_B (1 << 15) + +#define FPUC_EM 0x3f + const CPU86_LDouble f15rk[7]; void helper_fldt_ST0_A0(void); void helper_fstt_ST0_A0(void); +void fpu_raise_exception(void); +CPU86_LDouble helper_fdiv(CPU86_LDouble a, CPU86_LDouble b); void helper_fbld_ST0_A0(void); void helper_fbst_ST0_A0(void); void helper_f2xm1(void); diff --git a/target-i386/helper.c b/target-i386/helper.c index 27a7a5559c..f2305e32c4 100644 --- a/target-i386/helper.c +++ b/target-i386/helper.c @@ -24,7 +24,7 @@ #if 0 #define raise_exception_err(a, b)\ do {\ - printf("raise_exception line=%d\n", __LINE__);\ + fprintf(logfile, "raise_exception line=%d\n", __LINE__);\ (raise_exception_err)(a, b);\ } while (0) #endif @@ -859,10 +859,11 @@ void do_interrupt(int intno, int is_int, int error_code, if (loglevel & (CPU_LOG_PCALL | CPU_LOG_INT)) { if ((env->cr[0] & CR0_PE_MASK)) { static int count; - fprintf(logfile, "%6d: v=%02x e=%04x i=%d cpl=%d IP=%04x:%08x SP=%04x:%08x", + fprintf(logfile, "%6d: v=%02x e=%04x i=%d cpl=%d IP=%04x:%08x pc=%08x SP=%04x:%08x", count, intno, error_code, is_int, env->hflags & HF_CPL_MASK, env->segs[R_CS].selector, EIP, + (int)env->segs[R_CS].base + EIP, env->segs[R_SS].selector, ESP); if (intno == 0x0e) { fprintf(logfile, " CR2=%08x", env->cr[2]); @@ -1990,6 +1991,32 @@ void helper_fstt_ST0_A0(void) helper_fstt(ST0, (uint8_t *)A0); } +void fpu_set_exception(int mask) +{ + env->fpus |= mask; + if (env->fpus & (~env->fpuc & FPUC_EM)) + env->fpus |= FPUS_SE | FPUS_B; +} + +CPU86_LDouble helper_fdiv(CPU86_LDouble a, CPU86_LDouble b) +{ + if (b == 0.0) + fpu_set_exception(FPUS_ZE); + return a / b; +} + +void fpu_raise_exception(void) +{ + if (env->cr[0] & CR0_NE_MASK) { + raise_exception(EXCP10_COPR); + } +#if !defined(CONFIG_USER_ONLY) + else { + cpu_set_ferr(env); + } +#endif +} + /* BCD ops */ void helper_fbld_ST0_A0(void) diff --git a/target-i386/op.c b/target-i386/op.c index 1169f121ad..37823319d6 100644 --- a/target-i386/op.c +++ b/target-i386/op.c @@ -1738,12 +1738,12 @@ void OPPROTO op_fsubr_ST0_FT0(void) void OPPROTO op_fdiv_ST0_FT0(void) { - ST0 /= FT0; + ST0 = helper_fdiv(ST0, FT0); } void OPPROTO op_fdivr_ST0_FT0(void) { - ST0 = FT0 / ST0; + ST0 = helper_fdiv(FT0, ST0); } /* fp operations between STN and ST0 */ @@ -1772,14 +1772,16 @@ void OPPROTO op_fsubr_STN_ST0(void) void OPPROTO op_fdiv_STN_ST0(void) { - ST(PARAM1) /= ST0; + CPU86_LDouble *p; + p = &ST(PARAM1); + *p = helper_fdiv(*p, ST0); } void OPPROTO op_fdivr_STN_ST0(void) { CPU86_LDouble *p; p = &ST(PARAM1); - *p = ST0 / *p; + *p = helper_fdiv(ST0, *p); } /* misc FPU operations */ @@ -1959,6 +1961,13 @@ void OPPROTO op_fclex(void) env->fpus &= 0x7f00; } +void OPPROTO op_fwait(void) +{ + if (env->fpus & FPUS_SE) + fpu_raise_exception(); + FORCE_RET(); +} + void OPPROTO op_fninit(void) { env->fpus = 0; diff --git a/target-i386/translate.c b/target-i386/translate.c index a1a4c633dc..c6aa5030ed 100644 --- a/target-i386/translate.c +++ b/target-i386/translate.c @@ -3761,6 +3761,11 @@ static uint8_t *disas_insn(DisasContext *s, uint8_t *pc_start) if ((s->flags & (HF_MP_MASK | HF_TS_MASK)) == (HF_MP_MASK | HF_TS_MASK)) { gen_exception(s, EXCP07_PREX, pc_start - s->cs_base); + } else { + if (s->cc_op != CC_OP_DYNAMIC) + gen_op_set_cc_op(s->cc_op); + gen_op_jmp_im(pc_start - s->cs_base); + gen_op_fwait(); } break; case 0xcc: /* int3 */ -- 2.11.0