* [Qemu-devel] [PATCH][MIPS] FPU support for MIPS
@ 2006-04-26 14:26 Marius Groeger
2006-04-26 22:17 ` Fabrice Bellard
0 siblings, 1 reply; 7+ messages in thread
From: Marius Groeger @ 2006-04-26 14:26 UTC (permalink / raw)
To: qemu-devel
[-- Attachment #1: Type: TEXT/PLAIN, Size: 603 bytes --]
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 <mgroeger@sysgo.com>
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
[-- Attachment #2: Type: TEXT/PLAIN, Size: 36110 bytes --]
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
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [Qemu-devel] [PATCH][MIPS] FPU support for MIPS
2006-04-26 14:26 Marius Groeger
@ 2006-04-26 22:17 ` Fabrice Bellard
2006-04-27 10:22 ` Marius Groeger
0 siblings, 1 reply; 7+ messages in thread
From: Fabrice Bellard @ 2006-04-26 22:17 UTC (permalink / raw)
To: qemu-devel
A few remarks:
1) Why do you use 3 temporaries ? Maybe two suffice in most cases.
2) do_cmp_d() should be completely decoded at translation time.
3) I suspect the macro FPR() does too many things at runtime which gives
an important performance loss. CP0St_FR should be known at translation time.
You should use CONFIG_SOFTFLOAT to validate your code. The ARM target
does it, so it works (see the configure script).
Fabrice.
Marius Groeger wrote:
> 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
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [Qemu-devel] [PATCH][MIPS] FPU support for MIPS
2006-04-26 22:17 ` Fabrice Bellard
@ 2006-04-27 10:22 ` Marius Groeger
0 siblings, 0 replies; 7+ messages in thread
From: Marius Groeger @ 2006-04-27 10:22 UTC (permalink / raw)
To: qemu-devel
Hi Fabrice,
thanks for the review.
On Thu, 27 Apr 2006, Fabrice Bellard wrote:
> 1) Why do you use 3 temporaries ? Maybe two suffice in most cases.
Well, I less temporaries don't gain performance, while a
FDT2 = FDT0 * FDT1
fpu_dump_state()
allows for easier debugging. Therefore I'd rather keep three temps,
but I can change that if you think it's too much bloat.
> 2) do_cmp_d() should be completely decoded at translation time.
Hm. Yeah. I just don't quite now about the limitations of generated
code. In the !CONFIG_SOFTFLOAT case, the softfloat-native will use
compiler/libc defined stuff like isgreater(), libm, whatever. Is is
really safe to assume all that code can be generated? I felt better
with a CALL_FROM_TB(). Maybe you can enlighten me a bit there.
Also, is a call really that much slower? It might even benefit from
the called function being in the host cpu cache already. Any figures
on that available?
> 3) I suspect the macro FPR() does too many things at runtime which gives an
> important performance loss. CP0St_FR should be known at translation time.
I'll see what I can do.
> You should use CONFIG_SOFTFLOAT to validate your code. The ARM target does
> it, so it works (see the configure script).
Ok, I got pretty weird errors in the floatx80 area and it kind of
looked like this is broken. I must have missed something then.
While there: I noticed it's very difficult at best to properly emulate
floating point states and exceptions when using native float; the
softfloat has a nice interface for that stuff. How's the world order
to handle this?
Thanks,
Marius
--
Marius Groeger <mgroeger@sysgo.com>
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
^ permalink raw reply [flat|nested] 7+ messages in thread
* [Qemu-devel] [PATCH][MIPS] FPU support for MIPS
@ 2006-05-02 14:01 Marius Groeger
2006-05-02 14:20 ` Marius Groeger
0 siblings, 1 reply; 7+ messages in thread
From: Marius Groeger @ 2006-05-02 14:01 UTC (permalink / raw)
To: qemu-devel
[-- Attachment #1: Type: TEXT/PLAIN, Size: 675 bytes --]
Hi again,
again, a current version of my FPU patch for MIPS. Fabrice, I tried to
implement all your suggestions you made for the previous version. The
patch seems now a lot cleaner to me, easier to extend, and will now
also handle IEEE single floating point (.s) format. Also, the
exception handling of unimplemented arith operations/formats is now
more constistent. I also verified the implementation with
CONFIG_SOFTFLOAT.
Regards,
Marius
--
Marius Groeger <mgroeger@sysgo.com>
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
[-- Attachment #2: Type: TEXT/PLAIN, Size: 43046 bytes --]
Index: target-mips/cpu.h
===================================================================
RCS file: /sources/qemu/qemu/target-mips/cpu.h,v
retrieving revision 1.6
diff -u -r1.6 cpu.h
--- target-mips/cpu.h 11 Mar 2006 16:23:39 -0000 1.6
+++ target-mips/cpu.h 2 May 2006 13:51:32 -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,41 @@
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) ((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 +99,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 +167,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 -r1.7 exec.h
--- target-mips/exec.h 11 Mar 2006 15:00:08 -0000 1.7
+++ target-mips/exec.h 2 May 2006 13:51:32 -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,13 @@
void do_tlbwr (void);
void do_tlbp (void);
void do_tlbr (void);
+#ifdef MIPS_USES_FPU
+void dump_fpu(CPUState *env);
+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 -r1.1 mips-defs.h
--- target-mips/mips-defs.h 2 Jul 2005 14:57:14 -0000 1.1
+++ target-mips/mips-defs.h 2 May 2006 13:51:32 -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 -r1.7 op.c
--- target-mips/op.c 23 Apr 2006 15:23:48 -0000 1.7
+++ target-mips/op.c 2 May 2006 13:51:32 -0000
@@ -2,6 +2,7 @@
* MIPS emulation micro-operations for qemu.
*
* Copyright (c) 2004-2005 Jocelyn Mayer
+ * Copyright (c) 2006 Marius Groeger (FPU operations)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -149,6 +150,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 +668,259 @@
RETURN();
}
+#ifdef MIPS_USES_FPU
+
+#if 0
+# define DEBUG_FPU_STATE() CALL_FROM_TB1(dump_fpu, env)
+#else
+# define DEBUG_FPU_STATE() do { } while(0)
+#endif
+
+/* CP1 functions */
+void op_cfc1 (void)
+{
+ if (T1 == 0)
+ T0 = env->fcr0;
+ else
+ T0 = env->fcr31;
+ DEBUG_FPU_STATE();
+ 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
+ }
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+
+void op_mfc1 (void)
+{
+ T0 = WT0;
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+
+void op_mtc1 (void)
+{
+ WT0 = T0;
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+
+/* Float support.
+ Single precition routines have a "s" suffix, double precision a
+ "d" suffix. */
+
+#define FLOAT_OP(name, p) void OPPROTO op_float_##name##_##p(void)
+
+FLOAT_OP(cvtd, w)
+{
+ FDT2 = int32_to_float64(WT0, &env->fp_status);
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+FLOAT_OP(cvts, w)
+{
+ FST2 = int32_to_float32(WT0, &env->fp_status);
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+
+FLOAT_OP(truncw, d)
+{
+ WT2 = float64_to_int32_round_to_zero(FDT0, &env->fp_status);
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+FLOAT_OP(truncw, s)
+{
+ WT2 = float32_to_int32_round_to_zero(FST0, &env->fp_status);
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+
+FLOAT_OP(roundw, d)
+{
+ WT2 = float64_round_to_int(FDT0, &env->fp_status);
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+FLOAT_OP(roundw, s)
+{
+ WT2 = float32_round_to_int(FST0, &env->fp_status);
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+
+/* binary operations */
+#define FLOAT_BINOP(name) \
+FLOAT_OP(name, d) \
+{ \
+ FDT2 = float64_ ## name (FDT0, FDT1, &env->fp_status); \
+ DEBUG_FPU_STATE(); \
+} \
+FLOAT_OP(name, s) \
+{ \
+ FST2 = float32_ ## name (FST0, FST1, &env->fp_status); \
+ DEBUG_FPU_STATE(); \
+}
+FLOAT_BINOP(add)
+FLOAT_BINOP(sub)
+FLOAT_BINOP(mul)
+FLOAT_BINOP(div)
+#undef FLOAT_BINOP
+
+/* unary operations, modifying fp status */
+#define FLOAT_UNOP(name) \
+FLOAT_OP(name, d) \
+{ \
+ FDT2 = float64_ ## name(FDT0, &env->fp_status); \
+ DEBUG_FPU_STATE(); \
+} \
+FLOAT_OP(name, s) \
+{ \
+ FST2 = float32_ ## name(FST0, &env->fp_status); \
+ DEBUG_FPU_STATE(); \
+}
+FLOAT_UNOP(sqrt)
+#undef FLOAT_UNOP
+
+/* unary operations, not modifying fp status */
+#define FLOAT_UNOP(name) \
+FLOAT_OP(name, d) \
+{ \
+ FDT2 = float64_ ## name(FDT0); \
+ DEBUG_FPU_STATE(); \
+} \
+FLOAT_OP(name, s) \
+{ \
+ FST2 = float32_ ## name(FST0); \
+ DEBUG_FPU_STATE(); \
+}
+FLOAT_UNOP(abs)
+FLOAT_UNOP(chs)
+#undef FLOAT_UNOP
+
+FLOAT_OP(mov, d)
+{
+ FDT2 = FDT0;
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+FLOAT_OP(mov, s)
+{
+ FST2 = FST0;
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+
+#define FOP_COND(fmt, op, cond) \
+void op_cmp_ ## fmt ## _ ## op (void) \
+{ \
+ int c = (cond); \
+ SET_FP_COND(env->fcr31, c); \
+ DEBUG_FPU_STATE(); \
+ RETURN(); \
+}
+
+#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)
+FOP_COND(d, f, 0) /* false */
+FOP_COND(d, un, f_isunordered(FDT1, FDT0)) /* unordered */
+FOP_COND(d, eq, f_isequal(FDT0, FDT1)) /* equal */
+FOP_COND(d, ueq, (f_isunordered(FDT1, FDT0) || f_isequal(FDT0, FDT1))) /* unordered or equal */
+FOP_COND(d, olt, (!f_isunordered(FDT1, FDT0) && f_isless(FDT0, FDT1))) /* ordered less than */
+FOP_COND(d, ult, (f_isunordered(FDT1, FDT0) || f_isless(FDT0, FDT1))) /* unordered or less than */
+FOP_COND(d, ole, (!f_isunordered(FDT1, FDT0) && f_islessequal(FDT0, FDT1))) /* ordered less than or equal */
+FOP_COND(d, ule, (f_isunordered(FDT1, FDT0) || f_islessequal(FDT0, FDT1))) /* unordered or less than or equal */
+FOP_COND(d, sf, 0) /* signaling false */
+FOP_COND(d, ngle, (!f_isgreater(FDT1, FDT0) || f_islessequal(FDT0, FDT1))) /* not greater or less than or equal */
+FOP_COND(d, seq, f_isequalsig(FDT1, FDT0)) /* signaling equal */
+FOP_COND(d, ngl, (!f_isgreater(FDT1, FDT0) || f_isless(FDT0, FDT1))) /* not greater than or less than */
+FOP_COND(d, lt, f_isless(FDT0, FDT1)) /* lt */
+FOP_COND(d, nge, (!f_isgreater(FDT1, FDT0) || f_isequal(FDT0, FDT1))) /* not greater than or equal */
+FOP_COND(d, le, (f_isless(FDT1, FDT0) || f_isequal(FDT0, FDT1))) /* less than or equal */
+FOP_COND(d, ngt, !f_isgreater(FDT1, FDT0)) /* not greater than */
+#undef f_isunordered
+#undef f_isequal
+#undef f_islessequal
+#undef f_isless
+#undef f_isgreater
+#undef f_isequalsig
+
+#define f_isunordered(a, b) \
+ (float32_is_signaling_nan(a) || float32_is_signaling_nan(b))
+#define f_isequal(a, b) float32_eq(a, b, &env->fp_status)
+#define f_islessequal(a, b) float32_le(a, b, &env->fp_status)
+#define f_isless(a, b) float32_lt(a, b, &env->fp_status)
+#define f_isgreater(a, b) !f_islessequal(a, b)
+#define f_isequalsig(a, b) float32_eq_signaling(a, b, &env->fp_status)
+FOP_COND(s, f, 0) /* false */
+FOP_COND(s, un, f_isunordered(FST1, FST0)) /* unordered */
+FOP_COND(s, eq, f_isequal(FST0, FST1)) /* equal */
+FOP_COND(s, ueq, (f_isunordered(FST1, FST0) || f_isequal(FST0, FST1))) /* unordered or equal */
+FOP_COND(s, olt, (!f_isunordered(FST1, FST0) && f_isless(FST0, FST1))) /* ordered less than */
+FOP_COND(s, ult, (f_isunordered(FST1, FST0) || f_isless(FST0, FST1))) /* unordered or less than */
+FOP_COND(s, ole, (!f_isunordered(FST1, FST0) && f_islessequal(FST0, FST1))) /* ordered less than or equal */
+FOP_COND(s, ule, (f_isunordered(FST1, FST0) || f_islessequal(FST0, FST1))) /* unordered or less than or equal */
+FOP_COND(s, sf, 0) /* signaling false */
+FOP_COND(s, ngle, (!f_isgreater(FST1, FST0) || f_islessequal(FST0, FST1))) /* not greater or less than or equal */
+FOP_COND(s, seq, f_isequalsig(FST1, FST0)) /* signaling equal */
+FOP_COND(s, ngl, (!f_isgreater(FST1, FST0) || f_isless(FST0, FST1))) /* not greater than or less than */
+FOP_COND(s, lt, f_isless(FST0, FST1)) /* lt */
+FOP_COND(s, nge, (!f_isgreater(FST1, FST0) || f_isequal(FST0, FST1))) /* not greater than or equal */
+FOP_COND(s, le, (f_isless(FST1, FST0) || f_isequal(FST0, FST1))) /* less than or equal */
+FOP_COND(s, ngt, !f_isgreater(FST1, FST0)) /* not greater than */
+#undef f_isunordered
+#undef f_isequal
+#undef f_islessequal
+#undef f_isless
+#undef f_isgreater
+#undef f_isequalsig
+
+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 /* MIPS_USES_FPU */
+
#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 -r1.11 op_helper.c
--- target-mips/op_helper.c 23 Apr 2006 18:18:10 -0000 1.11
+++ target-mips/op_helper.c 2 May 2006 13:51:32 -0000
@@ -532,6 +532,47 @@
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
+}
+#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 -r1.2 op_mem.c
--- target-mips/op_mem.c 5 Dec 2005 19:59:36 -0000 1.2
+++ target-mips/op_mem.c 2 May 2006 13:51:32 -0000
@@ -61,6 +61,18 @@
RETURN();
}
+void glue(op_lwu, MEMSUFFIX) (void)
+{
+ T0 = glue(ldl, MEMSUFFIX)(T0);
+ 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 +130,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 -r1.12 translate.c
--- target-mips/translate.c 23 Apr 2006 15:21:24 -0000 1.12
+++ target-mips/translate.c 2 May 2006 13:51:32 -0000
@@ -2,6 +2,7 @@
* MIPS32 emulation for qemu: main translation routines.
*
* Copyright (c) 2004-2005 Jocelyn Mayer
+ * Copyright (c) 2006 Marius Groeger (FPU operations)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -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,76 @@
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);
+
+#define FOP_CONDS(fmt) \
+static GenOpFunc * cond_ ## fmt ## _table[16] = { \
+ gen_op_cmp_ ## fmt ## _f, \
+ gen_op_cmp_ ## fmt ## _un, \
+ gen_op_cmp_ ## fmt ## _eq, \
+ gen_op_cmp_ ## fmt ## _ueq, \
+ gen_op_cmp_ ## fmt ## _olt, \
+ gen_op_cmp_ ## fmt ## _ult, \
+ gen_op_cmp_ ## fmt ## _ole, \
+ gen_op_cmp_ ## fmt ## _ule, \
+ gen_op_cmp_ ## fmt ## _sf, \
+ gen_op_cmp_ ## fmt ## _ngle, \
+ gen_op_cmp_ ## fmt ## _seq, \
+ gen_op_cmp_ ## fmt ## _ngl, \
+ gen_op_cmp_ ## fmt ## _lt, \
+ gen_op_cmp_ ## fmt ## _nge, \
+ gen_op_cmp_ ## fmt ## _le, \
+ gen_op_cmp_ ## fmt ## _ngt, \
+}; \
+static inline void gen_cmp_ ## fmt(int n) \
+{ \
+ cond_ ## fmt ## _table[n](); \
+}
+
+FOP_CONDS(d)
+FOP_CONDS(s)
+
+#endif
+
typedef struct DisasContext {
struct TranslationBlock *tb;
target_ulong pc, saved_pc;
@@ -312,6 +393,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
@@ -397,6 +492,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,
@@ -551,6 +652,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)
@@ -1180,7 +1330,7 @@
const unsigned char *opn = "unk";
if (!(ctx->CP0_Status & (1 << CP0St_CU0)) &&
- (ctx->hflags & MIPS_HFLAG_UM) &&
+ !(ctx->hflags & MIPS_HFLAG_UM) &&
!(ctx->hflags & MIPS_HFLAG_ERL) &&
!(ctx->hflags & MIPS_HFLAG_EXL)) {
if (loglevel & CPU_LOG_TB_IN_ASM) {
@@ -1189,6 +1339,7 @@
generate_exception_err (ctx, EXCP_CpU, 0);
return;
}
+
switch (opc) {
case OPC_MFC0:
if (rt == 0) {
@@ -1265,7 +1416,383 @@
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]);
+}
+
+/* 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.
+ *
+ * Multiple float 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)
+
+#define FOP(func, fmt) (((fmt) << 21) | (func))
+
+static void gen_farith (DisasContext *ctx, int fmt, int ft, int fs, int fd, int func)
+{
+ const unsigned char *opn = "unk";
+ const char *condnames[] = {
+ "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",
+ };
+ int binary = 0;
+
+ switch (ctx->opcode & FOP(0x3f, 0x1f)) {
+ case FOP(0, 17):
+ CHECK_FR(ctx, fs | ft | fd);
+ GEN_LOAD_FREG_FTN(DT0, fs);
+ GEN_LOAD_FREG_FTN(DT1, ft);
+ gen_op_float_add_d();
+ GEN_STORE_FTN_FREG(fd, DT2);
+ opn = "add.d";
+ binary = 1;
+ break;
+ case FOP(1, 17):
+ CHECK_FR(ctx, fs | ft | fd);
+ GEN_LOAD_FREG_FTN(DT0, fs);
+ GEN_LOAD_FREG_FTN(DT1, ft);
+ gen_op_float_sub_d();
+ GEN_STORE_FTN_FREG(fd, DT2);
+ opn = "sub.d";
+ binary = 1;
+ break;
+ case FOP(2, 17):
+ CHECK_FR(ctx, fs | ft | fd);
+ GEN_LOAD_FREG_FTN(DT0, fs);
+ GEN_LOAD_FREG_FTN(DT1, ft);
+ gen_op_float_mul_d();
+ GEN_STORE_FTN_FREG(fd, DT2);
+ opn = "mul.d";
+ binary = 1;
+ break;
+ case FOP(3, 17):
+ CHECK_FR(ctx, fs | ft | fd);
+ GEN_LOAD_FREG_FTN(DT0, fs);
+ GEN_LOAD_FREG_FTN(DT1, ft);
+ gen_op_float_div_d();
+ GEN_STORE_FTN_FREG(fd, DT2);
+ opn = "div.d";
+ binary = 1;
+ break;
+ case FOP(4, 17):
+ CHECK_FR(ctx, fs | fd);
+ GEN_LOAD_FREG_FTN(DT0, fs);
+ gen_op_float_sqrt_d();
+ GEN_STORE_FTN_FREG(fd, DT2);
+ opn = "sqrt.d";
+ break;
+ case FOP(5, 17):
+ CHECK_FR(ctx, fs | fd);
+ GEN_LOAD_FREG_FTN(DT0, fs);
+ gen_op_float_abs_d();
+ GEN_STORE_FTN_FREG(fd, DT2);
+ opn = "abs.d";
+ break;
+ case FOP(6, 17):
+ CHECK_FR(ctx, fs | fd);
+ GEN_LOAD_FREG_FTN(DT0, fs);
+ gen_op_float_mov_d();
+ GEN_STORE_FTN_FREG(fd, DT2);
+ opn = "mov.d";
+ break;
+ case FOP(7, 17):
+ CHECK_FR(ctx, fs | fd);
+ GEN_LOAD_FREG_FTN(DT0, fs);
+ gen_op_float_chs_d();
+ GEN_STORE_FTN_FREG(fd, DT2);
+ opn = "neg.d";
+ break;
+ case FOP(12, 17):
+ CHECK_FR(ctx, fs | fd);
+ GEN_LOAD_FREG_FTN(DT0, fs);
+ gen_op_float_roundw_d();
+ GEN_STORE_FTN_FREG(fd, WT2);
+ opn = "round.w.d";
+ break;
+ case FOP(13, 17):
+ CHECK_FR(ctx, fs | fd);
+ GEN_LOAD_FREG_FTN(DT0, fs);
+ gen_op_float_truncw_d();
+ GEN_STORE_FTN_FREG(fd, WT2);
+ opn = "trunc.w.d";
+ break;
+ case FOP(33, 20): /* cvt.d.w */
+ CHECK_FR(ctx, fs | 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(48, 17):
+ case FOP(49, 17):
+ case FOP(50, 17):
+ case FOP(51, 17):
+ case FOP(52, 17):
+ case FOP(53, 17):
+ case FOP(54, 17):
+ case FOP(55, 17):
+ case FOP(56, 17):
+ case FOP(57, 17):
+ case FOP(58, 17):
+ case FOP(59, 17):
+ case FOP(60, 17):
+ case FOP(61, 17):
+ case FOP(62, 17):
+ case FOP(63, 17):
+ CHECK_FR(ctx, fs | ft);
+ GEN_LOAD_FREG_FTN(DT0, fs);
+ GEN_LOAD_FREG_FTN(DT1, ft);
+ gen_cmp_d(func-48);
+ opn = condnames[func-48];
+ break;
+ case FOP(0, 16):
+ CHECK_FR(ctx, fs | ft | fd);
+ 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):
+ CHECK_FR(ctx, fs | ft | fd);
+ 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):
+ CHECK_FR(ctx, fs | ft | fd);
+ 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):
+ CHECK_FR(ctx, fs | ft | fd);
+ 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):
+ CHECK_FR(ctx, fs | fd);
+ 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):
+ CHECK_FR(ctx, fs | fd);
+ 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):
+ CHECK_FR(ctx, fs | fd);
+ 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):
+ CHECK_FR(ctx, fs | fd);
+ GEN_LOAD_FREG_FTN(WT0, fs);
+ gen_op_float_chs_s();
+ GEN_STORE_FTN_FREG(fd, WT2);
+ opn = "neg.s";
+ break;
+ case FOP(12, 16):
+ CHECK_FR(ctx, fs | fd);
+ 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):
+ CHECK_FR(ctx, fs | fd);
+ GEN_LOAD_FREG_FTN(WT0, fs);
+ gen_op_float_truncw_s();
+ GEN_STORE_FTN_FREG(fd, WT2);
+ opn = "trunc.w.s";
+ break;
+ case FOP(32, 20): /* cvt.s.w */
+ CHECK_FR(ctx, fs | fd);
+ GEN_LOAD_FREG_FTN(WT0, fs);
+ gen_op_float_cvts_w();
+ GEN_STORE_FTN_FREG(fd, WT2);
+ opn = "cvt.s.w";
+ 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):
+ CHECK_FR(ctx, fs | ft);
+ GEN_LOAD_FREG_FTN(WT0, fs);
+ GEN_LOAD_FREG_FTN(WT1, ft);
+ gen_cmp_s(func-48);
+ opn = condnames[func-48];
+ break;
+ 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;
+ }
+ if (binary)
+ MIPS_DEBUG("%s %s, %s, %s", opn, fregnames[fd], fregnames[fs], fregnames[ft]);
+ else
+ MIPS_DEBUG("%s %s,%s", opn, fregnames[fd], fregnames[fs]);
+}
+#endif
/* ISA extensions */
/* MIPS16 extension to MIPS32 */
@@ -1436,7 +1963,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:
@@ -1495,14 +2022,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 +2091,7 @@
generate_exception(ctx, EXCP_RI);
break;
}
+
if (ctx->hflags & MIPS_HFLAG_BMASK) {
int hflags = ctx->hflags;
/* Branches completion */
@@ -1722,6 +2278,45 @@
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 fs:%g\n", \
+ (fp)->w, (fp)->u32[0], (fp)->u32[1], (fp)->fd, (fp)->fs); \
+ } 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, "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
+}
+
+void dump_fpu(CPUState *env)
+{
+ if (loglevel) {
+ fprintf(logfile, "pc=0x%08x HI=0x%08x LO=0x%08x ds %04x %08x %d\n",
+ env->PC, env->HI, env->LO, env->hflags, env->btarget, env->bcond);
+ fpu_dump_state(env, logfile, fprintf, 0);
+ }
+}
+
+#endif
+
void cpu_dump_state (CPUState *env, FILE *f,
int (*cpu_fprintf)(FILE *f, const char *fmt, ...),
int flags)
@@ -1751,6 +2346,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-05-01 19:23:51.000000000 +0200
@@ -0,0 +1,81 @@
+/*
+ * 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)
+
+#define SET_RESET(treg, tregname) \
+ void glue(op_set, tregname)(void) \
+ { \
+ treg = PARAM1; \
+ RETURN(); \
+ } \
+ void glue(op_reset, tregname)(void) \
+ { \
+ treg = 0; \
+ RETURN(); \
+ } \
+
+SET_RESET(WT0, _WT0)
+SET_RESET(WT1, _WT1)
+SET_RESET(WT2, _WT2)
+SET_RESET(DT0, _DT0)
+SET_RESET(DT1, _DT1)
+SET_RESET(DT2, _DT2)
+
+#endif
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [Qemu-devel] [PATCH][MIPS] FPU support for MIPS
2006-05-02 14:01 [Qemu-devel] [PATCH][MIPS] FPU support for MIPS Marius Groeger
@ 2006-05-02 14:20 ` Marius Groeger
[not found] ` <20060502143520.GB9551@lebarbe.net>
0 siblings, 1 reply; 7+ messages in thread
From: Marius Groeger @ 2006-05-02 14:20 UTC (permalink / raw)
To: qemu-devel
On Tue, 2 May 2006, Marius Groeger wrote:
> again, a current version of my FPU patch for MIPS. Fabrice, I tried to
Sorry, hunk #1 of the target-mips/op_mem.c patch got out wrong. (I
wanted to remove other feature patches[1] first and seemed to have
messed up in doing so.) Just delete this hunk before appling the
patch.
Sorry and Best Regards
Marius
[1] qemu-0.8.0-mips-cp0.patch,
qemu-0.8.0-mips-lwu.patch,
qemu-0.8.0-mips-aligned-pc.patch
--
Marius Groeger <mgroeger@sysgo.com>
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
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [Qemu-devel] [PATCH][MIPS] FPU support for MIPS
[not found] ` <20060502143520.GB9551@lebarbe.net>
@ 2006-05-03 8:19 ` Marius Groeger
0 siblings, 0 replies; 7+ messages in thread
From: Marius Groeger @ 2006-05-03 8:19 UTC (permalink / raw)
To: qemu-devel
[-- Attachment #1: Type: TEXT/PLAIN, Size: 626 bytes --]
On Tue, 2 May 2006, Flavien wrote:
>> Sorry, hunk #1 of the target-mips/op_mem.c patch got out wrong.
>
> Please re-diff against CVS and re-send it to the list.
>
> BTW, fix the indentation to be consistent with the rest of the code.
> One level is 4 spaces. Not 8, not tab.
>
> That'll be easier for everyone. Thanks !
Here's the patch again, rediffed and untabified as requested.
Thanks,
Marius
--
Marius Groeger <mgroeger@sysgo.com>
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
[-- Attachment #2: Type: TEXT/PLAIN, Size: 43774 bytes --]
Index: target-mips/cpu.h
===================================================================
RCS file: /sources/qemu/qemu/target-mips/cpu.h,v
retrieving revision 1.6
diff -u -r1.6 cpu.h
--- target-mips/cpu.h 11 Mar 2006 16:23:39 -0000 1.6
+++ target-mips/cpu.h 2 May 2006 15:14:04 -0000
@@ -10,9 +10,11 @@
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 +45,41 @@
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) ((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 +98,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 +166,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 -r1.7 exec.h
--- target-mips/exec.h 11 Mar 2006 15:00:08 -0000 1.7
+++ target-mips/exec.h 2 May 2006 15:14:04 -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,13 @@
void do_tlbwr (void);
void do_tlbp (void);
void do_tlbr (void);
+#ifdef MIPS_USES_FPU
+void dump_fpu(CPUState *env);
+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 -r1.1 mips-defs.h
--- target-mips/mips-defs.h 2 Jul 2005 14:57:14 -0000 1.1
+++ target-mips/mips-defs.h 2 May 2006 15:14:04 -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 -r1.7 op.c
--- target-mips/op.c 23 Apr 2006 15:23:48 -0000 1.7
+++ target-mips/op.c 2 May 2006 15:14:04 -0000
@@ -2,6 +2,7 @@
* MIPS emulation micro-operations for qemu.
*
* Copyright (c) 2004-2005 Jocelyn Mayer
+ * Copyright (c) 2006 Marius Groeger (FPU operations)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -149,6 +150,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 +668,259 @@
RETURN();
}
+#ifdef MIPS_USES_FPU
+
+#if 0
+# define DEBUG_FPU_STATE() CALL_FROM_TB1(dump_fpu, env)
+#else
+# define DEBUG_FPU_STATE() do { } while(0)
+#endif
+
+/* CP1 functions */
+void op_cfc1 (void)
+{
+ if (T1 == 0)
+ T0 = env->fcr0;
+ else
+ T0 = env->fcr31;
+ DEBUG_FPU_STATE();
+ 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
+ }
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+
+void op_mfc1 (void)
+{
+ T0 = WT0;
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+
+void op_mtc1 (void)
+{
+ WT0 = T0;
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+
+/* Float support.
+ Single precition routines have a "s" suffix, double precision a
+ "d" suffix. */
+
+#define FLOAT_OP(name, p) void OPPROTO op_float_##name##_##p(void)
+
+FLOAT_OP(cvtd, w)
+{
+ FDT2 = int32_to_float64(WT0, &env->fp_status);
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+FLOAT_OP(cvts, w)
+{
+ FST2 = int32_to_float32(WT0, &env->fp_status);
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+
+FLOAT_OP(truncw, d)
+{
+ WT2 = float64_to_int32_round_to_zero(FDT0, &env->fp_status);
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+FLOAT_OP(truncw, s)
+{
+ WT2 = float32_to_int32_round_to_zero(FST0, &env->fp_status);
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+
+FLOAT_OP(roundw, d)
+{
+ WT2 = float64_round_to_int(FDT0, &env->fp_status);
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+FLOAT_OP(roundw, s)
+{
+ WT2 = float32_round_to_int(FST0, &env->fp_status);
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+
+/* binary operations */
+#define FLOAT_BINOP(name) \
+FLOAT_OP(name, d) \
+{ \
+ FDT2 = float64_ ## name (FDT0, FDT1, &env->fp_status); \
+ DEBUG_FPU_STATE(); \
+} \
+FLOAT_OP(name, s) \
+{ \
+ FST2 = float32_ ## name (FST0, FST1, &env->fp_status); \
+ DEBUG_FPU_STATE(); \
+}
+FLOAT_BINOP(add)
+FLOAT_BINOP(sub)
+FLOAT_BINOP(mul)
+FLOAT_BINOP(div)
+#undef FLOAT_BINOP
+
+/* unary operations, modifying fp status */
+#define FLOAT_UNOP(name) \
+FLOAT_OP(name, d) \
+{ \
+ FDT2 = float64_ ## name(FDT0, &env->fp_status); \
+ DEBUG_FPU_STATE(); \
+} \
+FLOAT_OP(name, s) \
+{ \
+ FST2 = float32_ ## name(FST0, &env->fp_status); \
+ DEBUG_FPU_STATE(); \
+}
+FLOAT_UNOP(sqrt)
+#undef FLOAT_UNOP
+
+/* unary operations, not modifying fp status */
+#define FLOAT_UNOP(name) \
+FLOAT_OP(name, d) \
+{ \
+ FDT2 = float64_ ## name(FDT0); \
+ DEBUG_FPU_STATE(); \
+} \
+FLOAT_OP(name, s) \
+{ \
+ FST2 = float32_ ## name(FST0); \
+ DEBUG_FPU_STATE(); \
+}
+FLOAT_UNOP(abs)
+FLOAT_UNOP(chs)
+#undef FLOAT_UNOP
+
+FLOAT_OP(mov, d)
+{
+ FDT2 = FDT0;
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+FLOAT_OP(mov, s)
+{
+ FST2 = FST0;
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+
+#define FOP_COND(fmt, op, cond) \
+void op_cmp_ ## fmt ## _ ## op (void) \
+{ \
+ int c = (cond); \
+ SET_FP_COND(env->fcr31, c); \
+ DEBUG_FPU_STATE(); \
+ RETURN(); \
+}
+
+#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)
+FOP_COND(d, f, 0) /* false */
+FOP_COND(d, un, f_isunordered(FDT1, FDT0)) /* unordered */
+FOP_COND(d, eq, f_isequal(FDT0, FDT1)) /* equal */
+FOP_COND(d, ueq, (f_isunordered(FDT1, FDT0) || f_isequal(FDT0, FDT1))) /* unordered or equal */
+FOP_COND(d, olt, (!f_isunordered(FDT1, FDT0) && f_isless(FDT0, FDT1))) /* ordered less than */
+FOP_COND(d, ult, (f_isunordered(FDT1, FDT0) || f_isless(FDT0, FDT1))) /* unordered or less than */
+FOP_COND(d, ole, (!f_isunordered(FDT1, FDT0) && f_islessequal(FDT0, FDT1))) /* ordered less than or equal */
+FOP_COND(d, ule, (f_isunordered(FDT1, FDT0) || f_islessequal(FDT0, FDT1))) /* unordered or less than or equal */
+FOP_COND(d, sf, 0) /* signaling false */
+FOP_COND(d, ngle, (!f_isgreater(FDT1, FDT0) || f_islessequal(FDT0, FDT1))) /* not greater or less than or equal */
+FOP_COND(d, seq, f_isequalsig(FDT1, FDT0)) /* signaling equal */
+FOP_COND(d, ngl, (!f_isgreater(FDT1, FDT0) || f_isless(FDT0, FDT1))) /* not greater than or less than */
+FOP_COND(d, lt, f_isless(FDT0, FDT1)) /* lt */
+FOP_COND(d, nge, (!f_isgreater(FDT1, FDT0) || f_isequal(FDT0, FDT1))) /* not greater than or equal */
+FOP_COND(d, le, (f_isless(FDT1, FDT0) || f_isequal(FDT0, FDT1))) /* less than or equal */
+FOP_COND(d, ngt, !f_isgreater(FDT1, FDT0)) /* not greater than */
+#undef f_isunordered
+#undef f_isequal
+#undef f_islessequal
+#undef f_isless
+#undef f_isgreater
+#undef f_isequalsig
+
+#define f_isunordered(a, b) \
+ (float32_is_signaling_nan(a) || float32_is_signaling_nan(b))
+#define f_isequal(a, b) float32_eq(a, b, &env->fp_status)
+#define f_islessequal(a, b) float32_le(a, b, &env->fp_status)
+#define f_isless(a, b) float32_lt(a, b, &env->fp_status)
+#define f_isgreater(a, b) !f_islessequal(a, b)
+#define f_isequalsig(a, b) float32_eq_signaling(a, b, &env->fp_status)
+FOP_COND(s, f, 0) /* false */
+FOP_COND(s, un, f_isunordered(FST1, FST0)) /* unordered */
+FOP_COND(s, eq, f_isequal(FST0, FST1)) /* equal */
+FOP_COND(s, ueq, (f_isunordered(FST1, FST0) || f_isequal(FST0, FST1))) /* unordered or equal */
+FOP_COND(s, olt, (!f_isunordered(FST1, FST0) && f_isless(FST0, FST1))) /* ordered less than */
+FOP_COND(s, ult, (f_isunordered(FST1, FST0) || f_isless(FST0, FST1))) /* unordered or less than */
+FOP_COND(s, ole, (!f_isunordered(FST1, FST0) && f_islessequal(FST0, FST1))) /* ordered less than or equal */
+FOP_COND(s, ule, (f_isunordered(FST1, FST0) || f_islessequal(FST0, FST1))) /* unordered or less than or equal */
+FOP_COND(s, sf, 0) /* signaling false */
+FOP_COND(s, ngle, (!f_isgreater(FST1, FST0) || f_islessequal(FST0, FST1))) /* not greater or less than or equal */
+FOP_COND(s, seq, f_isequalsig(FST1, FST0)) /* signaling equal */
+FOP_COND(s, ngl, (!f_isgreater(FST1, FST0) || f_isless(FST0, FST1))) /* not greater than or less than */
+FOP_COND(s, lt, f_isless(FST0, FST1)) /* lt */
+FOP_COND(s, nge, (!f_isgreater(FST1, FST0) || f_isequal(FST0, FST1))) /* not greater than or equal */
+FOP_COND(s, le, (f_isless(FST1, FST0) || f_isequal(FST0, FST1))) /* less than or equal */
+FOP_COND(s, ngt, !f_isgreater(FST1, FST0)) /* not greater than */
+#undef f_isunordered
+#undef f_isequal
+#undef f_islessequal
+#undef f_isless
+#undef f_isgreater
+#undef f_isequalsig
+
+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 /* MIPS_USES_FPU */
+
#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 -r1.11 op_helper.c
--- target-mips/op_helper.c 23 Apr 2006 18:18:10 -0000 1.11
+++ target-mips/op_helper.c 2 May 2006 15:14:04 -0000
@@ -532,6 +532,47 @@
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
+}
+#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 -r1.2 op_mem.c
--- target-mips/op_mem.c 5 Dec 2005 19:59:36 -0000 1.2
+++ target-mips/op_mem.c 2 May 2006 15:14:04 -0000
@@ -118,3 +118,26 @@
}
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);
+ RETURN();
+}
+void glue(op_sdc1, MEMSUFFIX) (void)
+{
+ 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 -r1.12 translate.c
--- target-mips/translate.c 23 Apr 2006 15:21:24 -0000 1.12
+++ target-mips/translate.c 2 May 2006 15:14:04 -0000
@@ -2,6 +2,7 @@
* MIPS32 emulation for qemu: main translation routines.
*
* Copyright (c) 2004-2005 Jocelyn Mayer
+ * Copyright (c) 2006 Marius Groeger (FPU operations)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -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,76 @@
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);
+
+#define FOP_CONDS(fmt) \
+static GenOpFunc * cond_ ## fmt ## _table[16] = { \
+ gen_op_cmp_ ## fmt ## _f, \
+ gen_op_cmp_ ## fmt ## _un, \
+ gen_op_cmp_ ## fmt ## _eq, \
+ gen_op_cmp_ ## fmt ## _ueq, \
+ gen_op_cmp_ ## fmt ## _olt, \
+ gen_op_cmp_ ## fmt ## _ult, \
+ gen_op_cmp_ ## fmt ## _ole, \
+ gen_op_cmp_ ## fmt ## _ule, \
+ gen_op_cmp_ ## fmt ## _sf, \
+ gen_op_cmp_ ## fmt ## _ngle, \
+ gen_op_cmp_ ## fmt ## _seq, \
+ gen_op_cmp_ ## fmt ## _ngl, \
+ gen_op_cmp_ ## fmt ## _lt, \
+ gen_op_cmp_ ## fmt ## _nge, \
+ gen_op_cmp_ ## fmt ## _le, \
+ gen_op_cmp_ ## fmt ## _ngt, \
+}; \
+static inline void gen_cmp_ ## fmt(int n) \
+{ \
+ cond_ ## fmt ## _table[n](); \
+}
+
+FOP_CONDS(d)
+FOP_CONDS(s)
+
+#endif
+
typedef struct DisasContext {
struct TranslationBlock *tb;
target_ulong pc, saved_pc;
@@ -312,6 +393,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
@@ -397,6 +492,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,
@@ -551,6 +652,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)
@@ -1265,7 +1415,382 @@
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]);
+}
+
+/* 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.
+ *
+ * Multiple float 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)
+
+#define FOP(func, fmt) (((fmt) << 21) | (func))
+
+static void gen_farith (DisasContext *ctx, int fmt, int ft, int fs, int fd, int func)
+{
+ const unsigned char *opn = "unk";
+ const char *condnames[] = {
+ "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",
+ };
+ int binary = 0;
+
+ switch (ctx->opcode & FOP(0x3f, 0x1f)) {
+ case FOP(0, 17):
+ CHECK_FR(ctx, fs | ft | fd);
+ GEN_LOAD_FREG_FTN(DT0, fs);
+ GEN_LOAD_FREG_FTN(DT1, ft);
+ gen_op_float_add_d();
+ GEN_STORE_FTN_FREG(fd, DT2);
+ opn = "add.d";
+ binary = 1;
+ break;
+ case FOP(1, 17):
+ CHECK_FR(ctx, fs | ft | fd);
+ GEN_LOAD_FREG_FTN(DT0, fs);
+ GEN_LOAD_FREG_FTN(DT1, ft);
+ gen_op_float_sub_d();
+ GEN_STORE_FTN_FREG(fd, DT2);
+ opn = "sub.d";
+ binary = 1;
+ break;
+ case FOP(2, 17):
+ CHECK_FR(ctx, fs | ft | fd);
+ GEN_LOAD_FREG_FTN(DT0, fs);
+ GEN_LOAD_FREG_FTN(DT1, ft);
+ gen_op_float_mul_d();
+ GEN_STORE_FTN_FREG(fd, DT2);
+ opn = "mul.d";
+ binary = 1;
+ break;
+ case FOP(3, 17):
+ CHECK_FR(ctx, fs | ft | fd);
+ GEN_LOAD_FREG_FTN(DT0, fs);
+ GEN_LOAD_FREG_FTN(DT1, ft);
+ gen_op_float_div_d();
+ GEN_STORE_FTN_FREG(fd, DT2);
+ opn = "div.d";
+ binary = 1;
+ break;
+ case FOP(4, 17):
+ CHECK_FR(ctx, fs | fd);
+ GEN_LOAD_FREG_FTN(DT0, fs);
+ gen_op_float_sqrt_d();
+ GEN_STORE_FTN_FREG(fd, DT2);
+ opn = "sqrt.d";
+ break;
+ case FOP(5, 17):
+ CHECK_FR(ctx, fs | fd);
+ GEN_LOAD_FREG_FTN(DT0, fs);
+ gen_op_float_abs_d();
+ GEN_STORE_FTN_FREG(fd, DT2);
+ opn = "abs.d";
+ break;
+ case FOP(6, 17):
+ CHECK_FR(ctx, fs | fd);
+ GEN_LOAD_FREG_FTN(DT0, fs);
+ gen_op_float_mov_d();
+ GEN_STORE_FTN_FREG(fd, DT2);
+ opn = "mov.d";
+ break;
+ case FOP(7, 17):
+ CHECK_FR(ctx, fs | fd);
+ GEN_LOAD_FREG_FTN(DT0, fs);
+ gen_op_float_chs_d();
+ GEN_STORE_FTN_FREG(fd, DT2);
+ opn = "neg.d";
+ break;
+ case FOP(12, 17):
+ CHECK_FR(ctx, fs | fd);
+ GEN_LOAD_FREG_FTN(DT0, fs);
+ gen_op_float_roundw_d();
+ GEN_STORE_FTN_FREG(fd, WT2);
+ opn = "round.w.d";
+ break;
+ case FOP(13, 17):
+ CHECK_FR(ctx, fs | fd);
+ GEN_LOAD_FREG_FTN(DT0, fs);
+ gen_op_float_truncw_d();
+ GEN_STORE_FTN_FREG(fd, WT2);
+ opn = "trunc.w.d";
+ break;
+ case FOP(33, 20): /* cvt.d.w */
+ CHECK_FR(ctx, fs | 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(48, 17):
+ case FOP(49, 17):
+ case FOP(50, 17):
+ case FOP(51, 17):
+ case FOP(52, 17):
+ case FOP(53, 17):
+ case FOP(54, 17):
+ case FOP(55, 17):
+ case FOP(56, 17):
+ case FOP(57, 17):
+ case FOP(58, 17):
+ case FOP(59, 17):
+ case FOP(60, 17):
+ case FOP(61, 17):
+ case FOP(62, 17):
+ case FOP(63, 17):
+ CHECK_FR(ctx, fs | ft);
+ GEN_LOAD_FREG_FTN(DT0, fs);
+ GEN_LOAD_FREG_FTN(DT1, ft);
+ gen_cmp_d(func-48);
+ opn = condnames[func-48];
+ break;
+ case FOP(0, 16):
+ CHECK_FR(ctx, fs | ft | fd);
+ 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):
+ CHECK_FR(ctx, fs | ft | fd);
+ 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):
+ CHECK_FR(ctx, fs | ft | fd);
+ 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):
+ CHECK_FR(ctx, fs | ft | fd);
+ 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):
+ CHECK_FR(ctx, fs | fd);
+ 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):
+ CHECK_FR(ctx, fs | fd);
+ 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):
+ CHECK_FR(ctx, fs | fd);
+ 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):
+ CHECK_FR(ctx, fs | fd);
+ GEN_LOAD_FREG_FTN(WT0, fs);
+ gen_op_float_chs_s();
+ GEN_STORE_FTN_FREG(fd, WT2);
+ opn = "neg.s";
+ break;
+ case FOP(12, 16):
+ CHECK_FR(ctx, fs | fd);
+ 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):
+ CHECK_FR(ctx, fs | fd);
+ GEN_LOAD_FREG_FTN(WT0, fs);
+ gen_op_float_truncw_s();
+ GEN_STORE_FTN_FREG(fd, WT2);
+ opn = "trunc.w.s";
+ break;
+ case FOP(32, 20): /* cvt.s.w */
+ CHECK_FR(ctx, fs | fd);
+ GEN_LOAD_FREG_FTN(WT0, fs);
+ gen_op_float_cvts_w();
+ GEN_STORE_FTN_FREG(fd, WT2);
+ opn = "cvt.s.w";
+ 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):
+ CHECK_FR(ctx, fs | ft);
+ GEN_LOAD_FREG_FTN(WT0, fs);
+ GEN_LOAD_FREG_FTN(WT1, ft);
+ gen_cmp_s(func-48);
+ opn = condnames[func-48];
+ break;
+ 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;
+ }
+ if (binary)
+ MIPS_DEBUG("%s %s, %s, %s", opn, fregnames[fd], fregnames[fs], fregnames[ft]);
+ else
+ MIPS_DEBUG("%s %s,%s", opn, fregnames[fd], fregnames[fs]);
+}
+#endif
/* ISA extensions */
/* MIPS16 extension to MIPS32 */
@@ -1495,14 +2020,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 */
@@ -1722,6 +2275,44 @@
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 fs:%g\n", \
+ (fp)->w, (fp)->u32[0], (fp)->u32[1], (fp)->fd, (fp)->fs); \
+ } 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, "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
+}
+
+void dump_fpu(CPUState *env)
+{
+ if (loglevel) {
+ fprintf(logfile, "pc=0x%08x HI=0x%08x LO=0x%08x ds %04x %08x %d\n",
+ env->PC, env->HI, env->LO, env->hflags, env->btarget, env->bcond);
+ fpu_dump_state(env, logfile, fprintf, 0);
+ }
+}
+#endif
+
void cpu_dump_state (CPUState *env, FILE *f,
int (*cpu_fprintf)(FILE *f, const char *fmt, ...),
int flags)
@@ -1751,6 +2342,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-05-02 16:46:05.000000000 +0200
@@ -0,0 +1,81 @@
+/*
+ * 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)
+
+#define SET_RESET(treg, tregname) \
+ void glue(op_set, tregname)(void) \
+ { \
+ treg = PARAM1; \
+ RETURN(); \
+ } \
+ void glue(op_reset, tregname)(void) \
+ { \
+ treg = 0; \
+ RETURN(); \
+ } \
+
+SET_RESET(WT0, _WT0)
+SET_RESET(WT1, _WT1)
+SET_RESET(WT2, _WT2)
+SET_RESET(DT0, _DT0)
+SET_RESET(DT1, _DT1)
+SET_RESET(DT2, _DT2)
+
+#endif
^ permalink raw reply [flat|nested] 7+ messages in thread
* [Qemu-devel] [PATCH][MIPS] FPU support for MIPS
@ 2006-06-08 13:48 Marius Groeger
0 siblings, 0 replies; 7+ messages in thread
From: Marius Groeger @ 2006-06-08 13:48 UTC (permalink / raw)
To: qemu-devel
[-- Attachment #1: Type: TEXT/PLAIN, Size: 1294 bytes --]
Hello,
Here's another update of my previous FPU patches which brings a lot of
good stuff:
- completely switched to using the IEEE softfloat library with
it's extended support for exception/status handling
- fixed FP register implementation which tended to screw up
upon some internal offset calculation (FPR() macro)
- FP registers should behave identical on big/little endian hosts
(one of Fabrices requests)
- complete rewrite of conditional calculation
(triggerd by previous brokeness and Fabrice :-)
- implement CP1 FPR0 implementation register
- properly issue "CP1 not available" exceptions (needed badly for
Linux's lazy context switching)
- implement new insns:
cvt.w.s
cvt.w.d
floor.w.s
floor.w.d
ceil.w.s
ceil.w.d
- And my favorite: QEMU MIPS Linux now passes the "paranoia" testsuite:
No failures, defects nor flaws have been discovered.
Rounding appears to conform to the proposed IEEE standard P754.
The arithmetic diagnosed appears to be Excellent!
END OF TEST.
Regards,
Marius
--
Marius Groeger <mgroeger@sysgo.com>
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
[-- Attachment #2: Type: TEXT/PLAIN, Size: 52569 bytes --]
Index: configure
===================================================================
RCS file: /sources/qemu/qemu/configure,v
retrieving revision 1.102
diff -u -r1.102 configure
--- configure 14 May 2006 11:30:38 -0000 1.102
+++ configure 8 Jun 2006 13:42:15 -0000
@@ -817,6 +817,8 @@
echo "TARGET_ARCH=mips" >> $config_mak
echo "#define TARGET_ARCH \"mips\"" >> $config_h
echo "#define TARGET_MIPS 1" >> $config_h
+ echo "CONFIG_SOFTFLOAT=yes" >> $config_mak
+ echo "#define CONFIG_SOFTFLOAT 1" >> $config_h
elif test "$target_cpu" = "sh4" ; then
echo "TARGET_ARCH=sh4" >> $config_mak
echo "#define TARGET_ARCH \"sh4\"" >> $config_h
Index: target-mips/cpu.h
===================================================================
RCS file: /sources/qemu/qemu/target-mips/cpu.h,v
retrieving revision 1.6
diff -u -r1.6 cpu.h
--- target-mips/cpu.h 11 Mar 2006 16:23:39 -0000 1.6
+++ target-mips/cpu.h 8 Jun 2006 13:42:15 -0000
@@ -10,10 +10,19 @@
typedef union fpr_t fpr_t;
union fpr_t {
- double d;
- float f;
- uint32_t u[2];
+ float64 fd; /* ieee double precision */
+ float32 fs[2];/* ieee single precision */
+ uint64_t d; /* binary single fixed-point */
+ uint32_t w[2]; /* binary single fixed-point */
};
+/* define FP_ENDIAN_IDX to access the same location
+ * in the fpr_t union regardless of the host endianess
+ */
+#if defined(WORDS_BIGENDIAN)
+# define FP_ENDIAN_IDX 1
+#else
+# define FP_ENDIAN_IDX 0
+#endif
#if defined(MIPS_USES_R4K_TLB)
typedef struct tlb_t tlb_t;
@@ -44,12 +53,38 @@
#if defined(MIPS_USES_FPU)
/* Floating point registers */
fpr_t fpr[16];
- /* Floating point special purpose registers */
+#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 */
uint32_t fcr0;
- uint32_t fcr25;
- uint32_t fcr26;
- uint32_t fcr28;
- uint32_t fcsr;
+ /* 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 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];
@@ -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 -r1.7 exec.h
--- target-mips/exec.h 11 Mar 2006 15:00:08 -0000 1.7
+++ target-mips/exec.h 8 Jun 2006 13:42:15 -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[FP_ENDIAN_IDX])
+#define FST1 (env->ft1.fs[FP_ENDIAN_IDX])
+#define FST2 (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])
#endif
#if defined (DEBUG_OP)
@@ -65,6 +72,13 @@
void do_tlbwr (void);
void do_tlbp (void);
void do_tlbr (void);
+#ifdef MIPS_USES_FPU
+void dump_fpu(CPUState *env);
+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 -r1.1 mips-defs.h
--- target-mips/mips-defs.h 2 Jul 2005 14:57:14 -0000 1.1
+++ target-mips/mips-defs.h 8 Jun 2006 13:42:15 -0000
@@ -24,6 +24,12 @@
/* Uses MIPS R4Kc TLB model */
#define MIPS_USES_R4K_TLB
#define MIPS_TLB_NB 16
+/* basic FPU register support */
+#define MIPS_USES_FPU 1
+/* Define a implementation number of 1.
+ * Define a major version 1, minor version 0.
+ */
+#define MIPS_FCR0 ((0 << 16) | (1 << 8) | (1 << 4) | 0)
/* Have config1, runs in big-endian mode, uses TLB */
#define MIPS_CONFIG0 \
((1 << CP0C0_M) | (0x000 << CP0C0_K23) | (0x000 << CP0C0_KU) | \
@@ -31,14 +37,14 @@
/* 16 TLBs, 64 sets Icache, 16 bytes Icache line, 2-way Icache,
* 64 sets Dcache, 16 bytes Dcache line, 2-way Dcache,
* no performance counters, watch registers present, no code compression,
- * EJTAG present, no FPU
+ * EJTAG present, FPU enable bit depending on MIPS_USES_FPU
*/
#define MIPS_CONFIG1 \
((15 << CP0C1_MMU) | \
(0x000 << CP0C1_IS) | (0x3 << CP0C1_IL) | (0x01 << CP0C1_IA) | \
(0x000 << CP0C1_DS) | (0x3 << CP0C1_DL) | (0x01 << CP0C1_DA) | \
(0 << CP0C1_PC) | (1 << CP0C1_WR) | (0 << CP0C1_CA) | \
- (1 << CP0C1_EP) | (0 << CP0C1_FP))
+ (1 << CP0C1_EP) | (MIPS_USES_FPU << CP0C1_FP))
#elif defined (MIPS_CPU == MIPS_R4Kp)
/* 32 bits target */
#define TARGET_LONG_BITS 32
@@ -52,7 +58,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 -r1.7 op.c
--- target-mips/op.c 23 Apr 2006 15:23:48 -0000 1.7
+++ target-mips/op.c 8 Jun 2006 13:42:15 -0000
@@ -2,6 +2,7 @@
* MIPS emulation micro-operations for qemu.
*
* Copyright (c) 2004-2005 Jocelyn Mayer
+ * Copyright (c) 2006 Marius Groeger (FPU operations)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -149,6 +150,143 @@
#include "op_template.c"
#undef TN
+#ifdef MIPS_USES_FPU
+
+#define SFREG 0
+#define DFREG 0
+#include "fop_template.c"
+#undef SFREG
+#undef DFREG
+#define SFREG 1
+#include "fop_template.c"
+#undef SFREG
+#define SFREG 2
+#define DFREG 2
+#include "fop_template.c"
+#undef SFREG
+#undef DFREG
+#define SFREG 3
+#include "fop_template.c"
+#undef SFREG
+#define SFREG 4
+#define DFREG 4
+#include "fop_template.c"
+#undef SFREG
+#undef DFREG
+#define SFREG 5
+#include "fop_template.c"
+#undef SFREG
+#define SFREG 6
+#define DFREG 6
+#include "fop_template.c"
+#undef SFREG
+#undef DFREG
+#define SFREG 7
+#include "fop_template.c"
+#undef SFREG
+#define SFREG 8
+#define DFREG 8
+#include "fop_template.c"
+#undef SFREG
+#undef DFREG
+#define SFREG 9
+#include "fop_template.c"
+#undef SFREG
+#define SFREG 10
+#define DFREG 10
+#include "fop_template.c"
+#undef SFREG
+#undef DFREG
+#define SFREG 11
+#include "fop_template.c"
+#undef SFREG
+#define SFREG 12
+#define DFREG 12
+#include "fop_template.c"
+#undef SFREG
+#undef DFREG
+#define SFREG 13
+#include "fop_template.c"
+#undef SFREG
+#define SFREG 14
+#define DFREG 14
+#include "fop_template.c"
+#undef SFREG
+#undef DFREG
+#define SFREG 15
+#include "fop_template.c"
+#undef SFREG
+#define SFREG 16
+#define DFREG 16
+#include "fop_template.c"
+#undef SFREG
+#undef DFREG
+#define SFREG 17
+#include "fop_template.c"
+#undef SFREG
+#define SFREG 18
+#define DFREG 18
+#include "fop_template.c"
+#undef SFREG
+#undef DFREG
+#define SFREG 19
+#include "fop_template.c"
+#undef SFREG
+#define SFREG 20
+#define DFREG 20
+#include "fop_template.c"
+#undef SFREG
+#undef DFREG
+#define SFREG 21
+#include "fop_template.c"
+#undef SFREG
+#define SFREG 22
+#define DFREG 22
+#include "fop_template.c"
+#undef SFREG
+#undef DFREG
+#define SFREG 23
+#include "fop_template.c"
+#undef SFREG
+#define SFREG 24
+#define DFREG 24
+#include "fop_template.c"
+#undef SFREG
+#undef DFREG
+#define SFREG 25
+#include "fop_template.c"
+#undef SFREG
+#define SFREG 26
+#define DFREG 26
+#include "fop_template.c"
+#undef SFREG
+#undef DFREG
+#define SFREG 27
+#include "fop_template.c"
+#undef SFREG
+#define SFREG 28
+#define DFREG 28
+#include "fop_template.c"
+#undef SFREG
+#undef DFREG
+#define SFREG 29
+#include "fop_template.c"
+#undef SFREG
+#define SFREG 30
+#define DFREG 30
+#include "fop_template.c"
+#undef SFREG
+#undef DFREG
+#define SFREG 31
+#include "fop_template.c"
+#undef SFREG
+
+#define FTN
+#include "fop_template.c"
+#undef FTN
+
+#endif
+
void op_dup_T0 (void)
{
T2 = T0;
@@ -562,6 +700,353 @@
RETURN();
}
+#ifdef MIPS_USES_FPU
+
+#if 0
+# define DEBUG_FPU_STATE() CALL_FROM_TB1(dump_fpu, env)
+#else
+# define DEBUG_FPU_STATE() do { } while(0)
+#endif
+
+void op_cp1_enabled(void)
+{
+ if (!(env->CP0_Status & (1 << CP0St_CU1))) {
+ CALL_FROM_TB2(do_raise_exception_err, EXCP_CpU, 1);
+ }
+ 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,
+ float_round_to_zero,
+ float_round_up,
+ float_round_down
+};
+
+#define RESTORE_ROUNDING_MODE \
+ set_float_rounding_mode(ieee_rm[env->fcr31 & 3], &env->fp_status)
+
+void op_ctc1 (void)
+{
+ 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;
+
+ /* set rounding mode */
+ RESTORE_ROUNDING_MODE;
+
+#ifndef CONFIG_SOFTFLOAT
+ /* no floating point exception for native float */
+ SET_FP_ENABLE(env->fcr31, 0);
+#endif
+ }
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+
+void op_mfc1 (void)
+{
+ T0 = WT0;
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+
+void op_mtc1 (void)
+{
+ WT0 = T0;
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+
+/* Float support.
+ Single precition routines have a "s" suffix, double precision a
+ "d" suffix. */
+
+#define FLOAT_OP(name, p) void OPPROTO op_float_##name##_##p(void)
+
+FLOAT_OP(cvtd, w)
+{
+ FDT2 = int32_to_float64(WT0, &env->fp_status);
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+FLOAT_OP(cvts, w)
+{
+ FST2 = int32_to_float32(WT0, &env->fp_status);
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+FLOAT_OP(cvtw, s)
+{
+ WT2 = float32_to_int32(FST0, &env->fp_status);
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+FLOAT_OP(cvtw, d)
+{
+ WT2 = float64_to_int32(FDT0, &env->fp_status);
+ 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();
+}
+FLOAT_OP(roundw, s)
+{
+ set_float_rounding_mode(float_round_nearest_even, &env->fp_status);
+ WT2 = float32_round_to_int(FST0, &env->fp_status);
+ RESTORE_ROUNDING_MODE;
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+
+FLOAT_OP(truncw, d)
+{
+ WT2 = float64_to_int32_round_to_zero(FDT0, &env->fp_status);
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+FLOAT_OP(truncw, s)
+{
+ WT2 = float32_to_int32_round_to_zero(FST0, &env->fp_status);
+ 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();
+}
+FLOAT_OP(ceilw, s)
+{
+ set_float_rounding_mode(float_round_up, &env->fp_status);
+ WT2 = 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();
+}
+FLOAT_OP(floorw, s)
+{
+ set_float_rounding_mode(float_round_down, &env->fp_status);
+ WT2 = float32_round_to_int(FST0, &env->fp_status);
+ RESTORE_ROUNDING_MODE;
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+
+/* binary operations */
+#define FLOAT_BINOP(name) \
+FLOAT_OP(name, d) \
+{ \
+ FDT2 = float64_ ## name (FDT0, FDT1, &env->fp_status); \
+ DEBUG_FPU_STATE(); \
+} \
+FLOAT_OP(name, s) \
+{ \
+ FST2 = float32_ ## name (FST0, FST1, &env->fp_status); \
+ DEBUG_FPU_STATE(); \
+}
+FLOAT_BINOP(add)
+FLOAT_BINOP(sub)
+FLOAT_BINOP(mul)
+FLOAT_BINOP(div)
+#undef FLOAT_BINOP
+
+/* unary operations, modifying fp status */
+#define FLOAT_UNOP(name) \
+FLOAT_OP(name, d) \
+{ \
+ FDT2 = float64_ ## name(FDT0, &env->fp_status); \
+ DEBUG_FPU_STATE(); \
+} \
+FLOAT_OP(name, s) \
+{ \
+ FST2 = float32_ ## name(FST0, &env->fp_status); \
+ DEBUG_FPU_STATE(); \
+}
+FLOAT_UNOP(sqrt)
+#undef FLOAT_UNOP
+
+/* unary operations, not modifying fp status */
+#define FLOAT_UNOP(name) \
+FLOAT_OP(name, d) \
+{ \
+ FDT2 = float64_ ## name(FDT0); \
+ DEBUG_FPU_STATE(); \
+} \
+FLOAT_OP(name, s) \
+{ \
+ FST2 = float32_ ## name(FST0); \
+ DEBUG_FPU_STATE(); \
+}
+FLOAT_UNOP(abs)
+FLOAT_UNOP(chs)
+#undef FLOAT_UNOP
+
+FLOAT_OP(mov, d)
+{
+ FDT2 = FDT0;
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+FLOAT_OP(mov, s)
+{
+ FST2 = FST0;
+ DEBUG_FPU_STATE();
+ RETURN();
+}
+
+#ifdef CONFIG_SOFTFLOAT
+#define clear_invalid() do { \
+ int flags = get_float_exception_flags(&env->fp_status); \
+ flags &= ~float_flag_invalid; \
+ set_float_exception_flags(flags, &env->fp_status); \
+} while(0)
+#else
+#define clear_invalid() do { } while(0)
+#endif
+
+extern void dump_fpu_s(CPUState *env);
+
+#define FOP_COND(fmt, op, sig, cond) \
+void op_cmp_ ## fmt ## _ ## op (void) \
+{ \
+ if (cond) \
+ SET_FP_COND(env->fcr31); \
+ else \
+ CLEAR_FP_COND(env->fcr31); \
+ if (!sig) \
+ clear_invalid(); \
+ /*CALL_FROM_TB1(dump_fpu_s, env);*/ \
+ DEBUG_FPU_STATE(); \
+ RETURN(); \
+}
+
+flag float64_is_unordered(float64 a, float64 b STATUS_PARAM)
+{
+ extern flag float64_is_nan( float64 a );
+ if (float64_is_nan(a) || float64_is_nan(b)) {
+ float_raise(float_flag_invalid, status);
+ 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)) {
+ float_raise(float_flag_invalid, status);
+ 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))
+/* 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))
+
+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 /* MIPS_USES_FPU */
+
#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.13
diff -u -r1.13 op_helper.c
--- target-mips/op_helper.c 22 May 2006 22:14:43 -0000 1.13
+++ target-mips/op_helper.c 8 Jun 2006 13:42:15 -0000
@@ -529,6 +529,47 @@
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
+}
+#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 -r1.2 op_mem.c
--- target-mips/op_mem.c 5 Dec 2005 19:59:36 -0000 1.2
+++ target-mips/op_mem.c 8 Jun 2006 13:42:15 -0000
@@ -118,3 +118,26 @@
}
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);
+ RETURN();
+}
+void glue(op_sdc1, MEMSUFFIX) (void)
+{
+ 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.13
diff -u -r1.13 translate.c
--- target-mips/translate.c 22 May 2006 22:13:29 -0000 1.13
+++ target-mips/translate.c 8 Jun 2006 13:42:15 -0000
@@ -2,6 +2,7 @@
* MIPS32 emulation for qemu: main translation routines.
*
* Copyright (c) 2004-2005 Jocelyn Mayer
+ * Copyright (c) 2006 Marius Groeger (FPU operations)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -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,92 @@
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 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 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](); \
+}
+
+SFGEN32(gen_op_load_fpr_WT0, gen_op_load_fpr_WT0_fpr);
+SFGEN32(gen_op_store_fpr_WT0, gen_op_store_fpr_WT0_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);
+
+SFGEN32(gen_op_load_fpr_WT2, gen_op_load_fpr_WT2_fpr);
+SFGEN32(gen_op_store_fpr_WT2, gen_op_store_fpr_WT2_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);
+
+DFGEN32(gen_op_load_fpr_DT1, gen_op_load_fpr_DT1_fpr);
+DFGEN32(gen_op_store_fpr_DT1, gen_op_store_fpr_DT1_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);
+
+#define FOP_CONDS(fmt) \
+static GenOpFunc * cond_ ## fmt ## _table[16] = { \
+ gen_op_cmp_ ## fmt ## _f, \
+ gen_op_cmp_ ## fmt ## _un, \
+ gen_op_cmp_ ## fmt ## _eq, \
+ gen_op_cmp_ ## fmt ## _ueq, \
+ gen_op_cmp_ ## fmt ## _olt, \
+ gen_op_cmp_ ## fmt ## _ult, \
+ gen_op_cmp_ ## fmt ## _ole, \
+ gen_op_cmp_ ## fmt ## _ule, \
+ gen_op_cmp_ ## fmt ## _sf, \
+ gen_op_cmp_ ## fmt ## _ngle, \
+ gen_op_cmp_ ## fmt ## _seq, \
+ gen_op_cmp_ ## fmt ## _ngl, \
+ gen_op_cmp_ ## fmt ## _lt, \
+ gen_op_cmp_ ## fmt ## _nge, \
+ gen_op_cmp_ ## fmt ## _le, \
+ gen_op_cmp_ ## fmt ## _ngt, \
+}; \
+static inline void gen_cmp_ ## fmt(int n) \
+{ \
+ cond_ ## fmt ## _table[n](); \
+}
+
+FOP_CONDS(d)
+FOP_CONDS(s)
+
+#endif
+
typedef struct DisasContext {
struct TranslationBlock *tb;
target_ulong pc, saved_pc;
@@ -312,6 +409,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
@@ -397,6 +508,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,
@@ -551,6 +668,56 @@
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)
@@ -1265,7 +1432,406 @@
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";
+
+ 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:
+ 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("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;
+ 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]);
+}
+
+/* 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.
+ *
+ * Multiple float 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)
+
+#define FOP(func, fmt) (((fmt) << 21) | (func))
+
+static void gen_farith (DisasContext *ctx, int fmt, int ft, int fs, int fd, int func)
+{
+ const unsigned char *opn = "unk";
+ const char *condnames[] = {
+ "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",
+ };
+ int binary = 0;
+
+ switch (ctx->opcode & FOP(0x3f, 0x1f)) {
+ case FOP(0, 17):
+ CHECK_FR(ctx, fs | ft | fd);
+ GEN_LOAD_FREG_FTN(DT0, fs);
+ GEN_LOAD_FREG_FTN(DT1, ft);
+ gen_op_float_add_d();
+ GEN_STORE_FTN_FREG(fd, DT2);
+ opn = "add.d";
+ binary = 1;
+ break;
+ case FOP(1, 17):
+ CHECK_FR(ctx, fs | ft | fd);
+ GEN_LOAD_FREG_FTN(DT0, fs);
+ GEN_LOAD_FREG_FTN(DT1, ft);
+ gen_op_float_sub_d();
+ GEN_STORE_FTN_FREG(fd, DT2);
+ opn = "sub.d";
+ binary = 1;
+ break;
+ case FOP(2, 17):
+ CHECK_FR(ctx, fs | ft | fd);
+ GEN_LOAD_FREG_FTN(DT0, fs);
+ GEN_LOAD_FREG_FTN(DT1, ft);
+ gen_op_float_mul_d();
+ GEN_STORE_FTN_FREG(fd, DT2);
+ opn = "mul.d";
+ binary = 1;
+ break;
+ case FOP(3, 17):
+ CHECK_FR(ctx, fs | ft | fd);
+ GEN_LOAD_FREG_FTN(DT0, fs);
+ GEN_LOAD_FREG_FTN(DT1, ft);
+ gen_op_float_div_d();
+ GEN_STORE_FTN_FREG(fd, DT2);
+ opn = "div.d";
+ binary = 1;
+ break;
+ case FOP(4, 17):
+ CHECK_FR(ctx, fs | fd);
+ GEN_LOAD_FREG_FTN(DT0, fs);
+ gen_op_float_sqrt_d();
+ GEN_STORE_FTN_FREG(fd, DT2);
+ opn = "sqrt.d";
+ break;
+ case FOP(5, 17):
+ CHECK_FR(ctx, fs | fd);
+ GEN_LOAD_FREG_FTN(DT0, fs);
+ gen_op_float_abs_d();
+ GEN_STORE_FTN_FREG(fd, DT2);
+ opn = "abs.d";
+ break;
+ case FOP(6, 17):
+ CHECK_FR(ctx, fs | fd);
+ GEN_LOAD_FREG_FTN(DT0, fs);
+ gen_op_float_mov_d();
+ GEN_STORE_FTN_FREG(fd, DT2);
+ opn = "mov.d";
+ break;
+ case FOP(7, 17):
+ CHECK_FR(ctx, fs | fd);
+ GEN_LOAD_FREG_FTN(DT0, fs);
+ gen_op_float_chs_d();
+ GEN_STORE_FTN_FREG(fd, DT2);
+ opn = "neg.d";
+ break;
+ /* 8 - round.l */
+ /* 9 - trunc.l */
+ /* 10 - ceil.l */
+ /* 11 - floor.l */
+ case FOP(12, 17):
+ CHECK_FR(ctx, fs | fd);
+ GEN_LOAD_FREG_FTN(DT0, fs);
+ gen_op_float_roundw_d();
+ GEN_STORE_FTN_FREG(fd, WT2);
+ opn = "round.w.d";
+ break;
+ case FOP(13, 17):
+ CHECK_FR(ctx, fs | fd);
+ GEN_LOAD_FREG_FTN(DT0, fs);
+ gen_op_float_truncw_d();
+ GEN_STORE_FTN_FREG(fd, WT2);
+ opn = "trunc.w.d";
+ break;
+ case FOP(14, 17):
+ CHECK_FR(ctx, fs | fd);
+ GEN_LOAD_FREG_FTN(DT0, fs);
+ gen_op_float_ceilw_d();
+ GEN_STORE_FTN_FREG(fd, WT2);
+ opn = "ceil.w.d";
+ break;
+ case FOP(15, 17):
+ CHECK_FR(ctx, fs | fd);
+ GEN_LOAD_FREG_FTN(DT0, fs);
+ gen_op_float_floorw_d();
+ GEN_STORE_FTN_FREG(fd, WT2);
+ opn = "ceil.w.d";
+ break;
+ case FOP(33, 20): /* cvt.d.w */
+ CHECK_FR(ctx, fs | 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(48, 17):
+ case FOP(49, 17):
+ case FOP(50, 17):
+ case FOP(51, 17):
+ case FOP(52, 17):
+ case FOP(53, 17):
+ case FOP(54, 17):
+ case FOP(55, 17):
+ case FOP(56, 17):
+ case FOP(57, 17):
+ case FOP(58, 17):
+ case FOP(59, 17):
+ case FOP(60, 17):
+ case FOP(61, 17):
+ case FOP(62, 17):
+ case FOP(63, 17):
+ CHECK_FR(ctx, fs | ft);
+ GEN_LOAD_FREG_FTN(DT0, fs);
+ GEN_LOAD_FREG_FTN(DT1, ft);
+ gen_cmp_d(func-48);
+ opn = condnames[func-48];
+ break;
+ case FOP(0, 16):
+ CHECK_FR(ctx, fs | ft | fd);
+ 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):
+ CHECK_FR(ctx, fs | ft | fd);
+ 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):
+ CHECK_FR(ctx, fs | ft | fd);
+ 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):
+ CHECK_FR(ctx, fs | ft | fd);
+ 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):
+ CHECK_FR(ctx, fs | fd);
+ 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):
+ CHECK_FR(ctx, fs | fd);
+ 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):
+ CHECK_FR(ctx, fs | fd);
+ 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):
+ CHECK_FR(ctx, fs | fd);
+ GEN_LOAD_FREG_FTN(WT0, fs);
+ gen_op_float_chs_s();
+ GEN_STORE_FTN_FREG(fd, WT2);
+ opn = "neg.s";
+ break;
+ case FOP(12, 16):
+ CHECK_FR(ctx, fs | fd);
+ 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):
+ CHECK_FR(ctx, fs | fd);
+ GEN_LOAD_FREG_FTN(WT0, fs);
+ gen_op_float_truncw_s();
+ GEN_STORE_FTN_FREG(fd, WT2);
+ opn = "trunc.w.s";
+ break;
+ case FOP(32, 20): /* cvt.s.w */
+ CHECK_FR(ctx, fs | fd);
+ GEN_LOAD_FREG_FTN(WT0, fs);
+ gen_op_float_cvts_w();
+ GEN_STORE_FTN_FREG(fd, WT2);
+ opn = "cvt.s.w";
+ break;
+ case FOP(36, 16): /* cvt.w.s */
+ CHECK_FR(ctx, fs | fd);
+ GEN_LOAD_FREG_FTN(WT0, fs);
+ gen_op_float_cvtw_s();
+ GEN_STORE_FTN_FREG(fd, WT2);
+ opn = "cvt.w.s";
+ break;
+ case FOP(36, 17): /* cvt.w.d */
+ CHECK_FR(ctx, fs | fd);
+ GEN_LOAD_FREG_FTN(WT0, fs);
+ gen_op_float_cvtw_d();
+ GEN_STORE_FTN_FREG(fd, WT2);
+ opn = "cvt.w.d";
+ 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):
+ CHECK_FR(ctx, fs | ft);
+ GEN_LOAD_FREG_FTN(WT0, fs);
+ GEN_LOAD_FREG_FTN(WT1, ft);
+ gen_cmp_s(func-48);
+ opn = condnames[func-48];
+ break;
+ 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;
+ }
+ if (binary)
+ MIPS_DEBUG("%s %s, %s, %s", opn, fregnames[fd], fregnames[fs], fregnames[ft]);
+ else
+ MIPS_DEBUG("%s %s,%s", opn, fregnames[fd], fregnames[fs]);
+}
+#endif
/* ISA extensions */
/* MIPS16 extension to MIPS32 */
@@ -1495,9 +2061,39 @@
case 0x35: /* LDC1 */
case 0x39: /* SWC1 */
case 0x3D: /* SDC1 */
+#if defined(MIPS_USES_FPU)
+ gen_op_cp1_enabled();
+ 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)
- /* XXX: not correct */
+ gen_op_cp1_enabled();
+ 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_RI, 1);
+ break;
+ }
+ break;
#else
generate_exception_err(ctx, EXCP_CpU, 1);
#endif
@@ -1586,7 +2182,7 @@
int j, lj = -1;
if (search_pc && loglevel)
- fprintf (logfile, "search pc %d\n", search_pc);
+ fprintf (logfile, "search pc %d\n", search_pc);
pc_start = tb->pc;
gen_opc_ptr = gen_opc_buf;
@@ -1696,7 +2292,7 @@
#endif
if (loglevel & CPU_LOG_TB_IN_ASM) {
fprintf(logfile, "IN: %s\n", lookup_symbol(pc_start));
- target_disas(logfile, pc_start, ctx.pc - pc_start, 0);
+ target_disas(logfile, pc_start, ctx.pc - pc_start, 0);
fprintf(logfile, "\n");
}
if (loglevel & CPU_LOG_TB_OP) {
@@ -1722,6 +2318,42 @@
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 fs:%g\n", \
+ (fp)->w[FP_ENDIAN_IDX], (fp)->w[0], (fp)->w[1], (fp)->fd, (fp)->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, "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, "f%02d: ", i);
+ printfpr(FPR(env, i));
+ }
+
+#undef printfpr
+}
+
+void dump_fpu(CPUState *env)
+{
+ if (loglevel) {
+ fprintf(logfile, "pc=0x%08x HI=0x%08x LO=0x%08x ds %04x %08x %d\n",
+ env->PC, env->HI, env->LO, env->hflags, env->btarget, env->bcond);
+ fpu_dump_state(env, logfile, fprintf, 0);
+ }
+}
+#endif /* MIPS_USES_FPU */
+
void cpu_dump_state (CPUState *env, FILE *f,
int (*cpu_fprintf)(FILE *f, const char *fmt, ...),
int flags)
@@ -1751,6 +2383,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)
@@ -1788,5 +2423,8 @@
#if defined(CONFIG_USER_ONLY)
env->hflags |= MIPS_HFLAG_UM;
#endif
+#ifdef MIPS_USES_FPU
+ env->fcr0 = MIPS_FCR0;
+#endif
return env;
}
--- /dev/null 2004-03-09 17:05:24.000000000 +0100
+++ target-mips/fop_template.c 2006-06-07 10:08:24.000000000 +0200
@@ -0,0 +1,99 @@
+/*
+ * 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(SFREG)
+
+#define OP_WLOAD_FREG(treg, tregname, SFREG) \
+ void glue(glue(op_load_fpr_,tregname), SFREG) (void) \
+ { \
+ treg = FPR_W(env, SFREG); \
+ RETURN(); \
+ }
+
+#define OP_WSTORE_FREG(treg, tregname, SFREG) \
+ void glue(glue(op_store_fpr_,tregname), SFREG) (void)\
+ { \
+ FPR_W(env, SFREG) = 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)
+
+OP_WLOAD_FREG(WT1, WT1_fpr, SFREG)
+OP_WSTORE_FREG(WT1, WT1_fpr, SFREG)
+
+OP_WLOAD_FREG(WT2, WT2_fpr, SFREG)
+OP_WSTORE_FREG(WT2, WT2_fpr, SFREG)
+
+#endif
+
+#if defined(DFREG)
+
+#define OP_DLOAD_FREG(treg, tregname, DFREG) \
+ void glue(glue(op_load_fpr_,tregname), DFREG) (void) \
+ { \
+ treg = FPR_D(env, DFREG); \
+ RETURN(); \
+ }
+
+#define OP_DSTORE_FREG(treg, tregname, DFREG) \
+ void glue(glue(op_store_fpr_,tregname), DFREG) (void)\
+ { \
+ FPR_D(env, DFREG) = treg; \
+ RETURN(); \
+ }
+
+OP_DLOAD_FREG(DT0, DT0_fpr, DFREG)
+OP_DSTORE_FREG(DT0, DT0_fpr, DFREG)
+
+OP_DLOAD_FREG(DT1, DT1_fpr, DFREG)
+OP_DSTORE_FREG(DT1, DT1_fpr, DFREG)
+
+OP_DLOAD_FREG(DT2, DT2_fpr, DFREG)
+OP_DSTORE_FREG(DT2, DT2_fpr, DFREG)
+
+#endif
+
+#if defined (FTN)
+
+#define SET_RESET(treg, tregname) \
+ void glue(op_set, tregname)(void) \
+ { \
+ treg = PARAM1; \
+ RETURN(); \
+ } \
+ void glue(op_reset, tregname)(void) \
+ { \
+ treg = 0; \
+ RETURN(); \
+ } \
+
+SET_RESET(WT0, _WT0)
+SET_RESET(WT1, _WT1)
+SET_RESET(WT2, _WT2)
+SET_RESET(DT0, _DT0)
+SET_RESET(DT1, _DT1)
+SET_RESET(DT2, _DT2)
+
+#endif
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2006-06-08 13:48 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-05-02 14:01 [Qemu-devel] [PATCH][MIPS] FPU support for MIPS Marius Groeger
2006-05-02 14:20 ` Marius Groeger
[not found] ` <20060502143520.GB9551@lebarbe.net>
2006-05-03 8:19 ` Marius Groeger
-- strict thread matches above, loose matches on Subject: below --
2006-06-08 13:48 Marius Groeger
2006-04-26 14:26 Marius Groeger
2006-04-26 22:17 ` Fabrice Bellard
2006-04-27 10:22 ` Marius Groeger
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).