about summary refs log tree commit diff stats
path: root/src/emu
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu')
-rw-r--r--src/emu/x64rund8.c8
-rw-r--r--src/emu/x64rund9.c47
-rw-r--r--src/emu/x64runda.c7
-rw-r--r--src/emu/x64rundb.c8
-rw-r--r--src/emu/x64rundc.c7
-rw-r--r--src/emu/x64runde.c8
-rw-r--r--src/emu/x87emu_setround.h29
7 files changed, 104 insertions, 10 deletions
diff --git a/src/emu/x64rund8.c b/src/emu/x64rund8.c
index 000eab21..fd5ccf66 100644
--- a/src/emu/x64rund8.c
+++ b/src/emu/x64rund8.c
@@ -1,4 +1,5 @@
 #define _GNU_SOURCE

+#include <fenv.h>

 #include <stdint.h>

 #include <stdio.h>

 #include <stdlib.h>

@@ -17,6 +18,7 @@
 #include "x64primop.h"

 #include "x64trace.h"

 #include "x87emu_private.h"

+#include "x87emu_setround.h"

 #include "box64context.h"

 #include "bridge.h"

 

@@ -34,6 +36,7 @@ uintptr_t RunD8(x64emu_t *emu, rex_t rex, uintptr_t addr, uintptr_t offs)
     #ifdef TEST_INTERPRETER

     x64emu_t*emu = test->emu;

     #endif

+    int oldround = fpu_setround(emu);

 

     nextop = F8;

     if(MODREG)

@@ -121,6 +124,7 @@ uintptr_t RunD8(x64emu_t *emu, rex_t rex, uintptr_t addr, uintptr_t offs)
             ST0.d = ST(nextop&7).d / ST0.d;

             break;

         default:

+            fesetround(oldround);

             return 0;

     } else

         switch((nextop>>3)&7) {

@@ -190,7 +194,9 @@ uintptr_t RunD8(x64emu_t *emu, rex_t rex, uintptr_t addr, uintptr_t offs)
                 ST0.d = *(float*)ED / ST0.d;

                 break;

             default:

+                fesetround(oldround);

                 return 0;

         }

-   return addr;

+    fesetround(oldround);

+    return addr;

 }
\ No newline at end of file
diff --git a/src/emu/x64rund9.c b/src/emu/x64rund9.c
index 648eb0a7..c7b8d308 100644
--- a/src/emu/x64rund9.c
+++ b/src/emu/x64rund9.c
@@ -1,4 +1,5 @@
 #define _GNU_SOURCE

+#include <fenv.h>

 #include <stdint.h>

 #include <stdio.h>

 #include <stdlib.h>

@@ -17,6 +18,7 @@
 #include "x64primop.h"

 #include "x64trace.h"

 #include "x87emu_private.h"

+#include "x87emu_setround.h"

 #include "box64context.h"

 #include "bridge.h"

 

@@ -33,6 +35,7 @@ uintptr_t RunD9(x64emu_t *emu, rex_t rex, uintptr_t addr, uintptr_t offs)
     uint64_t ll;

     float f;

     reg64_t *oped;

+    int oldround;

     #ifdef TEST_INTERPRETER

     x64emu_t*emu = test->emu;

     #endif

@@ -123,20 +126,30 @@ uintptr_t RunD9(x64emu_t *emu, rex_t rex, uintptr_t addr, uintptr_t offs)
             break;

 

         case 0xF0:  /* F2XM1 */

-            ST0.d = exp2(ST0.d) - 1.0;

+            if (ST0.d == 0)

+                break;

+            // Using the expm1 instead of exp2(ST0)-1 can avoid losing precision much,

+            // expecially when ST0 is close to zero (which loses the precise when -1).

+            // printf("%a, %a\n", LN2 * ST0.d, expm1(LN2 * ST0.d));

+            ST0.d = expm1(LN2 * ST0.d);

+            //    = 2^ST0 - 1 + error. (in math)

             break;

         case 0xF1:  /* FYL2X */

             ST(1).d *= log2(ST0.d);

             fpu_do_pop(emu);

             break;

         case 0xF2:  /* FPTAN */

+            oldround = fpu_setround(emu);

             ST0.d = tan(ST0.d);

+            fesetround(oldround);

             fpu_do_push(emu);

             ST0.d = 1.0;

             emu->sw.f.F87_C2 = 0;

             break;

         case 0xF3:  /* FPATAN */

+            oldround = fpu_setround(emu);

             ST1.d = atan2(ST1.d, ST0.d);

+            fesetround(oldround);

             fpu_do_pop(emu);

             break;

         case 0xF4:  /* FXTRACT */

@@ -209,15 +222,22 @@ uintptr_t RunD9(x64emu_t *emu, rex_t rex, uintptr_t addr, uintptr_t offs)
                 emu->top=(emu->top+1)&7;    // this will probably break a few things

             break;

         case 0xF9:  /* FYL2XP1 */

-            ST(1).d *= log2(ST0.d + 1.0);

+            // Using the log1p instead of log2(ST0+1) can avoid losing precision much,

