Hi All,

a new version of my FPU patch, now actually doing some math. Known issues include, but may not be limited to:

- only support .d format, that is IEEE 64bit
- no proper float exception handling. If someone gets CONFIG_SOFTFLOAT
  to compile, this should be quite easy to improve. Most of
  the infrastructure required is in place.

Feedback welcome!

Cheers,
Marius

--
Marius Groeger <[EMAIL PROTECTED]>
SYSGO AG                      Embedded and Real-Time Software
Voice: +49 6136 9948 0                  FAX: +49 6136 9948 10
www.sysgo.com | www.elinos.com | www.osek.de | www.pikeos.com
Index: target-mips/cpu.h
===================================================================
RCS file: /sources/qemu/qemu/target-mips/cpu.h,v
retrieving revision 1.6
diff -u -u -r1.6 cpu.h
--- target-mips/cpu.h   11 Mar 2006 16:23:39 -0000      1.6
+++ target-mips/cpu.h   26 Apr 2006 14:02:18 -0000
@@ -9,10 +9,13 @@
 #include "softfloat.h"
 
 typedef union fpr_t fpr_t;
+
 union fpr_t {
-    double d;
-    float  f;
-    uint32_t u[2];
+    float64 fd; /* ieee double precision */
+       float32 fs; /* ieee single precision */
+    int64_t d; /* binary single fixed-point */
+    int32_t w; /* binary single fixed-point */
+    uint32_t u32[2];
 };
 
 #if defined(MIPS_USES_R4K_TLB)
@@ -43,16 +46,48 @@
     uint32_t DCR; /* ? */
 #if defined(MIPS_USES_FPU)
     /* Floating point registers */
-    fpr_t fpr[16];
-    /* Floating point special purpose registers */
+       uint32_t fpr[2*32];     
+#define FPR(cpu, n, fmt) \
+               (((cpu)->CP0_Status & (1<<CP0St_FR)) ? \
+                       ((fpr_t*)&(cpu)->fpr[(n) & 0x1f]) : \
+                       ((fmt) == 1 ? \
+                               ((fpr_t*)&(cpu)->fpr[(n) & 0x1e]) : \
+                               ((fpr_t*)&(cpu)->fpr[(n) & 0x1f]) \
+                       )\
+               )
+
+#ifndef USE_HOST_FLOAT_REGS
+    fpr_t ft0;
+    fpr_t ft1;
+    fpr_t ft2;
+#endif
+       float_status fp_status;
+       /* fpu implementation/revision register */
     uint32_t fcr0;
