@@ -121,6 +121,19 @@
#define VIP_MASK 0x00100000
#define ID_MASK 0x00200000
+/* FPU flags */
+#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
+
/* hidden flags - used internally by qemu to represent additional cpu
states. Only the CPL, INHIBIT_IRQ, SMM and SVMI are not
redundant. We avoid using the IOPL_MASK, TF_MASK and VM_MASK bit
@@ -877,6 +890,8 @@ void host_cpuid(uint32_t function, uint32_t count,
uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx);
/* helper.c */
+void cpu_x86_set_fpus(CPUX86State *s, uint16_t val);
+void cpu_x86_update_fpus(CPUX86State *s);
int cpu_x86_handle_mmu_fault(CPUX86State *env, target_ulong addr,
int is_write, int mmu_idx, int is_softmmu);
#define cpu_handle_mmu_fault cpu_x86_handle_mmu_fault
@@ -144,18 +144,6 @@ static inline void helper_fstt(floatx80 f, target_ulong ptr)
stw(ptr + 8, temp.l.upper);
}
-#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
-
static inline uint32_t compute_eflags(void)
{
return env->eflags | helper_cc_compute_all(CC_OP) | (DF & DF_MASK);
@@ -396,6 +396,7 @@ void cpu_dump_state(CPUState *env, FILE *f, fprintf_function cpu_fprintf,
for(i = 0; i < 8; i++) {
fptag |= ((!env->fptags[i]) << i);
}
+ cpu_x86_update_fpus(env);
cpu_fprintf(f, "FCW=%04x FSW=%04x [ST=%d] FTW=%02x MXCSR=%08x\n",
env->fpuc,
(env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11,
@@ -1238,6 +1239,41 @@ int cpu_x86_get_descr_debug(CPUX86State *env, unsigned int selector,
return 1;
}
+void cpu_x86_set_fpus(CPUX86State *s, uint16_t val)
+{
+ set_float_exception_flags(0, &s->fp_status);
+ s->fpus = val;
+}
+
+void cpu_x86_update_fpus(CPUX86State *s)
+{
+ int xcpt = get_float_exception_flags(&s->fp_status);
+
+ if (xcpt) {
+ if (xcpt & float_flag_invalid) {
+ s->fpus |= FPUS_IE;
+ }
+ if (xcpt & float_flag_input_denormal) {
+ s->fpus |= FPUS_DE;
+ }
+ if (xcpt & float_flag_divbyzero) {
+ s->fpus |= FPUS_ZE;
+ }
+ if (xcpt & float_flag_overflow) {
+ s->fpus |= FPUS_OE;
+ }
+ if (xcpt & float_flag_underflow) {
+ s->fpus |= FPUS_UE;
+ }
+ if (xcpt & float_flag_inexact) {
+ s->fpus |= FPUS_PE;
+ }
+ if (s->fpus & (~s->fpuc & FPUC_EM)) {
+ s->fpus |= FPUS_SE | FPUS_B;
+ }
+ }
+}
+
CPUX86State *cpu_x86_init(const char *cpu_model)
{
CPUX86State *env;
@@ -241,6 +241,7 @@ static void cpu_pre_save(void *opaque)
int i;
/* FPU */
+ cpu_x86_update_fpus(env);
env->fpus_vmstate = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;
env->fptag_vmstate = 0;
for(i = 0; i < 8; i++) {
@@ -257,7 +258,7 @@ static int cpu_post_load(void *opaque, int version_id)
/* XXX: restore FPU round state */
env->fpstt = (env->fpus_vmstate >> 11) & 7;
- env->fpus = env->fpus_vmstate & ~0x3800;
+ cpu_x86_set_fpus(env, env->fpus_vmstate & ~0x3800);
env->fptag_vmstate ^= 0xff;
for(i = 0; i < 8; i++) {
env->fptags[i] = (env->fptag_vmstate >> i) & 1;
@@ -3447,18 +3447,8 @@ static inline floatx80 double_to_floatx80(double a)
return float64_to_floatx80(u.f64, &env->fp_status);
}
-static void fpu_set_exception(int mask)
-{
- env->fpus |= mask;
- if (env->fpus & (~env->fpuc & FPUC_EM))
- env->fpus |= FPUS_SE | FPUS_B;
-}
-
static inline floatx80 helper_fdiv(floatx80 a, floatx80 b)
{
- if (floatx80_is_zero(b)) {
- fpu_set_exception(FPUS_ZE);
- }
return floatx80_div(a, b, &env->fp_status);
}
@@ -3845,6 +3835,7 @@ void helper_fldz_FT0(void)
uint32_t helper_fnstsw(void)
{
+ cpu_x86_update_fpus(env);
return (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;
}
@@ -3897,18 +3888,19 @@ void helper_fldcw(uint32_t val)
void helper_fclex(void)
{
- env->fpus &= 0x7f00;
+ cpu_x86_set_fpus(env, env->fpus & 0x7f00);
}
void helper_fwait(void)
{
+ cpu_x86_update_fpus(env);
if (env->fpus & FPUS_SE)
fpu_raise_exception();
}
void helper_fninit(void)
{
- env->fpus = 0;
+ cpu_x86_set_fpus(env, 0);
env->fpstt = 0;
env->fpuc = 0x37f;
env->fptags[0] = 1;
@@ -4208,7 +4200,13 @@ void helper_fscale(void)
if (floatx80_is_any_nan(ST1)) {
ST0 = ST1;
} else {
- int n = floatx80_to_int32_round_to_zero(ST1, &env->fp_status);
+ int n, x;
+
+ /* The float to int conversion should not generate any exception. */
+ x = get_float_exception_flags(&env->fp_status);
+ n = floatx80_to_int32_round_to_zero(ST1, &env->fp_status);
+ set_float_exception_flags(x, &env->fp_status);
+
ST0 = floatx80_scalbn(ST0, n, &env->fp_status);
}
}
@@ -4267,6 +4265,7 @@ void helper_fstenv(target_ulong ptr, int data32)
uint64_t mant;
CPU_LDoubleU tmp;
+ cpu_x86_update_fpus(env);
fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;
fptag = 0;
for (i=7; i>=0; i--) {
@@ -4324,7 +4323,7 @@ void helper_fldenv(target_ulong ptr, int data32)
fptag = lduw(ptr + 4);
}
env->fpstt = (fpus >> 11) & 7;
- env->fpus = fpus & ~0x3800;
+ cpu_x86_set_fpus(env, fpus & ~0x3800);
for(i = 0;i < 8; i++) {
env->fptags[i] = ((fptag & 3) == 3);
fptag >>= 2;
@@ -4346,7 +4345,7 @@ void helper_fsave(target_ulong ptr, int data32)
}
/* fninit */
- env->fpus = 0;
+ cpu_x86_set_fpus(env, 0);
env->fpstt = 0;
env->fpuc = 0x37f;
env->fptags[0] = 1;
@@ -4385,6 +4384,7 @@ void helper_fxsave(target_ulong ptr, int data64)
raise_exception(EXCP0D_GPF);
}
+ cpu_x86_update_fpus(env);
fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;
fptag = 0;
for(i = 0; i < 8; i++) {
@@ -4450,7 +4450,7 @@ void helper_fxrstor(target_ulong ptr, int data64)
fpus = lduw(ptr + 2);
fptag = lduw(ptr + 4);
env->fpstt = (fpus >> 11) & 7;
- env->fpus = fpus & ~0x3800;
+ cpu_x86_set_fpus(env, fpus & ~0x3800);
fptag ^= 0xff;
for(i = 0;i < 8; i++) {
env->fptags[i] = ((fptag >> i) & 1);
This patch adds support for FPU exceptions. It keeps the exception in the softfloat status, and copy them back to env->fpus when needed by oring them. When loading a new value to env->fpus, it starts with a clean softfloat status. Signed-off-by: Aurelien Jarno <aurelien@aurel32.net> --- target-i386/cpu.h | 15 +++++++++++++++ target-i386/exec.h | 12 ------------ target-i386/helper.c | 36 ++++++++++++++++++++++++++++++++++++ target-i386/machine.c | 3 ++- target-i386/op_helper.c | 32 ++++++++++++++++---------------- 5 files changed, 69 insertions(+), 29 deletions(-)