提交 5a5012ec 编写于 作者: T ths

MIPS 64-bit FPU support, plus some collateral bugfixes in the

conditional branch handling.


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2779 c046a42c-6fe2-441c-8c8c-71466251a162
上级 8b4af705
......@@ -4,6 +4,7 @@
- ds1225y nvram support (Herve Poussineau)
- CPU model selection support (J. Mayer, Paul Brook, Herve Poussineau)
- Several Sparc fixes (Aurelien Jarno, Blue Swirl)
- MIPS 64-bit FPU support (Thiemo Seufer)
version 0.9.0:
......
......@@ -575,7 +575,7 @@ static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
{
for (i = 0; i < 32; i++)
{
*(uint32_t *)ptr = tswapl(FPR_W (env, i));
*(uint32_t *)ptr = tswapl(env->fpr[i].fs[FP_ENDIAN_IDX]);
ptr += 4;
}
......@@ -637,7 +637,7 @@ static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
{
for (i = 0; i < 32; i++)
{
FPR_W (env, i) = tswapl(*(uint32_t *)ptr);
env->fpr[i].fs[FP_ENDIAN_IDX] = tswapl(*(uint32_t *)ptr);
ptr += 4;
}
......
......@@ -10,11 +10,12 @@ General
when the Qemu FPU emulation is disabled. Also gdb inside the emulated
system does not work. Both problems are caused by insufficient
handling of self-modifying code.
- Floating point exception emulation is incomplete.
MIPS64
------
- No 64bit TLB support
- no 64bit wide registers for FPU
- 64bit FPU not fully implemented
- 64bit mul/div handling broken
"Generic" 4Kc system emulation
......
......@@ -21,7 +21,7 @@ typedef union fpr_t fpr_t;
union fpr_t {
float64 fd; /* ieee double precision */
float32 fs[2];/* ieee single precision */
uint64_t d; /* binary single fixed-point */
uint64_t d; /* binary double fixed-point */
uint32_t w[2]; /* binary single fixed-point */
};
/* define FP_ENDIAN_IDX to access the same location
......@@ -64,31 +64,35 @@ struct CPUMIPSState {
target_ulong HI, LO;
/* Floating point registers */
fpr_t fpr[32];
#define FPR(cpu, n) ((fpr_t*)&(cpu)->fpr[(n) / 2])
#define FPR_FD(cpu, n) (FPR(cpu, n)->fd)
#define FPR_FS(cpu, n) (FPR(cpu, n)->fs[((n) & 1) ^ FP_ENDIAN_IDX])
#define FPR_D(cpu, n) (FPR(cpu, n)->d)
#define FPR_W(cpu, n) (FPR(cpu, n)->w[((n) & 1) ^ FP_ENDIAN_IDX])
#ifndef USE_HOST_FLOAT_REGS
fpr_t ft0;
fpr_t ft1;
fpr_t ft2;
#endif
float_status fp_status;
/* fpu implementation/revision register */
/* fpu implementation/revision register (fir) */
uint32_t fcr0;
#define FCR0_F64 22
#define FCR0_L 21
#define FCR0_W 20
#define FCR0_3D 19
#define FCR0_PS 18
#define FCR0_D 17
#define FCR0_S 16
#define FCR0_PRID 8
#define FCR0_REV 0
/* fcsr */
uint32_t fcr31;
#define SET_FP_COND(reg) do { (reg) |= (1<<23); } while(0)
#define CLEAR_FP_COND(reg) do { (reg) &= ~(1<<23); } while(0)
#define IS_FP_COND_SET(reg) (((reg) & (1<<23)) != 0)
#define GET_FP_CAUSE(reg) (((reg) >> 12) & 0x3f)
#define GET_FP_ENABLE(reg) (((reg) >> 7) & 0x1f)
#define GET_FP_FLAGS(reg) (((reg) >> 2) & 0x1f)
#define SET_FP_CAUSE(reg,v) do { (reg) = ((reg) & ~(0x3f << 12)) | ((v) << 12); } while(0)
#define SET_FP_ENABLE(reg,v) do { (reg) = ((reg) & ~(0x1f << 7)) | ((v) << 7); } while(0)
#define SET_FP_FLAGS(reg,v) do { (reg) = ((reg) & ~(0x1f << 2)) | ((v) << 2); } while(0)
#define SET_FP_COND(num,env) do { (env->fcr31) |= ((num) ? (1 << ((num) + 24)) : (1 << ((num) + 23))); } while(0)
#define CLEAR_FP_COND(num,env) do { (env->fcr31) &= ~((num) ? (1 << ((num) + 24)) : (1 << ((num) + 23))); } while(0)
#define IS_FP_COND_SET(num,env) (((env->fcr31) & ((num) ? (1 << ((num) + 24)) : (1 << ((num) + 23)))) != 0)
#define GET_FP_CAUSE(reg) (((reg) >> 12) & 0x3f)
#define GET_FP_ENABLE(reg) (((reg) >> 7) & 0x1f)
#define GET_FP_FLAGS(reg) (((reg) >> 2) & 0x1f)
#define SET_FP_CAUSE(reg,v) do { (reg) = ((reg) & ~(0x3f << 12)) | ((v & 0x3f) << 12); } while(0)
#define SET_FP_ENABLE(reg,v) do { (reg) = ((reg) & ~(0x1f << 7)) | ((v & 0x1f) << 7); } while(0)
#define SET_FP_FLAGS(reg,v) do { (reg) = ((reg) & ~(0x1f << 2)) | ((v & 0x1f) << 2); } while(0)
#define UPDATE_FP_FLAGS(reg,v) do { (reg) |= ((v & 0x1f) << 2); } while(0)
#define FP_INEXACT 1
#define FP_UNDERFLOW 2
#define FP_OVERFLOW 4
......@@ -267,6 +271,7 @@ struct CPUMIPSState {
int SYNCI_Step; /* Address step size for SYNCI */
int CCRes; /* Cycle count resolution/divisor */
int Status_rw_bitmask; /* Read/write bits in CP0_Status */
#if defined(CONFIG_USER_ONLY)
target_ulong tls_value;
......@@ -330,10 +335,11 @@ enum {
EXCP_RI,
EXCP_OVERFLOW,
EXCP_TRAP,
EXCP_FPE,
EXCP_DDBS,
EXCP_DWATCH,
EXCP_LAE,
EXCP_SAE, /* 24 */
EXCP_LAE, /* 24 */
EXCP_SAE,
EXCP_LTLBL,
EXCP_TLBL,
EXCP_TLBS,
......
......@@ -29,12 +29,18 @@ register target_ulong T2 asm(AREG3);
#define FST0 (env->ft0.fs[FP_ENDIAN_IDX])
#define FST1 (env->ft1.fs[FP_ENDIAN_IDX])
#define FST2 (env->ft2.fs[FP_ENDIAN_IDX])
#define FSTH0 (env->ft0.fs[!FP_ENDIAN_IDX])
#define FSTH1 (env->ft1.fs[!FP_ENDIAN_IDX])
#define FSTH2 (env->ft2.fs[!FP_ENDIAN_IDX])
#define DT0 (env->ft0.d)
#define DT1 (env->ft1.d)
#define DT2 (env->ft2.d)
#define WT0 (env->ft0.w[FP_ENDIAN_IDX])
#define WT1 (env->ft1.w[FP_ENDIAN_IDX])
#define WT2 (env->ft2.w[FP_ENDIAN_IDX])
#define WTH0 (env->ft0.w[!FP_ENDIAN_IDX])
#define WTH1 (env->ft1.w[!FP_ENDIAN_IDX])
#define WTH2 (env->ft2.w[!FP_ENDIAN_IDX])
#endif
#if defined (DEBUG_OP)
......
......@@ -19,75 +19,103 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#if defined(SFREG)
#if defined(FREG)
#define OP_WLOAD_FREG(treg, tregname, SFREG) \
void glue(glue(op_load_fpr_,tregname), SFREG) (void) \
{ \
treg = FPR_W(env, SFREG); \
RETURN(); \
#define OP_WLOAD_FREG(treg, tregname, FREG) \
void glue(glue(op_load_fpr_,tregname), FREG) (void) \
{ \
treg = env->fpr[FREG].fs[FP_ENDIAN_IDX]; \
RETURN(); \
}
#define OP_WSTORE_FREG(treg, tregname, SFREG) \
void glue(glue(op_store_fpr_,tregname), SFREG) (void)\
{ \
FPR_W(env, SFREG) = treg; \
RETURN(); \
#define OP_WSTORE_FREG(treg, tregname, FREG) \
void glue(glue(op_store_fpr_,tregname), FREG) (void) \
{ \
env->fpr[FREG].fs[FP_ENDIAN_IDX] = treg; \
RETURN(); \
}
/* WT0 = SFREG.w: op_load_fpr_WT0_fprSFREG */
OP_WLOAD_FREG(WT0, WT0_fpr, SFREG)
/* SFREG.w = WT0: op_store_fpr_WT0_fprSFREG */
OP_WSTORE_FREG(WT0, WT0_fpr, SFREG)
/* WT0 = FREG.w: op_load_fpr_WT0_fprFREG */
OP_WLOAD_FREG(WT0, WT0_fpr, FREG)
/* FREG.w = WT0: op_store_fpr_WT0_fprFREG */
OP_WSTORE_FREG(WT0, WT0_fpr, FREG)
OP_WLOAD_FREG(WT1, WT1_fpr, FREG)
OP_WSTORE_FREG(WT1, WT1_fpr, FREG)
OP_WLOAD_FREG(WT2, WT2_fpr, FREG)
OP_WSTORE_FREG(WT2, WT2_fpr, FREG)
#define OP_DLOAD_FREG(treg, tregname, FREG) \
void glue(glue(op_load_fpr_,tregname), FREG) (void) \
{ \
if (env->CP0_Status & (1 << CP0St_FR)) \
treg = env->fpr[FREG].fd; \
else \
treg = (uint64_t)(env->fpr[FREG | 1].fs[FP_ENDIAN_IDX]) << 32 | \
env->fpr[FREG & ~1].fs[FP_ENDIAN_IDX]; \
RETURN(); \
}
OP_WLOAD_FREG(WT1, WT1_fpr, SFREG)
OP_WSTORE_FREG(WT1, WT1_fpr, SFREG)
#define OP_DSTORE_FREG(treg, tregname, FREG) \
void glue(glue(op_store_fpr_,tregname), FREG) (void) \
{ \
if (env->CP0_Status & (1 << CP0St_FR)) \
env->fpr[FREG].fd = treg; \
else { \
env->fpr[FREG | 1].fs[FP_ENDIAN_IDX] = treg >> 32; \
env->fpr[FREG & ~1].fs[FP_ENDIAN_IDX] = treg; \
} \
RETURN(); \
}
OP_WLOAD_FREG(WT2, WT2_fpr, SFREG)
OP_WSTORE_FREG(WT2, WT2_fpr, SFREG)
OP_DLOAD_FREG(DT0, DT0_fpr, FREG)
OP_DSTORE_FREG(DT0, DT0_fpr, FREG)
#endif
OP_DLOAD_FREG(DT1, DT1_fpr, FREG)
OP_DSTORE_FREG(DT1, DT1_fpr, FREG)
#if defined(DFREG)
OP_DLOAD_FREG(DT2, DT2_fpr, FREG)
OP_DSTORE_FREG(DT2, DT2_fpr, FREG)
#define OP_DLOAD_FREG(treg, tregname, DFREG) \
void glue(glue(op_load_fpr_,tregname), DFREG) (void) \
{ \
treg = FPR_D(env, DFREG); \
RETURN(); \
#define OP_PSLOAD_FREG(treg, tregname, FREG) \
void glue(glue(op_load_fpr_,tregname), FREG) (void) \
{ \
treg = env->fpr[FREG].fs[!FP_ENDIAN_IDX]; \
RETURN(); \
}
#define OP_DSTORE_FREG(treg, tregname, DFREG) \
void glue(glue(op_store_fpr_,tregname), DFREG) (void)\
{ \
FPR_D(env, DFREG) = treg; \
RETURN(); \
#define OP_PSSTORE_FREG(treg, tregname, FREG) \
void glue(glue(op_store_fpr_,tregname), FREG) (void) \
{ \
env->fpr[FREG].fs[!FP_ENDIAN_IDX] = treg; \
RETURN(); \
}
OP_DLOAD_FREG(DT0, DT0_fpr, DFREG)
OP_DSTORE_FREG(DT0, DT0_fpr, DFREG)
OP_PSLOAD_FREG(WTH0, WTH0_fpr, FREG)
OP_PSSTORE_FREG(WTH0, WTH0_fpr, FREG)
OP_DLOAD_FREG(DT1, DT1_fpr, DFREG)
OP_DSTORE_FREG(DT1, DT1_fpr, DFREG)
OP_PSLOAD_FREG(WTH1, WTH1_fpr, FREG)
OP_PSSTORE_FREG(WTH1, WTH1_fpr, FREG)
OP_DLOAD_FREG(DT2, DT2_fpr, DFREG)
OP_DSTORE_FREG(DT2, DT2_fpr, DFREG)
OP_PSLOAD_FREG(WTH2, WTH2_fpr, FREG)
OP_PSSTORE_FREG(WTH2, WTH2_fpr, FREG)
#endif
#if defined (FTN)
#define SET_RESET(treg, tregname) \
#define SET_RESET(treg, tregname) \
void glue(op_set, tregname)(void) \
{ \
treg = PARAM1; \
RETURN(); \
} \
{ \
treg = PARAM1; \
RETURN(); \
} \
void glue(op_reset, tregname)(void) \
{ \
treg = 0; \
RETURN(); \
} \
{ \
treg = 0; \
RETURN(); \
}
SET_RESET(WT0, _WT0)
SET_RESET(WT1, _WT1)
......@@ -95,6 +123,9 @@ SET_RESET(WT2, _WT2)
SET_RESET(DT0, _DT0)
SET_RESET(DT1, _DT1)
SET_RESET(DT2, _DT2)
SET_RESET(WTH0, _WTH0)
SET_RESET(WTH1, _WTH1)
SET_RESET(WTH2, _WTH2)
#undef SET_RESET
#endif
......@@ -379,6 +379,9 @@ void do_interrupt (CPUState *env)
case EXCP_TRAP:
cause = 13;
goto set_EPC;
case EXCP_FPE:
cause = 15;
goto set_EPC;
case EXCP_LTLBL:
cause = 1;
goto set_EPC;
......
......@@ -23,27 +23,27 @@
#include "exec.h"
#ifndef CALL_FROM_TB0
#define CALL_FROM_TB0(func) func();
#define CALL_FROM_TB0(func) func()
#endif
#ifndef CALL_FROM_TB1
#define CALL_FROM_TB1(func, arg0) func(arg0);
#define CALL_FROM_TB1(func, arg0) func(arg0)
#endif
#ifndef CALL_FROM_TB1_CONST16
#define CALL_FROM_TB1_CONST16(func, arg0) CALL_FROM_TB1(func, arg0);
#define CALL_FROM_TB1_CONST16(func, arg0) CALL_FROM_TB1(func, arg0)
#endif
#ifndef CALL_FROM_TB2
#define CALL_FROM_TB2(func, arg0, arg1) func(arg0, arg1);
#define CALL_FROM_TB2(func, arg0, arg1) func(arg0, arg1)
#endif
#ifndef CALL_FROM_TB2_CONST16
#define CALL_FROM_TB2_CONST16(func, arg0, arg1) \
CALL_FROM_TB2(func, arg0, arg1);
CALL_FROM_TB2(func, arg0, arg1)
#endif
#ifndef CALL_FROM_TB3
#define CALL_FROM_TB3(func, arg0, arg1, arg2) func(arg0, arg1, arg2);
#define CALL_FROM_TB3(func, arg0, arg1, arg2) func(arg0, arg1, arg2)
#endif
#ifndef CALL_FROM_TB4
#define CALL_FROM_TB4(func, arg0, arg1, arg2, arg3) \
func(arg0, arg1, arg2, arg3);
func(arg0, arg1, arg2, arg3)
#endif
#define REG 1
......@@ -144,134 +144,102 @@ CALL_FROM_TB2(func, arg0, arg1);
#include "op_template.c"
#undef TN
#define SFREG 0
#define DFREG 0
#define FREG 0
#include "fop_template.c"
#undef SFREG
#undef DFREG
#define SFREG 1
#undef FREG
#define FREG 1
#include "fop_template.c"
#undef SFREG
#define SFREG 2
#define DFREG 2
#undef FREG
#define FREG 2
#include "fop_template.c"
#undef SFREG
#undef DFREG
#define SFREG 3
#undef FREG
#define FREG 3
#include "fop_template.c"
#undef SFREG
#define SFREG 4
#define DFREG 4
#undef FREG
#define FREG 4
#include "fop_template.c"
#undef SFREG
#undef DFREG
#define SFREG 5
#undef FREG
#define FREG 5
#include "fop_template.c"
#undef SFREG
#define SFREG 6
#define DFREG 6
#undef FREG
#define FREG 6
#include "fop_template.c"
#undef SFREG
#undef DFREG
#define SFREG 7
#undef FREG
#define FREG 7
#include "fop_template.c"
#undef SFREG
#define SFREG 8
#define DFREG 8
#undef FREG
#define FREG 8
#include "fop_template.c"
#undef SFREG
#undef DFREG
#define SFREG 9
#undef FREG
#define FREG 9
#include "fop_template.c"
#undef SFREG
#define SFREG 10
#define DFREG 10
#undef FREG
#define FREG 10
#include "fop_template.c"
#undef SFREG
#undef DFREG
#define SFREG 11
#undef FREG
#define FREG 11
#include "fop_template.c"
#undef SFREG
#define SFREG 12
#define DFREG 12
#undef FREG
#define FREG 12
#include "fop_template.c"
#undef SFREG
#undef DFREG
#define SFREG 13
#undef FREG
#define FREG 13
#include "fop_template.c"
#undef SFREG
#define SFREG 14
#define DFREG 14
#undef FREG
#define FREG 14
#include "fop_template.c"
#undef SFREG
#undef DFREG
#define SFREG 15
#undef FREG
#define FREG 15
#include "fop_template.c"
#undef SFREG
#define SFREG 16
#define DFREG 16
#undef FREG
#define FREG 16
#include "fop_template.c"
#undef SFREG
#undef DFREG
#define SFREG 17
#undef FREG
#define FREG 17
#include "fop_template.c"
#undef SFREG
#define SFREG 18
#define DFREG 18
#undef FREG
#define FREG 18
#include "fop_template.c"
#undef SFREG
#undef DFREG
#define SFREG 19
#undef FREG
#define FREG 19
#include "fop_template.c"
#undef SFREG
#define SFREG 20
#define DFREG 20
#undef FREG
#define FREG 20
#include "fop_template.c"
#undef SFREG
#undef DFREG
#define SFREG 21
#undef FREG
#define FREG 21
#include "fop_template.c"
#undef SFREG
#define SFREG 22
#define DFREG 22
#undef FREG
#define FREG 22
#include "fop_template.c"
#undef SFREG
#undef DFREG
#define SFREG 23
#undef FREG
#define FREG 23
#include "fop_template.c"
#undef SFREG
#define SFREG 24
#define DFREG 24
#undef FREG
#define FREG 24
#include "fop_template.c"
#undef SFREG
#undef DFREG
#define SFREG 25
#undef FREG
#define FREG 25
#include "fop_template.c"
#undef SFREG
#define SFREG 26
#define DFREG 26
#undef FREG
#define FREG 26
#include "fop_template.c"
#undef SFREG
#undef DFREG
#define SFREG 27
#undef FREG
#define FREG 27
#include "fop_template.c"
#undef SFREG
#define SFREG 28
#define DFREG 28
#undef FREG
#define FREG 28
#include "fop_template.c"
#undef SFREG
#undef DFREG
#define SFREG 29
#undef FREG
#define FREG 29
#include "fop_template.c"
#undef SFREG
#define SFREG 30
#define DFREG 30
#undef FREG
#define FREG 30
#include "fop_template.c"
#undef SFREG
#undef DFREG
#define SFREG 31
#undef FREG
#define FREG 31
#include "fop_template.c"
#undef SFREG
#undef FREG
#define FTN
#include "fop_template.c"
......@@ -919,14 +887,14 @@ void op_movz (void)
void op_movf (void)
{
if (!(env->fcr31 & PARAM1))
env->gpr[PARAM2] = env->gpr[PARAM3];
T0 = T1;
RETURN();
}
void op_movt (void)
{
if (env->fcr31 & PARAM1)
env->gpr[PARAM2] = env->gpr[PARAM3];
T0 = T1;
RETURN();
}
......@@ -1354,17 +1322,18 @@ void op_mtc0_compare (void)
void op_mtc0_status (void)
{
uint32_t val, old;
uint32_t mask = env->Status_rw_bitmask;
/* No 64bit FPU, no reverse endianness, no MDMX/DSP, no 64bit ops,
/* No reverse endianness, no MDMX/DSP, no 64bit ops,
no 64bit addressing implemented. */
val = (int32_t)T0 & 0xF878FF17;
val = (int32_t)T0 & mask;
old = env->CP0_Status;
if (!(val & (1 << CP0St_EXL)) &&
!(val & (1 << CP0St_ERL)) &&
!(env->hflags & MIPS_HFLAG_DM) &&
(val & (1 << CP0St_UM)))
env->hflags |= MIPS_HFLAG_UM;
env->CP0_Status = (env->CP0_Status & ~0xF878FF17) | val;
env->CP0_Status = (env->CP0_Status & ~mask) | val;
if (loglevel & CPU_LOG_EXEC)
CALL_FROM_TB2(do_mtc0_status_debug, old, val);
CALL_FROM_TB1(cpu_mips_update_irq, env);
......@@ -1643,6 +1612,7 @@ void op_dmtc0_errorepc (void)
}
#endif /* TARGET_MIPS64 */
/* CP1 functions */
#if 0
# define DEBUG_FPU_STATE() CALL_FROM_TB1(dump_fpu, env)
#else
......@@ -1666,20 +1636,6 @@ void op_cp1_enabled(void)
RETURN();
}
/* CP1 functions */
void op_cfc1 (void)
{
if (T1 == 0) {
T0 = env->fcr0;
}
else {
/* fetch fcr31, masking unused bits */
T0 = env->fcr31 & 0x0183FFFF;
}
DEBUG_FPU_STATE();
RETURN();
}
/* convert MIPS rounding mode in FCR31 to IEEE library */
unsigned int ieee_rm[] = {
float_round_nearest_even,
......@@ -1691,26 +1647,93 @@ unsigned int ieee_rm[] = {
#define RESTORE_ROUNDING_MODE \
set_float_rounding_mode(ieee_rm[env->fcr31 & 3], &env->fp_status)
void op_ctc1 (void)
inline char ieee_ex_to_mips(char ieee)
{
if (T1 == 0) {
/* XXX should this throw an exception?
* don't write to FCR0.
* env->fcr0 = T0;
*/
}
else {
/* store new fcr31, masking unused bits */
env->fcr31 = T0 & 0x0183FFFF;
return (ieee & float_flag_inexact) >> 5 |
(ieee & float_flag_underflow) >> 3 |
(ieee & float_flag_overflow) >> 1 |
(ieee & float_flag_divbyzero) << 1 |
(ieee & float_flag_invalid) << 4;
}
/* set rounding mode */
RESTORE_ROUNDING_MODE;
inline char mips_ex_to_ieee(char mips)
{
return (mips & FP_INEXACT) << 5 |
(mips & FP_UNDERFLOW) << 3 |
(mips & FP_OVERFLOW) << 1 |
(mips & FP_DIV0) >> 1 |
(mips & FP_INVALID) >> 4;
}
#ifndef CONFIG_SOFTFLOAT
/* no floating point exception for native float */
SET_FP_ENABLE(env->fcr31, 0);
#endif
inline void update_fcr31(void)
{
int tmp = ieee_ex_to_mips(get_float_exception_flags(&env->fp_status));
SET_FP_CAUSE(env->fcr31, tmp);
if (GET_FP_ENABLE(env->fcr31) & tmp)
CALL_FROM_TB1(do_raise_exception, EXCP_FPE);
else
UPDATE_FP_FLAGS(env->fcr31, tmp);
}
void op_cfc1 (void)
{
switch (T1) {
case 0:
T0 = (int32_t)env->fcr0;
break;
case 25:
T0 = ((env->fcr31 >> 24) & 0xfe) | ((env->fcr31 >> 23) & 0x1);
break;
case 26:
T0 = env->fcr31 & 0x0003f07c;
break;
case 28:
T0 = (env->fcr31 & 0x00000f83) | ((env->fcr31 >> 22) & 0x4);
break;
default:
T0 = (int32_t)env->fcr31;
break;
}
DEBUG_FPU_STATE();
RETURN();
}
void op_ctc1 (void)
{
switch(T1) {
case 25:
if (T0 & 0xffffff00)
goto leave;
env->fcr31 = (env->fcr31 & 0x017fffff) | ((T0 & 0xfe) << 24) |
((T0 & 0x1) << 23);
break;
case 26:
if (T0 & 0x007c0000)
goto leave;
env->fcr31 = (env->fcr31 & 0xfffc0f83) | (T0 & 0x0003f07c);
break;
case 28:
if (T0 & 0x007c0000)
goto leave;
env->fcr31 = (env->fcr31 & 0xfefff07c) | (T0 & 0x00000f83) |
((T0 & 0x4) << 22);
break;
case 31:
if (T0 & 0x007c0000)
goto leave;
env->fcr31 = T0;
break;
default:
goto leave;
}
/* set rounding mode */
RESTORE_ROUNDING_MODE;
set_float_exception_flags(0, &env->fp_status);
if ((GET_FP_ENABLE(env->fcr31) | 0x20) & GET_FP_CAUSE(env->fcr31))
CALL_FROM_TB1(do_raise_exception, EXCP_FPE);
leave:
DEBUG_FPU_STATE();
RETURN();
}
......@@ -1729,55 +1752,219 @@ void op_mtc1 (void)
RETURN();
}
void op_dmfc1 (void)
{
T0 = DT0;
DEBUG_FPU_STATE();
RETURN();
}
void op_dmtc1 (void)
{
DT0 = T0;
DEBUG_FPU_STATE();
RETURN();
}
void op_mfhc1 (void)
{
T0 = WTH0;
DEBUG_FPU_STATE();
RETURN();
}
void op_mthc1 (void)
{
WTH0 = T0;
DEBUG_FPU_STATE();
RETURN();
}
/* Float support.
Single precition routines have a "s" suffix, double precision a
"d" suffix. */
"d" suffix, 32bit integer "w", 64bit integer "l", paired singe "ps",
paired single lowwer "pl", paired single upper "pu". */
#define FLOAT_OP(name, p) void OPPROTO op_float_##name##_##p(void)
FLOAT_OP(cvtd, s)
{
set_float_exception_flags(0, &env->fp_status);
FDT2 = float32_to_float64(FST0, &env->fp_status);
update_fcr31();
DEBUG_FPU_STATE();
RETURN();
}
FLOAT_OP(cvtd, w)
{
set_float_exception_flags(0, &env->fp_status);
FDT2 = int32_to_float64(WT0, &env->fp_status);
update_fcr31();
DEBUG_FPU_STATE();
RETURN();
}
FLOAT_OP(cvtd, l)
{
set_float_exception_flags(0, &env->fp_status);
FDT2 = int64_to_float64(DT0, &env->fp_status);
update_fcr31();
DEBUG_FPU_STATE();
RETURN();
}
FLOAT_OP(cvtl, d)
{
set_float_exception_flags(0, &env->fp_status);
DT2 = float64_to_int64(FDT0, &env->fp_status);
update_fcr31();
if (GET_FP_CAUSE(env->fcr31) & (FP_OVERFLOW | FP_INVALID))
DT2 = 0x7fffffffffffffffULL;
DEBUG_FPU_STATE();
RETURN();
}
FLOAT_OP(cvtl, s)
{
set_float_exception_flags(0, &env->fp_status);
DT2 = float32_to_int64(FST0, &env->fp_status);
update_fcr31();
if (GET_FP_CAUSE(env->fcr31) & (FP_OVERFLOW | FP_INVALID))
DT2 = 0x7fffffffffffffffULL;
DEBUG_FPU_STATE();
RETURN();
}
FLOAT_OP(cvtps, s)
{
WT2 = WT0;
WTH2 = WT1;
DEBUG_FPU_STATE();
RETURN();
}
FLOAT_OP(cvtps, pw)
{
set_float_exception_flags(0, &env->fp_status);
FST2 = int32_to_float32(WT0, &env->fp_status);
FSTH2 = int32_to_float32(WTH0, &env->fp_status);
update_fcr31();
DEBUG_FPU_STATE();
RETURN();
}
FLOAT_OP(cvtpw, ps)
{
set_float_exception_flags(0, &env->fp_status);
WT2 = float32_to_int32(FST0, &env->fp_status);
WTH2 = float32_to_int32(FSTH0, &env->fp_status);
update_fcr31();
if (GET_FP_CAUSE(env->fcr31) & (FP_OVERFLOW | FP_INVALID))
WT2 = 0x7fffffff;
DEBUG_FPU_STATE();
RETURN();
}
FLOAT_OP(cvts, d)
{
set_float_exception_flags(0, &env->fp_status);
FST2 = float64_to_float32(FDT0, &env->fp_status);
update_fcr31();
DEBUG_FPU_STATE();
RETURN();
}
FLOAT_OP(cvts, w)
{
set_float_exception_flags(0, &env->fp_status);
FST2 = int32_to_float32(WT0, &env->fp_status);
update_fcr31();
DEBUG_FPU_STATE();
RETURN();
}
FLOAT_OP(cvts, l)
{
set_float_exception_flags(0, &env->fp_status);
FST2 = int64_to_float32(DT0, &env->fp_status);
update_fcr31();
DEBUG_FPU_STATE();
RETURN();
}
FLOAT_OP(cvts, pl)
{
set_float_exception_flags(0, &env->fp_status);
WT2 = WT0;
update_fcr31();
DEBUG_FPU_STATE();
RETURN();
}
FLOAT_OP(cvts, pu)
{
set_float_exception_flags(0, &env->fp_status);
WT2 = WTH0;
update_fcr31();
DEBUG_FPU_STATE();
RETURN();
}
FLOAT_OP(cvtw, s)
{
set_float_exception_flags(0, &env->fp_status);
WT2 = float32_to_int32(FST0, &env->fp_status);
update_fcr31();
if (GET_FP_CAUSE(env->fcr31) & (FP_OVERFLOW | FP_INVALID))
WT2 = 0x7fffffff;
DEBUG_FPU_STATE();
RETURN();
}
FLOAT_OP(cvtw, d)
{
set_float_exception_flags(0, &env->fp_status);
WT2 = float64_to_int32(FDT0, &env->fp_status);
update_fcr31();
if (GET_FP_CAUSE(env->fcr31) & (FP_OVERFLOW | FP_INVALID))
WT2 = 0x7fffffff;
DEBUG_FPU_STATE();
RETURN();
}
FLOAT_OP(pll, ps)
{
DT2 = ((uint64_t)WT0 << 32) | WT1;
DEBUG_FPU_STATE();
RETURN();
}
FLOAT_OP(plu, ps)
{
DT2 = ((uint64_t)WT0 << 32) | WTH1;
DEBUG_FPU_STATE();
RETURN();
}
FLOAT_OP(pul, ps)
{
DT2 = ((uint64_t)WTH0 << 32) | WT1;
DEBUG_FPU_STATE();
RETURN();
}
FLOAT_OP(puu, ps)
{
DT2 = ((uint64_t)WTH0 << 32) | WTH1;
DEBUG_FPU_STATE();
RETURN();
}
FLOAT_OP(roundl, d)
{
set_float_rounding_mode(float_round_nearest_even, &env->fp_status);
DT2 = float64_round_to_int(FDT0, &env->fp_status);
RESTORE_ROUNDING_MODE;
DEBUG_FPU_STATE();
RETURN();
}
FLOAT_OP(roundl, s)
{
set_float_rounding_mode(float_round_nearest_even, &env->fp_status);
DT2 = float32_round_to_int(FST0, &env->fp_status);
RESTORE_ROUNDING_MODE;
DEBUG_FPU_STATE();
RETURN();
}
FLOAT_OP(roundw, d)
{
set_float_rounding_mode(float_round_nearest_even, &env->fp_status);
WT2 = float64_round_to_int(FDT0, &env->fp_status);
RESTORE_ROUNDING_MODE;
DEBUG_FPU_STATE();
RETURN();
}
......@@ -1790,6 +1977,18 @@ FLOAT_OP(roundw, s)
RETURN();
}
FLOAT_OP(truncl, d)
{
DT2 = float64_to_int64_round_to_zero(FDT0, &env->fp_status);
DEBUG_FPU_STATE();
RETURN();
}
FLOAT_OP(truncl, s)
{
DT2 = float32_to_int64_round_to_zero(FST0, &env->fp_status);
DEBUG_FPU_STATE();
RETURN();
}
FLOAT_OP(truncw, d)
{
WT2 = float64_to_int32_round_to_zero(FDT0, &env->fp_status);
......@@ -1803,12 +2002,27 @@ FLOAT_OP(truncw, s)
RETURN();
}
FLOAT_OP(ceill, d)
{
set_float_rounding_mode(float_round_up, &env->fp_status);
DT2 = float64_round_to_int(FDT0, &env->fp_status);
RESTORE_ROUNDING_MODE;
DEBUG_FPU_STATE();
RETURN();
}
FLOAT_OP(ceill, s)
{
set_float_rounding_mode(float_round_up, &env->fp_status);
DT2 = float32_round_to_int(FST0, &env->fp_status);
RESTORE_ROUNDING_MODE;
DEBUG_FPU_STATE();
RETURN();
}
FLOAT_OP(ceilw, d)
{
set_float_rounding_mode(float_round_up, &env->fp_status);
WT2 = float64_round_to_int(FDT0, &env->fp_status);
RESTORE_ROUNDING_MODE;
DEBUG_FPU_STATE();
RETURN();
}
......@@ -1821,12 +2035,27 @@ FLOAT_OP(ceilw, s)
RETURN();
}
FLOAT_OP(floorl, d)
{
set_float_rounding_mode(float_round_down, &env->fp_status);
DT2 = float64_round_to_int(FDT0, &env->fp_status);
RESTORE_ROUNDING_MODE;
DEBUG_FPU_STATE();
RETURN();
}
FLOAT_OP(floorl, s)
{
set_float_rounding_mode(float_round_down, &env->fp_status);
DT2 = float32_round_to_int(FST0, &env->fp_status);
RESTORE_ROUNDING_MODE;
DEBUG_FPU_STATE();
RETURN();
}
FLOAT_OP(floorw, d)
{
set_float_rounding_mode(float_round_down, &env->fp_status);
WT2 = float64_round_to_int(FDT0, &env->fp_status);
RESTORE_ROUNDING_MODE;
DEBUG_FPU_STATE();
RETURN();
}
......@@ -1839,16 +2068,121 @@ FLOAT_OP(floorw, s)
RETURN();
}
FLOAT_OP(movf, d)
{
if (!(env->fcr31 & PARAM1))
DT2 = DT0;
DEBUG_FPU_STATE();
RETURN();
}
FLOAT_OP(movf, s)
{
if (!(env->fcr31 & PARAM1))
WT2 = WT0;
DEBUG_FPU_STATE();
RETURN();
}
FLOAT_OP(movf, ps)
{
if (!(env->fcr31 & PARAM1)) {
WT2 = WT0;
WTH2 = WTH0;
}
DEBUG_FPU_STATE();
RETURN();
}
FLOAT_OP(movt, d)
{
if (env->fcr31 & PARAM1)
DT2 = DT0;
DEBUG_FPU_STATE();
RETURN();
}
FLOAT_OP(movt, s)
{
if (env->fcr31 & PARAM1)
WT2 = WT0;
DEBUG_FPU_STATE();
RETURN();
}
FLOAT_OP(movt, ps)
{
if (env->fcr31 & PARAM1) {
WT2 = WT0;
WTH2 = WTH0;
}
DEBUG_FPU_STATE();
RETURN();
}
FLOAT_OP(movz, d)
{
if (!T0)
DT2 = DT0;
DEBUG_FPU_STATE();
RETURN();
}
FLOAT_OP(movz, s)
{
if (!T0)
WT2 = WT0;
DEBUG_FPU_STATE();
RETURN();
}
FLOAT_OP(movz, ps)
{
if (!T0) {
WT2 = WT0;
WTH2 = WTH0;
}
DEBUG_FPU_STATE();
RETURN();
}
FLOAT_OP(movn, d)
{
if (T0)
DT2 = DT0;
DEBUG_FPU_STATE();
RETURN();
}
FLOAT_OP(movn, s)
{
if (T0)
WT2 = WT0;
DEBUG_FPU_STATE();
RETURN();
}
FLOAT_OP(movn, ps)
{
if (T0) {
WT2 = WT0;
WTH2 = WTH0;
}
DEBUG_FPU_STATE();
RETURN();
}
/* binary operations */
#define FLOAT_BINOP(name) \
FLOAT_OP(name, d) \
{ \
set_float_exception_flags(0, &env->fp_status); \
FDT2 = float64_ ## name (FDT0, FDT1, &env->fp_status); \
update_fcr31(); \
DEBUG_FPU_STATE(); \
} \
FLOAT_OP(name, s) \
{ \
set_float_exception_flags(0, &env->fp_status); \
FST2 = float32_ ## name (FST0, FST1, &env->fp_status); \
update_fcr31(); \
DEBUG_FPU_STATE(); \
} \
FLOAT_OP(name, ps) \
{ \
set_float_exception_flags(0, &env->fp_status); \
FST2 = float32_ ## name (FST0, FST1, &env->fp_status); \
FSTH2 = float32_ ## name (FSTH0, FSTH1, &env->fp_status); \
update_fcr31(); \
DEBUG_FPU_STATE(); \
}
FLOAT_BINOP(add)
......@@ -1857,6 +2191,32 @@ FLOAT_BINOP(mul)
FLOAT_BINOP(div)
#undef FLOAT_BINOP
/* ternary operations */
#define FLOAT_TERNOP(name1, name2) \
FLOAT_OP(name1 ## name2, d) \
{ \
FDT0 = float64_ ## name1 (FDT0, FDT1, &env->fp_status); \
FDT2 = float64_ ## name2 (FDT0, FDT2, &env->fp_status); \
DEBUG_FPU_STATE(); \
} \
FLOAT_OP(name1 ## name2, s) \
{ \
FST0 = float32_ ## name1 (FST0, FST1, &env->fp_status); \
FST2 = float32_ ## name2 (FST0, FST2, &env->fp_status); \
DEBUG_FPU_STATE(); \
} \
FLOAT_OP(name1 ## name2, ps) \
{ \
FST0 = float32_ ## name1 (FST0, FST1, &env->fp_status); \
FSTH0 = float32_ ## name1 (FSTH0, FSTH1, &env->fp_status); \
FST2 = float32_ ## name2 (FST0, FST2, &env->fp_status); \
FSTH2 = float32_ ## name2 (FSTH0, FSTH2, &env->fp_status); \
DEBUG_FPU_STATE(); \
}
FLOAT_TERNOP(mul, add)
FLOAT_TERNOP(mul, sub)
#undef FLOAT_TERNOP
/* unary operations, modifying fp status */
#define FLOAT_UNOP(name) \
FLOAT_OP(name, d) \
......@@ -1868,6 +2228,12 @@ FLOAT_OP(name, s) \
{ \
FST2 = float32_ ## name(FST0, &env->fp_status); \
DEBUG_FPU_STATE(); \
} \
FLOAT_OP(name, ps) \
{ \
FST2 = float32_ ## name(FST0, &env->fp_status); \
FSTH2 = float32_ ## name(FSTH0, &env->fp_status); \
DEBUG_FPU_STATE(); \
}
FLOAT_UNOP(sqrt)
#undef FLOAT_UNOP
......@@ -1883,6 +2249,12 @@ FLOAT_OP(name, s) \
{ \
FST2 = float32_ ## name(FST0); \
DEBUG_FPU_STATE(); \
} \
FLOAT_OP(name, ps) \
{ \
FST2 = float32_ ## name(FST0); \
FSTH2 = float32_ ## name(FSTH0); \
DEBUG_FPU_STATE(); \
}
FLOAT_UNOP(abs)
FLOAT_UNOP(chs)
......@@ -1900,6 +2272,35 @@ FLOAT_OP(mov, s)
DEBUG_FPU_STATE();
RETURN();
}
FLOAT_OP(mov, ps)
{
FST2 = FST0;
FSTH2 = FSTH0;
DEBUG_FPU_STATE();
RETURN();
}
FLOAT_OP(alnv, ps)
{
switch (T0 & 0x7) {
case 0:
FST2 = FST0;
FSTH2 = FSTH0;
break;
case 4:
#ifdef TARGET_WORDS_BIGENDIAN
FSTH2 = FST0;
FST2 = FSTH1;
#else
FSTH2 = FST1;
FST2 = FSTH0;
#endif
break;
default: /* unpredictable */
break;
}
DEBUG_FPU_STATE();
RETURN();
}
#ifdef CONFIG_SOFTFLOAT
#define clear_invalid() do { \
......@@ -1913,96 +2314,200 @@ FLOAT_OP(mov, s)
extern void dump_fpu_s(CPUState *env);
#define FOP_COND(fmt, op, sig, cond) \
void op_cmp_ ## fmt ## _ ## op (void) \
#define FOP_COND_D(op, cond) \
void op_cmp_d_ ## op (void) \
{ \
if (cond) \
SET_FP_COND(env->fcr31); \
int c = cond; \
update_fcr31(); \
if (c) \
SET_FP_COND(PARAM1, env); \
else \
CLEAR_FP_COND(env->fcr31); \
if (!sig) \
clear_invalid(); \
/*CALL_FROM_TB1(dump_fpu_s, env);*/ \
CLEAR_FP_COND(PARAM1, env); \
DEBUG_FPU_STATE(); \
RETURN(); \
}
int float64_is_unordered(float64 a, float64 b STATUS_PARAM)
int float64_is_unordered(int sig, float64 a, float64 b STATUS_PARAM)
{
if (float64_is_nan(a) || float64_is_nan(b)) {
if (float64_is_signaling_nan(a) ||
float64_is_signaling_nan(b) ||
(sig && (float64_is_nan(a) || float64_is_nan(b)))) {
float_raise(float_flag_invalid, status);
return 1;
}
else {
} else if (float64_is_nan(a) || float64_is_nan(b)) {
return 1;
} else {
return 0;
}
}
FOP_COND(d, f, 0, 0)
FOP_COND(d, un, 0, float64_is_unordered(FDT1, FDT0, &env->fp_status))
FOP_COND(d, eq, 0, float64_eq(FDT0, FDT1, &env->fp_status))
FOP_COND(d, ueq, 0, float64_is_unordered(FDT1, FDT0, &env->fp_status) || float64_eq(FDT0, FDT1, &env->fp_status))
FOP_COND(d, olt, 0, float64_lt(FDT0, FDT1, &env->fp_status))
FOP_COND(d, ult, 0, float64_is_unordered(FDT1, FDT0, &env->fp_status) || float64_lt(FDT0, FDT1, &env->fp_status))
FOP_COND(d, ole, 0, float64_le(FDT0, FDT1, &env->fp_status))
FOP_COND(d, ule, 0, float64_is_unordered(FDT1, FDT0, &env->fp_status) || float64_le(FDT0, FDT1, &env->fp_status))
/* NOTE: the comma operator will make "cond" to eval to false,
* but float*_is_unordered() is still called
*/
FOP_COND(d, sf, 1, (float64_is_unordered(FDT0, FDT1, &env->fp_status), 0))
FOP_COND(d, ngle,1, float64_is_unordered(FDT1, FDT0, &env->fp_status))
FOP_COND(d, seq, 1, float64_eq(FDT0, FDT1, &env->fp_status))
FOP_COND(d, ngl, 1, float64_is_unordered(FDT1, FDT0, &env->fp_status) || float64_eq(FDT0, FDT1, &env->fp_status))
FOP_COND(d, lt, 1, float64_lt(FDT0, FDT1, &env->fp_status))
FOP_COND(d, nge, 1, float64_is_unordered(FDT1, FDT0, &env->fp_status) || float64_lt(FDT0, FDT1, &env->fp_status))
FOP_COND(d, le, 1, float64_le(FDT0, FDT1, &env->fp_status))
FOP_COND(d, ngt, 1, float64_is_unordered(FDT1, FDT0, &env->fp_status) || float64_le(FDT0, FDT1, &env->fp_status))
flag float32_is_unordered(float32 a, float32 b STATUS_PARAM)
{
extern flag float32_is_nan( float32 a );
if (float32_is_nan(a) || float32_is_nan(b)) {
* but float*_is_unordered() is still called. */
FOP_COND_D(f, (float64_is_unordered(0, FDT1, FDT0, &env->fp_status), 0))
FOP_COND_D(un, float64_is_unordered(0, FDT1, FDT0, &env->fp_status))
FOP_COND_D(eq, !float64_is_unordered(0, FDT1, FDT0, &env->fp_status) && float64_eq(FDT0, FDT1, &env->fp_status))
FOP_COND_D(ueq, float64_is_unordered(0, FDT1, FDT0, &env->fp_status) || float64_eq(FDT0, FDT1, &env->fp_status))
FOP_COND_D(olt, !float64_is_unordered(0, FDT1, FDT0, &env->fp_status) && float64_lt(FDT0, FDT1, &env->fp_status))
FOP_COND_D(ult, float64_is_unordered(0, FDT1, FDT0, &env->fp_status) || float64_lt(FDT0, FDT1, &env->fp_status))
FOP_COND_D(ole, !float64_is_unordered(0, FDT1, FDT0, &env->fp_status) && float64_le(FDT0, FDT1, &env->fp_status))
FOP_COND_D(ule, float64_is_unordered(0, FDT1, FDT0, &env->fp_status) || float64_le(FDT0, FDT1, &env->fp_status))
/* NOTE: the comma operator will make "cond" to eval to false,
* but float*_is_unordered() is still called. */
FOP_COND_D(sf, (float64_is_unordered(1, FDT1, FDT0, &env->fp_status), 0))
FOP_COND_D(ngle,float64_is_unordered(1, FDT1, FDT0, &env->fp_status))
FOP_COND_D(seq, !float64_is_unordered(1, FDT1, FDT0, &env->fp_status) && float64_eq(FDT0, FDT1, &env->fp_status))
FOP_COND_D(ngl, float64_is_unordered(1, FDT1, FDT0, &env->fp_status) || float64_eq(FDT0, FDT1, &env->fp_status))
FOP_COND_D(lt, !float64_is_unordered(1, FDT1, FDT0, &env->fp_status) && float64_lt(FDT0, FDT1, &env->fp_status))
FOP_COND_D(nge, float64_is_unordered(1, FDT1, FDT0, &env->fp_status) || float64_lt(FDT0, FDT1, &env->fp_status))
FOP_COND_D(le, !float64_is_unordered(1, FDT1, FDT0, &env->fp_status) && float64_le(FDT0, FDT1, &env->fp_status))
FOP_COND_D(ngt, float64_is_unordered(1, FDT1, FDT0, &env->fp_status) || float64_le(FDT0, FDT1, &env->fp_status))
#define FOP_COND_S(op, cond) \
void op_cmp_s_ ## op (void) \
{ \
int c = cond; \
update_fcr31(); \
if (c) \
SET_FP_COND(PARAM1, env); \
else \
CLEAR_FP_COND(PARAM1, env); \
DEBUG_FPU_STATE(); \
RETURN(); \
}
flag float32_is_unordered(int sig, float32 a, float32 b STATUS_PARAM)
{
extern flag float32_is_nan(float32 a);
if (float32_is_signaling_nan(a) ||
float32_is_signaling_nan(b) ||
(sig && (float32_is_nan(a) || float32_is_nan(b)))) {
float_raise(float_flag_invalid, status);
return 1;
}
else {
} else if (float32_is_nan(a) || float32_is_nan(b)) {
return 1;
} else {
return 0;
}
}
/* NOTE: the comma operator will make "cond" to eval to false,
* but float*_is_unordered() is still called
*/
FOP_COND(s, f, 0, 0)
FOP_COND(s, un, 0, float32_is_unordered(FST1, FST0, &env->fp_status))
FOP_COND(s, eq, 0, float32_eq(FST0, FST1, &env->fp_status))
FOP_COND(s, ueq, 0, float32_is_unordered(FST1, FST0, &env->fp_status) || float32_eq(FST0, FST1, &env->fp_status))
FOP_COND(s, olt, 0, float32_lt(FST0, FST1, &env->fp_status))
FOP_COND(s, ult, 0, float32_is_unordered(FST1, FST0, &env->fp_status) || float32_lt(FST0, FST1, &env->fp_status))
FOP_COND(s, ole, 0, float32_le(FST0, FST1, &env->fp_status))
FOP_COND(s, ule, 0, float32_is_unordered(FST1, FST0, &env->fp_status) || float32_le(FST0, FST1, &env->fp_status))
* but float*_is_unordered() is still called. */
FOP_COND_S(f, (float32_is_unordered(0, FST1, FST0, &env->fp_status), 0))
FOP_COND_S(un, float32_is_unordered(0, FST1, FST0, &env->fp_status))
FOP_COND_S(eq, !float32_is_unordered(0, FST1, FST0, &env->fp_status) && float32_eq(FST0, FST1, &env->fp_status))
FOP_COND_S(ueq, float32_is_unordered(0, FST1, FST0, &env->fp_status) || float32_eq(FST0, FST1, &env->fp_status))
FOP_COND_S(olt, !float32_is_unordered(0, FST1, FST0, &env->fp_status) && float32_lt(FST0, FST1, &env->fp_status))
FOP_COND_S(ult, float32_is_unordered(0, FST1, FST0, &env->fp_status) || float32_lt(FST0, FST1, &env->fp_status))
FOP_COND_S(ole, !float32_is_unordered(0, FST1, FST0, &env->fp_status) && float32_le(FST0, FST1, &env->fp_status))
FOP_COND_S(ule, float32_is_unordered(0, FST1, FST0, &env->fp_status) || float32_le(FST0, FST1, &env->fp_status))
/* NOTE: the comma operator will make "cond" to eval to false,
* but float*_is_unordered() is still called
*/
FOP_COND(s, sf, 1, (float32_is_unordered(FST0, FST1, &env->fp_status), 0))
FOP_COND(s, ngle,1, float32_is_unordered(FST1, FST0, &env->fp_status))
FOP_COND(s, seq, 1, float32_eq(FST0, FST1, &env->fp_status))
FOP_COND(s, ngl, 1, float32_is_unordered(FST1, FST0, &env->fp_status) || float32_eq(FST0, FST1, &env->fp_status))
FOP_COND(s, lt, 1, float32_lt(FST0, FST1, &env->fp_status))
FOP_COND(s, nge, 1, float32_is_unordered(FST1, FST0, &env->fp_status) || float32_lt(FST0, FST1, &env->fp_status))
FOP_COND(s, le, 1, float32_le(FST0, FST1, &env->fp_status))
FOP_COND(s, ngt, 1, float32_is_unordered(FST1, FST0, &env->fp_status) || float32_le(FST0, FST1, &env->fp_status))
* but float*_is_unordered() is still called. */
FOP_COND_S(sf, (float32_is_unordered(1, FST1, FST0, &env->fp_status), 0))
FOP_COND_S(ngle,float32_is_unordered(1, FST1, FST0, &env->fp_status))
FOP_COND_S(seq, !float32_is_unordered(1, FST1, FST0, &env->fp_status) && float32_eq(FST0, FST1, &env->fp_status))
FOP_COND_S(ngl, float32_is_unordered(1, FST1, FST0, &env->fp_status) || float32_eq(FST0, FST1, &env->fp_status))
FOP_COND_S(lt, !float32_is_unordered(1, FST1, FST0, &env->fp_status) && float32_lt(FST0, FST1, &env->fp_status))
FOP_COND_S(nge, float32_is_unordered(1, FST1, FST0, &env->fp_status) || float32_lt(FST0, FST1, &env->fp_status))
FOP_COND_S(le, !float32_is_unordered(1, FST1, FST0, &env->fp_status) && float32_le(FST0, FST1, &env->fp_status))
FOP_COND_S(ngt, float32_is_unordered(1, FST1, FST0, &env->fp_status) || float32_le(FST0, FST1, &env->fp_status))
#define FOP_COND_PS(op, condl, condh) \
void op_cmp_ps_ ## op (void) \
{ \
int cl = condl; \
int ch = condh; \
update_fcr31(); \
if (cl) \
SET_FP_COND(PARAM1, env); \
else \
CLEAR_FP_COND(PARAM1, env); \
if (ch) \
SET_FP_COND(PARAM1 + 1, env); \
else \
CLEAR_FP_COND(PARAM1 + 1, env); \
DEBUG_FPU_STATE(); \
RETURN(); \
}
/* NOTE: the comma operator will make "cond" to eval to false,
* but float*_is_unordered() is still called. */
FOP_COND_PS(f, (float32_is_unordered(0, FST1, FST0, &env->fp_status), 0),
(float32_is_unordered(0, FSTH1, FSTH0, &env->fp_status), 0))
FOP_COND_PS(un, float32_is_unordered(0, FST1, FST0, &env->fp_status),
float32_is_unordered(0, FSTH1, FSTH0, &env->fp_status))
FOP_COND_PS(eq, !float32_is_unordered(0, FST1, FST0, &env->fp_status) && float32_eq(FST0, FST1, &env->fp_status),
!float32_is_unordered(0, FSTH1, FSTH0, &env->fp_status) && float32_eq(FSTH0, FSTH1, &env->fp_status))
FOP_COND_PS(ueq, float32_is_unordered(0, FST1, FST0, &env->fp_status) || float32_eq(FST0, FST1, &env->fp_status),
float32_is_unordered(0, FSTH1, FSTH0, &env->fp_status) || float32_eq(FSTH0, FSTH1, &env->fp_status))
FOP_COND_PS(olt, !float32_is_unordered(0, FST1, FST0, &env->fp_status) && float32_lt(FST0, FST1, &env->fp_status),
!float32_is_unordered(0, FSTH1, FSTH0, &env->fp_status) && float32_lt(FSTH0, FSTH1, &env->fp_status))
FOP_COND_PS(ult, float32_is_unordered(0, FST1, FST0, &env->fp_status) || float32_lt(FST0, FST1, &env->fp_status),
float32_is_unordered(0, FSTH1, FSTH0, &env->fp_status) || float32_lt(FSTH0, FSTH1, &env->fp_status))
FOP_COND_PS(ole, !float32_is_unordered(0, FST1, FST0, &env->fp_status) && float32_le(FST0, FST1, &env->fp_status),
!float32_is_unordered(0, FSTH1, FSTH0, &env->fp_status) && float32_le(FSTH0, FSTH1, &env->fp_status))
FOP_COND_PS(ule, float32_is_unordered(0, FST1, FST0, &env->fp_status) || float32_le(FST0, FST1, &env->fp_status),
float32_is_unordered(0, FSTH1, FSTH0, &env->fp_status) || float32_le(FSTH0, FSTH1, &env->fp_status))
/* NOTE: the comma operator will make "cond" to eval to false,
* but float*_is_unordered() is still called. */
FOP_COND_PS(sf, (float32_is_unordered(1, FST1, FST0, &env->fp_status), 0),
(float32_is_unordered(1, FSTH1, FSTH0, &env->fp_status), 0))
FOP_COND_PS(ngle,float32_is_unordered(1, FST1, FST0, &env->fp_status),
float32_is_unordered(1, FSTH1, FSTH0, &env->fp_status))
FOP_COND_PS(seq, !float32_is_unordered(1, FST1, FST0, &env->fp_status) && float32_eq(FST0, FST1, &env->fp_status),
!float32_is_unordered(1, FSTH1, FSTH0, &env->fp_status) && float32_eq(FSTH0, FSTH1, &env->fp_status))
FOP_COND_PS(ngl, float32_is_unordered(1, FST1, FST0, &env->fp_status) || float32_eq(FST0, FST1, &env->fp_status),
float32_is_unordered(1, FSTH1, FSTH0, &env->fp_status) || float32_eq(FSTH0, FSTH1, &env->fp_status))
FOP_COND_PS(lt, !float32_is_unordered(1, FST1, FST0, &env->fp_status) && float32_lt(FST0, FST1, &env->fp_status),
!float32_is_unordered(1, FSTH1, FSTH0, &env->fp_status) && float32_lt(FSTH0, FSTH1, &env->fp_status))
FOP_COND_PS(nge, float32_is_unordered(1, FST1, FST0, &env->fp_status) || float32_lt(FST0, FST1, &env->fp_status),
float32_is_unordered(1, FSTH1, FSTH0, &env->fp_status) || float32_lt(FSTH0, FSTH1, &env->fp_status))
FOP_COND_PS(le, !float32_is_unordered(1, FST1, FST0, &env->fp_status) && float32_le(FST0, FST1, &env->fp_status),
!float32_is_unordered(1, FSTH1, FSTH0, &env->fp_status) && float32_le(FSTH0, FSTH1, &env->fp_status))
FOP_COND_PS(ngt, float32_is_unordered(1, FST1, FST0, &env->fp_status) || float32_le(FST0, FST1, &env->fp_status),
float32_is_unordered(1, FSTH1, FSTH0, &env->fp_status) || float32_le(FSTH0, FSTH1, &env->fp_status))
void op_bc1f (void)
{
T0 = ! IS_FP_COND_SET(env->fcr31);
T0 = !IS_FP_COND_SET(PARAM1, env);
DEBUG_FPU_STATE();
RETURN();
}
void op_bc1fany2 (void)
{
T0 = (!IS_FP_COND_SET(PARAM1, env) ||
!IS_FP_COND_SET(PARAM1 + 1, env));
DEBUG_FPU_STATE();
RETURN();
}
void op_bc1fany4 (void)
{
T0 = (!IS_FP_COND_SET(PARAM1, env) ||
!IS_FP_COND_SET(PARAM1 + 1, env) ||
!IS_FP_COND_SET(PARAM1 + 2, env) ||
!IS_FP_COND_SET(PARAM1 + 3, env));
DEBUG_FPU_STATE();
RETURN();
}
void op_bc1t (void)
{
T0 = IS_FP_COND_SET(env->fcr31);
T0 = IS_FP_COND_SET(PARAM1, env);
DEBUG_FPU_STATE();
RETURN();
}
void op_bc1tany2 (void)
{
T0 = (IS_FP_COND_SET(PARAM1, env) ||
IS_FP_COND_SET(PARAM1 + 1, env));
DEBUG_FPU_STATE();
RETURN();
}
void op_bc1tany4 (void)
{
T0 = (IS_FP_COND_SET(PARAM1, env) ||
IS_FP_COND_SET(PARAM1 + 1, env) ||
IS_FP_COND_SET(PARAM1 + 2, env) ||
IS_FP_COND_SET(PARAM1 + 3, env));
DEBUG_FPU_STATE();
RETURN();
}
......@@ -2037,7 +2542,7 @@ void op_tlbr (void)
#if defined (CONFIG_USER_ONLY)
void op_tls_value (void)
{
T0 = env->tls_value;
T0 = env->tls_value;
}
#endif
......@@ -2180,6 +2685,17 @@ void op_save_pc (void)
RETURN();
}
void op_save_fp_status (void)
{
union fps {
uint32_t i;
float_status f;
} fps;
fps.i = PARAM1;
env->fp_status = fps.f;
RETURN();
}
void op_interrupt_restart (void)
{
if (!(env->CP0_Status & (1 << CP0St_EXL)) &&
......
......@@ -220,3 +220,35 @@ void glue(op_sdc1, MEMSUFFIX) (void)
glue(stq, MEMSUFFIX)(T0, DT0);
RETURN();
}
void glue(op_lwxc1, MEMSUFFIX) (void)
{
WT0 = glue(ldl, MEMSUFFIX)(T0 + T1);
RETURN();
}
void glue(op_swxc1, MEMSUFFIX) (void)
{
glue(stl, MEMSUFFIX)(T0 + T1, WT0);
RETURN();
}
void glue(op_ldxc1, MEMSUFFIX) (void)
{
DT0 = glue(ldq, MEMSUFFIX)(T0 + T1);
RETURN();
}
void glue(op_sdxc1, MEMSUFFIX) (void)
{
glue(stq, MEMSUFFIX)(T0 + T1, DT0);
RETURN();
}
void glue(op_luxc1, MEMSUFFIX) (void)
{
/* XXX: is defined as unaligned */
DT0 = glue(ldq, MEMSUFFIX)(T0 + T1);
RETURN();
}
void glue(op_suxc1, MEMSUFFIX) (void)
{
/* XXX: is defined as unaligned */
glue(stq, MEMSUFFIX)(T0 + T1, DT0);
RETURN();
}
......@@ -333,20 +333,26 @@ enum {
OPC_MFC1 = (0x00 << 21) | OPC_CP1,
OPC_DMFC1 = (0x01 << 21) | OPC_CP1,
OPC_CFC1 = (0x02 << 21) | OPC_CP1,
OPC_MFHCI = (0x03 << 21) | OPC_CP1,
OPC_MFHC1 = (0x03 << 21) | OPC_CP1,
OPC_MTC1 = (0x04 << 21) | OPC_CP1,
OPC_DMTC1 = (0x05 << 21) | OPC_CP1,
OPC_CTC1 = (0x06 << 21) | OPC_CP1,
OPC_MTHCI = (0x07 << 21) | OPC_CP1,
OPC_MTHC1 = (0x07 << 21) | OPC_CP1,
OPC_BC1 = (0x08 << 21) | OPC_CP1, /* bc */
OPC_BC1ANY2 = (0x09 << 21) | OPC_CP1,
OPC_BC1ANY4 = (0x0A << 21) | OPC_CP1,
OPC_S_FMT = (0x10 << 21) | OPC_CP1, /* 16: fmt=single fp */
OPC_D_FMT = (0x11 << 21) | OPC_CP1, /* 17: fmt=double fp */
OPC_E_FMT = (0x12 << 21) | OPC_CP1, /* 18: fmt=extended fp */
OPC_Q_FMT = (0x13 << 21) | OPC_CP1, /* 19: fmt=quad fp */
OPC_W_FMT = (0x14 << 21) | OPC_CP1, /* 20: fmt=32bit fixed */
OPC_L_FMT = (0x15 << 21) | OPC_CP1, /* 21: fmt=64bit fixed */
OPC_PS_FMT = (0x16 << 21) | OPC_CP1, /* 22: fmt=paired single fp */
};
#define MASK_CP1_FUNC(op) MASK_CP1(op) | (op & 0x3F)
#define MASK_BC1(op) MASK_CP1(op) | (op & (0x3 << 16))
enum {
OPC_BC1F = (0x00 << 16) | OPC_BC1,
OPC_BC1T = (0x01 << 16) | OPC_BC1,
......@@ -354,8 +360,15 @@ enum {
OPC_BC1TL = (0x03 << 16) | OPC_BC1,
};
#define MASK_CP1_BCOND(op) MASK_CP1(op) | (op & (0x3 << 16))
#define MASK_CP1_FUNC(op) MASK_CP1(op) | (op & 0x3F)
enum {
OPC_BC1FANY2 = (0x00 << 16) | OPC_BC1ANY2,
OPC_BC1TANY2 = (0x01 << 16) | OPC_BC1ANY2,
};
enum {
OPC_BC1FANY4 = (0x00 << 16) | OPC_BC1ANY4,
OPC_BC1TANY4 = (0x01 << 16) | OPC_BC1ANY4,
};
#define MASK_CP2(op) MASK_OP_MAJOR(op) | (op & (0x1F << 21))
......@@ -404,20 +417,20 @@ const unsigned char *regnames[] =
"t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra", };
/* Warning: no function for r0 register (hard wired to zero) */
#define GEN32(func, NAME) \
static GenOpFunc *NAME ## _table [32] = { \
NULL, NAME ## 1, NAME ## 2, NAME ## 3, \
NAME ## 4, NAME ## 5, NAME ## 6, NAME ## 7, \
NAME ## 8, NAME ## 9, NAME ## 10, NAME ## 11, \
NAME ## 12, NAME ## 13, NAME ## 14, NAME ## 15, \
NAME ## 16, NAME ## 17, NAME ## 18, NAME ## 19, \
NAME ## 20, NAME ## 21, NAME ## 22, NAME ## 23, \
NAME ## 24, NAME ## 25, NAME ## 26, NAME ## 27, \
NAME ## 28, NAME ## 29, NAME ## 30, NAME ## 31, \
}; \
static inline void func(int n) \
{ \
NAME ## _table[n](); \
#define GEN32(func, NAME) \
static GenOpFunc *NAME ## _table [32] = { \
NULL, NAME ## 1, NAME ## 2, NAME ## 3, \
NAME ## 4, NAME ## 5, NAME ## 6, NAME ## 7, \
NAME ## 8, NAME ## 9, NAME ## 10, NAME ## 11, \
NAME ## 12, NAME ## 13, NAME ## 14, NAME ## 15, \
NAME ## 16, NAME ## 17, NAME ## 18, NAME ## 19, \
NAME ## 20, NAME ## 21, NAME ## 22, NAME ## 23, \
NAME ## 24, NAME ## 25, NAME ## 26, NAME ## 27, \
NAME ## 28, NAME ## 29, NAME ## 30, NAME ## 31, \
}; \
static inline void func(int n) \
{ \
NAME ## _table[n](); \
}
/* General purpose registers moves */
......@@ -434,58 +447,51 @@ static const char *fregnames[] =
"f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23",
"f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31", };
# define SFGEN32(func, NAME) \
static GenOpFunc *NAME ## _table [32] = { \
NAME ## 0, NAME ## 1, NAME ## 2, NAME ## 3, \
NAME ## 4, NAME ## 5, NAME ## 6, NAME ## 7, \
NAME ## 8, NAME ## 9, NAME ## 10, NAME ## 11, \
NAME ## 12, NAME ## 13, NAME ## 14, NAME ## 15, \
NAME ## 16, NAME ## 17, NAME ## 18, NAME ## 19, \
NAME ## 20, NAME ## 21, NAME ## 22, NAME ## 23, \
NAME ## 24, NAME ## 25, NAME ## 26, NAME ## 27, \
NAME ## 28, NAME ## 29, NAME ## 30, NAME ## 31, \
}; \
static inline void func(int n) \
{ \
NAME ## _table[n](); \
#define FGEN32(func, NAME) \
static GenOpFunc *NAME ## _table [32] = { \
NAME ## 0, NAME ## 1, NAME ## 2, NAME ## 3, \
NAME ## 4, NAME ## 5, NAME ## 6, NAME ## 7, \
NAME ## 8, NAME ## 9, NAME ## 10, NAME ## 11, \
NAME ## 12, NAME ## 13, NAME ## 14, NAME ## 15, \
NAME ## 16, NAME ## 17, NAME ## 18, NAME ## 19, \
NAME ## 20, NAME ## 21, NAME ## 22, NAME ## 23, \
NAME ## 24, NAME ## 25, NAME ## 26, NAME ## 27, \
NAME ## 28, NAME ## 29, NAME ## 30, NAME ## 31, \
}; \
static inline void func(int n) \
{ \
NAME ## _table[n](); \
}
# define DFGEN32(func, NAME) \
static GenOpFunc *NAME ## _table [32] = { \
NAME ## 0, 0, NAME ## 2, 0, \
NAME ## 4, 0, NAME ## 6, 0, \
NAME ## 8, 0, NAME ## 10, 0, \
NAME ## 12, 0, NAME ## 14, 0, \
NAME ## 16, 0, NAME ## 18, 0, \
NAME ## 20, 0, NAME ## 22, 0, \
NAME ## 24, 0, NAME ## 26, 0, \
NAME ## 28, 0, NAME ## 30, 0, \
}; \
static inline void func(int n) \
{ \
NAME ## _table[n](); \
}
FGEN32(gen_op_load_fpr_WT0, gen_op_load_fpr_WT0_fpr);
FGEN32(gen_op_store_fpr_WT0, gen_op_store_fpr_WT0_fpr);
FGEN32(gen_op_load_fpr_WT1, gen_op_load_fpr_WT1_fpr);
FGEN32(gen_op_store_fpr_WT1, gen_op_store_fpr_WT1_fpr);
SFGEN32(gen_op_load_fpr_WT0, gen_op_load_fpr_WT0_fpr);
SFGEN32(gen_op_store_fpr_WT0, gen_op_store_fpr_WT0_fpr);
FGEN32(gen_op_load_fpr_WT2, gen_op_load_fpr_WT2_fpr);
FGEN32(gen_op_store_fpr_WT2, gen_op_store_fpr_WT2_fpr);
SFGEN32(gen_op_load_fpr_WT1, gen_op_load_fpr_WT1_fpr);
SFGEN32(gen_op_store_fpr_WT1, gen_op_store_fpr_WT1_fpr);
FGEN32(gen_op_load_fpr_DT0, gen_op_load_fpr_DT0_fpr);
FGEN32(gen_op_store_fpr_DT0, gen_op_store_fpr_DT0_fpr);
SFGEN32(gen_op_load_fpr_WT2, gen_op_load_fpr_WT2_fpr);
SFGEN32(gen_op_store_fpr_WT2, gen_op_store_fpr_WT2_fpr);
FGEN32(gen_op_load_fpr_DT1, gen_op_load_fpr_DT1_fpr);
FGEN32(gen_op_store_fpr_DT1, gen_op_store_fpr_DT1_fpr);
DFGEN32(gen_op_load_fpr_DT0, gen_op_load_fpr_DT0_fpr);
DFGEN32(gen_op_store_fpr_DT0, gen_op_store_fpr_DT0_fpr);
FGEN32(gen_op_load_fpr_DT2, gen_op_load_fpr_DT2_fpr);
FGEN32(gen_op_store_fpr_DT2, gen_op_store_fpr_DT2_fpr);
DFGEN32(gen_op_load_fpr_DT1, gen_op_load_fpr_DT1_fpr);
DFGEN32(gen_op_store_fpr_DT1, gen_op_store_fpr_DT1_fpr);
FGEN32(gen_op_load_fpr_WTH0, gen_op_load_fpr_WTH0_fpr);
FGEN32(gen_op_store_fpr_WTH0, gen_op_store_fpr_WTH0_fpr);
DFGEN32(gen_op_load_fpr_DT2, gen_op_load_fpr_DT2_fpr);
DFGEN32(gen_op_store_fpr_DT2, gen_op_store_fpr_DT2_fpr);
FGEN32(gen_op_load_fpr_WTH1, gen_op_load_fpr_WTH1_fpr);
FGEN32(gen_op_store_fpr_WTH1, gen_op_store_fpr_WTH1_fpr);
FGEN32(gen_op_load_fpr_WTH2, gen_op_load_fpr_WTH2_fpr);
FGEN32(gen_op_store_fpr_WTH2, gen_op_store_fpr_WTH2_fpr);
#define FOP_CONDS(fmt) \
static GenOpFunc * cond_ ## fmt ## _table[16] = { \
static GenOpFunc1 * cond_ ## fmt ## _table[16] = { \
gen_op_cmp_ ## fmt ## _f, \
gen_op_cmp_ ## fmt ## _un, \
gen_op_cmp_ ## fmt ## _eq, \
......@@ -503,18 +509,20 @@ static GenOpFunc * cond_ ## fmt ## _table[16] = { \
gen_op_cmp_ ## fmt ## _le, \
gen_op_cmp_ ## fmt ## _ngt, \
}; \
static inline void gen_cmp_ ## fmt(int n) \
static inline void gen_cmp_ ## fmt(int n, long cc) \
{ \
cond_ ## fmt ## _table[n](); \
cond_ ## fmt ## _table[n](cc); \
}
FOP_CONDS(d)
FOP_CONDS(s)
FOP_CONDS(ps)
typedef struct DisasContext {
struct TranslationBlock *tb;
target_ulong pc, saved_pc;
uint32_t opcode;
uint32_t fp_status, saved_fp_status;
/* Routine used to access memory */
int mem_idx;
uint32_t hflags, saved_hflags;
......@@ -600,17 +608,31 @@ static inline void save_cpu_state (DisasContext *ctx, int do_save_pc)
if (ctx->hflags != ctx->saved_hflags) {
gen_op_save_state(ctx->hflags);
ctx->saved_hflags = ctx->hflags;
if (ctx->hflags & MIPS_HFLAG_BR) {
switch (ctx->hflags & MIPS_HFLAG_BMASK) {
case MIPS_HFLAG_BR:
gen_op_save_breg_target();
} else if (ctx->hflags & MIPS_HFLAG_B) {
gen_op_save_btarget(ctx->btarget);
} else if (ctx->hflags & MIPS_HFLAG_BMASK) {
break;
case MIPS_HFLAG_BC:
gen_op_save_bcond();
/* fall through */
case MIPS_HFLAG_BL:
/* bcond was already saved by the BL insn */
/* fall through */
case MIPS_HFLAG_B:
gen_op_save_btarget(ctx->btarget);
break;
}
}
}
static inline void save_fpu_state (DisasContext *ctx)
{
if (ctx->fp_status != ctx->saved_fp_status) {
gen_op_save_fp_status(ctx->fp_status);
ctx->saved_fp_status = ctx->fp_status;
}
}
static inline void generate_exception_err (DisasContext *ctx, int excp, int err)
{
#if defined MIPS_DEBUG_DISAS
......@@ -677,6 +699,12 @@ OP_LD_TABLE(wc1);
OP_ST_TABLE(wc1);
OP_LD_TABLE(dc1);
OP_ST_TABLE(dc1);
OP_LD_TABLE(wxc1);
OP_ST_TABLE(wxc1);
OP_LD_TABLE(dxc1);
OP_ST_TABLE(dxc1);
OP_LD_TABLE(uxc1);
OP_ST_TABLE(uxc1);
/* Load and store */
static void gen_ldst (DisasContext *ctx, uint32_t opc, int rt,
......@@ -1472,7 +1500,7 @@ static void gen_compute_branch (DisasContext *ctx, uint32_t opc,
if (ctx->hflags & MIPS_HFLAG_BMASK) {
if (loglevel & CPU_LOG_TB_IN_ASM) {
fprintf(logfile,
"undefined branch in delay slot at PC " TARGET_FMT_lx "\n",
"Branch in delay slot at PC 0x" TARGET_FMT_lx "\n",
ctx->pc);
}
MIPS_INVAL("branch/jump in bdelay slot");
......@@ -1672,6 +1700,7 @@ static void gen_compute_branch (DisasContext *ctx, uint32_t opc,
MIPS_DEBUG("bltzal %s, %08x", regnames[rs], btarget);
not_likely:
ctx->hflags |= MIPS_HFLAG_BC;
gen_op_set_bcond();
break;
case OPC_BLTZALL:
gen_op_ltz();
......@@ -1679,13 +1708,14 @@ static void gen_compute_branch (DisasContext *ctx, uint32_t opc,
MIPS_DEBUG("bltzall %s, %08x", regnames[rs], btarget);
likely:
ctx->hflags |= MIPS_HFLAG_BL;
gen_op_set_bcond();
gen_op_save_bcond();
break;
default:
MIPS_INVAL("conditional branch/jump");
generate_exception(ctx, EXCP_RI);
return;
}
gen_op_set_bcond();
}
MIPS_DEBUG("enter ds: link %d cond %02x target %08x",
blink, ctx->hflags, btarget);
......@@ -4220,7 +4250,7 @@ static void gen_cp0 (DisasContext *ctx, uint32_t opc, int rt, int rd)
/* CP1 Branches (before delay slot) */
static void gen_compute_branch1 (DisasContext *ctx, uint32_t op,
int32_t offset)
int32_t cc, int32_t offset)
{
target_ulong btarget;
......@@ -4228,31 +4258,49 @@ static void gen_compute_branch1 (DisasContext *ctx, uint32_t op,
switch (op) {
case OPC_BC1F:
gen_op_bc1f();
gen_op_bc1f(cc);
MIPS_DEBUG("bc1f " TARGET_FMT_lx, btarget);
goto not_likely;
case OPC_BC1FL:
gen_op_bc1f();
gen_op_bc1f(cc);
MIPS_DEBUG("bc1fl " TARGET_FMT_lx, btarget);
goto likely;
case OPC_BC1T:
gen_op_bc1t();
gen_op_bc1t(cc);
MIPS_DEBUG("bc1t " TARGET_FMT_lx, btarget);
not_likely:
ctx->hflags |= MIPS_HFLAG_BC;
break;
goto not_likely;
case OPC_BC1TL:
gen_op_bc1t();
gen_op_bc1t(cc);
MIPS_DEBUG("bc1tl " TARGET_FMT_lx, btarget);
likely:
ctx->hflags |= MIPS_HFLAG_BL;
gen_op_set_bcond();
gen_op_save_bcond();
break;
default:
MIPS_INVAL("cp1 branch/jump");
case OPC_BC1FANY2:
gen_op_bc1fany2(cc);
MIPS_DEBUG("bc1fany2 " TARGET_FMT_lx, btarget);
goto not_likely;
case OPC_BC1TANY2:
gen_op_bc1tany2(cc);
MIPS_DEBUG("bc1tany2 " TARGET_FMT_lx, btarget);
goto not_likely;
case OPC_BC1FANY4:
gen_op_bc1fany4(cc);
MIPS_DEBUG("bc1fany4 " TARGET_FMT_lx, btarget);
goto not_likely;
case OPC_BC1TANY4:
gen_op_bc1tany4(cc);
MIPS_DEBUG("bc1tany4 " TARGET_FMT_lx, btarget);
not_likely:
ctx->hflags |= MIPS_HFLAG_BC;
gen_op_set_bcond();
break;
default:
MIPS_INVAL("cp1 branch");
generate_exception (ctx, EXCP_RI);
return;
}
gen_op_set_bcond();
MIPS_DEBUG("enter ds: cond %02x target " TARGET_FMT_lx,
ctx->hflags, btarget);
......@@ -4262,6 +4310,29 @@ static void gen_compute_branch1 (DisasContext *ctx, uint32_t op,
}
/* Coprocessor 1 (FPU) */
/* verify if floating point register is valid; an operation is not defined
* if bit 0 of any register specification is set and the FR bit in the
* Status register equals zero, since the register numbers specify an
* even-odd pair of adjacent coprocessor general registers. When the FR bit
* in the Status register equals one, both even and odd register numbers
* are valid. This limitation exists only for 64 bit wide (d,l,ps) registers.
*
* Multiple 64 bit wide registers can be checked by calling
* CHECK_FR(ctx, freg1 | freg2 | ... | fregN);
*
* FIXME: This is broken for R2, it needs to be checked at runtime, not
* at translation time.
*/
#define CHECK_FR(ctx, freg) do { \
if (!((ctx)->CP0_Status & (1 << CP0St_FR)) && ((freg) & 1)) { \
generate_exception (ctx, EXCP_RI); \
return; \
} \
} while(0)
#define FOP(func, fmt) (((fmt) << 21) | (func))
static void gen_cp1 (DisasContext *ctx, uint32_t opc, int rt, int fs)
{
const char *opn = "unk";
......@@ -4280,30 +4351,43 @@ static void gen_cp1 (DisasContext *ctx, uint32_t opc, int rt, int fs)
opn = "mtc1";
break;
case OPC_CFC1:
if (fs != 0 && fs != 31) {
MIPS_INVAL("cfc1 freg");
generate_exception (ctx, EXCP_RI);
return;
}
GEN_LOAD_IMM_TN(T1, fs);
gen_op_cfc1();
GEN_STORE_TN_REG(rt, T0);
opn = "cfc1";
break;
case OPC_CTC1:
if (fs != 0 && fs != 31) {
MIPS_INVAL("ctc1 freg");
generate_exception (ctx, EXCP_RI);
return;
}
GEN_LOAD_IMM_TN(T1, fs);
GEN_LOAD_REG_TN(T0, rt);
gen_op_ctc1();
opn = "ctc1";
break;
case OPC_DMFC1:
GEN_LOAD_FREG_FTN(DT0, fs);
gen_op_dmfc1();
GEN_STORE_TN_REG(rt, T0);
opn = "dmfc1";
break;
case OPC_DMTC1:
/* Not implemented, fallthrough. */
GEN_LOAD_REG_TN(T0, rt);
gen_op_dmtc1();
GEN_STORE_FTN_FREG(fs, DT0);
opn = "dmtc1";
break;
case OPC_MFHC1:
CHECK_FR(ctx, fs);
GEN_LOAD_FREG_FTN(WTH0, fs);
gen_op_mfhc1();
GEN_STORE_TN_REG(rt, T0);
opn = "mfhc1";
break;
case OPC_MTHC1:
CHECK_FR(ctx, fs);
GEN_LOAD_REG_TN(T0, rt);
gen_op_mthc1();
GEN_STORE_FTN_FREG(fs, WTH0);
opn = "mthc1";
break;
default:
if (loglevel & CPU_LOG_TB_IN_ASM) {
fprintf(logfile, "Invalid CP1 opcode: %08x %03x %03x %03x\n",
......@@ -4316,26 +4400,44 @@ static void gen_cp1 (DisasContext *ctx, uint32_t opc, int rt, int fs)
MIPS_DEBUG("%s %s %s", opn, regnames[rt], fregnames[fs]);
}
/* verify if floating point register is valid; an operation is not defined
* if bit 0 of any register specification is set and the FR bit in the
* Status register equals zero, since the register numbers specify an
* even-odd pair of adjacent coprocessor general registers. When the FR bit
* in the Status register equals one, both even and odd register numbers
* are valid. This limitation exists only for 64 bit wide (d,l) registers.
*
* Multiple 64 bit wide registers can be checked by calling
* CHECK_FR(ctx, freg1 | freg2 | ... | fregN);
*/
#define CHECK_FR(ctx, freg) do { \
if (!((ctx)->CP0_Status & (1<<CP0St_FR)) && ((freg) & 1)) { \
generate_exception (ctx, EXCP_RI); \
return; \
} \
} while(0)
static void gen_movci (DisasContext *ctx, int rd, int rs, int cc, int tf)
{
uint32_t ccbit;
#define FOP(func, fmt) (((fmt) << 21) | (func))
GEN_LOAD_REG_TN(T0, rd);
GEN_LOAD_REG_TN(T1, rs);
if (cc)
ccbit = 1 << (24 + cc);
else
ccbit = 1 << 23;
if (!tf)
gen_op_movf(ccbit);
else
gen_op_movt(ccbit);
GEN_STORE_TN_REG(rd, T0);
}
#define GEN_MOVCF(fmt) \
static void glue(gen_movcf_, fmt) (DisasContext *ctx, int cc, int tf) \
{ \
uint32_t ccbit; \
\
if (cc) \
ccbit = 1 << (24 + cc); \
else \
ccbit = 1 << 23; \
if (!tf) \
glue(gen_op_float_movf_, fmt)(ccbit); \
else \
glue(gen_op_float_movt_, fmt)(ccbit); \
}
GEN_MOVCF(d);
GEN_MOVCF(s);
GEN_MOVCF(ps);
#undef GEN_MOVCF
static void gen_farith (DisasContext *ctx, uint32_t op1, int ft, int fs, int fd)
static void gen_farith (DisasContext *ctx, uint32_t op1, int ft,
int fs, int fd, int cc)
{
const char *opn = "unk";
const char *condnames[] = {
......@@ -4360,6 +4462,187 @@ static void gen_farith (DisasContext *ctx, uint32_t op1, int ft, int fs, int fd)
uint32_t func = ctx->opcode & 0x3f;
switch (ctx->opcode & FOP(0x3f, 0x1f)) {
case FOP(0, 16):
GEN_LOAD_FREG_FTN(WT0, fs);
GEN_LOAD_FREG_FTN(WT1, ft);
gen_op_float_add_s();
GEN_STORE_FTN_FREG(fd, WT2);
opn = "add.s";
binary = 1;
break;
case FOP(1, 16):
GEN_LOAD_FREG_FTN(WT0, fs);
GEN_LOAD_FREG_FTN(WT1, ft);
gen_op_float_sub_s();
GEN_STORE_FTN_FREG(fd, WT2);
opn = "sub.s";
binary = 1;
break;
case FOP(2, 16):
GEN_LOAD_FREG_FTN(WT0, fs);
GEN_LOAD_FREG_FTN(WT1, ft);
gen_op_float_mul_s();
GEN_STORE_FTN_FREG(fd, WT2);
opn = "mul.s";
binary = 1;
break;
case FOP(3, 16):
GEN_LOAD_FREG_FTN(WT0, fs);
GEN_LOAD_FREG_FTN(WT1, ft);
gen_op_float_div_s();
GEN_STORE_FTN_FREG(fd, WT2);
opn = "div.s";
binary = 1;
break;
case FOP(4, 16):
GEN_LOAD_FREG_FTN(WT0, fs);
gen_op_float_sqrt_s();
GEN_STORE_FTN_FREG(fd, WT2);
opn = "sqrt.s";
break;
case FOP(5, 16):
GEN_LOAD_FREG_FTN(WT0, fs);
gen_op_float_abs_s();
GEN_STORE_FTN_FREG(fd, WT2);
opn = "abs.s";
break;
case FOP(6, 16):
GEN_LOAD_FREG_FTN(WT0, fs);
gen_op_float_mov_s();
GEN_STORE_FTN_FREG(fd, WT2);
opn = "mov.s";
break;
case FOP(7, 16):
GEN_LOAD_FREG_FTN(WT0, fs);
gen_op_float_chs_s();
GEN_STORE_FTN_FREG(fd, WT2);
opn = "neg.s";
break;
case FOP(8, 16):
CHECK_FR(ctx, fs);
GEN_LOAD_FREG_FTN(WT0, fs);
gen_op_float_roundl_s();
GEN_STORE_FTN_FREG(fd, DT2);
opn = "round.l.s";
break;
case FOP(9, 16):
CHECK_FR(ctx, fs);
GEN_LOAD_FREG_FTN(WT0, fs);
gen_op_float_truncl_s();
GEN_STORE_FTN_FREG(fd, DT2);
opn = "trunc.l.s";
break;
case FOP(10, 16):
CHECK_FR(ctx, fs);
GEN_LOAD_FREG_FTN(WT0, fs);
gen_op_float_ceill_s();
GEN_STORE_FTN_FREG(fd, DT2);
opn = "ceil.l.s";
break;
case FOP(11, 16):
CHECK_FR(ctx, fs);
GEN_LOAD_FREG_FTN(WT0, fs);
gen_op_float_floorl_s();
GEN_STORE_FTN_FREG(fd, DT2);
opn = "floor.l.s";
break;
case FOP(12, 16):
GEN_LOAD_FREG_FTN(WT0, fs);
gen_op_float_roundw_s();
GEN_STORE_FTN_FREG(fd, WT2);
opn = "round.w.s";
break;
case FOP(13, 16):
GEN_LOAD_FREG_FTN(WT0, fs);
gen_op_float_truncw_s();
GEN_STORE_FTN_FREG(fd, WT2);
opn = "trunc.w.s";
break;
case FOP(14, 16):
GEN_LOAD_FREG_FTN(WT0, fs);
gen_op_float_ceilw_s();
GEN_STORE_FTN_FREG(fd, WT2);
opn = "ceil.w.s";
break;
case FOP(15, 16):
GEN_LOAD_FREG_FTN(WT0, fs);
gen_op_float_floorw_s();
GEN_STORE_FTN_FREG(fd, WT2);
opn = "floor.w.s";
break;
case FOP(17, 16):
GEN_LOAD_REG_TN(T0, ft);
GEN_LOAD_FREG_FTN(WT0, fs);
GEN_LOAD_FREG_FTN(WT2, fd);
gen_movcf_s(ctx, (ft >> 2) & 0x7, ft & 0x1);
GEN_STORE_FTN_FREG(fd, WT2);
opn = "movcf.s";
break;
case FOP(18, 16):
GEN_LOAD_REG_TN(T0, ft);
GEN_LOAD_FREG_FTN(WT0, fs);
GEN_LOAD_FREG_FTN(WT2, fd);
gen_op_float_movz_s();
GEN_STORE_FTN_FREG(fd, WT2);
opn = "movz.s";
break;
case FOP(19, 16):
GEN_LOAD_REG_TN(T0, ft);
GEN_LOAD_FREG_FTN(WT0, fs);
GEN_LOAD_FREG_FTN(WT2, fd);
gen_op_float_movn_s();
GEN_STORE_FTN_FREG(fd, WT2);
opn = "movn.s";
break;
case FOP(33, 16):
CHECK_FR(ctx, fd);
GEN_LOAD_FREG_FTN(WT0, fs);
gen_op_float_cvtd_s();
GEN_STORE_FTN_FREG(fd, DT2);
opn = "cvt.d.s";
break;
case FOP(36, 16):
GEN_LOAD_FREG_FTN(WT0, fs);
gen_op_float_cvtw_s();
GEN_STORE_FTN_FREG(fd, WT2);
opn = "cvt.w.s";
break;
case FOP(37, 16):
CHECK_FR(ctx, fs | fd);
GEN_LOAD_FREG_FTN(WT0, fs);
gen_op_float_cvtl_s();
GEN_STORE_FTN_FREG(fd, DT2);
opn = "cvt.l.s";
break;
case FOP(38, 16):
CHECK_FR(ctx, fs | ft | fd);
GEN_LOAD_FREG_FTN(WT1, fs);
GEN_LOAD_FREG_FTN(WT0, ft);
gen_op_float_cvtps_s();
GEN_STORE_FTN_FREG(fd, DT2);
opn = "cvt.ps.s";
break;
case FOP(48, 16):
case FOP(49, 16):
case FOP(50, 16):
case FOP(51, 16):
case FOP(52, 16):
case FOP(53, 16):
case FOP(54, 16):
case FOP(55, 16):
case FOP(56, 16):
case FOP(57, 16):
case FOP(58, 16):
case FOP(59, 16):
case FOP(60, 16):
case FOP(61, 16):
case FOP(62, 16):
case FOP(63, 16):
GEN_LOAD_FREG_FTN(WT0, fs);
GEN_LOAD_FREG_FTN(WT1, ft);
gen_cmp_s(func-48, cc);
opn = condnames[func-48];
break;
case FOP(0, 17):
CHECK_FR(ctx, fs | ft | fd);
GEN_LOAD_FREG_FTN(DT0, fs);
......@@ -4424,10 +4707,34 @@ static void gen_farith (DisasContext *ctx, uint32_t op1, int ft, int fs, int fd)
GEN_STORE_FTN_FREG(fd, DT2);
opn = "neg.d";
break;
/* 8 - round.l */
/* 9 - trunc.l */
/* 10 - ceil.l */
/* 11 - floor.l */
case FOP(8, 17):
CHECK_FR(ctx, fs);
GEN_LOAD_FREG_FTN(DT0, fs);
gen_op_float_roundl_d();
GEN_STORE_FTN_FREG(fd, DT2);
opn = "round.l.d";
break;
case FOP(9, 17):
CHECK_FR(ctx, fs);
GEN_LOAD_FREG_FTN(DT0, fs);
gen_op_float_truncl_d();
GEN_STORE_FTN_FREG(fd, DT2);
opn = "trunc.l.d";
break;
case FOP(10, 17):
CHECK_FR(ctx, fs);
GEN_LOAD_FREG_FTN(DT0, fs);
gen_op_float_ceill_d();
GEN_STORE_FTN_FREG(fd, DT2);
opn = "ceil.l.d";
break;
case FOP(11, 17):
CHECK_FR(ctx, fs);
GEN_LOAD_FREG_FTN(DT0, fs);
gen_op_float_floorl_d();
GEN_STORE_FTN_FREG(fd, DT2);
opn = "floor.l.d";
break;
case FOP(12, 17):
CHECK_FR(ctx, fs);
GEN_LOAD_FREG_FTN(DT0, fs);
......@@ -4456,19 +4763,29 @@ static void gen_farith (DisasContext *ctx, uint32_t op1, int ft, int fs, int fd)
GEN_STORE_FTN_FREG(fd, WT2);
opn = "floor.w.d";
break;
case FOP(33, 16):
CHECK_FR(ctx, fd);
GEN_LOAD_FREG_FTN(WT0, fs);
gen_op_float_cvtd_s();
case FOP(17, 17):
GEN_LOAD_REG_TN(T0, ft);
GEN_LOAD_FREG_FTN(DT0, fs);
GEN_LOAD_FREG_FTN(DT2, fd);
gen_movcf_d(ctx, (ft >> 2) & 0x7, ft & 0x1);
GEN_STORE_FTN_FREG(fd, DT2);
opn = "cvt.d.s";
opn = "movcf.d";
break;
case FOP(33, 20):
CHECK_FR(ctx, fd);
GEN_LOAD_FREG_FTN(WT0, fs);
gen_op_float_cvtd_w();
case FOP(18, 17):
GEN_LOAD_REG_TN(T0, ft);
GEN_LOAD_FREG_FTN(DT0, fs);
GEN_LOAD_FREG_FTN(DT2, fd);
gen_op_float_movz_d();
GEN_STORE_FTN_FREG(fd, DT2);
opn = "cvt.d.w";
opn = "movz.d";
break;
case FOP(19, 17):
GEN_LOAD_REG_TN(T0, ft);
GEN_LOAD_FREG_FTN(DT0, fs);
GEN_LOAD_FREG_FTN(DT2, fd);
gen_op_float_movn_d();
GEN_STORE_FTN_FREG(fd, DT2);
opn = "movn.d";
break;
case FOP(48, 17):
case FOP(49, 17):
......@@ -4489,125 +4806,240 @@ static void gen_farith (DisasContext *ctx, uint32_t op1, int ft, int fs, int fd)
CHECK_FR(ctx, fs | ft);
GEN_LOAD_FREG_FTN(DT0, fs);
GEN_LOAD_FREG_FTN(DT1, ft);
gen_cmp_d(func-48);
gen_cmp_d(func-48, cc);
opn = condnames[func-48];
break;
case FOP(0, 16):
case FOP(32, 17):
CHECK_FR(ctx, fs);
GEN_LOAD_FREG_FTN(DT0, fs);
gen_op_float_cvts_d();
GEN_STORE_FTN_FREG(fd, WT2);
opn = "cvt.s.d";
break;
case FOP(36, 17):
CHECK_FR(ctx, fs);
GEN_LOAD_FREG_FTN(DT0, fs);
gen_op_float_cvtw_d();
GEN_STORE_FTN_FREG(fd, WT2);
opn = "cvt.w.d";
break;
case FOP(37, 17):
CHECK_FR(ctx, fs | fd);
GEN_LOAD_FREG_FTN(DT0, fs);
gen_op_float_cvtl_d();
GEN_STORE_FTN_FREG(fd, DT2);
opn = "cvt.l.d";
break;
case FOP(32, 20):
GEN_LOAD_FREG_FTN(WT0, fs);
GEN_LOAD_FREG_FTN(WT1, ft);
gen_op_float_add_s();
gen_op_float_cvts_w();
GEN_STORE_FTN_FREG(fd, WT2);
opn = "add.s";
binary = 1;
opn = "cvt.s.w";
break;
case FOP(1, 16):
case FOP(33, 20):
CHECK_FR(ctx, fd);
GEN_LOAD_FREG_FTN(WT0, fs);
gen_op_float_cvtd_w();
GEN_STORE_FTN_FREG(fd, DT2);
opn = "cvt.d.w";
break;
case FOP(32, 21):
CHECK_FR(ctx, fs);
GEN_LOAD_FREG_FTN(DT0, fs);
gen_op_float_cvts_l();
GEN_STORE_FTN_FREG(fd, WT2);
opn = "cvt.s.l";
break;
case FOP(33, 21):
CHECK_FR(ctx, fs | fd);
GEN_LOAD_FREG_FTN(DT0, fs);
gen_op_float_cvtd_l();
GEN_STORE_FTN_FREG(fd, DT2);
opn = "cvt.d.l";
break;
case FOP(38, 20):
case FOP(38, 21):
CHECK_FR(ctx, fs | fd);
GEN_LOAD_FREG_FTN(WT0, fs);
GEN_LOAD_FREG_FTN(WTH0, fs);
gen_op_float_cvtps_pw();
GEN_STORE_FTN_FREG(fd, WT2);
GEN_STORE_FTN_FREG(fd, WTH2);
opn = "cvt.ps.pw";
break;
case FOP(0, 22):
CHECK_FR(ctx, fs | ft | fd);
GEN_LOAD_FREG_FTN(WT0, fs);
GEN_LOAD_FREG_FTN(WTH0, fs);
GEN_LOAD_FREG_FTN(WT1, ft);
gen_op_float_sub_s();
GEN_LOAD_FREG_FTN(WTH1, ft);
gen_op_float_add_ps();
GEN_STORE_FTN_FREG(fd, WT2);
opn = "sub.s";
binary = 1;
GEN_STORE_FTN_FREG(fd, WTH2);
opn = "add.ps";
break;
case FOP(2, 16):
case FOP(1, 22):
CHECK_FR(ctx, fs | ft | fd);
GEN_LOAD_FREG_FTN(WT0, fs);
GEN_LOAD_FREG_FTN(WTH0, fs);
GEN_LOAD_FREG_FTN(WT1, ft);
gen_op_float_mul_s();
GEN_LOAD_FREG_FTN(WTH1, ft);
gen_op_float_sub_ps();
GEN_STORE_FTN_FREG(fd, WT2);
opn = "mul.s";
binary = 1;
GEN_STORE_FTN_FREG(fd, WTH2);
opn = "sub.ps";
break;
case FOP(3, 16):
case FOP(2, 22):
CHECK_FR(ctx, fs | ft | fd);
GEN_LOAD_FREG_FTN(WT0, fs);
GEN_LOAD_FREG_FTN(WTH0, fs);
GEN_LOAD_FREG_FTN(WT1, ft);
gen_op_float_div_s();
GEN_LOAD_FREG_FTN(WTH1, ft);
gen_op_float_mul_ps();
GEN_STORE_FTN_FREG(fd, WT2);
opn = "div.s";
binary = 1;
GEN_STORE_FTN_FREG(fd, WTH2);
opn = "mul.ps";
break;
case FOP(4, 16):
case FOP(5, 22):
CHECK_FR(ctx, fs | fd);
GEN_LOAD_FREG_FTN(WT0, fs);
gen_op_float_sqrt_s();
GEN_LOAD_FREG_FTN(WTH0, fs);
gen_op_float_abs_ps();
GEN_STORE_FTN_FREG(fd, WT2);
opn = "sqrt.s";
GEN_STORE_FTN_FREG(fd, WTH2);
opn = "abs.ps";
break;
case FOP(5, 16):
case FOP(6, 22):
CHECK_FR(ctx, fs | fd);
GEN_LOAD_FREG_FTN(WT0, fs);
gen_op_float_abs_s();
GEN_LOAD_FREG_FTN(WTH0, fs);
gen_op_float_mov_ps();
GEN_STORE_FTN_FREG(fd, WT2);
opn = "abs.s";
GEN_STORE_FTN_FREG(fd, WTH2);
opn = "mov.ps";
break;
case FOP(6, 16):
case FOP(7, 22):
CHECK_FR(ctx, fs | fd);
GEN_LOAD_FREG_FTN(WT0, fs);
gen_op_float_mov_s();
GEN_LOAD_FREG_FTN(WTH0, fs);
gen_op_float_chs_ps();
GEN_STORE_FTN_FREG(fd, WT2);
opn = "mov.s";
GEN_STORE_FTN_FREG(fd, WTH2);
opn = "neg.ps";
break;
case FOP(7, 16):
case FOP(17, 22):
GEN_LOAD_REG_TN(T0, ft);
GEN_LOAD_FREG_FTN(WT0, fs);
gen_op_float_chs_s();
GEN_LOAD_FREG_FTN(WTH0, fs);
GEN_LOAD_FREG_FTN(WT2, fd);
GEN_LOAD_FREG_FTN(WTH2, fd);
gen_movcf_ps(ctx, (ft >> 2) & 0x7, ft & 0x1);
GEN_STORE_FTN_FREG(fd, WT2);
opn = "neg.s";
GEN_STORE_FTN_FREG(fd, WTH2);
opn = "movcf.ps";
break;
case FOP(12, 16):
case FOP(18, 22):
GEN_LOAD_REG_TN(T0, ft);
GEN_LOAD_FREG_FTN(WT0, fs);
gen_op_float_roundw_s();
GEN_LOAD_FREG_FTN(WTH0, fs);
GEN_LOAD_FREG_FTN(WT2, fd);
GEN_LOAD_FREG_FTN(WTH2, fd);
gen_op_float_movz_ps();
GEN_STORE_FTN_FREG(fd, WT2);
opn = "round.w.s";
GEN_STORE_FTN_FREG(fd, WTH2);
opn = "movz.ps";
break;
case FOP(13, 16):
case FOP(19, 22):
GEN_LOAD_REG_TN(T0, ft);
GEN_LOAD_FREG_FTN(WT0, fs);
gen_op_float_truncw_s();
GEN_LOAD_FREG_FTN(WTH0, fs);
GEN_LOAD_FREG_FTN(WT2, fd);
GEN_LOAD_FREG_FTN(WTH2, fd);
gen_op_float_movn_ps();
GEN_STORE_FTN_FREG(fd, WT2);
opn = "trunc.w.s";
GEN_STORE_FTN_FREG(fd, WTH2);
opn = "movn.ps";
break;
case FOP(32, 17):
case FOP(32, 22):
CHECK_FR(ctx, fs);
GEN_LOAD_FREG_FTN(DT0, fs);
gen_op_float_cvts_d();
GEN_LOAD_FREG_FTN(WTH0, fs);
gen_op_float_cvts_pu();
GEN_STORE_FTN_FREG(fd, WT2);
opn = "cvt.s.d";
opn = "cvt.s.pu";
break;
case FOP(32, 20):
case FOP(36, 22):
CHECK_FR(ctx, fs | fd);
GEN_LOAD_FREG_FTN(WT0, fs);
gen_op_float_cvts_w();
GEN_LOAD_FREG_FTN(WTH0, fs);
gen_op_float_cvtpw_ps();
GEN_STORE_FTN_FREG(fd, WT2);
opn = "cvt.s.w";
GEN_STORE_FTN_FREG(fd, WTH2);
opn = "cvt.pw.ps";
break;
case FOP(36, 16):
case FOP(40, 22):
CHECK_FR(ctx, fs);
GEN_LOAD_FREG_FTN(WT0, fs);
gen_op_float_cvtw_s();
gen_op_float_cvts_pl();
GEN_STORE_FTN_FREG(fd, WT2);
opn = "cvt.w.s";
opn = "cvt.s.pl";
break;
case FOP(36, 17):
CHECK_FR(ctx, fs);
GEN_LOAD_FREG_FTN(DT0, fs);
gen_op_float_cvtw_d();
GEN_STORE_FTN_FREG(fd, WT2);
opn = "cvt.w.d";
case FOP(44, 22):
CHECK_FR(ctx, fs | ft | fd);
GEN_LOAD_FREG_FTN(WT0, fs);
GEN_LOAD_FREG_FTN(WT1, ft);
gen_op_float_pll_ps();
GEN_STORE_FTN_FREG(fd, DT2);
opn = "pll.ps";
break;
case FOP(48, 16):
case FOP(49, 16):
case FOP(50, 16):
case FOP(51, 16):
case FOP(52, 16):
case FOP(53, 16):
case FOP(54, 16):
case FOP(55, 16):
case FOP(56, 16):
case FOP(57, 16):
case FOP(58, 16):
case FOP(59, 16):
case FOP(60, 16):
case FOP(61, 16):
case FOP(62, 16):
case FOP(63, 16):
case FOP(45, 22):
CHECK_FR(ctx, fs | ft | fd);
GEN_LOAD_FREG_FTN(WT0, fs);
GEN_LOAD_FREG_FTN(WTH1, ft);
gen_op_float_plu_ps();
GEN_STORE_FTN_FREG(fd, DT2);
opn = "plu.ps";
break;
case FOP(46, 22):
CHECK_FR(ctx, fs | ft | fd);
GEN_LOAD_FREG_FTN(WTH0, fs);
GEN_LOAD_FREG_FTN(WT1, ft);
gen_cmp_s(func-48);
gen_op_float_pul_ps();
GEN_STORE_FTN_FREG(fd, DT2);
opn = "pul.ps";
break;
case FOP(47, 22):
CHECK_FR(ctx, fs | ft | fd);
GEN_LOAD_FREG_FTN(WTH0, fs);
GEN_LOAD_FREG_FTN(WTH1, ft);
gen_op_float_puu_ps();
GEN_STORE_FTN_FREG(fd, DT2);
opn = "puu.ps";
break;
case FOP(48, 22):
case FOP(49, 22):
case FOP(50, 22):
case FOP(51, 22):
case FOP(52, 22):
case FOP(53, 22):
case FOP(54, 22):
case FOP(55, 22):
case FOP(56, 22):
case FOP(57, 22):
case FOP(58, 22):
case FOP(59, 22):
case FOP(60, 22):
case FOP(61, 22):
case FOP(62, 22):
case FOP(63, 22):
CHECK_FR(ctx, fs | ft);
GEN_LOAD_FREG_FTN(WT0, fs);
GEN_LOAD_FREG_FTN(WTH0, fs);
GEN_LOAD_FREG_FTN(WT1, ft);
GEN_LOAD_FREG_FTN(WTH1, ft);
gen_cmp_ps(func-48, cc);
opn = condnames[func-48];
break;
default:
default:
if (loglevel & CPU_LOG_TB_IN_ASM) {
fprintf(logfile, "Invalid FP arith function: %08x %03x %03x %03x\n",
ctx->opcode, ctx->opcode >> 26, ctx->opcode & 0x3F,
......@@ -4622,18 +5054,134 @@ static void gen_farith (DisasContext *ctx, uint32_t op1, int ft, int fs, int fd)
MIPS_DEBUG("%s %s,%s", opn, fregnames[fd], fregnames[fs]);
}
static void gen_movci (DisasContext *ctx, int rd, int rs, int cc, int tf)
/* Coprocessor 3 (FPU) */
static void gen_flt3_ldst (DisasContext *ctx, uint32_t opc, int fd,
int base, int index)
{
uint32_t ccbit;
const char *opn = "unk";
if (cc)
ccbit = 1 << (24 + cc);
else
ccbit = 1 << 23;
if (!tf)
gen_op_movf(ccbit, rd, rs);
else
gen_op_movt(ccbit, rd, rs);
GEN_LOAD_REG_TN(T0, base);
GEN_LOAD_REG_TN(T1, index);
/* Don't do NOP if destination is zero: we must perform the actual
* memory access
*/
switch (opc) {
case OPC_LWXC1:
op_ldst(lwxc1);
GEN_STORE_FTN_FREG(fd, WT0);
opn = "lwxc1";
break;
case OPC_LDXC1:
op_ldst(ldxc1);
GEN_STORE_FTN_FREG(fd, DT0);
opn = "ldxc1";
break;
case OPC_LUXC1:
op_ldst(luxc1);
GEN_STORE_FTN_FREG(fd, DT0);
opn = "luxc1";
break;
case OPC_SWXC1:
GEN_LOAD_FREG_FTN(WT0, fd);
op_ldst(swxc1);
opn = "swxc1";
break;
case OPC_SDXC1:
GEN_LOAD_FREG_FTN(DT0, fd);
op_ldst(sdxc1);
opn = "sdxc1";
break;
case OPC_SUXC1:
GEN_LOAD_FREG_FTN(DT0, fd);
op_ldst(suxc1);
opn = "suxc1";
break;
default:
MIPS_INVAL("extended float load/store");
generate_exception(ctx, EXCP_RI);
return;
}
MIPS_DEBUG("%s %s, %s(%s)", opn, fregnames[fd],regnames[index], regnames[base]);
}
static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, int fd,
int fr, int fs, int ft)
{
const char *opn = "unk";
/* All of those work only on 64bit FPUs. */
CHECK_FR(ctx, fd | fr | fs | ft);
switch (opc) {
case OPC_ALNV_PS:
GEN_LOAD_REG_TN(T0, fr);
GEN_LOAD_FREG_FTN(DT0, fs);
GEN_LOAD_FREG_FTN(DT1, ft);
gen_op_float_alnv_ps();
GEN_STORE_FTN_FREG(fd, DT2);
opn = "alnv.ps";
break;
case OPC_MADD_S:
GEN_LOAD_FREG_FTN(WT0, fs);
GEN_LOAD_FREG_FTN(WT1, ft);
GEN_LOAD_FREG_FTN(WT2, fr);
gen_op_float_muladd_s();
GEN_STORE_FTN_FREG(fd, WT2);
opn = "madd.s";
break;
case OPC_MADD_D:
generate_exception (ctx, EXCP_RI);
opn = "madd.d";
break;
case OPC_MADD_PS:
generate_exception (ctx, EXCP_RI);
opn = "madd.ps";
break;
case OPC_MSUB_S:
generate_exception (ctx, EXCP_RI);
opn = "msub.s";
break;
case OPC_MSUB_D:
generate_exception (ctx, EXCP_RI);
opn = "msub.d";
break;
case OPC_MSUB_PS:
generate_exception (ctx, EXCP_RI);
opn = "msub.ps";
break;
case OPC_NMADD_S:
generate_exception (ctx, EXCP_RI);
opn = "nmadd.s";
break;
case OPC_NMADD_D:
generate_exception (ctx, EXCP_RI);
opn = "nmadd.d";
break;
case OPC_NMADD_PS:
generate_exception (ctx, EXCP_RI);
opn = "nmadd.ps";
break;
case OPC_NMSUB_S:
generate_exception (ctx, EXCP_RI);
opn = "nmsub.s";
break;
case OPC_NMSUB_D:
generate_exception (ctx, EXCP_RI);
opn = "nmsub.d";
break;
case OPC_NMSUB_PS:
generate_exception (ctx, EXCP_RI);
opn = "nmsub.ps";
break;
default:
if (loglevel & CPU_LOG_TB_IN_ASM) {
fprintf(logfile, "Invalid extended FP arith function: %08x %03x %03x\n",
ctx->opcode, ctx->opcode >> 26, ctx->opcode & 0x3F);
}
generate_exception (ctx, EXCP_RI);
return;
}
MIPS_DEBUG("%s %s, %s, %s, %s", opn, fregnames[fd], fregnames[fr],
fregnames[fs], fregnames[ft]);
}
/* ISA extensions (ASEs) */
......@@ -4641,23 +5189,12 @@ static void gen_movci (DisasContext *ctx, int rd, int rs, int cc, int tf)
/* SmartMIPS extension to MIPS32 */
#ifdef TARGET_MIPS64
/* Coprocessor 3 (FPU) */
/* MDMX extension to MIPS64 */
/* MIPS-3D extension to MIPS64 */
#endif
static void gen_blikely(DisasContext *ctx)
{
int l1;
l1 = gen_new_label();
gen_op_jnz_T2(l1);
gen_op_save_state(ctx->hflags & ~MIPS_HFLAG_BMASK);
gen_goto_tb(ctx, 1, ctx->pc + 4);
gen_set_label(l1);
}
static void decode_opc (CPUState *env, DisasContext *ctx)
{
int32_t offset;
......@@ -4673,9 +5210,14 @@ static void decode_opc (CPUState *env, DisasContext *ctx)
}
if ((ctx->hflags & MIPS_HFLAG_BMASK) == MIPS_HFLAG_BL) {
int l1;
/* Handle blikely not taken case */
MIPS_DEBUG("blikely condition (" TARGET_FMT_lx ")", ctx->pc + 4);
gen_blikely(ctx);
l1 = gen_new_label();
gen_op_jnz_T2(l1);
gen_op_save_state(ctx->hflags & ~MIPS_HFLAG_BMASK);
gen_goto_tb(ctx, 1, ctx->pc + 4);
gen_set_label(l1);
}
op = MASK_OP_MAJOR(ctx->opcode);
rs = (ctx->opcode >> 21) & 0x1f;
......@@ -5024,16 +5566,21 @@ static void decode_opc (CPUState *env, DisasContext *ctx)
case OPC_DMFC1:
case OPC_DMTC1:
#endif
case OPC_MFHC1:
case OPC_MTHC1:
gen_cp1(ctx, op1, rt, rd);
break;
case OPC_BC1:
gen_compute_branch1(ctx, MASK_CP1_BCOND(ctx->opcode), imm << 2);
gen_compute_branch1(ctx, MASK_BC1(ctx->opcode),
(rt >> 2) & 0x7, imm << 2);
return;
case OPC_S_FMT:
case OPC_D_FMT:
case OPC_W_FMT:
case OPC_L_FMT:
gen_farith(ctx, MASK_CP1_FUNC(ctx->opcode), rt, rd, sa);
case OPC_PS_FMT:
gen_farith(ctx, MASK_CP1_FUNC(ctx->opcode), rt, rd, sa,
(imm >> 8) & 0x7);
break;
default:
generate_exception (ctx, EXCP_RI);
......@@ -5060,10 +5607,32 @@ static void decode_opc (CPUState *env, DisasContext *ctx)
gen_op_cp1_enabled();
op1 = MASK_CP3(ctx->opcode);
switch (op1) {
case OPC_LWXC1:
case OPC_LDXC1:
case OPC_LUXC1:
case OPC_SWXC1:
case OPC_SDXC1:
case OPC_SUXC1:
gen_flt3_ldst(ctx, op1, sa, rs, rt);
break;
case OPC_PREFX:
/* treat as noop */
break;
/* Not implemented */
case OPC_ALNV_PS:
case OPC_MADD_S:
case OPC_MADD_D:
case OPC_MADD_PS:
case OPC_MSUB_S:
case OPC_MSUB_D:
case OPC_MSUB_PS:
case OPC_NMADD_S:
case OPC_NMADD_D:
case OPC_NMADD_PS:
case OPC_NMSUB_S:
case OPC_NMSUB_D:
case OPC_NMSUB_PS:
gen_flt3_arith(ctx, op1, sa, rs, rd, rt);
break;
default:
generate_exception (ctx, EXCP_RI);
break;
......@@ -5107,7 +5676,7 @@ static void decode_opc (CPUState *env, DisasContext *ctx)
ctx->hflags &= ~MIPS_HFLAG_BMASK;
ctx->bstate = BS_BRANCH;
save_cpu_state(ctx, 0);
switch (hflags & MIPS_HFLAG_BMASK) {
switch (hflags) {
case MIPS_HFLAG_B:
/* unconditional branch */
MIPS_DEBUG("unconditional branch");
......@@ -5134,6 +5703,8 @@ static void decode_opc (CPUState *env, DisasContext *ctx)
/* unconditional branch to register */
MIPS_DEBUG("branch to register");
gen_op_breg();
gen_op_reset_T0();
gen_op_exit_tb();
break;
default:
MIPS_DEBUG("unknown branch");
......@@ -5166,16 +5737,18 @@ gen_intermediate_code_internal (CPUState *env, TranslationBlock *tb,
/* Restore delay slot state from the tb context. */
ctx.hflags = tb->flags;
ctx.saved_hflags = ctx.hflags;
if (ctx.hflags & MIPS_HFLAG_BR) {
switch (ctx.hflags & MIPS_HFLAG_BMASK) {
case MIPS_HFLAG_BR:
gen_op_restore_breg_target();
} else if (ctx.hflags & MIPS_HFLAG_B) {
break;
case MIPS_HFLAG_B:
ctx.btarget = env->btarget;
} else if (ctx.hflags & MIPS_HFLAG_BMASK) {
/* If we are in the delay slot of a conditional branch,
* restore the branch condition from env->bcond to T2
*/
break;
case MIPS_HFLAG_BC:
case MIPS_HFLAG_BL:
ctx.btarget = env->btarget;
gen_op_restore_bcond();
break;
}
#if defined(CONFIG_USER_ONLY)
ctx.mem_idx = 0;
......@@ -5237,12 +5810,6 @@ gen_intermediate_code_internal (CPUState *env, TranslationBlock *tb,
gen_op_debug();
} else {
switch (ctx.bstate) {
case BS_EXCP:
gen_op_interrupt_restart();
gen_op_reset_T0();
/* Generate the return instruction. */
gen_op_exit_tb();
break;
case BS_STOP:
gen_op_interrupt_restart();
/* Fall through. */
......@@ -5250,12 +5817,14 @@ gen_intermediate_code_internal (CPUState *env, TranslationBlock *tb,
save_cpu_state(ctxp, 0);
gen_goto_tb(&ctx, 0, ctx.pc);
break;
case BS_BRANCH:
default:
case BS_EXCP:
gen_op_interrupt_restart();
gen_op_reset_T0();
/* Generate the return instruction. */
gen_op_exit_tb();
break;
case BS_BRANCH:
default:
break;
}
}
done_generating:
......@@ -5307,21 +5876,33 @@ void fpu_dump_state(CPUState *env, FILE *f,
int flags)
{
int i;
# define printfpr(fp) do { \
fpu_fprintf(f, "w:%08x d:%08lx%08lx fd:%g fs:%g\n", \
(fp)->w[FP_ENDIAN_IDX], (fp)->w[0], (fp)->w[1], (fp)->fd, (fp)->fs[FP_ENDIAN_IDX]); \
int is_fpu64 = !!(env->CP0_Status & (1 << CP0St_FR));
#define printfpr(fp) \
do { \
if (is_fpu64) \
fpu_fprintf(f, "w:%08x d:%016lx fd:%13g fs:%13g psu: %13g\n", \
(fp)->w[FP_ENDIAN_IDX], (fp)->d, (fp)->fd, \
(fp)->fs[FP_ENDIAN_IDX], (fp)->fs[!FP_ENDIAN_IDX]); \
else { \
fpr_t tmp; \
tmp.w[FP_ENDIAN_IDX] = (fp)->w[FP_ENDIAN_IDX]; \
tmp.w[!FP_ENDIAN_IDX] = ((fp) + 1)->w[FP_ENDIAN_IDX]; \
fpu_fprintf(f, "w:%08x d:%016lx fd:%13g fs:%13g psu:%13g\n", \
tmp.w[FP_ENDIAN_IDX], tmp.d, tmp.fd, \
tmp.fs[FP_ENDIAN_IDX], tmp.fs[!FP_ENDIAN_IDX]); \
} \
} while(0)
fpu_fprintf(f, "CP1 FCR0 0x%08x FCR31 0x%08x SR.FR %d\n",
env->fcr0, env->fcr31,
(env->CP0_Status & (1 << CP0St_FR)) != 0);
fpu_fprintf(f, "CP1 FCR0 0x%08x FCR31 0x%08x SR.FR %d fp_status 0x%08x(0x%02x)\n",
env->fcr0, env->fcr31, is_fpu64, env->fp_status, get_float_exception_flags(&env->fp_status));
fpu_fprintf(f, "FT0: "); printfpr(&env->ft0);
fpu_fprintf(f, "FT1: "); printfpr(&env->ft1);
fpu_fprintf(f, "FT2: "); printfpr(&env->ft2);
for(i = 0; i < 32; i += 2) {
fpu_fprintf(f, "%s: ", fregnames[i]);
printfpr(FPR(env, i));
for (i = 0; i < 32; (is_fpu64) ? i++ : (i += 2)) {
fpu_fprintf(f, "%3s: ", fregnames[i]);
printfpr(&env->fpr[i]);
}
#undef printfpr
......
......@@ -55,7 +55,7 @@
/* Define a implementation number of 1.
Define a major version 1, minor version 0. */
#define MIPS_FCR0 ((0 << 16) | (1 << 8) | (1 << 4) | 0)
#define MIPS_FCR0 ((0 << FCR0_S) | (0x1 << FCR0_PRID) | (0x10 << FCR0_REV))
struct mips_def_t {
......@@ -69,6 +69,7 @@ struct mips_def_t {
int32_t CP0_Config7;
int32_t SYNCI_Step;
int32_t CCRes;
int32_t Status_rw_bitmask;
int32_t CP1_fcr0;
};
......@@ -86,7 +87,7 @@ static mips_def_t mips_defs[] =
.CP0_Config3 = MIPS_CONFIG3,
.SYNCI_Step = 32,
.CCRes = 2,
.CP1_fcr0 = MIPS_FCR0,
.Status_rw_bitmask = 0x3278FF17,
},
{
.name = "4KEcR1",
......@@ -97,7 +98,6 @@ static mips_def_t mips_defs[] =
.CP0_Config3 = MIPS_CONFIG3,
.SYNCI_Step = 32,
.CCRes = 2,
.CP1_fcr0 = MIPS_FCR0,
},
{
.name = "4KEc",
......@@ -108,7 +108,7 @@ static mips_def_t mips_defs[] =
.CP0_Config3 = MIPS_CONFIG3,
.SYNCI_Step = 32,
.CCRes = 2,
.CP1_fcr0 = MIPS_FCR0,
.Status_rw_bitmask = 0x3278FF17,
},
{
.name = "24Kc",
......@@ -119,7 +119,7 @@ static mips_def_t mips_defs[] =
.CP0_Config3 = MIPS_CONFIG3,
.SYNCI_Step = 32,
.CCRes = 2,
.CP1_fcr0 = MIPS_FCR0,
.Status_rw_bitmask = 0x3278FF17,
},
{
.name = "24Kf",
......@@ -130,7 +130,9 @@ static mips_def_t mips_defs[] =
.CP0_Config3 = MIPS_CONFIG3,
.SYNCI_Step = 32,
.CCRes = 2,
.CP1_fcr0 = MIPS_FCR0,
.Status_rw_bitmask = 0x3678FF17,
.CP1_fcr0 = (1 << FCR0_F64) | (1 << FCR0_L) | (1 << FCR0_W) |
(1 << FCR0_D) | (1 << FCR0_S) | (0x93 << FCR0_PRID),
},
#else
{
......@@ -142,7 +144,10 @@ static mips_def_t mips_defs[] =
.CP0_Config3 = MIPS_CONFIG3,
.SYNCI_Step = 16,
.CCRes = 2,
.CP1_fcr0 = MIPS_FCR0,
.Status_rw_bitmask = 0x3678FFFF,
.CP1_fcr0 = (1 << FCR0_F64) | (1 << FCR0_L) | (1 << FCR0_W) |
(1 << FCR0_D) | (1 << FCR0_S) |
(0x4 << FCR0_PRID) | (0x0 << FCR0_REV),
},
#endif
};
......@@ -191,6 +196,7 @@ int cpu_mips_register (CPUMIPSState *env, mips_def_t *def)
env->CP0_Config7 = def->CP0_Config7;
env->SYNCI_Step = def->SYNCI_Step;
env->CCRes = def->CCRes;
env->Status_rw_bitmask = def->Status_rw_bitmask;
env->fcr0 = def->CP1_fcr0;
#if defined (MIPS_USES_R4K_TLB)
env->nb_tlb = 1 + ((def->CP0_Config1 >> CP0C1_MMU) & 63);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册