]> git.proxmox.com Git - qemu.git/commitdiff
MIPS 64-bit FPU support, plus some collateral bugfixes in the
authorths <ths@c046a42c-6fe2-441c-8c8c-71466251a162>
Mon, 7 May 2007 13:55:33 +0000 (13:55 +0000)
committerths <ths@c046a42c-6fe2-441c-8c8c-71466251a162>
Mon, 7 May 2007 13:55:33 +0000 (13:55 +0000)
conditional branch handling.

git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2779 c046a42c-6fe2-441c-8c8c-71466251a162

Changelog
gdbstub.c
target-mips/TODO
target-mips/cpu.h
target-mips/exec.h
target-mips/fop_template.c
target-mips/helper.c
target-mips/op.c
target-mips/op_mem.c
target-mips/translate.c
target-mips/translate_init.c

index 19a5da7fda4bdde36850ab3a1b90c2091e4baa10..9aaac1877b0982de94a31224262c5d4dcfb11583 100644 (file)
--- 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:
 
index 79f54f51f91be8233717823a24d14d9d31b7b58e..62c1db297dda1f84d02db766bff6bc7706ca41db 100644 (file)
--- 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;
           }
 
index f91b5c6c6a0e8fadc59e2733c053916c0df78bae..a5842ff27612bb6b6023b9dbb1c7f6e3267543b8 100644 (file)
@@ -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
index 36b890efb0b73ee17852fa6577f77db66f7dff52..33cb6573eb0782db3191c6cdfa5c2cd9a99d0ad9 100644 (file)
@@ -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,
index b00263f1b1478b08047b72d5140b2c294084f432..d9160a1f3a78808e5929ba4b3016a8743480435a 100644 (file)
@@ -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)
index 4ebb46bff526b9c0a5ad914c008969c98845801b..bb288f11eef8e79be6507af575ec35ee2bde577b 100644 (file)
  * 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
index 8d9435a23219399d74569af19a9a82d43ea3a472..909db5d37f3548db66e0c6d79abf143662ef0b4d 100644 (file)
@@ -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;
index 0b5e8bacb5ee0baf382b99a50dca83960a9877a2..899892f68cb22a664e929d37c52470d289249b30 100644 (file)
 #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)) &&
index 19373cf00254b3107068cfa3a60406b4572a444d..f0eebc09ddeb6592c1bf6649a4f8295567065690 100644 (file)
@@ -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();
+}
index 06581f2cc6317ce63a5a48d10ba2a4b768590d5c..6098b2a10b49df1f48d042ff896304c510ea4129 100644 (file)
@@ -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
index 51c571d0e65ea73cac57d82e51d2246e7d1b2f67..a166bdd5e6b10c89d80e19e65e842be80da1ceb0 100644 (file)
@@ -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);