+            // expecially when ST0 is close to zero (which loses the precise when +1).

+            ST(1).d = (ST(1).d * log1p(ST0.d)) / M_LN2;

+            //      = ST1 * log2(ST0 + 1) + error. (in math)

             fpu_do_pop(emu);

             break;

         case 0xFA:  /* FSQRT */

+            oldround = fpu_setround(emu);

             ST0.d = sqrt(ST0.d);

+            fesetround(oldround);

             break;

         case 0xFB:  /* FSINCOS */

             fpu_do_push(emu);

+            oldround = fpu_setround(emu);

             sincos(ST1.d, &ST1.d, &ST0.d);

+            fesetround(oldround);

             emu->sw.f.F87_C2 = 0;

             break;

         case 0xFC:  /* FRNDINT */

@@ -225,15 +245,28 @@ uintptr_t RunD9(x64emu_t *emu, rex_t rex, uintptr_t addr, uintptr_t offs)
             break;

         case 0xFD:  /* FSCALE */

             // this could probably be done by just altering the exponant part of the float...

-            if(ST0.d!=0.0)

-                ST0.d *= exp2(trunc(ST1.d));

+            if (ST1.d > INT32_MAX)

+                tmp32s = INT32_MAX;

+            else if (ST1.d < INT32_MIN)

+                tmp32s = INT32_MIN;

+            else

+                tmp32s = ST1.d;

+            if(ST0.d!=0.0) {

+                oldround = fpu_setround(emu);

+                ST0.d = ldexp(ST0.d, tmp32s);

+                fesetround(oldround);

+            }

             break;

         case 0xFE:  /* FSIN */

+            oldround = fpu_setround(emu);

             ST0.d = sin(ST0.d);

+            fesetround(oldround);

             emu->sw.f.F87_C2 = 0;

             break;

         case 0xFF:  /* FCOS */

+            oldround = fpu_setround(emu);

             ST0.d = cos(ST0.d);

+            fesetround(oldround);

             emu->sw.f.F87_C2 = 0;

             break;

 

@@ -257,7 +290,9 @@ uintptr_t RunD9(x64emu_t *emu, rex_t rex, uintptr_t addr, uintptr_t offs)
                 } else {

                     GETE4(0);

                 }

+                oldround = fpu_setround(emu);

                 *(float*)ED = ST0.d;

+                fesetround(oldround);

                 break;

             case 3:     /* FSTP Ed, ST0 */

                 if(offs) {

@@ -265,7 +300,9 @@ uintptr_t RunD9(x64emu_t *emu, rex_t rex, uintptr_t addr, uintptr_t offs)
                 } else {

                     GETE4(0);

                 }

+                oldround = fpu_setround(emu);

                 *(float*)ED = ST0.d;

+                fesetround(oldround);

                 fpu_do_pop(emu);

                 break;

             case 4:     /* FLDENV m */

@@ -309,5 +346,5 @@ uintptr_t RunD9(x64emu_t *emu, rex_t rex, uintptr_t addr, uintptr_t offs)
             default:

                 return 0;

         }

-   return addr;

+    return addr;

 }
\ No newline at end of file
diff --git a/src/emu/x64runda.c b/src/emu/x64runda.c
index aed775f9..056937e7 100644
--- a/src/emu/x64runda.c
+++ b/src/emu/x64runda.c
@@ -1,4 +1,5 @@
 #define _GNU_SOURCE

+#include <fenv.h>

 #include <stdint.h>

 #include <stdio.h>

 #include <stdlib.h>

@@ -17,6 +18,7 @@
 #include "x64primop.h"

 #include "x64trace.h"

 #include "x87emu_private.h"

+#include "x87emu_setround.h"

 #include "box64context.h"

 #include "bridge.h"

 

@@ -94,7 +96,8 @@ uintptr_t RunDA(x64emu_t *emu, rex_t rex, uintptr_t addr)
 

     default:

         return 0;

-    } else

+    } else {

+        int oldround = fpu_setround(emu);

         switch((nextop>>3)&7) {

             case 0:     /* FIADD ST0, Ed int */

                 GETE4(0);

@@ -130,5 +133,7 @@ uintptr_t RunDA(x64emu_t *emu, rex_t rex, uintptr_t addr)
                 ST0.d = (double)ED->sdword[0] / ST0.d;

                 break;

         }

+        fesetround(oldround);

+    }

    return addr;

 }
\ No newline at end of file
diff --git a/src/emu/x64rundb.c b/src/emu/x64rundb.c
index 82ea43ff..b6ba0ff1 100644
--- a/src/emu/x64rundb.c
+++ b/src/emu/x64rundb.c
@@ -1,4 +1,5 @@
 #define _GNU_SOURCE

+#include <fenv.h>

 #include <stdint.h>

 #include <stdio.h>

 #include <stdlib.h>

@@ -17,6 +18,7 @@
 #include "x64primop.h"

 #include "x64trace.h"

 #include "x87emu_private.h"

+#include "x87emu_setround.h"

 #include "box64context.h"

 #include "bridge.h"

 

