MIPS FPU support (Marius Goeger)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1964 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
parent
180b700dc7
commit
6ea83fedc8
2
configure
vendored
2
configure
vendored
@ -819,6 +819,8 @@ elif test "$target_cpu" = "mips" -o "$target_cpu" = "mipsel" ; then
|
||||
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
|
||||
|
@ -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 @@ struct CPUMIPSState {
|
||||
#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 @@ struct CPUMIPSState {
|
||||
#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 @@ struct CPUMIPSState {
|
||||
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;
|
||||
|
@ -21,13 +21,20 @@ register host_uint_t T1 asm(AREG2);
|
||||
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_tlbwi (void);
|
||||
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);
|
||||
|
99
target-mips/fop_template.c
Normal file
99
target-mips/fop_template.c
Normal file
@ -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
|
@ -24,6 +24,12 @@ enum {
|
||||
/* 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 @@ enum {
|
||||
/* 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 @@ enum {
|
||||
#error "MIPS CPU not defined"
|
||||
/* Remainder for other flags */
|
||||
//#define TARGET_MIPS64
|
||||
//define MIPS_USES_FPU
|
||||
//#define MIPS_USES_FPU
|
||||
#endif
|
||||
|
||||
#endif /* !defined (__QEMU_MIPS_DEFS_H__) */
|
||||
|
485
target-mips/op.c
485
target-mips/op.c
@ -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 @@ CALL_FROM_TB2(func, arg0, arg1);
|
||||
#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 @@ void op_mtc0 (void)
|
||||
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)
|
||||
{
|
||||
|
@ -529,6 +529,47 @@ void do_mtc0 (int reg, int sel)
|
||||
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)
|
||||
|
@ -118,3 +118,26 @@ void glue(op_sc, MEMSUFFIX) (void)
|
||||
}
|
||||
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
|
||||
|
@ -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 @@ enum {
|
||||
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_load_gpr_T2, gen_op_load_gpr_T2_gpr);
|
||||
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 @@ do { \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#ifdef MIPS_USES_FPU
|
||||
|
||||
# define GEN_LOAD_FREG_FTN(FTn, Fn) \
|
||||
do { \
|
||||
glue(gen_op_load_fpr_, FTn)(Fn); \
|
||||
} while (0)
|
||||
|
||||
#define GEN_STORE_FTN_FREG(Fn, FTn) \
|
||||
do { \
|
||||
glue(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_LD_TABLE(bu);
|
||||
OP_ST_TABLE(b);
|
||||
OP_LD_TABLE(l);
|
||||
OP_ST_TABLE(c);
|
||||
#ifdef MIPS_USES_FPU
|
||||
OP_LD_TABLE(wc1);
|
||||
OP_ST_TABLE(wc1);
|
||||
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 @@ static void gen_ldst (DisasContext *ctx, uint16_t opc, int rt,
|
||||
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 @@ static void gen_cp0 (DisasContext *ctx, uint16_t opc, int rt, int rd)
|
||||
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 @@ static void decode_opc (DisasContext *ctx)
|
||||
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 gen_intermediate_code_internal (CPUState *env, TranslationBlock *tb,
|
||||
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 @@ done_generating:
|
||||
#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 @@ int gen_intermediate_code_pc (CPUState *env, struct TranslationBlock *tb)
|
||||
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 @@ void cpu_dump_state (CPUState *env, FILE *f,
|
||||
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)
|
||||
@ -1787,6 +2422,9 @@ CPUMIPSState *cpu_mips_init (void)
|
||||
env->exception_index = EXCP_NONE;
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
env->hflags |= MIPS_HFLAG_UM;
|
||||
#endif
|
||||
#ifdef MIPS_USES_FPU
|
||||
env->fcr0 = MIPS_FCR0;
|
||||
#endif
|
||||
return env;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user