-    uint32_t fcr25;
-    uint32_t fcr26;
-    uint32_t fcr28;
-    uint32_t fcsr;
+       /* fcsr */
+    uint32_t fcr31;
+/* set condition, "c" must be 0 or 1 */
+#define SET_FP_COND(reg, c) do { \
+               /*assert(c == 0 || c == 1);*/ \
+               (reg) = ((reg) & ~(1<<23)) | ((c)<<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 FP_INEXACT        1
+#define FP_UNDERFLOW      2
+#define FP_OVERFLOW       4
+#define FP_DIV0           8
+#define FP_INVALID        16
+#define FP_UNIMPLEMENTED  32
+               
 #endif
 #if defined(MIPS_USES_R4K_TLB)
-    tlb_t tlb[16];
+    tlb_t tlb[MIPS_TLB_NB];
 #endif
     uint32_t CP0_index;
     uint32_t CP0_random;
@@ -71,6 +106,7 @@
 #define CP0St_CU1   29
 #define CP0St_CU0   28
 #define CP0St_RP    27
+#define CP0St_FR    26
 #define CP0St_RE    25
 #define CP0St_BEV   22
 #define CP0St_TS    21
@@ -138,9 +174,6 @@
     uint32_t CP0_ErrorEPC;
     uint32_t CP0_DESAVE;
     /* Qemu */
-#if defined (USE_HOST_FLOAT_REGS) && defined(MIPS_USES_FPU)
-    double ft0, ft1, ft2;
-#endif
     struct QEMUTimer *timer; /* Internal timer */
     int interrupt_request;
     jmp_buf jmp_env;
Index: target-mips/exec.h
===================================================================
RCS file: /sources/qemu/qemu/target-mips/exec.h,v
retrieving revision 1.7
diff -u -u -r1.7 exec.h
--- target-mips/exec.h  11 Mar 2006 15:00:08 -0000      1.7
+++ target-mips/exec.h  26 Apr 2006 14:02:18 -0000
@@ -21,13 +21,20 @@
 register host_uint_t T2 asm(AREG3);
 
 #if defined (USE_HOST_FLOAT_REGS)
-register double FT0 asm(FREG0);
-register double FT1 asm(FREG1);
-register double FT2 asm(FREG2);
+#error "implement me."
 #else
-#define FT0 (env->ft0.d)
-#define FT1 (env->ft1.d)
-#define FT2 (env->ft2.d)
+#define FDT0 (env->ft0.fd)
+#define FDT1 (env->ft1.fd)
+#define FDT2 (env->ft2.fd)
+#define FST0 (env->ft0.fs)
+#define FST1 (env->ft1.fs)
+#define FST2 (env->ft2.fs)
+#define DT0 (env->ft0.d)
+#define DT1 (env->ft1.d)
+#define DT2 (env->ft2.d)
+#define WT0 (env->ft0.w)
+#define WT1 (env->ft1.w)
+#define WT2 (env->ft2.w)
 #endif
 
 #if defined (DEBUG_OP)
@@ -65,6 +72,14 @@
 void do_tlbwr (void);
 void do_tlbp (void);
 void do_tlbr (void);
+#ifdef MIPS_USES_FPU
+int do_cmp_d(void);
+void dump_fpu(void);
+void fpu_dump_state(CPUState *env, FILE *f, 
+                    int (*fpu_fprintf)(FILE *f, const char *fmt, ...),
+                    int flags);
+#endif
+void dump_sc (void);
 void do_lwl_raw (uint32_t);
 void do_lwr_raw (uint32_t);
 uint32_t do_swl_raw (uint32_t);
Index: target-mips/mips-defs.h
===================================================================
RCS file: /sources/qemu/qemu/target-mips/mips-defs.h,v
retrieving revision 1.1
diff -u -u -r1.1 mips-defs.h
--- target-mips/mips-defs.h     2 Jul 2005 14:57:14 -0000       1.1
+++ target-mips/mips-defs.h     26 Apr 2006 14:02:18 -0000
@@ -24,6 +24,8 @@
 /* Uses MIPS R4Kc TLB model */
 #define MIPS_USES_R4K_TLB
 #define MIPS_TLB_NB 16
+/* basic FPU register support */
+#define MIPS_USES_FPU
 /* Have config1, runs in big-endian mode, uses TLB */
 #define MIPS_CONFIG0                                            \
 ((1 << CP0C0_M) | (0x000 << CP0C0_K23) | (0x000 << CP0C0_KU) |  \
@@ -52,7 +54,7 @@
 #error "MIPS CPU not defined"
 /* Remainder for other flags */
 //#define TARGET_MIPS64
-//define MIPS_USES_FPU
+//#define MIPS_USES_FPU
 #endif
 
 #endif /* !defined (__QEMU_MIPS_DEFS_H__) */
Index: target-mips/op.c
===================================================================
RCS file: /sources/qemu/qemu/target-mips/op.c,v
retrieving revision 1.7
diff -u -u -r1.7 op.c
--- target-mips/op.c    23 Apr 2006 15:23:48 -0000      1.7
+++ target-mips/op.c    26 Apr 2006 14:02:18 -0000
@@ -149,6 +149,111 @@
 #include "op_template.c"
 #undef TN
 
+#ifdef MIPS_USES_FPU
+
+#define FREG 0
+#include "fop_template.c"
+#undef FREG
+#define FREG 1
+#include "fop_template.c"
+#undef FREG
+#define FREG 2
+#include "fop_template.c"
+#undef FREG
+#define FREG 3
+#include "fop_template.c"
+#undef FREG
+#define FREG 4
+#include "fop_template.c"
+#undef FREG
+#define FREG 5
+#include "fop_template.c"
+#undef FREG
+#define FREG 6
+#include "fop_template.c"
+#undef FREG
+#define FREG 7
+#include "fop_template.c"
+#undef FREG
+#define FREG 8
+#include "fop_template.c"
+#undef FREG
+#define FREG 9
+#include "fop_template.c"
+#undef FREG
+#define FREG 10
+#include "fop_template.c"
+#undef FREG
+#define FREG 11
+#include "fop_template.c"
+#undef FREG
+#define FREG 12
+#include "fop_template.c"
+#undef FREG
+#define FREG 13
+#include "fop_template.c"
+#undef FREG
+#define FREG 14
+#include "fop_template.c"
+#undef FREG
+#define FREG 15
+#include "fop_template.c"
+#undef FREG
+#define FREG 16
+#include "fop_template.c"
+#undef FREG
+#define FREG 17
+#include "fop_template.c"
+#undef FREG
+#define FREG 18
+#include "fop_template.c"
+#undef FREG
+#define FREG 19
+#include "fop_template.c"
+#undef FREG
+#define FREG 20
+#include "fop_template.c"
+#undef FREG
+#define FREG 21
+#include "fop_template.c"
+#undef FREG
+#define FREG 22
+#include "fop_template.c"
+#undef FREG
+#define FREG 23
+#include "fop_template.c"
+#undef FREG
+#define FREG 24
+#include "fop_template.c"
+#undef FREG
+#define FREG 25
+#include "fop_template.c"
+#undef FREG
+#define FREG 26
+#include "fop_template.c"
+#undef FREG
+#define FREG 27
+#include "fop_template.c"
+#undef FREG
+#define FREG 28
+#include "fop_template.c"
+#undef FREG
+#define FREG 29
+#include "fop_template.c"
+#undef FREG
+#define FREG 30
+#include "fop_template.c"
+#undef FREG
+#define FREG 31
+#include "fop_template.c"
+#undef FREG
+
+#define FTN
+#include "fop_template.c"
+#undef FTN
+
+#endif
+
 void op_dup_T0 (void)
 {
     T2 = T0;
@@ -562,6 +667,162 @@
     RETURN();
 }
 
+#ifdef MIPS_USES_FPU
+/* CP1 functions */
+void op_cfc1 (void)
+{
+    if (T1 == 0)
+        T0 = env->fcr0;
+    else
+        T0 = env->fcr31;
+    RETURN();
+}
+
+void op_ctc1 (void)
+{
+    if (T1 == 0)
+        env->fcr0 = T0;
+    else {
+               int rm;
+
+               /* store new fcr31, masking unused bits */      
+        env->fcr31 = T0 & 0xfe700000;
+
+               /* set rounding mode */ 
+               switch(env->fcr31 & 3) {
+                       case 0: rm = float_round_nearest_even; break;
+                       case 1: rm = float_round_to_zero; break;
+                       case 2: rm = float_round_up; break;
+                       case 3: rm = float_round_down; break;
+                       default: rm = 0;        
+               }
+               set_float_rounding_mode(rm, &env->fp_status);
+
+#ifndef CONFIG_SOFTFLOAT
+               /* no floating point exception for native float */
+               SET_FP_ENABLE(env->fcr31, 0);
+#endif
+       }
+    RETURN();
+}
+
+void op_mfc1 (void)
+{
+       T0 = WT0;
+    RETURN();
+}
+
+void op_mtc1 (void)
+{
+       WT0 = T0;
+    RETURN();
+}
+
+void op_cvt_dw (void)
+{
+       FDT0 = int32_to_float64(WT0, &env->fp_status);
+    RETURN();
+}
+
+void op_trunc_wd (void)
+{
+       WT0 = int32_to_float64(FDT0, &env->fp_status);
+    RETURN();
+}
+
+void op_round_wd (void)
+{
+       WT0 = float64_round_to_int(FDT0, &env->fp_status);
+    RETURN();
+}
+
+#if 0
+# define DEBUG_FPU_STATE() CALL_FROM_TB0(dump_fpu)
+#else
+# define DEBUG_FPU_STATE() do { } while(0)
+#endif
+
+void op_div_d (void)
+{
+    FDT2 = float64_div(FDT0, FDT1, &env->fp_status);
+       DEBUG_FPU_STATE();
+    RETURN();
+}
+
+void op_mul_d (void)
+{
+    FDT2 = float64_mul(FDT0, FDT1, &env->fp_status);
+       DEBUG_FPU_STATE();
+    RETURN();
+}
+
+void op_sub_d (void)
+{
+       FDT2 = float64_sub(FDT0, FDT1, &env->fp_status);
+       DEBUG_FPU_STATE();
+    RETURN();
+}
+
+void op_add_d (void)
+{
+       FDT2 = float64_add(FDT0, FDT1, &env->fp_status);
+       DEBUG_FPU_STATE();
+    RETURN();
+}
+
+void op_abs_d (void)
+{
+       FDT2 = float64_abs(FDT0);
+       DEBUG_FPU_STATE();
+    RETURN();
+}
+
+void op_neg_d (void)
+{
+       FDT2 = float64_chs(FDT0);
+    RETURN();
+}
+
+void op_sqrt_d (void)
+{
+       FDT2 = float64_sqrt(FDT0, &env->fp_status);
+    RETURN();
+}
+
+void op_mov_d (void)
+{
+    FDT2 = FDT0;
+       DEBUG_FPU_STATE();
+    RETURN();
+}
+
+void op_cmp_d (void)
+{
+       int c;
+
+       c = do_cmp_d();
+
+       /* save result in FCR31 */
+       SET_FP_COND(env->fcr31, c);
+
+    RETURN();
+}
+
+void op_bc1f (void)
+{
+       T0 = ! IS_FP_COND_SET(env->fcr31);
+       DEBUG_FPU_STATE();
+    RETURN();
+}
+
+void op_bc1t (void)
+{
+       T0 = IS_FP_COND_SET(env->fcr31);
+       DEBUG_FPU_STATE();
+    RETURN();
+}
+#endif
+
 #if defined(MIPS_USES_R4K_TLB)
 void op_tlbwi (void)
 {
Index: target-mips/op_helper.c
===================================================================
RCS file: /sources/qemu/qemu/target-mips/op_helper.c,v
retrieving revision 1.11
diff -u -u -r1.11 op_helper.c
--- target-mips/op_helper.c     23 Apr 2006 18:18:10 -0000      1.11
+++ target-mips/op_helper.c     26 Apr 2006 14:02:18 -0000
@@ -33,6 +33,8 @@
 void do_raise_exception_err (uint32_t exception, int error_code)
 {
 #if 1
+    if (logfile)
+               cpu_dump_state(env, logfile, fprintf, 0);
     if (logfile && exception < 0x100)
         fprintf(logfile, "%s: %d %d\n", __func__, exception, error_code);
 #endif
@@ -532,6 +534,113 @@
     return;
 }
 
+#ifdef MIPS_USES_FPU
+#include "softfloat.h"
+
+void fpu_handle_exception(void)
+{
+#ifdef CONFIG_SOFTFLOAT
+       int flags = get_float_exception_flags(&env->fp_status);
+       unsigned int cpuflags = 0, enable, cause = 0;
+
+       enable = GET_FP_ENABLE(env->fcr31);
+
+       /* determine current flags */   
+       if (flags & float_flag_invalid) {
+               cpuflags |= FP_INVALID;
+               cause |= FP_INVALID & enable;
+       }
+       if (flags & float_flag_divbyzero) {
+               cpuflags |= FP_DIV0;    
+               cause |= FP_DIV0 & enable;
+       }
+       if (flags & float_flag_overflow) {
+               cpuflags |= FP_OVERFLOW;        
+               cause |= FP_OVERFLOW & enable;
+       }
+       if (flags & float_flag_underflow) {
+               cpuflags |= FP_UNDERFLOW;       
+               cause |= FP_UNDERFLOW & enable;
+       }
+       if (flags & float_flag_inexact) {
+               cpuflags |= FP_INEXACT; 
+               cause |= FP_INEXACT & enable;
+       }
+       SET_FP_FLAGS(env->fcr31, cpuflags);
+       SET_FP_CAUSE(env->fcr31, cause);
+#else
+       SET_FP_FLAGS(env->fcr31, 0);
+       SET_FP_CAUSE(env->fcr31, 0);
+#endif
+}
+
+/* CP1 helpers */
+int do_cmp_d(void)
+{
+       int c = 0;
+               
+#      define f_isunordered(a, b) \
+               (float64_is_signaling_nan(a) || float64_is_signaling_nan(b))
+#      define f_isequal(a, b)     float64_eq(a, b, &env->fp_status)
+#      define f_islessequal(a, b) float64_le(a, b, &env->fp_status)
+#      define f_isless(a, b)      float64_lt(a, b, &env->fp_status)
+#      define f_isgreater(a, b)   !f_islessequal(a, b)
+#      define f_isequalsig(a, b)  float64_eq_signaling(a, b, &env->fp_status)
+
+       switch(T0) {
+       case 0: /* false */
+               c = 0;
+               break;
+       case 1: /* unordered */
+               c = f_isunordered(FDT1, FDT0);
+               break;
+       case 2: /* equal */
+               c = f_isequal(FDT0, FDT1);
+               break;
+       case 3: /* unordered or equal */
+               c = (f_isunordered(FDT1, FDT0) || f_isequal(FDT0, FDT1));
+               break;
+       case 4: /* ordered less than */
+               c = (!f_isunordered(FDT1, FDT0) && f_isless(FDT0, FDT1));
+               break;
+       case 5: /* unordered or less than */
+               c = (f_isunordered(FDT1, FDT0) || f_isless(FDT0, FDT1));
+               break;
+       case 6: /* ordered less than or equal */
+               c = (!f_isunordered(FDT1, FDT0) && f_islessequal(FDT0, FDT1));
+               break;
+       case 7: /* unordered or less than or equal */
+               c = (f_isunordered(FDT1, FDT0) || f_islessequal(FDT0, FDT1));
+               break;
+       case 8: /* signaling false */
+               c = 0; /* XXX */
+               break;
+       case 9: /* not greater or less than or equal */
+               c = (!f_isgreater(FDT1, FDT0) || f_islessequal(FDT0, FDT1));
+               break;
+       case 10: /* signaling equal  */
+               c = f_isequalsig(FDT1, FDT0);
+               break;
+       case 11: /* not greater than or less than */
+               c = (!f_isgreater(FDT1, FDT0) || f_isless(FDT0, FDT1));
+               break;
+       case 12: /* lt */
+               c = f_isless(FDT0, FDT1);
+               break;
+       case 13: /* not greater than or equal */
+               c = (!f_isgreater(FDT1, FDT0) || f_isequal(FDT0, FDT1));
+               break;
+       case 14: /* less than or equal */
+               c = (f_isless(FDT1, FDT0) || f_isequal(FDT0, FDT1));
+               break;
+       case 15: /* not greater than */
+               c = !f_isgreater(FDT1, FDT0);
+               break;
+       }
+       return c;       
+}
+#endif /* MIPS_USES_FPU */
+
 /* TLB management */
 #if defined(MIPS_USES_R4K_TLB)
 static void invalidate_tlb (int idx)
Index: target-mips/op_mem.c
===================================================================
RCS file: /sources/qemu/qemu/target-mips/op_mem.c,v
retrieving revision 1.2
diff -u -u -r1.2 op_mem.c
--- target-mips/op_mem.c        5 Dec 2005 19:59:36 -0000       1.2
+++ target-mips/op_mem.c        26 Apr 2006 14:02:18 -0000
@@ -61,6 +61,12 @@
     RETURN();
 }
 
+void glue(op_lwu, MEMSUFFIX) (void)
+{
+    T0 = glue(ldl, MEMSUFFIX)(T0);
+    RETURN();
+}
+
 void glue(op_sw, MEMSUFFIX) (void)
 {
     glue(stl, MEMSUFFIX)(T0, T1);
@@ -118,3 +124,28 @@
     }
     RETURN();
 }
+
+#ifdef MIPS_USES_FPU
+void glue(op_lwc1, MEMSUFFIX) (void)
+{
+       WT0 = glue(ldl, MEMSUFFIX)(T0);
+    RETURN();
+}
+void glue(op_swc1, MEMSUFFIX) (void)
+{
+       glue(stl, MEMSUFFIX)(T0, WT0);
+    RETURN();
+}
+void glue(op_ldc1, MEMSUFFIX) (void)
+{
+    DT0 = glue(ldq, MEMSUFFIX)(T0);
+    //CALL_FROM_TB0(dump_fpu);
+    RETURN();
+}
+void glue(op_sdc1, MEMSUFFIX) (void)
+{
+    //CALL_FROM_TB0(dump_fpu);
+    glue(stq, MEMSUFFIX)(T0, DT0);
+    RETURN();
+}
+#endif
Index: target-mips/translate.c
===================================================================
RCS file: /sources/qemu/qemu/target-mips/translate.c,v
retrieving revision 1.12
diff -u -u -r1.12 translate.c
--- target-mips/translate.c     23 Apr 2006 15:21:24 -0000      1.12
+++ target-mips/translate.c     26 Apr 2006 14:02:19 -0000
@@ -28,7 +28,7 @@
 #include "exec-all.h"
 #include "disas.h"
 
-//#define MIPS_DEBUG_DISAS
+#define MIPS_DEBUG_DISAS
 //#define MIPS_SINGLE_STEP
 
 #ifdef USE_DIRECT_JUMP
@@ -96,6 +96,7 @@
     OPC_LBU      = 0x24,
     OPC_LHU      = 0x25,
     OPC_LWR      = 0x26,
+    OPC_LWU      = 0x27,
     OPC_SB       = 0x28,
     OPC_SH       = 0x29,
     OPC_SWL      = 0x2A,
@@ -217,6 +218,16 @@
     OPC_WAIT     = 0x20 | EXT_CP0,
 };
 
