MIPS COP1X (and related) instructions, by Richard Sandiford.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3877 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
		
							parent
							
								
									6341fdcb78
								
							
						
					
					
						commit
						b8aa4598e2
					
				| 
						 | 
				
			
			@ -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)       */
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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__) */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue