--- previous/src/cpu/fpp.c 2018/04/24 19:30:17 1.1.1.3 +++ previous/src/cpu/fpp.c 2018/04/24 19:31:53 1.1.1.4 @@ -1,822 +1,1063 @@ /* -* UAE - The Un*x Amiga Emulator -* -* 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 + * UAE - The Un*x Amiga Emulator + * + * MC68881/68882/68040/68060 FPU emulation + * + * Copyright 1996 Herman ten Brugge + * Modified 2005 Peter Keunecke + * 68040+ exceptions and more by Toni Wilen + */ + +#include "main.h" +#include "hatari-glue.h" + #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 "md-fpp.h" -#include "savestate.h" -#include "cpu_prefetch.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 +#ifdef WITH_SOFTFLOAT +#include "fpp-softfloat.h" +#else +#include "md-fpp.h" #endif #define DEBUG_FPP 0 #define EXCEPTION_FPP 0 +#define ARITHMETIC_EXCEPTIONS 1 -STATIC_INLINE int isinrom (void) -{ - return (munge24 (m68k_getpc ()) & 0xFFF80000) == 0xF80000 && !currprefs.mmu_model; -} +static int warned = 100; -static uae_u32 xhex_pi[] ={0x2168c235, 0xc90fdaa2, 0x4000}; -uae_u32 xhex_exp_1[] ={0xa2bb4a9a, 0xadf85458, 0x4000}; -static uae_u32 xhex_l2_e[] ={0x5c17f0bc, 0xb8aa3b29, 0x3fff}; -static uae_u32 xhex_ln_2[] ={0xd1cf79ac, 0xb17217f7, 0x3ffe}; -uae_u32 xhex_ln_10[] ={0xaaa8ac17, 0x935d8ddd, 0x4000}; -uae_u32 xhex_l10_2[] ={0xfbcff798, 0x9a209a84, 0x3ffd}; -uae_u32 xhex_l10_e[] ={0x37287195, 0xde5bd8a9, 0x3ffd}; -uae_u32 xhex_1e16[] ={0x04000000, 0x8e1bc9bf, 0x4034}; -uae_u32 xhex_1e32[] ={0x2b70b59e, 0x9dc5ada8, 0x4069}; -uae_u32 xhex_1e64[] ={0xffcfa6d5, 0xc2781f49, 0x40d3}; -uae_u32 xhex_1e128[] ={0x80e98ce0, 0x93ba47c9, 0x41a8}; -uae_u32 xhex_1e256[] ={0x9df9de8e, 0xaa7eebfb, 0x4351}; -uae_u32 xhex_1e512[] ={0xa60e91c7, 0xe319a0ae, 0x46a3}; -uae_u32 xhex_1e1024[]={0x81750c17, 0xc9767586, 0x4d48}; -uae_u32 xhex_1e2048[]={0xc53d5de5, 0x9e8b3b5d, 0x5a92}; -uae_u32 xhex_1e4096[]={0x8a20979b, 0xc4605202, 0x7525}; -static uae_u32 xhex_inf[] ={0x00000000, 0x00000000, 0x7fff}; -static uae_u32 xhex_nan[] ={0xffffffff, 0xffffffff, 0x7fff}; -#if USE_LONG_DOUBLE -static long double *fp_pi = (long double *)xhex_pi; -static long double *fp_exp_1 = (long double *)xhex_exp_1; -static long double *fp_l2_e = (long double *)xhex_l2_e; -static long double *fp_ln_2 = (long double *)xhex_ln_2; -static long double *fp_ln_10 = (long double *)xhex_ln_10; -static long double *fp_l10_2 = (long double *)xhex_l10_2; -static long double *fp_l10_e = (long double *)xhex_l10_e; -static long double *fp_1e16 = (long double *)xhex_1e16; -static long double *fp_1e32 = (long double *)xhex_1e32; -static long double *fp_1e64 = (long double *)xhex_1e64; -static long double *fp_1e128 = (long double *)xhex_1e128; -static long double *fp_1e256 = (long double *)xhex_1e256; -static long double *fp_1e512 = (long double *)xhex_1e512; -static long double *fp_1e1024 = (long double *)xhex_1e1024; -static long double *fp_1e2048 = (long double *)xhex_1e2048; -static long double *fp_1e4096 = (long double *)xhex_1e4096; -static long double *fp_inf = (long double *)xhex_inf; -static long double *fp_nan = (long double *)xhex_nan; -#else -static uae_u32 dhex_pi[] ={0x54442D18, 0x400921FB}; -static uae_u32 dhex_exp_1[] ={0x8B145769, 0x4005BF0A}; -static uae_u32 dhex_l2_e[] ={0x652B82FE, 0x3FF71547}; -static uae_u32 dhex_ln_2[] ={0xFEFA39EF, 0x3FE62E42}; -static uae_u32 dhex_ln_10[] ={0xBBB55516, 0x40026BB1}; -static uae_u32 dhex_l10_2[] ={0x509F79FF, 0x3FD34413}; -static uae_u32 dhex_l10_e[] ={0x1526E50E, 0x3FDBCB7B}; -static uae_u32 dhex_1e16[] ={0x37E08000, 0x4341C379}; -static uae_u32 dhex_1e32[] ={0xB5056E17, 0x4693B8B5}; -static uae_u32 dhex_1e64[] ={0xE93FF9F5, 0x4D384F03}; -static uae_u32 dhex_1e128[] ={0xF9301D32, 0x5A827748}; -static uae_u32 dhex_1e256[] ={0x7F73BF3C, 0x75154FDD}; -static uae_u32 dhex_inf[] ={0x00000000, 0x7ff00000}; -static uae_u32 dhex_nan[] ={0xffffffff, 0x7fffffff}; -static double *fp_pi = (double *)dhex_pi; -static double *fp_exp_1 = (double *)dhex_exp_1; -static double *fp_l2_e = (double *)dhex_l2_e; -static double *fp_ln_2 = (double *)dhex_ln_2; -static double *fp_ln_10 = (double *)dhex_ln_10; -static double *fp_l10_2 = (double *)dhex_l10_2; -static double *fp_l10_e = (double *)dhex_l10_e; -static double *fp_1e16 = (double *)dhex_1e16; -static double *fp_1e32 = (double *)dhex_1e32; -static double *fp_1e64 = (double *)dhex_1e64; -static double *fp_1e128 = (double *)dhex_1e128; -static double *fp_1e256 = (double *)dhex_1e256; -static double *fp_1e512 = (double *)dhex_inf; -static double *fp_1e1024 = (double *)dhex_inf; -static double *fp_1e2048 = (double *)dhex_inf; -static double *fp_1e4096 = (double *)dhex_inf; -static double *fp_inf = (double *)dhex_inf; -static double *fp_nan = (double *)dhex_nan; -#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 +struct fpp_cr_entry { + uae_u32 val[3]; + uae_u8 inexact; + uae_s8 rndoff[4]; +}; -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 const struct fpp_cr_entry fpp_cr[22] = { + { {0x40000000, 0xc90fdaa2, 0x2168c235}, 1, {0,-1,-1, 0} }, // 0 = pi + { {0x3ffd0000, 0x9a209a84, 0xfbcff798}, 1, {0, 0, 0, 1} }, // 1 = log10(2) + { {0x40000000, 0xadf85458, 0xa2bb4a9a}, 1, {0, 0, 0, 1} }, // 2 = e + { {0x3fff0000, 0xb8aa3b29, 0x5c17f0bc}, 1, {0,-1,-1, 0} }, // 3 = log2(e) + { {0x3ffd0000, 0xde5bd8a9, 0x37287195}, 0, {0, 0, 0, 0} }, // 4 = log10(e) + { {0x00000000, 0x00000000, 0x00000000}, 0, {0, 0, 0, 0} }, // 5 = 0.0 + { {0x3ffe0000, 0xb17217f7, 0xd1cf79ac}, 1, {0,-1,-1, 0} }, // 6 = ln(2) + { {0x40000000, 0x935d8ddd, 0xaaa8ac17}, 1, {0,-1,-1, 0} }, // 7 = ln(10) + { {0x3fff0000, 0x80000000, 0x00000000}, 0, {0, 0, 0, 0} }, // 8 = 1e0 + { {0x40020000, 0xa0000000, 0x00000000}, 0, {0, 0, 0, 0} }, // 9 = 1e1 + { {0x40050000, 0xc8000000, 0x00000000}, 0, {0, 0, 0, 0} }, // 10 = 1e2 + { {0x400c0000, 0x9c400000, 0x00000000}, 0, {0, 0, 0, 0} }, // 11 = 1e4 + { {0x40190000, 0xbebc2000, 0x00000000}, 0, {0, 0, 0, 0} }, // 12 = 1e8 + { {0x40340000, 0x8e1bc9bf, 0x04000000}, 0, {0, 0, 0, 0} }, // 13 = 1e16 + { {0x40690000, 0x9dc5ada8, 0x2b70b59e}, 1, {0,-1,-1, 0} }, // 14 = 1e32 + { {0x40d30000, 0xc2781f49, 0xffcfa6d5}, 1, {0, 0, 0, 1} }, // 15 = 1e64 + { {0x41a80000, 0x93ba47c9, 0x80e98ce0}, 1, {0,-1,-1, 0} }, // 16 = 1e128 + { {0x43510000, 0xaa7eebfb, 0x9df9de8e}, 1, {0,-1,-1, 0} }, // 17 = 1e256 + { {0x46a30000, 0xe319a0ae, 0xa60e91c7}, 1, {0,-1,-1, 0} }, // 18 = 1e512 + { {0x4d480000, 0xc9767586, 0x81750c17}, 1, {0, 0, 0, 1} }, // 19 = 1e1024 + { {0x5a920000, 0x9e8b3b5d, 0xc53d5de5}, 1, {0,-1,-1, 0} }, // 20 = 1e2048 + { {0x75250000, 0xc4605202, 0x8a20979b}, 1, {0,-1,-1, 0} } // 21 = 1e4096 +}; -STATIC_INLINE void CLEAR_STATUS (void) -{ - feclearexcept (FE_ALL_EXCEPT); -} +#define FPP_CR_PI 0 +#define FPP_CR_LOG10_2 1 +#define FPP_CR_E 2 +#define FPP_CR_LOG2_E 3 +#define FPP_CR_LOG10_E 4 +#define FPP_CR_ZERO 5 +#define FPP_CR_LN_2 6 +#define FPP_CR_LN_10 7 +#define FPP_CR_1E0 8 +#define FPP_CR_1E1 9 +#define FPP_CR_1E2 10 +#define FPP_CR_1E4 11 +#define FPP_CR_1E8 12 +#define FPP_CR_1E16 13 +#define FPP_CR_1E32 14 +#define FPP_CR_1E64 15 +#define FPP_CR_1E128 16 +#define FPP_CR_1E256 17 +#define FPP_CR_1E512 18 +#define FPP_CR_1E1024 19 +#define FPP_CR_1E2048 20 +#define FPP_CR_1E4096 21 -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 -} +struct fpp_cr_entry_undef { + uae_u32 val[3]; +}; -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 -} +#define FPP_CR_NUM_SPECIAL_UNDEFINED 10 -#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 - -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 */ +// 68881 and 68882 have identical undefined fields +static const struct fpp_cr_entry_undef fpp_cr_undef[FPP_CR_NUM_SPECIAL_UNDEFINED+1] = { + { {0x40000000, 0x00000000, 0x00000000} }, + { {0x40010000, 0xfe000682, 0x00000000} }, + { {0x40010000, 0xffc00503, 0x80000000} }, + { {0x20000000, 0x7fffffff, 0x00000000} }, + { {0x00000000, 0xffffffff, 0xffffffff} }, + { {0x3c000000, 0xffffffff, 0xfffff800} }, + { {0x3f800000, 0xffffff00, 0x00000000} }, + { {0x00010000, 0xf65d8d9c, 0x00000000} }, + { {0x7fff0000, 0x001e0000, 0x00000000} }, + { {0x43ff0000, 0x000e0000, 0x00000000} }, + { {0x407f0000, 0x00060000, 0x00000000} } }; -#if USE_X86_FPUCW - uae_u16 x87_cw = x87_cw_tab[(m68k_cw >> 4) & 0xf]; -#if defined(X86_MSVC_ASSEMBLY) - __asm { - fldcw word ptr x87_cw - } -#elif defined(X86_ASSEMBLY) - __asm__ ("fldcw %0" : : "m" (*&x87_cw)); -#endif -#endif -#endif -#endif -} +uae_u32 xhex_nan[] ={0x7fff0000, 0xffffffff, 0xffffffff}; -#if defined(uae_s64) /* Close enough for government work? */ -typedef uae_s64 tointtype; -#else -typedef uae_s32 tointtype; -#endif +static bool fpu_mmu_fixup; -static void fpu_format_error (void) -{ - 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), 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), 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), ea); - m68k_areg (regs, 7) -= 2; - 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); + +/* Floating Point Control Register (FPCR) + * + * Exception Enable Byte + * x--- ---- ---- ---- bit 15: BSUN (branch/set on unordered) + * -x-- ---- ---- ---- bit 14: SNAN (signaling not a number) + * --x- ---- ---- ---- bit 13: OPERR (operand error) + * ---x ---- ---- ---- bit 12: OVFL (overflow) + * ---- x--- ---- ---- bit 11: UNFL (underflow) + * ---- -x-- ---- ---- bit 10: DZ (divide by zero) + * ---- --x- ---- ---- bit 9: INEX 2 (inexact operation) + * ---- ---x ---- ---- bit 8: INEX 1 (inexact decimal input) + * + * Mode Control Byte + * ---- ---- xx-- ---- bits 7 and 6: PREC (rounding precision) + * ---- ---- --xx ---- bits 5 and 4: RND (rounding mode) + * ---- ---- ---- xxxx bits 3 to 0: all 0 + */ + +#define FPCR_PREC 0x00C0 +#define FPCR_RND 0x0030 + +/* Floating Point Status Register (FPSR) + * + * Condition Code Byte + * xxxx ---- ---- ---- ---- ---- ---- ---- bits 31 to 28: all 0 + * ---- x--- ---- ---- ---- ---- ---- ---- bit 27: N (negative) + * ---- -x-- ---- ---- ---- ---- ---- ---- bit 26: Z (zero) + * ---- --x- ---- ---- ---- ---- ---- ---- bit 25: I (infinity) + * ---- ---x ---- ---- ---- ---- ---- ---- bit 24: NAN (not a number or unordered) + * + * Quotient Byte (set and reset only by FMOD and FREM) + * ---- ---- x--- ---- ---- ---- ---- ---- bit 23: sign of quotient + * ---- ---- -xxx xxxx ---- ---- ---- ---- bits 22 to 16: 7 least significant bits of quotient + * + * Exception Status Byte + * ---- ---- ---- ---- x--- ---- ---- ---- bit 15: BSUN (branch/set on unordered) + * ---- ---- ---- ---- -x-- ---- ---- ---- bit 14: SNAN (signaling not a number) + * ---- ---- ---- ---- --x- ---- ---- ---- bit 13: OPERR (operand error) + * ---- ---- ---- ---- ---x ---- ---- ---- bit 12: OVFL (overflow) + * ---- ---- ---- ---- ---- x--- ---- ---- bit 11: UNFL (underflow) + * ---- ---- ---- ---- ---- -x-- ---- ---- bit 10: DZ (divide by zero) + * ---- ---- ---- ---- ---- --x- ---- ---- bit 9: INEX 2 (inexact operation) + * ---- ---- ---- ---- ---- ---x ---- ---- bit 8: INEX 1 (inexact decimal input) + * + * Accrued Exception Byte + * ---- ---- ---- ---- ---- ---- x--- ---- bit 7: IOP (invalid operation) + * ---- ---- ---- ---- ---- ---- -x-- ---- bit 6: OVFL (overflow) + * ---- ---- ---- ---- ---- ---- --x- ---- bit 5: UNFL (underflow) + * ---- ---- ---- ---- ---- ---- ---x ---- bit 4: DZ (divide by zero) + * ---- ---- ---- ---- ---- ---- ---- x--- bit 3: INEX (inexact) + * ---- ---- ---- ---- ---- ---- ---- -xxx bits 2 to 0: all 0 + */ + +#define FPSR_ZEROBITS 0xF0000007 + +#define FPSR_CC_N 0x08000000 +#define FPSR_CC_Z 0x04000000 +#define FPSR_CC_I 0x02000000 +#define FPSR_CC_NAN 0x01000000 + +#define FPSR_QUOT_SIGN 0x00800000 +#define FPSR_QUOT_LSB 0x007F0000 + +#define FPSR_BSUN 0x00008000 +#define FPSR_SNAN 0x00004000 +#define FPSR_OPERR 0x00002000 +#define FPSR_OVFL 0x00001000 +#define FPSR_UNFL 0x00000800 +#define FPSR_DZ 0x00000400 +#define FPSR_INEX2 0x00000200 +#define FPSR_INEX1 0x00000100 + +#define FPSR_AE_IOP 0x00000080 +#define FPSR_AE_OVFL 0x00000040 +#define FPSR_AE_UNFL 0x00000020 +#define FPSR_AE_DZ 0x00000010 +#define FPSR_AE_INEX 0x00000008 + +struct { + // 6888x and 68060 + uae_u32 ccr; + uae_u32 eo[3]; + // 68060 + uae_u32 v; + // 68040 + uae_u32 fpiarcu; + uae_u32 cmdreg3b; + uae_u32 cmdreg1b; + uae_u32 stag, dtag; + uae_u32 e1, e3, t; + uae_u32 fpt[3]; + uae_u32 et[3]; + uae_u32 wbt[3]; + uae_u32 grs; + uae_u32 wbte15; + uae_u32 wbtm66; +} fsave_data; + +void reset_fsave_data(void) +{ + int i; + for (i = 0; i < 3; i++) { + fsave_data.eo[i] = 0; + fsave_data.fpt[i] = 0; + fsave_data.et[i] = 0; + fsave_data.wbt[i] = 0; + } + fsave_data.ccr = 0; + fsave_data.v = 0; + fsave_data.fpiarcu = 0; + fsave_data.cmdreg1b = 0; + fsave_data.cmdreg3b = 0; + fsave_data.stag = 0; + fsave_data.dtag = 0; + fsave_data.e1 = 0; + fsave_data.e3 = 0; + fsave_data.t = 0; + fsave_data.wbte15 = 0; + fsave_data.wbtm66 = 0; + fsave_data.grs = 0; +} + +static uae_u32 get_ftag(fptype *src, int size) +{ + if (fp_is_zero(src)) { + return 1; // ZERO + } else if (fp_is_unnormal(src) || fp_is_denormal(src)) { + if (size == 1 || size == 5) + return 5; // Single/double DENORMAL + return 4; // Extended DENORMAL or UNNORMAL + } else if (fp_is_nan(src)) { + return 3; // NAN + } else if (fp_is_infinity(src)) { + return 2; // INF + } + return 0; // NORMAL +} + +static inline bool fp_is_dyadic(uae_u16 extra) +{ + return ((extra & 0x30) == 0x20 || (extra & 0x7f) == 0x38); +} + +bool fp_exception_pending(bool pre) +{ + // first check for pending arithmetic exceptions +#if ARITHMETIC_EXCEPTIONS + if (regs.fp_exp_pend) { + if (warned > 0) { + write_log (_T("FPU arithmetic exception (%d) (%s)\n"), regs.fp_exp_pend, pre ? "pre" : "mid/post"); + } + regs.fpu_exp_pre = pre; + Exception(regs.fp_exp_pend); + if (currprefs.fpu_model != 68882) + regs.fp_exp_pend = 0; + + return true; + } +#endif + // no arithmetic exceptions pending, check for unimplemented datatype + if (regs.fp_unimp_pend) { + if (warned > 0) { + write_log (_T("FPU unimplemented datatype exception (%s)\n"), pre ? "pre" : "mid/post"); + } + regs.fpu_exp_pre = pre; + Exception(55); + regs.fp_unimp_pend = 0; + + return true; + } + + return false; +} +void fp_unimp_instruction_exception_pending(void) +{ + if (regs.fp_unimp_ins) { + if (warned > 0) { + write_log (_T("FPU unimplemented instruction exception\n")); + } + regs.fpu_exp_pre = true; + Exception(11); + regs.fp_unimp_ins = false; + regs.fp_unimp_pend = 0; + } +} + +void fpsr_set_exception(uae_u32 exception) +{ + regs.fpsr |= exception; +} +uae_u32 fpsr_get_vector(uae_u32 exception) +{ + static const int vtable[8] = { 49, 49, 50, 51, 53, 52, 54, 48 }; + int i; + exception >>= 8; + for (i = 7; i >= 0; i--) { + if (exception & (1 << i)) { + return vtable[i]; + } + } + return 0; +} +void fpsr_check_arithmetic_exception(uae_u32 mask, fptype *src, uae_u32 opcode, uae_u16 extra, uae_u32 ea) +{ +#if ARITHMETIC_EXCEPTIONS + bool nonmaskable; + uae_u32 exception; + // Any exception status bit and matching exception enable bits set? + exception = regs.fpsr & regs.fpcr & 0xff00; + // Add 68040/68060 nonmaskable exceptions + if (currprefs.cpu_model >= 68040 && currprefs.fpu_model) + exception |= regs.fpsr & (FPSR_OVFL | FPSR_UNFL | mask); + + if (exception) { + regs.fp_exp_pend = fpsr_get_vector(exception); + nonmaskable = (regs.fp_exp_pend != fpsr_get_vector(regs.fpsr & regs.fpcr)); + if (warned > 0) { + write_log (_T("FPU %s arithmetic exception: FPSR: %08x, FPCR: %04x (vector: %d)!\n"), + nonmaskable?"nonmaskable":"", regs.fpsr, regs.fpcr, regs.fp_exp_pend); #if EXCEPTION_FPP == 0 - warned--; + warned--; #endif - } - regs.fp_exception = true; - m68k_setpc (newpc); -#ifdef JIT - set_special (SPCFLAG_END_COMPILE); + } + + regs.fp_opword = opcode; + regs.fp_ea = ea; + + // data for FSAVE stack frame + fptype eo; + uae_u32 opclass = (extra >> 13) & 7; + + reset_fsave_data(); + + if (currprefs.fpu_model == 68881 || currprefs.fpu_model == 68882) { + // fsave data for 68881 and 68882 + regs.fpu_exp_state = 1; // 6888x IDLE frame + + if (opclass == 3) { // 011 + fsave_data.ccr = ((uae_u32)extra << 16) | extra; + } else { // 000 or 010 + fsave_data.ccr = ((uae_u32)(opcode | 0x0080) << 16) | extra; + } + if (regs.fp_exp_pend == 54 || regs.fp_exp_pend == 52 || regs.fp_exp_pend == 50) { // SNAN, OPERR, DZ + from_exten_fmovem(src, &fsave_data.eo[0], &fsave_data.eo[1], &fsave_data.eo[2]); + if (regs.fp_exp_pend == 52 && opclass == 3) { // OPERR from move to integer or packed + fsave_data.eo[0] &= 0x4fff0000; + fsave_data.eo[1] = fsave_data.eo[2] = 0; + } + } else if (regs.fp_exp_pend == 53) { // OVFL + eo = fp_get_internal_overflow(); + from_exten_fmovem(&eo, &fsave_data.eo[0], &fsave_data.eo[1], &fsave_data.eo[2]); + } else if (regs.fp_exp_pend == 51) { // UNFL + eo = fp_get_internal_underflow(); + from_exten_fmovem(&eo, &fsave_data.eo[0], &fsave_data.eo[1], &fsave_data.eo[2]); + } // else INEX1, INEX2: do nothing + } else if (currprefs.cpu_model == 68060) { + // fsave data for 68060 + regs.fpu_exp_state = 2; // 68060 EXCP frame + fsave_data.v = regs.fp_exp_pend & 7; + from_exten_fmovem(src, &fsave_data.eo[0], &fsave_data.eo[1], &fsave_data.eo[2]); + } else { + // fsave data for 68040 + regs.fpu_exp_state = 1; // 68040 UNIMP frame + + uae_u32 reg = (extra >> 7) & 7; + uae_u32 size = (extra >> 10) & 7; + + fsave_data.fpiarcu = regs.fpiar; + + if (regs.fp_exp_pend == 54) { // SNAN (undocumented) + fsave_data.wbte15 = 1; + fsave_data.grs = 7; + } else { + fsave_data.grs = 1; + } + + if (opclass == 3) { // OPCLASS 011 + fsave_data.cmdreg1b = extra; + fsave_data.e1 = 1; + fsave_data.t = 1; + fsave_data.wbte15 = (regs.fp_exp_pend == 51 || regs.fp_exp_pend == 54) ? 1 : 0; // UNFL, SNAN + + if (fp_is_snan(src)) { + fp_unset_snan(src); + } + from_exten_fmovem(src, &fsave_data.et[0], &fsave_data.et[1], &fsave_data.et[2]); + fsave_data.stag = get_ftag(src, -1); + } else { // OPCLASS 000 and 010 + fsave_data.cmdreg1b = extra; + fsave_data.e1 = 1; + fsave_data.wbte15 = (regs.fp_exp_pend == 54) ? 1 : 0; // SNAN (undocumented) + + if (regs.fp_exp_pend == 51 || regs.fp_exp_pend == 53 || regs.fp_exp_pend == 49) { // UNFL, OVFL, INEX + if ((extra & 0x30) == 0x20 || (extra & 0x3f) == 0x04) { // FADD, FSUB, FMUL, FDIV, FSQRT + regs.fpu_exp_state = 2; // 68040 BUSY frame + fsave_data.e3 = 1; + fsave_data.e1 = 0; + fsave_data.cmdreg3b = (extra & 0x3C3) | ((extra & 0x038)>>1) | ((extra & 0x004)<<3); + if (regs.fp_exp_pend == 51) { // UNFL + eo = fp_get_internal(); + } else { // OVFL, INEX + eo = fp_get_internal_round(); + } + fsave_data.grs = fp_get_internal_grs(); + from_exten_fmovem(&eo, &fsave_data.wbt[0], &fsave_data.wbt[1], &fsave_data.wbt[2]); + fsave_data.wbte15 = (regs.fp_exp_pend == 51) ? 1 : 0; // UNFL + // src and dst is stored (undocumented) + from_exten_fmovem(src, &fsave_data.et[0], &fsave_data.et[1], &fsave_data.et[2]); + fsave_data.stag = get_ftag(src, (opclass == 0) ? -1 : size); + if (fp_is_dyadic(extra)) { + from_exten_fmovem(®s.fp[reg], &fsave_data.fpt[0], &fsave_data.fpt[1], &fsave_data.fpt[2]); + fsave_data.dtag = get_ftag(®s.fp[reg], -1); + } + } else { // FMOVE to register, FABS, FNEG + eo = fp_get_internal_round_exten(); + fsave_data.grs = fp_get_internal_grs(); + from_exten_fmovem(&eo, &fsave_data.fpt[0], &fsave_data.fpt[1], &fsave_data.fpt[2]); + eo = fp_get_internal_round_all(); // weird + from_exten_fmovem(&eo, &fsave_data.et[0], &fsave_data.et[1], &fsave_data.et[2]); // undocumented + fsave_data.stag = get_ftag(src, (opclass == 0) ? -1 : size); + } + } else { // SNAN, OPERR, DZ + from_exten_fmovem(src, &fsave_data.et[0], &fsave_data.et[1], &fsave_data.et[2]); + fsave_data.stag = get_ftag(src, (opclass == 0) ? -1 : size); + if (fp_is_dyadic(extra)) { + from_exten_fmovem(®s.fp[reg], &fsave_data.fpt[0], &fsave_data.fpt[1], &fsave_data.fpt[2]); + fsave_data.dtag = get_ftag(®s.fp[reg], -1); + } + } + } + } + } +#endif +} +void fpsr_set_result(fptype *result) +{ + // condition code byte + regs.fpsr &= 0x00fffff8; // clear cc + if (fp_is_nan (result)) { + regs.fpsr |= FPSR_CC_NAN; + } else if (fp_is_zero(result)) { + regs.fpsr |= FPSR_CC_Z; + } else if (fp_is_infinity (result)) { + regs.fpsr |= FPSR_CC_I; + } + if (fp_is_neg(result)) { + regs.fpsr |= FPSR_CC_N; + } +} +void fpsr_clear_status(void) +{ + // clear exception status byte only + regs.fpsr &= 0x0fff00f8; + + // clear external status + clear_fp_status(); +} +uae_u32 fpsr_make_status(void) +{ + uae_u32 exception; + + // get external status + get_fp_status(®s.fpsr); + + // update accrued exception byte + if (regs.fpsr & (FPSR_BSUN | FPSR_SNAN | FPSR_OPERR)) + regs.fpsr |= FPSR_AE_IOP; // IOP = BSUN || SNAN || OPERR + if (regs.fpsr & FPSR_OVFL) + regs.fpsr |= FPSR_AE_OVFL; // OVFL = OVFL + if ((regs.fpsr & FPSR_UNFL) && (regs.fpsr & FPSR_INEX2)) + regs.fpsr |= FPSR_AE_UNFL; // UNFL = UNFL && INEX2 + if (regs.fpsr & FPSR_DZ) + regs.fpsr |= FPSR_AE_DZ; // DZ = DZ + if (regs.fpsr & (FPSR_OVFL | FPSR_INEX2 | FPSR_INEX1)) + regs.fpsr |= FPSR_AE_INEX; // INEX = INEX1 || INEX2 || OVFL + + // return exceptions that interrupt calculation + exception = regs.fpsr & regs.fpcr & (FPSR_SNAN | FPSR_OPERR | FPSR_DZ); + if (currprefs.cpu_model >= 68040 && currprefs.fpu_model) + exception |= regs.fpsr & (FPSR_OVFL | FPSR_UNFL); +#if ARITHMETIC_EXCEPTIONS + return exception; +#else + return 0; #endif } - -static void fpu_op_illg2 (uae_u16 opcode, uae_u16 extra, uae_u32 ea, uaecptr oldpc) +int fpsr_set_bsun(void) { - 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); + regs.fpsr |= FPSR_BSUN; + regs.fpsr |= FPSR_AE_IOP; + + if (regs.fpcr & FPSR_BSUN) { + // logging only so far + write_log (_T("FPU exception: BSUN! (FPSR: %08x, FPCR: %04x)\n"), regs.fpsr, regs.fpcr); +#if ARITHMETIC_EXCEPTIONS + regs.fp_exp_pend = fpsr_get_vector(FPSR_BSUN); + fp_exception_pending(true); + return 1; +#endif + } + return 0; +} +void fpsr_set_quotient(uae_u64 quot, uae_s8 sign) +{ + regs.fpsr &= 0x0f00fff8; + regs.fpsr |= (quot << 16) & FPSR_QUOT_LSB; + regs.fpsr |= sign ? FPSR_QUOT_SIGN : 0; +} +void fpsr_get_quotient(uae_u64 *quot, uae_s8 *sign) +{ + *quot = (regs.fpsr & FPSR_QUOT_LSB) >> 16; + *sign = (regs.fpsr & FPSR_QUOT_SIGN) ? 1 : 0; +} + + +uae_u32 fpp_get_fpsr (void) +{ + return regs.fpsr; +} + +void fpp_set_fpsr (uae_u32 val) +{ + regs.fpsr = val; +} + +void fpp_set_fpcr (uae_u32 val) +{ + set_fp_mode(val); + regs.fpcr = val & 0xffff; +} + + +static void fpnan (fptype *fp) +{ + to_exten(fp, xhex_nan[0], xhex_nan[1], xhex_nan[2]); +} +static void fpset (fptype *fp, uae_s32 val) +{ + *fp = from_int(val); +} + + +bool fpu_get_constant(fptype *fp, int cr) +{ + uae_u32 f[3] = { 0, 0, 0 }; + int entry = 0; + int mode = (regs.fpcr >> 4) & 3; + int prec = (regs.fpcr >> 6) & 3; + + switch (cr) + { + case 0x00: // pi + entry = FPP_CR_PI; + break; + case 0x0b: // log10(2) + entry = FPP_CR_LOG10_2; + break; + case 0x0c: // e + entry = FPP_CR_E; + break; + case 0x0d: // log2(e) + entry = FPP_CR_LOG2_E; + break; + case 0x0e: // log10(e) + entry = FPP_CR_LOG10_E; + break; + case 0x0f: // 0.0 + entry = FPP_CR_ZERO; + break; + case 0x30: // ln(2) + entry = FPP_CR_LN_2; + break; + case 0x31: // ln(10) + entry = FPP_CR_LN_10; + break; + case 0x32: // 1e0 + entry = FPP_CR_1E0; + break; + case 0x33: // 1e1 + entry = FPP_CR_1E1; + break; + case 0x34: // 1e2 + entry = FPP_CR_1E2; + break; + case 0x35: // 1e4 + entry = FPP_CR_1E4; + break; + case 0x36: // 1e8 + entry = FPP_CR_1E8; + break; + case 0x37: // 1e16 + entry = FPP_CR_1E16; + break; + case 0x38: // 1e32 + entry = FPP_CR_1E32; + break; + case 0x39: // 1e64 + entry = FPP_CR_1E64; + break; + case 0x3a: // 1e128 + entry = FPP_CR_1E128; + break; + case 0x3b: // 1e256 + entry = FPP_CR_1E256; + break; + case 0x3c: // 1e512 + entry = FPP_CR_1E512; + break; + case 0x3d: // 1e1024 + entry = FPP_CR_1E1024; + break; + case 0x3e: // 1e2048 + entry = FPP_CR_1E2048; + break; + case 0x3f: // 1e4096 + entry = FPP_CR_1E4096; + break; + default: // undefined + { + bool check_f1_adjust = false; + int f1_adjust = 0; + uae_u32 sr = 0; + + if (cr > FPP_CR_NUM_SPECIAL_UNDEFINED) { + cr = 0; // Most undefined fields contain this + } + f[0] = fpp_cr_undef[cr].val[0]; + f[1] = fpp_cr_undef[cr].val[1]; + f[2] = fpp_cr_undef[cr].val[2]; + // Rounding mode and precision works very strangely here.. + switch (cr) + { + case 1: + check_f1_adjust = true; + break; + case 2: + if (prec == 1 && mode == 3) + f1_adjust = -1; + break; + case 3: + if (prec == 1 && (mode == 0 || mode == 3)) + sr = FPSR_CC_I; + else + sr = FPSR_CC_NAN; + break; + case 7: + sr = FPSR_CC_NAN; + check_f1_adjust = true; + break; + } + if (check_f1_adjust) { + if (prec == 1) { + if (mode == 0) { + f1_adjust = -1; + } else if (mode == 1 || mode == 2) { + f1_adjust = 1; + } + } + } + + to_exten_fmovem(fp, f[0], f[1], f[2]); + + if (prec == 1) fp_round32(fp); + if (prec >= 2) fp_round64(fp); + + if (f1_adjust) { + from_exten_fmovem(fp, &f[0], &f[1], &f[2]); + f[1] += f1_adjust * 0x80; + to_exten_fmovem(fp, f[0], f[1], f[2]); + } + + fpsr_set_result(fp); + regs.fpsr |= sr; + } + return false; + } + + f[0] = fpp_cr[entry].val[0]; + f[1] = fpp_cr[entry].val[1]; + f[2] = fpp_cr[entry].val[2]; + // if constant is inexact, set inexact bit and round + // note: with valid constants, LSB never wraps + if (fpp_cr[entry].inexact) { + fpsr_set_exception(FPSR_INEX2); + f[2] += fpp_cr[entry].rndoff[mode]; + } + + to_exten_fmovem(fp, f[0], f[1], f[2]); + + if (prec == 1) fp_round32(fp); + if (prec >= 2) fp_round64(fp); + + fpsr_set_result(fp); + + return true; +} + + +static void fp_unimp_instruction(uae_u16 opcode, uae_u16 extra, uae_u32 ea, uaecptr oldpc, fptype *src, int reg, int size) +{ + if ((extra & 0x7f) == 4) // FSQRT 4->5 + extra |= 1; + + // data for fsave stack frame + regs.fpu_exp_state = 1; // 68060 IDLE frame, 68040 UNIMP frame + + if (currprefs.cpu_model == 68060) { + // fsave data for 68060 + reset_fsave_data(); + } else if (currprefs.cpu_model == 68040) { + // fsave data for 68040 + fsave_data.fpiarcu = regs.fpiar; + + if (regs.fp_unimp_pend == 0) { // else data has been saved by fp_unimp_datatype + reset_fsave_data(); + fsave_data.cmdreg3b = (extra & 0x3C3) | ((extra & 0x038) >> 1) | ((extra & 0x004) << 3); + fsave_data.cmdreg1b = extra; + from_exten_fmovem(src, &fsave_data.et[0], &fsave_data.et[1], &fsave_data.et[2]); + fsave_data.stag = get_ftag(src, size); + if (reg >= 0) { + from_exten_fmovem(®s.fp[reg], &fsave_data.fpt[0], &fsave_data.fpt[1], &fsave_data.fpt[2]); + fsave_data.dtag = get_ftag(®s.fp[reg], -1); + } + } + } + if (warned > 0) { + write_log (_T("FPU unimplemented instruction: OP=%04X-%04X SRC=%08X-%08X-%08X EA=%08X PC=%08X\n"), + opcode, extra, fsave_data.et[0],fsave_data.et[1],fsave_data.et[2], ea, oldpc); +#if EXCEPTION_FPP == 0 + warned--; +#endif + } + + regs.fp_ea = ea; + regs.fp_opword = opcode; + regs.fp_unimp_ins = true; + fp_unimp_instruction_exception_pending(); + + regs.fp_exception = true; +} + +static void fp_unimp_datatype(uae_u16 opcode, uae_u16 extra, uae_u32 ea, uaecptr oldpc, fptype *src, uae_u32 *packed) +{ + uae_u32 reg = (extra >> 7) & 7; + uae_u32 size = (extra >> 10) & 7; + uae_u32 opclass = (extra >> 13) & 7; + + regs.fp_ea = ea; + regs.fp_opword = opcode; + regs.fp_unimp_pend = packed ? 2 : 1; + + if ((extra & 0x7f) == 4) // FSQRT 4->5 + extra |= 1; + + // data for fsave stack frame + reset_fsave_data(); + regs.fpu_exp_state = 2; // 68060 EXCP frame, 68040 BUSY frame + + if (currprefs.cpu_model == 68060) { + // fsave data for 68060 + if (packed) { + regs.fpu_exp_state = 1; // 68060 IDLE frame + } else { + fsave_data.v = 7; // vector & 0x7 + from_exten_fmovem(src, &fsave_data.eo[0], &fsave_data.eo[1], &fsave_data.eo[2]); + } + } else if (currprefs.cpu_model == 68040) { + // fsave data for 68040 + fsave_data.cmdreg1b = extra; + fsave_data.fpiarcu = regs.fpiar; + if (packed) { + fsave_data.e1 = 1; // used to distinguish packed operands + } + if (opclass == 3) { // OPCLASS 011 + fsave_data.t = 1; + from_exten_fmovem(src, &fsave_data.et[0], &fsave_data.et[1], &fsave_data.et[2]); + fsave_data.stag = get_ftag(src, -1); + from_exten_fmovem(src, &fsave_data.fpt[0], &fsave_data.fpt[1], &fsave_data.fpt[2]); // undocumented + fsave_data.dtag = get_ftag(src, -1); + } else { // OPCLASS 000 and 010 + if (packed) { + fsave_data.fpt[2] = packed[0]; // yes, this is correct. + fsave_data.fpt[1] = packed[1]; // undocumented + fsave_data.et[1] = packed[1]; + fsave_data.et[2] = packed[2]; + fsave_data.stag = 7; // undocumented + } else { + from_exten_fmovem(src, &fsave_data.et[0], &fsave_data.et[1], &fsave_data.et[2]); + fsave_data.stag = get_ftag(src, (opclass == 0) ? -1 : size); + if (fsave_data.stag == 5) { + fsave_data.et[0] = (size == 1) ? 0x3f800000 : 0x3c000000; // exponent for denormalized single and double + } + if (fp_is_dyadic(extra)) { + from_exten_fmovem(®s.fp[reg], &fsave_data.fpt[0], &fsave_data.fpt[1], &fsave_data.fpt[2]); + fsave_data.dtag = get_ftag(®s.fp[reg], -1); + } + } + } + } + if (warned > 0) { + write_log (_T("FPU unimplemented datatype (%s): OP=%04X-%04X SRC=%08X-%08X-%08X EA=%08X PC=%08X\n"), + packed ? "packed" : "denormal", opcode, extra, + packed ? fsave_data.fpt[2] : fsave_data.et[0], fsave_data.et[1], fsave_data.et[2], ea, oldpc); +#if EXCEPTION_FPP == 0 + warned--; +#endif + } + regs.fp_exception = true; } -static void fpu_op_illg (uae_u16 opcode, uae_u16 extra, uaecptr oldpc) +static void fpu_op_illg (uae_u16 opcode, uae_u32 ea, uaecptr oldpc) { - fpu_op_illg2 (opcode, extra, 0, oldpc); + if ((currprefs.cpu_model == 68060 && (currprefs.fpu_model == 0 || (regs.pcr & 2))) + || (currprefs.cpu_model == 68040 && currprefs.fpu_model == 0)) { + regs.fp_unimp_ins = true; + regs.fp_ea = ea; + fp_unimp_instruction_exception_pending(); + return; + } + regs.fp_exception = true; + m68k_setpc (oldpc); + op_illg (opcode); } - static void fpu_noinst (uae_u16 opcode, uaecptr pc) { #if EXCEPTION_FPP - write_log (_T("Unknown FPU instruction %04X %08X\n"), opcode, pc); + write_log (_T("Unknown FPU instruction %04X %08X\n"), opcode, pc); #endif - regs.fp_exception = true; - m68k_setpc (pc); - op_illg (opcode); + regs.fp_exception = true; + m68k_setpc (pc); + op_illg (opcode); +} + +static bool if_no_fpu(void) +{ + return (regs.pcr & 2) || currprefs.fpu_model <= 0; } static bool fault_if_no_fpu (uae_u16 opcode, uae_u16 extra, uaecptr ea, uaecptr oldpc) { - if ((regs.pcr & 2) || currprefs.fpu_model <= 0) { + if (if_no_fpu()) { #if EXCEPTION_FPP - write_log (_T("no FPU: %04X-%04X PC=%08X\n"), opcode, extra, oldpc); + write_log (_T("no FPU: %04X-%04X PC=%08X\n"), opcode, extra, oldpc); #endif - fpu_op_illg2 (opcode, extra, ea, oldpc); - return true; - } - 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; + if (fpu_mmu_fixup) { + m68k_areg (regs, mmufixup[0].reg) = mmufixup[0].value; + mmufixup[0].reg = -1; + } + fpu_op_illg (opcode, ea, oldpc); + return true; + } + return false; +} + +static bool fault_if_unimplemented_680x0 (uae_u16 opcode, uae_u16 extra, uaecptr ea, uaecptr oldpc, fptype *src, int reg) +{ + if (fault_if_no_fpu (opcode, extra, ea, oldpc)) + return true; + if (currprefs.cpu_model >= 68040 && currprefs.fpu_model) { + if ((extra & (0x8000 | 0x2000)) != 0) + return false; + if ((extra & 0xfc00) == 0x5c00) { + // FMOVECR + fp_unimp_instruction(opcode, extra, ea, oldpc, src, reg, -1); + return true; + } + uae_u16 v = extra & 0x7f; + switch (v) + { + case 0x00: /* FMOVE */ + case 0x40: /* FSMOVE */ + case 0x44: /* FDMOVE */ + case 0x04: /* FSQRT */ + case 0x41: /* FSSQRT */ + case 0x45: /* FDSQRT */ + case 0x18: /* FABS */ + case 0x58: /* FSABS */ + case 0x5c: /* FDABS */ + case 0x1a: /* FNEG */ + case 0x5a: /* FSNEG */ + case 0x5e: /* FDNEG */ + case 0x20: /* FDIV */ + case 0x60: /* FSDIV */ + case 0x64: /* FDDIV */ + case 0x22: /* FADD */ + case 0x62: /* FSADD */ + case 0x66: /* FDADD */ + case 0x23: /* FMUL */ + case 0x63: /* FSMUL */ + case 0x67: /* FDMUL */ + case 0x24: /* FSGLDIV */ + case 0x27: /* FSGLMUL */ + case 0x28: /* FSUB */ + case 0x68: /* FSSUB */ + case 0x6c: /* FDSUB */ + case 0x38: /* FCMP */ + case 0x3a: /* FTST */ + return false; + case 0x01: /* FINT */ + case 0x03: /* FINTRZ */ + // Unimplemented only in 68040. + if (currprefs.cpu_model != 68040) { + return false; + } + default: + fp_unimp_instruction(opcode, extra, ea, oldpc, src, reg, -1); + 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; + if ((currprefs.fpu_model == 68881 || currprefs.fpu_model == 68882)) { + uae_u16 v = extra & 0x7f; + /* 68040/68060 only variants. 6888x = F-line exception. */ + switch (v) + { + case 0x00: /* FMOVE */ + case 0x01: /* FINT */ + case 0x02: /* FSINH */ + case 0x03: /* FINTRZ */ + case 0x04: /* FSQRT */ + 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 0x18: /* FABS */ + case 0x19: /* FCOSH */ + case 0x1a: /* FNEG */ + case 0x1c: /* FACOS */ + case 0x1d: /* FCOS */ + case 0x1e: /* FGETEXP */ + case 0x1f: /* FGETMAN */ + case 0x20: /* FDIV */ + case 0x21: /* FMOD */ + case 0x22: /* FADD */ + case 0x23: /* FMUL */ + case 0x24: /* FSGLDIV */ + case 0x25: /* FREM */ + case 0x26: /* FSCALE */ + case 0x27: /* FSGLMUL */ + case 0x28: /* FSUB */ + case 0x30: /* FSINCOS */ + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: /* FCMP */ + case 0x3a: /* FTST */ + return false; + default: + fpu_noinst (opcode, oldpc); + return true; + } + } + return false; +} + +static bool fault_if_60 (void) +{ + if (currprefs.cpu_model == 68060 && currprefs.fpu_model) { + Exception(60); + 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; + if (fault_if_no_fpu (opcode, extra, ea, oldpc)) + return true; + if (currprefs.cpu_model == 68060 && currprefs.fpu_model) { + // 68060 FTRAP, FDBcc or FScc are not implemented. + regs.fp_unimp_ins = true; + regs.fp_ea = ea; + fp_unimp_instruction_exception_pending(); + 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 (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); + 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; + m68k_setpc (oldpc); + regs.fp_exception = true; + op_illg (opcode); + return true; + } + return false; } static int get_fpu_version (void) { - int v = 0; - - switch (currprefs.fpu_model) - { - case 68881: - v = 0x1f; - break; - case 68882: - v = 0x20; - break; - case 68040: - if (currprefs.fpu_revision == 0x40) - v = 0x40; - else - v = 0x41; - break; - } - return v; + int v = 0; + + switch (currprefs.fpu_model) + { + case 68881: + case 68882: + v = 0x1f; + break; + case 68040: + if (currprefs.fpu_revision == 0x40) + v = 0x40; + else + v = 0x41; + break; + } + return v; } 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) -{ - if (src < minval) - src = minval; - if (src > maxval) - src = maxval; -#if defined(X86_MSVC_ASSEMBLY_FPU) - { - fptype tmp_fp; - __asm { - fld LDPTR src - frndint - fstp LDPTR tmp_fp - } - return (tointtype)tmp_fp; - } -#else /* no X86_MSVC */ - { - 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 -} - -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 + int i; + regs.fpu_state = 0; + regs.fpu_exp_state = 0; + regs.fpcr = 0; + regs.fpsr = 0; + regs.fpiar = 0; + for (i = 0; i < 8; i++) + fpnan (®s.fp[i]); } -uae_u32 get_fpsr (void) -{ - 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 - { - 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) - fpset (®s.fp_result, *fp_nan); - else if (x & 0x04000000) - fpset (®s.fp_result, 0); - else if (x & 0x08000000) - fpset (®s.fp_result, -1); - else - 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 */ /* double : S 11*E 52*F */ @@ -826,1842 +1067,1921 @@ uae_u32 get_ftag (uae_u32 w1, uae_u32 w2 /* E = MAX & F # 0 -> NotANumber */ /* E = biased by 127 (single) ,1023 (double) ,16383 (extended) */ -static fptype to_pack (uae_u32 *wrd) +// 68040/060 does not support denormals +static bool normalize_or_fault_if_no_denormal_support(uae_u16 opcode, uae_u16 extra, uaecptr ea, uaecptr oldpc, fptype *src) { - fptype d; - char *cp; - char str[100]; - - cp = str; - if (wrd[0] & 0x80000000) - *cp++ = '-'; - *cp++ = (wrd[0] & 0xf) + '0'; - *cp++ = '.'; - *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 (wrd[0] & 0x40000000) - *cp++ = '-'; - *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); -#else - sscanf (str, "%le", &d); -#endif - return d; -} - -void from_pack (fptype src, uae_u32 *wrd, int kfactor) -{ - 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, "%#.17Le", src); -#else - sprintf (str, "%#.17e", src); -#endif - - // get exponent - cp = str; - while (*cp++ != 'e'); - if (*cp == '+') - cp++; - exp = atoi (cp); - - // remove trailing zeros - cp = str; - while (*cp != 'e') - cp++; - cp[0] = 0; - cp--; - while (cp > str && *cp == '0') { - *cp = 0; - cp--; - } - - cp = str; - // get sign - if (*cp == '-') { - cp++; - wrd[0] = 0x80000000; - } else if (*cp == '+') { - cp++; - } - strp = cp; - - if (kfactor <= 0) { - ndigits = abs (exp) + (-kfactor) + 1; - } else { - if (kfactor > 17) { - kfactor = 17; - update_fpsr (FE_INVALID); - } - ndigits = kfactor; - } - - 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') - wrd[j] |= *cp++ - '0'; - } - } - - // 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 int get_fp_value (uae_u32 opcode, uae_u16 extra, fpdata *src, uaecptr oldpc, uae_u32 *adp) -{ - 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; - } - mode = (opcode >> 3) & 7; - reg = opcode & 7; - size = (extra >> 10) & 7; - - switch (mode) { - case 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: - return 0; - case 2: - 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_cp_next_iword (); - break; - case 6: - ad = x_cp_get_disp_ea_020 (m68k_areg (regs, reg), 0); - break; - case 7: - 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: - src->fp = (fptype) (uae_s32) (doext ? exts[0] : x_cp_get_long (ad)); - break; - case 1: - src->fp = to_single ((doext ? exts[0] : x_cp_get_long (ad))); - break; - case 2: - { - 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: - { - 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: - src->fp = (fptype) (uae_s16) (doext ? exts[0] : x_cp_get_word (ad)); - break; - 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); - } - break; - case 6: - src->fp = (fptype) (uae_s8) (doext ? exts[0] : x_cp_get_byte (ad)); - break; - default: - return 0; - } - return 1; -} - -static int put_fp_value (fpdata *value, uae_u32 opcode, uae_u16 extra, uaecptr oldpc) -{ - int size, mode, reg; - 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 (fp_is_unnormal(src) || fp_is_denormal(src)) { + if (currprefs.cpu_model >= 68040 && currprefs.fpu_model) { + if (fp_is_zero(src)) { + fp_normalize(src); // 68040/060 can only fix unnormal zeros + } else { + fp_unimp_datatype(opcode, extra, ea, oldpc, src, NULL); + return true; + } + } else { + fp_normalize(src); + } + } + return false; +} +static bool normalize_or_fault_if_no_denormal_support_dst(uae_u16 opcode, uae_u16 extra, uaecptr ea, uaecptr oldpc, fptype *dst, fptype *src) +{ + if (fp_is_unnormal(dst) || fp_is_denormal(dst)) { + if (currprefs.cpu_model >= 68040 && currprefs.fpu_model) { + if (fp_is_zero(dst)) { + fp_normalize(dst); // 68040/060 can only fix unnormal zeros + } else { + fp_unimp_datatype(opcode, extra, ea, oldpc, src, NULL); + return true; + } + } else { + fp_normalize(dst); + } + } + return false; +} +// 68040/060 does not support packed decimal format +static bool fault_if_no_packed_support(uae_u16 opcode, uae_u16 extra, uaecptr ea, uaecptr oldpc, fptype *src, uae_u32 *packed) +{ + if (currprefs.cpu_model >= 68040 && currprefs.fpu_model) { + fp_unimp_datatype(opcode, extra, ea, oldpc, src, packed); + return true; + } + return false; +} +// 68040 does not support move to integer format +static bool fault_if_68040_integer_nonmaskable(uae_u16 opcode, uae_u16 extra, uaecptr ea, uaecptr oldpc, fptype *src) +{ + if (currprefs.cpu_model == 68040 && currprefs.fpu_model) { + fpsr_make_status(); + if (regs.fpsr & (FPSR_SNAN | FPSR_OPERR)) { + fpsr_check_arithmetic_exception(FPSR_SNAN | FPSR_OPERR, src, opcode, extra, ea); + fp_exception_pending(false); // post +#if ARITHMETIC_EXCEPTIONS + return true; +#endif + } + } + return false; +} + +static int get_fp_value (uae_u32 opcode, uae_u16 extra, fptype *src, uaecptr oldpc, uae_u32 *adp) +{ + 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]; + normalize_or_fault_if_no_denormal_support(opcode, extra, 0, oldpc, src); + return 1; + } + mode = (opcode >> 3) & 7; + reg = opcode & 7; + size = (extra >> 10) & 7; + + switch (mode) { + case 0: + if ((size == 0 || size == 1 ||size == 4 || size == 6) && fault_if_no_fpu (opcode, extra, 0, oldpc)) + return -1; + + switch (size) + { + case 6: + fpset(src, (uae_s8) m68k_dreg (regs, reg)); + break; + case 4: + fpset(src, (uae_s16) m68k_dreg (regs, reg)); + break; + case 0: + fpset(src, (uae_s32) m68k_dreg (regs, reg)); + break; + case 1: + to_single (src, m68k_dreg (regs, reg)); + normalize_or_fault_if_no_denormal_support(opcode, extra, 0, oldpc, src); + break; + default: + return 0; + } + return 1; + case 1: + return 0; + case 2: + ad = m68k_areg (regs, reg); + break; + case 3: + // Also needed by fault_if_no_fpu + 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: + // Also needed by fault_if_no_fpu + 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); + // 68060 no fpu -(an): EA points to -4, not -12 if extended precision + if (currprefs.cpu_model == 68060 && if_no_fpu() && sz1[size] == 12) + ad += 8; + break; + case 5: + ad = m68k_areg (regs, reg) + (uae_s32) (uae_s16) x_cp_next_iword (); + break; + case 6: + ad = x_cp_get_disp_ea_020 (m68k_areg (regs, reg), 0); + break; + case 7: + 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()) + 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; + } + } + + if (fault_if_no_fpu (opcode, extra, ad, oldpc)) + return -1; + + *adp = ad; + uae_u32 adold = ad; + + if (currprefs.fpu_model == 68060) { + // Skip if 68040 because FSAVE frame can store both src and dst + if (fault_if_unimplemented_680x0(opcode, extra, ad, oldpc, src, -1)) { + return -1; + } + } + + switch (size) + { + case 0: + fpset(src, (uae_s32) (doext ? exts[0] : x_cp_get_long (ad))); + break; + case 1: + to_single (src, (doext ? exts[0] : x_cp_get_long (ad))); + normalize_or_fault_if_no_denormal_support(opcode, extra, adold, oldpc, src); + break; + case 2: + { + 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); + normalize_or_fault_if_no_denormal_support(opcode, extra, adold, oldpc, src); + } + break; + case 3: + { + uae_u32 wrd[3]; + if (currprefs.cpu_model == 68060) { + if (fault_if_no_packed_support (opcode, extra, adold, oldpc, 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_no_packed_support (opcode, extra, adold, oldpc, NULL, wrd)) + return 1; + to_pack (src, wrd); + fp_normalize(src); + return 1; + } + break; + case 4: + fpset(src, (uae_s16) (doext ? exts[0] : x_cp_get_word (ad))); + break; + 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)); + to_double (src, wrd1, wrd2); + normalize_or_fault_if_no_denormal_support(opcode, extra, adold, oldpc, src); + } + break; + case 6: + fpset(src, (uae_s8) (doext ? exts[0] : x_cp_get_byte (ad))); + break; + default: + return 0; + } + return 1; +} + +static int put_fp_value (fptype *value, uae_u32 opcode, uae_u16 extra, uaecptr oldpc, uae_u32 *adp) +{ + int size, mode, reg; + 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 (_T("PUTFP: %f %04X %04X\n"), value, opcode, extra); + write_log (_T("PUTFP: %04X %04X\n"), opcode, extra); #endif -#ifdef USE_SOFT_LONG_DOUBLE - value->fpx = false; -#endif - if (!(extra & 0x4000)) { - 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->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: - return 0; - case 2: - 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_cp_next_iword (); - break; - case 6: - 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_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; - } - } - - if (fault_if_no_fpu (opcode, extra, ad, oldpc)) - return 1; - - switch (size) - { - case 0: - x_cp_put_long (ad, (uae_u32)toint (value->fp, -2147483648.0, 2147483647.0)); - break; - case 1: - x_cp_put_long (ad, from_single (value->fp)); - break; - case 2: - { - uae_u32 wrd1, wrd2, wrd3; - from_exten (value, &wrd1, &wrd2, &wrd3); - x_cp_put_long (ad, wrd1); - ad += 4; - x_cp_put_long (ad, wrd2); - ad += 4; - x_cp_put_long (ad, wrd3); - } - break; - 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 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_cp_put_long (ad, wrd[1]); - ad += 4; - x_cp_put_long (ad, wrd[2]); - } - break; - case 4: - 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 6: - x_cp_put_byte (ad, (uae_s8)toint (value->fp, -128.0, 127.0)); - break; - default: - return 0; - } - return 1; +#if 0 + if (!(extra & 0x4000)) { + if (fault_if_no_fpu (opcode, extra, 0, oldpc)) + return 1; + regs.fp[(extra >> 10) & 7] = *value; + return 1; + } +#endif + reg = opcode & 7; + mode = (opcode >> 3) & 7; + size = (extra >> 10) & 7; + switch (mode) + { + case 0: + if ((size == 0 || size == 1 ||size == 4 || size == 6) && fault_if_no_fpu (opcode, extra, 0, oldpc)) + return -1; + + switch (size) + { + case 6: + if (normalize_or_fault_if_no_denormal_support(opcode, extra, 0, oldpc, value)) + return 1; + m68k_dreg (regs, reg) = (uae_u32)(((to_int (value, 0) & 0xff) + | (m68k_dreg (regs, reg) & ~0xff))); + if (fault_if_68040_integer_nonmaskable(opcode, extra, ad, oldpc, value)) + return -1; + break; + case 4: + if (normalize_or_fault_if_no_denormal_support(opcode, extra, 0, oldpc, value)) + return 1; + m68k_dreg (regs, reg) = (uae_u32)(((to_int (value, 1) & 0xffff) + | (m68k_dreg (regs, reg) & ~0xffff))); + if (fault_if_68040_integer_nonmaskable(opcode, extra, ad, oldpc, value)) + return -1; + break; + case 0: + if (normalize_or_fault_if_no_denormal_support(opcode, extra, 0, oldpc, value)) + return 1; + m68k_dreg (regs, reg) = (uae_u32)to_int (value, 2); + if (fault_if_68040_integer_nonmaskable(opcode, extra, ad, oldpc, value)) + return -1; + break; + case 1: + if (normalize_or_fault_if_no_denormal_support(opcode, extra, 0, oldpc, value)) + return 1; + m68k_dreg (regs, reg) = from_single (value); + break; + default: + return 0; + } + return 1; + case 1: + return 0; + case 2: + ad = m68k_areg (regs, reg); + break; + case 3: + // Also needed by fault_if_no_fpu + 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: + // Also needed by fault_if_no_fpu + 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); + // 68060 no fpu -(an): EA points to -4, not -12 if extended precision + if (currprefs.cpu_model == 68060 && if_no_fpu() && sz1[size] == 12) + ad += 8; + break; + case 5: + ad = m68k_areg (regs, reg) + (uae_s32) (uae_s16) x_cp_next_iword (); + break; + case 6: + 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_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; + } + } + + *adp = ad; + + if (fault_if_no_fpu (opcode, extra, ad, oldpc)) + return -1; + + switch (size) + { + case 0: + if (normalize_or_fault_if_no_denormal_support(opcode, extra, ad, oldpc, value)) + return 1; + x_cp_put_long(ad, (uae_u32)to_int(value, 2)); + if (fault_if_68040_integer_nonmaskable(opcode, extra, ad, oldpc, value)) + return -1; + break; + case 1: + if (normalize_or_fault_if_no_denormal_support(opcode, extra, ad, oldpc, value)) + return 1; + x_cp_put_long(ad, from_single(value)); + break; + case 2: + { + if (normalize_or_fault_if_no_denormal_support(opcode, extra, ad, oldpc, value)) + return 1; + uae_u32 wrd1, wrd2, wrd3; + from_exten(value, &wrd1, &wrd2, &wrd3); + x_cp_put_long (ad, wrd1); + ad += 4; + x_cp_put_long (ad, wrd2); + ad += 4; + x_cp_put_long (ad, wrd3); + } + break; + 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 wrd[3]; + int kfactor; + if (fault_if_no_packed_support (opcode, extra, ad, oldpc, value, wrd)) + return 1; + kfactor = size == 7 ? m68k_dreg (regs, (extra >> 4) & 7) : extra; + kfactor &= 127; + if (kfactor & 64) + kfactor |= ~63; + fp_normalize(value); + from_pack (value, wrd, kfactor); + x_cp_put_long (ad, wrd[0]); + ad += 4; + x_cp_put_long (ad, wrd[1]); + ad += 4; + x_cp_put_long (ad, wrd[2]); + } + break; + case 4: + if (normalize_or_fault_if_no_denormal_support(opcode, extra, ad, oldpc, value)) + return 1; + x_cp_put_word(ad, (uae_s16)to_int(value, 1)); + if (fault_if_68040_integer_nonmaskable(opcode, extra, ad, oldpc, value)) + return -1; + break; + case 5: + { + if (normalize_or_fault_if_no_denormal_support(opcode, extra, ad, oldpc, value)) + return 1; + uae_u32 wrd1, wrd2; + from_double(value, &wrd1, &wrd2); + x_cp_put_long (ad, wrd1); + ad += 4; + x_cp_put_long (ad, wrd2); + } + break; + case 6: + if (normalize_or_fault_if_no_denormal_support(opcode, extra, ad, oldpc, value)) + return 1; + x_cp_put_byte(ad, (uae_s8)to_int(value, 0)); + if (fault_if_68040_integer_nonmaskable(opcode, extra, ad, oldpc, value)) + return -1; + break; + default: + return 0; + } + return 1; } STATIC_INLINE int get_fp_ad (uae_u32 opcode, uae_u32 * ad) { - int mode; - int reg; - - mode = (opcode >> 3) & 7; - reg = opcode & 7; - switch (mode) - { - case 0: - case 1: - return 0; - case 2: - *ad = m68k_areg (regs, reg); - break; - case 3: - *ad = m68k_areg (regs, reg); - break; - case 4: - *ad = m68k_areg (regs, reg); - break; - case 5: - *ad = m68k_areg (regs, reg) + (uae_s32) (uae_s16) x_cp_next_iword (); - break; - case 6: - *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_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; + int mode; + int reg; + + mode = (opcode >> 3) & 7; + reg = opcode & 7; + switch (mode) + { + case 0: + case 1: + return 0; + case 2: + *ad = m68k_areg (regs, reg); + break; + case 3: + *ad = m68k_areg (regs, reg); + break; + case 4: + *ad = m68k_areg (regs, reg); + break; + case 5: + *ad = m68k_areg (regs, reg) + (uae_s32) (uae_s16) x_cp_next_iword (); + break; + case 6: + *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_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; } int fpp_cond (int condition) { - 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.fp); -#endif - - if (NotANumber) - N=Z=0; - - switch (condition) - { - case 0x00: - return 0; - case 0x01: - return Z; - case 0x02: - return !(NotANumber || Z || N); - case 0x03: - return Z || !(NotANumber || N); - case 0x04: - return N && !(NotANumber || Z); - case 0x05: - return Z || (N && !NotANumber); - case 0x06: - return !(NotANumber || Z); - case 0x07: - return !NotANumber; - case 0x08: - return NotANumber; - case 0x09: - return NotANumber || Z; - case 0x0a: - return NotANumber || !(N || Z); - case 0x0b: - return NotANumber || Z || !N; - case 0x0c: - return NotANumber || (N && !Z); - case 0x0d: - return NotANumber || Z || N; - case 0x0e: - return !Z; - case 0x0f: - return 1; - case 0x10: - return 0; - case 0x11: - return Z; - case 0x12: - return !(NotANumber || Z || N); - case 0x13: - return Z || !(NotANumber || N); - case 0x14: - return N && !(NotANumber || Z); - case 0x15: - return Z || (N && !NotANumber); - case 0x16: - return !(NotANumber || Z); - case 0x17: - return !NotANumber; - case 0x18: - return NotANumber; - case 0x19: - return NotANumber || Z; - case 0x1a: - return NotANumber || !(N || Z); - case 0x1b: - return NotANumber || Z || !N; - case 0x1c: - return NotANumber || (N && !Z); - case 0x1d: - return NotANumber || Z || N; - case 0x1e: - return !Z; - case 0x1f: - return 1; - } - return -1; + int NotANumber, Z, N; + + NotANumber = (regs.fpsr & FPSR_CC_NAN) ? 1 : 0; + N = (regs.fpsr & FPSR_CC_N) ? 1 : 0; + Z = (regs.fpsr & FPSR_CC_Z) ? 1 : 0; + + if ((condition & 0x10) && NotANumber) { + if (fpsr_set_bsun()) + return -2; + } + + switch (condition) + { + case 0x00: + return 0; + case 0x01: + return Z; + case 0x02: + return !(NotANumber || Z || N); + case 0x03: + return Z || !(NotANumber || N); + case 0x04: + return N && !(NotANumber || Z); + case 0x05: + return Z || (N && !NotANumber); + case 0x06: + return !(NotANumber || Z); + case 0x07: + return !NotANumber; + case 0x08: + return NotANumber; + case 0x09: + return NotANumber || Z; + case 0x0a: + return NotANumber || !(N || Z); + case 0x0b: + return NotANumber || Z || !N; + case 0x0c: + return NotANumber || (N && !Z); + case 0x0d: + return NotANumber || Z || N; + case 0x0e: + return !Z; + case 0x0f: + return 1; + case 0x10: + return 0; + case 0x11: + return Z; + case 0x12: + return !(NotANumber || Z || N); + case 0x13: + return Z || !(NotANumber || N); + case 0x14: + return N && !(NotANumber || Z); + case 0x15: + return Z || (N && !NotANumber); + case 0x16: + return !(NotANumber || Z); + case 0x17: + return !NotANumber; + case 0x18: + return NotANumber; + case 0x19: + return NotANumber || Z; + case 0x1a: + return NotANumber || !(N || Z); + case 0x1b: + return NotANumber || Z || !N; + case 0x1c: + return NotANumber || (N && !Z); + case 0x1d: + return NotANumber || Z || N; + case 0x1e: + return !Z; + case 0x1f: + return 1; + } + 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; + // 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 = m68k_getpc (); - uae_s32 disp; - int cc; + uaecptr pc = m68k_getpc (); + uae_s32 disp; + int cc; + + if (fp_exception_pending(true)) + return; - regs.fp_exception = false; + regs.fp_exception = false; #if DEBUG_FPP - if (!isinrom ()) - write_log (_T("fdbcc_opp at %08lx\n"), m68k_getpc ()); + write_log (_T("fdbcc_opp at %08x\n"), m68k_getpc ()); #endif - if (fault_if_no_6888x (opcode, extra, pc - 4)) - return; - - 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 < 0) { - fpu_op_illg (opcode, extra, regs.fpiar); - } else if (!cc) { - int reg = opcode & 0x7; - - m68k_dreg (regs, reg) = ((m68k_dreg (regs, reg) & 0xffff0000) - | (((m68k_dreg (regs, reg) & 0xffff) - 1) & 0xffff)); - if ((m68k_dreg (regs, reg) & 0xffff) != 0xffff) - m68k_setpc (pc + disp); - } + if (fault_if_no_6888x (opcode, extra, pc - 4)) + return; + + 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 < 0) { + if (cc == -2) + return; // BSUN + else + fpu_op_illg (opcode, 0, regs.fpiar); + } else if (!cc) { + int reg = opcode & 0x7; + + m68k_dreg (regs, reg) = ((m68k_dreg (regs, reg) & 0xffff0000) + | (((m68k_dreg (regs, reg) & 0xffff) - 1) & 0xffff)); + if ((m68k_dreg (regs, reg) & 0xffff) != 0xffff) { + m68k_setpc (pc + disp); + regs.fp_branch = true; + } + } } void fpuop_scc (uae_u32 opcode, uae_u16 extra) { - uae_u32 ad = 0; - int cc; - uaecptr pc = m68k_getpc () - 4; + uae_u32 ad = 0; + int cc; + uaecptr pc = m68k_getpc () - 4; + + if (fp_exception_pending(true)) + return; - regs.fp_exception = false; + regs.fp_exception = false; #if DEBUG_FPP - if (!isinrom ()) - write_log (_T("fscc_opp at %08lx\n"), m68k_getpc ()); + write_log (_T("fscc_opp at %08x\n"), m68k_getpc ()); #endif - - 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 < 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 { - x_cp_put_byte (ad, cc ? 0xff : 0x00); - } + + 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 < 0) { + if (cc == -2) + return; // BSUN + else + fpu_op_illg (opcode, 0, regs.fpiar); + } else if ((opcode & 0x38) == 0) { + m68k_dreg (regs, opcode & 7) = (m68k_dreg (regs, opcode & 7) & ~0xff) | (cc ? 0xff : 0x00); + } else { + x_cp_put_byte (ad, cc ? 0xff : 0x00); + } } void fpuop_trapcc (uae_u32 opcode, uaecptr oldpc, uae_u16 extra) { - int cc; + int cc; + + if (fp_exception_pending(true)) + return; - regs.fp_exception = false; + regs.fp_exception = false; #if DEBUG_FPP - if (!isinrom ()) - write_log (_T("ftrapcc_opp at %08lx\n"), m68k_getpc ()); + write_log (_T("ftrapcc_opp at %08x\n"), m68k_getpc ()); #endif - if (fault_if_no_fpu_u (opcode, extra, 0, oldpc)) - return; - - regs.fpiar = oldpc; - maybe_idle_state (); - cc = fpp_cond (extra & 0x3f); - if (cc < 0) { - fpu_op_illg (opcode, extra, oldpc); - } else if (cc) { - Exception (7); - } + if (fault_if_no_fpu_u (opcode, extra, 0, oldpc)) + return; + + regs.fpiar = oldpc; + maybe_idle_state (); + cc = fpp_cond (extra & 0x3f); + if (cc < 0) { + if (cc == -2) + return; // BSUN + else + fpu_op_illg (opcode, 0, regs.fpiar); + } else if (cc) { + Exception (7); + } } void fpuop_bcc (uae_u32 opcode, uaecptr oldpc, uae_u32 extra) { - int cc; - - regs.fp_exception = false; -#if DEBUG_FPP - if (!isinrom ()) - write_log (_T("fbcc_opp at %08lx\n"), m68k_getpc ()); -#endif - 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 < 0) { - fpu_op_illg (opcode, extra, oldpc - 2); - } else if (cc) { - if ((opcode & 0x40) == 0) - extra = (uae_s32) (uae_s16) extra; - m68k_setpc (oldpc + extra); - } -} - -void fpuop_save (uae_u32 opcode) -{ - uae_u32 ad; - int incr = (opcode & 0x38) == 0x20 ? -1 : 1; - int fpu_version = get_fpu_version (); - uaecptr pc = m68k_getpc () - 2; - int i; + int cc; + + if (fp_exception_pending(true)) + return; - regs.fp_exception = false; + regs.fp_exception = false; #if DEBUG_FPP - if (!isinrom ()) - write_log (_T("fsave_opp at %08lx\n"), m68k_getpc ()); -#endif - - if (fault_if_no_6888x (opcode, 0, pc)) - return; - - if (get_fp_ad (opcode, &ad) == 0) { - 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 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 { - 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) { - 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 { - /* 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); + write_log (_T("fbcc_opp at %08x\n"), m68k_getpc ()); #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 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; - 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; - } - } - } else { - 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; - } - } - } - } - - 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; + 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 < 0) { + if (cc == -2) + return; // BSUN + else + fpu_op_illg (opcode, 0, regs.fpiar); + } else if (cc) { + if ((opcode & 0x40) == 0) + extra = (uae_s32) (uae_s16) extra; + m68k_setpc (oldpc + extra); + regs.fp_branch = true; + } } -void fpuop_restore (uae_u32 opcode) +static uaecptr fmovem2mem (uaecptr ad, uae_u32 list, int incr, int regdir) { - 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 (_T("frestore_opp at %08lx\n"), m68k_getpc ()); -#endif - - if (fault_if_no_6888x (opcode, 0, pc)) - return; - - if (get_fp_ad (opcode, &ad) == 0) { - 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) { - int ff = (d >> 8) & 0xff; - uae_u32 v1, v2; - - if (incr < 0) { - ad -= 4; - v1 = x_get_long (ad); - ad -= 4; - v2 = x_get_long (ad); - } else { - v1 = x_get_long (ad); - ad += 4; - v2 = x_get_long (ad); - ad += 4; - } - 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 { - fpu_null (); - } - } else { - if ((d & 0xff000000) != 0) { - regs.fpu_state = 1; - if (incr < 0) - ad -= (d >> 16) & 0xff; - else - ad += (d >> 16) & 0xff; - } else { - fpu_null (); - } - } - - if ((opcode & 0x38) == 0x18) - m68k_areg (regs, opcode & 7) = ad; - if ((opcode & 0x38) == 0x20) - m68k_areg (regs, opcode & 7) = ad; + int reg; + + // 68030 MMU state saving is annoying! + if (currprefs.mmu_model == 68030) { + int idx = 0; + int r; + int i; + uae_u32 wrd[3]; + mmu030_state[1] |= MMU030_STATEFLAG1_MOVEM1; + for (r = 0; r < 8; r++) { + if (regdir < 0) + reg = 7 - r; + else + reg = r; + if (list & 0x80) { + from_exten_fmovem(®s.fp[reg], &wrd[0], &wrd[1], &wrd[2]); + if (incr < 0) + ad -= 3 * 4; + for (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 { + int r; + for (r = 0; r < 8; r++) { + uae_u32 wrd1, wrd2, wrd3; + if (regdir < 0) + reg = 7 - r; + else + reg = r; + if (list & 0x80) { + from_exten_fmovem(®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 void fround (int reg) +static uaecptr fmovem2fpp (uaecptr ad, uae_u32 list, int incr, int regdir) { - regs.fp[reg].fp = (float)regs.fp[reg].fp; + int reg; + + if (currprefs.mmu_model == 68030) { + uae_u32 wrd[3]; + int idx = 0; + int r; + int i; + 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 (r = 0; r < 8; r++) { + if (regdir < 0) + reg = 7 - r; + else + reg = r; + if (list & 0x80) { + if (incr < 0) + ad -= 3 * 4; + for (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_fmovem (®s.fp[reg], mmu030_fmovem_store[0], mmu030_fmovem_store[1], wrd[2]); + } + } + if (incr > 0) + ad += 3 * 4; + idx++; + } + list <<= 1; + } + } else { + int r; + for (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_fmovem (®s.fp[reg], wrd1, wrd2, wrd3); + } + list <<= 1; + } + } + return ad; +} + +static bool fp_arithmetic(fptype *src, fptype *dst, int extra) +{ + uae_u64 q = 0; + uae_s8 s = 0; + + switch (extra & 0x7f) + { + case 0x00: /* FMOVE */ + fp_move(dst, src); + break; + case 0x40: /* FSMOVE */ + fp_move_single(dst, src); + break; + case 0x44: /* FDMOVE */ + fp_move_double(dst, src); + break; + case 0x01: /* FINT */ + fp_int(dst, src); + break; + case 0x02: /* FSINH */ + fp_sinh(dst, src); + break; + case 0x03: /* FINTRZ */ + fp_intrz(dst, src); + break; + case 0x04: /* FSQRT */ + fp_sqrt(dst, src); + break; + case 0x41: /* FSSQRT */ + fp_sqrt_single(dst, src); + break; + case 0x45: /* FDSQRT */ + fp_sqrt_double(dst, src); + break; + case 0x06: /* FLOGNP1 */ + fp_lognp1(dst, src); + break; + case 0x08: /* FETOXM1 */ + fp_etoxm1(dst, src); + break; + case 0x09: /* FTANH */ + fp_tanh(dst, src); + break; + case 0x0a: /* FATAN */ + fp_atan(dst, src); + break; + case 0x0c: /* FASIN */ + fp_asin(dst, src); + break; + case 0x0d: /* FATANH */ + fp_atanh(dst, src); + break; + case 0x0e: /* FSIN */ + fp_sin(dst, src); + break; + case 0x0f: /* FTAN */ + fp_tan(dst, src); + break; + case 0x10: /* FETOX */ + fp_etox(dst, src); + break; + case 0x11: /* FTWOTOX */ + fp_twotox(dst, src); + break; + case 0x12: /* FTENTOX */ + fp_tentox(dst, src); + break; + case 0x14: /* FLOGN */ + fp_logn(dst, src); + break; + case 0x15: /* FLOG10 */ + fp_log10(dst, src); + break; + case 0x16: /* FLOG2 */ + fp_log2(dst, src); + break; + case 0x18: /* FABS */ + fp_abs(dst, src); + break; + case 0x58: /* FSABS */ + fp_abs_single(dst, src); + break; + case 0x5c: /* FDABS */ + fp_abs_double(dst, src); + break; + case 0x19: /* FCOSH */ + fp_cosh(dst, src); + break; + case 0x1a: /* FNEG */ + fp_neg(dst, src); + break; + case 0x5a: /* FSNEG */ + fp_neg_single(dst, src); + break; + case 0x5e: /* FDNEG */ + fp_neg_double(dst, src); + break; + case 0x1c: /* FACOS */ + fp_acos(dst, src); + break; + case 0x1d: /* FCOS */ + fp_cos(dst, src); + break; + case 0x1e: /* FGETEXP */ + fp_getexp(dst, src); + break; + case 0x1f: /* FGETMAN */ + fp_getman(dst, src); + break; + case 0x20: /* FDIV */ + fp_div(dst, src); + break; + case 0x60: /* FSDIV */ + fp_div_single(dst, src); + break; + case 0x64: /* FDDIV */ + fp_div_double(dst, src); + break; + case 0x21: /* FMOD */ + fpsr_get_quotient(&q, &s); + fp_mod(dst, src, &q, &s); + fpsr_set_quotient(q, s); + break; + case 0x22: /* FADD */ + fp_add(dst, src); + break; + case 0x62: /* FSADD */ + fp_add_single(dst, src); + break; + case 0x66: /* FDADD */ + fp_add_double(dst, src); + break; + case 0x23: /* FMUL */ + fp_mul(dst, src); + break; + case 0x63: /* FSMUL */ + fp_mul_single(dst, src); + break; + case 0x67: /* FDMUL */ + fp_mul_double(dst, src); + break; + case 0x24: /* FSGLDIV */ + fp_sgldiv(dst, src); + break; + case 0x25: /* FREM */ + fpsr_get_quotient(&q, &s); + fp_rem(dst, src, &q, &s); + fpsr_set_quotient(q, s); + break; + case 0x26: /* FSCALE */ + fp_scale(dst, src); + break; + case 0x27: /* FSGLMUL */ + fp_sglmul(dst, src); + break; + case 0x28: /* FSUB */ + fp_sub(dst, src); + break; + case 0x68: /* FSSUB */ + fp_sub_single(dst, src); + break; + case 0x6c: /* FDSUB */ + fp_sub_double(dst, src); + break; + case 0x30: /* FSINCOS */ + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + fp_cos(dst, src); + if (fpsr_make_status()) + return false; + regs.fp[extra & 7] = *dst; + fp_sin(dst, src); + break; + case 0x38: /* FCMP */ + fp_cmp(dst, src); + fpsr_make_status(); + fpsr_set_result(dst); + return false; + case 0x3a: /* FTST */ + fp_tst(dst, src); + fpsr_make_status(); + fpsr_set_result(dst); + return false; + + default: + write_log (_T("Unknown FPU arithmetic function (%02x)\n"), extra & 0x7f); + return false; + } + + fpsr_set_result(dst); + + if (fpsr_make_status()) + return false; + + return true; } -static uaecptr fmovem2mem (uaecptr ad, uae_u32 list, int incr, int regdir) +static void fpuop_arithmetic2 (uae_u32 opcode, uae_u16 extra) { - 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; + int reg = -1; + int v; + fptype src, dst; + uaecptr pc = m68k_getpc () - 4; + uaecptr ad = 0; + +#if DEBUG_FPP + write_log (_T("FPP %04x %04x at %08x\n"), opcode & 0xffff, extra, pc); +#endif + if (fault_if_no_6888x (opcode, extra, pc)) + return; + + switch ((extra >> 13) & 0x7) + { + case 3: + if (fp_exception_pending(true)) + return; + + regs.fpiar = pc; + fpsr_clear_status(); + src = regs.fp[(extra >> 7) & 7]; + v = put_fp_value (&src, opcode, extra, pc, &ad); + if (v <= 0) { + if (v == 0) + fpu_noinst (opcode, pc); + return; + } + fpsr_make_status(); + fpsr_check_arithmetic_exception(0, &src, opcode, extra, ad); + fp_exception_pending(false); // post/mid instruction + 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; + if (extra & 0x0800) + m68k_dreg (regs, opcode & 7) = fpp_get_fpsr (); + if (extra & 0x0400) + m68k_dreg (regs, opcode & 7) = regs.fpiar; + } else { + if (extra & 0x1000) + fpp_set_fpcr (m68k_dreg (regs, opcode & 7)); + if (extra & 0x0800) + fpp_set_fpsr (m68k_dreg (regs, opcode & 7)); + if (extra & 0x0400) + 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; + if (extra & 0x0800) + m68k_areg (regs, opcode & 7) = fpp_get_fpsr (); + if (extra & 0x0400) + m68k_areg (regs, opcode & 7) = regs.fpiar; + } else { + if (extra & 0x1000) + fpp_set_fpcr (m68k_areg (regs, opcode & 7)); + if (extra & 0x0800) + fpp_set_fpsr (m68k_areg (regs, opcode & 7)); + if (extra & 0x0400) + regs.fpiar = m68k_areg (regs, opcode & 7); + } + } else if ((opcode & 0x3f) == 0x3c) { + 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()) + 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 (fault_if_no_fpu (opcode, extra, 0, pc)) + return; + if (extra & 0x1000) + fpp_set_fpcr (ext[0]); + if (extra & 0x0800) + fpp_set_fpsr (ext[1]); + if (extra & 0x0400) + regs.fpiar = ext[2]; + } else { + // immediate as destination + fpu_noinst (opcode, pc); + return; + } + } else if (extra & 0x2000) { + /* FMOVEM FPP->memory */ + uae_u32 ad; + int incr = 0; + + if (get_fp_ad (opcode, &ad) == 0) { + fpu_noinst (opcode, pc); + return; + } + if (fault_if_no_fpu (opcode, extra, ad, pc)) + return; + + if ((opcode & 0x38) == 0x20) { + if (extra & 0x1000) + incr += 4; + if (extra & 0x0800) + incr += 4; + if (extra & 0x0400) + incr += 4; + } + ad -= incr; + if (extra & 0x1000) { + x_cp_put_long (ad, regs.fpcr & 0xffff); + ad += 4; + } + if (extra & 0x0800) { + x_cp_put_long (ad, fpp_get_fpsr ()); + ad += 4; + } + if (extra & 0x0400) { + x_cp_put_long (ad, regs.fpiar); + ad += 4; + } + ad -= incr; + if ((opcode & 0x38) == 0x18) + m68k_areg (regs, opcode & 7) = ad; + if ((opcode & 0x38) == 0x20) + m68k_areg (regs, opcode & 7) = ad; + } else { + /* FMOVEM memory->FPP */ + uae_u32 ad; + int incr = 0; + + if (get_fp_ad (opcode, &ad) == 0) { + fpu_noinst (opcode, pc); + return; + } + if (fault_if_no_fpu (opcode, extra, ad, pc)) + return; + + if((opcode & 0x38) == 0x20) { + if (extra & 0x1000) + incr += 4; + if (extra & 0x0800) + incr += 4; + if (extra & 0x0400) + incr += 4; + ad = ad - incr; + } + if (extra & 0x1000) { + fpp_set_fpcr (x_cp_get_long (ad)); + ad += 4; + } + if (extra & 0x0800) { + fpp_set_fpsr (x_cp_get_long (ad)); + ad += 4; + } + if (extra & 0x0400) { + regs.fpiar = x_cp_get_long (ad); + ad += 4; + } + if ((opcode & 0x38) == 0x18) + m68k_areg (regs, opcode & 7) = ad; + if ((opcode & 0x38) == 0x20) + m68k_areg (regs, opcode & 7) = ad - incr; + } + return; + + case 6: + case 7: + { + uae_u32 ad, list = 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 */ + case 2: /* static postinc */ + list = extra & 0xff; + break; + case 1: /* dynamic pred */ + case 3: /* dynamic postinc */ + if (fault_if_60()) + return; + list = m68k_dreg (regs, (extra >> 4) & 3) & 0xff; + break; + } + if ((opcode & 0x38) == 0x20) { // -(an) + incr = -1; + switch ((extra >> 11) & 3) + { + case 0: /* static pred */ + case 1: /* dynamic pred */ + regdir = -1; + break; + } + } + + if (extra & 0x2000) { + /* FMOVEM FPP->memory */ + ad = fmovem2mem (ad, list, incr, regdir); + } else { + /* FMOVEM memory->FPP */ + ad = fmovem2fpp (ad, list, incr, regdir); + } + if ((opcode & 0x38) == 0x18 || (opcode & 0x38) == 0x20) // (an)+ or -(an) + m68k_areg (regs, opcode & 7) = ad; + } + return; + + case 0: + case 2: /* Extremely common */ + if (fp_exception_pending(true)) + return; + + regs.fpiar = pc; + reg = (extra >> 7) & 7; + if ((extra & 0xfc00) == 0x5c00) { + if (fault_if_unimplemented_680x0 (opcode, extra, ad, pc, &src, reg)) + return; + if (extra & 0x40) { + // 6888x and ROM constant 0x40 - 0x7f: f-line + fpu_noinst (opcode, pc); + return; + } + fpsr_clear_status(); + fpu_get_constant(®s.fp[reg], extra & 0x3f); + fpsr_make_status(); + fpsr_check_arithmetic_exception(0, &src, opcode, extra, ad); + return; + } + + // 6888x does not have special exceptions, check immediately + if (fault_if_unimplemented_6888x (opcode, extra, pc)) + return; + + fpsr_clear_status(); + + v = get_fp_value (opcode, extra, &src, pc, &ad); + if (v <= 0) { + if (v == 0) + fpu_noinst (opcode, pc); + return; + } + + dst = regs.fp[reg]; + + if (fp_is_dyadic(extra)) + normalize_or_fault_if_no_denormal_support_dst(opcode, extra, ad, pc, &dst, &src); + + // check for 680x0 unimplemented instruction + if (fault_if_unimplemented_680x0 (opcode, extra, ad, pc, &src, reg)) + return; + + // unimplemented datatype was checked in get_fp_value + if (regs.fp_unimp_pend) { + fp_exception_pending(false); // simplification: always mid/post-instruction exception + return; + } + + v = fp_arithmetic(&src, &dst, extra); + + fpsr_check_arithmetic_exception(0, &src, opcode, extra, ad); + + if (v) + regs.fp[reg] = dst; + + fp_exception_pending(false); // simplification: always mid/post-instruction exception + + return; + default: + break; + } + fpu_noinst (opcode, pc); } -static uaecptr fmovem2fpp (uaecptr ad, uae_u32 list, int incr, int regdir) +void fpuop_arithmetic (uae_u32 opcode, uae_u16 extra) { - 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; + regs.fpu_state = 1; + regs.fp_exception = false; + fpu_mmu_fixup = false; + fpuop_arithmetic2 (opcode, extra); + if (fpu_mmu_fixup) { + mmufixup[0].reg = -1; + } } -static void fpuop_arithmetic2 (uae_u32 opcode, uae_u16 extra) +void fpuop_save (uae_u32 opcode) { - int reg = -1; - int v; - fptype src; - fpdata srcd; - uaecptr pc = m68k_getpc () - 4; - uaecptr ad = 0; - bool sgl; - + uae_u32 ad, adp; + int incr = (opcode & 0x38) == 0x20 ? -1 : 1; + int fpu_version = get_fpu_version (); + uaecptr pc = m68k_getpc () - 2; + int i; + + regs.fp_exception = false; #if DEBUG_FPP - if (!isinrom ()) - write_log (_T("FPP %04lx %04x at %08lx\n"), opcode & 0xffff, extra, pc); -#endif - if (fault_if_no_6888x (opcode, extra, pc)) - return; - - switch ((extra >> 13) & 0x7) - { - case 3: - 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; - if (extra & 0x0800) - m68k_dreg (regs, opcode & 7) = get_fpsr (); - if (extra & 0x0400) - m68k_dreg (regs, opcode & 7) = regs.fpiar; - } else { - if (extra & 0x1000) { - regs.fpcr = m68k_dreg (regs, opcode & 7); - native_set_fpucw (regs.fpcr); - } - if (extra & 0x0800) - set_fpsr (m68k_dreg (regs, opcode & 7)); - if (extra & 0x0400) - 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; - if (extra & 0x0800) - m68k_areg (regs, opcode & 7) = get_fpsr (); - if (extra & 0x0400) - m68k_areg (regs, opcode & 7) = regs.fpiar; - } else { - if (extra & 0x1000) { - regs.fpcr = m68k_areg (regs, opcode & 7); - native_set_fpucw (regs.fpcr); - } - if (extra & 0x0800) - set_fpsr (m68k_areg (regs, opcode & 7)); - if (extra & 0x0400) - 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 = ext[0]; - native_set_fpucw (regs.fpcr); - } - if (extra & 0x0800) - set_fpsr (ext[1]); - if (extra & 0x0400) - regs.fpiar = ext[2]; - } - } else if (extra & 0x2000) { - /* FMOVEM FPP->memory */ - uae_u32 ad; - int incr = 0; - - if (get_fp_ad (opcode, &ad) == 0) { - fpu_noinst (opcode, pc); - return; - } - if (fault_if_no_fpu (opcode, extra, ad, pc)) - return; - - if ((opcode & 0x38) == 0x20) { - if (extra & 0x1000) - incr += 4; - if (extra & 0x0800) - incr += 4; - if (extra & 0x0400) - incr += 4; - } - ad -= incr; - if (extra & 0x1000) { - x_cp_put_long (ad, regs.fpcr & 0xffff); - ad += 4; - } - if (extra & 0x0800) { - x_cp_put_long (ad, get_fpsr ()); - ad += 4; - } - if (extra & 0x0400) { - x_cp_put_long (ad, regs.fpiar); - ad += 4; - } - ad -= incr; - if ((opcode & 0x38) == 0x18) - m68k_areg (regs, opcode & 7) = ad; - if ((opcode & 0x38) == 0x20) - m68k_areg (regs, opcode & 7) = ad; - } else { - /* FMOVEM memory->FPP */ - uae_u32 ad; - int incr = 0; - - if (get_fp_ad (opcode, &ad) == 0) { - fpu_noinst (opcode, pc); - return; - } - if (fault_if_no_fpu (opcode, extra, ad, pc)) - return; - - if((opcode & 0x38) == 0x20) { - if (extra & 0x1000) - incr += 4; - if (extra & 0x0800) - incr += 4; - if (extra & 0x0400) - incr += 4; - ad = ad - incr; - } - if (extra & 0x1000) { - regs.fpcr = x_cp_get_long (ad); - native_set_fpucw (regs.fpcr); - ad += 4; - } - if (extra & 0x0800) { - set_fpsr (x_cp_get_long (ad)); - ad += 4; - } - if (extra & 0x0400) { - regs.fpiar = x_cp_get_long (ad); - ad += 4; - } - if ((opcode & 0x38) == 0x18) - m68k_areg (regs, opcode & 7) = ad; - if ((opcode & 0x38) == 0x20) - m68k_areg (regs, opcode & 7) = ad - incr; - } - return; - - case 6: - case 7: - { - uae_u32 ad, list = 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 */ - ad = fmovem2mem (ad, list, incr, regdir); - } else { - /* FMOVEM memory->FPP */ - 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) { - 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 (®s.fp[reg].fp); - return; - } - - // 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; - - // 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; - - regs.fpiar = pc; - - 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 & 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].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); -#else - regs.fp[reg].fp *= exp (*fp_ln_2 * (int) src); + write_log (_T("fsave_opp at %08x\n"), m68k_getpc ()); #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; - } - // round to float? - if (sgl || (extra & 0x44) == 0x40) - fround (reg); - MAKE_FPSR (®s.fp[reg].fp); - return; - default: - break; - } - fpu_noinst (opcode, pc); + + if (fault_if_no_6888x (opcode, 0, pc)) + return; + + if (get_fp_ad (opcode, &ad) == 0) { + 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 NULL/IDLE/EXCP frame. */ + int frame_size = 12; + uae_u32 frame_id; + + if (regs.fpu_exp_state > 1) { + frame_id = 0x0000e000 | fsave_data.v; + +#if 0 //EXCEPTION_FPP + write_log(_T("68060 FSAVE EXCP %s\n"), fp_print(&fsave_data.src)); +#endif + + } else { + frame_id = regs.fpu_state == 0 ? 0x00000000 : 0x00006000; + } + if (incr < 0) + ad -= frame_size; + adp = ad; + x_put_long (ad, (fsave_data.eo[0] & 0xffff0000) | frame_id); + ad += 4; + x_put_long (ad, fsave_data.eo[1]); + ad += 4; + x_put_long (ad, fsave_data.eo[2]); + ad += 4; + + } else if (currprefs.fpu_model == 68040) { + + 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; + adp = ad; + x_put_long (ad, frame_id); + ad += 4; + } else { + /* 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; + +#if 0//EXCEPTION_FPP + write_log(_T("68040 FSAVE %d (%d), CMDREG=%04X"), regs.fp_exp_pend, frame_size, extra); + if (regs.fp_exp_pend == FPU_EXP_UNIMP_DATATYPE_PACKED_PRE) { + write_log(_T(" PACKED %08x-%08x-%08x"), fsave_data.pack[0], fsave_data.pack[1], fsave_data.pack[2]); + } else if (regs.fp_exp_pend == FPU_EXP_UNIMP_DATATYPE_PACKED_POST) { + write_log(_T(" SRC=%s (%08x-%08x-%08x %d)"), + fp_print(&fsave_data.src), src1[0], src1[1], src1[2], stag); + write_log(_T(" DST=%s (%08x-%08x-%08x %d)"), + fp_print(&fsave_data.dst), src2[0], src2[1], src2[2], dtag); + } + write_log(_T("\n")); +#endif + + if (incr < 0) + ad -= frame_size; + adp = ad; + 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, fsave_data.wbt[0]); // WBTS/WBTE + ad += 4; + x_put_long(ad, fsave_data.wbt[1]); // WBTM + ad += 4; + x_put_long(ad, fsave_data.wbt[2]); // WBTM + ad += 4; + x_put_long(ad, 0); + ad += 4; + x_put_long(ad, fsave_data.fpiarcu); // 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, fsave_data.cmdreg3b<<16); // CMDREG3B + ad += 4; + x_put_long (ad, 0); + ad += 4; + } + x_put_long (ad, (fsave_data.stag<<29) | (fsave_data.wbtm66<<26) | (fsave_data.grs<<23)); // STAG + ad += 4; + x_put_long (ad, fsave_data.cmdreg1b<<16); // CMDREG1B + ad += 4; + x_put_long (ad, (fsave_data.dtag<<29) | (fsave_data.wbte15<<20)); // DTAG + ad += 4; + x_put_long (ad, (fsave_data.e1<<26) | (fsave_data.e3<<25) | (fsave_data.t<<20)); + ad += 4; + x_put_long (ad, fsave_data.fpt[0]); // FPTS/FPTE + ad += 4; + x_put_long (ad, fsave_data.fpt[1]); // FPTM + ad += 4; + x_put_long (ad, fsave_data.fpt[2]); // FPTM + ad += 4; + x_put_long (ad, fsave_data.et[0]); // ETS/ETE + ad += 4; + x_put_long (ad, fsave_data.et[1]); // ETM + ad += 4; + x_put_long (ad, fsave_data.et[2]); // ETM + ad += 4; + } + } else { /* 68881/68882 */ + uae_u32 biu_flags = 0x540effff; + int frame_size = currprefs.fpu_model == 68882 ? 0x3c : 0x1c; + uae_u32 frame_id = regs.fpu_state == 0 ? ((frame_size - 4) << 16) : (fpu_version << 24) | ((frame_size - 4) << 16); + + regs.fp_exp_pend = 0; + if (regs.fpu_exp_state) { + biu_flags |= 0x20000000; + } else { + biu_flags |= 0x08000000; + } + if (regs.fpu_state == 0) + frame_size = 4; + + if (currprefs.mmu_model) { + i = 0; + if (incr < 0) + ad -= frame_size; + adp = ad; + if(mmu030_state[0] == i) { + x_put_long(ad, frame_id); // frame id + mmu030_state[0]++; + } + ad += 4; + i++; + if (regs.fpu_state != 0) { // idle frame + if(mmu030_state[0] == i) { + x_put_long(ad, fsave_data.ccr); // command/condition register + mmu030_state[0]++; + } + ad += 4; + i++; + if (currprefs.fpu_model == 68882) { + while (i <= 9) { + if (mmu030_state[0] == i) { + x_put_long(ad, 0x00000000); // internal + mmu030_state[0]++; + } + ad += 4; + i++; + } + } + if (mmu030_state[0] == i) { + x_put_long (ad, fsave_data.eo[0]); // exceptional operand lo + mmu030_state[0]++; + } + ad += 4; + i++; + if (mmu030_state[0] == i) { + x_put_long (ad, fsave_data.eo[1]); // exceptional operand mid + mmu030_state[0]++; + } + ad += 4; + i++; + if (mmu030_state[0] == i) { + x_put_long (ad, fsave_data.eo[2]); // exceptional operand hi + mmu030_state[0]++; + } + ad += 4; + i++; + if (mmu030_state[0] == i) { + x_put_long(ad, 0x00000000); // operand register + mmu030_state[0]++; + } + ad += 4; + i++; + if (mmu030_state[0] == i) { + x_put_long(ad, biu_flags); // biu flags + mmu030_state[0]++; + } + ad += 4; + } + } else { + if (incr < 0) + ad -= frame_size; + adp = ad; + x_put_long(ad, frame_id); // frame id + ad += 4; + if (regs.fpu_state != 0) { // idle frame + x_put_long(ad, fsave_data.ccr); // command/condition register + ad += 4; + if(currprefs.fpu_model == 68882) { + for(i = 0; i < 32; i += 4) { + x_put_long(ad, 0x00000000); // internal + ad += 4; + } + } + x_put_long(ad, fsave_data.eo[0]); // exceptional operand hi + ad += 4; + x_put_long(ad, fsave_data.eo[1]); // exceptional operand mid + ad += 4; + x_put_long(ad, fsave_data.eo[2]); // exceptional operand lo + ad += 4; + x_put_long(ad, 0x00000000); // operand register + ad += 4; + x_put_long(ad, biu_flags); // biu flags + ad += 4; + } + } + } + + if ((opcode & 0x38) == 0x20) // predecrement + m68k_areg (regs, opcode & 7) = adp; + regs.fpu_exp_state = 0; } -void fpuop_arithmetic (uae_u32 opcode, uae_u16 extra) +void fpuop_restore (uae_u32 opcode) { - 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; - } - } - // logging only so far - write_log (_T("FPU exception: %08x %d!\n"), regs.fpsr, vector); - } + uaecptr pc = m68k_getpc () - 2; + uae_u32 ad; + uae_u32 d; + + int frame_version; + int fpu_version = get_fpu_version(); + + regs.fp_exception = false; +#if DEBUG_FPP + write_log (_T("frestore_opp at %08x\n"), m68k_getpc ()); #endif + + if (fault_if_no_6888x (opcode, 0, pc)) + return; + + if (get_fp_ad (opcode, &ad) == 0) { + fpu_op_illg (opcode, 0, pc); + return; + } + + if (fault_if_no_fpu (opcode, 0, ad, pc)) + return; + regs.fpiar = pc; + + // FRESTORE does not support predecrement + + d = x_get_long (ad); + ad += 4; + + frame_version = (d >> 24) & 0xff; + + if (currprefs.fpu_model == 68060) { + int ff = (d >> 8) & 0xff; + uae_u32 v = d & 0x7; + fsave_data.eo[0] = d & 0xffff0000; + + fsave_data.eo[1] = x_get_long (ad); + ad += 4; + fsave_data.eo[2] = x_get_long (ad); + ad += 4; + + if (ff == 0x60) { + regs.fpu_state = 1; + regs.fpu_exp_state = 0; + } else if (ff == 0xe0) { + regs.fpu_state = 1; + regs.fpu_exp_state = 2; + if (v == 7) { + regs.fp_unimp_pend = 1; + } else { + regs.fp_exp_pend = 48 + v; + } + } else if (ff) { + write_log (_T("FRESTORE invalid frame format %02X!\n"), ff); + Exception(14); + return; + } else { + fpu_null(); + } + } else if (currprefs.fpu_model == 68040) { + + if (frame_version == fpu_version) { // not null frame + uae_u32 frame_size = (d >> 16) & 0xff; + + if (frame_size == 0x60) { // busy + fptype src, dst; + uae_u32 tmp, v, opclass, cmdreg1b, fpte15, et15, cusavepc; + + ad += 0x4; // offset to CU_SAVEPC field + tmp = x_get_long (ad); + cusavepc = tmp >> 24; + ad += 0x20; // offset to FPIARCU field + regs.fpiar = x_get_long (ad); + ad += 0x14; // offset to ET15 field + tmp = x_get_long (ad); + et15 = (tmp & 0x10000000) >> 28; + ad += 0x4; // offset to CMDREG1B field + fsave_data.cmdreg1b = x_get_long (ad); + fsave_data.cmdreg1b >>= 16; + cmdreg1b = fsave_data.cmdreg1b; + ad += 0x4; // offset to FPTE15 field + tmp = x_get_long (ad); + fpte15 = (tmp & 0x10000000) >> 28; + ad += 0x8; // offset to FPTE field + fsave_data.fpt[0] = x_get_long (ad); + ad += 0x4; + fsave_data.fpt[1] = x_get_long (ad); + ad += 0x4; + fsave_data.fpt[2] = x_get_long (ad); + ad += 0x4; // offset to ET field + fsave_data.et[0] = x_get_long (ad); + ad += 0x4; + fsave_data.et[1] = x_get_long (ad); + ad += 0x4; + fsave_data.et[2] = x_get_long (ad); + ad += 0x4; + + opclass = (cmdreg1b >> 13) & 0x7; // just to be sure + + if (cusavepc == 0xFE) { + if (opclass == 0 || opclass == 2) { + to_exten_fmovem(&dst, fsave_data.fpt[0], fsave_data.fpt[1], fsave_data.fpt[2]); + fp_denormalize(&dst, fpte15); + to_exten_fmovem(&src, fsave_data.et[0], fsave_data.et[1], fsave_data.et[2]); + fp_denormalize(&src, et15); +#if EXCEPTION_FPP + uae_u32 tmpsrc[3], tmpdst[3]; + from_exten_fmovem(&src, &tmpsrc[0], &tmpsrc[1], &tmpsrc[2]); + from_exten_fmovem(&dst, &tmpdst[0], &tmpdst[1], &tmpdst[2]); + write_log (_T("FRESTORE src = %08X %08X %08X, dst = %08X %08X %08X, extra = %04X\n"), + tmpsrc[0], tmpsrc[1], tmpsrc[2], tmpdst[0], tmpdst[1], tmpdst[2], cmdreg1b); +#endif + fpsr_clear_status(); + + v = fp_arithmetic(&src, &dst, cmdreg1b); + + if (v) + regs.fp[(cmdreg1b>>7)&7] = dst; + + fpsr_check_arithmetic_exception(0, &src, regs.fp_opword, cmdreg1b, regs.fp_ea); + } else { + write_log (_T("FRESTORE resume of opclass %d instruction not supported!\n"), opclass); + } + } + } else if (frame_size == 0x30 || frame_size == 0x28) { // unimp + + + // TODO: restore frame contents + ad += frame_size; + + } else if (frame_size == 0x00) { // idle + regs.fpu_state = 1; + regs.fpu_exp_state = 0; + } else { + write_log (_T("FRESTORE invalid frame size %02X!\n"), frame_size); + Exception(14); + return; + } + } else if (frame_version == 0x00) { // null frame + fpu_null(); + } else { + write_log (_T("FRESTORE invalid frame version %02X!\n"), frame_version); + Exception(14); + return; + } + } else { + // 6888x + + if (frame_version == fpu_version) { // not null frame + uae_u32 biu_flags; + uae_u32 frame_size = (d >> 16) & 0xff; + regs.fpu_state = 1; + + if (frame_size == 0x18 || frame_size == 0x38) { // idle + fsave_data.ccr = x_get_long (ad); + ad += 4; + // 68882 internal registers (32 bytes, unused) + ad += frame_size - 24; + fsave_data.eo[0] = x_get_long (ad); + ad += 4; + fsave_data.eo[1] = x_get_long (ad); + ad += 4; + fsave_data.eo[2] = x_get_long (ad); + ad += 4; + // operand register (unused) + ad += 4; + biu_flags = x_get_long (ad); + ad += 4; + + if ((biu_flags & 0x08000000) == 0x00000000) { + regs.fpu_exp_state = 2; + regs.fp_exp_pend = fpsr_get_vector(regs.fpsr & regs.fpcr & 0xff00); + } else { + regs.fpu_exp_state = 0; + regs.fp_exp_pend = 0; + } + } else if (frame_size == 0xB4 || frame_size == 0xD4) { + write_log (_T("FRESTORE of busy frame not supported\n")); + ad += frame_size; + } else { + write_log (_T("FRESTORE invalid frame size %02X!\n"), frame_size); + Exception(14); + return; + } + } else if (frame_version == 0x00) { // null frame + fpu_null (); + } else { + write_log (_T("FRESTORE invalid frame version %02X!\n"), frame_version); + Exception(14); + return; + } + } + + if ((opcode & 0x38) == 0x18) // postincrement + m68k_areg (regs, opcode & 7) = ad; + + fp_exception_pending(false); } void fpu_reset (void) { - regs.fpcr = regs.fpsr = regs.fpiar = 0; - regs.fpu_exp_state = 0; - fpset (®s.fp_result, 1); - native_set_fpucw (regs.fpcr); - fpux_restore (NULL); + regs.fpiar = 0; + regs.fpu_exp_state = 0; + fpp_set_fpcr (0); + fpp_set_fpsr (0); } +#if 0 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++) { - 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 (); - } - 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_u32 w1, w2, w3; + int i; + uae_u32 flags; + + fpu_reset(); + changed_prefs.fpu_model = currprefs.fpu_model = restore_u32 (); + flags = restore_u32 (); + for (i = 0; i < 8; i++) { + w1 = restore_u16 () << 16; + w2 = restore_u32 (); + w3 = restore_u32 (); + 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 (); + } + if (flags & 0x40000000) { + w1 = restore_u16() << 16; + w2 = restore_u32(); + w3 = restore_u32(); + to_exten(®s.exp_src1, w1, w2, w3); + w1 = restore_u16() << 16; + w2 = restore_u32(); + w3 = restore_u32(); + 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_u32 w1, w2, w3; - uae_u8 *dstbak, *dst; - int i; - - *len = 0; - if (currprefs.fpu_model == 0) - return 0; - if (dstptr) - dstbak = dst = dstptr; - else - 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 | 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++) { - from_exten (®s.fp[i], &w1, &w2, &w3); - save_u32 (w1); - save_u32 (w2); - save_u16 (w3); - } - 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; + uae_u32 w1, w2, w3; + uae_u8 *dstbak, *dst; + int i; + + *len = 0; +#ifndef WINUAE_FOR_HATARI + /* Under Hatari, we save all FPU variables, even if fpu_model==0 */ + if (currprefs.fpu_model == 0) + return 0; +#endif + if (dstptr) + dstbak = dst = dstptr; + else + 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 | 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++) { + from_exten (®s.fp[i], &w1, &w2, &w3); + save_u16 (w1 >> 16); + save_u32 (w2); + save_u32 (w3); + } + 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_u16(w1 >> 16); + save_u32(w2); + save_u32(w3); + from_exten(®s.exp_src2, &w1, &w2, &w3); + save_u16(w1 >> 16); + save_u32(w2); + save_u32(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