+#ifdef MIPS_USES_FPU
+enum {
+    /* Coprocessor 1 (FPU) */
+    OPC_MFC1     = 0x00 | EXT_CP1,
+    OPC_MTC1     = 0x04 | EXT_CP1,
+    OPC_CFC1     = 0x02 | EXT_CP1,
+    OPC_CTC1     = 0x06 | EXT_CP1,
+};
+#endif
+
 const unsigned char *regnames[] =
     { "r0", "at", "v0", "v1", "a0", "a1", "a2", "a3",
       "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7",
@@ -248,6 +259,48 @@
 GEN32(gen_op_store_T0_gpr, gen_op_store_T0_gpr_gpr);
 GEN32(gen_op_store_T1_gpr, gen_op_store_T1_gpr_gpr);
 
+#ifdef MIPS_USES_FPU
+const unsigned char *fregnames[] =
+    { "f0",  "f1",  "f2",  "f3",  "f4",  "f5",  "f6",  "f7",
+      "f8",  "f9",  "f10", "f11", "f12", "f13", "f14", "f15",
+      "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23",
+      "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31", };
+
+# 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]();                                                      \
+}
+
+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);
+
+FGEN32(gen_op_load_fpr_WT2,  gen_op_load_fpr_WT2_fpr);
+FGEN32(gen_op_store_fpr_WT2, gen_op_store_fpr_WT2_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);
+
+FGEN32(gen_op_load_fpr_DT1,  gen_op_load_fpr_DT1_fpr);
+FGEN32(gen_op_store_fpr_DT1, gen_op_store_fpr_DT1_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);
+#endif
+
 typedef struct DisasContext {
     struct TranslationBlock *tb;
     target_ulong pc, saved_pc;
@@ -312,6 +365,20 @@
     }                                                                         \
 } while (0)
 
