Hello All,

this patch adds *basic* FPU support. Basic means:

- swc1
- lwc1
- ctc1
- cfc1
- only the lightest of testing was applied :-)

IOW, there is no real math on the host being done, I simply copy data back and forth. Alas, the basic infrastructure is there and might make it easier to add more CP1 functionality.

I'd be happy to further help improving this, and even happier if someone volunteered to help me with the job.

Thanks,
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
--- rpm-root/packages/BUILD/qemu-0.8.0/target-mips/cpu.h        2005-12-19 
23:51:53.000000000 +0100
+++ qemu-0.8.0/target-mips/cpu.h        2006-04-18 16:23:57.000000000 +0200
@@ -40,16 +40,19 @@ struct CPUMIPSState {
     uint32_t DCR; /* ? */
 #if defined(MIPS_USES_FPU)
     /* Floating point registers */
-    fpr_t fpr[16];
-    /* Floating point special purpose registers */
+    fpr_t fpr[32];
+#ifndef USE_HOST_FLOAT_REGS
+    fpr_t ft0;
+    fpr_t ft1;
+    fpr_t ft2;
+#endif
+       /* fpu implementation/revision register */
     uint32_t fcr0;
-    uint32_t fcr25;
-    uint32_t fcr26;
-    uint32_t fcr28;
-    uint32_t fcsr;
+       /* fcsr */
+    uint32_t fcr31;
 #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;
diff -purN rpm-root/packages/BUILD/qemu-0.8.0/target-mips/exec.h 
qemu-0.8.0/target-mips/exec.h
--- rpm-root/packages/BUILD/qemu-0.8.0/target-mips/exec.h       2005-12-19 
23:51:53.000000000 +0100
+++ qemu-0.8.0/target-mips/exec.h       2006-04-18 16:39:16.000000000 +0200
@@ -65,6 +65,10 @@ void do_tlbwi (void);
 void do_tlbwr (void);
 void do_tlbp (void);
 void do_tlbr (void);
+#ifdef MIPS_USES_FPU
+void do_cfc1(int reg);
+void do_ctc1(int reg);
+#endif
 void do_lwl_raw (uint32_t);
 void do_lwr_raw (uint32_t);
 uint32_t do_swl_raw (uint32_t);
diff -purN rpm-root/packages/BUILD/qemu-0.8.0/target-mips/fop_template.c 
qemu-0.8.0/target-mips/fop_template.c
--- rpm-root/packages/BUILD/qemu-0.8.0/target-mips/fop_template.c       
1970-01-01 01:00:00.000000000 +0100
+++ qemu-0.8.0/target-mips/fop_template.c       2006-04-18 15:45:34.000000000 
+0200
@@ -0,0 +1,87 @@
+/*
+ *  MIPS emulation micro-operations templates for reg load & store for qemu.
+ * 
+ *  Copyright (c) 2004-2005 Jocelyn Mayer
+ *
+ * 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)
+
+void glue(op_load_fpr_FT0_fpr, FREG) (void)
+{
+    FT0 = env->fpr[FREG].d;
+    RETURN();
+}
+
+void glue(op_store_FT0_fpr_fpr, FREG) (void)
+{
+    env->fpr[FREG].d = FT0;
+    RETURN();
+}
+
+void glue(op_load_fpr_FT1_fpr, FREG) (void)
+{
+    FT1 = env->fpr[FREG].d;
+    RETURN();
+}
+
+void glue(op_store_FT1_fpr_fpr, FREG) (void)
+{
+    env->fpr[FREG].d = FT1;
+    RETURN();
+}
+
+#endif
+
+#if defined (FTN)
+
+void op_set_FT0 (void)
+{
+    FT0 = PARAM1;
+    RETURN();
+}
+
+void op_reset_FT0 (void)
+{
+    FT0 = 0;
+    RETURN();
+}
+
+void op_set_FT1 (void)
+{
+    FT1 = PARAM1;
+    RETURN();
+}
+
+void op_reset_FT1 (void)
+{
+    FT1 = 0;
+    RETURN();
+}
+
+void op_set_FT2 (void)
+{
+    FT2 = PARAM1;
+    RETURN();
+}
+
+void op_reset_FT2 (void)
+{
+    FT2 = 0;
+    RETURN();
+}
+
+#endif
diff -purN rpm-root/packages/BUILD/qemu-0.8.0/target-mips/mips-defs.h 
qemu-0.8.0/target-mips/mips-defs.h
--- rpm-root/packages/BUILD/qemu-0.8.0/target-mips/mips-defs.h  2005-12-19 
23:51:53.000000000 +0100
+++ qemu-0.8.0/target-mips/mips-defs.h  2006-04-18 15:45:34.000000000 +0200
@@ -24,6 +24,8 @@ enum {
 /* 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 @@ enum {
 #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__) */
diff -purN rpm-root/packages/BUILD/qemu-0.8.0/target-mips/op.c 
qemu-0.8.0/target-mips/op.c
--- rpm-root/packages/BUILD/qemu-0.8.0/target-mips/op.c 2005-12-19 
23:51:53.000000000 +0100
+++ qemu-0.8.0/target-mips/op.c 2006-04-18 16:39:51.000000000 +0200
@@ -149,6 +149,111 @@ CALL_FROM_TB2(func, arg0, arg1);
 #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;
@@ -560,6 +665,21 @@ void op_mtc0 (void)
     RETURN();
 }
 
