fpu test max precision/double mode

This commit is contained in:
Toni Wilen 2023-09-16 20:31:48 +03:00
parent d2838c08e1
commit e58f45d5d4
3 changed files with 231 additions and 85 deletions

View 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,14 +1959,16 @@ static bool fpuregchange(int reg, fpdata *regs)
}
}
for(;;) {
switch(reg)
{
case 0: // positive
add = floatx80_div(int32_to_floatx80(1), int32_to_floatx80(10), &fpustatus);
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(10), &fpustatus);
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
@ -1908,34 +1976,40 @@ static bool fpuregchange(int reg, fpdata *regs)
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
v = packFloatx80(1, 0x700 + rand8(), 0x8000000000000000 | (((uae_u64)rand32()) << 32));
{
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 = 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;
break;
}
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) {
@ -1946,11 +2020,16 @@ static bool fpuregchange(int reg, fpdata *regs)
} else if ((regcnt & 3) == 3) {
v = packFloatx80(1, 0x7FFF, floatx80_default_infinity_low);
}
}
break;
}
if (fpu_precision_valid(v)) {
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();
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();
}
// fill extra zeros
for (int i = 0; i < size; i++) {
if (frand8() < 0x70)
p[i] = 0x00;
} 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) {
if (!fpu_precision_valid(regs.fp[i].fpx)) {
test_exception = -1;
break;
}
}
}
}
@ -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;
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);
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);

View 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

View 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;
}
}
fpu_adjust_exp += 16384;
} else if (!_stricmp(s, "-cycles")) {
cycles = 1;
if (i + 1 < argc && argv[i][0] != '-') {