+#ifdef MIPS_USES_FPU
+
+# define GEN_LOAD_FREG_FTN(FTn, Fn)                                           \
+do {                                                                          \
+    glue(gen_op_load_fpr_, FTn)(Fn);                                          \
+} while (0)
+
+#define GEN_STORE_FTN_FREG(Fn, FTn)                                           \
+do {                                                                          \
+    glue(gen_op_store_fpr_, FTn)(Fn);                                         \
+} while (0)
+
+#endif
+
 static inline void save_cpu_state (DisasContext *ctx, int do_save_pc)
 {
 #if defined MIPS_DEBUG_DISAS
@@ -384,6 +451,7 @@
 OP_ST_TABLE(dr);
 #endif
 OP_LD_TABLE(w);
+OP_LD_TABLE(wu);
 OP_LD_TABLE(wl);
 OP_LD_TABLE(wr);
 OP_ST_TABLE(w);
@@ -397,6 +465,12 @@
 OP_ST_TABLE(b);
 OP_LD_TABLE(l);
 OP_ST_TABLE(c);
+#ifdef MIPS_USES_FPU
+OP_LD_TABLE(wc1);
+OP_ST_TABLE(wc1);
+OP_LD_TABLE(dc1);
+OP_ST_TABLE(dc1);
+#endif
 
 /* Load and store */
 static void gen_ldst (DisasContext *ctx, uint16_t opc, int rt,
@@ -463,6 +537,11 @@
         GEN_STORE_TN_REG(rt, T0);
         opn = "lw";
         break;
+    case OPC_LWU:
+        op_ldst(lwu);
+        GEN_STORE_TN_REG(rt, T0);
+        opn = "lwu";
+        break;
     case OPC_SW:
 #if defined (MIPS_HAS_UNALIGNED_LS)
     case OPC_USW:
@@ -551,6 +630,55 @@
     MIPS_DEBUG("%s %s, %d(%s)", opn, regnames[rt], offset, regnames[base]);
 }
 
+#ifdef MIPS_USES_FPU
+/* Load and store */
+static void gen_flt_ldst (DisasContext *ctx, uint16_t opc, int ft,
+                      int base, int16_t offset)
+{
+    const unsigned char *opn = "unk";
+
+    if (base == 0) {
+        GEN_LOAD_IMM_TN(T0, offset);
+    } else if (offset == 0) {
+        gen_op_load_gpr_T0(base);
+    } else {
+        gen_op_load_gpr_T0(base);
+        gen_op_set_T1(offset);
+        gen_op_add();
+    }
+    /* Don't do NOP if destination is zero: we must perform the actual
+     * memory access
+     */
+    switch (opc) {
+    case OPC_LWC1:
+        op_ldst(lwc1);
+               GEN_STORE_FTN_FREG(ft, WT0);
+        opn = "lwc1";
+        break;
+    case OPC_SWC1:
+               GEN_LOAD_FREG_FTN(WT0, ft);
+        op_ldst(swc1);
+        opn = "swc1";
+        break;
+    case OPC_LDC1:
+               op_ldst(ldc1);
+        GEN_STORE_FTN_FREG(ft, DT0);
+        opn = "ldc1";
+        break;
+    case OPC_SDC1:
+        GEN_LOAD_FREG_FTN(DT0, ft);
+        op_ldst(sdc1);
+        opn = "sdc1";
+        break;
+    default:
+        MIPS_INVAL("float load/store");
+        generate_exception(ctx, EXCP_CpU);
+        return;
+    }
+    MIPS_DEBUG("%s %s, %d(%s)", opn, fregnames[ft], offset, regnames[base]);
+}
+#endif
+
 /* Arithmetic with immediate operand */
 static void gen_arith_imm (DisasContext *ctx, uint16_t opc, int rt,
                            int rs, int16_t imm)
@@ -1189,6 +1317,7 @@
         generate_exception_err (ctx, EXCP_CpU, 0);
         return;
     }
