diff --git a/src/x87_ops.h b/src/x87_ops.h index 2294134..5a8616c 100644 --- a/src/x87_ops.h +++ b/src/x87_ops.h @@ -164,6 +164,74 @@ static inline void x87_stmmx(MMX_REG r) writememw(easeg, eaaddr + 8, 0xffff); } +static inline uint16_t x87_compare(double a, double b) +{ +#if defined i386 || defined __i386 || defined __i386__ || defined _X86_ || defined WIN32 || defined _WIN32 || defined _WIN32 + uint32_t out; + + /* Memory barrier, to force GCC to write to the input parameters + * before the compare rather than after */ + asm volatile ("" : : : "memory"); + + asm( + "fldl %2\n" + "fldl %1\n" + "fclex\n" + "fcompp\n" + "fnstsw %0\n" + : "=m" (out) + : "m" (a), "m" (b) + ); + + return out & (C0|C2|C3); +#else + /* Generic C version is known to give incorrect results in some + * situations, eg comparison of infinity (Unreal) */ + uint32_t out = 0; + + if (a == b) + out |= C3; + else if (a < b) + out |= C0; + + return out; +#endif +} + +static inline uint16_t x87_ucompare(double a, double b) +{ +#if defined i386 || defined __i386 || defined __i386__ || defined _X86_ || defined WIN32 || defined _WIN32 || defined _WIN32 + uint32_t out; + + /* Memory barrier, to force GCC to write to the input parameters + * before the compare rather than after */ + asm volatile ("" : : : "memory"); + + asm( + "fldl %2\n" + "fldl %1\n" + "fclex\n" + "fucompp\n" + "fnstsw %0\n" + : "=m" (out) + : "m" (a), "m" (b) + ); + + return out & (C0|C2|C3); +#else + /* Generic C version is known to give incorrect results in some + * situations, eg comparison of infinity (Unreal) */ + uint32_t out = 0; + + if (a == b) + out |= C3; + else if (a < b) + out |= C0; + + return out; +#endif +} + typedef union { float s; diff --git a/src/x87_ops_arith.h b/src/x87_ops_arith.h index d2985e1..f9394cf 100644 --- a/src/x87_ops_arith.h +++ b/src/x87_ops_arith.h @@ -16,8 +16,7 @@ static int opFCOM ## name ## _a ## a_size(uint32_t fetchdat) \ fetch_ea_ ## a_size(fetchdat); \ load_var = get(); if (abrt) return 1; \ npxs &= ~(C0|C2|C3); \ - if (ST(0) == use_var) npxs |= C3; \ - else if (ST(0) < use_var) npxs |= C0; \ + npxs |= x87_compare(ST(0), (double)use_var); \ CLOCK_CYCLES(4); \ return 0; \ } \ @@ -28,8 +27,7 @@ static int opFCOMP ## name ## _a ## a_size(uint32_t fetchdat) \ fetch_ea_ ## a_size(fetchdat); \ load_var = get(); if (abrt) return 1; \ npxs &= ~(C0|C2|C3); \ - if (ST(0) == use_var) npxs |= C3; \ - else if (ST(0) < use_var) npxs |= C0; \ + npxs |= x87_compare(ST(0), (double)use_var); \ x87_pop(); \ CLOCK_CYCLES(4); \ return 0; \ @@ -146,8 +144,7 @@ static int opFCOMP(uint32_t fetchdat) pc++; if (fplog) pclog("FCOMP\n"); npxs &= ~(C0|C2|C3); - if (ST(0) == ST(fetchdat & 7)) npxs |= C3; - else if (ST(0) < ST(fetchdat & 7)) npxs |= C0; + npxs |= x87_compare(ST(0), ST(fetchdat & 7)); x87_pop(); CLOCK_CYCLES(4); return 0; @@ -161,10 +158,9 @@ static int opFCOMPP(uint32_t fetchdat) npxs &= ~(C0|C2|C3); if (*(uint64_t *)&ST(0) == ((uint64_t)1 << 63) && *(uint64_t *)&ST(1) == 0) npxs |= C0; /*Nasty hack to fix 80387 detection*/ - else if (ST(0) == ST(1)) - npxs |= C3; - else if (ST(0) < ST(1)) - npxs |= C0; + else + npxs |= x87_compare(ST(0), ST(1)); + x87_pop(); x87_pop(); CLOCK_CYCLES(4); @@ -176,8 +172,7 @@ static int opFUCOMPP(uint32_t fetchdat) pc++; if (fplog) pclog("FUCOMPP\n", easeg, eaaddr); npxs &= ~(C0|C2|C3); - if (ST(0) == ST(1)) npxs |= C3; - else if (ST(0) < ST(1)) npxs |= C0; + npxs |= x87_ucompare(ST(0), ST(1)); x87_pop(); x87_pop(); CLOCK_CYCLES(5); @@ -335,8 +330,7 @@ static int opFUCOM(uint32_t fetchdat) pc++; if (fplog) pclog("FUCOM\n"); npxs &= ~(C0|C2|C3); - if (ST(0) == ST(fetchdat&7)) npxs |= C3; - else if (ST(0) < ST(fetchdat&7)) npxs |= C0; + npxs |= x87_ucompare(ST(0), ST(fetchdat & 7)); CLOCK_CYCLES(4); return 0; } @@ -347,8 +341,7 @@ static int opFUCOMP(uint32_t fetchdat) pc++; if (fplog) pclog("FUCOMP\n"); npxs &= ~(C0|C2|C3); - if (ST(0) == ST(fetchdat&7)) npxs |= C3; - else if (ST(0) < ST(fetchdat&7)) npxs |= C0; + npxs |= x87_ucompare(ST(0), ST(fetchdat & 7)); x87_pop(); CLOCK_CYCLES(4); return 0;