mirror of
https://github.com/phabrics/Run-Sun3-SunOS-4.1.1.git
synced 2026-04-29 11:02:59 -04:00
2251 lines
75 KiB
C
2251 lines
75 KiB
C
/* $Id: m6888x.c,v 1.4 2007/08/25 20:37:30 fredette Exp $ */
|
|
|
|
/* ic/m68k/m6888x.c - m68k floating-point implementation */
|
|
|
|
/*
|
|
* Copyright (c) 2004 Matt Fredette
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by Matt Fredette.
|
|
* 4. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <tme/common.h>
|
|
_TME_RCSID("$Id: m6888x.c,v 1.4 2007/08/25 20:37:30 fredette Exp $");
|
|
|
|
/* includes: */
|
|
#include "m68k-impl.h"
|
|
|
|
/* macros: */
|
|
|
|
/* m6888x FPCR bits: */
|
|
#define TME_M6888X_FPCR_RND_MASK (0x00000030)
|
|
#define TME_M6888X_FPCR_RND_RN (0x00000000)
|
|
#define TME_M6888X_FPCR_RND_RZ (0x00000010)
|
|
#define TME_M6888X_FPCR_RND_RM (0x00000020)
|
|
#define TME_M6888X_FPCR_RND_RP (0x00000030)
|
|
#define TME_M6888X_FPCR_PREC_MASK (0x000000c0)
|
|
#define TME_M6888X_FPCR_PREC_X (0x00000000)
|
|
#define TME_M6888X_FPCR_PREC_S (0x00000040)
|
|
#define TME_M6888X_FPCR_PREC_D (0x00000080)
|
|
#define TME_M6888X_FPCR_PREC_UNDEF (0x000000c0)
|
|
#define TME_M6888X_FPCR_ENABLE_INEX1 TME_BIT(8)
|
|
#define TME_M6888X_FPCR_ENABLE_INEX2 TME_BIT(9)
|
|
#define TME_M6888X_FPCR_ENABLE_DZ TME_BIT(10)
|
|
#define TME_M6888X_FPCR_ENABLE_UNFL TME_BIT(11)
|
|
#define TME_M6888X_FPCR_ENABLE_OVFL TME_BIT(12)
|
|
#define TME_M6888X_FPCR_ENABLE_OPERR TME_BIT(13)
|
|
#define TME_M6888X_FPCR_ENABLE_SNAN TME_BIT(14)
|
|
#define TME_M6888X_FPCR_ENABLE_BSUN TME_BIT(15)
|
|
|
|
/* m6888x FPSR bits: */
|
|
#define TME_M6888X_FPSR_AEXC_INEX TME_BIT(3)
|
|
#define TME_M6888X_FPSR_AEXC_DZ TME_BIT(4)
|
|
#define TME_M6888X_FPSR_AEXC_UNFL TME_BIT(5)
|
|
#define TME_M6888X_FPSR_AEXC_OVFL TME_BIT(6)
|
|
#define TME_M6888X_FPSR_AEXC_IOP TME_BIT(7)
|
|
#define TME_M6888X_FPSR_EXC_INEX1 TME_M6888X_FPCR_ENABLE_INEX1
|
|
#define TME_M6888X_FPSR_EXC_INEX2 TME_M6888X_FPCR_ENABLE_INEX2
|
|
#define TME_M6888X_FPSR_EXC_DZ TME_M6888X_FPCR_ENABLE_DZ
|
|
#define TME_M6888X_FPSR_EXC_UNFL TME_M6888X_FPCR_ENABLE_UNFL
|
|
#define TME_M6888X_FPSR_EXC_OVFL TME_M6888X_FPCR_ENABLE_OVFL
|
|
#define TME_M6888X_FPSR_EXC_OPERR TME_M6888X_FPCR_ENABLE_OPERR
|
|
#define TME_M6888X_FPSR_EXC_SNAN TME_M6888X_FPCR_ENABLE_SNAN
|
|
#define TME_M6888X_FPSR_EXC_BSUN TME_M6888X_FPCR_ENABLE_BSUN
|
|
#define TME_M6888X_FPSR_QUOTIENT (0x00ff0000)
|
|
#define TME_M6888X_FPSR_CC_NAN TME_BIT(24)
|
|
#define TME_M6888X_FPSR_CC_I TME_BIT(25)
|
|
#define TME_M6888X_FPSR_CC_Z TME_BIT(26)
|
|
#define TME_M6888X_FPSR_CC_N TME_BIT(27)
|
|
|
|
/* m6888x exceptions: */
|
|
#define TME_M6888X_VECTOR_BSUN (0x30)
|
|
#define TME_M6888X_VECTOR_INEX (0x31)
|
|
#define TME_M6888X_VECTOR_DZ (0x32)
|
|
#define TME_M6888X_VECTOR_UNFL (0x33)
|
|
#define TME_M6888X_VECTOR_OPERR (0x34)
|
|
#define TME_M6888X_VECTOR_OVFL (0x35)
|
|
#define TME_M6888X_VECTOR_SNAN (0x36)
|
|
|
|
/* m6888x frame versions: */
|
|
#define TME_M6888X_FRAME_VERSION_NULL (0x00)
|
|
#define TME_M6888X_FRAME_VERSION_IDLE_M68881 (0x1f)
|
|
#define TME_M6888X_FRAME_VERSION_IDLE_M68882 (0x21)
|
|
#define TME_M6888X_FRAME_VERSION_IDLE_M68040 (0x23)
|
|
|
|
/* m6888x frame sizes: */
|
|
#define TME_M6888X_FRAME_SIZE_NULL (0x00)
|
|
#define TME_M6888X_FRAME_SIZE_IDLE_M68881 (0x18)
|
|
#define TME_M6888X_FRAME_SIZE_IDLE_M68882 (0x38)
|
|
#define TME_M6888X_FRAME_SIZE_IDLE_M68040 (0x00)
|
|
|
|
/* bits in a packed decimal real: */
|
|
#define TME_M6888X_PACKEDDEC_SM TME_BIT(31)
|
|
#define TME_M6888X_PACKEDDEC_SE TME_BIT(30)
|
|
#define TME_M6888X_PACKEDDEC_YY (TME_BIT(29) | TME_BIT(28))
|
|
|
|
/* rounding precisions: */
|
|
#define TME_M6888X_ROUNDING_PRECISION_CTL (0)
|
|
#define TME_M6888X_ROUNDING_PRECISION_SINGLE (32)
|
|
#define TME_M6888X_ROUNDING_PRECISION_DOUBLE (64)
|
|
#define TME_M6888X_ROUNDING_PRECISION_EXTENDED80 (80)
|
|
|
|
/* operation types: */
|
|
#define TME_M6888X_OPTYPE_MONADIC (0)
|
|
#define TME_M6888X_OPTYPE_DYADIC_SRC_DST (1)
|
|
#define TME_M6888X_OPTYPE_DYADIC_DST_SRC (2)
|
|
|
|
/* special opmodes: */
|
|
#define TME_M6888X_FPGEN_OPMODE_FCMP (0x38)
|
|
#define TME_M6888X_FPGEN_OPMODE_FTST (0x3a)
|
|
#define TME_M6888X_FPGEN_OPMODE_OTHER (0xff)
|
|
|
|
/* this causes an exception if there is no FPU, or if it isn't enabled: */
|
|
#define TME_M68K_INSN_FPU \
|
|
do { \
|
|
if (!ic->tme_m68k_fpu_enabled) { \
|
|
TME_M68K_INSN_EXCEPTION(TME_M68K_EXCEPTION_ILL); \
|
|
} \
|
|
} while (/* CONSTCOND */ 0)
|
|
|
|
/* these declare an m68k FPgen function: */
|
|
#define TME_M6888X_FPGEN_DECL(name) \
|
|
static void name _TME_P((struct tme_m68k *, const struct tme_float *, struct tme_float *))
|
|
#ifdef __STDC__
|
|
#define TME_M6888X_FPGEN(name) \
|
|
static void name(struct tme_m68k *ic, const struct tme_float *src, struct tme_float *dst)
|
|
#else /* !__STDC__ */
|
|
#define TME_M6888X_FPGEN(name) \
|
|
static void name(ic, src, dst) \
|
|
struct tme_m68k *ic; \
|
|
const struct tme_float *src; \
|
|
struct tme_float *dst;
|
|
#endif /* !__STDC__ */
|
|
|
|
/* this gets the offset of a function in the IEEE 754 operations structure: */
|
|
#define TME_M6888X_IEEE754_OP(func) ((unsigned long) ((char *) &((struct tme_ieee754_ops *) 0)->func))
|
|
|
|
/* these invoke an IEEE 754 operation: */
|
|
#define _TME_M6888X_IEEE754_OP(func, x) \
|
|
do { \
|
|
if ((func) == NULL) { \
|
|
if (ic->tme_m68k_fpu_incomplete_abort) { \
|
|
abort(); \
|
|
} \
|
|
TME_M68K_INSN_EXCEPTION(TME_M68K_EXCEPTION_ILL); \
|
|
} \
|
|
(*(func)) x; \
|
|
} while (/* CONSTCOND */ 0)
|
|
#define TME_M6888X_IEEE754_OP_MONADIC(func, src, dst) \
|
|
_TME_M6888X_IEEE754_OP(ic->tme_m68k_fpu_ieee754_ops->func, (&ic->tme_m68k_fpu_ieee754_ctl, src, dst))
|
|
#define TME_M6888X_IEEE754_OP_DYADIC(func, src0, src1, dst) \
|
|
_TME_M6888X_IEEE754_OP(ic->tme_m68k_fpu_ieee754_ops->func, (&ic->tme_m68k_fpu_ieee754_ctl, src0, src1, dst))
|
|
#define TME_M6888X_IEEE754_OP_FUNC(ops_offset) \
|
|
(*((void **) (((char *) ic->tme_m68k_fpu_ieee754_ops) + (ops_offset))))
|
|
#define TME_M6888X_IEEE754_OP_RUN(ops_offset, t, x) \
|
|
_TME_M6888X_IEEE754_OP(((void (*) _TME_P(t)) TME_M6888X_IEEE754_OP_FUNC(ops_offset)), x)
|
|
|
|
/* this gets the Nth raw unsigned 32-bit word from an EA operand: */
|
|
#define TME_M6888X_EA_OP32(n) (ic->tme_m68k_ireg_uint32(op1_ireg32 + (n)))
|
|
|
|
/* this gets the Nth raw digit from a packed decimal operand: */
|
|
#define TME_M6888X_PD_DIGIT(n) ((TME_M6888X_EA_OP32((n) / 8) >> (4 * ((n) % 8))) & 0xf)
|
|
|
|
/* types: */
|
|
|
|
/* the FPgen opmode table: */
|
|
struct tme_m6888x_fpgen {
|
|
|
|
/* any m6888x-specific function. this is normally NULL: */
|
|
void (*tme_m6888x_fpgen_func) _TME_P((struct tme_m68k *,
|
|
const struct tme_float *,
|
|
struct tme_float *));
|
|
|
|
/* unless there is an m6888x-specific function, this is the offset
|
|
in the IEEE 754 operations struct of the function: */
|
|
unsigned long tme_m6888x_fpgen_func_ops_offset;
|
|
|
|
/* the FPU types that have this function: */
|
|
tme_uint8_t tme_m6888x_fpgen_fpu_types;
|
|
|
|
/* the operation type: */
|
|
tme_uint8_t tme_m6888x_fpgen_optype;
|
|
|
|
/* the rounding mode used by the function: */
|
|
tme_uint8_t tme_m6888x_fpgen_rounding_mode;
|
|
|
|
/* the rounding precision used by the function: */
|
|
tme_uint8_t tme_m6888x_fpgen_rounding_precision;
|
|
};
|
|
|
|
/* an m6888x frame: */
|
|
struct tme_m6888x_frame {
|
|
|
|
/* the frame version: */
|
|
tme_uint8_t tme_m6888x_frame_version;
|
|
|
|
/* the frame size: */
|
|
tme_uint8_t tme_m6888x_frame_size;
|
|
|
|
/* reserved: */
|
|
tme_uint16_t tme_m6888x_frame_reserved2;
|
|
|
|
/* the command/condition register for an IDLE frame: */
|
|
tme_uint16_t tme_m6888x_frame_ccr;
|
|
|
|
/* reserved: */
|
|
tme_uint16_t tme_m6888x_frame_reserved6;
|
|
|
|
/* additional words: */
|
|
tme_uint32_t tme_m6888x_frame_words[(TME_M6888X_FRAME_SIZE_IDLE_M68882 / sizeof(tme_uint32_t)) - 1];
|
|
};
|
|
|
|
/* prototypes: */
|
|
TME_M6888X_FPGEN_DECL(_tme_m6888x_fmovecr);
|
|
TME_M6888X_FPGEN_DECL(_tme_m6888x_fsincos);
|
|
TME_M6888X_FPGEN_DECL(_tme_m6888x_fcmp);
|
|
TME_M6888X_FPGEN_DECL(_tme_m6888x_ftst);
|
|
TME_M6888X_FPGEN_DECL(_tme_m6888x_ftwotox);
|
|
TME_M6888X_FPGEN_DECL(_tme_m6888x_ftentox);
|
|
TME_M6888X_FPGEN_DECL(_tme_m6888x_flog2);
|
|
TME_M6888X_FPGEN_DECL(_tme_m6888x_fmod);
|
|
TME_M6888X_FPGEN_DECL(_tme_m6888x_frem);
|
|
TME_M6888X_FPGEN_DECL(_tme_m6888x_fsgldiv);
|
|
TME_M6888X_FPGEN_DECL(_tme_m6888x_fsglmul);
|
|
|
|
/* globals: */
|
|
|
|
/* special fpgen structures: */
|
|
static const struct tme_m6888x_fpgen _tme_m6888x_fpgen_fmovecr = {
|
|
_tme_m6888x_fmovecr,
|
|
0,
|
|
TME_M68K_FPU_ANY,
|
|
TME_M6888X_OPTYPE_MONADIC,
|
|
TME_FLOAT_ROUND_NULL,
|
|
TME_M6888X_ROUNDING_PRECISION_CTL
|
|
};
|
|
static const struct tme_m6888x_fpgen _tme_m6888x_fpgen_fmove_rm = {
|
|
NULL,
|
|
0,
|
|
TME_M68K_FPU_ANY,
|
|
TME_M6888X_OPTYPE_MONADIC,
|
|
TME_FLOAT_ROUND_NULL,
|
|
TME_M6888X_ROUNDING_PRECISION_CTL
|
|
};
|
|
|
|
/* include the automatically generated code: */
|
|
#include "m6888x-auto.c"
|
|
|
|
/* this resets the FPU: */
|
|
void
|
|
tme_m68k_fpu_reset(struct tme_m68k *ic)
|
|
{
|
|
unsigned int fp_i;
|
|
|
|
/* put nonsignaling NaNs in the floating-point data registers: */
|
|
for (fp_i = 0;
|
|
fp_i < (sizeof(ic->tme_m68k_fpu_fpreg) / sizeof(ic->tme_m68k_fpu_fpreg[0]));
|
|
fp_i++) {
|
|
ic->tme_m68k_fpu_fpreg[fp_i].tme_float_format = TME_FLOAT_FORMAT_IEEE754_EXTENDED80;
|
|
ic->tme_m68k_fpu_fpreg[fp_i].tme_float_value_ieee754_extended80 = ic->tme_m68k_fpu_ieee754_ctl.tme_ieee754_ctl_default_nan_extended80;
|
|
}
|
|
|
|
/* put zeroes in the floating-point control register, status
|
|
register, and instruction address register: */
|
|
ic->tme_m68k_fpu_fpcr = 0;
|
|
ic->tme_m68k_fpu_fpsr = 0;
|
|
ic->tme_m68k_fpu_fpiar = 0;
|
|
}
|
|
|
|
/* this handles an exception: */
|
|
static void
|
|
_tme_m6888x_exception(struct tme_m68k *ic, tme_uint32_t exceptions)
|
|
{
|
|
tme_uint8_t vector;
|
|
|
|
/* update the EXC byte in the FPSR: */
|
|
ic->tme_m68k_fpu_fpsr |= exceptions;
|
|
|
|
/* update the AEXC byte in the FPSR: */
|
|
if (exceptions & (TME_M6888X_FPSR_EXC_SNAN | TME_M6888X_FPSR_EXC_OPERR | TME_M6888X_FPSR_EXC_BSUN)) {
|
|
ic->tme_m68k_fpu_fpsr |= TME_M6888X_FPSR_AEXC_IOP;
|
|
}
|
|
if (exceptions & TME_M6888X_FPSR_EXC_OVFL) {
|
|
ic->tme_m68k_fpu_fpsr |= TME_M6888X_FPSR_AEXC_OVFL;
|
|
}
|
|
if (exceptions & (TME_M6888X_FPSR_EXC_UNFL | TME_M6888X_FPSR_EXC_INEX2)) {
|
|
ic->tme_m68k_fpu_fpsr |= TME_M6888X_FPSR_AEXC_UNFL;
|
|
}
|
|
if (exceptions & TME_M6888X_FPSR_EXC_DZ) {
|
|
ic->tme_m68k_fpu_fpsr |= TME_M6888X_FPSR_AEXC_DZ;
|
|
}
|
|
if (exceptions & (TME_M6888X_FPSR_EXC_INEX1 | TME_M6888X_FPSR_EXC_INEX2 | TME_M6888X_FPSR_EXC_OVFL)) {
|
|
ic->tme_m68k_fpu_fpsr |= TME_M6888X_FPSR_AEXC_INEX;
|
|
}
|
|
|
|
/* if any of the new exceptions are unmasked, take the exception: */
|
|
if ((ic->tme_m68k_fpu_fpcr & exceptions)) {
|
|
|
|
/* because it's possible for an instruction to cause multiple
|
|
exceptions, the exceptions are prioritized: */
|
|
/* XXX FIXME - when the predecrement or postincrement addressing
|
|
modes are used, are the address registers updated before or
|
|
after any exceptions are generated? */
|
|
if (exceptions & TME_M6888X_FPSR_EXC_BSUN) {
|
|
vector = TME_M6888X_VECTOR_BSUN;
|
|
}
|
|
else if (exceptions & TME_M6888X_FPSR_EXC_SNAN) {
|
|
vector = TME_M6888X_VECTOR_SNAN;
|
|
}
|
|
else if (exceptions & TME_M6888X_FPSR_EXC_OPERR) {
|
|
vector = TME_M6888X_VECTOR_OPERR;
|
|
}
|
|
else if (exceptions & TME_M6888X_FPSR_EXC_OVFL) {
|
|
vector = TME_M6888X_VECTOR_OVFL;
|
|
}
|
|
else if (exceptions & TME_M6888X_FPSR_EXC_UNFL) {
|
|
vector = TME_M6888X_VECTOR_UNFL;
|
|
}
|
|
else if (exceptions & TME_M6888X_FPSR_EXC_DZ) {
|
|
vector = TME_M6888X_VECTOR_DZ;
|
|
}
|
|
else {
|
|
assert (exceptions & (TME_M6888X_FPSR_EXC_INEX2 | TME_M6888X_FPSR_EXC_INEX1));
|
|
vector = TME_M6888X_VECTOR_INEX;
|
|
}
|
|
|
|
/* unlock any lock: */
|
|
if (ic->tme_m68k_fpu_ieee754_ctl.tme_ieee754_ctl_lock_unlock != NULL) {
|
|
(*ic->tme_m68k_fpu_ieee754_ctl.tme_ieee754_ctl_lock_unlock)();
|
|
ic->tme_m68k_fpu_ieee754_ctl.tme_ieee754_ctl_lock_unlock = NULL;
|
|
}
|
|
|
|
/* take the exception: */
|
|
/* XXX FIXME - we signal all m6888x exceptions as cp
|
|
Postinstruction exceptions. exceptions generated by a cpGEN
|
|
instruction are probably supposed to be cp Preinstruction
|
|
exceptions, signaled at the time of the next cpGEN instruction: */
|
|
ic->tme_m68k_ireg_pc_last = ic->tme_m68k_ireg_pc;
|
|
ic->tme_m68k_ireg_pc = ic->tme_m68k_ireg_pc_next;
|
|
TME_M68K_INSN_EXCEPTION(TME_M68K_EXCEPTION_INST(vector));
|
|
}
|
|
}
|
|
|
|
/* the IEEE 754 exception handler: */
|
|
static void
|
|
_tme_m6888x_exception_ieee754(struct tme_ieee754_ctl *ctl, tme_int8_t exceptions_ieee754)
|
|
{
|
|
tme_uint32_t exceptions_m6888x;
|
|
|
|
/* map the exceptions: */
|
|
exceptions_m6888x = 0;
|
|
if (exceptions_ieee754 & TME_FLOAT_EXCEPTION_GENERIC) {
|
|
exceptions_m6888x |= TME_M6888X_FPSR_EXC_OPERR;
|
|
}
|
|
if (exceptions_ieee754 & TME_FLOAT_EXCEPTION_INVALID) {
|
|
exceptions_m6888x |= TME_M6888X_FPSR_EXC_OPERR;
|
|
}
|
|
if (exceptions_ieee754 & TME_FLOAT_EXCEPTION_DIVBYZERO) {
|
|
exceptions_m6888x |= TME_M6888X_FPSR_EXC_DZ;
|
|
}
|
|
if (exceptions_ieee754 & TME_FLOAT_EXCEPTION_OVERFLOW) {
|
|
exceptions_m6888x |= TME_M6888X_FPSR_EXC_OVFL;
|
|
}
|
|
if (exceptions_ieee754 & TME_FLOAT_EXCEPTION_UNDERFLOW) {
|
|
exceptions_m6888x |= TME_M6888X_FPSR_EXC_UNFL;
|
|
}
|
|
if (exceptions_ieee754 & TME_FLOAT_EXCEPTION_INEXACT) {
|
|
exceptions_m6888x |= TME_M6888X_FPSR_EXC_INEX2;
|
|
}
|
|
if (exceptions_ieee754 & TME_FLOAT_EXCEPTION_OVERFLOW_INT) {
|
|
exceptions_m6888x |= TME_M6888X_FPSR_EXC_OVFL;
|
|
}
|
|
/* XXX FIXME - do denormals count as INEX2? */
|
|
if (exceptions_ieee754 & TME_FLOAT_EXCEPTION_DENORMAL) {
|
|
exceptions_m6888x |= TME_M6888X_FPSR_EXC_INEX2;
|
|
}
|
|
|
|
_tme_m6888x_exception((struct tme_m68k *) ctl->tme_ieee754_ctl_private, exceptions_m6888x);
|
|
}
|
|
|
|
/* signaling NaN tests: */
|
|
#define _TME_M6888X_IS_SNAN(a) (((a)->tme_float_ieee754_extended80_significand.tme_value64_uint32_hi & TME_BIT(30)) == 0)
|
|
static tme_int8_t
|
|
_tme_m6888x_is_snan_extended80(struct tme_float_ieee754_extended80 *value)
|
|
{
|
|
return (_TME_M6888X_IS_SNAN(value));
|
|
}
|
|
|
|
/* NaN propagation: */
|
|
static void
|
|
_tme_m6888x_nan_from_nans_extended80(struct tme_ieee754_ctl *ctl,
|
|
const struct tme_float_ieee754_extended80 *a,
|
|
const struct tme_float_ieee754_extended80 *b,
|
|
struct tme_float_ieee754_extended80 *z)
|
|
{
|
|
struct tme_m68k *ic;
|
|
int a_is_snan;
|
|
int b_is_snan;
|
|
|
|
/* recover the m68k: */
|
|
ic = ctl->tme_ieee754_ctl_private;
|
|
|
|
/* see if any of the NaNs are signaling NaNs: */
|
|
a_is_snan = _TME_M6888X_IS_SNAN(a);
|
|
b_is_snan = _TME_M6888X_IS_SNAN(b);
|
|
|
|
/* if either operand is a signaling NaN: */
|
|
if (a_is_snan || b_is_snan) {
|
|
|
|
/* signal the signaling NaN: */
|
|
_tme_m6888x_exception(ic, TME_M6888X_FPSR_EXC_SNAN);
|
|
}
|
|
|
|
/* if a and b are different NaNs: */
|
|
if ((a->tme_float_ieee754_extended80_sexp
|
|
!= b->tme_float_ieee754_extended80_sexp)
|
|
|| (a->tme_float_ieee754_extended80_significand.tme_value64_uint32_hi
|
|
!= b->tme_float_ieee754_extended80_significand.tme_value64_uint32_hi)
|
|
|| (a->tme_float_ieee754_extended80_significand.tme_value64_uint32_lo
|
|
!= b->tme_float_ieee754_extended80_significand.tme_value64_uint32_lo)) {
|
|
|
|
/* we need to return the NaN that is the destination operand: */
|
|
switch (_tme_m6888x_fpgen_opmode_table[TME_FIELD_EXTRACTU(TME_M68K_INSN_SPECOP, 0, 7)].tme_m6888x_fpgen_optype) {
|
|
default:
|
|
case TME_M6888X_OPTYPE_MONADIC: assert(FALSE);
|
|
case TME_M6888X_OPTYPE_DYADIC_SRC_DST: a = b; break;
|
|
case TME_M6888X_OPTYPE_DYADIC_DST_SRC: break;
|
|
}
|
|
}
|
|
|
|
/* return a as the NaN, but make sure it's nonsignaling: */
|
|
*z = *a;
|
|
z->tme_float_ieee754_extended80_significand.tme_value64_uint32_hi |= TME_BIT(30);
|
|
}
|
|
|
|
/* this prepares to run an fpgen instruction: */
|
|
static void inline
|
|
_tme_m6888x_fpgen_enter(struct tme_m68k *ic, const struct tme_m6888x_fpgen *fpgen)
|
|
{
|
|
tme_int8_t rounding_mode;
|
|
tme_int8_t rounding_precision;
|
|
|
|
/* set the rounding mode: */
|
|
rounding_mode = fpgen->tme_m6888x_fpgen_rounding_mode;
|
|
if (__tme_predict_true(rounding_mode == TME_FLOAT_ROUND_NULL)) {
|
|
switch (ic->tme_m68k_fpu_fpcr & TME_M6888X_FPCR_RND_MASK) {
|
|
default: assert(FALSE);
|
|
case TME_M6888X_FPCR_RND_RN: rounding_mode = TME_FLOAT_ROUND_NEAREST_EVEN; break;
|
|
case TME_M6888X_FPCR_RND_RZ: rounding_mode = TME_FLOAT_ROUND_TO_ZERO; break;
|
|
case TME_M6888X_FPCR_RND_RM: rounding_mode = TME_FLOAT_ROUND_DOWN; break;
|
|
case TME_M6888X_FPCR_RND_RP: rounding_mode = TME_FLOAT_ROUND_UP; break;
|
|
}
|
|
}
|
|
ic->tme_m68k_fpu_ieee754_ctl.tme_ieee754_ctl_rounding_mode = rounding_mode;
|
|
|
|
/* set the rounding precision: */
|
|
rounding_precision = fpgen->tme_m6888x_fpgen_rounding_precision;
|
|
if (__tme_predict_true(rounding_precision == TME_M6888X_ROUNDING_PRECISION_CTL)) {
|
|
switch (ic->tme_m68k_fpu_fpcr & TME_M6888X_FPCR_PREC_MASK) {
|
|
default: assert(FALSE); /* FALLTHROUGH */
|
|
case TME_M6888X_FPCR_PREC_UNDEF: /* FALLTHROUGH */
|
|
case TME_M6888X_FPCR_PREC_X: rounding_precision = TME_M6888X_ROUNDING_PRECISION_EXTENDED80; break;
|
|
case TME_M6888X_FPCR_PREC_S: rounding_precision = TME_M6888X_ROUNDING_PRECISION_SINGLE; break;
|
|
case TME_M6888X_FPCR_PREC_D: rounding_precision = TME_M6888X_ROUNDING_PRECISION_DOUBLE; break;
|
|
}
|
|
}
|
|
ic->tme_m68k_fpu_ieee754_ctl.tme_ieee754_ctl_extended80_rounding_precision = rounding_precision;
|
|
|
|
/* clear the exception status byte in the FPSR: */
|
|
ic->tme_m68k_fpu_fpsr
|
|
&= ~(TME_M6888X_FPSR_EXC_INEX1
|
|
| TME_M6888X_FPSR_EXC_INEX2
|
|
| TME_M6888X_FPSR_EXC_DZ
|
|
| TME_M6888X_FPSR_EXC_UNFL
|
|
| TME_M6888X_FPSR_EXC_OVFL
|
|
| TME_M6888X_FPSR_EXC_OPERR
|
|
| TME_M6888X_FPSR_EXC_SNAN
|
|
| TME_M6888X_FPSR_EXC_BSUN);
|
|
|
|
/* set the FPIAR: */
|
|
ic->tme_m68k_fpu_fpiar = ic->tme_m68k_ireg_pc;
|
|
}
|
|
|
|
/* this sets the floating-point condition codes: */
|
|
static void inline
|
|
_tme_m6888x_fpcc(struct tme_m68k *ic, const struct tme_float *dst, unsigned int dst_formats)
|
|
{
|
|
tme_uint32_t fpcc;
|
|
|
|
/* start with no floating-point condition codes: */
|
|
fpcc = 0;
|
|
|
|
/* set N: */
|
|
if (tme_float_is_negative(dst, dst_formats)) {
|
|
fpcc |= TME_M6888X_FPSR_CC_N;
|
|
}
|
|
|
|
/* set NAN or I or Z: */
|
|
if (tme_float_is_nan(dst, dst_formats)) {
|
|
fpcc |= TME_M6888X_FPSR_CC_NAN;
|
|
}
|
|
else if (tme_float_is_inf(dst, dst_formats)) {
|
|
fpcc |= TME_M6888X_FPSR_CC_I;
|
|
}
|
|
else if (tme_float_is_zero(dst, dst_formats)) {
|
|
fpcc |= TME_M6888X_FPSR_CC_Z;
|
|
}
|
|
|
|
/* set the floating-point condition codes: */
|
|
ic->tme_m68k_fpu_fpsr
|
|
= ((ic->tme_m68k_fpu_fpsr
|
|
& ~(TME_M6888X_FPSR_CC_N
|
|
| TME_M6888X_FPSR_CC_NAN
|
|
| TME_M6888X_FPSR_CC_I
|
|
| TME_M6888X_FPSR_CC_Z))
|
|
| fpcc);
|
|
}
|
|
|
|
TME_M68K_INSN(tme_m68k_fpgen)
|
|
{
|
|
struct tme_ieee754_ctl *ieee754_ctl;
|
|
tme_uint16_t command;
|
|
tme_uint16_t opmode;
|
|
const struct tme_m6888x_fpgen *fpgen;
|
|
unsigned int src_ea;
|
|
const struct tme_float *src;
|
|
struct tme_float *dst;
|
|
struct tme_float src_buffer;
|
|
struct tme_float dst_buffer;
|
|
struct tme_float conv_buffer;
|
|
union tme_value64 value64_buffer;
|
|
struct tme_float_ieee754_extended80 extended80_buffer;
|
|
unsigned int ea_mode;
|
|
unsigned int ea_reg;
|
|
unsigned int ea_size;
|
|
unsigned int op1_ireg32;
|
|
unsigned int src_specifier;
|
|
unsigned int digit_i;
|
|
tme_int32_t packed_value_int32;
|
|
struct tme_float packed_value_float;
|
|
tme_int32_t exponent;
|
|
|
|
/* get the IEEE 754 ctl: */
|
|
ieee754_ctl = &ic->tme_m68k_fpu_ieee754_ctl;
|
|
|
|
/* this is an FPU instruction: */
|
|
TME_M68K_INSN_FPU;
|
|
|
|
/* get the coprocessor-dependent command word: */
|
|
command = TME_M68K_INSN_SPECOP;
|
|
|
|
/* if this is an FMOVECR instruction
|
|
(command word pattern 0101 11dd dooo oooo): */
|
|
if ((command & 0xfc00) == 0x5c00
|
|
&& TME_FIELD_EXTRACTU(TME_M68K_INSN_OPCODE, 0, 6) == 0) {
|
|
|
|
/* use the FMOVECR opmode and FPgen structure: */
|
|
opmode = TME_M6888X_FPGEN_OPMODE_OTHER;
|
|
fpgen = &_tme_m6888x_fpgen_fmovecr;
|
|
|
|
/* the source operand does not use the EA: */
|
|
src_ea = FALSE;
|
|
}
|
|
|
|
/* otherwise, this is a generic FPgen instruction: */
|
|
else {
|
|
|
|
/* get the opmode: */
|
|
opmode = TME_FIELD_EXTRACTU(command, 0, 7);
|
|
|
|
/* decode this instruction: */
|
|
fpgen = &_tme_m6888x_fpgen_opmode_table[opmode];
|
|
|
|
/* the source operand uses the EA if this is an EA-to-register
|
|
operation: */
|
|
src_ea = (command & TME_BIT(14)) != 0;
|
|
}
|
|
|
|
/* catch illegal instructions: */
|
|
switch (fpgen->tme_m6888x_fpgen_fpu_types) {
|
|
|
|
case TME_M68K_FPU_M6888X:
|
|
/* instructions not supported in hardware by the m68040 are caught
|
|
later: */
|
|
case TME_M68K_FPU_ANY:
|
|
break;
|
|
|
|
case TME_M68K_FPU_M68040:
|
|
if (ic->tme_m68k_fpu_type == TME_M68K_FPU_M68040) {
|
|
break;
|
|
}
|
|
/* FALLTHROUGH */
|
|
case TME_M68K_FPU_NONE:
|
|
TME_M68K_INSN_EXCEPTION(TME_M68K_EXCEPTION_ILL);
|
|
break;
|
|
default:
|
|
abort();
|
|
}
|
|
|
|
/* get the source specifier: */
|
|
src_specifier = TME_FIELD_EXTRACTU(command, 10, 3);
|
|
|
|
/* if the source operand uses the EA: */
|
|
if (src_ea) {
|
|
|
|
/* assume that the most-significant first 32-bit part of the
|
|
source operand will end up in the internal memx register: */
|
|
op1_ireg32 = TME_M68K_IREG_MEMX32;
|
|
|
|
/* get the EA mode and register fields: */
|
|
ea_mode = TME_FIELD_EXTRACTU(TME_M68K_INSN_OPCODE, 3, 3);
|
|
ea_reg = TME_FIELD_EXTRACTU(TME_M68K_INSN_OPCODE, 0, 3);
|
|
|
|
/* if this is a data register direct EA: */
|
|
if (ea_mode == 0) {
|
|
|
|
/* dispatch on the source specifier, since we need to
|
|
sign-extend a byte or word to long, and we need to check that
|
|
only a byte, word, long, or single precision source is
|
|
specified: */
|
|
switch (src_specifier) {
|
|
case TME_M6888X_TYPE_LONG:
|
|
case TME_M6888X_TYPE_SINGLE:
|
|
op1_ireg32 = TME_M68K_IREG_D0 + ea_reg;
|
|
break;
|
|
case TME_M6888X_TYPE_WORD:
|
|
ic->tme_m68k_ireg_int32(TME_M68K_IREG_MEMX32) = (tme_int16_t) TME_M68K_INSN_OP1(tme_int32_t);
|
|
break;
|
|
case TME_M6888X_TYPE_BYTE:
|
|
ic->tme_m68k_ireg_int32(TME_M68K_IREG_MEMX32) = (tme_int8_t) TME_M68K_INSN_OP1(tme_int32_t);
|
|
break;
|
|
default:
|
|
TME_M68K_INSN_EXCEPTION(TME_M68K_EXCEPTION_ILL);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* otherwise, if this is an address register direct EA: */
|
|
else if (ea_mode == 1) {
|
|
TME_M68K_INSN_EXCEPTION(TME_M68K_EXCEPTION_ILL);
|
|
}
|
|
|
|
/* otherwise, if this is an immediate EA: */
|
|
else if (ea_mode == 7
|
|
&& ea_reg == 4) {
|
|
|
|
/* _op1 already points to the operand as one or more 32-bit
|
|
words: */
|
|
assert (_op1 == &ic->tme_m68k_ireg_uint32(TME_M68K_IREG_IMM32 + 0));
|
|
op1_ireg32 = TME_M68K_IREG_IMM32;
|
|
}
|
|
|
|
/* otherwise, this is a memory EA: */
|
|
else {
|
|
|
|
/* this instruction can fault: */
|
|
TME_M68K_INSN_CANFAULT;
|
|
|
|
/* adjust ea_reg to reference the address register: */
|
|
ea_reg += TME_M68K_IREG_A0;
|
|
|
|
/* dispatch on the source specifier to size the operand: */
|
|
switch (src_specifier) {
|
|
case TME_M6888X_TYPE_LONG:
|
|
case TME_M6888X_TYPE_SINGLE:
|
|
ea_size = TME_M68K_SIZE_32;
|
|
break;
|
|
|
|
case TME_M6888X_TYPE_PACKEDDEC:
|
|
case TME_M6888X_TYPE_EXTENDED80:
|
|
ea_size = TME_M68K_SIZE_96;
|
|
break;
|
|
|
|
case TME_M6888X_TYPE_WORD:
|
|
ea_size = TME_M68K_SIZE_16;
|
|
break;
|
|
|
|
case TME_M6888X_TYPE_DOUBLE:
|
|
ea_size = TME_M68K_SIZE_64;
|
|
break;
|
|
|
|
case TME_M6888X_TYPE_BYTE:
|
|
ea_size = TME_M68K_SIZE_8;
|
|
break;
|
|
|
|
default:
|
|
abort();
|
|
}
|
|
|
|
/* for the effective address predecrement and postincrement
|
|
modes, we require that these size macros correspond exactly
|
|
to the number of bytes: */
|
|
#if TME_M68K_SIZE_8 != 1
|
|
#error "TME_M68K_SIZE_8 must be 1"
|
|
#endif
|
|
#if TME_M68K_SIZE_16 != 2
|
|
#error "TME_M68K_SIZE_16 must be 2"
|
|
#endif
|
|
#if TME_M68K_SIZE_32 != 4
|
|
#error "TME_M68K_SIZE_32 must be 4"
|
|
#endif
|
|
#if TME_M68K_SIZE_64 != 8
|
|
#error "TME_M68K_SIZE_64 must be 8"
|
|
#endif
|
|
#if TME_M68K_SIZE_96 != 12
|
|
#error "TME_M68K_SIZE_96 must be 12"
|
|
#endif
|
|
#define TME_M68K_AREG_INCREMENT(areg, size) \
|
|
((size) + (((size) == TME_M68K_SIZE_8 && (areg) == TME_M68K_IREG_A7) ? 1 : 0))
|
|
|
|
/* address register indirect postincrement: */
|
|
if (ea_mode == 3) {
|
|
/* if we are not restarting, set the effective address: */
|
|
if (!TME_M68K_SEQUENCE_RESTARTING) {
|
|
ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(ea_reg);
|
|
ic->tme_m68k_ireg_uint32(ea_reg) += TME_M68K_AREG_INCREMENT(ea_reg, ea_size);
|
|
}
|
|
}
|
|
|
|
/* address register indirect predecrement: */
|
|
else if (ea_mode == 4) {
|
|
/* if we are not restarting, set the effective address: */
|
|
if (!TME_M68K_SEQUENCE_RESTARTING) {
|
|
ic->tme_m68k_ireg_uint32(ea_reg) -= TME_M68K_AREG_INCREMENT(ea_reg, ea_size);
|
|
ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(ea_reg);
|
|
}
|
|
}
|
|
|
|
/* dispatch on the operand size to read in the operand as one or
|
|
more 32-bit words. we will read up to three 32-bit words
|
|
into memx, memy, and memz: */
|
|
assert ((TME_M68K_IREG_MEMX32 + 1) == TME_M68K_IREG_MEMY32
|
|
&& (TME_M68K_IREG_MEMY32 + 1) == TME_M68K_IREG_MEMZ32);
|
|
switch (ea_size) {
|
|
|
|
/* this can only happen when the source operand is a byte. we
|
|
sign-extend the byte to a long: */
|
|
case TME_M68K_SIZE_8:
|
|
tme_m68k_read_memx8(ic);
|
|
assert (!TME_M68K_SEQUENCE_RESTARTING);
|
|
ic->tme_m68k_ireg_memx32 = TME_EXT_S8_S32((tme_int8_t) ic->tme_m68k_ireg_memx8);
|
|
break;
|
|
|
|
/* this can only happen when the source operand is a word. we
|
|
sign-extend the word to a long: */
|
|
case TME_M68K_SIZE_16:
|
|
tme_m68k_read_memx16(ic);
|
|
assert (!TME_M68K_SEQUENCE_RESTARTING);
|
|
ic->tme_m68k_ireg_memx32 = TME_EXT_S16_S32((tme_int16_t) ic->tme_m68k_ireg_memx16);
|
|
break;
|
|
|
|
/* everything else is one or more 32-bit words: */
|
|
default:
|
|
|
|
/* read the first 32 bits into the memx register: */
|
|
tme_m68k_read_memx32(ic);
|
|
if (ea_size == TME_M68K_SIZE_32) {
|
|
break;
|
|
}
|
|
|
|
/* read the second 32 bits into the memy register: */
|
|
if (!TME_M68K_SEQUENCE_RESTARTING) {
|
|
ic->_tme_m68k_ea_address += TME_M68K_SIZE_32;
|
|
}
|
|
tme_m68k_read_mem32(ic, TME_M68K_IREG_MEMY32);
|
|
if (ea_size == TME_M68K_SIZE_64) {
|
|
break;
|
|
}
|
|
|
|
/* read the third 32 bits into the memz register: */
|
|
if (!TME_M68K_SEQUENCE_RESTARTING) {
|
|
ic->_tme_m68k_ea_address += TME_M68K_SIZE_32;
|
|
}
|
|
tme_m68k_read_mem32(ic, TME_M68K_IREG_MEMZ32);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* convert the operand from one or more raw 32-bit words into the
|
|
internal extended precision format: */
|
|
switch (src_specifier) {
|
|
|
|
/* convert a 32-bit integral value. all of these integral types
|
|
have already been converted into 32-bit signed integers: */
|
|
case TME_M6888X_TYPE_BYTE:
|
|
case TME_M6888X_TYPE_WORD:
|
|
case TME_M6888X_TYPE_LONG:
|
|
tme_ieee754_extended80_from_int32((tme_int32_t) TME_M6888X_EA_OP32(0), &src_buffer);
|
|
break;
|
|
|
|
/* convert a single-precision value: */
|
|
case TME_M6888X_TYPE_SINGLE:
|
|
tme_ieee754_single_value_set(&conv_buffer, TME_M6888X_EA_OP32(0));
|
|
TME_M6888X_IEEE754_OP_MONADIC(tme_ieee754_ops_extended80_from_single,
|
|
&conv_buffer,
|
|
&src_buffer);
|
|
break;
|
|
|
|
/* convert a double-precision value: */
|
|
case TME_M6888X_TYPE_DOUBLE:
|
|
/* NB that TME_M6888X_EA_OP32(0) is always the most significant
|
|
32 bits of the double, regardless of the endianness of the
|
|
host. this is how both the executer fetches an immediate
|
|
double, and how the memory code above reads a double: */
|
|
value64_buffer.tme_value64_uint32_hi = TME_M6888X_EA_OP32(0);
|
|
value64_buffer.tme_value64_uint32_lo = TME_M6888X_EA_OP32(1);
|
|
tme_ieee754_double_value_set(&conv_buffer, value64_buffer);
|
|
TME_M6888X_IEEE754_OP_MONADIC(tme_ieee754_ops_extended80_from_double,
|
|
&conv_buffer,
|
|
&src_buffer);
|
|
break;
|
|
|
|
/* assign an extended-precision value: */
|
|
case TME_M6888X_TYPE_EXTENDED80:
|
|
/* NB that TME_M6888X_EA_OP32(0) is always the most significant
|
|
32 bits of the extended80, regardless of the endianness of
|
|
the host. this is how both the executer fetches an immediate
|
|
extended80, and how the memory code above reads a extended80: */
|
|
extended80_buffer.tme_float_ieee754_extended80_sexp = TME_M6888X_EA_OP32(0) >> 16;
|
|
extended80_buffer.tme_float_ieee754_extended80_significand.tme_value64_uint32_hi = TME_M6888X_EA_OP32(1);
|
|
extended80_buffer.tme_float_ieee754_extended80_significand.tme_value64_uint32_lo = TME_M6888X_EA_OP32(2);
|
|
tme_ieee754_extended80_value_set(&src_buffer, extended80_buffer);
|
|
break;
|
|
|
|
case TME_M6888X_TYPE_PACKEDDEC:
|
|
|
|
/* if this value's SE and YY bits are all set, and the exponent
|
|
is 0xFFF, the value is either an infinity or a NaN: */
|
|
if ((TME_M6888X_EA_OP32(0)
|
|
& (TME_M6888X_PACKEDDEC_SE
|
|
| TME_M6888X_PACKEDDEC_YY))
|
|
== (TME_M6888X_PACKEDDEC_SE
|
|
| TME_M6888X_PACKEDDEC_YY)
|
|
&& TME_M6888X_PD_DIGIT(22) == 0xf
|
|
&& TME_M6888X_PD_DIGIT(21) == 0xf
|
|
&& TME_M6888X_PD_DIGIT(20) == 0xf) {
|
|
|
|
/* "A packed decimal real data format with the SE and both Y
|
|
bits set, an exponent of $FFF and a nonzero 16-bit [sic]
|
|
decimal fraction is a NAN. When the FPU uses this format,
|
|
the fraction of the NAN is moved bit- by-bit into the
|
|
extended-precision mantissa of a floating-point data
|
|
register."
|
|
|
|
moving the fraction bit-by-bit works for the infinities,
|
|
too, since both the packed decimal and the extended
|
|
precision infinities have all-bits-zero fractions: */
|
|
extended80_buffer.tme_float_ieee754_extended80_significand.tme_value64_uint32_hi = TME_M6888X_EA_OP32(1);
|
|
extended80_buffer.tme_float_ieee754_extended80_significand.tme_value64_uint32_lo = TME_M6888X_EA_OP32(2);
|
|
|
|
/* "The exponent of the register is set to signify a NAN,
|
|
and no conversion occurs. The MSB of the most
|
|
significant digit in the decimal fraction (the MSB of
|
|
digit 15) is a don't care, as in extended-precision NANs,
|
|
and the MSB of minus one of digit 15 is the SNAN bit. If
|
|
the NAN bit is a zero, then it is an SNAN."
|
|
|
|
the biased exponent for NaNs and infinities is the same,
|
|
and the sign bit is a don't care for a NaN: */
|
|
extended80_buffer.tme_float_ieee754_extended80_sexp
|
|
= (0x7fff
|
|
| (TME_M6888X_EA_OP32(0) & TME_M6888X_PACKEDDEC_SM
|
|
? 0x8000
|
|
: 0));
|
|
|
|
/* finally create the source operand: */
|
|
tme_ieee754_extended80_value_set(&src_buffer, extended80_buffer);
|
|
}
|
|
|
|
/* otherwise, this should be an in-range value: */
|
|
else {
|
|
|
|
/* "The FPU does not detect non-decimal digits in the exponent,
|
|
integer, or fraction digits of an in-range packed decimal real data
|
|
format. These non-decimal digits are converted to binary in the
|
|
same manner as decimal digits; however, the result is probably
|
|
useless although it is repeatable." */
|
|
|
|
/* convert the significand: */
|
|
tme_ieee754_extended80_from_int32(TME_M6888X_PD_DIGIT(16), &src_buffer);
|
|
tme_ieee754_extended80_from_int32(100000000, &conv_buffer);
|
|
packed_value_int32 = 0;
|
|
digit_i = 15;
|
|
do {
|
|
packed_value_int32 = (packed_value_int32 * 10) + TME_M6888X_PD_DIGIT(digit_i);
|
|
if ((digit_i % 8) == 0) {
|
|
TME_M6888X_IEEE754_OP_DYADIC(tme_ieee754_ops_extended80_mul,
|
|
&src_buffer,
|
|
&conv_buffer,
|
|
&src_buffer);
|
|
tme_ieee754_extended80_from_int32(packed_value_int32, &packed_value_float);
|
|
TME_M6888X_IEEE754_OP_DYADIC(tme_ieee754_ops_extended80_add,
|
|
&src_buffer,
|
|
&packed_value_float,
|
|
&src_buffer);
|
|
packed_value_int32 = 0;
|
|
}
|
|
} while (digit_i-- > 0);
|
|
if (TME_M6888X_EA_OP32(0) & TME_M6888X_PACKEDDEC_SM) {
|
|
tme_ieee754_extended80_from_int32(-1, &conv_buffer);
|
|
TME_M6888X_IEEE754_OP_DYADIC(tme_ieee754_ops_extended80_mul,
|
|
&src_buffer,
|
|
&conv_buffer,
|
|
&src_buffer);
|
|
}
|
|
|
|
/* convert the exponent: */
|
|
exponent = 0;
|
|
digit_i = 22;
|
|
do {
|
|
exponent = (exponent * 10) + TME_M6888X_PD_DIGIT(digit_i);
|
|
} while (digit_i-- > 21);
|
|
if (TME_M6888X_EA_OP32(0) & TME_M6888X_PACKEDDEC_SE) {
|
|
exponent = -exponent;
|
|
}
|
|
|
|
/* adjust the exponent, since we ignored the implicit decimal
|
|
point when converting the significand: */
|
|
exponent -= 16;
|
|
|
|
/* scale the significand: */
|
|
tme_ieee754_extended80_from_int32(exponent, &conv_buffer);
|
|
tme_ieee754_extended80_radix10_scale(&ic->tme_m68k_fpu_ieee754_ctl, &src_buffer, &conv_buffer, &src_buffer);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
abort();
|
|
}
|
|
|
|
/* the source operand is in the buffer: */
|
|
src = &src_buffer;
|
|
}
|
|
|
|
/* otherwise, the source operand is in a register: */
|
|
else {
|
|
src = &ic->tme_m68k_fpu_fpreg[src_specifier];
|
|
}
|
|
|
|
/* XXX FIXME - a check for operand types not implemented on the
|
|
m68040 would go here: */
|
|
|
|
/* do the common fpgen setup: */
|
|
_tme_m6888x_fpgen_enter(ic, fpgen);
|
|
|
|
/* get the destination operand: */
|
|
dst = &ic->tme_m68k_fpu_fpreg[TME_FIELD_EXTRACTU(command, 7, 3)];
|
|
|
|
/* dispatch on the opmode to handle any special cases: */
|
|
switch (opmode) {
|
|
|
|
/* these instructions don't modify the destination register: */
|
|
case TME_M6888X_FPGEN_OPMODE_FCMP:
|
|
case TME_M6888X_FPGEN_OPMODE_FTST:
|
|
dst_buffer = *dst;
|
|
dst = &dst_buffer;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* if this instruction is m6888x specific: */
|
|
if (fpgen->tme_m6888x_fpgen_func != NULL) {
|
|
|
|
/* run the function: */
|
|
(*fpgen->tme_m6888x_fpgen_func)(ic, src, dst);
|
|
}
|
|
|
|
/* otherwise, this instruction has an IEEE 754 operation: */
|
|
else {
|
|
|
|
/* run the function: */
|
|
switch (fpgen->tme_m6888x_fpgen_optype) {
|
|
default: assert(FALSE);
|
|
case TME_M6888X_OPTYPE_MONADIC:
|
|
TME_M6888X_IEEE754_OP_RUN(fpgen->tme_m6888x_fpgen_func_ops_offset, (struct tme_ieee754_ctl *, const struct tme_float *, struct tme_float *), (&ic->tme_m68k_fpu_ieee754_ctl, src, dst));
|
|
break;
|
|
case TME_M6888X_OPTYPE_DYADIC_SRC_DST:
|
|
TME_M6888X_IEEE754_OP_RUN(fpgen->tme_m6888x_fpgen_func_ops_offset, (struct tme_ieee754_ctl *, const struct tme_float *, const struct tme_float *, struct tme_float *), (&ic->tme_m68k_fpu_ieee754_ctl, src, dst, dst));
|
|
break;
|
|
case TME_M6888X_OPTYPE_DYADIC_DST_SRC:
|
|
TME_M6888X_IEEE754_OP_RUN(fpgen->tme_m6888x_fpgen_func_ops_offset, (struct tme_ieee754_ctl *, const struct tme_float *, const struct tme_float *, struct tme_float *), (&ic->tme_m68k_fpu_ieee754_ctl, dst, src, dst));
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* set the floating-point condition codes: */
|
|
_tme_m6888x_fpcc(ic, dst, TME_FLOAT_FORMAT_IEEE754_EXTENDED80 | TME_FLOAT_FORMAT_IEEE754_EXTENDED80_BUILTIN);
|
|
|
|
#undef TME_M68K_AREG_INCREMENT
|
|
}
|
|
|
|
TME_M6888X_FPGEN(_tme_m6888x_fsincos)
|
|
{
|
|
/* "If FPs and FPc are specified to be the same register, the cosine
|
|
result is first loaded into the register and then is overwritten
|
|
with the sine result." */
|
|
TME_M6888X_IEEE754_OP_MONADIC(tme_ieee754_ops_extended80_cos,
|
|
src,
|
|
&ic->tme_m68k_fpu_fpreg[TME_FIELD_EXTRACTU(TME_M68K_INSN_SPECOP, 0, 3)]);
|
|
TME_M6888X_IEEE754_OP_MONADIC(tme_ieee754_ops_extended80_sin,
|
|
src,
|
|
dst);
|
|
}
|
|
|
|
TME_M6888X_FPGEN(_tme_m6888x_fcmp)
|
|
{
|
|
int dst_is_negative;
|
|
int src_is_negative;
|
|
|
|
/* check for a NaN operand: */
|
|
if (__tme_predict_false(tme_ieee754_extended80_check_nan_dyadic(&ic->tme_m68k_fpu_ieee754_ctl, src, dst, dst))) {
|
|
return;
|
|
}
|
|
|
|
/* see if the destination is negative: */
|
|
dst_is_negative
|
|
= (tme_float_is_negative(dst,
|
|
(TME_FLOAT_FORMAT_IEEE754_EXTENDED80
|
|
| TME_FLOAT_FORMAT_IEEE754_EXTENDED80_BUILTIN))
|
|
!= 0);
|
|
|
|
/* if the source operand is an infinity: */
|
|
if (tme_ieee754_extended80_is_inf(src)) {
|
|
|
|
/* see if the source operand is negative infinity: */
|
|
src_is_negative
|
|
= (tme_float_is_negative(src,
|
|
(TME_FLOAT_FORMAT_IEEE754_EXTENDED80
|
|
| TME_FLOAT_FORMAT_IEEE754_EXTENDED80_BUILTIN))
|
|
!= 0);
|
|
|
|
/* if the destination operand is the same infinity as the source operand: */
|
|
if (tme_ieee754_extended80_is_inf(dst)
|
|
&& dst_is_negative == src_is_negative) {
|
|
|
|
/* return a zero, to set Z, with the same sign as the source
|
|
operand, to set N appropriately: */
|
|
tme_ieee754_extended80_value_set_constant(dst, &tme_ieee754_extended80_constant_zero);
|
|
if (src_is_negative) {
|
|
assert (dst->tme_float_format == TME_FLOAT_FORMAT_IEEE754_EXTENDED80);
|
|
dst->tme_float_value_ieee754_extended80.tme_float_ieee754_extended80_sexp |= 0x8000;
|
|
}
|
|
}
|
|
|
|
/* otherwise, either the destination operand is not an infinity
|
|
or it is the other infinity: */
|
|
else {
|
|
|
|
/* return a one with the opposite sign as the source operand, to
|
|
set N appropriately: */
|
|
tme_ieee754_extended80_value_set_constant(dst, &tme_ieee754_extended80_constant_one);
|
|
if (!src_is_negative) {
|
|
assert (dst->tme_float_format == TME_FLOAT_FORMAT_IEEE754_EXTENDED80);
|
|
dst->tme_float_value_ieee754_extended80.tme_float_ieee754_extended80_sexp |= 0x8000;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* otherwise, if the destination operand is an infinity: */
|
|
else if (tme_ieee754_extended80_is_inf(dst)) {
|
|
|
|
/* return a one with the same sign as the destination operand, to
|
|
set N appropriately: */
|
|
tme_ieee754_extended80_value_set_constant(dst, &tme_ieee754_extended80_constant_one);
|
|
if (dst_is_negative) {
|
|
assert (dst->tme_float_format == TME_FLOAT_FORMAT_IEEE754_EXTENDED80);
|
|
dst->tme_float_value_ieee754_extended80.tme_float_ieee754_extended80_sexp |= 0x8000;
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* do the subtraction: */
|
|
TME_M6888X_IEEE754_OP_DYADIC(tme_ieee754_ops_extended80_sub,
|
|
dst,
|
|
src,
|
|
dst);
|
|
}
|
|
|
|
TME_M6888X_FPGEN(_tme_m6888x_ftst)
|
|
{
|
|
*dst = *src;
|
|
}
|
|
|
|
TME_M6888X_FPGEN(_tme_m6888x_ftwotox)
|
|
{
|
|
struct tme_float two;
|
|
|
|
tme_ieee754_extended80_value_set_constant(&two, &tme_ieee754_extended80_constant_2e2ex[0]);
|
|
TME_M6888X_IEEE754_OP_DYADIC(tme_ieee754_ops_extended80_pow,
|
|
src,
|
|
&two,
|
|
dst);
|
|
}
|
|
|
|
TME_M6888X_FPGEN(_tme_m6888x_ftentox)
|
|
{
|
|
struct tme_float ten;
|
|
|
|
tme_ieee754_extended80_value_set_constant(&ten, &tme_ieee754_extended80_constant_10e2ex[0]);
|
|
TME_M6888X_IEEE754_OP_DYADIC(tme_ieee754_ops_extended80_pow,
|
|
src,
|
|
&ten,
|
|
dst);
|
|
}
|
|
|
|
TME_M6888X_FPGEN(_tme_m6888x_flog2)
|
|
{
|
|
struct tme_float log_two;
|
|
|
|
/* 2^log2(x) = e^log(x) */
|
|
/* log(2^log2(x)) = log(e^log(x)) */
|
|
/* log2(x) * log(2) = log(x) * log(e) */
|
|
/* log2(x) = log(x) / log(2) */
|
|
|
|
TME_M6888X_IEEE754_OP_MONADIC(tme_ieee754_ops_extended80_log,
|
|
src,
|
|
dst);
|
|
tme_ieee754_extended80_value_set_constant(&log_two, &tme_ieee754_extended80_constant_ln_2);
|
|
TME_M6888X_IEEE754_OP_DYADIC(tme_ieee754_ops_extended80_div,
|
|
dst,
|
|
&log_two,
|
|
dst);
|
|
}
|
|
|
|
/* this internal function handles fmod and frem: */
|
|
static void
|
|
_tme_m6888x_fmodrem(struct tme_m68k *ic, const struct tme_float *src, struct tme_float *dst, int rounding)
|
|
{
|
|
struct tme_float quotient;
|
|
struct tme_float quotient_divisor;
|
|
tme_int32_t quotient_byte;
|
|
struct tme_float two_hundred_fifty_six;
|
|
|
|
/* check for a NaN operand: */
|
|
if (__tme_predict_false(tme_ieee754_extended80_check_nan_dyadic(&ic->tme_m68k_fpu_ieee754_ctl, src, dst, dst))) {
|
|
return;
|
|
}
|
|
|
|
/* if the source operand is zero, or if the destination operand is infinity: */
|
|
if (tme_ieee754_extended80_is_zero(src)
|
|
|| tme_ieee754_extended80_is_inf(dst)) {
|
|
|
|
/* return a NaN: */
|
|
dst->tme_float_format = TME_FLOAT_FORMAT_IEEE754_EXTENDED80;
|
|
dst->tme_float_value_ieee754_extended80 = ic->tme_m68k_fpu_ieee754_ctl.tme_ieee754_ctl_default_nan_extended80;
|
|
return;
|
|
}
|
|
|
|
/* do the division. the quotient must not be a NaN: */
|
|
ic->tme_m68k_fpu_ieee754_ctl.tme_ieee754_ctl_rounding_mode = rounding;
|
|
TME_M6888X_IEEE754_OP_DYADIC(tme_ieee754_ops_extended80_div, dst, src, "ient);
|
|
assert (!tme_ieee754_extended80_is_nan("ient));
|
|
|
|
/* round the quotient to an integer: */
|
|
/* XXX FIXME we assume that the rounding mode is the same as the division: */
|
|
TME_M6888X_IEEE754_OP_MONADIC(tme_ieee754_ops_extended80_rint, "ient, "ient);
|
|
|
|
/* get the remainder: */
|
|
TME_M6888X_IEEE754_OP_DYADIC(tme_ieee754_ops_extended80_mul, src, "ient, "ient_divisor);
|
|
TME_M6888X_IEEE754_OP_DYADIC(tme_ieee754_ops_extended80_sub, dst, "ient_divisor, dst);
|
|
|
|
/* get the quotient's least significant eight bits, eventually
|
|
truncating them to seven: */
|
|
tme_ieee754_extended80_from_int32(256, &two_hundred_fifty_six);
|
|
TME_M6888X_IEEE754_OP_DYADIC(tme_ieee754_ops_extended80_rem, "ient, &two_hundred_fifty_six, "ient);
|
|
quotient_byte = tme_ieee754_extended80_value_builtin_get("ient);
|
|
if (quotient_byte >= 0) {
|
|
quotient_byte &= 0x7f;
|
|
}
|
|
else {
|
|
quotient_byte = ((-quotient_byte) & 0x7f) | 0x80;
|
|
}
|
|
|
|
/* update the quotient byte in the FPSR: */
|
|
TME_FIELD_MASK_DEPOSITU(ic->tme_m68k_fpu_fpsr, TME_M6888X_FPSR_QUOTIENT, ((tme_uint32_t) quotient_byte));
|
|
}
|
|
|
|
TME_M6888X_FPGEN(_tme_m6888x_fmod)
|
|
{
|
|
_tme_m6888x_fmodrem(ic, src, dst, TME_FLOAT_ROUND_TO_ZERO);
|
|
}
|
|
|
|
TME_M6888X_FPGEN(_tme_m6888x_frem)
|
|
{
|
|
_tme_m6888x_fmodrem(ic, src, dst, TME_FLOAT_ROUND_NEAREST_EVEN);
|
|
}
|
|
|
|
TME_M6888X_FPGEN(_tme_m6888x_fsgldiv)
|
|
{
|
|
struct tme_float src_trunc, dst_trunc;
|
|
struct tme_float_ieee754_extended80 src_buffer, dst_buffer;
|
|
|
|
/* check for a NaN operand: */
|
|
if (__tme_predict_false(tme_ieee754_extended80_check_nan_dyadic(&ic->tme_m68k_fpu_ieee754_ctl, src, dst, dst))) {
|
|
return;
|
|
}
|
|
|
|
/* if the source and destination operands are both zero or both
|
|
infinity: */
|
|
if ((tme_ieee754_extended80_is_zero(src)
|
|
&& tme_ieee754_extended80_is_zero(dst))
|
|
|| (tme_ieee754_extended80_is_inf(src)
|
|
&& tme_ieee754_extended80_is_inf(dst))) {
|
|
|
|
/* return a NaN: */
|
|
dst->tme_float_format = TME_FLOAT_FORMAT_IEEE754_EXTENDED80;
|
|
dst->tme_float_value_ieee754_extended80 = ic->tme_m68k_fpu_ieee754_ctl.tme_ieee754_ctl_default_nan_extended80;
|
|
|
|
/* set OPERR: */
|
|
_tme_m6888x_exception(ic, TME_M6888X_FPSR_EXC_OPERR);
|
|
return;
|
|
}
|
|
|
|
/* truncate the significands of the source and destination to no
|
|
more than 24 bits to the right of the point. 24 becomes 25
|
|
because the extended80 format includes the explicit integer bit: */
|
|
tme_ieee754_extended80_value_set(&src_trunc, *tme_ieee754_extended80_value_get(src, &src_buffer));
|
|
src_trunc.tme_float_value_ieee754_extended80.tme_float_ieee754_extended80_significand.tme_value64_uint32_hi &= 0xffff8000;
|
|
src_trunc.tme_float_value_ieee754_extended80.tme_float_ieee754_extended80_significand.tme_value64_uint32_lo = 0x00000000;
|
|
tme_ieee754_extended80_value_set(&dst_trunc, *tme_ieee754_extended80_value_get(dst, &dst_buffer));
|
|
dst_trunc.tme_float_value_ieee754_extended80.tme_float_ieee754_extended80_significand.tme_value64_uint32_hi &= 0xffff8000;
|
|
dst_trunc.tme_float_value_ieee754_extended80.tme_float_ieee754_extended80_significand.tme_value64_uint32_lo = 0x00000000;
|
|
|
|
/* do the division: */
|
|
ic->tme_m68k_fpu_ieee754_ctl.tme_ieee754_ctl_extended80_rounding_precision = 32;
|
|
TME_M6888X_IEEE754_OP_DYADIC(tme_ieee754_ops_extended80_div, &dst_trunc, &src_trunc, dst);
|
|
}
|
|
|
|
TME_M6888X_FPGEN(_tme_m6888x_fsglmul)
|
|
{
|
|
struct tme_float src_trunc, dst_trunc;
|
|
struct tme_float_ieee754_extended80 src_buffer, dst_buffer;
|
|
|
|
/* check for a NaN operand: */
|
|
if (__tme_predict_false(tme_ieee754_extended80_check_nan_dyadic(&ic->tme_m68k_fpu_ieee754_ctl, src, dst, dst))) {
|
|
return;
|
|
}
|
|
|
|
/* if the source is a zero and the destination is a NaN, or vice
|
|
versa: */
|
|
if ((tme_ieee754_extended80_is_zero(src)
|
|
&& tme_ieee754_extended80_is_inf(dst))
|
|
|| (tme_ieee754_extended80_is_inf(src)
|
|
&& tme_ieee754_extended80_is_zero(dst))) {
|
|
|
|
/* return a NaN: */
|
|
dst->tme_float_format = TME_FLOAT_FORMAT_IEEE754_EXTENDED80;
|
|
dst->tme_float_value_ieee754_extended80 = ic->tme_m68k_fpu_ieee754_ctl.tme_ieee754_ctl_default_nan_extended80;
|
|
|
|
/* if the destination is a zero, set OPERR: */
|
|
if (tme_ieee754_extended80_is_zero(dst)) {
|
|
_tme_m6888x_exception(ic, TME_M6888X_FPSR_EXC_OPERR);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* truncate the significands of the source and destination to no
|
|
more than 24 bits to the right of the point. 24 becomes 25
|
|
because the extended80 format includes the explicit integer bit: */
|
|
tme_ieee754_extended80_value_set(&src_trunc, *tme_ieee754_extended80_value_get(src, &src_buffer));
|
|
src_trunc.tme_float_value_ieee754_extended80.tme_float_ieee754_extended80_significand.tme_value64_uint32_hi &= 0xffff8000;
|
|
src_trunc.tme_float_value_ieee754_extended80.tme_float_ieee754_extended80_significand.tme_value64_uint32_lo = 0x00000000;
|
|
tme_ieee754_extended80_value_set(&dst_trunc, *tme_ieee754_extended80_value_get(dst, &dst_buffer));
|
|
dst_trunc.tme_float_value_ieee754_extended80.tme_float_ieee754_extended80_significand.tme_value64_uint32_hi &= 0xffff8000;
|
|
dst_trunc.tme_float_value_ieee754_extended80.tme_float_ieee754_extended80_significand.tme_value64_uint32_lo = 0x00000000;
|
|
|
|
/* do the multiplication: */
|
|
ic->tme_m68k_fpu_ieee754_ctl.tme_ieee754_ctl_extended80_rounding_precision = 32;
|
|
TME_M6888X_IEEE754_OP_DYADIC(tme_ieee754_ops_extended80_mul, &src_trunc, &dst_trunc, dst);
|
|
}
|
|
|
|
TME_M6888X_FPGEN(_tme_m6888x_fmovecr)
|
|
{
|
|
const struct tme_ieee754_extended80_constant *constant;
|
|
tme_uint16_t offset;
|
|
|
|
offset = TME_FIELD_EXTRACTU(TME_M68K_INSN_SPECOP, 0, 7);
|
|
|
|
/* the binary powers of 10 offsets: */
|
|
if (offset >= 0x33
|
|
&& offset <= 0x3f) {
|
|
constant = &tme_ieee754_extended80_constant_10e2ex[offset - 0x33];
|
|
}
|
|
|
|
/* anything else: */
|
|
else {
|
|
switch (offset) {
|
|
case 0x00: constant = &tme_ieee754_extended80_constant_pi; break;
|
|
case 0x0b: constant = &tme_ieee754_extended80_constant_log10_2; break;
|
|
case 0x0c: constant = &tme_ieee754_extended80_constant_e; break;
|
|
case 0x0d: constant = &tme_ieee754_extended80_constant_log2_e; break;
|
|
case 0x0e: constant = &tme_ieee754_extended80_constant_log10_e; break;
|
|
default:
|
|
case 0x0f: constant = &tme_ieee754_extended80_constant_zero; break;
|
|
case 0x30: constant = &tme_ieee754_extended80_constant_ln_2; break;
|
|
case 0x31: constant = &tme_ieee754_extended80_constant_ln_10; break;
|
|
case 0x32: constant = &tme_ieee754_extended80_constant_one; break;
|
|
}
|
|
}
|
|
|
|
/* return the result: */
|
|
tme_ieee754_extended80_value_set_constant(dst, constant);
|
|
}
|
|
|
|
/* this can fault: */
|
|
TME_M68K_INSN(tme_m68k_fmove_rm)
|
|
{
|
|
unsigned int ea_mode;
|
|
unsigned int ea_reg;
|
|
unsigned int ea_size;
|
|
unsigned int destination_format;
|
|
const struct tme_float *src;
|
|
struct tme_float src_buffer;
|
|
const struct tme_float *dst;
|
|
struct tme_float dst_buffer;
|
|
unsigned int dst_formats;
|
|
int src_is_nan;
|
|
tme_int32_t value_int32_raw;
|
|
tme_int32_t value_int32;
|
|
tme_uint32_t single_buffer;
|
|
const union tme_value64 *value64;
|
|
union tme_value64 value64_buffer;
|
|
const struct tme_float_ieee754_extended80 *extended80;
|
|
struct tme_float_ieee754_extended80 extended80_buffer;
|
|
|
|
/* get the EA mode and register fields: */
|
|
ea_mode = TME_FIELD_EXTRACTU(TME_M68K_INSN_OPCODE, 3, 3);
|
|
ea_reg = TME_FIELD_EXTRACTU(TME_M68K_INSN_OPCODE, 0, 3);
|
|
|
|
/* get the destination format: */
|
|
destination_format = TME_FIELD_EXTRACTU(TME_M68K_INSN_SPECOP, 10, 3);
|
|
|
|
/* if this is an address register direct EA, or this is a data
|
|
register direct EA and the destination format isn't byte, word,
|
|
long, or single, this is an illegal instruction: */
|
|
if (ea_mode == 1
|
|
|| (ea_mode == 0
|
|
&& destination_format != TME_M6888X_TYPE_BYTE
|
|
&& destination_format != TME_M6888X_TYPE_WORD
|
|
&& destination_format != TME_M6888X_TYPE_LONG
|
|
&& destination_format != TME_M6888X_TYPE_SINGLE)) {
|
|
TME_M68K_INSN_EXCEPTION(TME_M68K_EXCEPTION_ILL);
|
|
}
|
|
|
|
/* for the effective address predecrement and postincrement modes,
|
|
and for the integer conversions, we require that these size
|
|
macros correspond exactly to the number of bytes: */
|
|
#if TME_M68K_SIZE_8 != 1
|
|
#error "TME_M68K_SIZE_8 must be 1"
|
|
#endif
|
|
#if TME_M68K_SIZE_16 != 2
|
|
#error "TME_M68K_SIZE_16 must be 2"
|
|
#endif
|
|
#if TME_M68K_SIZE_32 != 4
|
|
#error "TME_M68K_SIZE_32 must be 4"
|
|
#endif
|
|
#if TME_M68K_SIZE_64 != 8
|
|
#error "TME_M68K_SIZE_64 must be 8"
|
|
#endif
|
|
#if TME_M68K_SIZE_96 != 12
|
|
#error "TME_M68K_SIZE_96 must be 12"
|
|
#endif
|
|
#define TME_M68K_AREG_INCREMENT(areg, size) \
|
|
((size) + (((size) == TME_M68K_SIZE_8 && (areg) == TME_M68K_IREG_A7) ? 1 : 0))
|
|
|
|
/* dispatch on the destination format to get the size of the destination: */
|
|
switch (destination_format) {
|
|
case TME_M6888X_TYPE_BYTE: ea_size = TME_M68K_SIZE_8; break;
|
|
case TME_M6888X_TYPE_WORD: ea_size = TME_M68K_SIZE_16; break;
|
|
case TME_M6888X_TYPE_LONG: /* FALLTHROUGH */
|
|
case TME_M6888X_TYPE_SINGLE: ea_size = TME_M68K_SIZE_32; break;
|
|
case TME_M6888X_TYPE_DOUBLE: ea_size = TME_M68K_SIZE_64; break;
|
|
default: assert(FALSE);
|
|
case TME_M6888X_TYPE_PACKEDDEC: /* FALLTHROUGH */
|
|
case TME_M6888X_TYPE_PACKEDDEC_DK: /* FALLTHROUGH */
|
|
case TME_M6888X_TYPE_EXTENDED80: ea_size = TME_M68K_SIZE_96; break;
|
|
}
|
|
|
|
/* if we're not restarting: */
|
|
if (!TME_M68K_SEQUENCE_RESTARTING) {
|
|
|
|
/* do the common fpgen setup: */
|
|
_tme_m6888x_fpgen_enter(ic, &_tme_m6888x_fpgen_fmove_rm);
|
|
|
|
/* get the source register: */
|
|
src = &ic->tme_m68k_fpu_fpreg[TME_FIELD_EXTRACTU(TME_M68K_INSN_SPECOP, 7, 3)];
|
|
|
|
/* check for a NaN operand: */
|
|
src_is_nan = tme_ieee754_extended80_check_nan_monadic(&ic->tme_m68k_fpu_ieee754_ctl, src, &src_buffer);
|
|
if (src_is_nan) {
|
|
src = &src_buffer;
|
|
}
|
|
|
|
/* assume that the source is the destination: */
|
|
dst = src;
|
|
dst_formats = TME_FLOAT_FORMAT_IEEE754_EXTENDED80 | TME_FLOAT_FORMAT_IEEE754_EXTENDED80_BUILTIN;
|
|
|
|
/* dispatch on the destination format: */
|
|
switch (destination_format) {
|
|
|
|
case TME_M6888X_TYPE_BYTE:
|
|
case TME_M6888X_TYPE_WORD:
|
|
case TME_M6888X_TYPE_LONG:
|
|
if (src_is_nan) {
|
|
/* XXX how is a NaN converted into an integer? */
|
|
value_int32 = -1;
|
|
_tme_m6888x_exception(ic, TME_M6888X_FPSR_EXC_OPERR);
|
|
}
|
|
else {
|
|
TME_M6888X_IEEE754_OP_MONADIC(tme_ieee754_ops_extended80_to_int32, src, &value_int32_raw);
|
|
value_int32 = TME_MIN(value_int32_raw, (2147483647 / (1L << (8 * (TME_M68K_SIZE_32 - ea_size)))));
|
|
value_int32 = TME_MAX(value_int32, ((-1073741824 * 2) / (1L << (8 * (TME_M68K_SIZE_32 - ea_size)))));
|
|
if (tme_ieee754_extended80_is_inf(src)
|
|
|| value_int32 != value_int32_raw) {
|
|
_tme_m6888x_exception(ic, TME_M6888X_FPSR_EXC_OPERR);
|
|
}
|
|
}
|
|
ic->tme_m68k_ireg_memx32 = value_int32;
|
|
break;
|
|
|
|
case TME_M6888X_TYPE_SINGLE:
|
|
TME_M6888X_IEEE754_OP_MONADIC(tme_ieee754_ops_single_from_extended80, src, &dst_buffer);
|
|
ic->tme_m68k_ireg_memx32 = *tme_ieee754_single_value_get(&dst_buffer, &single_buffer);
|
|
dst = &dst_buffer;
|
|
dst_formats = TME_FLOAT_FORMAT_IEEE754_SINGLE | TME_FLOAT_FORMAT_IEEE754_SINGLE_BUILTIN;
|
|
break;
|
|
|
|
case TME_M6888X_TYPE_DOUBLE:
|
|
TME_M6888X_IEEE754_OP_MONADIC(tme_ieee754_ops_double_from_extended80, src, &dst_buffer);
|
|
value64 = tme_ieee754_double_value_get(&dst_buffer, &value64_buffer);
|
|
ic->tme_m68k_ireg_memx32 = value64->tme_value64_uint32_hi;
|
|
ic->tme_m68k_ireg_memy32 = value64->tme_value64_uint32_lo;
|
|
dst = &dst_buffer;
|
|
dst_formats = TME_FLOAT_FORMAT_IEEE754_DOUBLE | TME_FLOAT_FORMAT_IEEE754_DOUBLE_BUILTIN;
|
|
break;
|
|
|
|
case TME_M6888X_TYPE_EXTENDED80:
|
|
extended80 = tme_ieee754_extended80_value_get(src, &extended80_buffer);
|
|
ic->tme_m68k_ireg_memx32 = extended80->tme_float_ieee754_extended80_sexp << 16;
|
|
ic->tme_m68k_ireg_memy32 = extended80->tme_float_ieee754_extended80_significand.tme_value64_uint32_hi;
|
|
ic->tme_m68k_ireg_memz32 = extended80->tme_float_ieee754_extended80_significand.tme_value64_uint32_lo;
|
|
break;
|
|
|
|
default:
|
|
assert(FALSE);
|
|
/* FALLTHROUGH */
|
|
|
|
case TME_M6888X_TYPE_PACKEDDEC:
|
|
case TME_M6888X_TYPE_PACKEDDEC_DK:
|
|
|
|
/* we punt on the packed-decimal format for now: */
|
|
TME_M68K_INSN_EXCEPTION(TME_M68K_EXCEPTION_ILL);
|
|
break;
|
|
}
|
|
|
|
/* set the floating-point condition codes: */
|
|
_tme_m6888x_fpcc(ic, dst, dst_formats);
|
|
}
|
|
|
|
/* if this is a data register direct EA: */
|
|
if (ea_mode == 0) {
|
|
|
|
switch (ea_size) {
|
|
case TME_M68K_SIZE_8:
|
|
ic->tme_m68k_ireg_uint8(ea_reg << 2) = ic->tme_m68k_ireg_memx32;
|
|
break;
|
|
|
|
case TME_M68K_SIZE_16:
|
|
ic->tme_m68k_ireg_uint8(ea_reg << 1) = ic->tme_m68k_ireg_memx32;
|
|
break;
|
|
|
|
default:
|
|
assert (FALSE);
|
|
/* FALLTHROUGH */
|
|
|
|
case TME_M68K_SIZE_32:
|
|
ic->tme_m68k_ireg_uint32(ea_reg) = ic->tme_m68k_ireg_memx32;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* otherwise, this is a memory EA: */
|
|
else {
|
|
|
|
/* this instruction can fault: */
|
|
TME_M68K_INSN_CANFAULT;
|
|
|
|
/* adjust ea_reg to reference the address register: */
|
|
ea_reg += TME_M68K_IREG_A0;
|
|
|
|
/* address register indirect postincrement: */
|
|
if (ea_mode == 3) {
|
|
/* if we are not restarting, set the effective address: */
|
|
if (!TME_M68K_SEQUENCE_RESTARTING) {
|
|
ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(ea_reg);
|
|
ic->tme_m68k_ireg_uint32(ea_reg) += TME_M68K_AREG_INCREMENT(ea_reg, ea_size);
|
|
}
|
|
}
|
|
|
|
/* address register indirect predecrement: */
|
|
else if (ea_mode == 4) {
|
|
/* if we are not restarting, set the effective address: */
|
|
if (!TME_M68K_SEQUENCE_RESTARTING) {
|
|
ic->tme_m68k_ireg_uint32(ea_reg) -= TME_M68K_AREG_INCREMENT(ea_reg, ea_size);
|
|
ic->_tme_m68k_ea_address = ic->tme_m68k_ireg_uint32(ea_reg);
|
|
}
|
|
}
|
|
|
|
/* dispatch on the operand size to write in the destination as one
|
|
or more 32-bit words. we will write up to three 32-bit words
|
|
from memx, memy, and memz: */
|
|
switch (ea_size) {
|
|
|
|
/* this can only happen when the source operand is a byte: */
|
|
case TME_M68K_SIZE_8:
|
|
tme_m68k_write_memx8(ic);
|
|
assert (!TME_M68K_SEQUENCE_RESTARTING);
|
|
break;
|
|
|
|
/* this can only happen when the source operand is a word: */
|
|
case TME_M68K_SIZE_16:
|
|
tme_m68k_write_memx16(ic);
|
|
assert (!TME_M68K_SEQUENCE_RESTARTING);
|
|
break;
|
|
|
|
/* everything else is one or more 32-bit words: */
|
|
default:
|
|
|
|
/* write the first 32 bits from the memx register: */
|
|
tme_m68k_write_memx32(ic);
|
|
if (ea_size == TME_M68K_SIZE_32) {
|
|
break;
|
|
}
|
|
|
|
/* write the second 32 bits from the memy register: */
|
|
if (!TME_M68K_SEQUENCE_RESTARTING) {
|
|
ic->_tme_m68k_ea_address += TME_M68K_SIZE_32;
|
|
}
|
|
tme_m68k_write_mem32(ic, TME_M68K_IREG_MEMY32);
|
|
if (ea_size == TME_M68K_SIZE_64) {
|
|
break;
|
|
}
|
|
|
|
/* write the third 32 bits from the memz register: */
|
|
if (!TME_M68K_SEQUENCE_RESTARTING) {
|
|
ic->_tme_m68k_ea_address += TME_M68K_SIZE_32;
|
|
}
|
|
tme_m68k_write_mem32(ic, TME_M68K_IREG_MEMZ32);
|
|
break;
|
|
}
|
|
}
|
|
|
|
TME_M68K_INSN_OK;
|
|
|
|
#undef TME_M68K_AREG_INCREMENT
|
|
}
|
|
|
|
/* this can fault: */
|
|
TME_M68K_INSN(tme_m68k_fmovem)
|
|
{
|
|
unsigned int ea_mode;
|
|
unsigned int ea_reg;
|
|
unsigned int register_to_memory;
|
|
tme_uint16_t mask;
|
|
unsigned int bit;
|
|
unsigned int first_register;
|
|
struct tme_float *fpreg;
|
|
const struct tme_float_ieee754_extended80 *extended80;
|
|
struct tme_float_ieee754_extended80 extended80_buffer;
|
|
|
|
TME_M68K_INSN_FPU;
|
|
|
|
/* get the EA mode and register fields: */
|
|
ea_mode = TME_FIELD_EXTRACTU(TME_M68K_INSN_OPCODE, 3, 3);
|
|
ea_reg = TME_FIELD_EXTRACTU(TME_M68K_INSN_OPCODE, 0, 3);
|
|
|
|
/* get the register-to-memory flag: */
|
|
register_to_memory = (TME_M68K_INSN_SPECOP & TME_BIT(13)) != 0;
|
|
|
|
/* immediate EAs must have already been caught as illegal instructions: */
|
|
assert (!(ea_mode == 7 && ea_reg == 4));
|
|
|
|
/* if this is a data register direct EA or an address register
|
|
direct EA, or if this is a predecrement EA and this is a
|
|
memory-to-register operation, or if this is a postincrement EA
|
|
and this is a register-to-memory operation, this is an illegal
|
|
instruction: */
|
|
if (ea_mode == 0
|
|
|| ea_mode == 1
|
|
|| (ea_mode == 4
|
|
&& !register_to_memory)
|
|
|| (ea_mode == 3
|
|
&& register_to_memory)) {
|
|
TME_M68K_INSN_EXCEPTION(TME_M68K_EXCEPTION_ILL);
|
|
}
|
|
|
|
/* get the register list: */
|
|
mask = TME_FIELD_EXTRACTU(TME_M68K_INSN_SPECOP, 0, 8);
|
|
|
|
/* if the register list is dynamic: */
|
|
if (TME_M68K_INSN_SPECOP & TME_BIT(11)) {
|
|
|
|
/* the mask field is supposed to contain only a data register
|
|
number: */
|
|
if (mask & 0x8f) {
|
|
TME_M68K_INSN_EXCEPTION(TME_M68K_EXCEPTION_ILL);
|
|
}
|
|
|
|
/* get the dynamic register list: */
|
|
mask = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_D0 + TME_FIELD_EXTRACTU(mask, 4, 3));
|
|
}
|
|
|
|
/* get the FP register corresponding to bit 7 in the mask: */
|
|
if (TME_M68K_INSN_SPECOP & TME_BIT(12)) {
|
|
first_register = 0;
|
|
}
|
|
else {
|
|
|
|
/* this must be a predecrement EA: */
|
|
if (ea_mode != 4) {
|
|
TME_M68K_INSN_EXCEPTION(TME_M68K_EXCEPTION_ILL);
|
|
}
|
|
|
|
first_register = 7;
|
|
}
|
|
|
|
/* if the mask is empty, return now: */
|
|
if (mask == 0) {
|
|
TME_M68K_INSN_OK;
|
|
}
|
|
|
|
/* this instruction can fault: */
|
|
TME_M68K_INSN_CANFAULT;
|
|
|
|
/* we require that TME_M68K_SIZE_96 be 12: */
|
|
#if TME_M68K_SIZE_96 != 12
|
|
#error "TME_M68K_SIZE_96 must be 12"
|
|
#endif
|
|
|
|
/* loop over the bits in the mask: */
|
|
for (bit = 0; bit < 8; bit++, mask <<= 1) {
|
|
|
|
/* skip this register if its bit isn't set in the mask: */
|
|
if (!(mask & 0x80)) {
|
|
continue;
|
|
}
|
|
|
|
/* get this register: */
|
|
fpreg = &ic->tme_m68k_fpu_fpreg[bit ^ first_register];
|
|
|
|
/* if this is a register-to-memory operation: */
|
|
if (register_to_memory) {
|
|
|
|
/* if this is a predecrement EA, and we're not restarting,
|
|
predecrement the EA: */
|
|
if (!TME_M68K_SEQUENCE_RESTARTING
|
|
&& ea_mode == 4) {
|
|
ic->_tme_m68k_ea_address = (ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ea_reg) -= TME_M68K_SIZE_96);
|
|
}
|
|
|
|
/* write out the register: */
|
|
extended80 = tme_ieee754_extended80_value_get(fpreg, &extended80_buffer);
|
|
if (!TME_M68K_SEQUENCE_RESTARTING) {
|
|
ic->tme_m68k_ireg_memx32 = extended80->tme_float_ieee754_extended80_sexp << 16;
|
|
}
|
|
tme_m68k_write_memx32(ic);
|
|
if (!TME_M68K_SEQUENCE_RESTARTING) {
|
|
ic->_tme_m68k_ea_address += TME_M68K_SIZE_32;
|
|
ic->tme_m68k_ireg_memx32 = extended80->tme_float_ieee754_extended80_significand.tme_value64_uint32_hi;
|
|
}
|
|
tme_m68k_write_memx32(ic);
|
|
if (!TME_M68K_SEQUENCE_RESTARTING) {
|
|
ic->_tme_m68k_ea_address += TME_M68K_SIZE_32;
|
|
ic->tme_m68k_ireg_memx32 = extended80->tme_float_ieee754_extended80_significand.tme_value64_uint32_lo;
|
|
}
|
|
tme_m68k_write_memx32(ic);
|
|
if (!TME_M68K_SEQUENCE_RESTARTING) {
|
|
ic->_tme_m68k_ea_address += TME_M68K_SIZE_32;
|
|
}
|
|
}
|
|
|
|
/* otherwise, this is a memory-to-register operation: */
|
|
else {
|
|
|
|
/* read in this register: */
|
|
tme_m68k_read_memx32(ic);
|
|
if (!TME_M68K_SEQUENCE_RESTARTING) {
|
|
fpreg->tme_float_value_ieee754_extended80.tme_float_ieee754_extended80_sexp = (ic->tme_m68k_ireg_memx32 >> 16);
|
|
ic->_tme_m68k_ea_address += TME_M68K_SIZE_32;
|
|
}
|
|
tme_m68k_read_memx32(ic);
|
|
if (!TME_M68K_SEQUENCE_RESTARTING) {
|
|
fpreg->tme_float_value_ieee754_extended80.tme_float_ieee754_extended80_significand.tme_value64_uint32_hi = ic->tme_m68k_ireg_memx32;
|
|
ic->_tme_m68k_ea_address += TME_M68K_SIZE_32;
|
|
}
|
|
tme_m68k_read_memx32(ic);
|
|
if (!TME_M68K_SEQUENCE_RESTARTING) {
|
|
fpreg->tme_float_value_ieee754_extended80.tme_float_ieee754_extended80_significand.tme_value64_uint32_lo = ic->tme_m68k_ireg_memx32;
|
|
ic->_tme_m68k_ea_address += TME_M68K_SIZE_32;
|
|
fpreg->tme_float_format = TME_FLOAT_FORMAT_IEEE754_EXTENDED80;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* if this is the postincrement addressing mode: */
|
|
if (ea_mode == 3) {
|
|
|
|
/* update the address register: */
|
|
assert (!TME_M68K_SEQUENCE_RESTARTING);
|
|
ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ea_reg) = ic->_tme_m68k_ea_address;
|
|
}
|
|
|
|
TME_M68K_INSN_OK;
|
|
}
|
|
|
|
/* this can fault: */
|
|
TME_M68K_INSN(tme_m68k_fmovemctl)
|
|
{
|
|
tme_uint16_t mask;
|
|
unsigned int ea_mode;
|
|
unsigned int ea_reg;
|
|
unsigned int register_to_memory;
|
|
unsigned int bit;
|
|
tme_uint32_t *value;
|
|
|
|
TME_M68K_INSN_FPU;
|
|
|
|
/* get the register mask: */
|
|
mask = TME_FIELD_EXTRACTU(TME_M68K_INSN_SPECOP, 10, 3);
|
|
|
|
/* get the EA mode and register fields: */
|
|
ea_mode = TME_FIELD_EXTRACTU(TME_M68K_INSN_OPCODE, 3, 3);
|
|
ea_reg = TME_FIELD_EXTRACTU(TME_M68K_INSN_OPCODE, 0, 3);
|
|
|
|
/* get the register-to-memory flag: */
|
|
register_to_memory = (TME_M68K_INSN_SPECOP & TME_BIT(13)) != 0;
|
|
|
|
/* if no registers have been selected, or if this is a data register
|
|
direct EA and multiple registers have been selected, or if this
|
|
is an address register direct EA and the floating point
|
|
instruction address register is not the single register selected,
|
|
this is an illegal instruction: */
|
|
if (mask == 0
|
|
|| (ea_mode == 0
|
|
&& ((mask & (mask - 1)) != 0))
|
|
|| (ea_mode == 1
|
|
&& mask != 1)) {
|
|
TME_M68K_INSN_EXCEPTION(TME_M68K_EXCEPTION_ILL);
|
|
}
|
|
|
|
/* if this isn't a data register direct EA or an address register
|
|
direct EA, this instruction can fault: */
|
|
if (ea_mode != 0
|
|
&& ea_mode != 1) {
|
|
TME_M68K_INSN_CANFAULT;
|
|
}
|
|
|
|
/* if we're not restarting, and this is the predecrement addressing mode: */
|
|
if (!TME_M68K_SEQUENCE_RESTARTING
|
|
&& ea_mode == 4) {
|
|
|
|
/* update the effective address: */
|
|
for (; mask != 0; ic->_tme_m68k_ea_address -= sizeof(tme_uint32_t), mask &= (mask - 1));
|
|
|
|
/* update the address register: */
|
|
ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ea_reg) = ic->_tme_m68k_ea_address;
|
|
}
|
|
|
|
/* get the register mask: */
|
|
mask = TME_FIELD_EXTRACTU(TME_M68K_INSN_SPECOP, 10, 3);
|
|
|
|
/* loop over the register mask bits: */
|
|
for (bit = 3; bit-- > 0; ) {
|
|
|
|
/* ignore this register if its bit isn't set: */
|
|
if (!(mask & (1 << bit))) {
|
|
continue;
|
|
}
|
|
|
|
/* get a pointer to this register's value: */
|
|
value = (bit == 2
|
|
? &ic->tme_m68k_fpu_fpcr
|
|
: bit == 1
|
|
? &ic->tme_m68k_fpu_fpsr
|
|
: &ic->tme_m68k_fpu_fpiar);
|
|
|
|
/* transfer this register's value: */
|
|
|
|
/* if this is a data register direct EA: */
|
|
if (ea_mode == 0) {
|
|
if (register_to_memory) {
|
|
ic->tme_m68k_ireg_uint32(TME_M68K_IREG_D0 + ea_reg) = *value;
|
|
}
|
|
else {
|
|
*value = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_D0 + ea_reg);
|
|
}
|
|
}
|
|
|
|
/* if this is an address register direct EA: */
|
|
else if (ea_mode == 1) {
|
|
if (register_to_memory) {
|
|
ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ea_reg) = *value;
|
|
}
|
|
else {
|
|
*value = ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ea_reg);
|
|
}
|
|
}
|
|
|
|
/* otherwise, this is a memory EA: */
|
|
else {
|
|
if (register_to_memory) {
|
|
if (!TME_M68K_SEQUENCE_RESTARTING) {
|
|
ic->tme_m68k_ireg_memx32 = *value;
|
|
}
|
|
tme_m68k_write_memx32(ic);
|
|
if (!TME_M68K_SEQUENCE_RESTARTING) {
|
|
ic->_tme_m68k_ea_address += sizeof(tme_uint32_t);
|
|
}
|
|
}
|
|
else {
|
|
tme_m68k_read_memx32(ic);
|
|
if (!TME_M68K_SEQUENCE_RESTARTING) {
|
|
*value = ic->tme_m68k_ireg_memx32;
|
|
ic->_tme_m68k_ea_address += sizeof(tme_uint32_t);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* if this is the postincrement addressing mode: */
|
|
if (ea_mode == 3) {
|
|
|
|
/* update the address register: */
|
|
assert (!TME_M68K_SEQUENCE_RESTARTING);
|
|
ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0 + ea_reg) = ic->_tme_m68k_ea_address;
|
|
}
|
|
|
|
TME_M68K_INSN_OK;
|
|
}
|
|
|
|
/* this evaluates a floating-point predicate: */
|
|
static int
|
|
_tme_m6888x_predicate_true(struct tme_m68k *ic, tme_uint16_t predicate)
|
|
{
|
|
unsigned int cc_nan;
|
|
unsigned int cc_i;
|
|
unsigned int cc_z;
|
|
unsigned int cc_n;
|
|
|
|
/* get the condition codes: */
|
|
cc_nan = (ic->tme_m68k_fpu_fpsr & TME_M6888X_FPSR_CC_NAN) != 0;
|
|
cc_i = (ic->tme_m68k_fpu_fpsr & TME_M6888X_FPSR_CC_I) != 0;
|
|
cc_z = (ic->tme_m68k_fpu_fpsr & TME_M6888X_FPSR_CC_Z) != 0;
|
|
cc_n = (ic->tme_m68k_fpu_fpsr & TME_M6888X_FPSR_CC_N) != 0;
|
|
|
|
/* if this predicate is greater than 0x1f, this is an illegal instruction: */
|
|
if (predicate > 0x1f) {
|
|
TME_M68K_INSN_EXCEPTION(TME_M68K_EXCEPTION_ILL);
|
|
}
|
|
|
|
/* if this predicate sets BSUN when NaN is set: */
|
|
if (predicate > 0x0f) {
|
|
|
|
/* if NaN is set, set BSUN: */
|
|
if (cc_nan) {
|
|
_tme_m6888x_exception(ic, TME_M6888X_FPSR_EXC_BSUN);
|
|
}
|
|
|
|
/* adjust predicate to be its non-BSUN-setting version: */
|
|
predicate -= 0x10;
|
|
}
|
|
|
|
/* dispatch on the predicate: */
|
|
switch (predicate) {
|
|
default: assert(FALSE);
|
|
case 0x00: predicate = FALSE; break; /* F, SF */
|
|
case 0x01: predicate = cc_z; break; /* EQ, SEQ */
|
|
case 0x02: predicate = !(cc_nan || cc_z || cc_n); break; /* OGT, GT */
|
|
case 0x03: predicate = cc_z || !(cc_nan || cc_n); break; /* OGE, GE */
|
|
case 0x04: predicate = cc_n && !(cc_nan || cc_z); break; /* OLT, LT */
|
|
case 0x05: predicate = cc_z || (cc_n && !cc_nan); break; /* OLE, LE */
|
|
case 0x06: predicate = !(cc_nan || cc_z); break; /* OGL, GL */
|
|
case 0x07: predicate = !cc_nan; break; /* OR, GLE */
|
|
case 0x08: predicate = cc_nan; break; /* UN, NGLE */
|
|
case 0x09: predicate = (cc_nan || cc_z); break; /* UEQ, NGL */
|
|
case 0x0a: predicate = cc_nan || !(cc_n || cc_z); break; /* UGT, NLE */
|
|
case 0x0b: predicate = cc_nan || cc_z || !cc_n; break; /* UGE, NLT */
|
|
case 0x0c: predicate = cc_nan || (cc_n && !cc_z); break; /* ULT, NGE */
|
|
case 0x0d: predicate = (cc_nan || cc_z || cc_n); break; /* ULE, NGT */
|
|
case 0x0e: predicate = !cc_z; break; /* NE, SNE */
|
|
case 0x0f: predicate = FALSE; break; /* T, ST */
|
|
}
|
|
|
|
return (predicate);
|
|
}
|
|
|
|
/* this cannot fault: */
|
|
TME_M68K_INSN(tme_m68k_fdbcc)
|
|
{
|
|
TME_M68K_INSN_FPU;
|
|
|
|
if (_tme_m6888x_predicate_true(ic, TME_FIELD_EXTRACTU(TME_M68K_INSN_SPECOP, 0, 6))) {
|
|
if (--TME_M68K_INSN_OP0(tme_int16_t) != -1) {
|
|
TME_M68K_INSN_BRANCH(ic->tme_m68k_ireg_pc
|
|
+ 4
|
|
+ TME_EXT_S16_U32(TME_M68K_INSN_OP1(tme_int16_t)));
|
|
}
|
|
}
|
|
TME_M68K_INSN_OK;
|
|
}
|
|
|
|
/* this cannot fault: */
|
|
TME_M68K_INSN(tme_m68k_ftrapcc)
|
|
{
|
|
TME_M68K_INSN_FPU;
|
|
if (_tme_m6888x_predicate_true(ic, TME_FIELD_EXTRACTU(TME_M68K_INSN_SPECOP, 0, 6))) {
|
|
ic->tme_m68k_ireg_pc_last = ic->tme_m68k_ireg_pc;
|
|
ic->tme_m68k_ireg_pc = ic->tme_m68k_ireg_pc_next;
|
|
TME_M68K_INSN_EXCEPTION(TME_M68K_EXCEPTION_INST(TME_M68K_VECTOR_TRAP));
|
|
}
|
|
TME_M68K_INSN_OK;
|
|
}
|
|
|
|
/* this cannot fault: */
|
|
TME_M68K_INSN(tme_m68k_fscc)
|
|
{
|
|
TME_M68K_INSN_FPU;
|
|
TME_M68K_INSN_OP1(tme_uint8_t) =
|
|
(_tme_m6888x_predicate_true(ic, TME_FIELD_EXTRACTU(TME_M68K_INSN_SPECOP, 0, 6))
|
|
? 0xff
|
|
: 0x00);
|
|
TME_M68K_INSN_OK;
|
|
}
|
|
|
|
/* this cannot fault: */
|
|
TME_M68K_INSN(tme_m68k_fbcc)
|
|
{
|
|
TME_M68K_INSN_FPU;
|
|
|
|
if (_tme_m6888x_predicate_true(ic, TME_FIELD_EXTRACTU(TME_M68K_INSN_OPCODE, 0, 6))) {
|
|
TME_M68K_INSN_BRANCH(ic->tme_m68k_ireg_pc
|
|
+ sizeof(tme_uint16_t)
|
|
+ TME_M68K_INSN_OP0(tme_uint32_t));
|
|
}
|
|
TME_M68K_INSN_OK;
|
|
}
|
|
|
|
/* this can fault: */
|
|
TME_M68K_INSN(tme_m68k_fsave)
|
|
{
|
|
struct tme_m6888x_frame frame;
|
|
tme_uint32_t frame_size;
|
|
|
|
TME_M68K_INSN_FPU;
|
|
TME_M68K_INSN_PRIV;
|
|
TME_M68K_INSN_CANFAULT;
|
|
|
|
/* zero the frame: */
|
|
memset(&frame, 0, sizeof(frame));
|
|
|
|
/* dispatch on the FPU type: */
|
|
switch (ic->tme_m68k_fpu_type) {
|
|
default: assert (FALSE);
|
|
case TME_M68K_FPU_M68881:
|
|
frame.tme_m6888x_frame_version = TME_M6888X_FRAME_VERSION_IDLE_M68881;
|
|
frame.tme_m6888x_frame_size = TME_M6888X_FRAME_SIZE_IDLE_M68881;
|
|
break;
|
|
case TME_M68K_FPU_M68882:
|
|
frame.tme_m6888x_frame_version = TME_M6888X_FRAME_VERSION_IDLE_M68882;
|
|
frame.tme_m6888x_frame_size = TME_M6888X_FRAME_SIZE_IDLE_M68882;
|
|
break;
|
|
case TME_M68K_FPU_M68040:
|
|
frame.tme_m6888x_frame_version = TME_M6888X_FRAME_VERSION_IDLE_M68040;
|
|
frame.tme_m6888x_frame_size = TME_M6888X_FRAME_SIZE_IDLE_M68040;
|
|
break;
|
|
}
|
|
|
|
/* if this is the m68881 or m68882: */
|
|
if (ic->tme_m68k_fpu_type & TME_M68K_FPU_M6888X) {
|
|
|
|
/* fill in a minimal BIU flags field: */
|
|
frame.tme_m6888x_frame_words[(frame.tme_m6888x_frame_size / sizeof(tme_uint32_t)) - 2] = tme_htobe_u32(0x70000000);
|
|
}
|
|
|
|
/* get the total size of the frame: */
|
|
frame_size
|
|
= (sizeof(frame.tme_m6888x_frame_version)
|
|
+ sizeof(frame.tme_m6888x_frame_size)
|
|
+ sizeof(frame.tme_m6888x_frame_reserved2)
|
|
+ frame.tme_m6888x_frame_size);
|
|
|
|
/* if we're not restarting, and this is the predecrement addressing
|
|
mode, update the effective address and the address register: */
|
|
if (!TME_M68K_SEQUENCE_RESTARTING
|
|
&& TME_FIELD_EXTRACTU(TME_M68K_INSN_OPCODE, 3, 3) == 4) {
|
|
ic->_tme_m68k_ea_address -= frame_size;
|
|
ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0
|
|
+ TME_FIELD_EXTRACTU(TME_M68K_INSN_OPCODE, 0, 3))
|
|
= ic->_tme_m68k_ea_address;
|
|
}
|
|
|
|
/* write out the saved frame: */
|
|
tme_m68k_write_mem(ic, (tme_uint8_t *) &frame, frame_size);
|
|
}
|
|
|
|
/* this can fault: */
|
|
TME_M68K_INSN(tme_m68k_frestore)
|
|
{
|
|
tme_uint8_t frame_version;
|
|
tme_uint8_t frame_size;
|
|
int format_error;
|
|
|
|
TME_M68K_INSN_FPU;
|
|
TME_M68K_INSN_PRIV;
|
|
TME_M68K_INSN_CANFAULT;
|
|
|
|
/* read in the format word: */
|
|
tme_m68k_read_memx32(ic);
|
|
frame_version = (ic->tme_m68k_ireg_memx32 >> 24) & 0xff;
|
|
frame_size = (ic->tme_m68k_ireg_memx32 >> 16) & 0xff;
|
|
|
|
/* determine if we have a format error: */
|
|
if (frame_version == TME_M6888X_FRAME_VERSION_NULL) {
|
|
format_error = (frame_size != TME_M6888X_FRAME_SIZE_NULL);
|
|
}
|
|
else {
|
|
switch (ic->tme_m68k_fpu_type) {
|
|
default: assert (FALSE);
|
|
case TME_M68K_FPU_M68881:
|
|
format_error = (frame_version != TME_M6888X_FRAME_VERSION_IDLE_M68881
|
|
|| frame_size != TME_M6888X_FRAME_SIZE_IDLE_M68881);
|
|
break;
|
|
case TME_M68K_FPU_M68882:
|
|
format_error = (frame_version != TME_M6888X_FRAME_VERSION_IDLE_M68882
|
|
|| frame_size != TME_M6888X_FRAME_SIZE_IDLE_M68882);
|
|
break;
|
|
case TME_M68K_FPU_M68040:
|
|
format_error = (frame_version != TME_M6888X_FRAME_VERSION_IDLE_M68040
|
|
|| frame_size != TME_M6888X_FRAME_SIZE_IDLE_M68040);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* if we have a format error: */
|
|
if (format_error) {
|
|
TME_M68K_INSN_EXCEPTION(TME_M68K_EXCEPTION_INST(TME_M68K_VECTOR_FORMAT));
|
|
}
|
|
|
|
/* XXX FIXME - we don't bother reading in the rest of the frame.
|
|
this gives an incomplete emulation: */
|
|
|
|
/* if this is the postincrement addressing mode, update the address
|
|
register: */
|
|
if (TME_FIELD_EXTRACTU(TME_M68K_INSN_OPCODE, 3, 3) == 3) {
|
|
ic->tme_m68k_ireg_uint32(TME_M68K_IREG_A0
|
|
+ TME_FIELD_EXTRACTU(TME_M68K_INSN_OPCODE, 0, 3))
|
|
+= (sizeof(ic->tme_m68k_ireg_memx32)
|
|
+ frame_size);
|
|
}
|
|
|
|
/* if this was a NULL frame, reset the FPU: */
|
|
if (frame_version == TME_M6888X_FRAME_VERSION_NULL) {
|
|
tme_m68k_fpu_reset(ic);
|
|
}
|
|
}
|
|
|
|
/* this checks for an FPU argument: */
|
|
int
|
|
tme_m68k_fpu_new(struct tme_m68k *ic, const char * const *args, int *_arg_i, int *_usage, char **_output)
|
|
{
|
|
int arg_i;
|
|
int fpu_type;
|
|
const char *compliance;
|
|
int complete;
|
|
unsigned int opmode_i;
|
|
struct tme_ieee754_ctl *ctl;
|
|
|
|
/* get the argument index: */
|
|
arg_i = *_arg_i;
|
|
|
|
/* if this is not an FPU type, this is not an m6888x argument: */
|
|
if (!TME_ARG_IS(args[arg_i + 0], "fpu-type")) {
|
|
return (FALSE);
|
|
}
|
|
|
|
/* you can't specify more than one FPU type: */
|
|
if (ic->tme_m68k_fpu_type != TME_M68K_FPU_NONE) {
|
|
tme_output_append_error(_output,
|
|
"%s fpu-type %s",
|
|
_("multiple"),
|
|
_("unexpected"));
|
|
*_usage = TRUE;
|
|
return (TRUE);
|
|
}
|
|
|
|
/* get the FPU type: */
|
|
if (args[arg_i + 1] == NULL) {
|
|
*_usage = TRUE;
|
|
return (TRUE);
|
|
}
|
|
if (TME_ARG_IS(args[arg_i + 1], "m68881")) {
|
|
fpu_type = TME_M68K_FPU_M68881;
|
|
}
|
|
else if (TME_ARG_IS(args[arg_i + 1], "m68882")) {
|
|
fpu_type = TME_M68K_FPU_M68882;
|
|
}
|
|
else if (TME_ARG_IS(args[arg_i + 1], "m68040")) {
|
|
fpu_type = TME_M68K_FPU_M68040;
|
|
}
|
|
else {
|
|
tme_output_append_error(_output,
|
|
"%s fpu-type %s",
|
|
_("bad"),
|
|
args[arg_i + 1]);
|
|
*_usage = TRUE;
|
|
return (TRUE);
|
|
}
|
|
ic->tme_m68k_fpu_type = fpu_type;
|
|
arg_i += 2;
|
|
|
|
/* the next argument must be a compliance level: */
|
|
compliance = args[arg_i + 1];
|
|
if (!TME_ARG_IS(args[arg_i + 0], "fpu-compliance")
|
|
|| compliance == NULL) {
|
|
*_usage = TRUE;
|
|
return (TRUE);
|
|
}
|
|
ic->tme_m68k_fpu_ieee754_ops = tme_ieee754_ops_lookup(compliance);
|
|
if (ic->tme_m68k_fpu_ieee754_ops == NULL) {
|
|
tme_output_append_error(_output,
|
|
"%s fpu-compliance %s",
|
|
_("bad"),
|
|
compliance);
|
|
*_usage = TRUE;
|
|
return (TRUE);
|
|
}
|
|
arg_i += 2;
|
|
|
|
/* see if the operations for this compliance level are complete: */
|
|
complete = TRUE;
|
|
for (opmode_i = 0;
|
|
opmode_i < (sizeof(_tme_m6888x_fpgen_opmode_table) / sizeof(_tme_m6888x_fpgen_opmode_table[0]));
|
|
opmode_i++) {
|
|
if (_tme_m6888x_fpgen_opmode_table[opmode_i].tme_m6888x_fpgen_func_ops_offset != 0
|
|
&& TME_M6888X_IEEE754_OP_FUNC(_tme_m6888x_fpgen_opmode_table[opmode_i].tme_m6888x_fpgen_func_ops_offset) == NULL) {
|
|
complete = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* if the next argument is an incomplete disposition: */
|
|
if (TME_ARG_IS(args[arg_i + 0], "fpu-incomplete")) {
|
|
|
|
if (TME_ARG_IS(args[arg_i + 1], "abort")) {
|
|
ic->tme_m68k_fpu_incomplete_abort = TRUE;
|
|
}
|
|
else if (TME_ARG_IS(args[arg_i + 1], "line-f")) {
|
|
ic->tme_m68k_fpu_incomplete_abort = FALSE;
|
|
}
|
|
else {
|
|
tme_output_append_error(_output,
|
|
"%s fpu-incomplete %s",
|
|
_("bad"),
|
|
args[arg_i + 1]);
|
|
*_usage = TRUE;
|
|
return (TRUE);
|
|
}
|
|
arg_i += 2;
|
|
}
|
|
|
|
/* otherwise, no incomplete disposition is given. if this
|
|
compliance is incomplete: */
|
|
else if (!complete) {
|
|
tme_output_append_error(_output,
|
|
"%s %s %s fpu-incomplete",
|
|
_("compliance"),
|
|
compliance,
|
|
_("is incomplete, needs"));
|
|
*_usage = TRUE;
|
|
return (TRUE);
|
|
}
|
|
|
|
/* initialize the IEEE 754 control: */
|
|
ctl = &ic->tme_m68k_fpu_ieee754_ctl;
|
|
|
|
/* a private data structure: */
|
|
ctl->tme_ieee754_ctl_private = ic;
|
|
|
|
/* the underflow tininess-detection mode: */
|
|
/* XXX FIXME - is this right for the m6888x? */
|
|
ctl->tme_ieee754_ctl_detect_tininess = TME_IEEE754_CTL_DETECT_TININESS_BEFORE_ROUNDING;
|
|
|
|
/* the exception function: */
|
|
ctl->tme_ieee754_ctl_exception = _tme_m6888x_exception_ieee754;
|
|
|
|
/* we don't check whether or not a value is a NaN when converting it
|
|
from one precision to another: */
|
|
ctl->tme_ieee754_ctl_check_snan_on_conversion = FALSE;
|
|
|
|
/* the default generated NaN patterns: */
|
|
ctl->tme_ieee754_ctl_default_nan_single = 0x7fffffff;
|
|
ctl->tme_ieee754_ctl_default_nan_double.tme_value64_uint32_hi = 0x7fffffff;
|
|
ctl->tme_ieee754_ctl_default_nan_double.tme_value64_uint32_lo = 0xffffffff;
|
|
ctl->tme_ieee754_ctl_default_nan_extended80.tme_float_ieee754_extended80_sexp = 0x7fff;
|
|
ctl->tme_ieee754_ctl_default_nan_extended80.tme_float_ieee754_extended80_significand.tme_value64_uint32_hi = 0xffffffff;
|
|
ctl->tme_ieee754_ctl_default_nan_extended80.tme_float_ieee754_extended80_significand.tme_value64_uint32_lo = 0xffffffff;
|
|
|
|
/* NaN tests: */
|
|
ctl->tme_ieee754_ctl_is_snan_extended80 = _tme_m6888x_is_snan_extended80;
|
|
|
|
/* NaN canonicalization: */
|
|
ctl->tme_ieee754_ctl_nan_single_to_common = tme_ieee754_default_nan_single_to_common;
|
|
ctl->tme_ieee754_ctl_nan_common_to_single = tme_ieee754_default_nan_common_to_single;
|
|
ctl->tme_ieee754_ctl_nan_double_to_common = tme_ieee754_default_nan_double_to_common;
|
|
ctl->tme_ieee754_ctl_nan_common_to_double = tme_ieee754_default_nan_common_to_double;
|
|
ctl->tme_ieee754_ctl_nan_extended80_to_common = tme_ieee754_default_nan_extended80_to_common;
|
|
ctl->tme_ieee754_ctl_nan_common_to_extended80 = tme_ieee754_default_nan_common_to_extended80;
|
|
|
|
/* NaN propagation: */
|
|
ctl->tme_ieee754_ctl_nan_from_nans_extended80 = _tme_m6888x_nan_from_nans_extended80;
|
|
|
|
/* done: */
|
|
*_arg_i = arg_i;
|
|
return (TRUE);
|
|
}
|
|
|
|
/* this returns the FPU usage: */
|
|
void
|
|
tme_m68k_fpu_usage(char **_output)
|
|
{
|
|
tme_output_append_error(_output,
|
|
"[ fpu-type { m68881 | m68882 | m68040 } fpu-compliance %s [ fpu-incomplete { abort | line-f } ] ]",
|
|
tme_ieee754_compliance_options);
|
|
}
|