]> git.unchartedbackwaters.co.uk Git - francis/winuae.git/commitdiff
fpu test max precision/double mode
authorToni Wilen <twilen@winuae.net>
Sat, 16 Sep 2023 17:31:48 +0000 (20:31 +0300)
committerToni Wilen <twilen@winuae.net>
Sat, 16 Sep 2023 17:31:48 +0000 (20:31 +0300)
cputest.cpp
cputest/asm.S
cputest/main.c

index 77877c121587c87aaa2bf057c0afadee1370bcbb..c636dc2a52fb971f2650d0124d71e5f7bc79b255 100644 (file)
@@ -75,7 +75,7 @@ static int feature_exception_vectors = 0;
 static int feature_interrupts = 0;
 static int feature_waitstates = 0;
 static int feature_instruction_size = 0;
-static int fpu_min_exponent, fpu_max_exponent;
+static int fpu_min_exponent, fpu_max_exponent, fpu_max_precision, fpu_unnormals;
 static int feature_ipl_delay;
 static int max_file_size;
 static int rnd_seed, rnd_seed_prev;
@@ -1853,6 +1853,57 @@ static uae_u8 frand8(void)
        return (uae_u8)xorshift32();
 }
 
+static bool fpu_precision_valid(floatx80 f)
+{
+       int exp = f.high & 0x7fff;
+       if (exp != 0x0000) {
+               exp -= 16384;
+               if (exp < fpu_min_exponent || exp > fpu_max_exponent) {
+                       return false;
+               }
+       }
+       float_status status = { 0 };
+       status.floatx80_rounding_precision = 80;
+       status.float_rounding_mode = float_round_nearest_even;
+       status.float_exception_flags = 0;
+       if (fpu_max_precision == 2) {
+               float64 fo = floatx80_to_float64(f, &status);
+               int exp = (float64_val(fo) >> 52) & 0x7FF;
+               if (exp >= 0x700) {
+                       return false;
+               }
+               if (float64_is_nan(fo)) {
+                       return false;
+               }
+       } else if (fpu_max_precision == 1) {
+               float32 fo = floatx80_to_float32(f, &status);
+               int exp = (float32_val(fo) >> 23) & 0xFF;
+               if (exp >= 0xe0) {
+                       return false;
+               }
+               if (float32_is_nan(fo)) {
+                       return false;
+               }
+       }
+       if (fpu_max_precision) {
+               if (status.float_exception_flags & (float_flag_underflow | float_flag_overflow | float_flag_denormal | float_flag_invalid)) {
+                       return false;
+               }
+               if (floatx80_is_any_nan(f)) {
+                       return false;
+               }
+       }
+       if (!fpu_unnormals) {
+               if (!(f.low & 0x8000000000000000) && (f.high & 0x7fff) && (f.high & 0x7fff) != 0x7fff) {
+                       return false;
+               }
+               if (status.float_exception_flags & float_flag_denormal) {
+                       return false;
+               }
+       }
+       return true;
+}
+
 static uae_u32 registers[] =
 {
        0x00000010,     // 0
@@ -1877,6 +1928,21 @@ static int regcnts[16];
 static int fpuregcnts[8];
 static float_status fpustatus;
 
+static floatx80 fpu_random(void)
+{
+       floatx80 v = int32_to_floatx80(rand32());
+       for (int i = 0; i < 10; i++) {
+               uae_u64 n = rand32() | (((uae_u64)rand16()) << 32);
+               // don't create denormals yet
+               if (!((v.low + n) & 0x8000000000000000)) {
+                       v.low |= 0x8000000000000000;
+                       continue;
+               }
+               v.low += n;
+       }
+       return v;
+}
+
 static bool fpuregchange(int reg, fpdata *regs)
 {
        int regcnt = fpuregcnts[reg];
@@ -1893,64 +1959,77 @@ static bool fpuregchange(int reg, fpdata *regs)
                }
        }
 
