aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162>2004-05-08 21:08:41 +0000
committerbellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162>2004-05-08 21:08:41 +0000
commit2ee73ac3a855fb0cfba3db91fdd1ecebdbc6f971 (patch)
tree9759c191fd2b12e00749c4ea4b45298c9336c35c
parent28c3ee3fed3bb51c45320bec1ede3585cd36f8a4 (diff)
division by zero FPU exception support
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@795 c046a42c-6fe2-441c-8c8c-71466251a162
-rw-r--r--target-i386/cpu.h3
-rw-r--r--target-i386/exec.h14
-rw-r--r--target-i386/helper.c31
-rw-r--r--target-i386/op.c17
-rw-r--r--target-i386/translate.c5
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 */