aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorths <ths@c046a42c-6fe2-441c-8c8c-71466251a162>2007-05-07 13:55:33 +0000
committerths <ths@c046a42c-6fe2-441c-8c8c-71466251a162>2007-05-07 13:55:33 +0000
commit5a5012ecbdcd341bb1d2e8200db91f6212aa44df (patch)
treefd30050630993cfe86d7005645af6161a9273b68
parent8b4af7052757965b9a03998f400955016b456431 (diff)
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
-rw-r--r--Changelog1
-rw-r--r--gdbstub.c4
-rw-r--r--target-mips/TODO3
-rw-r--r--target-mips/cpu.h44
-rw-r--r--target-mips/exec.h6
-rw-r--r--target-mips/fop_template.c123
-rw-r--r--target-mips/helper.c3
-rw-r--r--target-mips/op.c920
-rw-r--r--target-mips/op_mem.c32
-rw-r--r--target-mips/translate.c1067
-rw-r--r--target-mips/translate_init.c20
11 files changed, 1703 insertions, 520 deletions
diff --git a/Changelog b/Changelog
index 19a5da7fda..9aaac1877b 100644
--- a/Changelog
+++ b/Changelog
@@ -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:
diff --git a/gdbstub.c b/gdbstub.c
index 79f54f51f9..62c1db297d 100644
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -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;
}
diff --git a/target-mips/TODO b/target-mips/TODO
index f91b5c6c6a..a5842ff276 100644
--- a/target-mips/TODO
+++ b/target-mips/TODO
@@ -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
diff --git a/target-mips/cpu.h b/target-mips/cpu.h
index 36b890efb0..33cb6573eb 100644
--- a/target-mips/cpu.h
+++ b/target-mips/cpu.h
@@ -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,
diff --git a/target-mips/exec.h b/target-mips/exec.h
index b00263f1b1..d9160a1f3a 100644
--- a/target-mips/exec.h
+++ b/target-mips/exec.h
@@ -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)
diff --git a/target-mips/fop_template.c b/target-mips/fop_template.c
index 4ebb46bff5..bb288f11ee 100644
--- a/target-mips/fop_template.c
+++ b/target-mips/fop_template.c
@@ -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
diff --git a/target-mips/helper.c b/target-mips/helper.c
index 8d9435a232..909db5d37f 100644
--- a/target-mips/helper.c
+++ b/target-mips/helper.c
@@ -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;
diff --git a/target-mips/op.c b/target-mips/op.c
index 0b5e8bacb5..899892f68c 100644
--- a/target-mips/op.c
+++ b/target-mips/op.c
@@ -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)) &&
diff --git a/target-mips/op_mem.c b/target-mips/op_mem.c
index 19373cf002..f0eebc09dd 100644
--- a/target-mips/op_mem.c
+++ b/target-mips/op_mem.c
@@ -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();
+}
diff --git a/target-mips/translate.c b/target-mips/translate.c
index 06581f2cc6..6098b2a10b 100644
--- a/target-mips/translate.c
+++ b/target-mips/translate.c
@@ -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
diff --git a/target-mips/translate_init.c b/target-mips/translate_init.c
index 51c571d0e6..a166bdd5e6 100644
--- a/target-mips/translate_init.c
+++ b/target-mips/translate_init.c
@@ -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);