+
     switch (opc) {
     case OPC_MFC0:
         if (rt == 0) {
@@ -1265,7 +1394,265 @@
     MIPS_DEBUG("%s %s %d", opn, regnames[rt], rd);
 }
 
+#ifdef MIPS_USES_FPU
+/* CP1 Branches (before delay slot) */
+static void gen_compute_branch1 (DisasContext *ctx, uint16_t cond,
+                                 int32_t offset)
+{
+    target_ulong btarget;
+
+    btarget = ctx->pc + 4 + offset;
+
+       switch (cond) {
+    case 0x0000: /* bc1f */
+               gen_op_bc1f();
+               MIPS_DEBUG("bc1f %08x", btarget);
+               goto not_likely;
+    case 0x0002: /* bc1fl */
+               gen_op_bc1f();
+               MIPS_DEBUG("bc1fl %08x", btarget);
+               goto likely;
+    case 0x0001: /* bc1t */
+               gen_op_bc1t();
+               MIPS_DEBUG("bc1t %08x", btarget);
+       not_likely:
+               ctx->hflags |= MIPS_HFLAG_BC;
+               break;
+    case 0x0003: /* bc1tl */
+               gen_op_bc1t();
+               MIPS_DEBUG("bc1tl %08x", btarget);
+       likely:
+               ctx->hflags |= MIPS_HFLAG_BL;
+               break;
+       default:        
+        MIPS_INVAL("cp1 branch/jump");
+        generate_exception(ctx, EXCP_RI);
+        return;
+       }
+       gen_op_set_bcond();
+
+    MIPS_DEBUG("enter ds: cond %02x target %08x",
+               ctx->hflags, btarget);
+    ctx->btarget = btarget;
+
+    return;
+}
+
 /* Coprocessor 1 (FPU) */
+static void gen_cp1 (DisasContext *ctx, uint16_t opc, int rt, int fs)
+{
+    const unsigned char *opn = "unk";
+
+    if (!(ctx->CP0_Status & (1 << CP0St_CU1))) {
+        if (loglevel & CPU_LOG_TB_IN_ASM) {
+            fprintf(logfile, "CP1 is not usable\n");
+        }
+        generate_exception_err (ctx, EXCP_CpU, 0);
+        return;
+    }
+
+    switch (opc) {
+    case OPC_MFC1:
+               GEN_LOAD_FREG_FTN(WT0, fs);
+        gen_op_mfc1();
+        GEN_STORE_TN_REG(rt, T0);
+        opn = "mfc1";
+        break;
+    case OPC_MTC1:
+        /* If we get an exception, we want to restart at next instruction */
+        GEN_LOAD_REG_TN(T0, rt);
+        gen_op_mtc1();
+               GEN_STORE_FTN_FREG(fs, WT0);
+        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("cfc1 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;
+    default:
+        if (loglevel & CPU_LOG_TB_IN_ASM) {
+            fprintf(logfile, "Invalid CP1 opcode: %08x %03x %03x %03x\n",
+                    ctx->opcode, ctx->opcode >> 26, ctx->opcode & 0x3F,
+                    ((ctx->opcode >> 16) & 0x1F));
+        }
+        generate_exception(ctx, EXCP_RI);
+        return;
+    }
+    MIPS_DEBUG("%s %s %s", opn, regnames[rt], fregnames[fs]);
+}
+
+static void gen_farith (DisasContext *ctx, int fmt, int ft, int fs, int fd, 
int func)
+{
+    const unsigned char *opn = "unk";
+       const char *conds[] = {
+                       "c.f",
+                       "c.un",
+                       "c.eq",
+                       "c.ueq",
+                       "c.olt",
+                       "c.ult",
+                       "c.ole",
+                       "c.ule",
+                       "c.sf",
+                       "c.ngle",
+                       "c.seq",
+                       "c.ngl",
+                       "c.lt",
+                       "c.nge",
+                       "c.le",
+                       "c.ngt",
+       };
+       
+    switch (func) {
+    case 33:
+           switch (fmt) {
+                       case 20: /* cvt.d.w */
+                       GEN_LOAD_FREG_FTN(WT0, fs);
+                               gen_op_cvt_dw();
+                       GEN_STORE_FTN_FREG(fd, DT0);
+                       opn = "cvt.d.w";
+                               break;
+                       default:        
+               if (loglevel & CPU_LOG_TB_IN_ASM) {
+                   fprintf(logfile, "Invalid CVT format: %08x %03x %03x 
%03x\n",
+                           ctx->opcode, ctx->opcode >> 26, ctx->opcode & 0x3F,
+                           ((ctx->opcode >> 16) & 0x1F));
+               }
+               generate_exception(ctx, EXCP_RI);
+               return;
+               }
+        break;
+       case 48 ... 63:
+               GEN_LOAD_IMM_TN(T0, func-48);
+        GEN_LOAD_FREG_FTN(DT0, fs);
+        GEN_LOAD_FREG_FTN(DT1, ft);
+               gen_op_cmp_d();
+        opn = conds[func-48];
+               break;
+       case 0:
+        GEN_LOAD_FREG_FTN(DT0, fs);
+        GEN_LOAD_FREG_FTN(DT1, ft);
+               gen_op_add_d();
+        GEN_STORE_FTN_FREG(fd, DT2);
+        opn = "add.d";
+               break;
+       case 1:
+        GEN_LOAD_FREG_FTN(DT0, fs);
+        GEN_LOAD_FREG_FTN(DT1, ft);
+               gen_op_sub_d();
+        GEN_STORE_FTN_FREG(fd, DT2);
+        opn = "sub.d";
+               break;
+       case 2:
+        GEN_LOAD_FREG_FTN(DT0, fs);
+        GEN_LOAD_FREG_FTN(DT1, ft);
+               gen_op_mul_d();
+        GEN_STORE_FTN_FREG(fd, DT2);
+        opn = "mul.d";
+               break;
+       case 3:
+        GEN_LOAD_FREG_FTN(DT0, fs);
+        GEN_LOAD_FREG_FTN(DT1, ft);
+               gen_op_div_d();
+        GEN_STORE_FTN_FREG(fd, DT2);
+        opn = "div.d";
+               break;
+       case 4:
+        GEN_LOAD_FREG_FTN(DT0, fs);
+        GEN_LOAD_FREG_FTN(DT1, ft);
+               gen_op_sqrt_d();
+        GEN_STORE_FTN_FREG(fd, DT2);
+        opn = "sqrt.d";
+               break;
+       case 5:
+        GEN_LOAD_FREG_FTN(DT0, fs);
+        GEN_LOAD_FREG_FTN(DT1, ft);
+               gen_op_div_d();
+        GEN_STORE_FTN_FREG(fd, DT2);
+        opn = "abs.d";
+               break;
+       case 6:
+        GEN_LOAD_FREG_FTN(DT0, fs);
+               gen_op_mov_d();
+        GEN_STORE_FTN_FREG(fd, DT2);
+        opn = "mov.d";
+               break;
+       case 7:
+        GEN_LOAD_FREG_FTN(DT0, fs);
+               gen_op_neg_d();
+        GEN_STORE_FTN_FREG(fd, DT2);
+        opn = "neg.d";
+               break;
+       case 12:
+           switch (fmt) {
+                       case 17: /* trunc.w.d */
+                       GEN_LOAD_FREG_FTN(DT0, fs);
+                               gen_op_round_wd();
+                       GEN_STORE_FTN_FREG(fd, WT0);
+                       opn = "round.w.d";
+                               break;
+                       default:
+               if (loglevel & CPU_LOG_TB_IN_ASM) {
+                   fprintf(logfile, "Invalid ROUND format: %08x %03x %03x 
%03x\n",
+                           ctx->opcode, ctx->opcode >> 26, ctx->opcode & 0x3F,
+                           ((ctx->opcode >> 16) & 0x1F));
+               }
+               generate_exception(ctx, EXCP_RI);
+               return;
+               }
+        break;
+    case 13:
+           switch (fmt) {
+                       case 17: /* trunc.w.d */
+                       GEN_LOAD_FREG_FTN(DT0, fs);
+                               gen_op_trunc_wd();
+                       GEN_STORE_FTN_FREG(fd, WT0);
+                       opn = "trunc.w.d";
+                               break;
+                       default:
+               if (loglevel & CPU_LOG_TB_IN_ASM) {
+                   fprintf(logfile, "Invalid TRUNC format: %08x %03x %03x 
%03x\n",
+                           ctx->opcode, ctx->opcode >> 26, ctx->opcode & 0x3F,
+                           ((ctx->opcode >> 16) & 0x1F));
+               }
+               generate_exception(ctx, EXCP_RI);
+               return;
+               }
+        break;
+       case 8: /* round.l */
+       case 9: /* trunc.l */
+       case 10: /* ceil.l */
+       case 11: /* floor.l */
+       default:        
+        if (loglevel & CPU_LOG_TB_IN_ASM) {
+            fprintf(logfile, "Invalid arith function: %08x %03x %03x %03x\n",
+                    ctx->opcode, ctx->opcode >> 26, ctx->opcode & 0x3F,
+                    ((ctx->opcode >> 16) & 0x1F));
+        }
+        generate_exception(ctx, EXCP_RI);
+        return;
+    }
+    MIPS_DEBUG("%s %s %s", opn, fregnames[fs], fregnames[ft]);
+}
+#endif
 
 /* ISA extensions */
 /* MIPS16 extension to MIPS32 */
@@ -1436,7 +1829,7 @@
         }
         break;
     case 0x01:          /* B REGIMM opcode */
-        op1 = ((ctx->opcode >> 16) & 0x1F);
+               op1 = ((ctx->opcode >> 16) & 0x1F);
         switch (op1) {
         case 0x00 ... 0x03: /* REGIMM branches */
         case 0x10 ... 0x13:
@@ -1475,8 +1868,7 @@
     case 0x14 ... 0x17:
         gen_compute_branch(ctx, op, rs, rt, imm << 2);
         return;
-    case 0x20 ... 0x26: /* Load and stores */
-    case 0x28 ... 0x2E:
+    case 0x20 ... 0x2E: /* Load and stores */
     case 0x30:
     case 0x38:
         gen_ldst(ctx, op, rt, rs, imm);
@@ -1495,14 +1887,42 @@
     case 0x35: /* LDC1 */
     case 0x39: /* SWC1 */
     case 0x3D: /* SDC1 */
-    case 0x11:          /* CP1 opcode */
 #if defined(MIPS_USES_FPU)
-        /* XXX: not correct */
+        gen_flt_ldst(ctx, op, rt, rs, imm);
 #else
         generate_exception_err(ctx, EXCP_CpU, 1);
 #endif
         break;
 
+    case 0x11:          /* CP1 opcode */
+#if defined(MIPS_USES_FPU)
+        op1 = ((ctx->opcode >> 21) & 0x1F);
+        switch (op1) {
+        case 0x00: /* mfc1 */
+        case 0x02: /* cfc1 */
+        case 0x04: /* mtc1 */
+        case 0x06: /* ctc1 */
+            gen_cp1(ctx, op1 | EXT_CP1, rt, rd);
+            break;
+        case 0x08: /* bc */
+                       gen_compute_branch1(ctx, rt, imm << 2);
+               return;
+               case 0x10: /* 16: fmt=single fp */
+               case 0x11: /* 17: fmt=double fp */
+               case 0x14: /* 20: fmt=32bit fixed */
+               case 0x15: /* 21: fmt=64bit fixed */
+            gen_farith(ctx, op1, rt, rd, sa, ctx->opcode & 0x3f);
+            break;
+        default:
+               generate_exception_err(ctx, EXCP_CpU, 1);
+            break;
+        }
+        break;
+#else
+        generate_exception_err(ctx, EXCP_CpU, 1);
+#endif
+        break;
+                       
     /* COP2.  */
     case 0x32: /* LWC2 */
     case 0x36: /* LDC2 */
@@ -1536,6 +1956,7 @@
         generate_exception(ctx, EXCP_RI);
         break;
     }
+
     if (ctx->hflags & MIPS_HFLAG_BMASK) {
         int hflags = ctx->hflags;
         /* Branches completion */
@@ -1722,6 +2143,39 @@
     return gen_intermediate_code_internal(env, tb, 1);
 }
 
+#ifdef MIPS_USES_FPU
+void fpu_dump_state(CPUState *env, FILE *f, 
+                    int (*fpu_fprintf)(FILE *f, const char *fmt, ...),
+                    int flags)
+{
+       int i;
+
+#      define printfpr(fp) do { \
+               fpu_fprintf(f, "w:%08x d:%08lx%08lx fd:%g\n", \
+                               (fp)->w, (fp)->u32[0], (fp)->u32[1], (fp)->fd); 
\
+       } while(0)
+
+    fpu_fprintf(f, "CP1 FCR0 0x%08x FCR31 0x%08x\n",
+                env->fcr0, env->fcr31);
+       fpu_fprintf(f, "PC: %08lx  SR.FR %d  FCR0 %08lx  FCR31 %08lx\n",
+                       env->PC,
+                       (env->CP0_Status & (1<<CP0St_FR)) != 0,
+                       env->fcr0, env->fcr31);
+       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++) {
+        fpu_fprintf(f, "f%02d: ", i);
+               printfpr(FPR(env, i, 0));
+               if (!(env->CP0_Status & (1<<CP0St_FR)))
+                       i++;
+       }
+
+#undef printfpr
+}
+
+#endif
+
 void cpu_dump_state (CPUState *env, FILE *f, 
                      int (*cpu_fprintf)(FILE *f, const char *fmt, ...),
                      int flags)
