From b8aa4598e2cc109b0884740a42116acaab01e67d Mon Sep 17 00:00:00 2001 From: ths Date: Sun, 30 Dec 2007 15:36:58 +0000 Subject: [PATCH] MIPS COP1X (and related) instructions, by Richard Sandiford. git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3877 c046a42c-6fe2-441c-8c8c-71466251a162 --- target-mips/cpu.h | 18 +++++++++------- target-mips/exec.h | 18 ++++++++++++++-- target-mips/translate.c | 56 +++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 74 insertions(+), 18 deletions(-) diff --git a/target-mips/cpu.h b/target-mips/cpu.h index 2ca2b64..8b6b8bd 100644 --- a/target-mips/cpu.h +++ b/target-mips/cpu.h @@ -417,7 +417,7 @@ struct CPUMIPSState { int user_mode_only; /* user mode only simulation */ uint32_t hflags; /* CPU State */ /* TMASK defines different execution modes */ -#define MIPS_HFLAG_TMASK 0x00FF +#define MIPS_HFLAG_TMASK 0x01FF #define MIPS_HFLAG_MODE 0x0007 /* execution modes */ /* The KSU flags must be the lowest bits in hflags. The flag order must be the same as defined for CP0 Status. This allows to use @@ -431,16 +431,20 @@ struct CPUMIPSState { #define MIPS_HFLAG_CP0 0x0010 /* CP0 enabled */ #define MIPS_HFLAG_FPU 0x0020 /* FPU enabled */ #define MIPS_HFLAG_F64 0x0040 /* 64-bit FPU enabled */ -#define MIPS_HFLAG_RE 0x0080 /* Reversed endianness */ + /* True if the MIPS IV COP1X instructions can be used. This also + controls the non-COP1X instructions RECIP.S, RECIP.D, RSQRT.S + and RSQRT.D. */ +#define MIPS_HFLAG_COP1X 0x0080 /* COP1X instructions enabled */ +#define MIPS_HFLAG_RE 0x0100 /* Reversed endianness */ /* If translation is interrupted between the branch instruction and * the delay slot, record what type of branch it is so that we can * resume translation properly. It might be possible to reduce * this from three bits to two. */ -#define MIPS_HFLAG_BMASK 0x0700 -#define MIPS_HFLAG_B 0x0100 /* Unconditional branch */ -#define MIPS_HFLAG_BC 0x0200 /* Conditional branch */ -#define MIPS_HFLAG_BL 0x0300 /* Likely branch */ -#define MIPS_HFLAG_BR 0x0400 /* branch to register (can't link TB) */ +#define MIPS_HFLAG_BMASK 0x0e00 +#define MIPS_HFLAG_B 0x0200 /* Unconditional branch */ +#define MIPS_HFLAG_BC 0x0400 /* Conditional branch */ +#define MIPS_HFLAG_BL 0x0600 /* Likely branch */ +#define MIPS_HFLAG_BR 0x0800 /* branch to register (can't link TB) */ target_ulong btarget; /* Jump / branch target */ int bcond; /* Branch condition (if needed) */ diff --git a/target-mips/exec.h b/target-mips/exec.h index 7b2c468..35e71e4 100644 --- a/target-mips/exec.h +++ b/target-mips/exec.h @@ -237,8 +237,8 @@ static always_inline int cpu_halted(CPUState *env) static always_inline void compute_hflags(CPUState *env) { - env->hflags &= ~(MIPS_HFLAG_64 | MIPS_HFLAG_CP0 | MIPS_HFLAG_F64 | - MIPS_HFLAG_FPU | MIPS_HFLAG_KSU); + env->hflags &= ~(MIPS_HFLAG_COP1X | MIPS_HFLAG_64 | MIPS_HFLAG_CP0 | + MIPS_HFLAG_F64 | MIPS_HFLAG_FPU | MIPS_HFLAG_KSU); if (!(env->CP0_Status & (1 << CP0St_EXL)) && !(env->CP0_Status & (1 << CP0St_ERL)) && !(env->hflags & MIPS_HFLAG_DM)) { @@ -257,6 +257,20 @@ static always_inline void compute_hflags(CPUState *env) env->hflags |= MIPS_HFLAG_FPU; if (env->CP0_Status & (1 << CP0St_FR)) env->hflags |= MIPS_HFLAG_F64; + if (env->insn_flags & ISA_MIPS32R2) { + if (env->fpu->fcr0 & FCR0_F64) + env->hflags |= MIPS_HFLAG_COP1X; + } else if (env->insn_flags & ISA_MIPS32) { + if (env->hflags & MIPS_HFLAG_64) + env->hflags |= MIPS_HFLAG_COP1X; + } else if (env->insn_flags & ISA_MIPS4) { + /* All supported MIPS IV CPUs use the XX (CU3) to enable + and disable the MIPS IV extensions to the MIPS III ISA. + Some other MIPS IV CPUs ignore the bit, so the check here + would be too restrictive for them. */ + if (env->CP0_Status & (1 << CP0St_CU3)) + env->hflags |= MIPS_HFLAG_COP1X; + } } #endif /* !defined(__QEMU_MIPS_EXEC_H__) */ diff --git a/target-mips/translate.c b/target-mips/translate.c index 1d8aea1..719af3a 100644 --- a/target-mips/translate.c +++ b/target-mips/translate.c @@ -794,9 +794,22 @@ static always_inline void check_cp1_enabled(DisasContext *ctx) generate_exception_err(ctx, EXCP_CpU, 1); } +/* Verify that the processor is running with COP1X instructions enabled. + This is associated with the nabla symbol in the MIPS32 and MIPS64 + opcode tables. */ + +static always_inline void check_cop1x(DisasContext *ctx) +{ + if (unlikely(!(ctx->hflags & MIPS_HFLAG_COP1X))) + generate_exception(ctx, EXCP_RI); +} + +/* Verify that the processor is running with 64-bit floating-point + operations enabled. */ + static always_inline void check_cp1_64bitmode(DisasContext *ctx) { - if (unlikely(!(ctx->hflags & MIPS_HFLAG_F64))) + if (unlikely(~ctx->hflags & (MIPS_HFLAG_F64 | MIPS_HFLAG_COP1X))) generate_exception(ctx, EXCP_RI); } @@ -5178,12 +5191,14 @@ static void gen_farith (DisasContext *ctx, uint32_t op1, opn = "movn.s"; break; case FOP(21, 16): + check_cop1x(ctx); GEN_LOAD_FREG_FTN(WT0, fs); gen_op_float_recip_s(); GEN_STORE_FTN_FREG(fd, WT2); opn = "recip.s"; break; case FOP(22, 16): + check_cop1x(ctx); GEN_LOAD_FREG_FTN(WT0, fs); gen_op_float_rsqrt_s(); GEN_STORE_FTN_FREG(fd, WT2); @@ -5266,7 +5281,7 @@ static void gen_farith (DisasContext *ctx, uint32_t op1, GEN_LOAD_FREG_FTN(WT0, fs); GEN_LOAD_FREG_FTN(WT1, ft); if (ctx->opcode & (1 << 6)) { - check_cp1_64bitmode(ctx); + check_cop1x(ctx); gen_cmpabs_s(func-48, cc); opn = condnames_abs[func-48]; } else { @@ -5419,14 +5434,14 @@ static void gen_farith (DisasContext *ctx, uint32_t op1, opn = "movn.d"; break; case FOP(21, 17): - check_cp1_registers(ctx, fs | fd); + check_cp1_64bitmode(ctx); GEN_LOAD_FREG_FTN(DT0, fs); gen_op_float_recip_d(); GEN_STORE_FTN_FREG(fd, DT2); opn = "recip.d"; break; case FOP(22, 17): - check_cp1_registers(ctx, fs | fd); + check_cp1_64bitmode(ctx); GEN_LOAD_FREG_FTN(DT0, fs); gen_op_float_rsqrt_d(); GEN_STORE_FTN_FREG(fd, DT2); @@ -5481,7 +5496,8 @@ static void gen_farith (DisasContext *ctx, uint32_t op1, GEN_LOAD_FREG_FTN(DT0, fs); GEN_LOAD_FREG_FTN(DT1, ft); if (ctx->opcode & (1 << 6)) { - check_cp1_64bitmode(ctx); + check_cop1x(ctx); + check_cp1_registers(ctx, fs | ft); gen_cmpabs_d(func-48, cc); opn = condnames_abs[func-48]; } else { @@ -5814,8 +5830,6 @@ static void gen_flt3_ldst (DisasContext *ctx, uint32_t opc, const char *opn = "extended float load/store"; int store = 0; - /* All of those work only on 64bit FPUs. */ - check_cp1_64bitmode(ctx); if (base == 0) { if (index == 0) gen_op_reset_T0(); @@ -5832,33 +5846,41 @@ static void gen_flt3_ldst (DisasContext *ctx, uint32_t opc, memory access. */ switch (opc) { case OPC_LWXC1: + check_cop1x(ctx); op_ldst(lwc1); GEN_STORE_FTN_FREG(fd, WT0); opn = "lwxc1"; break; case OPC_LDXC1: + check_cop1x(ctx); + check_cp1_registers(ctx, fd); op_ldst(ldc1); GEN_STORE_FTN_FREG(fd, DT0); opn = "ldxc1"; break; case OPC_LUXC1: + check_cp1_64bitmode(ctx); op_ldst(luxc1); GEN_STORE_FTN_FREG(fd, DT0); opn = "luxc1"; break; case OPC_SWXC1: + check_cop1x(ctx); GEN_LOAD_FREG_FTN(WT0, fs); op_ldst(swc1); opn = "swxc1"; store = 1; break; case OPC_SDXC1: + check_cop1x(ctx); + check_cp1_registers(ctx, fs); GEN_LOAD_FREG_FTN(DT0, fs); op_ldst(sdc1); opn = "sdxc1"; store = 1; break; case OPC_SUXC1: + check_cp1_64bitmode(ctx); GEN_LOAD_FREG_FTN(DT0, fs); op_ldst(suxc1); opn = "suxc1"; @@ -5878,10 +5900,9 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, { const char *opn = "flt3_arith"; - /* All of those work only on 64bit FPUs. */ - check_cp1_64bitmode(ctx); switch (opc) { case OPC_ALNV_PS: + check_cp1_64bitmode(ctx); GEN_LOAD_REG_T0(fr); GEN_LOAD_FREG_FTN(DT0, fs); GEN_LOAD_FREG_FTN(DT1, ft); @@ -5890,6 +5911,7 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, opn = "alnv.ps"; break; case OPC_MADD_S: + check_cop1x(ctx); GEN_LOAD_FREG_FTN(WT0, fs); GEN_LOAD_FREG_FTN(WT1, ft); GEN_LOAD_FREG_FTN(WT2, fr); @@ -5898,6 +5920,8 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, opn = "madd.s"; break; case OPC_MADD_D: + check_cop1x(ctx); + check_cp1_registers(ctx, fd | fs | ft | fr); GEN_LOAD_FREG_FTN(DT0, fs); GEN_LOAD_FREG_FTN(DT1, ft); GEN_LOAD_FREG_FTN(DT2, fr); @@ -5906,6 +5930,7 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, opn = "madd.d"; break; case OPC_MADD_PS: + check_cp1_64bitmode(ctx); GEN_LOAD_FREG_FTN(WT0, fs); GEN_LOAD_FREG_FTN(WTH0, fs); GEN_LOAD_FREG_FTN(WT1, ft); @@ -5918,6 +5943,7 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, opn = "madd.ps"; break; case OPC_MSUB_S: + check_cop1x(ctx); GEN_LOAD_FREG_FTN(WT0, fs); GEN_LOAD_FREG_FTN(WT1, ft); GEN_LOAD_FREG_FTN(WT2, fr); @@ -5926,6 +5952,8 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, opn = "msub.s"; break; case OPC_MSUB_D: + check_cop1x(ctx); + check_cp1_registers(ctx, fd | fs | ft | fr); GEN_LOAD_FREG_FTN(DT0, fs); GEN_LOAD_FREG_FTN(DT1, ft); GEN_LOAD_FREG_FTN(DT2, fr); @@ -5934,6 +5962,7 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, opn = "msub.d"; break; case OPC_MSUB_PS: + check_cp1_64bitmode(ctx); GEN_LOAD_FREG_FTN(WT0, fs); GEN_LOAD_FREG_FTN(WTH0, fs); GEN_LOAD_FREG_FTN(WT1, ft); @@ -5946,6 +5975,7 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, opn = "msub.ps"; break; case OPC_NMADD_S: + check_cop1x(ctx); GEN_LOAD_FREG_FTN(WT0, fs); GEN_LOAD_FREG_FTN(WT1, ft); GEN_LOAD_FREG_FTN(WT2, fr); @@ -5954,6 +5984,8 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, opn = "nmadd.s"; break; case OPC_NMADD_D: + check_cop1x(ctx); + check_cp1_registers(ctx, fd | fs | ft | fr); GEN_LOAD_FREG_FTN(DT0, fs); GEN_LOAD_FREG_FTN(DT1, ft); GEN_LOAD_FREG_FTN(DT2, fr); @@ -5962,6 +5994,7 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, opn = "nmadd.d"; break; case OPC_NMADD_PS: + check_cp1_64bitmode(ctx); GEN_LOAD_FREG_FTN(WT0, fs); GEN_LOAD_FREG_FTN(WTH0, fs); GEN_LOAD_FREG_FTN(WT1, ft); @@ -5974,6 +6007,7 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, opn = "nmadd.ps"; break; case OPC_NMSUB_S: + check_cop1x(ctx); GEN_LOAD_FREG_FTN(WT0, fs); GEN_LOAD_FREG_FTN(WT1, ft); GEN_LOAD_FREG_FTN(WT2, fr); @@ -5982,6 +6016,8 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, opn = "nmsub.s"; break; case OPC_NMSUB_D: + check_cop1x(ctx); + check_cp1_registers(ctx, fd | fs | ft | fr); GEN_LOAD_FREG_FTN(DT0, fs); GEN_LOAD_FREG_FTN(DT1, ft); GEN_LOAD_FREG_FTN(DT2, fr); @@ -5990,6 +6026,7 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, opn = "nmsub.d"; break; case OPC_NMSUB_PS: + check_cp1_64bitmode(ctx); GEN_LOAD_FREG_FTN(WT0, fs); GEN_LOAD_FREG_FTN(WTH0, fs); GEN_LOAD_FREG_FTN(WT1, ft); @@ -6465,6 +6502,7 @@ static void decode_opc (CPUState *env, DisasContext *ctx) #endif case OPC_BC1ANY2: case OPC_BC1ANY4: + check_cop1x(ctx); check_insn(env, ctx, ASE_MIPS3D); /* fall through */ case OPC_BC1: -- 2.7.4