Commit b8aa4598e2cc109b0884740a42116acaab01e67d
1 parent
6341fdcb
MIPS COP1X (and related) instructions, by Richard Sandiford.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3877 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
3 changed files
with
74 additions
and
18 deletions
target-mips/cpu.h
| ... | ... | @@ -417,7 +417,7 @@ struct CPUMIPSState { |
| 417 | 417 | int user_mode_only; /* user mode only simulation */ |
| 418 | 418 | uint32_t hflags; /* CPU State */ |
| 419 | 419 | /* TMASK defines different execution modes */ |
| 420 | -#define MIPS_HFLAG_TMASK 0x00FF | |
| 420 | +#define MIPS_HFLAG_TMASK 0x01FF | |
| 421 | 421 | #define MIPS_HFLAG_MODE 0x0007 /* execution modes */ |
| 422 | 422 | /* The KSU flags must be the lowest bits in hflags. The flag order |
| 423 | 423 | must be the same as defined for CP0 Status. This allows to use |
| ... | ... | @@ -431,16 +431,20 @@ struct CPUMIPSState { |
| 431 | 431 | #define MIPS_HFLAG_CP0 0x0010 /* CP0 enabled */ |
| 432 | 432 | #define MIPS_HFLAG_FPU 0x0020 /* FPU enabled */ |
| 433 | 433 | #define MIPS_HFLAG_F64 0x0040 /* 64-bit FPU enabled */ |
| 434 | -#define MIPS_HFLAG_RE 0x0080 /* Reversed endianness */ | |
| 434 | + /* True if the MIPS IV COP1X instructions can be used. This also | |
| 435 | + controls the non-COP1X instructions RECIP.S, RECIP.D, RSQRT.S | |
| 436 | + and RSQRT.D. */ | |
| 437 | +#define MIPS_HFLAG_COP1X 0x0080 /* COP1X instructions enabled */ | |
| 438 | +#define MIPS_HFLAG_RE 0x0100 /* Reversed endianness */ | |
| 435 | 439 | /* If translation is interrupted between the branch instruction and |
| 436 | 440 | * the delay slot, record what type of branch it is so that we can |
| 437 | 441 | * resume translation properly. It might be possible to reduce |
| 438 | 442 | * this from three bits to two. */ |
| 439 | -#define MIPS_HFLAG_BMASK 0x0700 | |
| 440 | -#define MIPS_HFLAG_B 0x0100 /* Unconditional branch */ | |
| 441 | -#define MIPS_HFLAG_BC 0x0200 /* Conditional branch */ | |
| 442 | -#define MIPS_HFLAG_BL 0x0300 /* Likely branch */ | |
| 443 | -#define MIPS_HFLAG_BR 0x0400 /* branch to register (can't link TB) */ | |
| 443 | +#define MIPS_HFLAG_BMASK 0x0e00 | |
| 444 | +#define MIPS_HFLAG_B 0x0200 /* Unconditional branch */ | |
| 445 | +#define MIPS_HFLAG_BC 0x0400 /* Conditional branch */ | |
| 446 | +#define MIPS_HFLAG_BL 0x0600 /* Likely branch */ | |
| 447 | +#define MIPS_HFLAG_BR 0x0800 /* branch to register (can't link TB) */ | |
| 444 | 448 | target_ulong btarget; /* Jump / branch target */ |
| 445 | 449 | int bcond; /* Branch condition (if needed) */ |
| 446 | 450 | ... | ... |
target-mips/exec.h
| ... | ... | @@ -237,8 +237,8 @@ static always_inline int cpu_halted(CPUState *env) |
| 237 | 237 | |
| 238 | 238 | static always_inline void compute_hflags(CPUState *env) |
| 239 | 239 | { |
| 240 | - env->hflags &= ~(MIPS_HFLAG_64 | MIPS_HFLAG_CP0 | MIPS_HFLAG_F64 | | |
| 241 | - MIPS_HFLAG_FPU | MIPS_HFLAG_KSU); | |
| 240 | + env->hflags &= ~(MIPS_HFLAG_COP1X | MIPS_HFLAG_64 | MIPS_HFLAG_CP0 | | |
| 241 | + MIPS_HFLAG_F64 | MIPS_HFLAG_FPU | MIPS_HFLAG_KSU); | |
| 242 | 242 | if (!(env->CP0_Status & (1 << CP0St_EXL)) && |
| 243 | 243 | !(env->CP0_Status & (1 << CP0St_ERL)) && |
| 244 | 244 | !(env->hflags & MIPS_HFLAG_DM)) { |
| ... | ... | @@ -257,6 +257,20 @@ static always_inline void compute_hflags(CPUState *env) |
| 257 | 257 | env->hflags |= MIPS_HFLAG_FPU; |
| 258 | 258 | if (env->CP0_Status & (1 << CP0St_FR)) |
| 259 | 259 | env->hflags |= MIPS_HFLAG_F64; |
| 260 | + if (env->insn_flags & ISA_MIPS32R2) { | |
| 261 | + if (env->fpu->fcr0 & FCR0_F64) | |
| 262 | + env->hflags |= MIPS_HFLAG_COP1X; | |
| 263 | + } else if (env->insn_flags & ISA_MIPS32) { | |
| 264 | + if (env->hflags & MIPS_HFLAG_64) | |
| 265 | + env->hflags |= MIPS_HFLAG_COP1X; | |
| 266 | + } else if (env->insn_flags & ISA_MIPS4) { | |
| 267 | + /* All supported MIPS IV CPUs use the XX (CU3) to enable | |
| 268 | + and disable the MIPS IV extensions to the MIPS III ISA. | |
| 269 | + Some other MIPS IV CPUs ignore the bit, so the check here | |
| 270 | + would be too restrictive for them. */ | |
| 271 | + if (env->CP0_Status & (1 << CP0St_CU3)) | |
| 272 | + env->hflags |= MIPS_HFLAG_COP1X; | |
| 273 | + } | |
| 260 | 274 | } |
| 261 | 275 | |
| 262 | 276 | #endif /* !defined(__QEMU_MIPS_EXEC_H__) */ | ... | ... |
target-mips/translate.c
| ... | ... | @@ -794,9 +794,22 @@ static always_inline void check_cp1_enabled(DisasContext *ctx) |
| 794 | 794 | generate_exception_err(ctx, EXCP_CpU, 1); |
| 795 | 795 | } |
| 796 | 796 | |
| 797 | +/* Verify that the processor is running with COP1X instructions enabled. | |
| 798 | + This is associated with the nabla symbol in the MIPS32 and MIPS64 | |
| 799 | + opcode tables. */ | |
| 800 | + | |
| 801 | +static always_inline void check_cop1x(DisasContext *ctx) | |
| 802 | +{ | |
| 803 | + if (unlikely(!(ctx->hflags & MIPS_HFLAG_COP1X))) | |
| 804 | + generate_exception(ctx, EXCP_RI); | |
| 805 | +} | |
| 806 | + | |
| 807 | +/* Verify that the processor is running with 64-bit floating-point | |
| 808 | + operations enabled. */ | |
| 809 | + | |
| 797 | 810 | static always_inline void check_cp1_64bitmode(DisasContext *ctx) |
| 798 | 811 | { |
| 799 | - if (unlikely(!(ctx->hflags & MIPS_HFLAG_F64))) | |
| 812 | + if (unlikely(~ctx->hflags & (MIPS_HFLAG_F64 | MIPS_HFLAG_COP1X))) | |
| 800 | 813 | generate_exception(ctx, EXCP_RI); |
| 801 | 814 | } |
| 802 | 815 | |
| ... | ... | @@ -5178,12 +5191,14 @@ static void gen_farith (DisasContext *ctx, uint32_t op1, |
| 5178 | 5191 | opn = "movn.s"; |
| 5179 | 5192 | break; |
| 5180 | 5193 | case FOP(21, 16): |
| 5194 | + check_cop1x(ctx); | |
| 5181 | 5195 | GEN_LOAD_FREG_FTN(WT0, fs); |
| 5182 | 5196 | gen_op_float_recip_s(); |
| 5183 | 5197 | GEN_STORE_FTN_FREG(fd, WT2); |
| 5184 | 5198 | opn = "recip.s"; |
| 5185 | 5199 | break; |
| 5186 | 5200 | case FOP(22, 16): |
| 5201 | + check_cop1x(ctx); | |
| 5187 | 5202 | GEN_LOAD_FREG_FTN(WT0, fs); |
| 5188 | 5203 | gen_op_float_rsqrt_s(); |
| 5189 | 5204 | GEN_STORE_FTN_FREG(fd, WT2); |
| ... | ... | @@ -5266,7 +5281,7 @@ static void gen_farith (DisasContext *ctx, uint32_t op1, |
| 5266 | 5281 | GEN_LOAD_FREG_FTN(WT0, fs); |
| 5267 | 5282 | GEN_LOAD_FREG_FTN(WT1, ft); |
| 5268 | 5283 | if (ctx->opcode & (1 << 6)) { |
| 5269 | - check_cp1_64bitmode(ctx); | |
| 5284 | + check_cop1x(ctx); | |
| 5270 | 5285 | gen_cmpabs_s(func-48, cc); |
| 5271 | 5286 | opn = condnames_abs[func-48]; |
| 5272 | 5287 | } else { |
| ... | ... | @@ -5419,14 +5434,14 @@ static void gen_farith (DisasContext *ctx, uint32_t op1, |
| 5419 | 5434 | opn = "movn.d"; |
| 5420 | 5435 | break; |
| 5421 | 5436 | case FOP(21, 17): |
| 5422 | - check_cp1_registers(ctx, fs | fd); | |
| 5437 | + check_cp1_64bitmode(ctx); | |
| 5423 | 5438 | GEN_LOAD_FREG_FTN(DT0, fs); |
| 5424 | 5439 | gen_op_float_recip_d(); |
| 5425 | 5440 | GEN_STORE_FTN_FREG(fd, DT2); |
| 5426 | 5441 | opn = "recip.d"; |
| 5427 | 5442 | break; |
| 5428 | 5443 | case FOP(22, 17): |
| 5429 | - check_cp1_registers(ctx, fs | fd); | |
| 5444 | + check_cp1_64bitmode(ctx); | |
| 5430 | 5445 | GEN_LOAD_FREG_FTN(DT0, fs); |
| 5431 | 5446 | gen_op_float_rsqrt_d(); |
| 5432 | 5447 | GEN_STORE_FTN_FREG(fd, DT2); |
| ... | ... | @@ -5481,7 +5496,8 @@ static void gen_farith (DisasContext *ctx, uint32_t op1, |
| 5481 | 5496 | GEN_LOAD_FREG_FTN(DT0, fs); |
| 5482 | 5497 | GEN_LOAD_FREG_FTN(DT1, ft); |
| 5483 | 5498 | if (ctx->opcode & (1 << 6)) { |
| 5484 | - check_cp1_64bitmode(ctx); | |
| 5499 | + check_cop1x(ctx); | |
| 5500 | + check_cp1_registers(ctx, fs | ft); | |
| 5485 | 5501 | gen_cmpabs_d(func-48, cc); |
| 5486 | 5502 | opn = condnames_abs[func-48]; |
| 5487 | 5503 | } else { |
| ... | ... | @@ -5814,8 +5830,6 @@ static void gen_flt3_ldst (DisasContext *ctx, uint32_t opc, |
| 5814 | 5830 | const char *opn = "extended float load/store"; |
| 5815 | 5831 | int store = 0; |
| 5816 | 5832 | |
| 5817 | - /* All of those work only on 64bit FPUs. */ | |
| 5818 | - check_cp1_64bitmode(ctx); | |
| 5819 | 5833 | if (base == 0) { |
| 5820 | 5834 | if (index == 0) |
| 5821 | 5835 | gen_op_reset_T0(); |
| ... | ... | @@ -5832,33 +5846,41 @@ static void gen_flt3_ldst (DisasContext *ctx, uint32_t opc, |
| 5832 | 5846 | memory access. */ |
| 5833 | 5847 | switch (opc) { |
| 5834 | 5848 | case OPC_LWXC1: |
| 5849 | + check_cop1x(ctx); | |
| 5835 | 5850 | op_ldst(lwc1); |
| 5836 | 5851 | GEN_STORE_FTN_FREG(fd, WT0); |
| 5837 | 5852 | opn = "lwxc1"; |
| 5838 | 5853 | break; |
| 5839 | 5854 | case OPC_LDXC1: |
| 5855 | + check_cop1x(ctx); | |
| 5856 | + check_cp1_registers(ctx, fd); | |
| 5840 | 5857 | op_ldst(ldc1); |
| 5841 | 5858 | GEN_STORE_FTN_FREG(fd, DT0); |
| 5842 | 5859 | opn = "ldxc1"; |
| 5843 | 5860 | break; |
| 5844 | 5861 | case OPC_LUXC1: |
| 5862 | + check_cp1_64bitmode(ctx); | |
| 5845 | 5863 | op_ldst(luxc1); |
| 5846 | 5864 | GEN_STORE_FTN_FREG(fd, DT0); |
| 5847 | 5865 | opn = "luxc1"; |
| 5848 | 5866 | break; |
| 5849 | 5867 | case OPC_SWXC1: |
| 5868 | + check_cop1x(ctx); | |
| 5850 | 5869 | GEN_LOAD_FREG_FTN(WT0, fs); |
| 5851 | 5870 | op_ldst(swc1); |
| 5852 | 5871 | opn = "swxc1"; |
| 5853 | 5872 | store = 1; |
| 5854 | 5873 | break; |
| 5855 | 5874 | case OPC_SDXC1: |
| 5875 | + check_cop1x(ctx); | |
| 5876 | + check_cp1_registers(ctx, fs); | |
| 5856 | 5877 | GEN_LOAD_FREG_FTN(DT0, fs); |
| 5857 | 5878 | op_ldst(sdc1); |
| 5858 | 5879 | opn = "sdxc1"; |
| 5859 | 5880 | store = 1; |
| 5860 | 5881 | break; |
| 5861 | 5882 | case OPC_SUXC1: |
| 5883 | + check_cp1_64bitmode(ctx); | |
| 5862 | 5884 | GEN_LOAD_FREG_FTN(DT0, fs); |
| 5863 | 5885 | op_ldst(suxc1); |
| 5864 | 5886 | opn = "suxc1"; |
| ... | ... | @@ -5878,10 +5900,9 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, |
| 5878 | 5900 | { |
| 5879 | 5901 | const char *opn = "flt3_arith"; |
| 5880 | 5902 | |
| 5881 | - /* All of those work only on 64bit FPUs. */ | |
| 5882 | - check_cp1_64bitmode(ctx); | |
| 5883 | 5903 | switch (opc) { |
| 5884 | 5904 | case OPC_ALNV_PS: |
| 5905 | + check_cp1_64bitmode(ctx); | |
| 5885 | 5906 | GEN_LOAD_REG_T0(fr); |
| 5886 | 5907 | GEN_LOAD_FREG_FTN(DT0, fs); |
| 5887 | 5908 | GEN_LOAD_FREG_FTN(DT1, ft); |
| ... | ... | @@ -5890,6 +5911,7 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, |
| 5890 | 5911 | opn = "alnv.ps"; |
| 5891 | 5912 | break; |
| 5892 | 5913 | case OPC_MADD_S: |
| 5914 | + check_cop1x(ctx); | |
| 5893 | 5915 | GEN_LOAD_FREG_FTN(WT0, fs); |
| 5894 | 5916 | GEN_LOAD_FREG_FTN(WT1, ft); |
| 5895 | 5917 | GEN_LOAD_FREG_FTN(WT2, fr); |
| ... | ... | @@ -5898,6 +5920,8 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, |
| 5898 | 5920 | opn = "madd.s"; |
| 5899 | 5921 | break; |
| 5900 | 5922 | case OPC_MADD_D: |
| 5923 | + check_cop1x(ctx); | |
| 5924 | + check_cp1_registers(ctx, fd | fs | ft | fr); | |
| 5901 | 5925 | GEN_LOAD_FREG_FTN(DT0, fs); |
| 5902 | 5926 | GEN_LOAD_FREG_FTN(DT1, ft); |
| 5903 | 5927 | GEN_LOAD_FREG_FTN(DT2, fr); |
| ... | ... | @@ -5906,6 +5930,7 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, |
| 5906 | 5930 | opn = "madd.d"; |
| 5907 | 5931 | break; |
| 5908 | 5932 | case OPC_MADD_PS: |
| 5933 | + check_cp1_64bitmode(ctx); | |
| 5909 | 5934 | GEN_LOAD_FREG_FTN(WT0, fs); |
| 5910 | 5935 | GEN_LOAD_FREG_FTN(WTH0, fs); |
| 5911 | 5936 | GEN_LOAD_FREG_FTN(WT1, ft); |
| ... | ... | @@ -5918,6 +5943,7 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, |
| 5918 | 5943 | opn = "madd.ps"; |
| 5919 | 5944 | break; |
| 5920 | 5945 | case OPC_MSUB_S: |
| 5946 | + check_cop1x(ctx); | |
| 5921 | 5947 | GEN_LOAD_FREG_FTN(WT0, fs); |
| 5922 | 5948 | GEN_LOAD_FREG_FTN(WT1, ft); |
| 5923 | 5949 | GEN_LOAD_FREG_FTN(WT2, fr); |
| ... | ... | @@ -5926,6 +5952,8 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, |
| 5926 | 5952 | opn = "msub.s"; |
| 5927 | 5953 | break; |
| 5928 | 5954 | case OPC_MSUB_D: |
| 5955 | + check_cop1x(ctx); | |
| 5956 | + check_cp1_registers(ctx, fd | fs | ft | fr); | |
| 5929 | 5957 | GEN_LOAD_FREG_FTN(DT0, fs); |
| 5930 | 5958 | GEN_LOAD_FREG_FTN(DT1, ft); |
| 5931 | 5959 | GEN_LOAD_FREG_FTN(DT2, fr); |
| ... | ... | @@ -5934,6 +5962,7 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, |
| 5934 | 5962 | opn = "msub.d"; |
| 5935 | 5963 | break; |
| 5936 | 5964 | case OPC_MSUB_PS: |
| 5965 | + check_cp1_64bitmode(ctx); | |
| 5937 | 5966 | GEN_LOAD_FREG_FTN(WT0, fs); |
| 5938 | 5967 | GEN_LOAD_FREG_FTN(WTH0, fs); |
| 5939 | 5968 | GEN_LOAD_FREG_FTN(WT1, ft); |
| ... | ... | @@ -5946,6 +5975,7 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, |
| 5946 | 5975 | opn = "msub.ps"; |
| 5947 | 5976 | break; |
| 5948 | 5977 | case OPC_NMADD_S: |
| 5978 | + check_cop1x(ctx); | |
| 5949 | 5979 | GEN_LOAD_FREG_FTN(WT0, fs); |
| 5950 | 5980 | GEN_LOAD_FREG_FTN(WT1, ft); |
| 5951 | 5981 | GEN_LOAD_FREG_FTN(WT2, fr); |
| ... | ... | @@ -5954,6 +5984,8 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, |
| 5954 | 5984 | opn = "nmadd.s"; |
| 5955 | 5985 | break; |
| 5956 | 5986 | case OPC_NMADD_D: |
| 5987 | + check_cop1x(ctx); | |
| 5988 | + check_cp1_registers(ctx, fd | fs | ft | fr); | |
| 5957 | 5989 | GEN_LOAD_FREG_FTN(DT0, fs); |
| 5958 | 5990 | GEN_LOAD_FREG_FTN(DT1, ft); |
| 5959 | 5991 | GEN_LOAD_FREG_FTN(DT2, fr); |
| ... | ... | @@ -5962,6 +5994,7 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, |
| 5962 | 5994 | opn = "nmadd.d"; |
| 5963 | 5995 | break; |
| 5964 | 5996 | case OPC_NMADD_PS: |
| 5997 | + check_cp1_64bitmode(ctx); | |
| 5965 | 5998 | GEN_LOAD_FREG_FTN(WT0, fs); |
| 5966 | 5999 | GEN_LOAD_FREG_FTN(WTH0, fs); |
| 5967 | 6000 | GEN_LOAD_FREG_FTN(WT1, ft); |
| ... | ... | @@ -5974,6 +6007,7 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, |
| 5974 | 6007 | opn = "nmadd.ps"; |
| 5975 | 6008 | break; |
| 5976 | 6009 | case OPC_NMSUB_S: |
| 6010 | + check_cop1x(ctx); | |
| 5977 | 6011 | GEN_LOAD_FREG_FTN(WT0, fs); |
| 5978 | 6012 | GEN_LOAD_FREG_FTN(WT1, ft); |
| 5979 | 6013 | GEN_LOAD_FREG_FTN(WT2, fr); |
| ... | ... | @@ -5982,6 +6016,8 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, |
| 5982 | 6016 | opn = "nmsub.s"; |
| 5983 | 6017 | break; |
| 5984 | 6018 | case OPC_NMSUB_D: |
| 6019 | + check_cop1x(ctx); | |
| 6020 | + check_cp1_registers(ctx, fd | fs | ft | fr); | |
| 5985 | 6021 | GEN_LOAD_FREG_FTN(DT0, fs); |
| 5986 | 6022 | GEN_LOAD_FREG_FTN(DT1, ft); |
| 5987 | 6023 | GEN_LOAD_FREG_FTN(DT2, fr); |
| ... | ... | @@ -5990,6 +6026,7 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, |
| 5990 | 6026 | opn = "nmsub.d"; |
| 5991 | 6027 | break; |
| 5992 | 6028 | case OPC_NMSUB_PS: |
| 6029 | + check_cp1_64bitmode(ctx); | |
| 5993 | 6030 | GEN_LOAD_FREG_FTN(WT0, fs); |
| 5994 | 6031 | GEN_LOAD_FREG_FTN(WTH0, fs); |
| 5995 | 6032 | GEN_LOAD_FREG_FTN(WT1, ft); |
| ... | ... | @@ -6465,6 +6502,7 @@ static void decode_opc (CPUState *env, DisasContext *ctx) |
| 6465 | 6502 | #endif |
| 6466 | 6503 | case OPC_BC1ANY2: |
| 6467 | 6504 | case OPC_BC1ANY4: |
| 6505 | + check_cop1x(ctx); | |
| 6468 | 6506 | check_insn(env, ctx, ASE_MIPS3D); |
| 6469 | 6507 | /* fall through */ |
| 6470 | 6508 | case OPC_BC1: | ... | ... |