@@ -1751,6 +2205,9 @@
                 c0_status, env->CP0_Cause, env->CP0_EPC);
     cpu_fprintf(f, "    Config0 0x%08x Config1 0x%08x LLAddr 0x%08x\n",
                 env->CP0_Config0, env->CP0_Config1, env->CP0_LLAddr);
+#ifdef MIPS_USES_FPU
+       fpu_dump_state(env, f, cpu_fprintf, flags);
+#endif
 }
 
 CPUMIPSState *cpu_mips_init (void)
--- /dev/null   2004-03-09 17:05:24.000000000 +0100
+++ target-mips/fop_template.c  2006-04-24 16:36:56.000000000 +0200
@@ -0,0 +1,134 @@
+/*
+ *  MIPS emulation micro-operations templates for floating point reg 
+ *  load & store for qemu.
+ * 
+ *  Copyright (c) 2006 Marius Groeger
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#if defined(FREG)
+
+#define OP_LOAD_FREG(treg, tregname, width, FREG) \
+       void glue(glue(op_load_fpr_,tregname), FREG) (void) \
+       { \
+           treg = FPR(env, FREG, 0)->width; \
+           RETURN(); \
+       } \
+
+#define OP_STORE_FREG(treg, tregname, width, FREG) \
+       void glue(glue(op_store_fpr_,tregname), FREG) (void) \
+       { \
+           FPR(env, FREG, 0)->width = treg; \
+           RETURN(); \
+       } \
+
+/* WT0 = FREG.w: op_load_fpr_WT0_fprFREG */
+OP_LOAD_FREG(WT0, WT0_fpr, w, FREG)
+/* FREG.w = WT0: op_store_fpr_WT0_fprFREG */
+OP_STORE_FREG(WT0, WT0_fpr, w, FREG)
+
+OP_LOAD_FREG(WT1, WT1_fpr, w, FREG)
+OP_STORE_FREG(WT1, WT1_fpr, w, FREG)
+
+OP_LOAD_FREG(WT2, WT2_fpr, w, FREG)
+OP_STORE_FREG(WT2, WT2_fpr, w, FREG)
+
+OP_LOAD_FREG(DT0, DT0_fpr, d, FREG)
+OP_STORE_FREG(DT0, DT0_fpr, d, FREG)
+
+OP_LOAD_FREG(DT1, DT1_fpr, d, FREG)
+OP_STORE_FREG(DT1, DT1_fpr, d, FREG)
+
+OP_LOAD_FREG(DT2, DT2_fpr, d, FREG)
+OP_STORE_FREG(DT2, DT2_fpr, d, FREG)
+
+#endif
+
+#if defined (FTN)
+
+void op_set_WT0 (void)
+{
+    WT0 = PARAM1;
+    RETURN();
+}
+
+void op_set_DT0 (void)
+{
+    DT0 = PARAM1;
+    RETURN();
+}
+
+void op_reset_WT0 (void)
+{
+    WT0 = 0;
+    RETURN();
+}
+
+void op_reset_DT0 (void)
+{
+    DT0 = 0;
+    RETURN();
+}
+
+void op_set_WT1 (void)
+{
+    WT1 = PARAM1;
+    RETURN();
+}
+
+void op_set_DT1 (void)
+{
+    DT1 = PARAM1;
+    RETURN();
+}
+
+void op_reset_WT1 (void)
+{
+    WT1 = 0;
+    RETURN();
+}
+
+void op_reset_DT1 (void)
+{
+    DT1 = 0;
+    RETURN();
+}
+
+void op_set_WT2 (void)
+{
+    WT2 = PARAM1;
+    RETURN();
+}
+
+void op_set_DT2 (void)
+{
+    DT2 = PARAM1;
+    RETURN();
+}
+
+void op_reset_WT2 (void)
+{
+    WT2 = 0;
+    RETURN();
+}
+
+void op_reset_DT2 (void)
+{
+    DT2 = 0;
+    RETURN();
+}
+
+#endif
_______________________________________________
Qemu-devel mailing list
Qemu-devel@nongnu.org
http://lists.nongnu.org/mailman/listinfo/qemu-devel

Reply via email to