X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=m68kfpu.c;h=68c3f6f1614702554c5ea3ba240faa20d6517581;hb=5043664ac4009a607cbf7153710522b824266463;hp=473e07859bd29184b822fb6a029ec72fbc725fd2;hpb=e2f86a9c1a5c7a039831289f730a01eb7140402b;p=pistorm diff --git a/m68kfpu.c b/m68kfpu.c index 473e078..68c3f6f 100644 --- a/m68kfpu.c +++ b/m68kfpu.c @@ -1,7 +1,11 @@ +// SPDX-License-Identifier: MIT #include #include #include +#include "softfloat/softfloat.h" +float_status status; + extern void exit(int); static void fatalerror(char *format, ...) { @@ -17,11 +21,25 @@ static void fatalerror(char *format, ...) { #define FPCC_I 0x02000000 #define FPCC_NAN 0x01000000 +#define FPES_OE 0x00002000 +#define FPAE_IOP 0x00000080 + #define DOUBLE_INFINITY (unsigned long long)(0x7ff0000000000000) #define DOUBLE_EXPONENT (unsigned long long)(0x7ff0000000000000) #define DOUBLE_MANTISSA (unsigned long long)(0x000fffffffffffff) -extern flag floatx80_is_nan( floatx80 a ); +/*---------------------------------------------------------------------------- +| Returns 1 if the extended double-precision floating-point value `a' is a +| NaN; otherwise returns 0. +*----------------------------------------------------------------------------*/ + +flag floatx80_is_nan( floatx80 a ) +{ + + return ( ( a.high & 0x7FFF ) == 0x7FFF ) && (uint64_t) ( a.low<<1 ); + +} + // masks for packed dwords, positive k-factor static uint32 pkmask2[18] = @@ -46,7 +64,7 @@ static inline double fx80_to_double(floatx80 fx) foo = (double *)&d; - d = floatx80_to_float64(fx); + d = floatx80_to_float64(fx, &status); return *foo; } @@ -57,7 +75,7 @@ static inline floatx80 double_to_fx80(double in) d = (uint64 *)∈ - return float64_to_floatx80(*d); + return float64_to_floatx80(*d, &status); } static inline floatx80 load_extended_float80(uint32 ea) @@ -269,6 +287,10 @@ static inline void store_pack_float80(uint32 ea, int k, floatx80 fpr) static inline void SET_CONDITION_CODES(floatx80 reg) { +// u64 *regi; + +// regi = (u64 *)® + REG_FPSR &= ~(FPCC_N|FPCC_Z|FPCC_I|FPCC_NAN); // sign flag @@ -374,6 +396,16 @@ static uint8 READ_EA_8(int ea) uint32 ea = REG_A[reg]; return m68ki_read_8(ea); } + case 3: // (An)+ + { + uint32 ea = EA_AY_PI_8(); + return m68ki_read_8(ea); + } + case 4: // -(An) + { + uint32 ea = EA_AY_PD_8(); + return m68ki_read_8(ea); + } case 5: // (d16, An) { uint32 ea = EA_AY_DI_8(); @@ -400,6 +432,16 @@ static uint8 READ_EA_8(int ea) uint32 ea = (d1 << 16) | d2; return m68ki_read_8(ea); } + case 2: // (d16, PC) + { + uint32 ea = EA_PCDI_8(); + return m68ki_read_8(ea); + } + case 3: // (PC) + (Xn) + d8 + { + uint32 ea = EA_PCIX_8(); + return m68ki_read_8(ea); + } case 4: // # { return OPER_I_8(); @@ -430,6 +472,16 @@ static uint16 READ_EA_16(int ea) uint32 ea = REG_A[reg]; return m68ki_read_16(ea); } + case 3: // (An)+ + { + uint32 ea = EA_AY_PI_16(); + return m68ki_read_16(ea); + } + case 4: // -(An) + { + uint32 ea = EA_AY_PD_16(); + return m68ki_read_16(ea); + } case 5: // (d16, An) { uint32 ea = EA_AY_DI_16(); @@ -456,6 +508,16 @@ static uint16 READ_EA_16(int ea) uint32 ea = (d1 << 16) | d2; return m68ki_read_16(ea); } + case 2: // (d16, PC) + { + uint32 ea = EA_PCDI_16(); + return m68ki_read_16(ea); + } + case 3: // (PC) + (Xn) + d8 + { + uint32 ea = EA_PCIX_16(); + return m68ki_read_16(ea); + } case 4: // # { return OPER_I_16(); @@ -492,6 +554,11 @@ static uint32 READ_EA_32(int ea) uint32 ea = EA_AY_PI_32(); return m68ki_read_32(ea); } + case 4: // -(An) + { + uint32 ea = EA_AY_PD_32(); + return m68ki_read_32(ea); + } case 5: // (d16, An) { uint32 ea = EA_AY_DI_32(); @@ -523,6 +590,11 @@ static uint32 READ_EA_32(int ea) uint32 ea = EA_PCDI_32(); return m68ki_read_32(ea); } + case 3: // (PC) + (Xn) + d8 + { + uint32 ea = EA_PCIX_32(); + return m68ki_read_32(ea); + } case 4: // # { return OPER_I_32(); @@ -559,6 +631,14 @@ static uint64 READ_EA_64(int ea) h2 = m68ki_read_32(ea+4); return (uint64)(h1) << 32 | (uint64)(h2); } + case 4: // -(An) + { + uint32 ea = REG_A[reg]-8; + REG_A[reg] -= 8; + h1 = m68ki_read_32(ea+0); + h2 = m68ki_read_32(ea+4); + return (uint64)(h1) << 32 | (uint64)(h2); + } case 5: // (d16, An) { uint32 ea = EA_AY_DI_32(); @@ -566,10 +646,31 @@ static uint64 READ_EA_64(int ea) h2 = m68ki_read_32(ea+4); return (uint64)(h1) << 32 | (uint64)(h2); } + case 6: // (An) + (Xn) + d8 + { + uint32 ea = EA_AY_IX_32(); + h1 = m68ki_read_32(ea+0); + h2 = m68ki_read_32(ea+4); + return (uint64)(h1) << 32 | (uint64)(h2); + } case 7: { switch (reg) { + case 1: // (xxx).L + { + uint32 d1 = OPER_I_16(); + uint32 d2 = OPER_I_16(); + uint32 ea = (d1 << 16) | d2; + return (uint64)(m68ki_read_32(ea)) << 32 | (uint64)(m68ki_read_32(ea+4)); + } + case 3: // (PC) + (Xn) + d8 + { + uint32 ea = EA_PCIX_32(); + h1 = m68ki_read_32(ea+0); + h2 = m68ki_read_32(ea+4); + return (uint64)(h1) << 32 | (uint64)(h2); + } case 4: // # { h1 = OPER_I_32(); @@ -594,9 +695,11 @@ static uint64 READ_EA_64(int ea) } -static floatx80 READ_EA_FPE(int mode, int reg, uint32 di_mode_ea) +static floatx80 READ_EA_FPE(uint32 ea) { floatx80 fpr; + int mode = (ea >> 3) & 0x7; + int reg = (ea & 0x7); switch (mode) { @@ -614,16 +717,41 @@ static floatx80 READ_EA_FPE(int mode, int reg, uint32 di_mode_ea) fpr = load_extended_float80(ea); break; } - case 5: // (d16, An) (added by JFF) + case 4: // -(An) { - fpr = load_extended_float80(di_mode_ea); + uint32 ea = REG_A[reg]-12; + REG_A[reg] -= 12; + fpr = load_extended_float80(ea); + break; + } + case 5: // (d16, An) + { + // FIXME: will fail for fmovem + uint32 ea = EA_AY_DI_32(); + fpr = load_extended_float80(ea); break; - } + case 6: // (An) + (Xn) + d8 + { + // FIXME: will fail for fmovem + uint32 ea = EA_AY_IX_32(); + fpr = load_extended_float80(ea); + break; + } + case 7: // extended modes { switch (reg) { + case 1: // (xxx) + { + uint32 d1 = OPER_I_16(); + uint32 d2 = OPER_I_16(); + uint32 ea = (d1 << 16) | d2; + fpr = load_extended_float80(ea); + } + break; + case 2: // (d16, PC) { uint32 ea = EA_PCDI_32(); @@ -637,13 +765,15 @@ static floatx80 READ_EA_FPE(int mode, int reg, uint32 di_mode_ea) fpr = load_extended_float80(ea); } break; - case 4: // immediate (JFF) - { - uint32 ea = REG_PC; - fpr = load_extended_float80(ea); - REG_PC += 12; - } - break; + + case 4: // immediate (JFF) + { + uint32 ea = REG_PC; + fpr = load_extended_float80(ea); + REG_PC += 12; + } + break; + default: fatalerror("M68kFPU: READ_EA_FPE: unhandled mode %d, reg %d, at %08X\n", mode, reg, REG_PC); break; @@ -891,6 +1021,12 @@ static void WRITE_EA_32(int ea, uint32 data) { switch (reg) { + case 0: // (xxx).W + { + uint32 ea = OPER_I_16(); + m68ki_write_32(ea, data); + break; + } case 1: // (xxx).L { uint32 d1 = OPER_I_16(); @@ -927,6 +1063,14 @@ static void WRITE_EA_64(int ea, uint64 data) m68ki_write_32(ea+4, (uint32)(data)); break; } + case 3: // (An)+ + { + uint32 ea = REG_A[reg]; + REG_A[reg] += 8; + m68ki_write_32(ea+0, (uint32)(data >> 32)); + m68ki_write_32(ea+4, (uint32)(data)); + break; + } case 4: // -(An) { uint32 ea; @@ -943,13 +1087,45 @@ static void WRITE_EA_64(int ea, uint64 data) m68ki_write_32(ea+4, (uint32)(data)); break; } + case 6: // (An) + (Xn) + d8 + { + uint32 ea = EA_AY_IX_32(); + m68ki_write_32(ea+0, (uint32)(data >> 32)); + m68ki_write_32(ea+4, (uint32)(data)); + break; + } + case 7: + { + switch (reg) + { + case 1: // (xxx).L + { + uint32 d1 = OPER_I_16(); + uint32 d2 = OPER_I_16(); + uint32 ea = (d1 << 16) | d2; + m68ki_write_32(ea+0, (uint32)(data >> 32)); + m68ki_write_32(ea+4, (uint32)(data)); + break; + } + case 2: // (d16, PC) + { + uint32 ea = EA_PCDI_32(); + m68ki_write_32(ea+0, (uint32)(data >> 32)); + m68ki_write_32(ea+4, (uint32)(data)); + break; + } + default: fatalerror("M68kFPU: WRITE_EA_64: unhandled mode %d, reg %d at %08X\n", mode, reg, REG_PC); + } + break; + } default: fatalerror("M68kFPU: WRITE_EA_64: unhandled mode %d, reg %d, data %08X%08X at %08X\n", mode, reg, (uint32)(data >> 32), (uint32)(data), REG_PC); } } -static void WRITE_EA_FPE(int mode, int reg, floatx80 fpr, uint32 di_mode_ea) +static void WRITE_EA_FPE(uint32 ea, floatx80 fpr) { - + int mode = (ea >> 3) & 0x7; + int reg = (ea & 0x7); switch (mode) { @@ -978,12 +1154,10 @@ static void WRITE_EA_FPE(int mode, int reg, floatx80 fpr, uint32 di_mode_ea) store_extended_float80(ea, fpr); break; } - case 5: // (d16, An) (added by JFF) + case 5: // (d16, An) { - // EA_AY_DI_32() should not be done here because fmovem would increase - // PC each time, reading incorrect displacement & advancing PC too much - // uint32 ea = EA_AY_DI_32(); - store_extended_float80(di_mode_ea, fpr); + uint32 ea = EA_AY_DI_32(); + store_extended_float80(ea, fpr); break; } @@ -1069,16 +1243,13 @@ static void fpgen_rm_reg(uint16 w2) case 1: // Single-precision Real { uint32 d = READ_EA_32(ea); - source = float32_to_floatx80(d); + source = float32_to_floatx80(d, &status); break; } case 2: // Extended-precision Real { - int imode = (ea >> 3) & 0x7; - int reg = (ea & 0x7); - uint32 di_mode_ea = imode == 5 ? (REG_A[reg]+MAKE_INT_16(m68ki_read_imm_16())) : 0; - source = READ_EA_FPE(imode,reg,di_mode_ea); - break; + source = READ_EA_FPE(ea); + break; } case 3: // Packed-decimal Real { @@ -1095,7 +1266,7 @@ static void fpgen_rm_reg(uint16 w2) { uint64 d = READ_EA_64(ea); - source = float64_to_floatx80(d); + source = float64_to_floatx80(d, &status); break; } case 6: // Byte Integer @@ -1158,6 +1329,59 @@ static void fpgen_rm_reg(uint16 w2) case 0x34: // 10^2 source = int32_to_floatx80((sint32)10*10); break; + case 0x35: // 10^4 + source = int32_to_floatx80((sint32)1000*10); + break; + + case 0x36: // 1.0e8 + source = int32_to_floatx80((sint32)10000000*10); + break; + + case 0x37: // 1.0e16 - can't get the right precision from s32 so go "direct" with constants from h/w + source.high = 0x4034; + source.low = U64(0x8e1bc9bf04000000); + break; + + case 0x38: // 1.0e32 + source.high = 0x4069; + source.low = U64(0x9dc5ada82b70b59e); + break; + + case 0x39: // 1.0e64 + source.high = 0x40d3; + source.low = U64(0xc2781f49ffcfa6d5); + break; + + case 0x3a: // 1.0e128 + source.high = 0x41a8; + source.low = U64(0x93ba47c980e98ce0); + break; + + case 0x3b: // 1.0e256 + source.high = 0x4351; + source.low = U64(0xaa7eebfb9df9de8e); + break; + + case 0x3c: // 1.0e512 + source.high = 0x46a3; + source.low = U64(0xe319a0aea60e91c7); + break; + + case 0x3d: // 1.0e1024 + source.high = 0x4d48; + source.low = U64(0xc976758681750c17); + break; + + case 0x3e: // 1.0e2048 + source.high = 0x5a92; + source.low = U64(0x9e8b3b5dc53d5de5); + break; + + case 0x3f: // 1.0e4096 + source.high = 0x7525; + source.low = U64(0xc46052028a20979b); + break; + default: fatalerror("fmove_rm_reg: unknown constant ROM offset %x at %08x\n", w2&0x7f, REG_PC-4); @@ -1166,7 +1390,8 @@ static void fpgen_rm_reg(uint16 w2) // handle it right here, the usual opmode bits aren't valid in the FMOVECR case REG_FP[dst] = source; - SET_CONDITION_CODES(REG_FP[dst]); // JFF when destination is a register, we HAVE to update FPCR + //FIXME mame doesn't use SET_CONDITION_CODES here + SET_CONDITION_CODES(REG_FP[dst]); // JFF when destination is a register, we HAVE to update FPCR USE_CYCLES(4); return; } @@ -1178,40 +1403,160 @@ static void fpgen_rm_reg(uint16 w2) source = REG_FP[src]; } - + // For FD* and FS* prefixes we already converted the source to floatx80 + // so we can treat these as their parent op. switch (opmode) { + case 0x44: // FDMOVE + case 0x40: // FSMOVE case 0x00: // FMOVE { REG_FP[dst] = source; - SET_CONDITION_CODES(REG_FP[dst]); // JFF needs update condition codes + SET_CONDITION_CODES(REG_FP[dst]); USE_CYCLES(4); break; } - case 0x01: // Fsint + case 0x01: // FINT { sint32 temp; - temp = floatx80_to_int32(source); + temp = floatx80_to_int32(source, &status); REG_FP[dst] = int32_to_floatx80(temp); - SET_CONDITION_CODES(REG_FP[dst]); // JFF needs update condition codes + //FIXME mame doesn't use SET_CONDITION_CODES here + SET_CONDITION_CODES(REG_FP[dst]); // JFF needs update condition codes + USE_CYCLES(4); break; } - case 0x03: // FsintRZ + case 0x02: // FSINH + { + REG_FP[dst] = floatx80_sinh(source, &status); + SET_CONDITION_CODES(REG_FP[dst]); + USE_CYCLES(75); + break; + } + case 0x03: // FINTRZ { sint32 temp; - temp = floatx80_to_int32_round_to_zero(source); + temp = floatx80_to_int32_round_to_zero(source, &status); REG_FP[dst] = int32_to_floatx80(temp); + //FIXME mame doesn't use SET_CONDITION_CODES here SET_CONDITION_CODES(REG_FP[dst]); // JFF needs update condition codes break; } + case 0x45: // FDSQRT + case 0x41: // FSSQRT case 0x04: // FSQRT + case 0x05: // FSQRT { - REG_FP[dst] = floatx80_sqrt(source); + REG_FP[dst] = floatx80_sqrt(source, &status); SET_CONDITION_CODES(REG_FP[dst]); USE_CYCLES(109); break; } + case 0x06: // FLOGNP1 + case 0x07: // FLOGNP1 + { + REG_FP[dst] = floatx80_lognp1 (source, &status); + SET_CONDITION_CODES(REG_FP[dst]); + USE_CYCLES(594); // for MC68881 + break; + } + case 0x08: // FETOXM1 + { + REG_FP[dst] = floatx80_etoxm1(source, &status); + SET_CONDITION_CODES(REG_FP[dst]); + USE_CYCLES(6); + break; + } + case 0x09: // FTANH + { + REG_FP[dst] = floatx80_tanh(source, &status); + SET_CONDITION_CODES(REG_FP[dst]); + USE_CYCLES(75); + break; + } + case 0x0a: // FATAN + case 0x0b: // FATAN + { + REG_FP[dst] = floatx80_atan(source, &status); + SET_CONDITION_CODES(REG_FP[dst]); + USE_CYCLES(75); + break; + } + case 0x0c: // FASIN + { + REG_FP[dst] = floatx80_asin(source, &status); + SET_CONDITION_CODES(REG_FP[dst]); + USE_CYCLES(75); + break; + } + case 0x0d: // FATANH + { + REG_FP[dst] = floatx80_atanh(source, &status); + SET_CONDITION_CODES(REG_FP[dst]); + USE_CYCLES(75); + break; + } + case 0x0e: // FSIN + { + REG_FP[dst] = floatx80_sin(source, &status); + SET_CONDITION_CODES(REG_FP[dst]); + USE_CYCLES(75); + break; + } + case 0x0f: // FTAN + { + REG_FP[dst] = floatx80_tan(source, &status); + SET_CONDITION_CODES(REG_FP[dst]); + USE_CYCLES(75); + break; + } + case 0x10: // FETOX + { + REG_FP[dst] = floatx80_etox(source, &status); + SET_CONDITION_CODES(REG_FP[dst]); + USE_CYCLES(75); + break; + } + case 0x11: // FTWOTOX + { + REG_FP[dst] = floatx80_twotox(source, &status); + SET_CONDITION_CODES(REG_FP[dst]); + USE_CYCLES(75); + break; + } + case 0x12: // FTENTOX + case 0x13: // FTENTOX + { + REG_FP[dst] = floatx80_tentox(source, &status); + SET_CONDITION_CODES(REG_FP[dst]); + USE_CYCLES(75); + break; + } + case 0x14: // FLOGN + { + REG_FP[dst] = floatx80_logn(source, &status); + SET_CONDITION_CODES(REG_FP[dst]); + USE_CYCLES(548); // for MC68881 + break; + } + case 0x15: // FLOG10 + { + REG_FP[dst] = floatx80_log10(source, &status); + SET_CONDITION_CODES(REG_FP[dst]); + USE_CYCLES(604); // for MC68881 + break; + } + case 0x16: // FLOG2 + case 0x17: // FLOG2 + { + REG_FP[dst] = floatx80_log2(source, &status); + SET_CONDITION_CODES(REG_FP[dst]); + USE_CYCLES(604); // for MC68881 + break; + } + case 0x5C: // FDABS + case 0x58: // FSABS case 0x18: // FABS { REG_FP[dst] = source; @@ -1220,7 +1565,17 @@ static void fpgen_rm_reg(uint16 w2) USE_CYCLES(3); break; } + case 0x19: // FCOSH + { + REG_FP[dst] = floatx80_cosh(source, &status); + SET_CONDITION_CODES(REG_FP[dst]); + USE_CYCLES(64); + break; + } + case 0x5e: // FDNEG + case 0x5a: // FSNEG case 0x1a: // FNEG + case 0x1b: // FNEG { REG_FP[dst] = source; REG_FP[dst].high ^= 0x8000; @@ -1228,62 +1583,154 @@ static void fpgen_rm_reg(uint16 w2) USE_CYCLES(3); break; } + case 0x1c: // FACOS + { + REG_FP[dst] = floatx80_acos(source, &status); + SET_CONDITION_CODES(REG_FP[dst]); + USE_CYCLES(604); // for MC68881 + break; + break; + } + case 0x1d: // FCOS + { + REG_FP[dst] = floatx80_cos(source, &status); + SET_CONDITION_CODES(REG_FP[dst]); + USE_CYCLES(75); + break; + } case 0x1e: // FGETEXP { - sint16 temp; - temp = source.high; // get the exponent - temp -= 0x3fff; // take off the bias - REG_FP[dst] = double_to_fx80((double)temp); + REG_FP[dst] = floatx80_getexp(source, &status); SET_CONDITION_CODES(REG_FP[dst]); USE_CYCLES(6); break; } - case 0x60: // FSDIVS (JFF) (source has already been converted to floatx80) + case 0x1f: // FGETMAN + { + REG_FP[dst] = floatx80_getman(source, &status); + SET_CONDITION_CODES(REG_FP[dst]); + USE_CYCLES(6); + break; + } + case 0x64: // FDDIV + case 0x60: // FSDIV case 0x20: // FDIV { - REG_FP[dst] = floatx80_div(REG_FP[dst], source); - SET_CONDITION_CODES(REG_FP[dst]); // JFF + REG_FP[dst] = floatx80_div(REG_FP[dst], source, &status); + //FIXME mame doesn't use SET_CONDITION_CODES here + SET_CONDITION_CODES(REG_FP[dst]); // JFF USE_CYCLES(43); break; } + case 0x21: // FMOD + { + sint8 const mode = status.float_rounding_mode; + status.float_rounding_mode = float_round_to_zero; + uint64_t q; + flag s; + REG_FP[dst] = floatx80_rem(REG_FP[dst], source, &q, &s, &status); + SET_CONDITION_CODES(REG_FP[dst]); + status.float_rounding_mode = mode; + USE_CYCLES(43); // guess + break; + } + case 0x66: // FDADD + case 0x62: // FSADD case 0x22: // FADD { - REG_FP[dst] = floatx80_add(REG_FP[dst], source); + REG_FP[dst] = floatx80_add(REG_FP[dst], source, &status); SET_CONDITION_CODES(REG_FP[dst]); USE_CYCLES(9); break; } - case 0x63: // FSMULS (JFF) (source has already been converted to floatx80) + case 0x67: // FDMUL + case 0x63: // FSMUL case 0x23: // FMUL { - REG_FP[dst] = floatx80_mul(REG_FP[dst], source); + REG_FP[dst] = floatx80_mul(REG_FP[dst], source, &status); SET_CONDITION_CODES(REG_FP[dst]); USE_CYCLES(11); break; } + case 0x24: // FSGLDIV + { + REG_FP[dst] = floatx80_sgldiv(REG_FP[dst], source, &status); + USE_CYCLES(43); // // ? (value is from FDIV) + break; + } case 0x25: // FREM { - REG_FP[dst] = floatx80_rem(REG_FP[dst], source); + sint8 const mode = status.float_rounding_mode; + status.float_rounding_mode = float_round_nearest_even; + uint64_t q; + flag s; + REG_FP[dst] = floatx80_rem(REG_FP[dst], source, &q, &s, &status); SET_CONDITION_CODES(REG_FP[dst]); + status.float_rounding_mode = mode; USE_CYCLES(43); // guess break; } - case 0x28: // FSUB + case 0x26: // FSCALE { - REG_FP[dst] = floatx80_sub(REG_FP[dst], source); + REG_FP[dst] = floatx80_scale(REG_FP[dst], source, &status); + SET_CONDITION_CODES(REG_FP[dst]); + USE_CYCLES(46); // (better?) guess + break; + } + case 0x27: // FSGLMUL + { + REG_FP[dst] = floatx80_sglmul(REG_FP[dst], source, &status); + SET_CONDITION_CODES(REG_FP[dst]); + USE_CYCLES(11); // ? (value is from FMUL) + break; + } + case 0x6c: // FDSUB + case 0x68: // FSSUB + case 0x28: // FSUB + case 0x29: // FSUB + case 0x2a: // FSUB + case 0x2b: // FSUB + case 0x2c: // FSUB + case 0x2d: // FSUB + case 0x2e: // FSUB + case 0x2f: // FSUB + { + REG_FP[dst] = floatx80_sub(REG_FP[dst], source, &status); SET_CONDITION_CODES(REG_FP[dst]); USE_CYCLES(9); break; } + case 0x30: // FSINCOS + case 0x31: // FSINCOS + case 0x32: // FSINCOS + case 0x33: // FSINCOS + case 0x34: // FSINCOS + case 0x35: // FSINCOS + case 0x36: // FSINCOS + case 0x37: // FSINCOS + { + REG_FP[dst] = floatx80_cos(source, &status); + REG_FP[w2&7] = floatx80_sin(source, &status); + SET_CONDITION_CODES(REG_FP[dst]); + USE_CYCLES(75); + + break; + } case 0x38: // FCMP + case 0x39: // FCMP + case 0x3c: // FCMP + case 0x3d: // FCMP { floatx80 res; - res = floatx80_sub(REG_FP[dst], source); + res = floatx80_sub(REG_FP[dst], source, &status); SET_CONDITION_CODES(res); USE_CYCLES(7); break; } case 0x3a: // FTST + case 0x3b: // FTST + case 0x3e: // FTST + case 0x3f: // FTST { floatx80 res; res = source; @@ -1307,22 +1754,19 @@ static void fmove_reg_mem(uint16 w2) { case 0: // Long-Word Integer { - sint32 d = (sint32)floatx80_to_int32(REG_FP[src]); + sint32 d = (sint32)floatx80_to_int32(REG_FP[src], &status); WRITE_EA_32(ea, d); break; } case 1: // Single-precision Real { - uint32 d = floatx80_to_float32(REG_FP[src]); + uint32 d = floatx80_to_float32(REG_FP[src], &status); WRITE_EA_32(ea, d); break; } case 2: // Extended-precision Real { - int mode = (ea >> 3) & 0x7; - int reg = (ea & 0x7); - uint32 di_mode_ea = mode == 5 ? (REG_A[reg]+MAKE_INT_16(m68ki_read_imm_16())) : 0; - WRITE_EA_FPE(mode, reg, REG_FP[src], di_mode_ea); + WRITE_EA_FPE(ea, REG_FP[src]); break; } case 3: // Packed-decimal Real with Static K-factor @@ -1334,21 +1778,31 @@ static void fmove_reg_mem(uint16 w2) } case 4: // Word Integer { - WRITE_EA_16(ea, (sint16)floatx80_to_int32(REG_FP[src])); + sint32 value = floatx80_to_int32(REG_FP[src], &status); + if (value > 0x7fff || value < -0x8000 ) + { + REG_FPSR |= FPES_OE | FPAE_IOP; + } + WRITE_EA_16(ea, (sint16)value); break; } case 5: // Double-precision Real { uint64 d; - d = floatx80_to_float64(REG_FP[src]); + d = floatx80_to_float64(REG_FP[src], &status); WRITE_EA_64(ea, d); break; } case 6: // Byte Integer { - WRITE_EA_8(ea, (sint8)floatx80_to_int32(REG_FP[src])); + sint32 value = floatx80_to_int32(REG_FP[src], &status); + if (value > 127 || value < -128) + { + REG_FPSR |= FPES_OE | FPAE_IOP; + } + WRITE_EA_8(ea, (sint8)value); break; } case 7: // Packed-decimal Real with Dynamic K-factor @@ -1365,24 +1819,97 @@ static void fmove_fpcr(uint16 w2) { int ea = REG_IR & 0x3f; int dir = (w2 >> 13) & 0x1; - int reg = (w2 >> 10) & 0x7; + int regsel = (w2 >> 10) & 0x7; + int mode = (ea >> 3) & 0x7; + + if ((mode == 5) || (mode == 6)) + { + uint32 address = 0xffffffff; // force a bus error if this doesn't get assigned - if (dir) // From system control reg to + if (mode == 5) + { + address = EA_AY_DI_32(); + } + else if (mode == 6) + { + address = EA_AY_IX_32(); + } + + if (dir) // From system control reg to + { + if (regsel & 4) { m68ki_write_32(address, REG_FPCR); address += 4; } + if (regsel & 2) { m68ki_write_32(address, REG_FPSR); address += 4; } + if (regsel & 1) { m68ki_write_32(address, REG_FPIAR); address += 4; } + } + else // From to system control reg + { + if (regsel & 4) { REG_FPCR = m68ki_read_32(address); address += 4; } + if (regsel & 2) { REG_FPSR = m68ki_read_32(address); address += 4; } + if (regsel & 1) { REG_FPIAR = m68ki_read_32(address); address += 4; } + } + } + else { - if (reg & 4) WRITE_EA_32(ea, REG_FPCR); - if (reg & 2) WRITE_EA_32(ea, REG_FPSR); - if (reg & 1) WRITE_EA_32(ea, REG_FPIAR); + if (dir) // From system control reg to + { + if (regsel & 4) WRITE_EA_32(ea, REG_FPCR); + if (regsel & 2) WRITE_EA_32(ea, REG_FPSR); + if (regsel & 1) WRITE_EA_32(ea, REG_FPIAR); + } + else // From to system control reg + { + if (regsel & 4) REG_FPCR = READ_EA_32(ea); + if (regsel & 2) REG_FPSR = READ_EA_32(ea); + if (regsel & 1) REG_FPIAR = READ_EA_32(ea); + } } - else // From to system control reg + + // FIXME: (2011-12-18 ost) + // rounding_mode and rounding_precision of softfloat.c should be set according to current fpcr + // but: with this code on Apollo the following programs in /systest/fptest will fail: + // 1. Single Precision Whetstone will return wrong results never the less + // 2. Vector Test will fault with 00040004: reference to illegal address + + if ((regsel & 4) && dir == 0) { - if (reg & 4) + int rnd = (REG_FPCR >> 4) & 3; + int prec = (REG_FPCR >> 6) & 3; + +// logerror("m68k_fpsp:fmove_fpcr fpcr=%04x prec=%d rnd=%d\n", m_fpcr, prec, rnd); + +#ifdef FLOATX80 + switch (prec) { - REG_FPCR = READ_EA_32(ea); - // JFF: need to update rounding mode from softfloat module - float_rounding_mode = (REG_FPCR >> 4) & 0x3; + case 0: // Extend (X) + status.floatx80_rounding_precision = 80; + break; + case 1: // Single (S) + status.floatx80_rounding_precision = 32; + break; + case 2: // Double (D) + status.floatx80_rounding_precision = 64; + break; + case 3: // Undefined + status.floatx80_rounding_precision = 80; + break; + } +#endif + + switch (rnd) + { + case 0: // To Nearest (RN) + status.float_rounding_mode = float_round_nearest_even; + break; + case 1: // To Zero (RZ) + status.float_rounding_mode = float_round_to_zero; + break; + case 2: // To Minus Infinitiy (RM) + status.float_rounding_mode = float_round_down; + break; + case 3: // To Plus Infinitiy (RP) + status.float_rounding_mode = float_round_up; + break; } - if (reg & 2) REG_FPSR = READ_EA_32(ea); - if (reg & 1) REG_FPIAR = READ_EA_32(ea); } USE_CYCLES(10); @@ -1396,122 +1923,121 @@ static void fmovem(uint16 w2) int mode = (w2 >> 11) & 0x3; int reglist = w2 & 0xff; + uint32 mem_addr = 0; + switch (ea >> 3) + { + case 5: // (d16, An) + mem_addr= EA_AY_DI_32(); + break; + case 6: // (An) + (Xn) + d8 + mem_addr= EA_AY_IX_32(); + break; + } + if (dir) // From FP regs to mem { switch (mode) - { - case 2: // (JFF): Static register list, postincrement or control addressing mode. - { - int imode = (ea >> 3) & 0x7; - int reg = (ea & 0x7); - int di_mode = imode == 5; - uint32 di_mode_ea = di_mode ? (REG_A[reg]+MAKE_INT_16(m68ki_read_imm_16())) : 0; - for (i=0; i < 8; i++) + { + case 1: // Dynamic register list, postincrement or control addressing mode. + // FIXME: not really tested, but seems to work + reglist = REG_D[(reglist >> 4) & 7]; + /* fall through */ + /* no break */ + case 0: // Static register list, predecrement or control addressing mode { - if (reglist & (1 << i)) - { - WRITE_EA_FPE(imode,reg, REG_FP[7-i],di_mode_ea); - USE_CYCLES(2); - if (di_mode) + for (i=0; i < 8; i++) { - di_mode_ea += 12; + if (reglist & (1 << i)) + { + switch (ea >> 3) + { + case 5: // (d16, An) + case 6: // (An) + (Xn) + d8 + store_extended_float80(mem_addr, REG_FP[i]); + mem_addr += 12; + break; + default: + WRITE_EA_FPE(ea, REG_FP[i]); + break; + } + + USE_CYCLES(2); + } } - } + break; } - break; - } - case 0: // Static register list, predecrement addressing mode + + case 2: // Static register list, postdecrement or control addressing mode. { - int imode = (ea >> 3) & 0x7; - int reg = (ea & 0x7); - // the "di_mode_ea" parameter kludge is required here else WRITE_EA_FPE would have - // to call EA_AY_DI_32() (that advances PC & reads displacement) each time - // when the proper behaviour is 1) read once, 2) increment ea for each matching register - // this forces to pre-read the mode (named "imode") so we can decide to read displacement, only once - int di_mode = imode == 5; - uint32 di_mode_ea = di_mode ? (REG_A[reg]+MAKE_INT_16(m68ki_read_imm_16())) : 0; for (i=0; i < 8; i++) { if (reglist & (1 << i)) { - WRITE_EA_FPE(imode,reg, REG_FP[i],di_mode_ea); - USE_CYCLES(2); - if (di_mode) + switch (ea >> 3) { - di_mode_ea += 12; + case 5: // (d16, An) + case 6: // (An) + (Xn) + d8 + store_extended_float80(mem_addr, REG_FP[7-i]); + mem_addr += 12; + break; + default: + WRITE_EA_FPE(ea, REG_FP[7-i]); + break; } + + USE_CYCLES(2); } } break; } - default: fatalerror("040fpu0: FMOVEM: mode %d unimplemented at %08X\n", mode, REG_PC-4); + default: fatalerror("M680x0: FMOVEM: mode %d unimplemented at %08X\n", mode, REG_PC-4); } } else // From mem to FP regs { switch (mode) { - case 2: // Static register list, postincrement addressing mode + case 3: // Dynamic register list, predecrement addressing mode. + // FIXME: not really tested, but seems to work + reglist = REG_D[(reglist >> 4) & 7]; + /* fall through */ + /* no break */ + case 2: // Static register list, postincrement or control addressing mode { - int imode = (ea >> 3) & 0x7; - int reg = (ea & 0x7); - int di_mode = imode == 5; - uint32 di_mode_ea = di_mode ? (REG_A[reg]+MAKE_INT_16(m68ki_read_imm_16())) : 0; for (i=0; i < 8; i++) { if (reglist & (1 << i)) { - REG_FP[7-i] = READ_EA_FPE(imode,reg,di_mode_ea); - USE_CYCLES(2); - if (di_mode) + switch (ea >> 3) { - di_mode_ea += 12; + case 5: // (d16, An) + case 6: // (An) + (Xn) + d8 + REG_FP[7-i] = load_extended_float80(mem_addr); + mem_addr += 12; + break; + default: + REG_FP[7-i] = READ_EA_FPE(ea); + break; } + USE_CYCLES(2); } } break; } - default: fatalerror("040fpu0: FMOVEM: mode %d unimplemented at %08X\n", mode, REG_PC-4); + default: fatalerror("M680x0: FMOVEM: mode %d unimplemented at %08X\n", mode, REG_PC-4); } } } static void fscc() { - // added by JFF, this seems to work properly now - int condition = OPER_I_16() & 0x3f; - - int cc = TEST_CONDITION(condition); - int mode = (REG_IR & 0x38) >> 3; - int v = (cc ? 0xff : 0x00); - - switch (mode) - { - case 0: // fscc Dx - { - // If the specified floating-point condition is true, sets the byte integer operand at - // the destination to TRUE (all ones); otherwise, sets the byte to FALSE (all zeros). - - REG_D[REG_IR & 7] = (REG_D[REG_IR & 7] & 0xFFFFFF00) | v; - break; - } - case 5: // (disp,Ax) - { - int reg = REG_IR & 7; - uint32 ea = REG_A[reg]+MAKE_INT_16(m68ki_read_imm_16()); - m68ki_write_8(ea,v); - break; - } - - default: - { - // unimplemented see fpu_uae.cpp around line 1300 - fatalerror("040fpu0: fscc: mode %d not implemented at %08X\n", mode, REG_PC-4); - } - } - USE_CYCLES(7); // JFF unsure of the number of cycles!! + int ea = REG_IR & 0x3f; + int condition = (sint16)(OPER_I_16()); + + WRITE_EA_8(ea, TEST_CONDITION(condition) ? 0xff : 0); + USE_CYCLES(7); // ??? } static void fbcc16(void) { @@ -1528,7 +2054,7 @@ static void fbcc16(void) } USE_CYCLES(7); - } +} static void fbcc32(void) { @@ -1591,28 +2117,51 @@ void m68040_fpu_op0() break; } - case 1: // FScc (JFF) + case 1: // FBcc disp16 { - fscc(); - break; + switch ((REG_IR >> 3) & 0x7) { + case 1: // FDBcc + // TODO: + printf("M68kFPU: unimplemented FDBcc main op %d with mode %d at %08X\n", (REG_IR >> 6) & 0x3, (REG_IR >> 3) & 0x7, REG_PC-4); + break; + default: // FScc (?) + fscc(); + return; + } + fatalerror("M68kFPU: unimplemented main op %d with mode %d at %08X\n", (REG_IR >> 6) & 0x3, (REG_IR >> 3) & 0x7, REG_PC-4); + break; } - case 2: // FBcc disp16 + case 2: // FBcc disp16 { fbcc16(); break; } - case 3: // FBcc disp32 + case 3: // FBcc disp32 { fbcc32(); break; } - default: fatalerror("M68kFPU: unimplemented main op %d at %08X\n", (m68ki_cpu.ir >> 6) & 0x3, REG_PC-4); + default: fatalerror("M68kFPU: unimplemented main op %d\n", (REG_IR >> 6) & 0x3); } } -static void perform_fsave(uint32 addr, int inc) +static int perform_fsave(uint32 addr, int inc) { + if(m68ki_cpu.cpu_type & CPU_TYPE_040) + { + if(inc) + { + m68ki_write_32(addr, 0x41000000); + return 4 -4; + } + else + { + m68ki_write_32(addr, 0x41000000); + return -4 +4; + } + } + if (inc) { // 68881 IDLE, version 0x1f @@ -1623,21 +2172,23 @@ static void perform_fsave(uint32 addr, int inc) m68ki_write_32(addr+16, 0); m68ki_write_32(addr+20, 0); m68ki_write_32(addr+24, 0x70000000); + return 7*4 -4; } else { - m68ki_write_32(addr, 0x70000000); - m68ki_write_32(addr-4, 0); - m68ki_write_32(addr-8, 0); - m68ki_write_32(addr-12, 0); - m68ki_write_32(addr-16, 0); - m68ki_write_32(addr-20, 0); - m68ki_write_32(addr-24, 0x1f180000); + m68ki_write_32(addr+4-4, 0x70000000); + m68ki_write_32(addr+4-8, 0); + m68ki_write_32(addr+4-12, 0); + m68ki_write_32(addr+4-16, 0); + m68ki_write_32(addr+4-20, 0); + m68ki_write_32(addr+4-24, 0); + m68ki_write_32(addr+4-28, 0x1f180000); + return -7*4 +4; } } // FRESTORE on a NULL frame reboots the FPU - all registers to NaN, the 3 status regs to 0 -static void do_frestore_null() +static void do_frestore_null(void) { int i; @@ -1655,12 +2206,64 @@ static void do_frestore_null() m68ki_cpu.fpu_just_reset = 1; } +void m68040_do_fsave(uint32 addr, int reg, int inc) +{ + if (m68ki_cpu.fpu_just_reset) + { + m68ki_write_32(addr, 0); + } + else + { + // we normally generate an IDLE frame + int delta = perform_fsave(addr, inc); + if(reg != -1) + REG_A[reg] += delta; + } +} + +void m68040_do_frestore(uint32 addr, int reg) +{ + uint32 temp = m68ki_read_32(addr); + // check for nullptr frame + if (temp & 0xff000000) + { + // we don't handle non-nullptr frames + m68ki_cpu.fpu_just_reset = 0; + + if (reg != -1) + { + uint8 m40 = !!(m68ki_cpu.cpu_type & CPU_TYPE_040); + // how about an IDLE frame? + if (!m40 && ((temp & 0x00ff0000) == 0x00180000)) + { + REG_A[reg] += 7*4-4; + } + else if (m40 && ((temp & 0xffff0000) == 0x41000000)) + { +// REG_A[reg] += 4; + } // check UNIMP + else if ((temp & 0x00ff0000) == 0x00380000) + { + REG_A[reg] += 14*4; + } // check BUSY + else if ((temp & 0x00ff0000) == 0x00b40000) + { + REG_A[reg] += 45*4; + } + } + } + else + { + do_frestore_null(); + } +} + void m68040_fpu_op1() { int ea = REG_IR & 0x3f; int mode = (ea >> 3) & 0x7; int reg = (ea & 0x7); - uint32 addr, temp; + uint32 addr; switch ((REG_IR >> 6) & 0x3) { @@ -1668,40 +2271,56 @@ void m68040_fpu_op1() { switch (mode) { - case 3: // (An)+ - addr = EA_AY_PI_32(); + case 2: // (An) + addr = REG_A[reg]; + m68040_do_fsave(addr, -1, 1); + break; - if (m68ki_cpu.fpu_just_reset) - { - m68ki_write_32(addr, 0); - } - else - { - // we normally generate an IDLE frame - REG_A[reg] += 6*4; - perform_fsave(addr, 1); - } + case 3: // (An)+ + addr = EA_AY_PI_32(); + printf("FSAVE mode %d, reg A%d=0x%08x\n",mode,reg,REG_A[reg]); + m68040_do_fsave(addr, -1, 1); // FIXME: -1 was reg break; case 4: // -(An) - addr = EA_AY_PD_32(); + addr = EA_AY_PD_32(); + m68040_do_fsave(addr, reg, 0); // FIXME: -1 was reg + break; + case 5: // (D16, An) + addr = EA_AY_DI_16(); + m68040_do_fsave(addr, -1, 1); + break; - if (m68ki_cpu.fpu_just_reset) - { - m68ki_write_32(addr, 0); - } - else + case 6: // (An) + (Xn) + d8 + addr = EA_AY_IX_16(); + m68040_do_fsave(addr, -1, 1); + break; + + case 7: // + { + switch (reg) { - // we normally generate an IDLE frame - REG_A[reg] -= 6*4; - perform_fsave(addr, 0); + case 1: // (abs32) + { + addr = EA_AL_32(); + m68040_do_fsave(addr, -1, 1); + break; + } + case 2: // (d16, PC) + { + addr = EA_PCDI_16(); + m68040_do_fsave(addr, -1, 1); + break; + } + default: + fatalerror("M68kFPU: FSAVE unhandled mode %d reg %d at %x\n", mode, reg, REG_PC); } - break; + } + break; default: fatalerror("M68kFPU: FSAVE unhandled mode %d reg %d at %x\n", mode, reg, REG_PC); } - break; } break; @@ -1711,59 +2330,80 @@ void m68040_fpu_op1() { case 2: // (An) addr = REG_A[reg]; - temp = m68ki_read_32(addr); - - // check for NULL frame - if (temp & 0xff000000) - { - // we don't handle non-NULL frames and there's no pre/post inc/dec to do here - m68ki_cpu.fpu_just_reset = 0; - } - else - { - do_frestore_null(); - } + m68040_do_frestore(addr, -1); break; case 3: // (An)+ - addr = EA_AY_PI_32(); - temp = m68ki_read_32(addr); + addr = EA_AY_PI_32(); + m68040_do_frestore(addr, reg); + break; - // check for NULL frame - if (temp & 0xff000000) - { - m68ki_cpu.fpu_just_reset = 0; + case 5: // (D16, An) + addr = EA_AY_DI_16(); + m68040_do_frestore(addr, -1); + break; - // how about an IDLE frame? - if ((temp & 0x00ff0000) == 0x00180000) - { - REG_A[reg] += 6*4; - } // check UNIMP - else if ((temp & 0x00ff0000) == 0x00380000) + case 6: // (An) + (Xn) + d8 + addr = EA_AY_IX_16(); + m68040_do_frestore(addr, -1); + break; + + case 7: // + { + switch (reg) + { + case 1: // (abs32) { - REG_A[reg] += 14*4; - } // check BUSY - else if ((temp & 0x00ff0000) == 0x00b40000) + addr = EA_AL_32(); + m68040_do_frestore(addr, -1); + break; + } + case 2: // (d16, PC) { - REG_A[reg] += 45*4; + addr = EA_PCDI_16(); + m68040_do_frestore(addr, -1); + break; } + default: + fatalerror("M68kFPU: FRESTORE unhandled mode %d reg %d at %x\n", mode, reg, REG_PC); } - else - { - do_frestore_null(); - } - break; + } + break; default: fatalerror("M68kFPU: FRESTORE unhandled mode %d reg %d at %x\n", mode, reg, REG_PC); } - break; } break; - default: fatalerror("m68040_fpu_op1: unimplemented op %d at %08X\n", (REG_IR >> 6) & 0x3, REG_PC-2); + default: fatalerror("m68040_fpu_op1: unimplemented op %d at %08X\n", (REG_IR >> 6) & 0x3, REG_PC-2); } } +void m68881_ftrap() +{ + uint16 w2 = OPER_I_16(); + // now check the condition + if (TEST_CONDITION(w2 & 0x3f)) + { + // trap here + m68ki_exception_trap(EXCEPTION_TRAPV); + } + else // fall through, requires eating the operand + { + switch (REG_IR & 0x7) + { + case 2: // word operand + OPER_I_16(); + break; + case 3: // long word operand + OPER_I_32(); + break; + + case 4: // no operand + break; + } + } +}