--- previous/src/cpu/fpp.c 2018/04/24 19:27:54 1.1.1.2 +++ previous/src/cpu/fpp.c 2018/04/24 19:30:17 1.1.1.3 @@ -1,34 +1,48 @@ /* * UAE - The Un*x Amiga Emulator * -* MC68881 emulation +* MC68881/68882/68040/68060 FPU emulation * * Copyright 1996 Herman ten Brugge * Modified 2005 Peter Keunecke +* 68040+ exceptions and more by Toni Wilen */ #define __USE_ISOC9X /* We might be able to pick up a NaN */ #include #include +#include +#include #include "sysconfig.h" #include "sysdeps.h" +#ifdef _MSC_VER +#pragma fenv_access(on) +#endif + #include "options_cpu.h" #include "memory.h" #include "custom.h" #include "events.h" #include "newcpu.h" -//#include "ersatz.h" #include "md-fpp.h" #include "savestate.h" #include "cpu_prefetch.h" -#include "main.h" #include "cpummu.h" +#include "cpummu030.h" +//#include "debug.h" + +#define write_log printf + +#ifdef X86_MSVC_ASSEMBLY +#define X86_MSVC_ASSEMBLY_FPU +#define NATIVE_FPUCW +#endif #define DEBUG_FPP 0 -#define write_log printf +#define EXCEPTION_FPP 0 STATIC_INLINE int isinrom (void) { @@ -108,22 +122,178 @@ static double *fp_nan = (double *)dhe #endif double fp_1e8 = 1.0e8; float fp_1e0 = 1, fp_1e1 = 10, fp_1e2 = 100, fp_1e4 = 10000; +static bool fpu_mmu_fixup; #define FFLAG_Z 0x4000 #define FFLAG_N 0x0100 #define FFLAG_NAN 0x0400 -#define MAKE_FPSR(r) (regs).fp_result=(r) +STATIC_INLINE void MAKE_FPSR (fptype *fp) +{ + int status = fetestexcept (FE_ALL_EXCEPT); + if (status) + regs.fp_result_status |= status; + regs.fp_result.fp = *fp; +} + +STATIC_INLINE void CLEAR_STATUS (void) +{ + feclearexcept (FE_ALL_EXCEPT); +} + +static void fpnan (fpdata *fpd) +{ + fpd->fp = *fp_nan; +#ifdef USE_SOFT_LONG_DOUBLE + fpd->fpe = ((uae_u64)xhex_nan[0] << 32) | xhex_nan[1]; + fpd->fpm = xhex_nan[2]; +#endif +} + +static void fpclear (fpdata *fpd) +{ + fpd->fp = 0; +#ifdef USE_SOFT_LONG_DOUBLE + fpd->fpe = 0; + fpd->fpm = 0; +#endif +} +static void fpset (fpdata *fpd, fptype f) +{ + fpd->fp = f; +#ifdef USE_SOFT_LONG_DOUBLE + fpd->fpe = 0; + fpd->fpm = 0; +#endif +} + +#if 0 +static void normalize(uae_u32 *pwrd1, uae_u32 *pwrd2, uae_u32 *pwrd3) +{ + uae_u32 wrd1 = *pwrd1; + uae_u32 wrd2 = *pwrd2; + uae_u32 wrd3 = *pwrd3; + int exp = (wrd1 >> 16) & 0x7fff; + // Normalize if unnormal. + if (exp != 0 && exp != 0x7fff && !(wrd2 & 0x80000000)) { + while (!(wrd2 & 0x80000000) && (wrd2 || wrd3)) { + wrd2 <<= 1; + if (wrd3 & 0x80000000) + wrd2 |= 1; + wrd3 <<= 1; + exp--; + } + if (exp < 0) + exp = 0; + if (!wrd2 && !wrd3) + exp = 0; + *pwrd1 = (wrd1 & 0x80000000) | (exp << 16); + *pwrd2 = wrd2; + *pwrd3 = wrd3; + } +} +#endif -static uae_u16 x87_cw_tab[] = { +bool fpu_get_constant(fpdata *fp, int cr) +{ + fptype f; + switch (cr & 0x7f) + { + case 0x00: + f = *fp_pi; + break; + case 0x0b: + f = *fp_l10_2; + break; + case 0x0c: + f = *fp_exp_1; + break; + case 0x0d: + f = *fp_l2_e; + break; + case 0x0e: + f = *fp_l10_e; + break; + case 0x0f: + f = 0.0; + break; + case 0x30: + f = *fp_ln_2; + break; + case 0x31: + f = *fp_ln_10; + break; + case 0x32: + f = (fptype)fp_1e0; + break; + case 0x33: + f = (fptype)fp_1e1; + break; + case 0x34: + f = (fptype)fp_1e2; + break; + case 0x35: + f = (fptype)fp_1e4; + break; + case 0x36: + f = (fptype)fp_1e8; + break; + case 0x37: + f = *fp_1e16; + break; + case 0x38: + f = *fp_1e32; + break; + case 0x39: + f = *fp_1e64; + break; + case 0x3a: + f = *fp_1e128; + break; + case 0x3b: + f = *fp_1e256; + break; + case 0x3c: + f = *fp_1e512; + break; + case 0x3d: + f = *fp_1e1024; + break; + case 0x3e: + f = *fp_1e2048; + break; + case 0x3f: + f = *fp_1e4096; + break; + default: + return false; + } + fp->fp = f; + return true; +} + +static __inline__ void native_set_fpucw (uae_u32 m68k_cw) +{ +#ifdef NATIVE_FPUCW +#ifdef _WIN32 + static int ex = 0; + // RN, RZ, RM, RP + static const unsigned int fp87_round[4] = { _RC_NEAR, _RC_CHOP, _RC_DOWN, _RC_UP }; + // Extend X, Single S, Double D, Undefined + static const unsigned int fp87_prec[4] = { _PC_64 , _PC_24 , _PC_53, 0 }; + +#ifdef WIN64 + _controlfp (ex | fp87_round[(m68k_cw >> 4) & 3], _MCW_RC); +#else + _control87 (ex | fp87_round[(m68k_cw >> 4) & 3] | fp87_prec[(m68k_cw >> 6) & 3], _MCW_RC | _MCW_PC); +#endif +#else +static const uae_u16 x87_cw_tab[] = { 0x137f, 0x1f7f, 0x177f, 0x1b7f, /* Extended */ 0x107f, 0x1c7f, 0x147f, 0x187f, /* Single */ 0x127f, 0x1e7f, 0x167f, 0x1a7f, /* Double */ 0x137f, 0x1f7f, 0x177f, 0x1b7f /* undefined */ }; -/* Nearest, toZero, Down, Up */ -static __inline__ void native_set_fpucw (uae_u32 m68k_cw) -{ #if USE_X86_FPUCW uae_u16 x87_cw = x87_cw_tab[(m68k_cw >> 4) & 0xf]; @@ -135,6 +305,8 @@ static __inline__ void native_set_fpucw __asm__ ("fldcw %0" : : "m" (*&x87_cw)); #endif #endif +#endif +#endif } #if defined(uae_s64) /* Close enough for government work? */ @@ -143,76 +315,363 @@ typedef uae_s64 tointtype; typedef uae_s32 tointtype; #endif -static void fpu_op_illg (uae_u32 opcode, int pcoffset) +static void fpu_format_error (void) { - if ((currprefs.cpu_model == 68060 && (currprefs.fpu_model == 0 || (regs.pcr & 2))) - || (currprefs.cpu_model == 68040 && currprefs.fpu_model == 0)) { - /* 68040 unimplemented/68060 FPU disabled exception. - * Line F exception with different stack frame.. */ - uaecptr newpc = m68k_getpc (); - uaecptr oldpc = newpc - pcoffset; - regs.t0 = regs.t1 = 0; - MakeSR (); - if (!regs.s) { - regs.usp = m68k_areg (regs, 7); - m68k_areg (regs, 7) = regs.isp; - } - regs.s = 1; + uaecptr newpc; + regs.t0 = regs.t1 = 0; + MakeSR (); + if (!regs.s) { + regs.usp = m68k_areg (regs, 7); + m68k_areg (regs, 7) = regs.isp; + } + regs.s = 1; + m68k_areg (regs, 7) -= 2; + x_put_long (m68k_areg (regs, 7), 0x0000 + 14 * 4); + m68k_areg (regs, 7) -= 4; + x_put_long (m68k_areg (regs, 7), m68k_getpc ()); + m68k_areg (regs, 7) -= 2; + x_put_long (m68k_areg (regs, 7), regs.sr); + newpc = x_get_long (regs.vbr + 14 * 4); + m68k_setpc (newpc); +#ifdef JIT + set_special (SPCFLAG_END_COMPILE); +#endif + regs.fp_exception = true; +} + +#define FPU_EXP_UNIMP_INS 0 +#define FPU_EXP_DISABLED 1 +#define FPU_EXP_UNIMP_DATATYPE_PACKED_PRE 2 +#define FPU_EXP_UNIMP_DATATYPE_PACKED_POST 3 +#define FPU_EXP_UNIMP_EA 4 + +static void fpu_arithmetic_exception (uae_u16 opcode, uae_u16 extra, uae_u32 ea, uaecptr oldpc, int type, fpdata *src, int reg) +{ + // TODO +} + +static void fpu_op_unimp (uae_u16 opcode, uae_u16 extra, uae_u32 ea, uaecptr oldpc, int type, fpdata *src, int reg) +{ + /* 68040 unimplemented/68060 FPU disabled exception. + * Line F exception with different stack frame.. */ + int vector = 11; + uaecptr newpc = m68k_getpc (); // next instruction + static int warned = 20; + + regs.t0 = regs.t1 = 0; + MakeSR (); + if (!regs.s) { + regs.usp = m68k_areg (regs, 7); + if (currprefs.cpu_model == 68060) { + m68k_areg (regs, 7) = regs.isp; + } else if (currprefs.cpu_model >= 68020) { + m68k_areg (regs, 7) = regs.m ? regs.msp : regs.isp; + } else { + m68k_areg (regs, 7) = regs.isp; + } + regs.s = 1; + if (currprefs.mmu_model) + mmu_set_super (regs.s != 0); + } + regs.fpu_exp_state = 1; + if (currprefs.cpu_model == 68060) { + regs.fpiar = oldpc; + regs.exp_extra = extra; + regs.exp_opcode = opcode; + if (src) + regs.exp_src1 = *src; + regs.exp_type = type; + if (type == FPU_EXP_DISABLED) { + // current PC + newpc = oldpc; m68k_areg (regs, 7) -= 4; x_put_long (m68k_areg (regs, 7), oldpc); m68k_areg (regs, 7) -= 4; - x_put_long (m68k_areg (regs, 7), oldpc); + x_put_long (m68k_areg (regs, 7), ea); + m68k_areg (regs, 7) -= 2; + x_put_word (m68k_areg (regs, 7), 0x4000 + vector * 4); + } else if (type == FPU_EXP_UNIMP_INS) { + // PC = next instruction + m68k_areg (regs, 7) -= 4; + x_put_long (m68k_areg (regs, 7), ea); + m68k_areg (regs, 7) -= 2; + x_put_word (m68k_areg (regs, 7), 0x2000 + vector * 4); + } else if (type == FPU_EXP_UNIMP_DATATYPE_PACKED_PRE || type == FPU_EXP_UNIMP_DATATYPE_PACKED_POST) { + regs.fpu_exp_state = 2; // EXC frame + // PC = next instruction + vector = 55; + m68k_areg (regs, 7) -= 4; + x_put_long (m68k_areg (regs, 7), ea); + m68k_areg (regs, 7) -= 2; + x_put_word (m68k_areg (regs, 7), 0x2000 + vector * 4); + } else { // FPU_EXP_UNIMP_EA + // current PC + newpc = oldpc; + vector = 60; + m68k_areg (regs, 7) -= 2; + x_put_word (m68k_areg (regs, 7), 0x0000 + vector * 4); + } + } else if (currprefs.cpu_model == 68040) { + regs.fpiar = oldpc; + regs.exp_extra = extra; + regs.exp_opcode = opcode; + if (src) + regs.exp_src1 = *src; + regs.exp_type = type; + if (reg >= 0) + regs.exp_src2 = regs.fp[reg]; + else + fpclear (®s.exp_src2); + if (type == FPU_EXP_UNIMP_INS || type == FPU_EXP_DISABLED) { + // PC = next instruction + m68k_areg (regs, 7) -= 4; + x_put_long (m68k_areg (regs, 7), ea); m68k_areg (regs, 7) -= 2; - x_put_word (m68k_areg (regs, 7), 0x4000 + 11 * 4); + x_put_word (m68k_areg (regs, 7), 0x2000 + vector * 4); + } else if (type == FPU_EXP_UNIMP_DATATYPE_PACKED_PRE || type == FPU_EXP_UNIMP_DATATYPE_PACKED_POST) { + // PC = next instruction + vector = 55; m68k_areg (regs, 7) -= 4; - x_put_long (m68k_areg (regs, 7), newpc); + x_put_long (m68k_areg (regs, 7), ea); m68k_areg (regs, 7) -= 2; - x_put_word (m68k_areg (regs, 7), regs.sr); - write_log ("68040/060 FPU disabled exception PC=%x\n", newpc); - newpc = x_get_long (regs.vbr + 11 * 4); - m68k_setpc (newpc); + x_put_word (m68k_areg (regs, 7), 0x2000 + vector * 4); + regs.fpu_exp_state = 2; // BUSY frame + } + } + oldpc = newpc; + m68k_areg (regs, 7) -= 4; + x_put_long (m68k_areg (regs, 7), newpc); + m68k_areg (regs, 7) -= 2; + x_put_word (m68k_areg (regs, 7), regs.sr); + newpc = x_get_long (regs.vbr + vector * 4); + if (warned > 0) { + write_log (_T("FPU EXCEPTION %d OP=%04X-%04X EA=%08X PC=%08X -> %08X\n"), type, opcode, extra, ea, oldpc, newpc); +#if EXCEPTION_FPP == 0 + warned--; +#endif + } + regs.fp_exception = true; + m68k_setpc (newpc); #ifdef JIT - set_special (SPCFLAG_END_COMPILE); + set_special (SPCFLAG_END_COMPILE); #endif +} + +static void fpu_op_illg2 (uae_u16 opcode, uae_u16 extra, uae_u32 ea, uaecptr oldpc) +{ + if ((currprefs.cpu_model == 68060 && (currprefs.fpu_model == 0 || (regs.pcr & 2))) + || (currprefs.cpu_model == 68040 && currprefs.fpu_model == 0)) { + fpu_op_unimp (opcode, extra, ea, oldpc, FPU_EXP_DISABLED, NULL, -1); return; } + regs.fp_exception = true; + m68k_setpc (oldpc); op_illg (opcode); } -STATIC_INLINE int fault_if_no_fpu (uae_u32 opcode, int pcoffset) +static void fpu_op_illg (uae_u16 opcode, uae_u16 extra, uaecptr oldpc) +{ + fpu_op_illg2 (opcode, extra, 0, oldpc); +} + + +static void fpu_noinst (uae_u16 opcode, uaecptr pc) +{ +#if EXCEPTION_FPP + write_log (_T("Unknown FPU instruction %04X %08X\n"), opcode, pc); +#endif + regs.fp_exception = true; + m68k_setpc (pc); + op_illg (opcode); +} + +static bool fault_if_no_fpu (uae_u16 opcode, uae_u16 extra, uaecptr ea, uaecptr oldpc) { if ((regs.pcr & 2) || currprefs.fpu_model <= 0) { - fpu_op_illg (opcode, pcoffset); - return 1; +#if EXCEPTION_FPP + write_log (_T("no FPU: %04X-%04X PC=%08X\n"), opcode, extra, oldpc); +#endif + fpu_op_illg2 (opcode, extra, ea, oldpc); + return true; } - return 0; + return false; } +static bool fault_if_unimplemented_680x0 (uae_u16 opcode, uae_u16 extra, uaecptr ea, uaecptr oldpc, fpdata *src, int reg) +{ + if (fault_if_no_fpu (opcode, extra, ea, oldpc)) + return true; + if (currprefs.cpu_model >= 68040 && currprefs.fpu_model/* && currprefs.fpu_no_unimplemented*/) { + if ((extra & (0x8000 | 0x2000)) != 0) + return false; + if ((extra & 0xfc00) == 0x5c00) { + // FMOVECR + fpu_op_unimp (opcode, extra, ea, oldpc, FPU_EXP_UNIMP_INS, src, reg); + return true; + } + uae_u16 v = extra & 0x7f; + switch (v) + { + case 0x01: /* FINT */ + case 0x03: /* FINTRZ */ + // Unimplemented only in 68040. + if (currprefs.cpu_model == 68040) { + fpu_op_unimp (opcode, extra, ea, oldpc, FPU_EXP_UNIMP_INS, src, reg); + return true; + } + return false; + case 0x02: /* FSINH */ + case 0x06: /* FLOGNP1 */ + case 0x08: /* FETOXM1 */ + case 0x09: /* FTANH */ + case 0x0a: /* FATAN */ + case 0x0c: /* FASIN */ + case 0x0d: /* FATANH */ + case 0x0e: /* FSIN */ + case 0x0f: /* FTAN */ + case 0x10: /* FETOX */ + case 0x11: /* FTWOTOX */ + case 0x12: /* FTENTOX */ + case 0x14: /* FLOGN */ + case 0x15: /* FLOG10 */ + case 0x16: /* FLOG2 */ + case 0x19: /* FCOSH */ + case 0x1c: /* FACOS */ + case 0x1d: /* FCOS */ + case 0x1e: /* FGETEXP */ + case 0x1f: /* FGETMAN */ + case 0x30: /* FSINCOS */ + case 0x31: /* FSINCOS */ + case 0x32: /* FSINCOS */ + case 0x33: /* FSINCOS */ + case 0x34: /* FSINCOS */ + case 0x35: /* FSINCOS */ + case 0x36: /* FSINCOS */ + case 0x37: /* FSINCOS */ + case 0x21: /* FMOD */ + case 0x25: /* FREM */ + case 0x26: /* FSCALE */ + fpu_op_unimp (opcode, extra, ea, oldpc, FPU_EXP_UNIMP_INS, src, reg); + return true; + } + } + return false; +} + +static bool fault_if_unimplemented_6888x (uae_u16 opcode, uae_u16 extra, uaecptr oldpc) +{ + if ((currprefs.fpu_model == 68881 || currprefs.fpu_model == 68882)/* && currprefs.fpu_no_unimplemented*/) { + uae_u16 v = extra & 0x7f; + /* 68040/68060 only variants. 6888x = F-line exception. */ + switch (v) + { + case 0x62: /* FSADD */ + case 0x66: /* FDADD */ + case 0x68: /* FSSUB */ + case 0x6c: /* FDSUB */ + case 0x5a: /* FSNEG */ + case 0x5e: /* FDNEG */ + case 0x58: /* FSABS */ + case 0x5c: /* FDABS */ + case 0x63: /* FSMUL */ + case 0x67: /* FDMUL */ + case 0x41: /* FSSQRT */ + case 0x45: /* FDSQRT */ + fpu_noinst (opcode, oldpc); + return true; + } + } + return false; +} + +static bool fault_if_60 (uae_u16 opcode, uae_u16 extra, uaecptr ea, uaecptr oldpc, int type) +{ + if (currprefs.cpu_model == 68060 && currprefs.fpu_model/* && currprefs.fpu_no_unimplemented*/) { + fpu_op_unimp (opcode, extra, ea, oldpc, type, NULL, -1); + return true; + } + return false; +} + +static bool fault_if_4060 (uae_u16 opcode, uae_u16 extra, uaecptr ea, uaecptr oldpc, int type, fpdata *src, uae_u32 *pack) +{ + if (currprefs.cpu_model >= 68040 && currprefs.fpu_model/* && currprefs.fpu_no_unimplemented*/) { + if (pack) { + regs.exp_pack[0] = pack[0]; + regs.exp_pack[1] = pack[1]; + regs.exp_pack[2] = pack[2]; + } + fpu_op_unimp (opcode, extra, ea, oldpc, type, src, -1); + return true; + } + return false; +} + +static bool fault_if_no_fpu_u (uae_u16 opcode, uae_u16 extra, uaecptr ea, uaecptr oldpc) +{ + if (fault_if_no_fpu (opcode, extra, ea, oldpc)) + return true; + if (currprefs.cpu_model == 68060 && currprefs.fpu_model/* && currprefs.fpu_no_unimplemented*/) { + // 68060 FTRAP, FDBcc or FScc are not implemented. + fpu_op_unimp (opcode, extra, ea, oldpc, FPU_EXP_UNIMP_INS, NULL, -1); + return true; + } + return false; +} + +static bool fault_if_no_6888x (uae_u16 opcode, uae_u16 extra, uaecptr oldpc) +{ + if (currprefs.cpu_model < 68040 && currprefs.fpu_model <= 0) { +#if EXCEPTION_FPP + write_log (_T("6888x no FPU: %04X-%04X PC=%08X\n"), opcode, extra, oldpc); +#endif + m68k_setpc (oldpc); + regs.fp_exception = true; + op_illg (opcode); + return true; + } + return false; +} + + static int get_fpu_version (void) { int v = 0; -/* removed for previous by andreas_g - if (currprefs.fpu_revision >= 0) - return currprefs.fpu_revision; */ + switch (currprefs.fpu_model) { case 68881: v = 0x1f; break; case 68882: - v = 0x20; /* ??? */ + v = 0x20; break; case 68040: - v = 0x41; + if (currprefs.fpu_revision == 0x40) + v = 0x40; + else + v = 0x41; break; } return v; } -#define fp_round_to_minus_infinity(x) fp_floor(x) -#define fp_round_to_plus_infinity(x) fp_ceil(x) -#define fp_round_to_zero(x) ((int)(x)) -#define fp_round_to_nearest(x) ((int)((x) + 0.5)) +static void fpu_null (void) +{ + regs.fpu_state = 0; + regs.fpu_exp_state = 0; + regs.fpcr = 0; + regs.fpsr = 0; + regs.fpiar = 0; + fpclear (®s.fp_result); + for (int i = 0; i < 8; i++) + fpnan (®s.fp[i]); +} + +#define fp_round_to_minus_infinity(x) floor(x) +#define fp_round_to_plus_infinity(x) ceil(x) +#define fp_round_to_zero(x) ((x) >= 0.0 ? floor(x) : ceil(x)) +#define fp_round_to_nearest(x) ((x) >= 0.0 ? (int)((x) + 0.5) : (int)((x) - 0.5)) STATIC_INLINE tointtype toint (fptype src, fptype minval, fptype maxval) { @@ -220,7 +679,7 @@ STATIC_INLINE tointtype toint (fptype sr src = minval; if (src > maxval) src = maxval; -#if defined(X86_MSVC_ASSEMBLY) +#if defined(X86_MSVC_ASSEMBLY_FPU) { fptype tmp_fp; __asm { @@ -232,66 +691,131 @@ STATIC_INLINE tointtype toint (fptype sr } #else /* no X86_MSVC */ { - int result = src; -#if 0 - switch (get_fpcr () & 0x30) { - case FPCR_ROUND_ZERO: - result = fp_round_to_zero (src); - break; - case FPCR_ROUND_MINF: - result = fp_round_to_minus_infinity (src); - break; - case FPCR_ROUND_NEAR: - result = fp_round_to_nearest (src); - break; - case FPCR_ROUND_PINF: - result = fp_round_to_plus_infinity (src); - break; - default: - result = src; /* should never be reached */ - break; -#endif + int result = (int)src; + switch (regs.fpcr & 0x30) + { + case FPCR_ROUND_ZERO: + result = (int)fp_round_to_zero (src); + break; + case FPCR_ROUND_MINF: + result = (int)fp_round_to_minus_infinity (src); + break; + case FPCR_ROUND_NEAR: + result = fp_round_to_nearest (src); + break; + case FPCR_ROUND_PINF: + result = (int)fp_round_to_plus_infinity (src); + break; + } return result; } #endif } -/*extern int isinf (double x); //Not used */ +static bool fpu_isnan (fptype fp) +{ +#ifdef HAVE_ISNAN + return isnan (fp) != 0; +#else + return false; +#endif +} +static bool fpu_isinfinity (fptype fp) +{ +#ifdef _MSC_VER + return !_finite (fp); +#elifdef HAVE_ISINF + return _isinf (fp); +#else + return false; +#endif +} uae_u32 get_fpsr (void) { - uae_u32 answer = regs.fpsr & 0x00ffffff; -#ifdef HAVE_ISNAN - if (isnan (regs.fp_result)) - answer |= 0x01000000; + uae_u32 answer = regs.fpsr & 0x00ff00f8; + + // exception status byte + if (regs.fp_result_status & FE_INEXACT) + answer |= 1 << 9; + if (regs.fp_result_status & FE_DIVBYZERO) + answer |= 1 << 10; + if (regs.fp_result_status & FE_UNDERFLOW) + answer |= 1 << 11; + if (regs.fp_result_status & FE_OVERFLOW) + answer |= 1 << 12; + if (regs.fp_result_status & FE_INVALID) + answer |= 1 << 13; + + // accrued exception byte + if (answer & ((1 << 14) | (1 << 13))) + answer |= 0x80; // IOP = SNAN | OPERR + if (answer & (1 << 12)) + answer |= 0x40; // OVFL = OVFL + if (answer & ((1 << 11) | (1 << 9))) + answer |= 0x20; // UNFL = UNFL | INEX2 + if (answer & (1 << 10)) + answer |= 0x10; // DZ = DZ + if (answer & ((1 << 12) | (1 << 9) | (1 << 8))) + answer |= 0x08; // INEX = INEX1 | INEX2 | OVFL + + regs.fpsr = answer; + + // condition code byte + if (fpu_isnan (regs.fp_result.fp)) + answer |= 1 << 24; else -#endif { - if (regs.fp_result == 0) - answer |= 0x04000000; - else if (regs.fp_result < 0) - answer |= 0x08000000; -#ifdef HAVE_ISINF - if (isinf (regs.fp_result)) - answer |= 0x02000000; -#endif + if (regs.fp_result.fp == 0) + answer |= 1 << 26; + else if (regs.fp_result.fp < 0) + answer |= 1 << 27; + if (fpu_isinfinity (regs.fp_result.fp)) + answer |= 1 << 25; } return answer; } +static void update_fpsr (uae_u32 v) +{ + regs.fp_result_status = FE_INVALID; + get_fpsr (); +} + STATIC_INLINE void set_fpsr (uae_u32 x) { regs.fpsr = x; + regs.fp_result_status = 0; - if (x & 0x01000000) { - regs.fp_result = *fp_nan; - } + if (x & 0x01000000) + fpset (®s.fp_result, *fp_nan); else if (x & 0x04000000) - regs.fp_result = 0; + fpset (®s.fp_result, 0); else if (x & 0x08000000) - regs.fp_result = -1; + fpset (®s.fp_result, -1); else - regs.fp_result = 1; + fpset (®s.fp_result, 1); +} + +uae_u32 get_ftag (uae_u32 w1, uae_u32 w2, uae_u32 w3) +{ + int exp = (w1 >> 16) & 0x7fff; + + if (exp == 0) { + if (!w2 && !w3) + return 1; // ZERO + return 4; // DENORMAL or UNNORMAL + } else if (exp == 0x7fff) { + int s = w2 >> 30; + int z = (w2 & 0x3fffffff) == 0 && w3 == 0; + if ((s == 0 && !z) || (s == 2 && !z)) + return 2; // INF + return 3; // NAN + } else { + if (!(w2 & 0x80000000)) + return 4; // UNNORMAL + return 0; // NORMAL + } } /* single : S 8*E 23*F */ @@ -302,107 +826,199 @@ STATIC_INLINE void set_fpsr (uae_u32 x) /* E = MAX & F # 0 -> NotANumber */ /* E = biased by 127 (single) ,1023 (double) ,16383 (extended) */ -STATIC_INLINE fptype to_pack (uae_u32 wrd1, uae_u32 wrd2, uae_u32 wrd3) +static fptype to_pack (uae_u32 *wrd) { fptype d; char *cp; char str[100]; cp = str; - if (wrd1 & 0x80000000) + if (wrd[0] & 0x80000000) *cp++ = '-'; - *cp++ = (wrd1 & 0xf) + '0'; + *cp++ = (wrd[0] & 0xf) + '0'; *cp++ = '.'; - *cp++ = ((wrd2 >> 28) & 0xf) + '0'; - *cp++ = ((wrd2 >> 24) & 0xf) + '0'; - *cp++ = ((wrd2 >> 20) & 0xf) + '0'; - *cp++ = ((wrd2 >> 16) & 0xf) + '0'; - *cp++ = ((wrd2 >> 12) & 0xf) + '0'; - *cp++ = ((wrd2 >> 8) & 0xf) + '0'; - *cp++ = ((wrd2 >> 4) & 0xf) + '0'; - *cp++ = ((wrd2 >> 0) & 0xf) + '0'; - *cp++ = ((wrd3 >> 28) & 0xf) + '0'; - *cp++ = ((wrd3 >> 24) & 0xf) + '0'; - *cp++ = ((wrd3 >> 20) & 0xf) + '0'; - *cp++ = ((wrd3 >> 16) & 0xf) + '0'; - *cp++ = ((wrd3 >> 12) & 0xf) + '0'; - *cp++ = ((wrd3 >> 8) & 0xf) + '0'; - *cp++ = ((wrd3 >> 4) & 0xf) + '0'; - *cp++ = ((wrd3 >> 0) & 0xf) + '0'; + *cp++ = ((wrd[1] >> 28) & 0xf) + '0'; + *cp++ = ((wrd[1] >> 24) & 0xf) + '0'; + *cp++ = ((wrd[1] >> 20) & 0xf) + '0'; + *cp++ = ((wrd[1] >> 16) & 0xf) + '0'; + *cp++ = ((wrd[1] >> 12) & 0xf) + '0'; + *cp++ = ((wrd[1] >> 8) & 0xf) + '0'; + *cp++ = ((wrd[1] >> 4) & 0xf) + '0'; + *cp++ = ((wrd[1] >> 0) & 0xf) + '0'; + *cp++ = ((wrd[2] >> 28) & 0xf) + '0'; + *cp++ = ((wrd[2] >> 24) & 0xf) + '0'; + *cp++ = ((wrd[2] >> 20) & 0xf) + '0'; + *cp++ = ((wrd[2] >> 16) & 0xf) + '0'; + *cp++ = ((wrd[2] >> 12) & 0xf) + '0'; + *cp++ = ((wrd[2] >> 8) & 0xf) + '0'; + *cp++ = ((wrd[2] >> 4) & 0xf) + '0'; + *cp++ = ((wrd[2] >> 0) & 0xf) + '0'; *cp++ = 'E'; - if (wrd1 & 0x40000000) + if (wrd[0] & 0x40000000) *cp++ = '-'; - *cp++ = ((wrd1 >> 24) & 0xf) + '0'; - *cp++ = ((wrd1 >> 20) & 0xf) + '0'; - *cp++ = ((wrd1 >> 16) & 0xf) + '0'; + *cp++ = ((wrd[0] >> 24) & 0xf) + '0'; + *cp++ = ((wrd[0] >> 20) & 0xf) + '0'; + *cp++ = ((wrd[0] >> 16) & 0xf) + '0'; *cp = 0; #if USE_LONG_DOUBLE - sscanf (str, "%Le", &d); + sscanf (str, "%Le", &d); #else sscanf (str, "%le", &d); #endif return d; } -STATIC_INLINE void from_pack (fptype src, uae_u32 * wrd1, uae_u32 * wrd2, uae_u32 * wrd3) +void from_pack (fptype src, uae_u32 *wrd, int kfactor) { - int i; - int t; - char *cp; + int i, j, t; + int exp; + int ndigits; + char *cp, *strp; char str[100]; + + wrd[0] = wrd[1] = wrd[2] = 0; + + if (fpu_isnan (src) || fpu_isinfinity (src)) { + wrd[0] |= (1 << 30) | (1 << 29) | (1 << 30); // YY=1 + wrd[0] |= 0xfff << 16; // Exponent=FFF + // TODO: mantissa should be set if NAN + return; + } + #if USE_LONG_DOUBLE - sprintf (str, "%.16Le", src); + sprintf (str, "%#.17Le", src); #else - sprintf (str, "%.16e", src); + sprintf (str, "%#.17e", src); #endif + + // get exponent cp = str; - *wrd1 = *wrd2 = *wrd3 = 0; - if (*cp == '-') { + while (*cp++ != 'e'); + if (*cp == '+') cp++; - *wrd1 = 0x80000000; + exp = atoi (cp); + + // remove trailing zeros + cp = str; + while (*cp != 'e') + cp++; + cp[0] = 0; + cp--; + while (cp > str && *cp == '0') { + *cp = 0; + cp--; } - if (*cp == '+') + + cp = str; + // get sign + if (*cp == '-') { cp++; - *wrd1 |= (*cp++ - '0'); - if (*cp == '.') + wrd[0] = 0x80000000; + } else if (*cp == '+') { cp++; - for (i = 0; i < 8; i++) { - *wrd2 <<= 4; - if (*cp >= '0' && *cp <= '9') - *wrd2 |= *cp++ - '0'; } - for (i = 0; i < 8; i++) { - *wrd3 <<= 4; - if (*cp >= '0' && *cp <= '9') - *wrd3 |= *cp++ - '0'; + strp = cp; + + if (kfactor <= 0) { + ndigits = abs (exp) + (-kfactor) + 1; + } else { + if (kfactor > 17) { + kfactor = 17; + update_fpsr (FE_INVALID); + } + ndigits = kfactor; } - if (*cp == 'e' || *cp == 'E') { - cp++; - if (*cp == '-') { - cp++; - *wrd1 |= 0x40000000; - } - if (*cp == '+') - cp++; - t = 0; - for (i = 0; i < 3; i++) { + + if (ndigits < 0) + ndigits = 0; + if (ndigits > 16) + ndigits = 16; + + // remove decimal point + strp[1] = strp[0]; + strp++; + // add trailing zeros + i = strlen (strp); + cp = strp + i; + while (i < ndigits) { + *cp++ = '0'; + i++; + } + i = ndigits + 1; + while (i < 17) { + strp[i] = 0; + i++; + } + *cp = 0; + i = ndigits - 1; + // need to round? + if (i >= 0 && strp[i + 1] >= '5') { + while (i >= 0) { + strp[i]++; + if (strp[i] <= '9') + break; + if (i == 0) { + strp[i] = '1'; + exp++; + } else { + strp[i] = '0'; + } + i--; + } + } + strp[ndigits] = 0; + + // store first digit of mantissa + cp = strp; + wrd[0] |= *cp++ - '0'; + + // store rest of mantissa + for (j = 1; j < 3; j++) { + for (i = 0; i < 8; i++) { + wrd[j] <<= 4; if (*cp >= '0' && *cp <= '9') - t = (t << 4) | (*cp++ - '0'); + wrd[j] |= *cp++ - '0'; } - *wrd1 |= t << 16; } + + // exponent + if (exp < 0) { + wrd[0] |= 0x40000000; + exp = -exp; + } + if (exp > 9999) // ?? + exp = 9999; + if (exp > 999) { + int d = exp / 1000; + wrd[0] |= d << 12; + exp -= d * 1000; + update_fpsr (FE_INVALID); + } + i = 100; + t = 0; + while (i >= 1) { + int d = exp / i; + t <<= 4; + t |= d; + exp -= d * i; + i /= 10; + } + wrd[0] |= t << 16; + } -STATIC_INLINE int get_fp_value (uae_u32 opcode, uae_u16 extra, fptype *src) +static int get_fp_value (uae_u32 opcode, uae_u16 extra, fpdata *src, uaecptr oldpc, uae_u32 *adp) { - uaecptr tmppc; - uae_u16 tmp; int size, mode, reg; uae_u32 ad = 0; static const int sz1[8] = { 4, 4, 12, 12, 2, 8, 1, 0 }; static const int sz2[8] = { 4, 4, 12, 12, 2, 8, 2, 0 }; + uae_u32 exts[3]; + int doext = 0; if (!(extra & 0x4000)) { + if (fault_if_no_fpu (opcode, extra, 0, oldpc)) + return -1; *src = regs.fp[(extra >> 10) & 7]; return 1; } @@ -412,21 +1028,22 @@ STATIC_INLINE int get_fp_value (uae_u32 switch (mode) { case 0: - switch (size) { - case 6: - *src = (fptype) (uae_s8) m68k_dreg (regs, reg); - break; - case 4: - *src = (fptype) (uae_s16) m68k_dreg (regs, reg); - break; - case 0: - *src = (fptype) (uae_s32) m68k_dreg (regs, reg); - break; - case 1: - *src = to_single (m68k_dreg (regs, reg)); - break; - default: - return 0; + switch (size) + { + case 6: + src->fp = (fptype) (uae_s8) m68k_dreg (regs, reg); + break; + case 4: + src->fp = (fptype) (uae_s16) m68k_dreg (regs, reg); + break; + case 0: + src->fp = (fptype) (uae_s32) m68k_dreg (regs, reg); + break; + case 1: + src->fp = to_single (m68k_dreg (regs, reg)); + break; + default: + return 0; } return 1; case 1: @@ -435,86 +1052,135 @@ STATIC_INLINE int get_fp_value (uae_u32 ad = m68k_areg (regs, reg); break; case 3: + if (currprefs.mmu_model) { + mmufixup[0].reg = reg; + mmufixup[0].value = m68k_areg (regs, reg); + fpu_mmu_fixup = true; + } ad = m68k_areg (regs, reg); m68k_areg (regs, reg) += reg == 7 ? sz2[size] : sz1[size]; break; case 4: + if (currprefs.mmu_model) { + mmufixup[0].reg = reg; + mmufixup[0].value = m68k_areg (regs, reg); + fpu_mmu_fixup = true; + } m68k_areg (regs, reg) -= reg == 7 ? sz2[size] : sz1[size]; ad = m68k_areg (regs, reg); break; case 5: - ad = m68k_areg (regs, reg) + (uae_s32) (uae_s16) x_next_iword (); + ad = m68k_areg (regs, reg) + (uae_s32) (uae_s16) x_cp_next_iword (); break; case 6: - ad = x_get_disp_ea_020 (m68k_areg (regs, reg), x_next_iword ()); + ad = x_cp_get_disp_ea_020 (m68k_areg (regs, reg), 0); break; case 7: - switch (reg) { + switch (reg) + { + case 0: // (xxx).W + ad = (uae_s32) (uae_s16) x_cp_next_iword (); + break; + case 1: // (xxx).L + ad = x_cp_next_ilong (); + break; + case 2: // (d16,PC) + ad = m68k_getpc (); + ad += (uae_s32) (uae_s16) x_cp_next_iword (); + break; + case 3: // (d8,PC,Xn)+ + ad = x_cp_get_disp_ea_020 (m68k_getpc (), 0); + break; + case 4: // #imm + doext = 1; + switch (size) + { + case 0: // L + case 1: // S + exts[0] = x_cp_next_ilong (); + break; + case 2: // X + case 3: // P + // 68060 and immediate X or P: unimplemented effective address + if (fault_if_60 (opcode, extra, ad, oldpc, FPU_EXP_UNIMP_EA)) + return -1; + exts[0] = x_cp_next_ilong (); + exts[1] = x_cp_next_ilong (); + exts[2] = x_cp_next_ilong (); + break; + case 4: // W + exts[0] = x_cp_next_iword (); + break; + case 5: // D + exts[0] = x_cp_next_ilong (); + exts[1] = x_cp_next_ilong (); + break; + case 6: // B + exts[0] = x_cp_next_iword (); + break; + } + break; + default: + return 0; + } + } + + *adp = ad; + + if (currprefs.fpu_model == 68060 && fault_if_unimplemented_680x0 (opcode, extra, ad, oldpc, src, -1)) + return -1; + + switch (size) + { case 0: - ad = (uae_s32) (uae_s16) x_next_iword (); + src->fp = (fptype) (uae_s32) (doext ? exts[0] : x_cp_get_long (ad)); break; case 1: - ad = x_next_ilong (); + src->fp = to_single ((doext ? exts[0] : x_cp_get_long (ad))); break; case 2: - ad = m68k_getpc (); - ad += (uae_s32) (uae_s16) x_next_iword (); + { + uae_u32 wrd1, wrd2, wrd3; + wrd1 = (doext ? exts[0] : x_cp_get_long (ad)); + ad += 4; + wrd2 = (doext ? exts[1] : x_cp_get_long (ad)); + ad += 4; + wrd3 = (doext ? exts[2] : x_cp_get_long (ad)); + to_exten (src, wrd1, wrd2, wrd3); + } break; case 3: - tmppc = m68k_getpc (); - tmp = x_next_iword (); - ad = x_get_disp_ea_020 (tmppc, tmp); + { + uae_u32 wrd[3]; + uae_u32 adold = ad; + if (currprefs.cpu_model == 68060) { + if (fault_if_4060 (opcode, extra, adold, oldpc, FPU_EXP_UNIMP_DATATYPE_PACKED_PRE, NULL, wrd)) + return -1; + } + wrd[0] = (doext ? exts[0] : x_cp_get_long (ad)); + ad += 4; + wrd[1] = (doext ? exts[1] : x_cp_get_long (ad)); + ad += 4; + wrd[2] = (doext ? exts[2] : x_cp_get_long (ad)); + if (fault_if_4060 (opcode, extra, adold, oldpc, FPU_EXP_UNIMP_DATATYPE_PACKED_PRE, NULL, wrd)) + return -1; + src->fp = to_pack (wrd); + } break; case 4: - ad = m68k_getpc (); - m68k_setpc (ad + sz2[size]); - if (size == 6) - ad++; + src->fp = (fptype) (uae_s16) (doext ? exts[0] : x_cp_get_word (ad)); break; - default: - return 0; + case 5: + { + uae_u32 wrd1, wrd2; + wrd1 = (doext ? exts[0] : x_cp_get_long (ad)); + ad += 4; + wrd2 = (doext ? exts[1] : x_cp_get_long (ad)); + src->fp = to_double (wrd1, wrd2); } - } - switch (size) { - case 0: - *src = (fptype) (uae_s32) x_get_long (ad); - break; - case 1: - *src = to_single (x_get_long (ad)); - break; - case 2:{ - uae_u32 wrd1, wrd2, wrd3; - wrd1 = x_get_long (ad); - ad += 4; - wrd2 = x_get_long (ad); - ad += 4; - wrd3 = x_get_long (ad); - *src = to_exten (wrd1, wrd2, wrd3); - } - break; - case 3:{ - uae_u32 wrd1, wrd2, wrd3; - wrd1 = x_get_long (ad); - ad += 4; - wrd2 = x_get_long (ad); - ad += 4; - wrd3 = x_get_long (ad); - *src = to_pack (wrd1, wrd2, wrd3); - } - break; - case 4: - *src = (fptype) (uae_s16) x_get_word (ad); break; - case 5:{ - uae_u32 wrd1, wrd2; - wrd1 = x_get_long (ad); - ad += 4; - wrd2 = x_get_long (ad); - *src = to_double (wrd1, wrd2); - } - break; case 6: - *src = (fptype) (uae_s8) x_get_byte (ad); + src->fp = (fptype) (uae_s8) (doext ? exts[0] : x_cp_get_byte (ad)); break; default: return 0; @@ -522,46 +1188,51 @@ STATIC_INLINE int get_fp_value (uae_u32 return 1; } -STATIC_INLINE int put_fp_value (fptype value, uae_u32 opcode, uae_u16 extra) +static int put_fp_value (fpdata *value, uae_u32 opcode, uae_u16 extra, uaecptr oldpc) { - uae_u16 tmp; - uaecptr tmppc; int size, mode, reg; - uae_u32 ad; + uae_u32 ad = 0; static int sz1[8] = { 4, 4, 12, 12, 2, 8, 1, 0 }; static int sz2[8] = { 4, 4, 12, 12, 2, 8, 2, 0 }; #if DEBUG_FPP if (!isinrom ()) - write_log ("PUTFP: %f %04X %04X\n", value, opcode, extra); + write_log (_T("PUTFP: %f %04X %04X\n"), value, opcode, extra); +#endif +#ifdef USE_SOFT_LONG_DOUBLE + value->fpx = false; #endif if (!(extra & 0x4000)) { - regs.fp[(extra >> 10) & 7] = value; + if (fault_if_no_fpu (opcode, extra, 0, oldpc)) + return 1; + regs.fp[(extra >> 10) & 7] = *value; return 1; } reg = opcode & 7; mode = (opcode >> 3) & 7; size = (extra >> 10) & 7; ad = -1; - switch (mode) { - case 0: - switch (size) { - case 6: - m68k_dreg (regs, reg) = (uae_u32)(((toint (value, -128.0, 127.0) & 0xff) - | (m68k_dreg (regs, reg) & ~0xff))); - break; - case 4: - m68k_dreg (regs, reg) = (uae_u32)(((toint (value, -32768.0, 32767.0) & 0xffff) - | (m68k_dreg (regs, reg) & ~0xffff))); - break; + switch (mode) + { case 0: - m68k_dreg (regs, reg) = (uae_u32)toint (value, -2147483648.0, 2147483647.0); - break; - case 1: - m68k_dreg (regs, reg) = from_single (value); - break; - default: - return 0; + switch (size) + { + case 6: + m68k_dreg (regs, reg) = (uae_u32)(((toint (value->fp, -128.0, 127.0) & 0xff) + | (m68k_dreg (regs, reg) & ~0xff))); + break; + case 4: + m68k_dreg (regs, reg) = (uae_u32)(((toint (value->fp, -32768.0, 32767.0) & 0xffff) + | (m68k_dreg (regs, reg) & ~0xffff))); + break; + case 0: + m68k_dreg (regs, reg) = (uae_u32)toint (value->fp, -2147483648.0, 2147483647.0); + break; + case 1: + m68k_dreg (regs, reg) = from_single (value->fp); + break; + default: + return 0; } return 1; case 1: @@ -570,86 +1241,105 @@ STATIC_INLINE int put_fp_value (fptype v ad = m68k_areg (regs, reg); break; case 3: + if (currprefs.mmu_model) { + mmufixup[0].reg = reg; + mmufixup[0].value = m68k_areg (regs, reg); + fpu_mmu_fixup = true; + } ad = m68k_areg (regs, reg); m68k_areg (regs, reg) += reg == 7 ? sz2[size] : sz1[size]; break; case 4: + if (currprefs.mmu_model) { + mmufixup[0].reg = reg; + mmufixup[0].value = m68k_areg (regs, reg); + fpu_mmu_fixup = true; + } m68k_areg (regs, reg) -= reg == 7 ? sz2[size] : sz1[size]; ad = m68k_areg (regs, reg); break; case 5: - ad = m68k_areg (regs, reg) + (uae_s32) (uae_s16) x_next_iword (); + ad = m68k_areg (regs, reg) + (uae_s32) (uae_s16) x_cp_next_iword (); break; case 6: - ad = x_get_disp_ea_020 (m68k_areg (regs, reg), x_next_iword ()); + ad = x_cp_get_disp_ea_020 (m68k_areg (regs, reg), 0); break; case 7: - switch (reg) { - case 0: - ad = (uae_s32) (uae_s16) x_next_iword (); - break; - case 1: - ad = x_next_ilong (); - break; - case 2: - ad = m68k_getpc (); - ad += (uae_s32) (uae_s16) x_next_iword (); - break; - case 3: - tmppc = m68k_getpc (); - tmp = x_next_iword (); - ad = x_get_disp_ea_020 (tmppc, tmp); - break; - case 4: - ad = m68k_getpc (); - m68k_setpc (ad + sz2[size]); - break; - default: - return 0; + switch (reg) + { + case 0: + ad = (uae_s32) (uae_s16) x_cp_next_iword (); + break; + case 1: + ad = x_cp_next_ilong (); + break; + case 2: + ad = m68k_getpc (); + ad += (uae_s32) (uae_s16) x_cp_next_iword (); + break; + case 3: + ad = x_cp_get_disp_ea_020 (m68k_getpc (), 0); + break; + default: + return 0; } } - switch (size) { + + if (fault_if_no_fpu (opcode, extra, ad, oldpc)) + return 1; + + switch (size) + { case 0: - x_put_long (ad, (uae_u32)toint (value, -2147483648.0, 2147483647.0)); + x_cp_put_long (ad, (uae_u32)toint (value->fp, -2147483648.0, 2147483647.0)); break; case 1: - x_put_long (ad, from_single (value)); + x_cp_put_long (ad, from_single (value->fp)); break; case 2: { uae_u32 wrd1, wrd2, wrd3; from_exten (value, &wrd1, &wrd2, &wrd3); - x_put_long (ad, wrd1); + x_cp_put_long (ad, wrd1); ad += 4; - x_put_long (ad, wrd2); + x_cp_put_long (ad, wrd2); ad += 4; - x_put_long (ad, wrd3); + x_cp_put_long (ad, wrd3); } break; - case 3: + case 3: // Packed-Decimal Real with Static k-Factor + case 7: // Packed-Decimal Real with Dynamic k-Factor (P{Dn}) (reg to memory only) { - uae_u32 wrd1, wrd2, wrd3; - from_pack (value, &wrd1, &wrd2, &wrd3); - x_put_long (ad, wrd1); + uae_u32 wrd[3]; + int kfactor; + if (fault_if_4060 (opcode, extra, ad, oldpc, FPU_EXP_UNIMP_DATATYPE_PACKED_POST, value, NULL)) + return -1; + kfactor = size == 7 ? m68k_dreg (regs, (extra >> 4) & 7) : extra; + kfactor &= 127; + if (kfactor & 64) + kfactor |= ~63; + from_pack (value->fp, wrd, kfactor); + x_cp_put_long (ad, wrd[0]); ad += 4; - x_put_long (ad, wrd2); + x_cp_put_long (ad, wrd[1]); ad += 4; - x_put_long (ad, wrd3); + x_cp_put_long (ad, wrd[2]); } break; case 4: - x_put_word (ad, (uae_s16) toint (value, -32768.0, 32767.0)); + x_cp_put_word (ad, (uae_s16) toint (value->fp, -32768.0, 32767.0)); + break; + case 5: + { + uae_u32 wrd1, wrd2; + from_double (value->fp, &wrd1, &wrd2); + x_cp_put_long (ad, wrd1); + ad += 4; + x_cp_put_long (ad, wrd2); + } break; - case 5:{ - uae_u32 wrd1, wrd2; - from_double (value, &wrd1, &wrd2); - x_put_long (ad, wrd1); - ad += 4; - x_put_long (ad, wrd2); - } - break; case 6: - x_put_byte (ad, (uae_s8)toint (value, -128.0, 127.0)); + x_cp_put_byte (ad, (uae_s8)toint (value->fp, -128.0, 127.0)); break; default: return 0; @@ -659,14 +1349,13 @@ STATIC_INLINE int put_fp_value (fptype v STATIC_INLINE int get_fp_ad (uae_u32 opcode, uae_u32 * ad) { - uae_u16 tmp; - uaecptr tmppc; int mode; int reg; mode = (opcode >> 3) & 7; reg = opcode & 7; - switch (mode) { + switch (mode) + { case 0: case 1: return 0; @@ -680,49 +1369,49 @@ STATIC_INLINE int get_fp_ad (uae_u32 opc *ad = m68k_areg (regs, reg); break; case 5: - *ad = m68k_areg (regs, reg) + (uae_s32) (uae_s16) x_next_iword (); + *ad = m68k_areg (regs, reg) + (uae_s32) (uae_s16) x_cp_next_iword (); break; case 6: - *ad = x_get_disp_ea_020 (m68k_areg (regs, reg), x_next_iword ()); + *ad = x_cp_get_disp_ea_020 (m68k_areg (regs, reg), 0); break; case 7: - switch (reg) { - case 0: - *ad = (uae_s32) (uae_s16) x_next_iword (); - break; - case 1: - *ad = x_next_ilong (); - break; - case 2: - *ad = m68k_getpc (); - *ad += (uae_s32) (uae_s16) x_next_iword (); - break; - case 3: - tmppc = m68k_getpc (); - tmp = x_next_iword (); - *ad = x_get_disp_ea_020 (tmppc, tmp); - break; - default: - return 0; + switch (reg) + { + case 0: + *ad = (uae_s32) (uae_s16) x_cp_next_iword (); + break; + case 1: + *ad = x_cp_next_ilong (); + break; + case 2: + *ad = m68k_getpc (); + *ad += (uae_s32) (uae_s16) x_cp_next_iword (); + break; + case 3: + *ad = x_cp_get_disp_ea_020 (m68k_getpc (), 0); + break; + default: + return 0; } } return 1; } -STATIC_INLINE int fpp_cond (int condition) +int fpp_cond (int condition) { - int N = (regs.fp_result < 0.0); - int Z = (regs.fp_result == 0.0); + int N = (regs.fp_result.fp < 0.0); + int Z = (regs.fp_result.fp == 0.0); int NotANumber = 0; #ifdef HAVE_ISNAN - NotANumber = isnan (regs.fp_result); + NotANumber = isnan (regs.fp_result.fp); #endif if (NotANumber) N=Z=0; - switch (condition) { + switch (condition) + { case 0x00: return 0; case 0x01: @@ -791,23 +1480,36 @@ STATIC_INLINE int fpp_cond (int conditio return -1; } +static void maybe_idle_state (void) +{ + // conditional floating point instruction does not change state + // from null to idle on 68040/060. + if (currprefs.fpu_model == 68881 || currprefs.fpu_model == 68882) + regs.fpu_state = 1; +} + void fpuop_dbcc (uae_u32 opcode, uae_u16 extra) { - uaecptr pc = (uae_u32) m68k_getpc (); + uaecptr pc = m68k_getpc (); uae_s32 disp; int cc; + regs.fp_exception = false; #if DEBUG_FPP if (!isinrom ()) - write_log ("fdbcc_opp at %08lx\n", m68k_getpc ()); + write_log (_T("fdbcc_opp at %08lx\n"), m68k_getpc ()); #endif - if (fault_if_no_fpu (opcode, 4)) + if (fault_if_no_6888x (opcode, extra, pc - 4)) return; - disp = (uae_s32) (uae_s16) x_next_iword (); + disp = (uae_s32) (uae_s16) x_cp_next_iword (); + if (fault_if_no_fpu_u (opcode, extra, pc + disp, pc - 4)) + return; + regs.fpiar = pc - 4; + maybe_idle_state (); cc = fpp_cond (extra & 0x3f); - if (cc == -1) { - fpu_op_illg (opcode, 4); + if (cc < 0) { + fpu_op_illg (opcode, extra, regs.fpiar); } else if (!cc) { int reg = opcode & 0x7; @@ -820,27 +1522,38 @@ void fpuop_dbcc (uae_u32 opcode, uae_u16 void fpuop_scc (uae_u32 opcode, uae_u16 extra) { - uae_u32 ad; + uae_u32 ad = 0; int cc; + uaecptr pc = m68k_getpc () - 4; + regs.fp_exception = false; #if DEBUG_FPP if (!isinrom ()) - write_log ("fscc_opp at %08lx\n", m68k_getpc ()); + write_log (_T("fscc_opp at %08lx\n"), m68k_getpc ()); #endif - if (fault_if_no_fpu (opcode, 4)) + + if (fault_if_no_6888x (opcode, extra, pc)) + return; + + if (opcode & 0x38) { + if (get_fp_ad (opcode, &ad) == 0) { + fpu_noinst (opcode, regs.fpiar); + return; + } + } + + if (fault_if_no_fpu_u (opcode, extra, ad, pc)) return; + regs.fpiar = pc; + maybe_idle_state (); cc = fpp_cond (extra & 0x3f); - if (cc == -1) { - fpu_op_illg (opcode, 4); + if (cc < 0) { + fpu_op_illg (opcode, extra, regs.fpiar); } else if ((opcode & 0x38) == 0) { m68k_dreg (regs, opcode & 7) = (m68k_dreg (regs, opcode & 7) & ~0xff) | (cc ? 0xff : 0x00); } else { - if (get_fp_ad (opcode, &ad) == 0) { - m68k_setpc (m68k_getpc () - 4); - op_illg (opcode); - } else - x_put_byte (ad, cc ? 0xff : 0x00); + x_cp_put_byte (ad, cc ? 0xff : 0x00); } } @@ -848,39 +1561,45 @@ void fpuop_trapcc (uae_u32 opcode, uaecp { int cc; + regs.fp_exception = false; #if DEBUG_FPP if (!isinrom ()) - write_log ("ftrapcc_opp at %08lx\n", m68k_getpc ()); + write_log (_T("ftrapcc_opp at %08lx\n"), m68k_getpc ()); #endif - if (fault_if_no_fpu (opcode, m68k_getpc() - oldpc)) + if (fault_if_no_fpu_u (opcode, extra, 0, oldpc)) return; + regs.fpiar = oldpc; + maybe_idle_state (); cc = fpp_cond (extra & 0x3f); - if (cc == -1) { - fpu_op_illg (opcode, m68k_getpc () - oldpc); + if (cc < 0) { + fpu_op_illg (opcode, extra, oldpc); + } else if (cc) { + Exception (7); } - if (cc) - Exception (7, oldpc - 2, M68000_EXC_SRC_CPU); } -void fpuop_bcc (uae_u32 opcode, uaecptr pc, uae_u32 extra) +void fpuop_bcc (uae_u32 opcode, uaecptr oldpc, uae_u32 extra) { int cc; + regs.fp_exception = false; #if DEBUG_FPP if (!isinrom ()) - write_log ("fbcc_opp at %08lx\n", m68k_getpc ()); + write_log (_T("fbcc_opp at %08lx\n"), m68k_getpc ()); #endif - if (fault_if_no_fpu (opcode, m68k_getpc () - pc)) + if (fault_if_no_fpu (opcode, extra, 0, oldpc - 2)) return; + regs.fpiar = oldpc - 2; + maybe_idle_state (); cc = fpp_cond (opcode & 0x3f); - if (cc == -1) { - fpu_op_illg (opcode, m68k_getpc () - pc); + if (cc < 0) { + fpu_op_illg (opcode, extra, oldpc - 2); } else if (cc) { if ((opcode & 0x40) == 0) extra = (uae_s32) (uae_s16) extra; - m68k_setpc (pc + extra); + m68k_setpc (oldpc + extra); } } @@ -888,156 +1607,314 @@ void fpuop_save (uae_u32 opcode) { uae_u32 ad; int incr = (opcode & 0x38) == 0x20 ? -1 : 1; - int fpu_version = get_fpu_version(); + int fpu_version = get_fpu_version (); + uaecptr pc = m68k_getpc () - 2; int i; + regs.fp_exception = false; #if DEBUG_FPP if (!isinrom ()) - write_log ("fsave_opp at %08lx\n", m68k_getpc ()); + write_log (_T("fsave_opp at %08lx\n"), m68k_getpc ()); #endif - if (fault_if_no_fpu (opcode, 2)) + + if (fault_if_no_6888x (opcode, 0, pc)) return; if (get_fp_ad (opcode, &ad) == 0) { - fpu_op_illg (opcode, 2); + fpu_op_illg (opcode, 0, pc); return; } + if (fault_if_no_fpu (opcode, 0, ad, pc)) + return; + if (currprefs.fpu_model == 68060) { - /* 12 byte 68060 IDLE frame. */ - if (incr < 0) { - ad -= 4; - x_put_long (ad, 0x00000000); - ad -= 4; - x_put_long (ad, 0x00000000); - ad -= 4; - x_put_long (ad, 0x00006000); + /* 12 byte 68060 NULL/IDLE/EXCP frame. */ + int frame_size = 12; + uae_u32 frame_id, frame_v1, frame_v2; + + if (regs.fpu_exp_state > 1) { + uae_u32 src1[3]; + from_exten (®s.exp_src1, &src1[0], &src1[1], &src1[2]); + frame_id = 0x0000e000 | src1[0]; + frame_v1 = src1[1]; + frame_v2 = src1[2]; + +#if EXCEPTION_FPP +#if USE_LONG_DOUBLE + write_log(_T("68060 FSAVE EXCP %Le\n"), regs.exp_src1.fp); +#else + write_log(_T("68060 FSAVE EXCP %e\n"), regs.exp_src1.fp); +#endif +#endif + } else { - x_put_long (ad, 0x00006000); - ad += 4; - x_put_long (ad, 0x00000000); - ad += 4; - x_put_long (ad, 0x00000000); - ad += 4; + frame_id = regs.fpu_state == 0 ? 0x00000000 : 0x00006000; + frame_v1 = 0; + frame_v2 = 0; } + if (incr < 0) + ad -= frame_size; + x_put_long (ad, frame_id); + ad += 4; + x_put_long (ad, frame_v1); + ad += 4; + x_put_long (ad, frame_v2); + ad += 4; + if (incr < 0) + ad -= frame_size; } else if (currprefs.fpu_model == 68040) { - /* 4 byte 68040 IDLE frame. */ - if (incr < 0) { - ad -= 4; - x_put_long (ad, fpu_version << 24); + if (!regs.fpu_exp_state) { + /* 4 byte 68040 NULL/IDLE frame. */ + uae_u32 frame_id = regs.fpu_state == 0 ? 0 : fpu_version << 24; + if (incr < 0) { + ad -= 4; + x_put_long (ad, frame_id); + } else { + x_put_long (ad, frame_id); + ad += 4; + } } else { - x_put_long (ad, fpu_version << 24); + /* 44 (rev $40) and 52 (rev $41) byte 68040 unimplemented instruction frame */ + /* 96 byte 68040 busy frame */ + int frame_size = regs.fpu_exp_state == 2 ? 0x64 : (fpu_version >= 0x41 ? 0x34 : 0x2c); + uae_u32 frame_id = ((fpu_version << 8) | (frame_size - 4)) << 16; + uae_u32 src1[3], src2[3]; + uae_u32 stag, dtag; + uae_u32 extra = regs.exp_extra; + + from_exten(®s.exp_src1, &src1[0], &src1[1], &src1[2]); + from_exten(®s.exp_src2, &src2[0], &src2[1], &src2[2]); + stag = get_ftag(src1[0], src1[1], src1[2]); + dtag = get_ftag(src2[0], src2[1], src2[2]); + if ((extra & 0x7f) == 4) // FSQRT 4->5 + extra |= 1; + +#if EXCEPTION_FPP + write_log(_T("68040 FSAVE %d (%d), CMDREG=%04X"), regs.exp_type, frame_size, extra); + if (regs.exp_type == FPU_EXP_UNIMP_DATATYPE_PACKED_PRE) { + write_log(_T(" PACKED %08x-%08x-%08x"), regs.exp_pack[0], regs.exp_pack[1], regs.exp_pack[2]); + } else { +#if USE_LONG_DOUBLE + write_log(_T(" SRC=%Le (%08x-%08x-%08x %d), DST=%Le (%08x-%08x-%08x %d)"), regs.exp_src1.fp, src1[0], src1[1], src1[2], stag, regs.exp_src2.fp, src2[0], src2[1], src2[2], dtag); +#else + write_log(_T(" SRC=%e (%08x-%08x-%08x %d), DST=%e (%08x-%08x-%08x %d)"), regs.exp_src1.fp, src1[0], src1[1], src1[2], stag, regs.exp_src2.fp, src2[0], src2[1], src2[2], dtag); +#endif + } + write_log(_T("\n")); +#endif + + if (incr < 0) + ad -= frame_size; + x_put_long (ad, frame_id); ad += 4; + if (regs.fpu_exp_state == 2) { + /* BUSY frame */ + x_put_long(ad, 0); + ad += 4; + x_put_long(ad, 0); // CU_SAVEPC (Software shouldn't care) + ad += 4; + x_put_long(ad, 0); + ad += 4; + x_put_long(ad, 0); + ad += 4; + x_put_long(ad, 0); + ad += 4; + x_put_long(ad, 0); // WBTS/WBTE (No E3 emulated yet) + ad += 4; + x_put_long(ad, 0); // WBTM + ad += 4; + x_put_long(ad, 0); // WBTM + ad += 4; + x_put_long(ad, 0); + ad += 4; + x_put_long(ad, regs.fpiar); // FPIARCU (same as FPU PC or something else?) + ad += 4; + x_put_long(ad, 0); + ad += 4; + x_put_long(ad, 0); + ad += 4; + } + if (fpu_version >= 0x41 || regs.fpu_exp_state == 2) { + x_put_long (ad, ((extra & (0x200 | 0x100 | 0x80)) | (extra & (0x40 | 0x02 | 0x01)) | ((extra >> 1) & (0x04 | 0x08 | 0x10)) | ((extra & 0x04) ? 0x20 : 0x00)) << 16); // CMDREG3B + ad += 4; + x_put_long (ad, 0); + ad += 4; + } + x_put_long (ad, stag << 29); // STAG + ad += 4; + x_put_long (ad, extra << 16); // CMDREG1B + ad += 4; + x_put_long (ad, dtag << 29); // DTAG + ad += 4; + if (fpu_version >= 0x41 || regs.fpu_exp_state == 2) { + x_put_long(ad, (regs.exp_type == FPU_EXP_UNIMP_DATATYPE_PACKED_PRE ? 1 << 26 : 0) | (regs.exp_type == FPU_EXP_UNIMP_DATATYPE_PACKED_POST ? 1 << 20 : 0)); // E1 and T + ad += 4; + } else { + x_put_long(ad, (regs.exp_type == FPU_EXP_UNIMP_DATATYPE_PACKED_PRE || regs.exp_type == FPU_EXP_UNIMP_DATATYPE_PACKED_POST) ? 1 << 26 : 0); // E1 + ad += 4; + } + if (regs.exp_type == FPU_EXP_UNIMP_DATATYPE_PACKED_PRE) { + x_put_long (ad, 0); // FPTS/FPTE + ad += 4; + x_put_long (ad, 0); // FPTM + ad += 4; + x_put_long (ad, regs.exp_pack[0]); // FPTM + ad += 4; + x_put_long (ad, 0); // ETS/ETE + ad += 4; + x_put_long (ad, regs.exp_pack[1]); // ETM + ad += 4; + x_put_long (ad, regs.exp_pack[2]); // ETM + ad += 4; + } else { + x_put_long (ad, src2[0]); // FPTS/FPTE + ad += 4; + x_put_long (ad, src2[1]); // FPTM + ad += 4; + x_put_long (ad, src2[2]); // FPTM + ad += 4; + x_put_long (ad, src1[0]); // ETS/ETE + ad += 4; + x_put_long (ad, src1[1]); // ETM + ad += 4; + x_put_long (ad, src1[2]); // ETM + ad += 4; + } + if (incr < 0) + ad -= frame_size; } } else { /* 68881/68882 */ - int idle_size = currprefs.fpu_model == 68882 ? 0x38 : 0x18; - if (incr < 0) { - ad -= 4; - x_put_long (ad, 0x70000000); - for (i = 0; i < (idle_size - 1) / 4; i++) { + int frame_size = regs.fpu_state == 0 ? 0 : currprefs.fpu_model == 68882 ? 0x3c : 0x1c; + uae_u32 frame_id = regs.fpu_state == 0 ? 0x18 << 16 : (fpu_version << 24) | ((frame_size - 4) << 16); + + if (currprefs.mmu_model) { + if (incr < 0) { + for (i = 0; i < (frame_size / 4) - 1; i++) { + ad -= 4; + if (mmu030_state[0] == i) { + x_put_long (ad, i == 0 ? 0x70000000 : 0x00000000); + mmu030_state[0]++; + } + } ad -= 4; - x_put_long (ad, 0x00000000); + if (mmu030_state[0] == (frame_size / 4) - 1 || (mmu030_state[0] == 0 && frame_size == 0)) { + x_put_long (ad, frame_id); + mmu030_state[0]++; + } + } else { + if (mmu030_state[0] == 0) { + x_put_long (ad, frame_id); + mmu030_state[0]++; + } + ad += 4; + for (i = 0; i < (frame_size / 4) - 1; i++) { + if (mmu030_state[0] == i + 1) { + x_put_long (ad, i == (frame_size / 4) - 2 ? 0x70000000 : 0x00000000); + mmu030_state[0]++; + } + ad += 4; + } } - ad -= 4; - x_put_long (ad, (fpu_version << 24) | (idle_size << 16)); } else { - x_put_long (ad, (fpu_version << 24) | (idle_size << 16)); - ad += 4; - for (i = 0; i < (idle_size - 1) / 4; i++) { - x_put_long (ad, 0x00000000); + if (incr < 0) { + for (i = 0; i < (frame_size / 4) - 1; i++) { + ad -= 4; + x_put_long (ad, i == 0 ? 0x70000000 : 0x00000000); + } + ad -= 4; + x_put_long (ad, frame_id); + } else { + x_put_long (ad, frame_id); ad += 4; + for (i = 0; i < (frame_size / 4) - 1; i++) { + x_put_long (ad, i == (frame_size / 4) - 2 ? 0x70000000 : 0x00000000); + ad += 4; + } } - x_put_long (ad, 0x70000000); - ad += 4; } } + if ((opcode & 0x38) == 0x18) m68k_areg (regs, opcode & 7) = ad; if ((opcode & 0x38) == 0x20) m68k_areg (regs, opcode & 7) = ad; + regs.fpu_exp_state = 0; } void fpuop_restore (uae_u32 opcode) { + int fpu_version = get_fpu_version (); + uaecptr pc = m68k_getpc () - 2; uae_u32 ad; uae_u32 d; int incr = (opcode & 0x38) == 0x20 ? -1 : 1; + regs.fp_exception = false; #if DEBUG_FPP if (!isinrom ()) - write_log ("frestore_opp at %08lx\n", m68k_getpc ()); + write_log (_T("frestore_opp at %08lx\n"), m68k_getpc ()); #endif - if (fault_if_no_fpu (opcode, 2)) + + if (fault_if_no_6888x (opcode, 0, pc)) return; if (get_fp_ad (opcode, &ad) == 0) { - fpu_op_illg (opcode, 2); + fpu_op_illg (opcode, 0, pc); return; } + if (fault_if_no_fpu (opcode, 0, ad, pc)) + return; + regs.fpiar = pc; + + uae_u32 pad = ad; + if (incr < 0) { + ad -= 4; + d = x_get_long (ad); + } else { + d = x_get_long (ad); + ad += 4; + } + if (currprefs.fpu_model == 68060) { - /* all 68060 FPU frames are 12 bytes */ + int ff = (d >> 8) & 0xff; + uae_u32 v1, v2; + if (incr < 0) { ad -= 4; - d = x_get_long (ad); - ad -= 8; + v1 = x_get_long (ad); + ad -= 4; + v2 = x_get_long (ad); } else { - d = x_get_long (ad); + v1 = x_get_long (ad); + ad += 4; + v2 = x_get_long (ad); ad += 4; - ad += 8; } - - } else if (currprefs.fpu_model == 68040) { - /* 68040 */ - if (incr < 0) { - /* @@@ This may be wrong. */ - ad -= 4; - d = x_get_long (ad); - if ((d & 0xff000000) != 0) { /* Not a NULL frame? */ - if ((d & 0x00ff0000) == 0) { /* IDLE */ - } else if ((d & 0x00ff0000) == 0x00300000) { /* UNIMP */ - ad -= 44; - } else if ((d & 0x00ff0000) == 0x00600000) { /* BUSY */ - ad -= 92; - } - } + if (ff == 0x60) { + regs.fpu_state = 1; + regs.fpu_exp_state = 0; + } else if (ff == 0xe0) { + regs.fpu_exp_state = 1; + to_exten (®s.exp_src1, d & 0xffff0000, v1, v2); + } else if (ff) { + write_log (_T("FRESTORE invalid frame format %X!\n"), (d >> 8) & 0xff); } else { - d = x_get_long (ad); - ad += 4; - if ((d & 0xff000000) != 0) { /* Not a NULL frame? */ - if ((d & 0x00ff0000) == 0) { /* IDLE */ - } else if ((d & 0x00ff0000) == 0x00300000) { /* UNIMP */ - ad += 44; - } else if ((d & 0x00ff0000) == 0x00600000) { /* BUSY */ - ad += 92; - } - } + fpu_null (); } - } else { /* 68881/68882 */ - if (incr < 0) { - ad -= 4; - d = x_get_long (ad); - if ((d & 0xff000000) != 0) { - if ((d & 0x00ff0000) == 0x00180000) - ad -= 6 * 4; - else if ((d & 0x00ff0000) == 0x00380000) - ad -= 14 * 4; - else if ((d & 0x00ff0000) == 0x00b40000) - ad -= 45 * 4; - } + } else { + if ((d & 0xff000000) != 0) { + regs.fpu_state = 1; + if (incr < 0) + ad -= (d >> 16) & 0xff; + else + ad += (d >> 16) & 0xff; } else { - d = x_get_long (ad); - ad += 4; - if ((d & 0xff000000) != 0) { - if ((d & 0x00ff0000) == 0x00180000) - ad += 6 * 4; - else if ((d & 0x00ff0000) == 0x00380000) - ad += 14 * 4; - else if ((d & 0x00ff0000) == 0x00b40000) - ad += 45 * 4; - } + fpu_null (); } } + if ((opcode & 0x38) == 0x18) m68k_areg (regs, opcode & 7) = ad; if ((opcode & 0x38) == 0x20) @@ -1046,33 +1923,162 @@ void fpuop_restore (uae_u32 opcode) static void fround (int reg) { - regs.fp[reg] = (float)regs.fp[reg]; + regs.fp[reg].fp = (float)regs.fp[reg].fp; } -void fpuop_arithmetic (uae_u32 opcode, uae_u16 extra) +static uaecptr fmovem2mem (uaecptr ad, uae_u32 list, int incr, int regdir) { int reg; + + // 68030 MMU state saving is annoying! + if (currprefs.mmu_model == 68030) { + int idx = 0; + uae_u32 wrd[3]; + mmu030_state[1] |= MMU030_STATEFLAG1_MOVEM1; + for (int r = 0; r < 8; r++) { + if (regdir < 0) + reg = 7 - r; + else + reg = r; + if (list & 0x80) { + from_exten(®s.fp[reg], &wrd[0], &wrd[1], &wrd[2]); + if (incr < 0) + ad -= 3 * 4; + for (int i = 0; i < 3; i++) { + if (mmu030_state[0] == idx * 3 + i) { + if (mmu030_state[1] & MMU030_STATEFLAG1_MOVEM2) { + mmu030_state[1] &= ~MMU030_STATEFLAG1_MOVEM2; + } + else { + mmu030_data_buffer = wrd[i]; + x_put_long(ad + i * 4, wrd[i]); + } + mmu030_state[0]++; + } + } + if (incr > 0) + ad += 3 * 4; + idx++; + } + list <<= 1; + } + } else { + for (int r = 0; r < 8; r++) { + uae_u32 wrd1, wrd2, wrd3; + if (regdir < 0) + reg = 7 - r; + else + reg = r; + if (list & 0x80) { + from_exten(®s.fp[reg], &wrd1, &wrd2, &wrd3); + if (incr < 0) + ad -= 3 * 4; + x_put_long(ad + 0, wrd1); + x_put_long(ad + 4, wrd2); + x_put_long(ad + 8, wrd3); + if (incr > 0) + ad += 3 * 4; + } + list <<= 1; + } + } + return ad; +} + +static uaecptr fmovem2fpp (uaecptr ad, uae_u32 list, int incr, int regdir) +{ + int reg; + + if (currprefs.mmu_model == 68030) { + uae_u32 wrd[3]; + int idx = 0; + mmu030_state[1] |= MMU030_STATEFLAG1_MOVEM1 | MMU030_STATEFLAG1_FMOVEM; + if (mmu030_state[1] & MMU030_STATEFLAG1_MOVEM2) + ad = mmu030_ad[mmu030_idx].val; + else + mmu030_ad[mmu030_idx].val = ad; + for (int r = 0; r < 8; r++) { + if (regdir < 0) + reg = 7 - r; + else + reg = r; + if (list & 0x80) { + if (incr < 0) + ad -= 3 * 4; + for (int i = 0; i < 3; i++) { + if (mmu030_state[0] == idx * 3 + i) { + if (mmu030_state[1] & MMU030_STATEFLAG1_MOVEM2) { + mmu030_state[1] &= ~MMU030_STATEFLAG1_MOVEM2; + wrd[i] = mmu030_data_buffer; + } else { + wrd[i] = x_get_long (ad + i * 4); + } + // save first two entries if 2nd or 3rd get_long() faults. + if (i == 0 || i == 1) + mmu030_fmovem_store[i] = wrd[i]; + mmu030_state[0]++; + if (i == 2) + to_exten (®s.fp[reg], mmu030_fmovem_store[0], mmu030_fmovem_store[1], wrd[2]); + } + } + if (incr > 0) + ad += 3 * 4; + idx++; + } + list <<= 1; + } + } else { + for (int r = 0; r < 8; r++) { + uae_u32 wrd1, wrd2, wrd3; + if (regdir < 0) + reg = 7 - r; + else + reg = r; + if (list & 0x80) { + if (incr < 0) + ad -= 3 * 4; + wrd1 = x_get_long (ad + 0); + wrd2 = x_get_long (ad + 4); + wrd3 = x_get_long (ad + 8); + if (incr > 0) + ad += 3 * 4; + to_exten (®s.fp[reg], wrd1, wrd2, wrd3); + } + list <<= 1; + } + } + return ad; +} + +static void fpuop_arithmetic2 (uae_u32 opcode, uae_u16 extra) +{ + int reg = -1; + int v; fptype src; + fpdata srcd; + uaecptr pc = m68k_getpc () - 4; + uaecptr ad = 0; + bool sgl; #if DEBUG_FPP if (!isinrom ()) - write_log ("FPP %04lx %04x at %08lx\n", opcode & 0xffff, extra, m68k_getpc () - 4); + write_log (_T("FPP %04lx %04x at %08lx\n"), opcode & 0xffff, extra, pc); #endif - if (fault_if_no_fpu (opcode, 4)) + if (fault_if_no_6888x (opcode, extra, pc)) return; - switch ((extra >> 13) & 0x7) { - + switch ((extra >> 13) & 0x7) + { case 3: - if (put_fp_value (regs.fp[(extra >> 7) & 7], opcode, extra) == 0) { - m68k_setpc (m68k_getpc () - 4); - op_illg (opcode); - } + if (put_fp_value (®s.fp[(extra >> 7) & 7], opcode, extra, pc) == 0) + fpu_noinst (opcode, pc); return; case 4: case 5: if ((opcode & 0x38) == 0) { + if (fault_if_no_fpu (opcode, extra, 0, pc)) + return; if (extra & 0x2000) { if (extra & 0x1000) m68k_dreg (regs, opcode & 7) = regs.fpcr & 0xffff; @@ -1091,6 +2097,8 @@ void fpuop_arithmetic (uae_u32 opcode, u regs.fpiar = m68k_dreg (regs, opcode & 7); } } else if ((opcode & 0x38) == 0x08) { + if (fault_if_no_fpu (opcode, extra, 0, pc)) + return; if (extra & 0x2000) { if (extra & 0x1000) m68k_areg (regs, opcode & 7) = regs.fpcr & 0xffff; @@ -1109,15 +2117,32 @@ void fpuop_arithmetic (uae_u32 opcode, u regs.fpiar = m68k_areg (regs, opcode & 7); } } else if ((opcode & 0x3f) == 0x3c) { + if (fault_if_no_fpu (opcode, extra, 0, pc)) + return; if ((extra & 0x2000) == 0) { + uae_u32 ext[3]; + // 68060 FMOVEM.L #imm,more than 1 control register: unimplemented EA + uae_u16 bits = extra & (0x1000 | 0x0800 | 0x0400); + if (bits && bits != 0x1000 && bits != 0x0800 && bits != 0x400) { + if (fault_if_60 (opcode, extra, ad, pc, FPU_EXP_UNIMP_EA)) + return; + } + // fetch first, use only after all data has been fetched + ext[0] = ext[1] = ext[2] = 0; + if (extra & 0x1000) + ext[0] = x_cp_next_ilong (); + if (extra & 0x0800) + ext[1] = x_cp_next_ilong (); + if (extra & 0x0400) + ext[2] = x_cp_next_ilong (); if (extra & 0x1000) { - regs.fpcr = x_next_ilong (); + regs.fpcr = ext[0]; native_set_fpucw (regs.fpcr); } if (extra & 0x0800) - set_fpsr (x_next_ilong ()); + set_fpsr (ext[1]); if (extra & 0x0400) - regs.fpiar = x_next_ilong (); + regs.fpiar = ext[2]; } } else if (extra & 0x2000) { /* FMOVEM FPP->memory */ @@ -1125,10 +2150,12 @@ void fpuop_arithmetic (uae_u32 opcode, u int incr = 0; if (get_fp_ad (opcode, &ad) == 0) { - m68k_setpc (m68k_getpc () - 4); - op_illg (opcode); + fpu_noinst (opcode, pc); return; } + if (fault_if_no_fpu (opcode, extra, ad, pc)) + return; + if ((opcode & 0x38) == 0x20) { if (extra & 0x1000) incr += 4; @@ -1139,15 +2166,15 @@ void fpuop_arithmetic (uae_u32 opcode, u } ad -= incr; if (extra & 0x1000) { - x_put_long (ad, regs.fpcr & 0xffff); + x_cp_put_long (ad, regs.fpcr & 0xffff); ad += 4; } if (extra & 0x0800) { - x_put_long (ad, get_fpsr()); + x_cp_put_long (ad, get_fpsr ()); ad += 4; } if (extra & 0x0400) { - x_put_long (ad, regs.fpiar); + x_cp_put_long (ad, regs.fpiar); ad += 4; } ad -= incr; @@ -1161,10 +2188,12 @@ void fpuop_arithmetic (uae_u32 opcode, u int incr = 0; if (get_fp_ad (opcode, &ad) == 0) { - m68k_setpc (m68k_getpc () - 4); - op_illg (opcode); + fpu_noinst (opcode, pc); return; } + if (fault_if_no_fpu (opcode, extra, ad, pc)) + return; + if((opcode & 0x38) == 0x20) { if (extra & 0x1000) incr += 4; @@ -1175,16 +2204,16 @@ void fpuop_arithmetic (uae_u32 opcode, u ad = ad - incr; } if (extra & 0x1000) { - regs.fpcr = x_get_long (ad); + regs.fpcr = x_cp_get_long (ad); native_set_fpucw (regs.fpcr); ad += 4; } if (extra & 0x0800) { - set_fpsr(x_get_long (ad)); + set_fpsr (x_cp_get_long (ad)); ad += 4; } if (extra & 0x0400) { - regs.fpiar = x_get_long (ad); + regs.fpiar = x_cp_get_long (ad); ad += 4; } if ((opcode & 0x38) == 0x18) @@ -1198,484 +2227,398 @@ void fpuop_arithmetic (uae_u32 opcode, u case 7: { uae_u32 ad, list = 0; - int incr = 0; + int incr = 1; + int regdir = 1; + if (get_fp_ad (opcode, &ad) == 0) { + fpu_noinst (opcode, pc); + return; + } + if (fault_if_no_fpu (opcode, extra, ad, pc)) + return; + switch ((extra >> 11) & 3) + { + case 0: /* static pred */ + list = extra & 0xff; + regdir = -1; + break; + case 1: /* dynamic pred */ + if (fault_if_60 (opcode, extra, ad, pc, FPU_EXP_UNIMP_EA)) + return; + list = m68k_dreg (regs, (extra >> 4) & 3) & 0xff; + regdir = -1; + break; + case 2: /* static postinc */ + list = extra & 0xff; + break; + case 3: /* dynamic postinc */ + if (fault_if_60 (opcode, extra, ad, pc, FPU_EXP_UNIMP_EA)) + return; + list = m68k_dreg (regs, (extra >> 4) & 3) & 0xff; + break; + } + if ((opcode & 0x38) == 0x20) // -(an) + incr = -1; if (extra & 0x2000) { /* FMOVEM FPP->memory */ - if (get_fp_ad (opcode, &ad) == 0) { - m68k_setpc (m68k_getpc () - 4); - op_illg (opcode); - return; - } - switch ((extra >> 11) & 3) { - case 0: /* static pred */ - list = extra & 0xff; - incr = -1; - break; - case 1: /* dynamic pred */ - list = m68k_dreg (regs, (extra >> 4) & 3) & 0xff; - incr = -1; - break; - case 2: /* static postinc */ - list = extra & 0xff; - incr = 1; - break; - case 3: /* dynamic postinc */ - list = m68k_dreg (regs, (extra >> 4) & 3) & 0xff; - incr = 1; - break; - } - if (incr < 0) { - for (reg = 7; reg >= 0; reg--) { - uae_u32 wrd1, wrd2, wrd3; - if (list & 0x80) { - from_exten (regs.fp[reg], &wrd1, &wrd2, &wrd3); - ad -= 4; - x_put_long (ad, wrd3); - ad -= 4; - x_put_long (ad, wrd2); - ad -= 4; - x_put_long (ad, wrd1); - } - list <<= 1; - } - } else { - for (reg = 0; reg <= 7; reg++) { - uae_u32 wrd1, wrd2, wrd3; - if (list & 0x80) { - from_exten (regs.fp[reg], &wrd1, &wrd2, &wrd3); - x_put_long (ad, wrd1); - ad += 4; - x_put_long (ad, wrd2); - ad += 4; - x_put_long (ad, wrd3); - ad += 4; - } - list <<= 1; - } - } - if ((opcode & 0x38) == 0x18) - m68k_areg (regs, opcode & 7) = ad; - if ((opcode & 0x38) == 0x20) - m68k_areg (regs, opcode & 7) = ad; + ad = fmovem2mem (ad, list, incr, regdir); } else { /* FMOVEM memory->FPP */ - if (get_fp_ad (opcode, &ad) == 0) { - m68k_setpc (m68k_getpc () - 4); - op_illg (opcode); - return; - } - switch ((extra >> 11) & 3) { - case 0: /* static pred */ - list = extra & 0xff; - incr = -1; - break; - case 1: /* dynamic pred */ - list = m68k_dreg (regs, (extra >> 4) & 3) & 0xff; - incr = -1; - break; - case 2: /* static postinc */ - list = extra & 0xff; - incr = 1; - break; - case 3: /* dynamic postinc */ - list = m68k_dreg (regs, (extra >> 4) & 3) & 0xff; - incr = 1; - break; - } - if (incr < 0) { - for (reg = 7; reg >= 0; reg--) { - uae_u32 wrd1, wrd2, wrd3; - if (list & 0x80) { - ad -= 4; - wrd3 = x_get_long (ad); - ad -= 4; - wrd2 = x_get_long (ad); - ad -= 4; - wrd1 = x_get_long (ad); - regs.fp[reg] = to_exten(wrd1, wrd2, wrd3); - } - list <<= 1; - } - } else { - for (reg = 0; reg <= 7; reg++) { - uae_u32 wrd1, wrd2, wrd3; - if (list & 0x80) { - wrd1 = x_get_long (ad); - ad += 4; - wrd2 = x_get_long (ad); - ad += 4; - wrd3 = x_get_long (ad); - ad += 4; - regs.fp[reg] = to_exten(wrd1, wrd2, wrd3); - } - list <<= 1; - } - } - if ((opcode & 0x38) == 0x18) - m68k_areg (regs, opcode & 7) = ad; - if ((opcode & 0x38) == 0x20) - m68k_areg (regs, opcode & 7) = ad; + ad = fmovem2fpp (ad, list, incr, regdir); } + if ((opcode & 0x38) == 0x18 || (opcode & 0x38) == 0x20) + m68k_areg (regs, opcode & 7) = ad; } return; case 0: case 2: /* Extremely common */ + regs.fpiar = pc; reg = (extra >> 7) & 7; +#ifdef USE_SOFT_LONG_DOUBLE + regs.fp[reg].fpx = false; +#endif if ((extra & 0xfc00) == 0x5c00) { - switch (extra & 0x7f) { - case 0x00: - regs.fp[reg] = *fp_pi; - break; - case 0x0b: - regs.fp[reg] = *fp_l10_2; - break; - case 0x0c: - regs.fp[reg] = *fp_exp_1; - break; - case 0x0d: - regs.fp[reg] = *fp_l2_e; - break; - case 0x0e: - regs.fp[reg] = *fp_l10_e; - break; - case 0x0f: - regs.fp[reg] = 0.0; - break; - case 0x30: - regs.fp[reg] = *fp_ln_2; - break; - case 0x31: - regs.fp[reg] = *fp_ln_10; - break; - case 0x32: - regs.fp[reg] = (fptype)fp_1e0; - break; - case 0x33: - regs.fp[reg] = (fptype)fp_1e1; - break; - case 0x34: - regs.fp[reg] = (fptype)fp_1e2; - break; - case 0x35: - regs.fp[reg] = (fptype)fp_1e4; - break; - case 0x36: - regs.fp[reg] = (fptype)fp_1e8; - break; - case 0x37: - regs.fp[reg] = *fp_1e16; - break; - case 0x38: - regs.fp[reg] = *fp_1e32; - break; - case 0x39: - regs.fp[reg] = *fp_1e64; - break; - case 0x3a: - regs.fp[reg] = *fp_1e128; - break; - case 0x3b: - regs.fp[reg] = *fp_1e256; - break; - case 0x3c: - regs.fp[reg] = *fp_1e512; - break; - case 0x3d: - regs.fp[reg] = *fp_1e1024; - break; - case 0x3e: - regs.fp[reg] = *fp_1e2048; - break; - case 0x3f: - regs.fp[reg] = *fp_1e4096; - break; - default: - m68k_setpc (m68k_getpc () - 4); - op_illg (opcode); - return; + if (fault_if_no_fpu (opcode, extra, 0, pc)) + return; + if (fault_if_unimplemented_680x0 (opcode, extra, ad, pc, &srcd, reg)) + return; + CLEAR_STATUS (); + if (!fpu_get_constant(®s.fp[reg], extra)) { + fpu_noinst(opcode, pc); + return; } - MAKE_FPSR (regs.fp[reg]); + MAKE_FPSR (®s.fp[reg].fp); return; } - if (get_fp_value (opcode, extra, &src) == 0) { - m68k_setpc (m68k_getpc () - 4); - op_illg (opcode); + + // 6888x does not have special exceptions, check immediately + if (fault_if_unimplemented_6888x (opcode, extra, pc)) + return; + + v = get_fp_value (opcode, extra, &srcd, pc, &ad); + if (v <= 0) { + if (v == 0) + fpu_noinst (opcode, pc); return; } + src = srcd.fp; - switch (extra & 0x7f) { + // get_fp_value() checked this, but only if EA was nonzero (non-register) + if (fault_if_unimplemented_680x0 (opcode, extra, ad, pc, &srcd, reg)) + return; - case 0x00: /* FMOVE */ - case 0x40: /* Explicit rounding. This is just a quick fix. */ - case 0x44: /* Same for all other cases that have three choices */ - regs.fp[reg] = src; /* Brian King was here. */ - /* to register needs FPSR updated. See Motorola 68K Manual. */ - if ((extra & 0x44) == 0x40) - fround (reg); - break; - case 0x01: /* FINT */ - /* need to take the current rounding mode into account */ -#if defined(X86_MSVC_ASSEMBLY) - { - fptype tmp_fp; + regs.fpiar = pc; - __asm { - fld LDPTR src - frndint - fstp LDPTR tmp_fp - } - regs.fp[reg] = tmp_fp; - } + CLEAR_STATUS (); + sgl = false; + switch (extra & 0x7f) + { + case 0x00: /* FMOVE */ + case 0x40: /* Explicit rounding. This is just a quick fix. */ + case 0x44: /* Same for all other cases that have three choices */ + regs.fp[reg].fp = src; /* Brian King was here. */ + /* to register needs FPSR updated. See Motorola 68K Manual. */ + break; + case 0x01: /* FINT */ + /* need to take the current rounding mode into account */ +#if defined(X86_MSVC_ASSEMBLY_FPU) + { + fptype tmp_fp; + + __asm { + fld LDPTR src + frndint + fstp LDPTR tmp_fp + } + regs.fp[reg].fp = tmp_fp; + } #else /* no X86_MSVC */ - switch ((regs.fpcr >> 4) & 3) { - case 0: /* to nearest */ - regs.fp[reg] = floor (src + 0.5); - break; - case 1: /* to zero */ - if (src >= 0.0) - regs.fp[reg] = floor (src); - else - regs.fp[reg] = ceil (src); - break; - case 2: /* down */ - regs.fp[reg] = floor (src); - break; - case 3: /* up */ - regs.fp[reg] = ceil (src); - break; - default: /* never reached */ - regs.fp[reg] = src; - } + switch (regs.fpcr & 0x30) + { + case FPCR_ROUND_NEAR: + regs.fp[reg].fp = fp_round_to_nearest(src); + break; + case FPCR_ROUND_ZERO: + regs.fp[reg].fp = fp_round_to_zero(src); + break; + case FPCR_ROUND_MINF: + regs.fp[reg].fp = fp_round_to_minus_infinity(src); + break; + case FPCR_ROUND_PINF: + regs.fp[reg].fp = fp_round_to_plus_infinity(src); + break; + default: /* never reached */ + regs.fp[reg].fp = src; + break; + } #endif /* X86_MSVC */ - break; - case 0x02: /* FSINH */ - regs.fp[reg] = sinh (src); - break; - case 0x03: /* FINTRZ */ - regs.fp[reg] = fp_round_to_zero(src); - break; - case 0x04: /* FSQRT */ - case 0x41: - case 0x45: - regs.fp[reg] = sqrt (src); - if ((extra & 0x44) == 0x40) - fround (reg); - break; - case 0x06: /* FLOGNP1 */ - regs.fp[reg] = log (src + 1.0); - break; - case 0x08: /* FETOXM1 */ - regs.fp[reg] = exp (src) - 1.0; - break; - case 0x09: /* FTANH */ - regs.fp[reg] = tanh (src); - break; - case 0x0a: /* FATAN */ - regs.fp[reg] = atan (src); - break; - case 0x0c: /* FASIN */ - regs.fp[reg] = asin (src); - break; - case 0x0d: /* FATANH */ -#if 1 /* The BeBox doesn't have atanh, and it isn't in the HPUX libm either */ - regs.fp[reg] = 0.5 * log ((1 + src) / (1 - src)); -#else - regs.fp[reg] = atanh (src); -#endif - break; - case 0x0e: /* FSIN */ - regs.fp[reg] = sin (src); - break; - case 0x0f: /* FTAN */ - regs.fp[reg] = tan (src); - break; - case 0x10: /* FETOX */ - regs.fp[reg] = exp (src); - break; - case 0x11: /* FTWOTOX */ - regs.fp[reg] = pow (2.0, src); - break; - case 0x12: /* FTENTOX */ - regs.fp[reg] = pow (10.0, src); - break; - case 0x14: /* FLOGN */ - regs.fp[reg] = log (src); - break; - case 0x15: /* FLOG10 */ - regs.fp[reg] = log10 (src); - break; - case 0x16: /* FLOG2 */ - regs.fp[reg] = *fp_l2_e * log (src); - break; - case 0x18: /* FABS */ - case 0x58: - case 0x5c: - regs.fp[reg] = src < 0 ? -src : src; - if ((extra & 0x44) == 0x40) - fround (reg); - break; - case 0x19: /* FCOSH */ - regs.fp[reg] = cosh (src); - break; - case 0x1a: /* FNEG */ - case 0x5a: - case 0x5e: - regs.fp[reg] = -src; - if ((extra & 0x44) == 0x40) - fround (reg); - break; - case 0x1c: /* FACOS */ - regs.fp[reg] = acos (src); - break; - case 0x1d: /* FCOS */ - regs.fp[reg] = cos (src); - break; - case 0x1e: /* FGETEXP */ - { - if (src == 0) { - regs.fp[reg] = 0; - } else { - int expon; - frexp (src, &expon); - regs.fp[reg] = (double) (expon - 1); - } - } - break; - case 0x1f: /* FGETMAN */ - { - if (src == 0) { - regs.fp[reg] = 0; - } else { - int expon; - regs.fp[reg] = frexp (src, &expon) * 2.0; - } - } - break; - case 0x20: /* FDIV */ - case 0x60: - case 0x64: - regs.fp[reg] /= src; - if ((extra & 0x44) == 0x40) - fround (reg); - break; - case 0x21: /* FMOD */ - { - fptype quot = fp_round_to_zero(regs.fp[reg] / src); - regs.fp[reg] = regs.fp[reg] - quot * src; - } - break; - case 0x22: /* FADD */ - case 0x62: - case 0x66: - regs.fp[reg] += src; - if ((extra & 0x44) == 0x40) - fround (reg); - break; - case 0x23: /* FMUL */ - case 0x63: - case 0x67: - regs.fp[reg] *= src; - if ((extra & 0x44) == 0x40) - fround (reg); - break; - case 0x24: /* FSGLDIV */ - regs.fp[reg] /= src; - break; - case 0x25: /* FREM */ - { - fptype quot = fp_round_to_nearest(regs.fp[reg] / src); - regs.fp[reg] = regs.fp[reg] - quot * src; - } - break; - case 0x26: /* FSCALE */ - if (src != 0) { + break; + case 0x02: /* FSINH */ + regs.fp[reg].fp = sinh (src); + break; + case 0x03: /* FINTRZ */ + regs.fp[reg].fp = fp_round_to_zero (src); + break; + case 0x04: /* FSQRT */ + case 0x41: /* FSSQRT */ + case 0x45: /* FDSQRT */ + regs.fp[reg].fp = sqrt (src); + break; + case 0x06: /* FLOGNP1 */ + regs.fp[reg].fp = log (src + 1.0); + break; + case 0x08: /* FETOXM1 */ + regs.fp[reg].fp = exp (src) - 1.0; + break; + case 0x09: /* FTANH */ + regs.fp[reg].fp = tanh (src); + break; + case 0x0a: /* FATAN */ + regs.fp[reg].fp = atan (src); + break; + case 0x0c: /* FASIN */ + regs.fp[reg].fp = asin (src); + break; + case 0x0d: /* FATANH */ + regs.fp[reg].fp = atanh (src); + break; + case 0x0e: /* FSIN */ + regs.fp[reg].fp = sin (src); + break; + case 0x0f: /* FTAN */ + regs.fp[reg].fp = tan (src); + break; + case 0x10: /* FETOX */ + regs.fp[reg].fp = exp (src); + break; + case 0x11: /* FTWOTOX */ + regs.fp[reg].fp = pow (2.0, src); + break; + case 0x12: /* FTENTOX */ + regs.fp[reg].fp = pow (10.0, src); + break; + case 0x14: /* FLOGN */ + regs.fp[reg].fp = log (src); + break; + case 0x15: /* FLOG10 */ + regs.fp[reg].fp = log10 (src); + break; + case 0x16: /* FLOG2 */ + regs.fp[reg].fp = *fp_l2_e * log (src); + break; + case 0x18: /* FABS */ + case 0x58: /* FSABS */ + case 0x5c: /* FDABS */ + regs.fp[reg].fp = src < 0 ? -src : src; + break; + case 0x19: /* FCOSH */ + regs.fp[reg].fp = cosh (src); + break; + case 0x1a: /* FNEG */ + case 0x5a: /* FSNEG */ + case 0x5e: /* FDNEG */ + regs.fp[reg].fp = -src; + break; + case 0x1c: /* FACOS */ + regs.fp[reg].fp = acos (src); + break; + case 0x1d: /* FCOS */ + regs.fp[reg].fp = cos (src); + break; + case 0x1e: /* FGETEXP */ + { + if (src == 0) { + regs.fp[reg].fp = 0; + } else { + int expon; + frexp (src, &expon); + regs.fp[reg].fp = (double) (expon - 1); + } + } + break; + case 0x1f: /* FGETMAN */ + { + if (src == 0) { + regs.fp[reg].fp = 0; + } else { + int expon; + regs.fp[reg].fp = frexp (src, &expon) * 2.0; + } + } + break; + case 0x20: /* FDIV */ + case 0x60: /* FSDIV */ + case 0x64: /* FDDIV */ + regs.fp[reg].fp /= src; + break; + case 0x21: /* FMOD */ + { + fptype quot = fp_round_to_zero(regs.fp[reg].fp / src); + regs.fp[reg].fp = regs.fp[reg].fp - quot * src; + } + break; + case 0x22: /* FADD */ + case 0x62: /* FSADD */ + case 0x66: /* FDADD */ + regs.fp[reg].fp += src; + break; + case 0x23: /* FMUL */ + case 0x63: /* FSMUL */ + case 0x67: /* FDMUL */ + regs.fp[reg].fp *= src; + break; + case 0x24: /* FSGLDIV */ + regs.fp[reg].fp /= src; + sgl = true; + break; + case 0x25: /* FREM */ + { + fptype quot = fp_round_to_nearest(regs.fp[reg].fp / src); + regs.fp[reg].fp = regs.fp[reg].fp - quot * src; + } + break; + case 0x26: /* FSCALE */ + if (src != 0) { #ifdef ldexp - regs.fp[reg] = ldexp (regs.fp[reg], (int) src); + regs.fp[reg] = ldexp (regs.fp[reg], (int) src); #else - regs.fp[reg] *= exp (*fp_ln_2 * (int) src); + regs.fp[reg].fp *= exp (*fp_ln_2 * (int) src); #endif + } + break; + case 0x27: /* FSGLMUL */ + regs.fp[reg].fp *= src; + sgl = true; + break; + case 0x28: /* FSUB */ + case 0x68: /* FSSUB */ + case 0x6c: /* FDSUB */ + regs.fp[reg].fp -= src; + break; + case 0x30: /* FSINCOS */ + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + regs.fp[extra & 7].fp = cos (src); + regs.fp[reg].fp = sin (src); + break; + case 0x38: /* FCMP */ + { + fptype tmp = regs.fp[reg].fp - src; + regs.fpsr = 0; + MAKE_FPSR (&tmp); + } + return; + case 0x3a: /* FTST */ + regs.fpsr = 0; + MAKE_FPSR (&src); + return; + default: + fpu_noinst (opcode, pc); + return; } - break; - case 0x27: /* FSGLMUL */ - regs.fp[reg] *= src; - break; - case 0x28: /* FSUB */ - case 0x68: - case 0x6c: - regs.fp[reg] -= src; - if ((extra & 0x44) == 0x40) + // round to float? + if (sgl || (extra & 0x44) == 0x40) fround (reg); - break; - case 0x30: /* FSINCOS */ - case 0x31: - case 0x32: - case 0x33: - case 0x34: - case 0x35: - case 0x36: - case 0x37: - regs.fp[extra & 7] = cos (src); - regs.fp[reg] = sin (src); - break; - case 0x38: /* FCMP */ - { - fptype tmp = regs.fp[reg] - src; - regs.fpsr = 0; - MAKE_FPSR (tmp); - } - return; - case 0x3a: /* FTST */ - regs.fpsr = 0; - MAKE_FPSR (src); + MAKE_FPSR (®s.fp[reg].fp); return; default: - m68k_setpc (m68k_getpc () - 4); - op_illg (opcode); - return; + break; + } + fpu_noinst (opcode, pc); +} + +void fpuop_arithmetic (uae_u32 opcode, uae_u16 extra) +{ + regs.fpu_state = 1; + regs.fp_exception = false; + fpu_mmu_fixup = false; + fpuop_arithmetic2 (opcode, extra); + if (fpu_mmu_fixup) { + mmufixup[0].reg = -1; + } +#if 0 + // Any exception status bit and matching exception enable bits set? + if ((regs.fpcr >> 8) & (regs.fpsr >> 8)) { + uae_u32 mask = regs.fpcr >> 8; + int vector = 0; + for (int i = 7; i >= 0; i--) { + if (mask & (1 << i)) { + if (i > 0) + i--; + vector = i + 48; + break; } - MAKE_FPSR (regs.fp[reg]); - return; + } + // logging only so far + write_log (_T("FPU exception: %08x %d!\n"), regs.fpsr, vector); } - m68k_setpc (m68k_getpc () - 4); - op_illg (opcode); +#endif } void fpu_reset (void) { regs.fpcr = regs.fpsr = regs.fpiar = 0; - regs.fp_result = 1; + regs.fpu_exp_state = 0; + fpset (®s.fp_result, 1); + native_set_fpucw (regs.fpcr); fpux_restore (NULL); } uae_u8 *restore_fpu (uae_u8 *src) { + uae_u32 w1, w2, w3; int i; uae_u32 flags; changed_prefs.fpu_model = currprefs.fpu_model = restore_u32 (); flags = restore_u32 (); for (i = 0; i < 8; i++) { - uae_u32 w1 = restore_u32 (); - uae_u32 w2 = restore_u32 (); - uae_u32 w3 = restore_u16 (); - regs.fp[i] = to_exten (w1, w2, w3); + w1 = restore_u32 (); + w2 = restore_u32 (); + w3 = restore_u16 (); + to_exten (®s.fp[i], w1, w2, w3); } regs.fpcr = restore_u32 (); native_set_fpucw (regs.fpcr); regs.fpsr = restore_u32 (); regs.fpiar = restore_u32 (); if (flags & 0x80000000) { - restore_u32(); - restore_u32(); + restore_u32 (); + restore_u32 (); } - write_log ("FPU: %d\n", currprefs.fpu_model); + if (flags & 0x40000000) { + w1 = restore_u32(); + w2 = restore_u32(); + w3 = restore_u16(); + to_exten(®s.exp_src1, w1, w2, w3); + w1 = restore_u32(); + w2 = restore_u32(); + w3 = restore_u16(); + to_exten(®s.exp_src2, w1, w2, w3); + regs.exp_pack[0] = restore_u32(); + regs.exp_pack[1] = restore_u32(); + regs.exp_pack[2] = restore_u32(); + regs.exp_opcode = restore_u16(); + regs.exp_extra = restore_u16(); + regs.exp_type = restore_u16(); + } + regs.fpu_state = (flags & 1) ? 0 : 1; + regs.fpu_exp_state = (flags & 2) ? 1 : 0; + if (flags & 4) + regs.fpu_exp_state = 2; + write_log(_T("FPU: %d\n"), currprefs.fpu_model); return src; } uae_u8 *save_fpu (int *len, uae_u8 *dstptr) { - uae_u8 *dstbak,*dst; + uae_u32 w1, w2, w3; + uae_u8 *dstbak, *dst; int i; *len = 0; @@ -1684,12 +2627,11 @@ uae_u8 *save_fpu (int *len, uae_u8 *dstp if (dstptr) dstbak = dst = dstptr; else - dstbak = dst = xmalloc (uae_u8, 4+4+8*10+4+4+4+4+4); + dstbak = dst = xmalloc (uae_u8, 4+4+8*10+4+4+4+4+4+2*10+3*(4+2)); save_u32 (currprefs.fpu_model); - save_u32 (0x80000000); + save_u32 (0x80000000 | 0x40000000 | (regs.fpu_state == 0 ? 1 : 0) | (regs.fpu_exp_state ? 2 : 0) | (regs.fpu_exp_state > 1 ? 4 : 0)); for (i = 0; i < 8; i++) { - uae_u32 w1, w2, w3; - from_exten (regs.fp[i], &w1, &w2, &w3); + from_exten (®s.fp[i], &w1, &w2, &w3); save_u32 (w1); save_u32 (w2); save_u16 (w3); @@ -1697,8 +2639,29 @@ uae_u8 *save_fpu (int *len, uae_u8 *dstp save_u32 (regs.fpcr); save_u32 (regs.fpsr); save_u32 (regs.fpiar); + save_u32 (-1); save_u32 (0); + + from_exten(®s.exp_src1, &w1, &w2, &w3); + save_u32(w1); + save_u32(w2); + save_u16(w3); + from_exten(®s.exp_src2, &w1, &w2, &w3); + save_u32(w1); + save_u32(w2); + save_u16(w3); + save_u32(regs.exp_pack[0]); + save_u32(regs.exp_pack[1]); + save_u32(regs.exp_pack[2]); + save_u16(regs.exp_opcode); + save_u16(regs.exp_extra); + save_u16(regs.exp_type); + *len = dst - dstbak; return dstbak; } + +#ifdef _MSC_VER +#pragma fenv_access(off) +#endif