-       switch(reg)
-       {
-       case 0: // positive
-               add = floatx80_div(int32_to_floatx80(1), int32_to_floatx80(10), &fpustatus);
-               v = floatx80_add(v, add, &fpustatus);
-               break;
-       case 1: // negative
-               add = floatx80_div(int32_to_floatx80(1), int32_to_floatx80(10), &fpustatus);
-               v = floatx80_sub(v, add, &fpustatus);
-               break;
-       case 2: // positive/negative zero
-               v.high ^= 0x8000;
-               break;
-       case 3:
-               // very large value, larger than fits in double
-               v = packFloatx80(1, 0x7000 + (rand16() & 0xfff), 0x8000000000000000 | (((uae_u64)rand32()) << 32));
-               if (regcnt & 1) {
-                       v.high ^= 0x8000;
-               }
-               break;
-       case 4:
-               // value that fits in double but does not fit in single
-               v = packFloatx80(1, 0x700 + rand8(), 0x8000000000000000 | (((uae_u64)rand32()) << 32));
-               if (regcnt & 1) {
+       for(;;) {
+
+               switch(reg)
+               {
+               case 0: // positive
+                       add = floatx80_div(int32_to_floatx80(1), int32_to_floatx80(5 + regcnt), &fpustatus);
+                       v = floatx80_add(v, add, &fpustatus);
+                       break;
+               case 1: // negative
+                       add = floatx80_div(int32_to_floatx80(1), int32_to_floatx80(6 + regcnt), &fpustatus);
+                       v = floatx80_sub(v, add, &fpustatus);
+                       break;
+               case 2: // positive/negative zero
                        v.high ^= 0x8000;
-               }
-               break;
-       case 5:
-       case 6:
-               // random
-               v = int32_to_floatx80(rand32());
-               for (int i = 0; i < 10; i++) {
-                       uae_u64 n = rand32() | (((uae_u64)rand16()) << 32);
-                       // don't create denormals yet
-                       if (!((v.low + n) & 0x8000000000000000)) {
-                               v.low |= 0x8000000000000000;
-                               continue;
+                       break;
+               case 3:
+                       // very large value, larger than fits in double
+                       if (fpu_max_precision) {
+                               v = fpu_random();
+                       } else {
+                               v = packFloatx80(1, 0x7000 + (rand16() & 0xfff), 0x8000000000000000 | (((uae_u64)rand32()) << 32));
+                               if (regcnt & 1) {
+                                       v.high ^= 0x8000;
+                               }
+                       }
+                       break;
+               case 4:
+                       // value that fits in double but does not fit in single
+                       {
+                               int exp;
+                               if (fpu_max_precision < 128) {
+                                       exp = fpu_max_precision + rand8();
+                               } else {
+                                       exp = 128 + rand8();
+                               }
+                               exp += 16384;
+                               v = packFloatx80(1, exp, 0x8000000000000000 | (((uae_u64)rand32()) << 32));
+                               if (regcnt & 1) {
+                                       v.high ^= 0x8000;
+                               }
+                       }
+                       break;
+               case 5:
+               case 6:
+                       // random
+                       v = fpu_random();
+                       break;
+               case 7: // +NaN, -Nan, +Inf, -Inf
+                       if (fpu_max_precision) {
+                               v = fpu_random();
+                       } else {
+                               if ((regcnt & 3) == 0) {
+                                       v = floatx80_default_nan(NULL);
+                               } else if ((regcnt & 3) == 1) {
+                                       v = floatx80_default_nan(NULL);
+                                       v.high ^= 0x8000;
+                               } else if ((regcnt & 3) == 2) {
+                                       v = packFloatx80(0, 0x7FFF, floatx80_default_infinity_low);
+                               } else if ((regcnt & 3) == 3) {
+                                       v = packFloatx80(1, 0x7FFF, floatx80_default_infinity_low);
+                               }
                        }
-                       v.low += n;
                        break;
                }
-               break;
-       case 7: // +NaN, -Nan, +Inf, -Inf
-               if ((regcnt & 3) == 0) {
-                       v = floatx80_default_nan(NULL);
-               } else if ((regcnt & 3) == 1) {
-                       v = floatx80_default_nan(NULL);
-                       v.high ^= 0x8000;
-               } else if ((regcnt & 3) == 2) {
-                       v = packFloatx80(0, 0x7FFF, floatx80_default_infinity_low);
-               } else if ((regcnt & 3) == 3) {
-                       v = packFloatx80(1, 0x7FFF, floatx80_default_infinity_low);
+
+               if (fpu_precision_valid(v)) {
+                       fpuregcnts[reg]++;
+                       regs[reg].fpx = v;
+                       return true;
                }
-               break;
        }
-       fpuregcnts[reg]++;
-       regs[reg].fpx = v;
-       return true;
 }
 
 static bool regchange(int reg, uae_u32 *regs)
@@ -2060,13 +2139,51 @@ static bool regchange(int reg, uae_u32 *regs)
 
 static void fill_memory_buffer(uae_u8 *p, int size)
 {
-       for (int i = 0; i < size; i++) {
-               p[i] = frand8();
-       }
-       // fill extra zeros
-       for (int i = 0; i < size; i++) {
-               if (frand8() < 0x70)
-                       p[i] = 0x00;
+       uae_u8 *pend = p - 64;
+       int i = 0;
+       while (p < pend) {
+               int x = (i & 3);
+               if (x == 1) {
+                       floatx80 v = int32_to_floatx80(xorshift32());
+                       *p++ = v.high >> 8;
+                       *p++ = v.high >> 0;
+                       *p++ = 0;
+                       *p++ = 0;
+                       *p++ = (uae_u8)(v.low >> 56);
+                       *p++ = (uae_u8)(v.low >> 48);
+                       *p++ = (uae_u8)(v.low >> 40);
+                       *p++ = (uae_u8)(v.low >> 32);
+                       if ((i & 15) < 8) {
+                               *p++ = (uae_u8)(v.low >> 24);
+                               *p++ = (uae_u8)(v.low >> 16);
+                               *p++ = (uae_u8)(v.low >>  8);
+                               *p++ = (uae_u8)(v.low >>  0);
+                       } else {
+                               *p++ = frand8();
+                               *p++ = frand8();
+                               *p++ = frand8();
+                               *p++ = frand8();
+                       }
+               } else if (x == 2) {
+                       floatx80 v = int32_to_floatx80(xorshift32());
+                       float64 v2 = floatx80_to_float64(v, &fpustatus);
+                       *p++ = (uae_u8)(v2 >> 56);
+                       *p++ = (uae_u8)(v2 >> 48);
+                       *p++ = (uae_u8)(v2 >> 40);
+                       *p++ = (uae_u8)(v2 >> 32);
+                       *p++ = (uae_u8)(v2 >> 24);
+                       *p++ = (uae_u8)(v2 >> 16);
+                       *p++ = (uae_u8)(v2 >>  8);
+                       *p++ = (uae_u8)(v2 >>  0);
+               } else if (x == 3) {
+                       floatx80 v = int32_to_floatx80(xorshift32());
+                       float32 v2 = floatx80_to_float32(v, &fpustatus);
+                       *p++ = (uae_u8)(v2 >> 24);
+                       *p++ = (uae_u8)(v2 >> 16);
+                       *p++ = (uae_u8)(v2 >> 8);
+                       *p++ = (uae_u8)(v2 >> 0);
+               }
+               i++;
        }
 }
 
@@ -2619,7 +2736,7 @@ static void save_data(uae_u8 *dst, const TCHAR *dir, int size)
                        (feature_min_interrupt_mask << 20) | (safe_memory_mode << 23) | (feature_interrupts << 26) |
                        ((feature_loop_mode_jit ? 1 : 0) << 28) | ((feature_loop_mode_68010 ? 1 : 0) << 29));
                fwrite(data, 1, 4, f);
-               pl(data, (feature_initial_interrupt_mask & 7) | ((feature_initial_interrupt & 7) << 3));
+               pl(data, (feature_initial_interrupt_mask & 7) | ((feature_initial_interrupt & 7) << 3) | (fpu_max_precision << 6));
                fwrite(data, 1, 4, f);
                pl(data, 0);
                fwrite(data, 1, 4, f);
@@ -4329,12 +4446,8 @@ static void execute_ins(uaecptr endpc, uaecptr targetpc, struct instr *dp, bool
                        // skip result if it has too large or small exponent
                        for (int i = 0; i < 8; i++) {
                                if (regs.fp[i].fpx.high != cur_regs.fp[i].fpx.high || regs.fp[i].fpx.low != cur_regs.fp[i].fpx.low) {
-                                       int exp = regs.fp[i].fpx.high & 0x7fff;
-                                       if (exp != 0x0000) {
-                                               if (exp < fpu_min_exponent || exp > fpu_max_exponent) {
-                                                       test_exception = -1;
-                                                       break;
-                                               }
+                                       if (!fpu_precision_valid(regs.fp[i].fpx)) {
+                                               test_exception = -1;
                                        }
                                }
                        }
@@ -4900,7 +5013,13 @@ static void test_mnemo(const TCHAR *path, const TCHAR *mnemo, const TCHAR *ovrfi
        target_ea_opcode_cnt = 0;
        generate_address_mode = 0;
 
-       fpustatus.floatx80_rounding_precision = 80;
+       if (fpu_max_precision == 2) {
+               fpustatus.floatx80_rounding_precision = 64;
+       } else if (fpu_max_precision == 1) {
+               fpustatus.floatx80_rounding_precision = 32;
+       } else {
+               fpustatus.floatx80_rounding_precision = 80;
+       }
        fpustatus.float_rounding_mode = float_round_nearest_even;
 
        // 1.0
@@ -4909,12 +5028,16 @@ static void test_mnemo(const TCHAR *path, const TCHAR *mnemo, const TCHAR *ovrfi
        fpuregisters[1] = int32_to_floatx80(-1);
        // 0.0
        fpuregisters[2] = int32_to_floatx80(0);
-       // NaN
-       fpuregisters[7] = floatx80_default_nan(NULL);
+       if (fpu_max_precision) {
+               fpuregisters[7] = int32_to_floatx80(2);
+       } else {
+               // NaN
+               fpuregisters[7] = floatx80_default_nan(NULL);
+       }
 
        for (int i = 3; i < 7; i++) {
                uae_u32 v = rand32();
-               if (v < 10 || v > -10)
+               if (v < 15 || v > -15)
                        continue;
                fpuregisters[i] = int32_to_floatx80(v);
        }
@@ -6954,8 +7077,9 @@ static int test(struct ini_data *ini, const TCHAR *sections, const TCHAR *testna
                }
        }
 
-       fpu_min_exponent = 0;
-       fpu_max_exponent = 32768;
+       fpu_min_exponent = -65535;
+       fpu_max_exponent = 65535;
+       fpu_max_precision = 0;
        if (ini_getvalx(ini, sections, _T("fpu_min_exponent"), &v)) {
                if (v >= 0) {
                        fpu_min_exponent = v;
@@ -6966,6 +7090,16 @@ static int test(struct ini_data *ini, const TCHAR *sections, const TCHAR *testna
                        fpu_max_exponent = v;
                }
        }
+       if (ini_getvalx(ini, sections, _T("fpu_max_precision"), &v)) {
+               if (v == 1 || v == 2) {
+                       fpu_max_precision = v;
+               }
+       }
+       if (ini_getvalx(ini, sections, _T("fpu_unnormals"), &v)) {
+               if (v) {
+                       fpu_unnormals = 1;
+               }
+       }
 
        rnd_seed = 0;
        ini_getvalx(ini, sections, _T("seed"), &rnd_seed);
index fa10af8ae3144f5c014f285e3daab98f2fb6f4e7..4baf3b846fa695a6791175bb39430e6c1d77e97c 100644 (file)
@@ -56,7 +56,7 @@ S_NEXT = S_FSAVE+216
 asm_start:
 
 _initfpu:
-       moveq #0,d0
+       move.l 4(sp),d0
        fmove.l d0,fpcr
        rts
 
index 0e3c73721b571545ed42530f93ef29ab0c12584f..b7c4f166f14d76738107de0bae5c40ae20260e5f 100644 (file)
@@ -90,6 +90,7 @@ static uae_u8 *vbr_zero = 0;
 static int hmem_rom, lmem_rom;
 static uae_u8 *absallocated;
 static int cpu_lvl, fpu_model;
+static uae_u8 fpu_max_precision;
 static uae_u16 sr_undefined_mask;
 static int check_undefined_sr;
 static short is_fpu_adjust;
@@ -137,6 +138,7 @@ static short dooutput = 1;
 static short quit;
 static uae_u8 ccr_mask;
 static uae_u32 fpsr_ignore_mask;
+static short fpiar_ignore;
 static uae_u32 addressing_mask = 0x00ffffff;
 static uae_u32 interrupt_mask;
 static short initial_interrupt_mask;
@@ -257,7 +259,7 @@ static uae_u32 fpucompzero(void *v)
 {
        return 0;
 }
-static void initfpu(void)
+static void initfpu(uae_u32 v)
 {
 }
 static void *error_vector;
@@ -288,7 +290,7 @@ extern void *error_vector;
 extern void berrcopy(void*, void*, uae_u32, uae_u32);
 extern uae_u32 fpucomp(void *);
 extern uae_u32 fpucompzero(void *);
-extern void initfpu(void);
+extern void initfpu(uae_u32);
 
 #endif
 static uae_u32 exceptiontableinuse;
@@ -2832,7 +2834,7 @@ static uae_u8 *validate_test(uae_u8 *p, short ignore_errors, short ignore_sr, st
                        int size;
                        p = restore_value(p, &val, &size);
                        if (val != tregs->fpiar) {
-                               if (!ignore_errors) {
+                               if (!ignore_errors && !fpiar_ignore) {
                                        if (dooutput) {
                                                if (sregs->fpiar == tregs->fpiar) {
                                                        sprintf(outbp, "FPIAR: expected %08x but register was not modified\n", val);
@@ -2968,7 +2970,7 @@ static uae_u8 *validate_test(uae_u8 *p, short ignore_errors, short ignore_sr, st
                        }
                        errflag |= 1 << 4;
                }
-               if (fpiar_changed && tregs->fpiar != lregs->fpiar) {
+               if (fpiar_changed && tregs->fpiar != lregs->fpiar && !fpiar_ignore) {
                        if (dooutput) {
                                uae_u32 val = lregs->fpiar;
                                sprintf(outbp, "FPIAR: expected %08x but got %08x\n", val, tregs->fpiar);
@@ -3241,7 +3243,13 @@ static void process_test(uae_u8 *p)
 
                int fpumode = fpu_model && (opcode_memory[0] & 0xf0) == 0xf0;
                if (fpumode) {
-                       initfpu();
+                       uae_u32 v = 0;
+                       if (fpu_max_precision == 2) {
+                               v = 2 << 6;
+                       } else if (fpu_max_precision == 1) {
+                               v = 1 << 6;
+                       }
+                       initfpu(v);
                }
                copyregs(&last_regs, &cur_regs, fpumode);
 
@@ -3667,6 +3675,7 @@ static int test_mnemo(const char *opcode)
        v = read_u32(headerfile, &headoffset);
        initial_interrupt_mask = v & 7;
        initial_interrupt = (v >> 3) & 7;
+       fpu_max_precision = (v >> 6) & 3;
        v = read_u32(headerfile, &headoffset);
        v = read_u32(headerfile, &headoffset);
        fpu_model = read_u32(headerfile, &headoffset);
@@ -3959,8 +3968,9 @@ int main(int argc, char *argv[])
                printf("-askifmissing = ask for new path if dat file is missing.\n");
                printf("-exit n = exit after n tests.\n");
                printf("-exitok n = exit after n tests, continue normally.\n");
-               printf("-fpuadj <exp> 16-bit exponent range value. (16383 = 1.0)\n");
+               printf("-fpuadj <exp> 16-bit exponent range value. (0 = 1.0)\n");
                printf("-fpsrmask = ignore FPSR bits that are not set.\n");
+               printf("-nofpiar = ignore FPIAR.\n");
                printf("-cycles [range adjust] = check cycle counts.\n");
                printf("-cyclecnt <address>. Use custom hardware cycle counter.\n");
 #ifdef AMIGA
@@ -3975,6 +3985,7 @@ int main(int argc, char *argv[])
        check_undefined_sr = 1;
        ccr_mask = 0xff;
        fpsr_ignore_mask = 0xffffffff;
+       fpiar_ignore = 0;
        disasm = 1;
        exitcnt2 = -1;
        exitmode = 0;
@@ -4002,11 +4013,13 @@ int main(int argc, char *argv[])
                                i++;
                        }
                } else if (!_stricmp(s, "-fpsrmask")) {
-                       fpsr_ignore_mask = 0;
+                       fpsr_ignore_mask = (1 << 27) | (1 << 26);
                        if (next) {
                                fpsr_ignore_mask = ~getparamval(next);
                                i++;
                        }
+               } else if (!_stricmp(s, "-nofpiar")) {
+                       fpiar_ignore = 1;
                } else if (!_stricmp(s, "-silent")) {
                        dooutput = 0;
                } else if (!_stricmp(s, "-68000")) {
@@ -4057,13 +4070,12 @@ int main(int argc, char *argv[])
                } else if (!_stricmp(s, "-prealloc")) {
                        prealloc = 1;
                } else if (!_stricmp(s, "-fpuadj")) {
+                       fpu_adjust_exp = 0;
                        if (next) {
                                fpu_adjust_exp = atol(next);
-                               if (fpu_adjust_exp >= 0) {
-                                       is_fpu_adjust = 1;
-                               }
-
                        }
+                       is_fpu_adjust = 1;
+                       fpu_adjust_exp += 16384;
                } else if (!_stricmp(s, "-cycles")) {
                        cycles = 1;
                        if (i + 1 < argc && argv[i][0] != '-') {