Commit 6ea83fedc802c6d678e36c380d72733d89d17bba

Authored by bellard
1 parent 180b700d

MIPS FPU support (Marius Goeger)


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1964 c046a42c-6fe2-441c-8c8c-71466251a162
configure
... ... @@ -819,6 +819,8 @@ elif test "$target_cpu" = "mips" -o "$target_cpu" = "mipsel" ; then
819 819 echo "TARGET_ARCH=mips" >> $config_mak
820 820 echo "#define TARGET_ARCH \"mips\"" >> $config_h
821 821 echo "#define TARGET_MIPS 1" >> $config_h
  822 + echo "CONFIG_SOFTFLOAT=yes" >> $config_mak
  823 + echo "#define CONFIG_SOFTFLOAT 1" >> $config_h
822 824 elif test "$target_cpu" = "sh4" ; then
823 825 echo "TARGET_ARCH=sh4" >> $config_mak
824 826 echo "#define TARGET_ARCH \"sh4\"" >> $config_h
... ...
target-mips/cpu.h
... ... @@ -10,10 +10,19 @@
10 10  
11 11 typedef union fpr_t fpr_t;
12 12 union fpr_t {
13   - double d;
14   - float f;
15   - uint32_t u[2];
  13 + float64 fd; /* ieee double precision */
  14 + float32 fs[2];/* ieee single precision */
  15 + uint64_t d; /* binary single fixed-point */
  16 + uint32_t w[2]; /* binary single fixed-point */
16 17 };
  18 +/* define FP_ENDIAN_IDX to access the same location
  19 + * in the fpr_t union regardless of the host endianess
  20 + */
  21 +#if defined(WORDS_BIGENDIAN)
  22 +# define FP_ENDIAN_IDX 1
  23 +#else
  24 +# define FP_ENDIAN_IDX 0
  25 +#endif