+#ifdef MIPS_USES_FPU
+/* CP1 functions */
+void op_cfc1 (void)
+{
+    CALL_FROM_TB1(do_cfc1, PARAM1);
+    RETURN();
+}
+
+void op_ctc1 (void)
+{
+    CALL_FROM_TB1(do_ctc1, PARAM1);
+    RETURN();
+}
+#endif
+
 #if defined(MIPS_USES_R4K_TLB)
 void op_tlbwi (void)
 {
diff -purN rpm-root/packages/BUILD/qemu-0.8.0/target-mips/op_helper.c 
qemu-0.8.0/target-mips/op_helper.c
--- rpm-root/packages/BUILD/qemu-0.8.0/target-mips/op_helper.c  2005-12-19 
23:51:53.000000000 +0100
+++ qemu-0.8.0/target-mips/op_helper.c  2006-04-18 16:41:52.000000000 +0200
@@ -533,6 +533,68 @@ void do_mtc0 (int reg, int sel)
     return;
 }
 
+#ifdef MIPS_USES_FPU
+/* CP1 helpers */
+void do_cfc1 (int reg)
+{
+    const unsigned char *rn;
+
+    switch (reg) {
+    case 0:
+        T0 = env->fcr0;
+        rn = "FCR0";
+        break;
+    case 31:
+        T0 = env->fcr31;
+        rn = "FCR31";
+        break;
+    default:
+        rn = "unknown";
+        break;
+    }
+#if defined MIPS_DEBUG_DISAS
+    if (loglevel & CPU_LOG_TB_IN_ASM) {
+        fprintf(logfile, "%08x cfc1 %s => %08x (%d)\n",
+                env->PC, rn, T0, reg);
+    }
+#endif
+    return;
+}
+
+void do_ctc1 (int reg)
+{
+    const unsigned char *rn;
+    uint32_t val, old;
+
+    switch (reg) {
+    case 0:
+        val = T0;
+        old = env->fcr0;
+        env->fcr0 = T0;
+        rn = "FCR0";
+        break;
+    case 31:
+        val = T0;
+        old = env->fcr31;
+        env->fcr31 = T0;
+        rn = "FCR31";
+        break;
+    default:
+        val = -1;
+        old = -1;
+        rn = "unknown";
+        break;
+    }
+#if defined MIPS_DEBUG_DISAS
+    if (loglevel & CPU_LOG_TB_IN_ASM) {
+        fprintf(logfile, "%08x mtc0 %s %08x => %08x (%d %08x)\n",
+                env->PC, rn, T0, val, reg, old);
+    }
+#endif
+    return;
+}
+#endif
+
 /* TLB management */
 #if defined(MIPS_USES_R4K_TLB)
 static void invalidate_tb (int idx)
diff -purN rpm-root/packages/BUILD/qemu-0.8.0/target-mips/op_mem.c 
qemu-0.8.0/target-mips/op_mem.c
--- rpm-root/packages/BUILD/qemu-0.8.0/target-mips/op_mem.c     2006-04-18 
17:01:50.000000000 +0200
+++ qemu-0.8.0/target-mips/op_mem.c     2006-04-18 15:45:34.000000000 +0200
@@ -124,3 +124,16 @@ void glue(op_sc, MEMSUFFIX) (void)
     }
     RETURN();
 }