@@ -35,6 +37,7 @@ uintptr_t RunDB(x64emu_t *emu, rex_t rex, uintptr_t addr)
     x64emu_t*emu = test->emu;

     #endif

 

+    int oldround = fpu_setround(emu);

     nextop = F8;

     if(MODREG)

     switch(nextop) {

@@ -128,6 +131,7 @@ uintptr_t RunDB(x64emu_t *emu, rex_t rex, uintptr_t addr)
         break;

 

     default:

+        fesetround(oldround);

         return 0;

     } else

         switch((nextop>>3)&7) {

@@ -179,7 +183,9 @@ uintptr_t RunDB(x64emu_t *emu, rex_t rex, uintptr_t addr)
                 fpu_do_pop(emu);

                 break;

             default:

+                fesetround(oldround);

                 return 0;

         }

-  return addr;

+    fesetround(oldround);

+    return addr;

 }

diff --git a/src/emu/x64rundc.c b/src/emu/x64rundc.c
index 6d9ff07b..c9ffe738 100644
--- a/src/emu/x64rundc.c
+++ b/src/emu/x64rundc.c
@@ -17,6 +17,7 @@
 #include "x64primop.h"
 #include "x64trace.h"
 #include "x87emu_private.h"
+#include "x87emu_setround.h"
 #include "box64context.h"
 #include "bridge.h"
 
@@ -34,6 +35,7 @@ uintptr_t RunDC(x64emu_t *emu, rex_t rex, uintptr_t addr)
     x64emu_t*emu = test->emu;
     #endif
 
+    int oldround = fpu_setround(emu);
     nextop = F8;
     if(MODREG)
     switch(nextop) {
@@ -119,6 +121,7 @@ uintptr_t RunDC(x64emu_t *emu, rex_t rex, uintptr_t addr)
             ST(nextop&7).d /=  ST0.d;
             break;
         default:
+            fesetround(oldround);
             return 0;
     } else {
             GETE8(0);
@@ -149,8 +152,10 @@ uintptr_t RunDC(x64emu_t *emu, rex_t rex, uintptr_t addr)
                 ST0.d = *(double*)ED / ST0.d;
                 break;
            default:
+                fesetround(oldround);
                 return 0;
         }
     }
-  return addr;
+    fesetround(oldround);
+    return addr;
 }
diff --git a/src/emu/x64runde.c b/src/emu/x64runde.c
index 4cb5e4a2..8b082aa5 100644
--- a/src/emu/x64runde.c
+++ b/src/emu/x64runde.c
@@ -1,4 +1,5 @@
 #define _GNU_SOURCE
+#include <fenv.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -17,6 +18,7 @@
 #include "x64primop.h"
 #include "x64trace.h"
 #include "x87emu_private.h"
+#include "x87emu_setround.h"
 #include "box64context.h"
 #include "bridge.h"
 
@@ -34,6 +36,7 @@ uintptr_t RunDE(x64emu_t *emu, rex_t rex, uintptr_t addr)
     x64emu_t*emu = test->emu;
     #endif
 
+    int oldround = fpu_setround(emu);
     nextop = F8;
     if(MODREG)
     switch(nextop) {
@@ -126,6 +129,7 @@ uintptr_t RunDE(x64emu_t *emu, rex_t rex, uintptr_t addr)
             break;
 
         default:
+            fesetround(oldround);
             return 0;
     } else
         switch((nextop>>3)&7) {
@@ -154,7 +158,9 @@ uintptr_t RunDE(x64emu_t *emu, rex_t rex, uintptr_t addr)
                 ST0.d = (double)EW->sword[0] / ST0.d;
                 break;
            default:
+                fesetround(oldround);
                 return 0;
         }
-  return addr;
+    fesetround(oldround);
+    return addr;
 }
\ No newline at end of file
diff --git a/src/emu/x87emu_setround.h b/src/emu/x87emu_setround.h
new file mode 100644
index 00000000..4791c3ea
--- /dev/null
+++ b/src/emu/x87emu_setround.h
@@ -0,0 +1,29 @@
+#ifndef __SETROUND_H__
+#define __SETROUND_H__
+#pragma STDC FENV_ACCESS ON
+#include <fenv.h>
+#include <stdint.h>
+#include "x64emu.h"
+#include "x64emu_private.h"
+// set the rounding mode to the emulator's one, and return the old one
+static inline int fpu_setround(x64emu_t* emu) {
+    int ret = fegetround();
+    int rounding_direction;
+    switch (emu->cw.f.C87_RD) {
+        case ROUND_Nearest:
+            rounding_direction = FE_TONEAREST;
+            break;
+        case ROUND_Down:
+            rounding_direction = FE_DOWNWARD;
+            break;
+        case ROUND_Up:
+            rounding_direction = FE_UPWARD;
+            break;
+        case ROUND_Chop:
+            rounding_direction = FE_TOWARDZERO;
+            break;
+    }
+    fesetround(rounding_direction);
+    return ret;
+}
+#endif
\ No newline at end of file