17 26  
18 27 #if defined(MIPS_USES_R4K_TLB)
19 28 typedef struct tlb_t tlb_t;
... ... @@ -44,12 +53,38 @@ struct CPUMIPSState {
44 53 #if defined(MIPS_USES_FPU)
45 54 /* Floating point registers */
46 55 fpr_t fpr[16];
47   - /* Floating point special purpose registers */
  56 +#define FPR(cpu, n) ((fpr_t*)&(cpu)->fpr[(n) / 2])
  57 +#define FPR_FD(cpu, n) (FPR(cpu, n)->fd)
  58 +#define FPR_FS(cpu, n) (FPR(cpu, n)->fs[((n) & 1) ^ FP_ENDIAN_IDX])
  59 +#define FPR_D(cpu, n) (FPR(cpu, n)->d)
  60 +#define FPR_W(cpu, n) (FPR(cpu, n)->w[((n) & 1) ^ FP_ENDIAN_IDX])
  61 +
  62 +#ifndef USE_HOST_FLOAT_REGS
  63 + fpr_t ft0;
  64 + fpr_t ft1;
  65 + fpr_t ft2;
  66 +#endif
  67 + float_status fp_status;
  68 + /* fpu implementation/revision register */
48 69 uint32_t fcr0;
49   - uint32_t fcr25;
50   - uint32_t fcr26;
51   - uint32_t fcr28;
52   - uint32_t fcsr;
  70 + /* fcsr */
  71 + uint32_t fcr31;
  72 +#define SET_FP_COND(reg) do { (reg) |= (1<<23); } while(0)
  73 +#define CLEAR_FP_COND(reg) do { (reg) &= ~(1<<23); } while(0)
  74 +#define IS_FP_COND_SET(reg) (((reg) & (1<<23)) != 0)
  75 +#define GET_FP_CAUSE(reg) (((reg) >> 12) & 0x3f)
  76 +#define GET_FP_ENABLE(reg) (((reg) >> 7) & 0x1f)
  77 +#define GET_FP_FLAGS(reg) (((reg) >> 2) & 0x1f)
  78 +#define SET_FP_CAUSE(reg,v) do { (reg) = ((reg) & ~(0x3f << 12)) | ((v) << 12); } while(0)
  79 +#define SET_FP_ENABLE(reg,v) do { (reg) = ((reg) & ~(0x1f << 7)) | ((v) << 7); } while(0)
  80 +#define SET_FP_FLAGS(reg,v) do { (reg) = ((reg) & ~(0x1f << 2)) | ((v) << 2); } while(0)
  81 +#define FP_INEXACT 1
  82 +#define FP_UNDERFLOW 2
  83 +#define FP_OVERFLOW 4
  84 +#define FP_DIV0 8
  85 +#define FP_INVALID 16
  86 +#define FP_UNIMPLEMENTED 32
  87 +
53 88 #endif
54 89 #if defined(MIPS_USES_R4K_TLB)
55 90 tlb_t tlb[16];
... ... @@ -71,6 +106,7 @@ struct CPUMIPSState {
71 106 #define CP0St_CU1 29
72 107 #define CP0St_CU0 28
73 108 #define CP0St_RP 27
  109 +#define CP0St_FR 26
74 110 #define CP0St_RE 25
75 111 #define CP0St_BEV 22
76 112 #define CP0St_TS 21
... ... @@ -138,9 +174,6 @@ struct CPUMIPSState {
138 174 uint32_t CP0_ErrorEPC;
139 175 uint32_t CP0_DESAVE;
140 176 /* Qemu */
141   -#if defined (USE_HOST_FLOAT_REGS) && defined(MIPS_USES_FPU)
142   - double ft0, ft1, ft2;
143   -#endif
144 177 struct QEMUTimer *timer; /* Internal timer */
145 178 int interrupt_request;
146 179 jmp_buf jmp_env;
... ...
target-mips/exec.h
... ... @@ -21,13 +21,20 @@ register host_uint_t T1 asm(AREG2);
21 21 register host_uint_t T2 asm(AREG3);
22 22  
23 23 #if defined (USE_HOST_FLOAT_REGS)
24   -register double FT0 asm(FREG0);
25   -register double FT1 asm(FREG1);
26   -register double FT2 asm(FREG2);
  24 +#error "implement me."
27 25 #else
28   -#define FT0 (env->ft0.d)
29   -#define FT1 (env->ft1.d)
30   -#define FT2 (env->ft2.d)
  26 +#define FDT0 (env->ft0.fd)
  27 +#define FDT1 (env->ft1.fd)
  28 +#define FDT2 (env->ft2.fd)
  29 +#define FST0 (env->ft0.fs[FP_ENDIAN_IDX])
  30 +#define FST1 (env->ft1.fs[FP_ENDIAN_IDX])
  31 +#define FST2 (env->ft2.fs[FP_ENDIAN_IDX])
  32 +#define DT0 (env->ft0.d)
  33 +#define DT1 (env->ft1.d)
  34 +#define DT2 (env->ft2.d)
  35 +#define WT0 (env->ft0.w[FP_ENDIAN_IDX])
  36 +#define WT1 (env->ft1.w[FP_ENDIAN_IDX])
  37 +#define WT2 (env->ft2.w[FP_ENDIAN_IDX])
31 38 #endif
32 39  
33 40 #if defined (DEBUG_OP)
... ... @@ -65,6 +72,13 @@ void do_tlbwi (void);
65 72 void do_tlbwr (void);
66 73 void do_tlbp (void);
67 74 void do_tlbr (void);
  75 +#ifdef MIPS_USES_FPU
  76 +void dump_fpu(CPUState *env);
  77 +void fpu_dump_state(CPUState *env, FILE *f,
  78 + int (*fpu_fprintf)(FILE *f, const char *fmt, ...),
  79 + int flags);
  80 +#endif
  81 +void dump_sc (void);
68 82 void do_lwl_raw (uint32_t);
69 83 void do_lwr_raw (uint32_t);
70 84 uint32_t do_swl_raw (uint32_t);
... ...
target-mips/fop_template.c 0 โ†’ 100644
  1 +/*
  2 + * MIPS emulation micro-operations templates for floating point reg
  3 + * load & store for qemu.
  4 + *
  5 + * Copyright (c) 2006 Marius Groeger
  6 + *
  7 + * This library is free software; you can redistribute it and/or
  8 + * modify it under the terms of the GNU Lesser General Public
  9 + * License as published by the Free Software Foundation; either
  10 + * version 2 of the License, or (at your option) any later version.
  11 + *
  12 + * This library is distributed in the hope that it will be useful,
  13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15 + * Lesser General Public License for more details.
  16 + *
  17 + * You should have received a copy of the GNU Lesser General Public
  18 + * License along with this library; if not, write to the Free Software
  19 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  20 + */
  21 +
  22 +#if defined(SFREG)
  23 +
  24 +#define OP_WLOAD_FREG(treg, tregname, SFREG) \
  25 + void glue(glue(op_load_fpr_,tregname), SFREG) (void) \
  26 + { \
  27 + treg = FPR_W(env, SFREG); \
  28 + RETURN(); \
  29 + }
  30 +
  31 +#define OP_WSTORE_FREG(treg, tregname, SFREG) \
  32 + void glue(glue(op_store_fpr_,tregname), SFREG) (void)\
  33 + { \
  34 + FPR_W(env, SFREG) = treg; \
  35 + RETURN(); \
  36 + }
  37 +
  38 +/* WT0 = SFREG.w: op_load_fpr_WT0_fprSFREG */
  39 +OP_WLOAD_FREG(WT0, WT0_fpr, SFREG)
  40 +/* SFREG.w = WT0: op_store_fpr_WT0_fprSFREG */
  41 +OP_WSTORE_FREG(WT0, WT0_fpr, SFREG)
  42 +
  43 +OP_WLOAD_FREG(WT1, WT1_fpr, SFREG)
  44 +OP_WSTORE_FREG(WT1, WT1_fpr, SFREG)
  45 +
  46 +OP_WLOAD_FREG(WT2, WT2_fpr, SFREG)
  47 +OP_WSTORE_FREG(WT2, WT2_fpr, SFREG)
  48 +
  49 +#endif
  50 +
  51 +#if defined(DFREG)
  52 +
  53 +#define OP_DLOAD_FREG(treg, tregname, DFREG) \
  54 + void glue(glue(op_load_fpr_,tregname), DFREG) (void) \
  55 + { \
  56 + treg = FPR_D(env, DFREG); \
  57 + RETURN(); \
  58 + }
  59 +
  60 +#define OP_DSTORE_FREG(treg, tregname, DFREG) \
  61 + void glue(glue(op_store_fpr_,tregname), DFREG) (void)\
  62 + { \
  63 + FPR_D(env, DFREG) = treg; \
  64 + RETURN(); \
  65 + }
  66 +
  67 +OP_DLOAD_FREG(DT0, DT0_fpr, DFREG)
  68 +OP_DSTORE_FREG(DT0, DT0_fpr, DFREG)
  69 +
  70 +OP_DLOAD_FREG(DT1, DT1_fpr, DFREG)
  71 +OP_DSTORE_FREG(DT1, DT1_fpr, DFREG)
  72 +
  73 +OP_DLOAD_FREG(DT2, DT2_fpr, DFREG)
  74 +OP_DSTORE_FREG(DT2, DT2_fpr, DFREG)
  75 +
  76 +#endif
  77 +
  78 +#if defined (FTN)
  79 +
  80 +#define SET_RESET(treg, tregname) \
  81 + void glue(op_set, tregname)(void) \
  82 + { \
  83 + treg = PARAM1; \
  84 + RETURN(); \
  85 + } \
  86 + void glue(op_reset, tregname)(void) \
  87 + { \
  88 + treg = 0; \
  89 + RETURN(); \
  90 + } \
  91 +
  92 +SET_RESET(WT0, _WT0)
  93 +SET_RESET(WT1, _WT1)
  94 +SET_RESET(WT2, _WT2)
  95 +SET_RESET(DT0, _DT0)
  96 +SET_RESET(DT1, _DT1)
  97 +SET_RESET(DT2, _DT2)
  98 +
  99 +#endif
... ...
target-mips/mips-defs.h
... ... @@ -24,6 +24,12 @@ enum {
24 24 /* Uses MIPS R4Kc TLB model */
25 25 #define MIPS_USES_R4K_TLB
26 26 #define MIPS_TLB_NB 16
  27 +/* basic FPU register support */
  28 +#define MIPS_USES_FPU 1
  29 +/* Define a implementation number of 1.
  30 + * Define a major version 1, minor version 0.
  31 + */
  32 +#define MIPS_FCR0 ((0 << 16) | (1 << 8) | (1 << 4) | 0)
27 33 /* Have config1, runs in big-endian mode, uses TLB */
28 34 #define MIPS_CONFIG0 \
29 35 ((1 << CP0C0_M) | (0x000 << CP0C0_K23) | (0x000 << CP0C0_KU) | \
... ... @@ -31,14 +37,14 @@ enum {
31 37 /* 16 TLBs, 64 sets Icache, 16 bytes Icache line, 2-way Icache,
32 38 * 64 sets Dcache, 16 bytes Dcache line, 2-way Dcache,
33 39 * no performance counters, watch registers present, no code compression,
34   - * EJTAG present, no FPU
  40 + * EJTAG present, FPU enable bit depending on MIPS_USES_FPU
35 41 */
36 42 #define MIPS_CONFIG1 \
37 43 ((15 << CP0C1_MMU) | \
38 44 (0x000 << CP0C1_IS) | (0x3 << CP0C1_IL) | (0x01 << CP0C1_IA) | \
39 45 (0x000 << CP0C1_DS) | (0x3 << CP0C1_DL) | (0x01 << CP0C1_DA) | \
40 46 (0 << CP0C1_PC) | (1 << CP0C1_WR) | (0 << CP0C1_CA) | \
41   - (1 << CP0C1_EP) | (0 << CP0C1_FP))
  47 + (1 << CP0C1_EP) | (MIPS_USES_FPU << CP0C1_FP))
42 48 #elif defined (MIPS_CPU == MIPS_R4Kp)
43 49 /* 32 bits target */
44 50 #define TARGET_LONG_BITS 32
... ... @@ -52,7 +58,7 @@ enum {
52 58 #error "MIPS CPU not defined"
53 59 /* Remainder for other flags */
54 60 //#define TARGET_MIPS64
55   -//define MIPS_USES_FPU
  61 +//#define MIPS_USES_FPU
56 62 #endif
57 63  
58 64 #endif /* !defined (__QEMU_MIPS_DEFS_H__) */
... ...
target-mips/op.c
... ... @@ -2,6 +2,7 @@
2 2 * MIPS emulation micro-operations for qemu.
3 3 *
4 4 * Copyright (c) 2004-2005 Jocelyn Mayer
  5 + * Copyright (c) 2006 Marius Groeger (FPU operations)
5 6 *
6 7 * This library is free software; you can redistribute it and/or
7 8 * modify it under the terms of the GNU Lesser General Public
... ... @@ -149,6 +150,143 @@ CALL_FROM_TB2(func, arg0, arg1);
149 150 #include "op_template.c"
150 151 #undef TN
151 152  
  153 +#ifdef MIPS_USES_FPU
  154 +
  155 +#define SFREG 0
  156 +#define DFREG 0
  157 +#include "fop_template.c"
  158 +#undef SFREG
  159 +#undef DFREG
  160 +#define SFREG 1
  161 +#include "fop_template.c"
  162 +#undef SFREG
  163 +#define SFREG 2
  164 +#define DFREG 2
  165 +#include "fop_template.c"
  166 +#undef SFREG
  167 +#undef DFREG
  168 +#define SFREG 3
  169 +#include "fop_template.c"
  170 +#undef SFREG
  171 +#define SFREG 4
  172 +#define DFREG 4
  173 +#include "fop_template.c"
  174 +#undef SFREG
  175 +#undef DFREG
  176 +#define SFREG 5
  177 +#include "fop_template.c"
  178 +#undef SFREG
  179 +#define SFREG 6
  180 +#define DFREG 6
  181 +#include "fop_template.c"
  182 +#undef SFREG
  183 +#undef DFREG
  184 +#define SFREG 7
  185 +#include "fop_template.c"
  186 +#undef SFREG
  187 +#define SFREG 8
  188 +#define DFREG 8
  189 +#include "fop_template.c"
  190 +#undef SFREG
  191 +#undef DFREG
  192 +#define SFREG 9
  193 +#include "fop_template.c"
  194 +#undef SFREG
  195 +#define SFREG 10
  196 +#define DFREG 10
  197 +#include "fop_template.c"
  198 +#undef SFREG
  199 +#undef DFREG
  200 +#define SFREG 11
  201 +#include "fop_template.c"
  202 +#undef SFREG
  203 +#define SFREG 12
  204 +#define DFREG 12
  205 +#include "fop_template.c"
  206 +#undef SFREG
  207 +#undef DFREG
  208 +#define SFREG 13
  209 +#include "fop_template.c"
  210 +#undef SFREG
  211 +#define SFREG 14
  212 +#define DFREG 14
  213 +#include "fop_template.c"
  214 +#undef SFREG
  215 +#undef DFREG
  216 +#define SFREG 15
  217 +#include "fop_template.c"
  218 +#undef SFREG
  219 +#define SFREG 16
  220 +#define DFREG 16
  221 +#include "fop_template.c"
  222 +#undef SFREG
  223 +#undef DFREG
  224 +#define SFREG 17
  225 +#include "fop_template.c"
  226 +#undef SFREG
  227 +#define SFREG 18
  228 +#define DFREG 18
  229 +#include "fop_template.c"
  230 +#undef SFREG
  231 +#undef DFREG
  232 +#define SFREG 19
  233 +#include "fop_template.c"
  234 +#undef SFREG
  235 +#define SFREG 20
  236 +#define DFREG 20
  237 +#include "fop_template.c"
  238 +#undef SFREG
  239 +#undef DFREG
  240 +#define SFREG 21
  241 +#include "fop_template.c"
  242 +#undef SFREG
  243 +#define SFREG 22
  244 +#define DFREG 22
  245 +#include "fop_template.c"
  246 +#undef SFREG
  247 +#undef DFREG
  248 +#define SFREG 23
  249 +#include "fop_template.c"
  250 +#undef SFREG
  251 +#define SFREG 24
  252 +#define DFREG 24
  253 +#include "fop_template.c"
  254 +#undef SFREG
  255 +#undef DFREG
  256 +#define SFREG 25
  257 +#include "fop_template.c"
  258 +#undef SFREG
  259 +#define SFREG 26
  260 +#define DFREG 26
  261 +#include "fop_template.c"
  262 +#undef SFREG
  263 +#undef DFREG
  264 +#define SFREG 27
  265 +#include "fop_template.c"
  266 +#undef SFREG
  267 +#define SFREG 28
  268 +#define DFREG 28
  269 +#include "fop_template.c"
  270 +#undef SFREG
  271 +#undef DFREG
  272 +#define SFREG 29
  273 +#include "fop_template.c"
  274 +#undef SFREG
  275 +#define SFREG 30
  276 +#define DFREG 30
  277 +#include "fop_template.c"
  278 +#undef SFREG
  279 +#undef DFREG
  280 +#define SFREG 31
  281 +#include "fop_template.c"
  282 +#undef SFREG
  283 +
  284 +#define FTN
  285 +#include "fop_template.c"
  286 +#undef FTN
  287 +
  288 +#endif
  289 +
152 290 void op_dup_T0 (void)
153 291 {
154 292 T2 = T0;
... ... @@ -562,6 +700,353 @@ void op_mtc0 (void)
562 700 RETURN();
563 701 }
564 702  
  703 +#ifdef MIPS_USES_FPU
  704 +
  705 +#if 0
  706 +# define DEBUG_FPU_STATE() CALL_FROM_TB1(dump_fpu, env)
  707 +#else
  708 +# define DEBUG_FPU_STATE() do { } while(0)
  709 +#endif
  710 +
  711 +void op_cp1_enabled(void)
  712 +{
  713 + if (!(env->CP0_Status & (1 << CP0St_CU1))) {
  714 + CALL_FROM_TB2(do_raise_exception_err, EXCP_CpU, 1);
  715 + }
  716 + RETURN();
  717 +}
  718 +
  719 +/* CP1 functions */
  720 +void op_cfc1 (void)
  721 +{
  722 + if (T1 == 0) {
  723 + T0 = env->fcr0;
  724 + }
  725 + else {
  726 + /* fetch fcr31, masking unused bits */
  727 + T0 = env->fcr31 & 0x0183FFFF;
  728 + }
  729 + DEBUG_FPU_STATE();
  730 + RETURN();
  731 +}
  732 +
  733 +/* convert MIPS rounding mode in FCR31 to IEEE library */
  734 +unsigned int ieee_rm[] = {
  735 + float_round_nearest_even,
  736 + float_round_to_zero,
  737 + float_round_up,
  738 + float_round_down
  739 +};
  740 +
  741 +#define RESTORE_ROUNDING_MODE \
  742 + set_float_rounding_mode(ieee_rm[env->fcr31 & 3], &env->fp_status)
  743 +
  744 +void op_ctc1 (void)
  745 +{
  746 + if (T1 == 0) {
  747 + /* XXX should this throw an exception?
  748 + * don't write to FCR0.
  749 + * env->fcr0 = T0;
  750 + */
  751 + }
  752 + else {
  753 + /* store new fcr31, masking unused bits */
  754 + env->fcr31 = T0 & 0x0183FFFF;
  755 +
  756 + /* set rounding mode */
  757 + RESTORE_ROUNDING_MODE;
  758 +
  759 +#ifndef CONFIG_SOFTFLOAT
  760 + /* no floating point exception for native float */
  761 + SET_FP_ENABLE(env->fcr31, 0);
  762 +#endif
  763 + }
  764 + DEBUG_FPU_STATE();
  765 + RETURN();
  766 +}
  767 +
  768 +void op_mfc1 (void)
  769 +{
  770 + T0 = WT0;
  771 + DEBUG_FPU_STATE();
  772 + RETURN();
  773 +}
  774 +
  775 +void op_mtc1 (void)
  776 +{
  777 + WT0 = T0;
  778 + DEBUG_FPU_STATE();
  779 + RETURN();
  780 +}
  781 +
  782 +/* Float support.
  783 + Single precition routines have a "s" suffix, double precision a
  784 + "d" suffix. */
  785 +
  786 +#define FLOAT_OP(name, p) void OPPROTO op_float_##name##_##p(void)
  787 +
  788 +FLOAT_OP(cvtd, w)
  789 +{
  790 + FDT2 = int32_to_float64(WT0, &env->fp_status);
  791 + DEBUG_FPU_STATE();
  792 + RETURN();
  793 +}
  794 +FLOAT_OP(cvts, w)
  795 +{
  796 + FST2 = int32_to_float32(WT0, &env->fp_status);
  797 + DEBUG_FPU_STATE();
  798 + RETURN();
  799 +}
  800 +FLOAT_OP(cvtw, s)
  801 +{
  802 + WT2 = float32_to_int32(FST0, &env->fp_status);
  803 + DEBUG_FPU_STATE();
  804 + RETURN();
  805 +}
  806 +FLOAT_OP(cvtw, d)
  807 +{
  808 + WT2 = float64_to_int32(FDT0, &env->fp_status);
  809 + DEBUG_FPU_STATE();
  810 + RETURN();
  811 +}
  812 +
  813 +FLOAT_OP(roundw, d)
  814 +{
  815 + set_float_rounding_mode(float_round_nearest_even, &env->fp_status);
  816 + WT2 = float64_round_to_int(FDT0, &env->fp_status);
  817 + RESTORE_ROUNDING_MODE;
  818 +
  819 + DEBUG_FPU_STATE();
  820 + RETURN();
  821 +}
  822 +FLOAT_OP(roundw, s)
  823 +{
  824 + set_float_rounding_mode(float_round_nearest_even, &env->fp_status);
  825 + WT2 = float32_round_to_int(FST0, &env->fp_status);
  826 + RESTORE_ROUNDING_MODE;
  827 + DEBUG_FPU_STATE();
  828 + RETURN();
  829 +}
  830 +
  831 +FLOAT_OP(truncw, d)
  832 +{
  833 + WT2 = float64_to_int32_round_to_zero(FDT0, &env->fp_status);
  834 + DEBUG_FPU_STATE();
  835 + RETURN();
  836 +}
  837 +FLOAT_OP(truncw, s)
  838 +{
  839 + WT2 = float32_to_int32_round_to_zero(FST0, &env->fp_status);
  840 + DEBUG_FPU_STATE();
  841 + RETURN();
  842 +}
  843 +
  844 +FLOAT_OP(ceilw, d)
  845 +{
  846 + set_float_rounding_mode(float_round_up, &env->fp_status);
  847 + WT2 = float64_round_to_int(FDT0, &env->fp_status);
  848 + RESTORE_ROUNDING_MODE;
  849 +
  850 + DEBUG_FPU_STATE();
  851 + RETURN();
  852 +}
  853 +FLOAT_OP(ceilw, s)
  854 +{
  855 + set_float_rounding_mode(float_round_up, &env->fp_status);
  856 + WT2 = float32_round_to_int(FST0, &env->fp_status);
  857 + RESTORE_ROUNDING_MODE;
  858 + DEBUG_FPU_STATE();
  859 + RETURN();
  860 +}
  861 +
  862 +FLOAT_OP(floorw, d)
  863 +{
  864 + set_float_rounding_mode(float_round_down, &env->fp_status);
  865 + WT2 = float64_round_to_int(FDT0, &env->fp_status);
  866 + RESTORE_ROUNDING_MODE;
  867 +
  868 + DEBUG_FPU_STATE();
  869 + RETURN();
  870 +}
  871 +FLOAT_OP(floorw, s)
  872 +{
  873 + set_float_rounding_mode(float_round_down, &env->fp_status);
  874 + WT2 = float32_round_to_int(FST0, &env->fp_status);
  875 + RESTORE_ROUNDING_MODE;
  876 + DEBUG_FPU_STATE();
  877 + RETURN();
  878 +}
  879 +
  880 +/* binary operations */
  881 +#define FLOAT_BINOP(name) \
  882 +FLOAT_OP(name, d) \
  883 +{ \
  884 + FDT2 = float64_ ## name (FDT0, FDT1, &env->fp_status); \
  885 + DEBUG_FPU_STATE(); \
  886 +} \
  887 +FLOAT_OP(name, s) \
  888 +{ \
  889 + FST2 = float32_ ## name (FST0, FST1, &env->fp_status); \
  890 + DEBUG_FPU_STATE(); \
  891 +}
  892 +FLOAT_BINOP(add)
  893 +FLOAT_BINOP(sub)
  894 +FLOAT_BINOP(mul)
  895 +FLOAT_BINOP(div)
  896 +#undef FLOAT_BINOP
  897 +
  898 +/* unary operations, modifying fp status */
  899 +#define FLOAT_UNOP(name) \
  900 +FLOAT_OP(name, d) \
  901 +{ \
  902 + FDT2 = float64_ ## name(FDT0, &env->fp_status); \
  903 + DEBUG_FPU_STATE(); \
  904 +} \
  905 +FLOAT_OP(name, s) \
  906 +{ \
  907 + FST2 = float32_ ## name(FST0, &env->fp_status); \
  908 + DEBUG_FPU_STATE(); \
  909 +}
  910 +FLOAT_UNOP(sqrt)
  911 +#undef FLOAT_UNOP
  912 +
  913 +/* unary operations, not modifying fp status */
  914 +#define FLOAT_UNOP(name) \
  915 +FLOAT_OP(name, d) \
  916 +{ \
  917 + FDT2 = float64_ ## name(FDT0); \
  918 + DEBUG_FPU_STATE(); \
  919 +} \
  920 +FLOAT_OP(name, s) \
  921 +{ \
  922 + FST2 = float32_ ## name(FST0); \
  923 + DEBUG_FPU_STATE(); \
  924 +}
  925 +FLOAT_UNOP(abs)
  926 +FLOAT_UNOP(chs)
  927 +#undef FLOAT_UNOP
  928 +
  929 +FLOAT_OP(mov, d)
  930 +{
  931 + FDT2 = FDT0;
  932 + DEBUG_FPU_STATE();
  933 + RETURN();
  934 +}
  935 +FLOAT_OP(mov, s)
  936 +{
  937 + FST2 = FST0;
  938 + DEBUG_FPU_STATE();
  939 + RETURN();
  940 +}
  941 +
  942 +#ifdef CONFIG_SOFTFLOAT
  943 +#define clear_invalid() do { \
  944 + int flags = get_float_exception_flags(&env->fp_status); \
  945 + flags &= ~float_flag_invalid; \
  946 + set_float_exception_flags(flags, &env->fp_status); \
  947 +} while(0)
  948 +#else
  949 +#define clear_invalid() do { } while(0)
  950 +#endif
  951 +
  952 +extern void dump_fpu_s(CPUState *env);
  953 +
  954 +#define FOP_COND(fmt, op, sig, cond) \
  955 +void op_cmp_ ## fmt ## _ ## op (void) \
  956 +{ \
  957 + if (cond) \
  958 + SET_FP_COND(env->fcr31); \
  959 + else \
  960 + CLEAR_FP_COND(env->fcr31); \
  961 + if (!sig) \
  962 + clear_invalid(); \
  963 + /*CALL_FROM_TB1(dump_fpu_s, env);*/ \
  964 + DEBUG_FPU_STATE(); \
  965 + RETURN(); \
  966 +}
  967 +
  968 +flag float64_is_unordered(float64 a, float64 b STATUS_PARAM)
  969 +{
  970 + extern flag float64_is_nan( float64 a );
  971 + if (float64_is_nan(a) || float64_is_nan(b)) {
  972 + float_raise(float_flag_invalid, status);
  973 + return 1;
  974 + }
  975 + else {
  976 + return 0;
  977 + }
  978 +}
  979 +
  980 +FOP_COND(d, f, 0, 0)
  981 +FOP_COND(d, un, 0, float64_is_unordered(FDT1, FDT0, &env->fp_status))
  982 +FOP_COND(d, eq, 0, float64_eq(FDT0, FDT1, &env->fp_status))
  983 +FOP_COND(d, ueq, 0, float64_is_unordered(FDT1, FDT0, &env->fp_status) || float64_eq(FDT0, FDT1, &env->fp_status))
  984 +FOP_COND(d, olt, 0, float64_lt(FDT0, FDT1, &env->fp_status))
  985 +FOP_COND(d, ult, 0, float64_is_unordered(FDT1, FDT0, &env->fp_status) || float64_lt(FDT0, FDT1, &env->fp_status))
  986 +FOP_COND(d, ole, 0, float64_le(FDT0, FDT1, &env->fp_status))
  987 +FOP_COND(d, ule, 0, float64_is_unordered(FDT1, FDT0, &env->fp_status) || float64_le(FDT0, FDT1, &env->fp_status))
  988 +/* NOTE: the comma operator will make "cond" to eval to false,
  989 + * but float*_is_unordered() is still called
  990 + */
  991 +FOP_COND(d, sf, 1, (float64_is_unordered(FDT0, FDT1, &env->fp_status), 0))
  992 +FOP_COND(d, ngle,1, float64_is_unordered(FDT1, FDT0, &env->fp_status))
  993 +FOP_COND(d, seq, 1, float64_eq(FDT0, FDT1, &env->fp_status))
  994 +FOP_COND(d, ngl, 1, float64_is_unordered(FDT1, FDT0, &env->fp_status) || float64_eq(FDT0, FDT1, &env->fp_status))
  995 +FOP_COND(d, lt, 1, float64_lt(FDT0, FDT1, &env->fp_status))
  996 +FOP_COND(d, nge, 1, float64_is_unordered(FDT1, FDT0, &env->fp_status) || float64_lt(FDT0, FDT1, &env->fp_status))
  997 +FOP_COND(d, le, 1, float64_le(FDT0, FDT1, &env->fp_status))
  998 +FOP_COND(d, ngt, 1, float64_is_unordered(FDT1, FDT0, &env->fp_status) || float64_le(FDT0, FDT1, &env->fp_status))
  999 +
  1000 +flag float32_is_unordered(float32 a, float32 b STATUS_PARAM)
  1001 +{
  1002 + extern flag float32_is_nan( float32 a );
  1003 + if (float32_is_nan(a) || float32_is_nan(b)) {
  1004 + float_raise(float_flag_invalid, status);
  1005 + return 1;
  1006 + }
  1007 + else {
  1008 + return 0;
  1009 + }
  1010 +}
  1011 +
  1012 +/* NOTE: the comma operator will make "cond" to eval to false,
  1013 + * but float*_is_unordered() is still called
  1014 + */
  1015 +FOP_COND(s, f, 0, 0)
  1016 +FOP_COND(s, un, 0, float32_is_unordered(FST1, FST0, &env->fp_status))
  1017 +FOP_COND(s, eq, 0, float32_eq(FST0, FST1, &env->fp_status))
  1018 +FOP_COND(s, ueq, 0, float32_is_unordered(FST1, FST0, &env->fp_status) || float32_eq(FST0, FST1, &env->fp_status))
  1019 +FOP_COND(s, olt, 0, float32_lt(FST0, FST1, &env->fp_status))
  1020 +FOP_COND(s, ult, 0, float32_is_unordered(FST1, FST0, &env->fp_status) || float32_lt(FST0, FST1, &env->fp_status))
  1021 +FOP_COND(s, ole, 0, float32_le(FST0, FST1, &env->fp_status))
  1022 +FOP_COND(s, ule, 0, float32_is_unordered(FST1, FST0, &env->fp_status) || float32_le(FST0, FST1, &env->fp_status))
  1023 +/* NOTE: the comma operator will make "cond" to eval to false,
  1024 + * but float*_is_unordered() is still called
  1025 + */
  1026 +FOP_COND(s, sf, 1, (float32_is_unordered(FST0, FST1, &env->fp_status), 0))
  1027 +FOP_COND(s, ngle,1, float32_is_unordered(FST1, FST0, &env->fp_status))
  1028 +FOP_COND(s, seq, 1, float32_eq(FST0, FST1, &env->fp_status))
  1029 +FOP_COND(s, ngl, 1, float32_is_unordered(FST1, FST0, &env->fp_status) || float32_eq(FST0, FST1, &env->fp_status))
  1030 +FOP_COND(s, lt, 1, float32_lt(FST0, FST1, &env->fp_status))
  1031 +FOP_COND(s, nge, 1, float32_is_unordered(FST1, FST0, &env->fp_status) || float32_lt(FST0, FST1, &env->fp_status))
  1032 +FOP_COND(s, le, 1, float32_le(FST0, FST1, &env->fp_status))
  1033 +FOP_COND(s, ngt, 1, float32_is_unordered(FST1, FST0, &env->fp_status) || float32_le(FST0, FST1, &env->fp_status))
  1034 +
  1035 +void op_bc1f (void)
  1036 +{
  1037 + T0 = ! IS_FP_COND_SET(env->fcr31);
  1038 + DEBUG_FPU_STATE();
  1039 + RETURN();
  1040 +}
  1041 +
  1042 +void op_bc1t (void)
  1043 +{
  1044 + T0 = IS_FP_COND_SET(env->fcr31);
  1045 + DEBUG_FPU_STATE();
  1046 + RETURN();
  1047 +}
  1048 +#endif /* MIPS_USES_FPU */
  1049 +
565 1050 #if defined(MIPS_USES_R4K_TLB)
566 1051 void op_tlbwi (void)
567 1052 {
... ...
target-mips/op_helper.c
... ... @@ -529,6 +529,47 @@ void do_mtc0 (int reg, int sel)
529 529 return;
530 530 }
531 531  
  532 +#ifdef MIPS_USES_FPU
  533 +#include "softfloat.h"
  534 +
  535 +void fpu_handle_exception(void)
  536 +{
  537 +#ifdef CONFIG_SOFTFLOAT
  538 + int flags = get_float_exception_flags(&env->fp_status);
  539 + unsigned int cpuflags = 0, enable, cause = 0;
  540 +
  541 + enable = GET_FP_ENABLE(env->fcr31);
  542 +
  543 + /* determine current flags */
  544 + if (flags & float_flag_invalid) {
  545 + cpuflags |= FP_INVALID;
  546 + cause |= FP_INVALID & enable;
  547 + }
  548 + if (flags & float_flag_divbyzero) {
  549 + cpuflags |= FP_DIV0;
  550 + cause |= FP_DIV0 & enable;
  551 + }
  552 + if (flags & float_flag_overflow) {
  553 + cpuflags |= FP_OVERFLOW;
  554 + cause |= FP_OVERFLOW & enable;
  555 + }
  556 + if (flags & float_flag_underflow) {
  557 + cpuflags |= FP_UNDERFLOW;
  558 + cause |= FP_UNDERFLOW & enable;
  559 + }
  560 + if (flags & float_flag_inexact) {
  561 + cpuflags |= FP_INEXACT;
  562 + cause |= FP_INEXACT & enable;
  563 + }
  564 + SET_FP_FLAGS(env->fcr31, cpuflags);
  565 + SET_FP_CAUSE(env->fcr31, cause);
  566 +#else
  567 + SET_FP_FLAGS(env->fcr31, 0);
  568 + SET_FP_CAUSE(env->fcr31, 0);
  569 +#endif
  570 +}
  571 +#endif /* MIPS_USES_FPU */
  572 +
532 573 /* TLB management */
533 574 #if defined(MIPS_USES_R4K_TLB)
534 575 static void invalidate_tlb (int idx)
... ...
target-mips/op_mem.c
... ... @@ -118,3 +118,26 @@ void glue(op_sc, MEMSUFFIX) (void)
118 118 }
119 119 RETURN();
120 120 }
  121 +
  122 +#ifdef MIPS_USES_FPU
  123 +void glue(op_lwc1, MEMSUFFIX) (void)
  124 +{
  125 + WT0 = glue(ldl, MEMSUFFIX)(T0);
  126 + RETURN();
  127 +}
  128 +void glue(op_swc1, MEMSUFFIX) (void)
  129 +{
  130 + glue(stl, MEMSUFFIX)(T0, WT0);
  131 + RETURN();
  132 +}
  133 +void glue(op_ldc1, MEMSUFFIX) (void)
  134 +{
  135 + DT0 = glue(ldq, MEMSUFFIX)(T0);
  136 + RETURN();
  137 +}
  138 +void glue(op_sdc1, MEMSUFFIX) (void)
  139 +{
  140 + glue(stq, MEMSUFFIX)(T0, DT0);
  141 + RETURN();
  142 +}
  143 +#endif
... ...
target-mips/translate.c
... ... @@ -2,6 +2,7 @@
2 2 * MIPS32 emulation for qemu: main translation routines.
3 3 *
4 4 * Copyright (c) 2004-2005 Jocelyn Mayer
  5 + * Copyright (c) 2006 Marius Groeger (FPU operations)
5 6 *
6 7 * This library is free software; you can redistribute it and/or
7 8 * modify it under the terms of the GNU Lesser General Public
... ... @@ -217,6 +218,16 @@ enum {
217 218 OPC_WAIT = 0x20 | EXT_CP0,
218 219 };
219 220  
  221 +#ifdef MIPS_USES_FPU
  222 +enum {
  223 + /* Coprocessor 1 (FPU) */
  224 + OPC_MFC1 = 0x00 | EXT_CP1,
  225 + OPC_MTC1 = 0x04 | EXT_CP1,
  226 + OPC_CFC1 = 0x02 | EXT_CP1,
  227 + OPC_CTC1 = 0x06 | EXT_CP1,
  228 +};
  229 +#endif
  230 +
220 231 const unsigned char *regnames[] =
221 232 { "r0", "at", "v0", "v1", "a0", "a1", "a2", "a3",
222 233 "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7",
... ... @@ -248,6 +259,92 @@ GEN32(gen_op_load_gpr_T2, gen_op_load_gpr_T2_gpr);
248 259 GEN32(gen_op_store_T0_gpr, gen_op_store_T0_gpr_gpr);
249 260 GEN32(gen_op_store_T1_gpr, gen_op_store_T1_gpr_gpr);
250 261  
  262 +#ifdef MIPS_USES_FPU
  263 +const unsigned char *fregnames[] =
  264 + { "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7",
  265 + "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15",
  266 + "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23",
  267 + "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31", };
  268 +
  269 +# define SFGEN32(func, NAME) \
  270 +static GenOpFunc *NAME ## _table [32] = { \
  271 +NAME ## 0, NAME ## 1, NAME ## 2, NAME ## 3, \
  272 +NAME ## 4, NAME ## 5, NAME ## 6, NAME ## 7, \
  273 +NAME ## 8, NAME ## 9, NAME ## 10, NAME ## 11, \
  274 +NAME ## 12, NAME ## 13, NAME ## 14, NAME ## 15, \
  275 +NAME ## 16, NAME ## 17, NAME ## 18, NAME ## 19, \
  276 +NAME ## 20, NAME ## 21, NAME ## 22, NAME ## 23, \
  277 +NAME ## 24, NAME ## 25, NAME ## 26, NAME ## 27, \
  278 +NAME ## 28, NAME ## 29, NAME ## 30, NAME ## 31, \
  279 +}; \
  280 +static inline void func(int n) \
  281 +{ \
  282 + NAME ## _table[n](); \
  283 +}
  284 +
  285 +# define DFGEN32(func, NAME) \
  286 +static GenOpFunc *NAME ## _table [32] = { \
  287 +NAME ## 0, 0, NAME ## 2, 0, \
  288 +NAME ## 4, 0, NAME ## 6, 0, \
  289 +NAME ## 8, 0, NAME ## 10, 0, \
  290 +NAME ## 12, 0, NAME ## 14, 0, \
  291 +NAME ## 16, 0, NAME ## 18, 0, \
  292 +NAME ## 20, 0, NAME ## 22, 0, \
  293 +NAME ## 24, 0, NAME ## 26, 0, \
  294 +NAME ## 28, 0, NAME ## 30, 0, \
  295 +}; \
  296 +static inline void func(int n) \
  297 +{ \
  298 + NAME ## _table[n](); \
  299 +}
  300 +
  301 +SFGEN32(gen_op_load_fpr_WT0, gen_op_load_fpr_WT0_fpr);
  302 +SFGEN32(gen_op_store_fpr_WT0, gen_op_store_fpr_WT0_fpr);
  303 +
  304 +SFGEN32(gen_op_load_fpr_WT1, gen_op_load_fpr_WT1_fpr);
  305 +SFGEN32(gen_op_store_fpr_WT1, gen_op_store_fpr_WT1_fpr);
  306 +
  307 +SFGEN32(gen_op_load_fpr_WT2, gen_op_load_fpr_WT2_fpr);
  308 +SFGEN32(gen_op_store_fpr_WT2, gen_op_store_fpr_WT2_fpr);
  309 +
  310 +DFGEN32(gen_op_load_fpr_DT0, gen_op_load_fpr_DT0_fpr);
  311 +DFGEN32(gen_op_store_fpr_DT0, gen_op_store_fpr_DT0_fpr);
  312 +
  313 +DFGEN32(gen_op_load_fpr_DT1, gen_op_load_fpr_DT1_fpr);
  314 +DFGEN32(gen_op_store_fpr_DT1, gen_op_store_fpr_DT1_fpr);
  315 +
  316 +DFGEN32(gen_op_load_fpr_DT2, gen_op_load_fpr_DT2_fpr);
  317 +DFGEN32(gen_op_store_fpr_DT2, gen_op_store_fpr_DT2_fpr);
  318 +
  319 +#define FOP_CONDS(fmt) \
  320 +static GenOpFunc * cond_ ## fmt ## _table[16] = { \
  321 + gen_op_cmp_ ## fmt ## _f, \
  322 + gen_op_cmp_ ## fmt ## _un, \
  323 + gen_op_cmp_ ## fmt ## _eq, \
  324 + gen_op_cmp_ ## fmt ## _ueq, \
  325 + gen_op_cmp_ ## fmt ## _olt, \
  326 + gen_op_cmp_ ## fmt ## _ult, \
  327 + gen_op_cmp_ ## fmt ## _ole, \
  328 + gen_op_cmp_ ## fmt ## _ule, \
  329 + gen_op_cmp_ ## fmt ## _sf, \
  330 + gen_op_cmp_ ## fmt ## _ngle, \
  331 + gen_op_cmp_ ## fmt ## _seq, \
  332 + gen_op_cmp_ ## fmt ## _ngl, \
  333 + gen_op_cmp_ ## fmt ## _lt, \
  334 + gen_op_cmp_ ## fmt ## _nge, \
  335 + gen_op_cmp_ ## fmt ## _le, \
  336 + gen_op_cmp_ ## fmt ## _ngt, \
  337 +}; \
  338 +static inline void gen_cmp_ ## fmt(int n) \
  339 +{ \
  340 + cond_ ## fmt ## _table[n](); \
  341 +}
  342 +
  343 +FOP_CONDS(d)
  344 +FOP_CONDS(s)
  345 +
  346 +#endif
  347 +
251 348 typedef struct DisasContext {
252 349 struct TranslationBlock *tb;
253 350 target_ulong pc, saved_pc;
... ... @@ -312,6 +409,20 @@ do { \
312 409 } \
313 410 } while (0)
314 411  
  412 +#ifdef MIPS_USES_FPU
  413 +
  414 +# define GEN_LOAD_FREG_FTN(FTn, Fn) \
  415 +do { \
  416 + glue(gen_op_load_fpr_, FTn)(Fn); \
  417 +} while (0)
  418 +
  419 +#define GEN_STORE_FTN_FREG(Fn, FTn) \
  420 +do { \
  421 + glue(gen_op_store_fpr_, FTn)(Fn); \
  422 +} while (0)
  423 +
  424 +#endif
  425 +
315 426 static inline void save_cpu_state (DisasContext *ctx, int do_save_pc)
316 427 {
317 428 #if defined MIPS_DEBUG_DISAS
... ... @@ -397,6 +508,12 @@ OP_LD_TABLE(bu);
397 508 OP_ST_TABLE(b);
398 509 OP_LD_TABLE(l);
399 510 OP_ST_TABLE(c);
  511 +#ifdef MIPS_USES_FPU
  512 +OP_LD_TABLE(wc1);
  513 +OP_ST_TABLE(wc1);
  514 +OP_LD_TABLE(dc1);
  515 +OP_ST_TABLE(dc1);
  516 +#endif
400 517  
401 518 /* Load and store */
402 519 static void gen_ldst (DisasContext *ctx, uint16_t opc, int rt,
... ... @@ -551,6 +668,56 @@ static void gen_ldst (DisasContext *ctx, uint16_t opc, int rt,
551 668 MIPS_DEBUG("%s %s, %d(%s)", opn, regnames[rt], offset, regnames[base]);
552 669 }
553 670  
  671 +#ifdef MIPS_USES_FPU
  672 +
  673 +/* Load and store */
  674 +static void gen_flt_ldst (DisasContext *ctx, uint16_t opc, int ft,
  675 + int base, int16_t offset)
  676 +{
  677 + const unsigned char *opn = "unk";
  678 +
  679 + if (base == 0) {
  680 + GEN_LOAD_IMM_TN(T0, offset);
  681 + } else if (offset == 0) {
  682 + gen_op_load_gpr_T0(base);
  683 + } else {
  684 + gen_op_load_gpr_T0(base);
  685 + gen_op_set_T1(offset);
  686 + gen_op_add();
  687 + }
  688 + /* Don't do NOP if destination is zero: we must perform the actual
  689 + * memory access
  690 + */
  691 + switch (opc) {
  692 + case OPC_LWC1:
  693 + op_ldst(lwc1);
  694 + GEN_STORE_FTN_FREG(ft, WT0);
  695 + opn = "lwc1";
  696 + break;
  697 + case OPC_SWC1:
  698 + GEN_LOAD_FREG_FTN(WT0, ft);
  699 + op_ldst(swc1);
  700 + opn = "swc1";
  701 + break;
  702 + case OPC_LDC1:
  703 + op_ldst(ldc1);
  704 + GEN_STORE_FTN_FREG(ft, DT0);
  705 + opn = "ldc1";
  706 + break;
  707 + case OPC_SDC1:
  708 + GEN_LOAD_FREG_FTN(DT0, ft);
  709 + op_ldst(sdc1);
  710 + opn = "sdc1";
  711 + break;
  712 + default:
  713 + MIPS_INVAL("float load/store");
  714 + generate_exception(ctx, EXCP_CpU);
  715 + return;
  716 + }
  717 + MIPS_DEBUG("%s %s, %d(%s)", opn, fregnames[ft], offset, regnames[base]);
  718 +}
  719 +#endif
  720 +
554 721 /* Arithmetic with immediate operand */
555 722 static void gen_arith_imm (DisasContext *ctx, uint16_t opc, int rt,
556 723 int rs, int16_t imm)
... ... @@ -1265,7 +1432,406 @@ static void gen_cp0 (DisasContext *ctx, uint16_t opc, int rt, int rd)
1265 1432 MIPS_DEBUG("%s %s %d", opn, regnames[rt], rd);
1266 1433 }
1267 1434  
  1435 +#ifdef MIPS_USES_FPU
  1436 +/* CP1 Branches (before delay slot) */
  1437 +static void gen_compute_branch1 (DisasContext *ctx, uint16_t cond,
  1438 + int32_t offset)
  1439 +{
  1440 + target_ulong btarget;
  1441 +
  1442 + btarget = ctx->pc + 4 + offset;
  1443 +
  1444 + switch (cond) {
  1445 + case 0x0000: /* bc1f */
  1446 + gen_op_bc1f();
  1447 + MIPS_DEBUG("bc1f %08x", btarget);
  1448 + goto not_likely;
  1449 + case 0x0002: /* bc1fl */
  1450 + gen_op_bc1f();
  1451 + MIPS_DEBUG("bc1fl %08x", btarget);
  1452 + goto likely;
  1453 + case 0x0001: /* bc1t */
  1454 + gen_op_bc1t();
  1455 + MIPS_DEBUG("bc1t %08x", btarget);
  1456 + not_likely:
  1457 + ctx->hflags |= MIPS_HFLAG_BC;
  1458 + break;
  1459 + case 0x0003: /* bc1tl */
  1460 + gen_op_bc1t();
  1461 + MIPS_DEBUG("bc1tl %08x", btarget);
  1462 + likely:
  1463 + ctx->hflags |= MIPS_HFLAG_BL;
  1464 + break;
  1465 + default:
  1466 + MIPS_INVAL("cp1 branch/jump");
  1467 + generate_exception(ctx, EXCP_RI);
  1468 + return;
  1469 + }
  1470 + gen_op_set_bcond();
  1471 +
  1472 + MIPS_DEBUG("enter ds: cond %02x target %08x",
  1473 + ctx->hflags, btarget);
  1474 + ctx->btarget = btarget;
  1475 +
  1476 + return;
  1477 +}
  1478 +
1268 1479 /* Coprocessor 1 (FPU) */
  1480 +static void gen_cp1 (DisasContext *ctx, uint16_t opc, int rt, int fs)
  1481 +{
  1482 + const unsigned char *opn = "unk";
  1483 +
  1484 + switch (opc) {
  1485 + case OPC_MFC1:
  1486 + GEN_LOAD_FREG_FTN(WT0, fs);
  1487 + gen_op_mfc1();
  1488 + GEN_STORE_TN_REG(rt, T0);
  1489 + opn = "mfc1";
  1490 + break;
  1491 + case OPC_MTC1:
  1492 + GEN_LOAD_REG_TN(T0, rt);
  1493 + gen_op_mtc1();
  1494 + GEN_STORE_FTN_FREG(fs, WT0);
  1495 + opn = "mtc1";
  1496 + break;
  1497 + case OPC_CFC1:
  1498 + if (fs != 0 && fs != 31) {
  1499 + MIPS_INVAL("cfc1 freg");
  1500 + generate_exception(ctx, EXCP_RI);
  1501 + return;
  1502 + }
  1503 + GEN_LOAD_IMM_TN(T1, fs);
  1504 + gen_op_cfc1();
  1505 + GEN_STORE_TN_REG(rt, T0);
  1506 + opn = "cfc1";
  1507 + break;
  1508 + case OPC_CTC1:
  1509 + if (fs != 0 && fs != 31) {
  1510 + MIPS_INVAL("ctc1 freg");
  1511 + generate_exception(ctx, EXCP_RI);
  1512 + return;
  1513 + }
  1514 + GEN_LOAD_IMM_TN(T1, fs);
  1515 + GEN_LOAD_REG_TN(T0, rt);
  1516 + gen_op_ctc1();
  1517 + opn = "ctc1";
  1518 + break;
  1519 + default:
  1520 + if (loglevel & CPU_LOG_TB_IN_ASM) {
  1521 + fprintf(logfile, "Invalid CP1 opcode: %08x %03x %03x %03x\n",
  1522 + ctx->opcode, ctx->opcode >> 26, ctx->opcode & 0x3F,
  1523 + ((ctx->opcode >> 16) & 0x1F));
  1524 + }
  1525 + generate_exception(ctx, EXCP_RI);
  1526 + return;
  1527 + }
  1528 + MIPS_DEBUG("%s %s %s", opn, regnames[rt], fregnames[fs]);
  1529 +}
  1530 +
  1531 +/* verify if floating point register is valid; an operation is not defined
  1532 + * if bit 0 of any register specification is set and the FR bit in the
  1533 + * Status register equals zero, since the register numbers specify an
  1534 + * even-odd pair of adjacent coprocessor general registers. When the FR bit
  1535 + * in the Status register equals one, both even and odd register numbers
  1536 + * are valid.
  1537 + *
  1538 + * Multiple float registers can be checked by calling
  1539 + * CHECK_FR(ctx, freg1 | freg2 | ... | fregN);
  1540 + */
  1541 +#define CHECK_FR(ctx, freg) do { \
  1542 + if (!((ctx)->CP0_Status & (1<<CP0St_FR)) && ((freg) & 1)) { \
  1543 + generate_exception(ctx, EXCP_RI); \
  1544 + return; \
  1545 + } \
  1546 + } while(0)
  1547 +
  1548 +#define FOP(func, fmt) (((fmt) << 21) | (func))
  1549 +
  1550 +static void gen_farith (DisasContext *ctx, int fmt, int ft, int fs, int fd, int func)
  1551 +{
  1552 + const unsigned char *opn = "unk";
  1553 + const char *condnames[] = {
  1554 + "c.f",
  1555 + "c.un",
  1556 + "c.eq",
  1557 + "c.ueq",
  1558 + "c.olt",
  1559 + "c.ult",
  1560 + "c.ole",
  1561 + "c.ule",
  1562 + "c.sf",
  1563 + "c.ngle",
  1564 + "c.seq",
  1565 + "c.ngl",
  1566 + "c.lt",
  1567 + "c.nge",
  1568 + "c.le",
  1569 + "c.ngt",
  1570 + };
  1571 + int binary = 0;
  1572 +
  1573 + switch (ctx->opcode & FOP(0x3f, 0x1f)) {
  1574 + case FOP(0, 17):
  1575 + CHECK_FR(ctx, fs | ft | fd);
  1576 + GEN_LOAD_FREG_FTN(DT0, fs);
  1577 + GEN_LOAD_FREG_FTN(DT1, ft);
  1578 + gen_op_float_add_d();
  1579 + GEN_STORE_FTN_FREG(fd, DT2);
  1580 + opn = "add.d";
  1581 + binary = 1;
  1582 + break;
  1583 + case FOP(1, 17):
  1584 + CHECK_FR(ctx, fs | ft | fd);
  1585 + GEN_LOAD_FREG_FTN(DT0, fs);
  1586 + GEN_LOAD_FREG_FTN(DT1, ft);
  1587 + gen_op_float_sub_d();
  1588 + GEN_STORE_FTN_FREG(fd, DT2);
  1589 + opn = "sub.d";
  1590 + binary = 1;
  1591 + break;
  1592 + case FOP(2, 17):
  1593 + CHECK_FR(ctx, fs | ft | fd);
  1594 + GEN_LOAD_FREG_FTN(DT0, fs);
  1595 + GEN_LOAD_FREG_FTN(DT1, ft);
  1596 + gen_op_float_mul_d();
  1597 + GEN_STORE_FTN_FREG(fd, DT2);
  1598 + opn = "mul.d";
  1599 + binary = 1;
  1600 + break;
  1601 + case FOP(3, 17):
  1602 + CHECK_FR(ctx, fs | ft | fd);
  1603 + GEN_LOAD_FREG_FTN(DT0, fs);
  1604 + GEN_LOAD_FREG_FTN(DT1, ft);
  1605 + gen_op_float_div_d();
  1606 + GEN_STORE_FTN_FREG(fd, DT2);
  1607 + opn = "div.d";
  1608 + binary = 1;
  1609 + break;
  1610 + case FOP(4, 17):
  1611 + CHECK_FR(ctx, fs | fd);
  1612 + GEN_LOAD_FREG_FTN(DT0, fs);
  1613 + gen_op_float_sqrt_d();
  1614 + GEN_STORE_FTN_FREG(fd, DT2);
  1615 + opn = "sqrt.d";
  1616 + break;
  1617 + case FOP(5, 17):
  1618 + CHECK_FR(ctx, fs | fd);
  1619 + GEN_LOAD_FREG_FTN(DT0, fs);
  1620 + gen_op_float_abs_d();
  1621 + GEN_STORE_FTN_FREG(fd, DT2);
  1622 + opn = "abs.d";
  1623 + break;
  1624 + case FOP(6, 17):
  1625 + CHECK_FR(ctx, fs | fd);
  1626 + GEN_LOAD_FREG_FTN(DT0, fs);
  1627 + gen_op_float_mov_d();
  1628 + GEN_STORE_FTN_FREG(fd, DT2);
  1629 + opn = "mov.d";
  1630 + break;
  1631 + case FOP(7, 17):
  1632 + CHECK_FR(ctx, fs | fd);
  1633 + GEN_LOAD_FREG_FTN(DT0, fs);
  1634 + gen_op_float_chs_d();
  1635 + GEN_STORE_FTN_FREG(fd, DT2);
  1636 + opn = "neg.d";
  1637 + break;
  1638 + /* 8 - round.l */
  1639 + /* 9 - trunc.l */
  1640 + /* 10 - ceil.l */
  1641 + /* 11 - floor.l */
  1642 + case FOP(12, 17):
  1643 + CHECK_FR(ctx, fs | fd);
  1644 + GEN_LOAD_FREG_FTN(DT0, fs);
  1645 + gen_op_float_roundw_d();
  1646 + GEN_STORE_FTN_FREG(fd, WT2);
  1647 + opn = "round.w.d";
  1648 + break;
  1649 + case FOP(13, 17):
  1650 + CHECK_FR(ctx, fs | fd);
  1651 + GEN_LOAD_FREG_FTN(DT0, fs);
  1652 + gen_op_float_truncw_d();
  1653 + GEN_STORE_FTN_FREG(fd, WT2);
  1654 + opn = "trunc.w.d";
  1655 + break;
  1656 + case FOP(14, 17):
  1657 + CHECK_FR(ctx, fs | fd);
  1658 + GEN_LOAD_FREG_FTN(DT0, fs);
  1659 + gen_op_float_ceilw_d();
  1660 + GEN_STORE_FTN_FREG(fd, WT2);
  1661 + opn = "ceil.w.d";
  1662 + break;
  1663 + case FOP(15, 17):
  1664 + CHECK_FR(ctx, fs | fd);
  1665 + GEN_LOAD_FREG_FTN(DT0, fs);
  1666 + gen_op_float_floorw_d();
  1667 + GEN_STORE_FTN_FREG(fd, WT2);
  1668 + opn = "ceil.w.d";
  1669 + break;
  1670 + case FOP(33, 20): /* cvt.d.w */
  1671 + CHECK_FR(ctx, fs | fd);
  1672 + GEN_LOAD_FREG_FTN(WT0, fs);
  1673 + gen_op_float_cvtd_w();
  1674 + GEN_STORE_FTN_FREG(fd, DT2);
  1675 + opn = "cvt.d.w";
  1676 + break;
  1677 + case FOP(48, 17):
  1678 + case FOP(49, 17):
  1679 + case FOP(50, 17):
  1680 + case FOP(51, 17):
  1681 + case FOP(52, 17):
  1682 + case FOP(53, 17):
  1683 + case FOP(54, 17):
  1684 + case FOP(55, 17):
  1685 + case FOP(56, 17):
  1686 + case FOP(57, 17):
  1687 + case FOP(58, 17):
  1688 + case FOP(59, 17):
  1689 + case FOP(60, 17):
  1690 + case FOP(61, 17):
  1691 + case FOP(62, 17):
  1692 + case FOP(63, 17):
  1693 + CHECK_FR(ctx, fs | ft);
  1694 + GEN_LOAD_FREG_FTN(DT0, fs);
  1695 + GEN_LOAD_FREG_FTN(DT1, ft);
  1696 + gen_cmp_d(func-48);
  1697 + opn = condnames[func-48];
  1698 + break;
  1699 + case FOP(0, 16):
  1700 + CHECK_FR(ctx, fs | ft | fd);
  1701 + GEN_LOAD_FREG_FTN(WT0, fs);
  1702 + GEN_LOAD_FREG_FTN(WT1, ft);
  1703 + gen_op_float_add_s();
  1704 + GEN_STORE_FTN_FREG(fd, WT2);
  1705 + opn = "add.s";
  1706 + binary = 1;
  1707 + break;
  1708 + case FOP(1, 16):
  1709 + CHECK_FR(ctx, fs | ft | fd);
  1710 + GEN_LOAD_FREG_FTN(WT0, fs);
  1711 + GEN_LOAD_FREG_FTN(WT1, ft);
  1712 + gen_op_float_sub_s();
  1713 + GEN_STORE_FTN_FREG(fd, WT2);
  1714 + opn = "sub.s";
  1715 + binary = 1;
  1716 + break;
  1717 + case FOP(2, 16):
  1718 + CHECK_FR(ctx, fs | ft | fd);
  1719 + GEN_LOAD_FREG_FTN(WT0, fs);
  1720 + GEN_LOAD_FREG_FTN(WT1, ft);
  1721 + gen_op_float_mul_s();
  1722 + GEN_STORE_FTN_FREG(fd, WT2);
  1723 + opn = "mul.s";
  1724 + binary = 1;
  1725 + break;
  1726 + case FOP(3, 16):
  1727 + CHECK_FR(ctx, fs | ft | fd);
  1728 + GEN_LOAD_FREG_FTN(WT0, fs);
  1729 + GEN_LOAD_FREG_FTN(WT1, ft);
  1730 + gen_op_float_div_s();
  1731 + GEN_STORE_FTN_FREG(fd, WT2);
  1732 + opn = "div.s";
  1733 + binary = 1;
  1734 + break;
  1735 + case FOP(4, 16):
  1736 + CHECK_FR(ctx, fs | fd);
  1737 + GEN_LOAD_FREG_FTN(WT0, fs);
  1738 + gen_op_float_sqrt_s();
  1739 + GEN_STORE_FTN_FREG(fd, WT2);
  1740 + opn = "sqrt.s";
  1741 + break;
  1742 + case FOP(5, 16):
  1743 + CHECK_FR(ctx, fs | fd);
  1744 + GEN_LOAD_FREG_FTN(WT0, fs);
  1745 + gen_op_float_abs_s();
  1746 + GEN_STORE_FTN_FREG(fd, WT2);
  1747 + opn = "abs.s";
  1748 + break;
  1749 + case FOP(6, 16):
  1750 + CHECK_FR(ctx, fs | fd);
  1751 + GEN_LOAD_FREG_FTN(WT0, fs);
  1752 + gen_op_float_mov_s();
  1753 + GEN_STORE_FTN_FREG(fd, WT2);
  1754 + opn = "mov.s";
  1755 + break;
  1756 + case FOP(7, 16):
  1757 + CHECK_FR(ctx, fs | fd);
  1758 + GEN_LOAD_FREG_FTN(WT0, fs);
  1759 + gen_op_float_chs_s();
  1760 + GEN_STORE_FTN_FREG(fd, WT2);
  1761 + opn = "neg.s";
  1762 + break;
  1763 + case FOP(12, 16):
  1764 + CHECK_FR(ctx, fs | fd);
  1765 + GEN_LOAD_FREG_FTN(WT0, fs);
  1766 + gen_op_float_roundw_s();
  1767 + GEN_STORE_FTN_FREG(fd, WT2);
  1768 + opn = "round.w.s";
  1769 + break;
  1770 + case FOP(13, 16):
  1771 + CHECK_FR(ctx, fs | fd);
  1772 + GEN_LOAD_FREG_FTN(WT0, fs);
  1773 + gen_op_float_truncw_s();
  1774 + GEN_STORE_FTN_FREG(fd, WT2);
  1775 + opn = "trunc.w.s";
  1776 + break;
  1777 + case FOP(32, 20): /* cvt.s.w */
  1778 + CHECK_FR(ctx, fs | fd);
  1779 + GEN_LOAD_FREG_FTN(WT0, fs);
  1780 + gen_op_float_cvts_w();
  1781 + GEN_STORE_FTN_FREG(fd, WT2);
  1782 + opn = "cvt.s.w";
  1783 + break;
  1784 + case FOP(36, 16): /* cvt.w.s */
  1785 + CHECK_FR(ctx, fs | fd);
  1786 + GEN_LOAD_FREG_FTN(WT0, fs);
  1787 + gen_op_float_cvtw_s();
  1788 + GEN_STORE_FTN_FREG(fd, WT2);
  1789 + opn = "cvt.w.s";
  1790 + break;
  1791 + case FOP(36, 17): /* cvt.w.d */
  1792 + CHECK_FR(ctx, fs | fd);
  1793 + GEN_LOAD_FREG_FTN(WT0, fs);
  1794 + gen_op_float_cvtw_d();
  1795 + GEN_STORE_FTN_FREG(fd, WT2);
  1796 + opn = "cvt.w.d";
  1797 + break;
  1798 + case FOP(48, 16):
  1799 + case FOP(49, 16):
  1800 + case FOP(50, 16):
  1801 + case FOP(51, 16):
  1802 + case FOP(52, 16):
  1803 + case FOP(53, 16):
  1804 + case FOP(54, 16):
  1805 + case FOP(55, 16):
  1806 + case FOP(56, 16):
  1807 + case FOP(57, 16):
  1808 + case FOP(58, 16):
  1809 + case FOP(59, 16):
  1810 + case FOP(60, 16):
  1811 + case FOP(61, 16):
  1812 + case FOP(62, 16):
  1813 + case FOP(63, 16):
  1814 + CHECK_FR(ctx, fs | ft);
  1815 + GEN_LOAD_FREG_FTN(WT0, fs);
  1816 + GEN_LOAD_FREG_FTN(WT1, ft);
  1817 + gen_cmp_s(func-48);
  1818 + opn = condnames[func-48];
  1819 + break;
  1820 + default:
  1821 + if (loglevel & CPU_LOG_TB_IN_ASM) {
  1822 + fprintf(logfile, "Invalid arith function: %08x %03x %03x %03x\n",
  1823 + ctx->opcode, ctx->opcode >> 26, ctx->opcode & 0x3F,
  1824 + ((ctx->opcode >> 16) & 0x1F));
  1825 + }
  1826 + generate_exception(ctx, EXCP_RI);
  1827 + return;
  1828 + }
  1829 + if (binary)
  1830 + MIPS_DEBUG("%s %s, %s, %s", opn, fregnames[fd], fregnames[fs], fregnames[ft]);
  1831 + else
  1832 + MIPS_DEBUG("%s %s,%s", opn, fregnames[fd], fregnames[fs]);
  1833 +}
  1834 +#endif
1269 1835  
1270 1836 /* ISA extensions */
1271 1837 /* MIPS16 extension to MIPS32 */
... ... @@ -1495,9 +2061,39 @@ static void decode_opc (DisasContext *ctx)
1495 2061 case 0x35: /* LDC1 */
1496 2062 case 0x39: /* SWC1 */
1497 2063 case 0x3D: /* SDC1 */
  2064 +#if defined(MIPS_USES_FPU)
  2065 + gen_op_cp1_enabled();
  2066 + gen_flt_ldst(ctx, op, rt, rs, imm);
  2067 +#else
  2068 + generate_exception_err(ctx, EXCP_CpU, 1);
  2069 +#endif
  2070 + break;
  2071 +
1498 2072 case 0x11: /* CP1 opcode */
1499 2073 #if defined(MIPS_USES_FPU)
1500   - /* XXX: not correct */
  2074 + gen_op_cp1_enabled();
  2075 + op1 = ((ctx->opcode >> 21) & 0x1F);
  2076 + switch (op1) {
  2077 + case 0x00: /* mfc1 */
  2078 + case 0x02: /* cfc1 */
  2079 + case 0x04: /* mtc1 */
  2080 + case 0x06: /* ctc1 */
  2081 + gen_cp1(ctx, op1 | EXT_CP1, rt, rd);
  2082 + break;
  2083 + case 0x08: /* bc */
  2084 + gen_compute_branch1(ctx, rt, imm << 2);
  2085 + return;
  2086 + case 0x10: /* 16: fmt=single fp */
  2087 + case 0x11: /* 17: fmt=double fp */
  2088 + case 0x14: /* 20: fmt=32bit fixed */
  2089 + case 0x15: /* 21: fmt=64bit fixed */
  2090 + gen_farith(ctx, op1, rt, rd, sa, ctx->opcode & 0x3f);
  2091 + break;
  2092 + default:
  2093 + generate_exception_err(ctx, EXCP_RI, 1);
  2094 + break;
  2095 + }
  2096 + break;
1501 2097 #else
1502 2098 generate_exception_err(ctx, EXCP_CpU, 1);
1503 2099 #endif
... ... @@ -1586,7 +2182,7 @@ int gen_intermediate_code_internal (CPUState *env, TranslationBlock *tb,
1586 2182 int j, lj = -1;
1587 2183  
1588 2184 if (search_pc && loglevel)
1589   - fprintf (logfile, "search pc %d\n", search_pc);
  2185 + fprintf (logfile, "search pc %d\n", search_pc);
1590 2186  
1591 2187 pc_start = tb->pc;
1592 2188 gen_opc_ptr = gen_opc_buf;
... ... @@ -1696,7 +2292,7 @@ done_generating:
1696 2292 #endif
1697 2293 if (loglevel & CPU_LOG_TB_IN_ASM) {
1698 2294 fprintf(logfile, "IN: %s\n", lookup_symbol(pc_start));
1699   - target_disas(logfile, pc_start, ctx.pc - pc_start, 0);
  2295 + target_disas(logfile, pc_start, ctx.pc - pc_start, 0);
1700 2296 fprintf(logfile, "\n");
1701 2297 }
1702 2298 if (loglevel & CPU_LOG_TB_OP) {
... ... @@ -1722,6 +2318,42 @@ int gen_intermediate_code_pc (CPUState *env, struct TranslationBlock *tb)
1722 2318 return gen_intermediate_code_internal(env, tb, 1);
1723 2319 }
1724 2320  
  2321 +#ifdef MIPS_USES_FPU
  2322 +void fpu_dump_state(CPUState *env, FILE *f,
  2323 + int (*fpu_fprintf)(FILE *f, const char *fmt, ...),
  2324 + int flags)
  2325 +{
  2326 + int i;
  2327 +
  2328 +# define printfpr(fp) do { \
  2329 + fpu_fprintf(f, "w:%08x d:%08lx%08lx fd:%g fs:%g\n", \
  2330 + (fp)->w[FP_ENDIAN_IDX], (fp)->w[0], (fp)->w[1], (fp)->fd, (fp)->fs[FP_ENDIAN_IDX]); \
  2331 + } while(0)
  2332 +
  2333 + fpu_fprintf(f, "CP1 FCR0 0x%08x FCR31 0x%08x SR.FR %d\n",
  2334 + env->fcr0, env->fcr31,
  2335 + (env->CP0_Status & (1<<CP0St_FR)) != 0);
  2336 + fpu_fprintf(f, "FT0: "); printfpr(&env->ft0);
  2337 + fpu_fprintf(f, "FT1: "); printfpr(&env->ft1);
  2338 + fpu_fprintf(f, "FT2: "); printfpr(&env->ft2);
  2339 + for(i=0; i < 32; i+=2) {
  2340 + fpu_fprintf(f, "f%02d: ", i);
  2341 + printfpr(FPR(env, i));
  2342 + }
  2343 +
  2344 +#undef printfpr
  2345 +}
  2346 +
  2347 +void dump_fpu(CPUState *env)
  2348 +{
  2349 + if (loglevel) {
  2350 + fprintf(logfile, "pc=0x%08x HI=0x%08x LO=0x%08x ds %04x %08x %d\n",
  2351 + env->PC, env->HI, env->LO, env->hflags, env->btarget, env->bcond);
  2352 + fpu_dump_state(env, logfile, fprintf, 0);
  2353 + }
  2354 +}
  2355 +#endif /* MIPS_USES_FPU */
  2356 +
1725 2357 void cpu_dump_state (CPUState *env, FILE *f,
1726 2358 int (*cpu_fprintf)(FILE *f, const char *fmt, ...),
1727 2359 int flags)
... ... @@ -1751,6 +2383,9 @@ void cpu_dump_state (CPUState *env, FILE *f,
1751 2383 c0_status, env->CP0_Cause, env->CP0_EPC);
1752 2384 cpu_fprintf(f, " Config0 0x%08x Config1 0x%08x LLAddr 0x%08x\n",
1753 2385 env->CP0_Config0, env->CP0_Config1, env->CP0_LLAddr);
  2386 +#ifdef MIPS_USES_FPU
  2387 + fpu_dump_state(env, f, cpu_fprintf, flags);
  2388 +#endif
1754 2389 }
1755 2390  
1756 2391 CPUMIPSState *cpu_mips_init (void)
... ... @@ -1788,5 +2423,8 @@ CPUMIPSState *cpu_mips_init (void)
1788 2423 #if defined(CONFIG_USER_ONLY)
1789 2424 env->hflags |= MIPS_HFLAG_UM;
1790 2425 #endif
  2426 +#ifdef MIPS_USES_FPU
  2427 + env->fcr0 = MIPS_FCR0;
  2428 +#endif
1791 2429 return env;
1792 2430 }
... ...