+
+#ifdef MIPS_USES_FPU
+void glue(op_lwc1, MEMSUFFIX) (void)
+{
+    FT0 = glue(ldfl, MEMSUFFIX)(T0);
+    RETURN();
+}
+void glue(op_swc1, MEMSUFFIX) (void)
+{
+    glue(stfl, MEMSUFFIX)(T0, FT1);
+    RETURN();
+}
+#endif
diff -purN rpm-root/packages/BUILD/qemu-0.8.0/target-mips/translate.c 
qemu-0.8.0/target-mips/translate.c
--- rpm-root/packages/BUILD/qemu-0.8.0/target-mips/translate.c  2006-04-18 
17:01:50.000000000 +0200
+++ qemu-0.8.0/target-mips/translate.c  2006-04-18 16:53:01.000000000 +0200
@@ -218,6 +218,14 @@ enum {
     OPC_WAIT     = 0x20 | EXT_CP0,
 };
 
+#ifdef MIPS_USES_FPU
+enum {
+    /* Coprocessor 1 (FPU) */
+    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",
@@ -249,6 +257,35 @@ GEN32(gen_op_load_gpr_T2, gen_op_load_gp
 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_FT0, gen_op_load_fpr_FT0_fpr);
+FGEN32(gen_op_load_fpr_FT1, gen_op_load_fpr_FT1_fpr);
+
+FGEN32(gen_op_store_FT0_fpr, gen_op_store_FT0_fpr_fpr);
+FGEN32(gen_op_store_FT1_fpr, gen_op_store_FT1_fpr_fpr);
+#endif
+
 typedef struct DisasContext {
     struct TranslationBlock *tb;
     target_ulong pc, saved_pc;
@@ -313,6 +350,20 @@ do {                                    
     }                                                                         \
 } 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(glue(gen_op_store_, FTn),_fpr)(Fn);                                  \
+} while (0)
+
+#endif
+
 static inline void save_cpu_state (DisasContext *ctx, int do_save_pc)
 {
 #if defined MIPS_DEBUG_DISAS
@@ -399,6 +450,10 @@ OP_LD_TABLE(bu);
 OP_ST_TABLE(b);
 OP_LD_TABLE(l);
 OP_ST_TABLE(c);
+#ifdef MIPS_USES_FPU
+OP_LD_TABLE(wc1);
+OP_ST_TABLE(wc1);
+#endif
 
 /* Load and store */
 static void gen_ldst (DisasContext *ctx, uint16_t opc, int rt,
@@ -558,6 +613,45 @@ static void gen_ldst (DisasContext *ctx,
     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, FT0);
+        opn = "lwc1";
+        break;
+    case OPC_SWC1:
+        GEN_LOAD_FREG_FTN(FT1, ft);
+        op_ldst(swc1);
+        opn = "swc1";
+        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)
@@ -1274,6 +1368,54 @@ static void gen_cp0 (DisasContext *ctx, 
 }
 
 /* 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_CFC1:
+        if (fs != 0 && fs != 31) {
+            MIPS_INVAL("cp1");
+            generate_exception(ctx, EXCP_RI);
+            return;
+        }
+        gen_op_cfc1(fs);
+        gen_op_store_T0_gpr(rt);
+        opn = "cfc1";
+        break;
+    case OPC_CTC1:
+        if (fs != 0 && fs != 31) {
+            MIPS_INVAL("cp1");
+            generate_exception(ctx, EXCP_RI);
+            return;
+        }
+        /* If we get an exception, we want to restart at next instruction */
+        ctx->pc += 4;
+        save_cpu_state(ctx, 1);
+        ctx->pc -= 4;
+        GEN_LOAD_REG_TN(T0, rt);
+        gen_op_ctc1(fs);
+        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 %d", opn, regnames[rt], rd);
+}
 
 /* ISA extensions */
 /* MIPS16 extension to MIPS32 */
@@ -1450,7 +1592,7 @@ static void decode_opc (DisasContext *ct
         }
         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:
@@ -1508,14 +1650,32 @@ static void decode_opc (DisasContext *ct
     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)
+printf("opcode = %x\n", ctx->opcode);                  
+        op1 = ((ctx->opcode >> 21) & 0x1F);
+        switch (op1) {
+        case 0x02: /* cfc1 */
+        case 0x06: /* ctc1 */
+            gen_cp1(ctx, op1 | EXT_CP1, rt, rd);
+            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 */
_______________________________________________
Qemu-devel mailing list
Qemu-devel@nongnu.org
http://lists.nongnu.org/mailman/listinfo/qemu-devel

Reply via email to