From 5a5012ecbdcd341bb1d2e8200db91f6212aa44df Mon Sep 17 00:00:00 2001 From: ths Date: Mon, 7 May 2007 13:55:33 +0000 Subject: [PATCH] MIPS 64-bit FPU support, plus some collateral bugfixes in the conditional branch handling. git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2779 c046a42c-6fe2-441c-8c8c-71466251a162 --- Changelog | 1 + gdbstub.c | 4 +- target-mips/TODO | 3 +- target-mips/cpu.h | 44 +- target-mips/exec.h | 6 + target-mips/fop_template.c | 127 ++-- target-mips/helper.c | 3 + target-mips/op.c | 934 +++++++++++++++++++++------- target-mips/op_mem.c | 32 + target-mips/translate.c | 1123 ++++++++++++++++++++++++++-------- target-mips/translate_init.c | 20 +- 11 files changed, 1740 insertions(+), 557 deletions(-) diff --git a/Changelog b/Changelog index 19a5da7fda..9aaac1877b 100644 --- a/Changelog +++ b/Changelog @@ -4,6 +4,7 @@ - ds1225y nvram support (Herve Poussineau) - CPU model selection support (J. Mayer, Paul Brook, Herve Poussineau) - Several Sparc fixes (Aurelien Jarno, Blue Swirl) + - MIPS 64-bit FPU support (Thiemo Seufer) version 0.9.0: diff --git a/gdbstub.c b/gdbstub.c index 79f54f51f9..62c1db297d 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -575,7 +575,7 @@ static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf) { for (i = 0; i < 32; i++) { - *(uint32_t *)ptr = tswapl(FPR_W (env, i)); + *(uint32_t *)ptr = tswapl(env->fpr[i].fs[FP_ENDIAN_IDX]); ptr += 4; } @@ -637,7 +637,7 @@ static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size) { for (i = 0; i < 32; i++) { - FPR_W (env, i) = tswapl(*(uint32_t *)ptr); + env->fpr[i].fs[FP_ENDIAN_IDX] = tswapl(*(uint32_t *)ptr); ptr += 4; } diff --git a/target-mips/TODO b/target-mips/TODO index f91b5c6c6a..a5842ff276 100644 --- a/target-mips/TODO +++ b/target-mips/TODO @@ -10,11 +10,12 @@ General when the Qemu FPU emulation is disabled. Also gdb inside the emulated system does not work. Both problems are caused by insufficient handling of self-modifying code. +- Floating point exception emulation is incomplete. MIPS64 ------ - No 64bit TLB support -- no 64bit wide registers for FPU +- 64bit FPU not fully implemented - 64bit mul/div handling broken "Generic" 4Kc system emulation diff --git a/target-mips/cpu.h b/target-mips/cpu.h index 36b890efb0..33cb6573eb 100644 --- a/target-mips/cpu.h +++ b/target-mips/cpu.h @@ -21,7 +21,7 @@ typedef union fpr_t fpr_t; union fpr_t { float64 fd; /* ieee double precision */ float32 fs[2];/* ieee single precision */ - uint64_t d; /* binary single fixed-point */ + uint64_t d; /* binary double fixed-point */ uint32_t w[2]; /* binary single fixed-point */ }; /* define FP_ENDIAN_IDX to access the same location @@ -64,31 +64,35 @@ struct CPUMIPSState { target_ulong HI, LO; /* Floating point registers */ fpr_t fpr[32]; -#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 */ + /* fpu implementation/revision register (fir) */ uint32_t fcr0; +#define FCR0_F64 22 +#define FCR0_L 21 +#define FCR0_W 20 +#define FCR0_3D 19 +#define FCR0_PS 18 +#define FCR0_D 17 +#define FCR0_S 16 +#define FCR0_PRID 8 +#define FCR0_REV 0 /* 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 SET_FP_COND(num,env) do { (env->fcr31) |= ((num) ? (1 << ((num) + 24)) : (1 << ((num) + 23))); } while(0) +#define CLEAR_FP_COND(num,env) do { (env->fcr31) &= ~((num) ? (1 << ((num) + 24)) : (1 << ((num) + 23))); } while(0) +#define IS_FP_COND_SET(num,env) (((env->fcr31) & ((num) ? (1 << ((num) + 24)) : (1 << ((num) + 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 & 0x3f) << 12); } while(0) +#define SET_FP_ENABLE(reg,v) do { (reg) = ((reg) & ~(0x1f << 7)) | ((v & 0x1f) << 7); } while(0) +#define SET_FP_FLAGS(reg,v) do { (reg) = ((reg) & ~(0x1f << 2)) | ((v & 0x1f) << 2); } while(0) +#define UPDATE_FP_FLAGS(reg,v) do { (reg) |= ((v & 0x1f) << 2); } while(0) #define FP_INEXACT 1 #define FP_UNDERFLOW 2 #define FP_OVERFLOW 4 @@ -267,6 +271,7 @@ struct CPUMIPSState { int SYNCI_Step; /* Address step size for SYNCI */ int CCRes; /* Cycle count resolution/divisor */ + int Status_rw_bitmask; /* Read/write bits in CP0_Status */ #if defined(CONFIG_USER_ONLY) target_ulong tls_value; @@ -330,10 +335,11 @@ enum { EXCP_RI, EXCP_OVERFLOW, EXCP_TRAP, + EXCP_FPE, EXCP_DDBS, EXCP_DWATCH, - EXCP_LAE, - EXCP_SAE, /* 24 */ + EXCP_LAE, /* 24 */ + EXCP_SAE, EXCP_LTLBL, EXCP_TLBL, EXCP_TLBS, diff --git a/target-mips/exec.h b/target-mips/exec.h index b00263f1b1..d9160a1f3a 100644 --- a/target-mips/exec.h +++ b/target-mips/exec.h @@ -29,12 +29,18 @@ register target_ulong T2 asm(AREG3); #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 FSTH0 (env->ft0.fs[!FP_ENDIAN_IDX]) +#define FSTH1 (env->ft1.fs[!FP_ENDIAN_IDX]) +#define FSTH2 (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]) +#define WTH0 (env->ft0.w[!FP_ENDIAN_IDX]) +#define WTH1 (env->ft1.w[!FP_ENDIAN_IDX]) +#define WTH2 (env->ft2.w[!FP_ENDIAN_IDX]) #endif #if defined (DEBUG_OP) diff --git a/target-mips/fop_template.c b/target-mips/fop_template.c index 4ebb46bff5..bb288f11ee 100644 --- a/target-mips/fop_template.c +++ b/target-mips/fop_template.c @@ -19,75 +19,103 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#if defined(SFREG) +#if defined(FREG) -#define OP_WLOAD_FREG(treg, tregname, SFREG) \ - void glue(glue(op_load_fpr_,tregname), SFREG) (void) \ - { \ - treg = FPR_W(env, SFREG); \ - RETURN(); \ +#define OP_WLOAD_FREG(treg, tregname, FREG) \ + void glue(glue(op_load_fpr_,tregname), FREG) (void) \ + { \ + treg = env->fpr[FREG].fs[FP_ENDIAN_IDX]; \ + RETURN(); \ } -#define OP_WSTORE_FREG(treg, tregname, SFREG) \ - void glue(glue(op_store_fpr_,tregname), SFREG) (void)\ - { \ - FPR_W(env, SFREG) = treg; \ - RETURN(); \ +#define OP_WSTORE_FREG(treg, tregname, FREG) \ + void glue(glue(op_store_fpr_,tregname), FREG) (void) \ + { \ + env->fpr[FREG].fs[FP_ENDIAN_IDX] = 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) +/* WT0 = FREG.w: op_load_fpr_WT0_fprFREG */ +OP_WLOAD_FREG(WT0, WT0_fpr, FREG) +/* FREG.w = WT0: op_store_fpr_WT0_fprFREG */ +OP_WSTORE_FREG(WT0, WT0_fpr, FREG) -OP_WLOAD_FREG(WT1, WT1_fpr, SFREG) -OP_WSTORE_FREG(WT1, WT1_fpr, SFREG) +OP_WLOAD_FREG(WT1, WT1_fpr, FREG) +OP_WSTORE_FREG(WT1, WT1_fpr, FREG) -OP_WLOAD_FREG(WT2, WT2_fpr, SFREG) -OP_WSTORE_FREG(WT2, WT2_fpr, SFREG) +OP_WLOAD_FREG(WT2, WT2_fpr, FREG) +OP_WSTORE_FREG(WT2, WT2_fpr, FREG) -#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_DLOAD_FREG(treg, tregname, FREG) \ + void glue(glue(op_load_fpr_,tregname), FREG) (void) \ + { \ + if (env->CP0_Status & (1 << CP0St_FR)) \ + treg = env->fpr[FREG].fd; \ + else \ + treg = (uint64_t)(env->fpr[FREG | 1].fs[FP_ENDIAN_IDX]) << 32 | \ + env->fpr[FREG & ~1].fs[FP_ENDIAN_IDX]; \ + RETURN(); \ } -#define OP_DSTORE_FREG(treg, tregname, DFREG) \ - void glue(glue(op_store_fpr_,tregname), DFREG) (void)\ - { \ - FPR_D(env, DFREG) = treg; \ - RETURN(); \ +#define OP_DSTORE_FREG(treg, tregname, FREG) \ + void glue(glue(op_store_fpr_,tregname), FREG) (void) \ + { \ + if (env->CP0_Status & (1 << CP0St_FR)) \ + env->fpr[FREG].fd = treg; \ + else { \ + env->fpr[FREG | 1].fs[FP_ENDIAN_IDX] = treg >> 32; \ + env->fpr[FREG & ~1].fs[FP_ENDIAN_IDX] = treg; \ + } \ + RETURN(); \ } -OP_DLOAD_FREG(DT0, DT0_fpr, DFREG) -OP_DSTORE_FREG(DT0, DT0_fpr, DFREG) +OP_DLOAD_FREG(DT0, DT0_fpr, FREG) +OP_DSTORE_FREG(DT0, DT0_fpr, FREG) -OP_DLOAD_FREG(DT1, DT1_fpr, DFREG) -OP_DSTORE_FREG(DT1, DT1_fpr, DFREG) +OP_DLOAD_FREG(DT1, DT1_fpr, FREG) +OP_DSTORE_FREG(DT1, DT1_fpr, FREG) -OP_DLOAD_FREG(DT2, DT2_fpr, DFREG) -OP_DSTORE_FREG(DT2, DT2_fpr, DFREG) +OP_DLOAD_FREG(DT2, DT2_fpr, FREG) +OP_DSTORE_FREG(DT2, DT2_fpr, FREG) + +#define OP_PSLOAD_FREG(treg, tregname, FREG) \ + void glue(glue(op_load_fpr_,tregname), FREG) (void) \ + { \ + treg = env->fpr[FREG].fs[!FP_ENDIAN_IDX]; \ + RETURN(); \ + } + +#define OP_PSSTORE_FREG(treg, tregname, FREG) \ + void glue(glue(op_store_fpr_,tregname), FREG) (void) \ + { \ + env->fpr[FREG].fs[!FP_ENDIAN_IDX] = treg; \ + RETURN(); \ + } + +OP_PSLOAD_FREG(WTH0, WTH0_fpr, FREG) +OP_PSSTORE_FREG(WTH0, WTH0_fpr, FREG) + +OP_PSLOAD_FREG(WTH1, WTH1_fpr, FREG) +OP_PSSTORE_FREG(WTH1, WTH1_fpr, FREG) + +OP_PSLOAD_FREG(WTH2, WTH2_fpr, FREG) +OP_PSSTORE_FREG(WTH2, WTH2_fpr, FREG) #endif #if defined (FTN) -#define SET_RESET(treg, tregname) \ +#define SET_RESET(treg, tregname) \ void glue(op_set, tregname)(void) \ - { \ - treg = PARAM1; \ - RETURN(); \ - } \ + { \ + treg = PARAM1; \ + RETURN(); \ + } \ void glue(op_reset, tregname)(void) \ - { \ - treg = 0; \ - RETURN(); \ - } \ + { \ + treg = 0; \ + RETURN(); \ + } SET_RESET(WT0, _WT0) SET_RESET(WT1, _WT1) @@ -95,6 +123,9 @@ SET_RESET(WT2, _WT2) SET_RESET(DT0, _DT0) SET_RESET(DT1, _DT1) SET_RESET(DT2, _DT2) +SET_RESET(WTH0, _WTH0) +SET_RESET(WTH1, _WTH1) +SET_RESET(WTH2, _WTH2) #undef SET_RESET #endif diff --git a/target-mips/helper.c b/target-mips/helper.c index 8d9435a232..909db5d37f 100644 --- a/target-mips/helper.c +++ b/target-mips/helper.c @@ -379,6 +379,9 @@ void do_interrupt (CPUState *env) case EXCP_TRAP: cause = 13; goto set_EPC; + case EXCP_FPE: + cause = 15; + goto set_EPC; case EXCP_LTLBL: cause = 1; goto set_EPC; diff --git a/target-mips/op.c b/target-mips/op.c index 0b5e8bacb5..899892f68c 100644 --- a/target-mips/op.c +++ b/target-mips/op.c @@ -23,27 +23,27 @@ #include "exec.h" #ifndef CALL_FROM_TB0 -#define CALL_FROM_TB0(func) func(); +#define CALL_FROM_TB0(func) func() #endif #ifndef CALL_FROM_TB1 -#define CALL_FROM_TB1(func, arg0) func(arg0); +#define CALL_FROM_TB1(func, arg0) func(arg0) #endif #ifndef CALL_FROM_TB1_CONST16 -#define CALL_FROM_TB1_CONST16(func, arg0) CALL_FROM_TB1(func, arg0); +#define CALL_FROM_TB1_CONST16(func, arg0) CALL_FROM_TB1(func, arg0) #endif #ifndef CALL_FROM_TB2 -#define CALL_FROM_TB2(func, arg0, arg1) func(arg0, arg1); +#define CALL_FROM_TB2(func, arg0, arg1) func(arg0, arg1) #endif #ifndef CALL_FROM_TB2_CONST16 #define CALL_FROM_TB2_CONST16(func, arg0, arg1) \ -CALL_FROM_TB2(func, arg0, arg1); + CALL_FROM_TB2(func, arg0, arg1) #endif #ifndef CALL_FROM_TB3 -#define CALL_FROM_TB3(func, arg0, arg1, arg2) func(arg0, arg1, arg2); +#define CALL_FROM_TB3(func, arg0, arg1, arg2) func(arg0, arg1, arg2) #endif #ifndef CALL_FROM_TB4 #define CALL_FROM_TB4(func, arg0, arg1, arg2, arg3) \ - func(arg0, arg1, arg2, arg3); + func(arg0, arg1, arg2, arg3) #endif #define REG 1 @@ -144,134 +144,102 @@ CALL_FROM_TB2(func, arg0, arg1); #include "op_template.c" #undef TN -#define SFREG 0 -#define DFREG 0 +#define FREG 0 #include "fop_template.c" -#undef SFREG -#undef DFREG -#define SFREG 1 +#undef FREG +#define FREG 1 #include "fop_template.c" -#undef SFREG -#define SFREG 2 -#define DFREG 2 +#undef FREG +#define FREG 2 #include "fop_template.c" -#undef SFREG -#undef DFREG -#define SFREG 3 +#undef FREG +#define FREG 3 #include "fop_template.c" -#undef SFREG -#define SFREG 4 -#define DFREG 4 +#undef FREG +#define FREG 4 #include "fop_template.c" -#undef SFREG -#undef DFREG -#define SFREG 5 +#undef FREG +#define FREG 5 #include "fop_template.c" -#undef SFREG -#define SFREG 6 -#define DFREG 6 +#undef FREG +#define FREG 6 #include "fop_template.c" -#undef SFREG -#undef DFREG -#define SFREG 7 +#undef FREG +#define FREG 7 #include "fop_template.c" -#undef SFREG -#define SFREG 8 -#define DFREG 8 +#undef FREG +#define FREG 8 #include "fop_template.c" -#undef SFREG -#undef DFREG -#define SFREG 9 +#undef FREG +#define FREG 9 #include "fop_template.c" -#undef SFREG -#define SFREG 10 -#define DFREG 10 +#undef FREG +#define FREG 10 #include "fop_template.c" -#undef SFREG -#undef DFREG -#define SFREG 11 +#undef FREG +#define FREG 11 #include "fop_template.c" -#undef SFREG -#define SFREG 12 -#define DFREG 12 +#undef FREG +#define FREG 12 #include "fop_template.c" -#undef SFREG -#undef DFREG -#define SFREG 13 +#undef FREG +#define FREG 13 #include "fop_template.c" -#undef SFREG -#define SFREG 14 -#define DFREG 14 +#undef FREG +#define FREG 14 #include "fop_template.c" -#undef SFREG -#undef DFREG -#define SFREG 15 +#undef FREG +#define FREG 15 #include "fop_template.c" -#undef SFREG -#define SFREG 16 -#define DFREG 16 +#undef FREG +#define FREG 16 #include "fop_template.c" -#undef SFREG -#undef DFREG -#define SFREG 17 +#undef FREG +#define FREG 17 #include "fop_template.c" -#undef SFREG -#define SFREG 18 -#define DFREG 18 +#undef FREG +#define FREG 18 #include "fop_template.c" -#undef SFREG -#undef DFREG -#define SFREG 19 +#undef FREG +#define FREG 19 #include "fop_template.c" -#undef SFREG -#define SFREG 20 -#define DFREG 20 +#undef FREG +#define FREG 20 #include "fop_template.c" -#undef SFREG -#undef DFREG -#define SFREG 21 +#undef FREG +#define FREG 21 #include "fop_template.c" -#undef SFREG -#define SFREG 22 -#define DFREG 22 +#undef FREG +#define FREG 22 #include "fop_template.c" -#undef SFREG -#undef DFREG -#define SFREG 23 +#undef FREG +#define FREG 23 #include "fop_template.c" -#undef SFREG -#define SFREG 24 -#define DFREG 24 +#undef FREG +#define FREG 24 #include "fop_template.c" -#undef SFREG -#undef DFREG -#define SFREG 25 +#undef FREG +#define FREG 25 #include "fop_template.c" -#undef SFREG -#define SFREG 26 -#define DFREG 26 +#undef FREG +#define FREG 26 #include "fop_template.c" -#undef SFREG -#undef DFREG -#define SFREG 27 +#undef FREG +#define FREG 27 #include "fop_template.c" -#undef SFREG -#define SFREG 28 -#define DFREG 28 +#undef FREG +#define FREG 28 #include "fop_template.c" -#undef SFREG -#undef DFREG -#define SFREG 29 +#undef FREG +#define FREG 29 #include "fop_template.c" -#undef SFREG -#define SFREG 30 -#define DFREG 30 +#undef FREG +#define FREG 30 #include "fop_template.c" -#undef SFREG -#undef DFREG -#define SFREG 31 +#undef FREG +#define FREG 31 #include "fop_template.c" -#undef SFREG +#undef FREG #define FTN #include "fop_template.c" @@ -919,14 +887,14 @@ void op_movz (void) void op_movf (void) { if (!(env->fcr31 & PARAM1)) - env->gpr[PARAM2] = env->gpr[PARAM3]; + T0 = T1; RETURN(); } void op_movt (void) { if (env->fcr31 & PARAM1) - env->gpr[PARAM2] = env->gpr[PARAM3]; + T0 = T1; RETURN(); } @@ -1354,17 +1322,18 @@ void op_mtc0_compare (void) void op_mtc0_status (void) { uint32_t val, old; + uint32_t mask = env->Status_rw_bitmask; - /* No 64bit FPU, no reverse endianness, no MDMX/DSP, no 64bit ops, + /* No reverse endianness, no MDMX/DSP, no 64bit ops, no 64bit addressing implemented. */ - val = (int32_t)T0 & 0xF878FF17; + val = (int32_t)T0 & mask; old = env->CP0_Status; if (!(val & (1 << CP0St_EXL)) && !(val & (1 << CP0St_ERL)) && !(env->hflags & MIPS_HFLAG_DM) && (val & (1 << CP0St_UM))) env->hflags |= MIPS_HFLAG_UM; - env->CP0_Status = (env->CP0_Status & ~0xF878FF17) | val; + env->CP0_Status = (env->CP0_Status & ~mask) | val; if (loglevel & CPU_LOG_EXEC) CALL_FROM_TB2(do_mtc0_status_debug, old, val); CALL_FROM_TB1(cpu_mips_update_irq, env); @@ -1643,6 +1612,7 @@ void op_dmtc0_errorepc (void) } #endif /* TARGET_MIPS64 */ +/* CP1 functions */ #if 0 # define DEBUG_FPU_STATE() CALL_FROM_TB1(dump_fpu, env) #else @@ -1666,20 +1636,6 @@ void op_cp1_enabled(void) 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, @@ -1691,26 +1647,93 @@ unsigned int ieee_rm[] = { #define RESTORE_ROUNDING_MODE \ set_float_rounding_mode(ieee_rm[env->fcr31 & 3], &env->fp_status) +inline char ieee_ex_to_mips(char ieee) +{ + return (ieee & float_flag_inexact) >> 5 | + (ieee & float_flag_underflow) >> 3 | + (ieee & float_flag_overflow) >> 1 | + (ieee & float_flag_divbyzero) << 1 | + (ieee & float_flag_invalid) << 4; +} + +inline char mips_ex_to_ieee(char mips) +{ + return (mips & FP_INEXACT) << 5 | + (mips & FP_UNDERFLOW) << 3 | + (mips & FP_OVERFLOW) << 1 | + (mips & FP_DIV0) >> 1 | + (mips & FP_INVALID) >> 4; +} + +inline void update_fcr31(void) +{ + int tmp = ieee_ex_to_mips(get_float_exception_flags(&env->fp_status)); + + SET_FP_CAUSE(env->fcr31, tmp); + if (GET_FP_ENABLE(env->fcr31) & tmp) + CALL_FROM_TB1(do_raise_exception, EXCP_FPE); + else + UPDATE_FP_FLAGS(env->fcr31, tmp); +} + + +void op_cfc1 (void) +{ + switch (T1) { + case 0: + T0 = (int32_t)env->fcr0; + break; + case 25: + T0 = ((env->fcr31 >> 24) & 0xfe) | ((env->fcr31 >> 23) & 0x1); + break; + case 26: + T0 = env->fcr31 & 0x0003f07c; + break; + case 28: + T0 = (env->fcr31 & 0x00000f83) | ((env->fcr31 >> 22) & 0x4); + break; + default: + T0 = (int32_t)env->fcr31; + break; + } + DEBUG_FPU_STATE(); + RETURN(); +} + 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 + switch(T1) { + case 25: + if (T0 & 0xffffff00) + goto leave; + env->fcr31 = (env->fcr31 & 0x017fffff) | ((T0 & 0xfe) << 24) | + ((T0 & 0x1) << 23); + break; + case 26: + if (T0 & 0x007c0000) + goto leave; + env->fcr31 = (env->fcr31 & 0xfffc0f83) | (T0 & 0x0003f07c); + break; + case 28: + if (T0 & 0x007c0000) + goto leave; + env->fcr31 = (env->fcr31 & 0xfefff07c) | (T0 & 0x00000f83) | + ((T0 & 0x4) << 22); + break; + case 31: + if (T0 & 0x007c0000) + goto leave; + env->fcr31 = T0; + break; + default: + goto leave; } + /* set rounding mode */ + RESTORE_ROUNDING_MODE; + set_float_exception_flags(0, &env->fp_status); + if ((GET_FP_ENABLE(env->fcr31) | 0x20) & GET_FP_CAUSE(env->fcr31)) + CALL_FROM_TB1(do_raise_exception, EXCP_FPE); + leave: DEBUG_FPU_STATE(); RETURN(); } @@ -1729,55 +1752,219 @@ void op_mtc1 (void) RETURN(); } +void op_dmfc1 (void) +{ + T0 = DT0; + DEBUG_FPU_STATE(); + RETURN(); +} + +void op_dmtc1 (void) +{ + DT0 = T0; + DEBUG_FPU_STATE(); + RETURN(); +} + +void op_mfhc1 (void) +{ + T0 = WTH0; + DEBUG_FPU_STATE(); + RETURN(); +} + +void op_mthc1 (void) +{ + WTH0 = T0; + DEBUG_FPU_STATE(); + RETURN(); +} + /* Float support. Single precition routines have a "s" suffix, double precision a - "d" suffix. */ + "d" suffix, 32bit integer "w", 64bit integer "l", paired singe "ps", + paired single lowwer "pl", paired single upper "pu". */ #define FLOAT_OP(name, p) void OPPROTO op_float_##name##_##p(void) FLOAT_OP(cvtd, s) { + set_float_exception_flags(0, &env->fp_status); FDT2 = float32_to_float64(FST0, &env->fp_status); + update_fcr31(); DEBUG_FPU_STATE(); RETURN(); } FLOAT_OP(cvtd, w) { + set_float_exception_flags(0, &env->fp_status); FDT2 = int32_to_float64(WT0, &env->fp_status); + update_fcr31(); + DEBUG_FPU_STATE(); + RETURN(); +} +FLOAT_OP(cvtd, l) +{ + set_float_exception_flags(0, &env->fp_status); + FDT2 = int64_to_float64(DT0, &env->fp_status); + update_fcr31(); + DEBUG_FPU_STATE(); + RETURN(); +} +FLOAT_OP(cvtl, d) +{ + set_float_exception_flags(0, &env->fp_status); + DT2 = float64_to_int64(FDT0, &env->fp_status); + update_fcr31(); + if (GET_FP_CAUSE(env->fcr31) & (FP_OVERFLOW | FP_INVALID)) + DT2 = 0x7fffffffffffffffULL; + DEBUG_FPU_STATE(); + RETURN(); +} +FLOAT_OP(cvtl, s) +{ + set_float_exception_flags(0, &env->fp_status); + DT2 = float32_to_int64(FST0, &env->fp_status); + update_fcr31(); + if (GET_FP_CAUSE(env->fcr31) & (FP_OVERFLOW | FP_INVALID)) + DT2 = 0x7fffffffffffffffULL; + DEBUG_FPU_STATE(); + RETURN(); +} +FLOAT_OP(cvtps, s) +{ + WT2 = WT0; + WTH2 = WT1; + DEBUG_FPU_STATE(); + RETURN(); +} +FLOAT_OP(cvtps, pw) +{ + set_float_exception_flags(0, &env->fp_status); + FST2 = int32_to_float32(WT0, &env->fp_status); + FSTH2 = int32_to_float32(WTH0, &env->fp_status); + update_fcr31(); + DEBUG_FPU_STATE(); + RETURN(); +} +FLOAT_OP(cvtpw, ps) +{ + set_float_exception_flags(0, &env->fp_status); + WT2 = float32_to_int32(FST0, &env->fp_status); + WTH2 = float32_to_int32(FSTH0, &env->fp_status); + update_fcr31(); + if (GET_FP_CAUSE(env->fcr31) & (FP_OVERFLOW | FP_INVALID)) + WT2 = 0x7fffffff; DEBUG_FPU_STATE(); RETURN(); } FLOAT_OP(cvts, d) { + set_float_exception_flags(0, &env->fp_status); FST2 = float64_to_float32(FDT0, &env->fp_status); + update_fcr31(); DEBUG_FPU_STATE(); RETURN(); } FLOAT_OP(cvts, w) { + set_float_exception_flags(0, &env->fp_status); FST2 = int32_to_float32(WT0, &env->fp_status); + update_fcr31(); + DEBUG_FPU_STATE(); + RETURN(); +} +FLOAT_OP(cvts, l) +{ + set_float_exception_flags(0, &env->fp_status); + FST2 = int64_to_float32(DT0, &env->fp_status); + update_fcr31(); + DEBUG_FPU_STATE(); + RETURN(); +} +FLOAT_OP(cvts, pl) +{ + set_float_exception_flags(0, &env->fp_status); + WT2 = WT0; + update_fcr31(); + DEBUG_FPU_STATE(); + RETURN(); +} +FLOAT_OP(cvts, pu) +{ + set_float_exception_flags(0, &env->fp_status); + WT2 = WTH0; + update_fcr31(); DEBUG_FPU_STATE(); RETURN(); } FLOAT_OP(cvtw, s) { + set_float_exception_flags(0, &env->fp_status); WT2 = float32_to_int32(FST0, &env->fp_status); + update_fcr31(); + if (GET_FP_CAUSE(env->fcr31) & (FP_OVERFLOW | FP_INVALID)) + WT2 = 0x7fffffff; DEBUG_FPU_STATE(); RETURN(); } FLOAT_OP(cvtw, d) { + set_float_exception_flags(0, &env->fp_status); WT2 = float64_to_int32(FDT0, &env->fp_status); + update_fcr31(); + if (GET_FP_CAUSE(env->fcr31) & (FP_OVERFLOW | FP_INVALID)) + WT2 = 0x7fffffff; DEBUG_FPU_STATE(); RETURN(); } +FLOAT_OP(pll, ps) +{ + DT2 = ((uint64_t)WT0 << 32) | WT1; + DEBUG_FPU_STATE(); + RETURN(); +} +FLOAT_OP(plu, ps) +{ + DT2 = ((uint64_t)WT0 << 32) | WTH1; + DEBUG_FPU_STATE(); + RETURN(); +} +FLOAT_OP(pul, ps) +{ + DT2 = ((uint64_t)WTH0 << 32) | WT1; + DEBUG_FPU_STATE(); + RETURN(); +} +FLOAT_OP(puu, ps) +{ + DT2 = ((uint64_t)WTH0 << 32) | WTH1; + DEBUG_FPU_STATE(); + RETURN(); +} + +FLOAT_OP(roundl, d) +{ + set_float_rounding_mode(float_round_nearest_even, &env->fp_status); + DT2 = float64_round_to_int(FDT0, &env->fp_status); + RESTORE_ROUNDING_MODE; + DEBUG_FPU_STATE(); + RETURN(); +} +FLOAT_OP(roundl, s) +{ + set_float_rounding_mode(float_round_nearest_even, &env->fp_status); + DT2 = float32_round_to_int(FST0, &env->fp_status); + RESTORE_ROUNDING_MODE; + 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(); } @@ -1790,6 +1977,18 @@ FLOAT_OP(roundw, s) RETURN(); } +FLOAT_OP(truncl, d) +{ + DT2 = float64_to_int64_round_to_zero(FDT0, &env->fp_status); + DEBUG_FPU_STATE(); + RETURN(); +} +FLOAT_OP(truncl, s) +{ + DT2 = float32_to_int64_round_to_zero(FST0, &env->fp_status); + DEBUG_FPU_STATE(); + RETURN(); +} FLOAT_OP(truncw, d) { WT2 = float64_to_int32_round_to_zero(FDT0, &env->fp_status); @@ -1803,12 +2002,27 @@ FLOAT_OP(truncw, s) RETURN(); } +FLOAT_OP(ceill, d) +{ + set_float_rounding_mode(float_round_up, &env->fp_status); + DT2 = float64_round_to_int(FDT0, &env->fp_status); + RESTORE_ROUNDING_MODE; + DEBUG_FPU_STATE(); + RETURN(); +} +FLOAT_OP(ceill, s) +{ + set_float_rounding_mode(float_round_up, &env->fp_status); + DT2 = float32_round_to_int(FST0, &env->fp_status); + RESTORE_ROUNDING_MODE; + 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(); } @@ -1821,12 +2035,27 @@ FLOAT_OP(ceilw, s) RETURN(); } +FLOAT_OP(floorl, d) +{ + set_float_rounding_mode(float_round_down, &env->fp_status); + DT2 = float64_round_to_int(FDT0, &env->fp_status); + RESTORE_ROUNDING_MODE; + DEBUG_FPU_STATE(); + RETURN(); +} +FLOAT_OP(floorl, s) +{ + set_float_rounding_mode(float_round_down, &env->fp_status); + DT2 = 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(); } @@ -1839,16 +2068,121 @@ FLOAT_OP(floorw, s) RETURN(); } +FLOAT_OP(movf, d) +{ + if (!(env->fcr31 & PARAM1)) + DT2 = DT0; + DEBUG_FPU_STATE(); + RETURN(); +} +FLOAT_OP(movf, s) +{ + if (!(env->fcr31 & PARAM1)) + WT2 = WT0; + DEBUG_FPU_STATE(); + RETURN(); +} +FLOAT_OP(movf, ps) +{ + if (!(env->fcr31 & PARAM1)) { + WT2 = WT0; + WTH2 = WTH0; + } + DEBUG_FPU_STATE(); + RETURN(); +} +FLOAT_OP(movt, d) +{ + if (env->fcr31 & PARAM1) + DT2 = DT0; + DEBUG_FPU_STATE(); + RETURN(); +} +FLOAT_OP(movt, s) +{ + if (env->fcr31 & PARAM1) + WT2 = WT0; + DEBUG_FPU_STATE(); + RETURN(); +} +FLOAT_OP(movt, ps) +{ + if (env->fcr31 & PARAM1) { + WT2 = WT0; + WTH2 = WTH0; + } + DEBUG_FPU_STATE(); + RETURN(); +} +FLOAT_OP(movz, d) +{ + if (!T0) + DT2 = DT0; + DEBUG_FPU_STATE(); + RETURN(); +} +FLOAT_OP(movz, s) +{ + if (!T0) + WT2 = WT0; + DEBUG_FPU_STATE(); + RETURN(); +} +FLOAT_OP(movz, ps) +{ + if (!T0) { + WT2 = WT0; + WTH2 = WTH0; + } + DEBUG_FPU_STATE(); + RETURN(); +} +FLOAT_OP(movn, d) +{ + if (T0) + DT2 = DT0; + DEBUG_FPU_STATE(); + RETURN(); +} +FLOAT_OP(movn, s) +{ + if (T0) + WT2 = WT0; + DEBUG_FPU_STATE(); + RETURN(); +} +FLOAT_OP(movn, ps) +{ + if (T0) { + WT2 = WT0; + WTH2 = WTH0; + } + DEBUG_FPU_STATE(); + RETURN(); +} + /* binary operations */ #define FLOAT_BINOP(name) \ FLOAT_OP(name, d) \ { \ + set_float_exception_flags(0, &env->fp_status); \ FDT2 = float64_ ## name (FDT0, FDT1, &env->fp_status); \ + update_fcr31(); \ DEBUG_FPU_STATE(); \ } \ FLOAT_OP(name, s) \ { \ + set_float_exception_flags(0, &env->fp_status); \ FST2 = float32_ ## name (FST0, FST1, &env->fp_status); \ + update_fcr31(); \ + DEBUG_FPU_STATE(); \ +} \ +FLOAT_OP(name, ps) \ +{ \ + set_float_exception_flags(0, &env->fp_status); \ + FST2 = float32_ ## name (FST0, FST1, &env->fp_status); \ + FSTH2 = float32_ ## name (FSTH0, FSTH1, &env->fp_status); \ + update_fcr31(); \ DEBUG_FPU_STATE(); \ } FLOAT_BINOP(add) @@ -1857,6 +2191,32 @@ FLOAT_BINOP(mul) FLOAT_BINOP(div) #undef FLOAT_BINOP +/* ternary operations */ +#define FLOAT_TERNOP(name1, name2) \ +FLOAT_OP(name1 ## name2, d) \ +{ \ + FDT0 = float64_ ## name1 (FDT0, FDT1, &env->fp_status); \ + FDT2 = float64_ ## name2 (FDT0, FDT2, &env->fp_status); \ + DEBUG_FPU_STATE(); \ +} \ +FLOAT_OP(name1 ## name2, s) \ +{ \ + FST0 = float32_ ## name1 (FST0, FST1, &env->fp_status); \ + FST2 = float32_ ## name2 (FST0, FST2, &env->fp_status); \ + DEBUG_FPU_STATE(); \ +} \ +FLOAT_OP(name1 ## name2, ps) \ +{ \ + FST0 = float32_ ## name1 (FST0, FST1, &env->fp_status); \ + FSTH0 = float32_ ## name1 (FSTH0, FSTH1, &env->fp_status); \ + FST2 = float32_ ## name2 (FST0, FST2, &env->fp_status); \ + FSTH2 = float32_ ## name2 (FSTH0, FSTH2, &env->fp_status); \ + DEBUG_FPU_STATE(); \ +} +FLOAT_TERNOP(mul, add) +FLOAT_TERNOP(mul, sub) +#undef FLOAT_TERNOP + /* unary operations, modifying fp status */ #define FLOAT_UNOP(name) \ FLOAT_OP(name, d) \ @@ -1868,6 +2228,12 @@ FLOAT_OP(name, s) \ { \ FST2 = float32_ ## name(FST0, &env->fp_status); \ DEBUG_FPU_STATE(); \ +} \ +FLOAT_OP(name, ps) \ +{ \ + FST2 = float32_ ## name(FST0, &env->fp_status); \ + FSTH2 = float32_ ## name(FSTH0, &env->fp_status); \ + DEBUG_FPU_STATE(); \ } FLOAT_UNOP(sqrt) #undef FLOAT_UNOP @@ -1883,6 +2249,12 @@ FLOAT_OP(name, s) \ { \ FST2 = float32_ ## name(FST0); \ DEBUG_FPU_STATE(); \ +} \ +FLOAT_OP(name, ps) \ +{ \ + FST2 = float32_ ## name(FST0); \ + FSTH2 = float32_ ## name(FSTH0); \ + DEBUG_FPU_STATE(); \ } FLOAT_UNOP(abs) FLOAT_UNOP(chs) @@ -1900,6 +2272,35 @@ FLOAT_OP(mov, s) DEBUG_FPU_STATE(); RETURN(); } +FLOAT_OP(mov, ps) +{ + FST2 = FST0; + FSTH2 = FSTH0; + DEBUG_FPU_STATE(); + RETURN(); +} +FLOAT_OP(alnv, ps) +{ + switch (T0 & 0x7) { + case 0: + FST2 = FST0; + FSTH2 = FSTH0; + break; + case 4: +#ifdef TARGET_WORDS_BIGENDIAN + FSTH2 = FST0; + FST2 = FSTH1; +#else + FSTH2 = FST1; + FST2 = FSTH0; +#endif + break; + default: /* unpredictable */ + break; + } + DEBUG_FPU_STATE(); + RETURN(); +} #ifdef CONFIG_SOFTFLOAT #define clear_invalid() do { \ @@ -1913,96 +2314,200 @@ FLOAT_OP(mov, s) extern void dump_fpu_s(CPUState *env); -#define FOP_COND(fmt, op, sig, cond) \ -void op_cmp_ ## fmt ## _ ## op (void) \ +#define FOP_COND_D(op, cond) \ +void op_cmp_d_ ## op (void) \ { \ - if (cond) \ - SET_FP_COND(env->fcr31); \ + int c = cond; \ + update_fcr31(); \ + if (c) \ + SET_FP_COND(PARAM1, env); \ else \ - CLEAR_FP_COND(env->fcr31); \ - if (!sig) \ - clear_invalid(); \ - /*CALL_FROM_TB1(dump_fpu_s, env);*/ \ + CLEAR_FP_COND(PARAM1, env); \ DEBUG_FPU_STATE(); \ RETURN(); \ } -int float64_is_unordered(float64 a, float64 b STATUS_PARAM) +int float64_is_unordered(int sig, float64 a, float64 b STATUS_PARAM) { - if (float64_is_nan(a) || float64_is_nan(b)) { + if (float64_is_signaling_nan(a) || + float64_is_signaling_nan(b) || + (sig && (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); + } else if (float64_is_nan(a) || float64_is_nan(b)) { return 1; - } - else { + } 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)) + * but float*_is_unordered() is still called. */ +FOP_COND_D(f, (float64_is_unordered(0, FDT1, FDT0, &env->fp_status), 0)) +FOP_COND_D(un, float64_is_unordered(0, FDT1, FDT0, &env->fp_status)) +FOP_COND_D(eq, !float64_is_unordered(0, FDT1, FDT0, &env->fp_status) && float64_eq(FDT0, FDT1, &env->fp_status)) +FOP_COND_D(ueq, float64_is_unordered(0, FDT1, FDT0, &env->fp_status) || float64_eq(FDT0, FDT1, &env->fp_status)) +FOP_COND_D(olt, !float64_is_unordered(0, FDT1, FDT0, &env->fp_status) && float64_lt(FDT0, FDT1, &env->fp_status)) +FOP_COND_D(ult, float64_is_unordered(0, FDT1, FDT0, &env->fp_status) || float64_lt(FDT0, FDT1, &env->fp_status)) +FOP_COND_D(ole, !float64_is_unordered(0, FDT1, FDT0, &env->fp_status) && float64_le(FDT0, FDT1, &env->fp_status)) +FOP_COND_D(ule, float64_is_unordered(0, 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(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)) + * but float*_is_unordered() is still called. */ +FOP_COND_D(sf, (float64_is_unordered(1, FDT1, FDT0, &env->fp_status), 0)) +FOP_COND_D(ngle,float64_is_unordered(1, FDT1, FDT0, &env->fp_status)) +FOP_COND_D(seq, !float64_is_unordered(1, FDT1, FDT0, &env->fp_status) && float64_eq(FDT0, FDT1, &env->fp_status)) +FOP_COND_D(ngl, float64_is_unordered(1, FDT1, FDT0, &env->fp_status) || float64_eq(FDT0, FDT1, &env->fp_status)) +FOP_COND_D(lt, !float64_is_unordered(1, FDT1, FDT0, &env->fp_status) && float64_lt(FDT0, FDT1, &env->fp_status)) +FOP_COND_D(nge, float64_is_unordered(1, FDT1, FDT0, &env->fp_status) || float64_lt(FDT0, FDT1, &env->fp_status)) +FOP_COND_D(le, !float64_is_unordered(1, FDT1, FDT0, &env->fp_status) && float64_le(FDT0, FDT1, &env->fp_status)) +FOP_COND_D(ngt, float64_is_unordered(1, FDT1, FDT0, &env->fp_status) || float64_le(FDT0, FDT1, &env->fp_status)) + +#define FOP_COND_S(op, cond) \ +void op_cmp_s_ ## op (void) \ +{ \ + int c = cond; \ + update_fcr31(); \ + if (c) \ + SET_FP_COND(PARAM1, env); \ + else \ + CLEAR_FP_COND(PARAM1, env); \ + DEBUG_FPU_STATE(); \ + RETURN(); \ +} + +flag float32_is_unordered(int sig, float32 a, float32 b STATUS_PARAM) +{ + extern flag float32_is_nan(float32 a); + if (float32_is_signaling_nan(a) || + float32_is_signaling_nan(b) || + (sig && (float32_is_nan(a) || float32_is_nan(b)))) { + float_raise(float_flag_invalid, status); + return 1; + } else if (float32_is_nan(a) || float32_is_nan(b)) { + 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, (float32_is_unordered(0, FST1, FST0, &env->fp_status), 0)) +FOP_COND_S(un, float32_is_unordered(0, FST1, FST0, &env->fp_status)) +FOP_COND_S(eq, !float32_is_unordered(0, FST1, FST0, &env->fp_status) && float32_eq(FST0, FST1, &env->fp_status)) +FOP_COND_S(ueq, float32_is_unordered(0, FST1, FST0, &env->fp_status) || float32_eq(FST0, FST1, &env->fp_status)) +FOP_COND_S(olt, !float32_is_unordered(0, FST1, FST0, &env->fp_status) && float32_lt(FST0, FST1, &env->fp_status)) +FOP_COND_S(ult, float32_is_unordered(0, FST1, FST0, &env->fp_status) || float32_lt(FST0, FST1, &env->fp_status)) +FOP_COND_S(ole, !float32_is_unordered(0, FST1, FST0, &env->fp_status) && float32_le(FST0, FST1, &env->fp_status)) +FOP_COND_S(ule, float32_is_unordered(0, 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, (float32_is_unordered(1, FST1, FST0, &env->fp_status), 0)) +FOP_COND_S(ngle,float32_is_unordered(1, FST1, FST0, &env->fp_status)) +FOP_COND_S(seq, !float32_is_unordered(1, FST1, FST0, &env->fp_status) && float32_eq(FST0, FST1, &env->fp_status)) +FOP_COND_S(ngl, float32_is_unordered(1, FST1, FST0, &env->fp_status) || float32_eq(FST0, FST1, &env->fp_status)) +FOP_COND_S(lt, !float32_is_unordered(1, FST1, FST0, &env->fp_status) && float32_lt(FST0, FST1, &env->fp_status)) +FOP_COND_S(nge, float32_is_unordered(1, FST1, FST0, &env->fp_status) || float32_lt(FST0, FST1, &env->fp_status)) +FOP_COND_S(le, !float32_is_unordered(1, FST1, FST0, &env->fp_status) && float32_le(FST0, FST1, &env->fp_status)) +FOP_COND_S(ngt, float32_is_unordered(1, FST1, FST0, &env->fp_status) || float32_le(FST0, FST1, &env->fp_status)) + +#define FOP_COND_PS(op, condl, condh) \ +void op_cmp_ps_ ## op (void) \ +{ \ + int cl = condl; \ + int ch = condh; \ + update_fcr31(); \ + if (cl) \ + SET_FP_COND(PARAM1, env); \ + else \ + CLEAR_FP_COND(PARAM1, env); \ + if (ch) \ + SET_FP_COND(PARAM1 + 1, env); \ + else \ + CLEAR_FP_COND(PARAM1 + 1, env); \ + DEBUG_FPU_STATE(); \ + RETURN(); \ +} + +/* NOTE: the comma operator will make "cond" to eval to false, + * but float*_is_unordered() is still called. */ +FOP_COND_PS(f, (float32_is_unordered(0, FST1, FST0, &env->fp_status), 0), + (float32_is_unordered(0, FSTH1, FSTH0, &env->fp_status), 0)) +FOP_COND_PS(un, float32_is_unordered(0, FST1, FST0, &env->fp_status), + float32_is_unordered(0, FSTH1, FSTH0, &env->fp_status)) +FOP_COND_PS(eq, !float32_is_unordered(0, FST1, FST0, &env->fp_status) && float32_eq(FST0, FST1, &env->fp_status), + !float32_is_unordered(0, FSTH1, FSTH0, &env->fp_status) && float32_eq(FSTH0, FSTH1, &env->fp_status)) +FOP_COND_PS(ueq, float32_is_unordered(0, FST1, FST0, &env->fp_status) || float32_eq(FST0, FST1, &env->fp_status), + float32_is_unordered(0, FSTH1, FSTH0, &env->fp_status) || float32_eq(FSTH0, FSTH1, &env->fp_status)) +FOP_COND_PS(olt, !float32_is_unordered(0, FST1, FST0, &env->fp_status) && float32_lt(FST0, FST1, &env->fp_status), + !float32_is_unordered(0, FSTH1, FSTH0, &env->fp_status) && float32_lt(FSTH0, FSTH1, &env->fp_status)) +FOP_COND_PS(ult, float32_is_unordered(0, FST1, FST0, &env->fp_status) || float32_lt(FST0, FST1, &env->fp_status), + float32_is_unordered(0, FSTH1, FSTH0, &env->fp_status) || float32_lt(FSTH0, FSTH1, &env->fp_status)) +FOP_COND_PS(ole, !float32_is_unordered(0, FST1, FST0, &env->fp_status) && float32_le(FST0, FST1, &env->fp_status), + !float32_is_unordered(0, FSTH1, FSTH0, &env->fp_status) && float32_le(FSTH0, FSTH1, &env->fp_status)) +FOP_COND_PS(ule, float32_is_unordered(0, FST1, FST0, &env->fp_status) || float32_le(FST0, FST1, &env->fp_status), + float32_is_unordered(0, FSTH1, FSTH0, &env->fp_status) || float32_le(FSTH0, FSTH1, &env->fp_status)) +/* NOTE: the comma operator will make "cond" to eval to false, + * but float*_is_unordered() is still called. */ +FOP_COND_PS(sf, (float32_is_unordered(1, FST1, FST0, &env->fp_status), 0), + (float32_is_unordered(1, FSTH1, FSTH0, &env->fp_status), 0)) +FOP_COND_PS(ngle,float32_is_unordered(1, FST1, FST0, &env->fp_status), + float32_is_unordered(1, FSTH1, FSTH0, &env->fp_status)) +FOP_COND_PS(seq, !float32_is_unordered(1, FST1, FST0, &env->fp_status) && float32_eq(FST0, FST1, &env->fp_status), + !float32_is_unordered(1, FSTH1, FSTH0, &env->fp_status) && float32_eq(FSTH0, FSTH1, &env->fp_status)) +FOP_COND_PS(ngl, float32_is_unordered(1, FST1, FST0, &env->fp_status) || float32_eq(FST0, FST1, &env->fp_status), + float32_is_unordered(1, FSTH1, FSTH0, &env->fp_status) || float32_eq(FSTH0, FSTH1, &env->fp_status)) +FOP_COND_PS(lt, !float32_is_unordered(1, FST1, FST0, &env->fp_status) && float32_lt(FST0, FST1, &env->fp_status), + !float32_is_unordered(1, FSTH1, FSTH0, &env->fp_status) && float32_lt(FSTH0, FSTH1, &env->fp_status)) +FOP_COND_PS(nge, float32_is_unordered(1, FST1, FST0, &env->fp_status) || float32_lt(FST0, FST1, &env->fp_status), + float32_is_unordered(1, FSTH1, FSTH0, &env->fp_status) || float32_lt(FSTH0, FSTH1, &env->fp_status)) +FOP_COND_PS(le, !float32_is_unordered(1, FST1, FST0, &env->fp_status) && float32_le(FST0, FST1, &env->fp_status), + !float32_is_unordered(1, FSTH1, FSTH0, &env->fp_status) && float32_le(FSTH0, FSTH1, &env->fp_status)) +FOP_COND_PS(ngt, float32_is_unordered(1, FST1, FST0, &env->fp_status) || float32_le(FST0, FST1, &env->fp_status), + float32_is_unordered(1, FSTH1, FSTH0, &env->fp_status) || float32_le(FSTH0, FSTH1, &env->fp_status)) void op_bc1f (void) { - T0 = ! IS_FP_COND_SET(env->fcr31); + T0 = !IS_FP_COND_SET(PARAM1, env); + DEBUG_FPU_STATE(); + RETURN(); +} +void op_bc1fany2 (void) +{ + T0 = (!IS_FP_COND_SET(PARAM1, env) || + !IS_FP_COND_SET(PARAM1 + 1, env)); + DEBUG_FPU_STATE(); + RETURN(); +} +void op_bc1fany4 (void) +{ + T0 = (!IS_FP_COND_SET(PARAM1, env) || + !IS_FP_COND_SET(PARAM1 + 1, env) || + !IS_FP_COND_SET(PARAM1 + 2, env) || + !IS_FP_COND_SET(PARAM1 + 3, env)); DEBUG_FPU_STATE(); RETURN(); } void op_bc1t (void) { - T0 = IS_FP_COND_SET(env->fcr31); + T0 = IS_FP_COND_SET(PARAM1, env); + DEBUG_FPU_STATE(); + RETURN(); +} +void op_bc1tany2 (void) +{ + T0 = (IS_FP_COND_SET(PARAM1, env) || + IS_FP_COND_SET(PARAM1 + 1, env)); + DEBUG_FPU_STATE(); + RETURN(); +} +void op_bc1tany4 (void) +{ + T0 = (IS_FP_COND_SET(PARAM1, env) || + IS_FP_COND_SET(PARAM1 + 1, env) || + IS_FP_COND_SET(PARAM1 + 2, env) || + IS_FP_COND_SET(PARAM1 + 3, env)); DEBUG_FPU_STATE(); RETURN(); } @@ -2037,7 +2542,7 @@ void op_tlbr (void) #if defined (CONFIG_USER_ONLY) void op_tls_value (void) { - T0 = env->tls_value; + T0 = env->tls_value; } #endif @@ -2180,6 +2685,17 @@ void op_save_pc (void) RETURN(); } +void op_save_fp_status (void) +{ + union fps { + uint32_t i; + float_status f; + } fps; + fps.i = PARAM1; + env->fp_status = fps.f; + RETURN(); +} + void op_interrupt_restart (void) { if (!(env->CP0_Status & (1 << CP0St_EXL)) && diff --git a/target-mips/op_mem.c b/target-mips/op_mem.c index 19373cf002..f0eebc09dd 100644 --- a/target-mips/op_mem.c +++ b/target-mips/op_mem.c @@ -220,3 +220,35 @@ void glue(op_sdc1, MEMSUFFIX) (void) glue(stq, MEMSUFFIX)(T0, DT0); RETURN(); } +void glue(op_lwxc1, MEMSUFFIX) (void) +{ + WT0 = glue(ldl, MEMSUFFIX)(T0 + T1); + RETURN(); +} +void glue(op_swxc1, MEMSUFFIX) (void) +{ + glue(stl, MEMSUFFIX)(T0 + T1, WT0); + RETURN(); +} +void glue(op_ldxc1, MEMSUFFIX) (void) +{ + DT0 = glue(ldq, MEMSUFFIX)(T0 + T1); + RETURN(); +} +void glue(op_sdxc1, MEMSUFFIX) (void) +{ + glue(stq, MEMSUFFIX)(T0 + T1, DT0); + RETURN(); +} +void glue(op_luxc1, MEMSUFFIX) (void) +{ + /* XXX: is defined as unaligned */ + DT0 = glue(ldq, MEMSUFFIX)(T0 + T1); + RETURN(); +} +void glue(op_suxc1, MEMSUFFIX) (void) +{ + /* XXX: is defined as unaligned */ + glue(stq, MEMSUFFIX)(T0 + T1, DT0); + RETURN(); +} diff --git a/target-mips/translate.c b/target-mips/translate.c index 06581f2cc6..6098b2a10b 100644 --- a/target-mips/translate.c +++ b/target-mips/translate.c @@ -333,20 +333,26 @@ enum { OPC_MFC1 = (0x00 << 21) | OPC_CP1, OPC_DMFC1 = (0x01 << 21) | OPC_CP1, OPC_CFC1 = (0x02 << 21) | OPC_CP1, - OPC_MFHCI = (0x03 << 21) | OPC_CP1, + OPC_MFHC1 = (0x03 << 21) | OPC_CP1, OPC_MTC1 = (0x04 << 21) | OPC_CP1, OPC_DMTC1 = (0x05 << 21) | OPC_CP1, OPC_CTC1 = (0x06 << 21) | OPC_CP1, - OPC_MTHCI = (0x07 << 21) | OPC_CP1, + OPC_MTHC1 = (0x07 << 21) | OPC_CP1, OPC_BC1 = (0x08 << 21) | OPC_CP1, /* bc */ + OPC_BC1ANY2 = (0x09 << 21) | OPC_CP1, + OPC_BC1ANY4 = (0x0A << 21) | OPC_CP1, OPC_S_FMT = (0x10 << 21) | OPC_CP1, /* 16: fmt=single fp */ OPC_D_FMT = (0x11 << 21) | OPC_CP1, /* 17: fmt=double fp */ OPC_E_FMT = (0x12 << 21) | OPC_CP1, /* 18: fmt=extended fp */ OPC_Q_FMT = (0x13 << 21) | OPC_CP1, /* 19: fmt=quad fp */ OPC_W_FMT = (0x14 << 21) | OPC_CP1, /* 20: fmt=32bit fixed */ OPC_L_FMT = (0x15 << 21) | OPC_CP1, /* 21: fmt=64bit fixed */ + OPC_PS_FMT = (0x16 << 21) | OPC_CP1, /* 22: fmt=paired single fp */ }; +#define MASK_CP1_FUNC(op) MASK_CP1(op) | (op & 0x3F) +#define MASK_BC1(op) MASK_CP1(op) | (op & (0x3 << 16)) + enum { OPC_BC1F = (0x00 << 16) | OPC_BC1, OPC_BC1T = (0x01 << 16) | OPC_BC1, @@ -354,8 +360,15 @@ enum { OPC_BC1TL = (0x03 << 16) | OPC_BC1, }; -#define MASK_CP1_BCOND(op) MASK_CP1(op) | (op & (0x3 << 16)) -#define MASK_CP1_FUNC(op) MASK_CP1(op) | (op & 0x3F) +enum { + OPC_BC1FANY2 = (0x00 << 16) | OPC_BC1ANY2, + OPC_BC1TANY2 = (0x01 << 16) | OPC_BC1ANY2, +}; + +enum { + OPC_BC1FANY4 = (0x00 << 16) | OPC_BC1ANY4, + OPC_BC1TANY4 = (0x01 << 16) | OPC_BC1ANY4, +}; #define MASK_CP2(op) MASK_OP_MAJOR(op) | (op & (0x1F << 21)) @@ -404,20 +417,20 @@ const unsigned char *regnames[] = "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra", }; /* Warning: no function for r0 register (hard wired to zero) */ -#define GEN32(func, NAME) \ -static GenOpFunc *NAME ## _table [32] = { \ -NULL, 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 GEN32(func, NAME) \ +static GenOpFunc *NAME ## _table [32] = { \ +NULL, 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](); \ } /* General purpose registers moves */ @@ -434,58 +447,51 @@ static const char *fregnames[] = "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 FGEN32(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](); \ -} +FGEN32(gen_op_load_fpr_WT0, gen_op_load_fpr_WT0_fpr); +FGEN32(gen_op_store_fpr_WT0, gen_op_store_fpr_WT0_fpr); -SFGEN32(gen_op_load_fpr_WT0, gen_op_load_fpr_WT0_fpr); -SFGEN32(gen_op_store_fpr_WT0, gen_op_store_fpr_WT0_fpr); +FGEN32(gen_op_load_fpr_WT1, gen_op_load_fpr_WT1_fpr); +FGEN32(gen_op_store_fpr_WT1, gen_op_store_fpr_WT1_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); +FGEN32(gen_op_load_fpr_WT2, gen_op_load_fpr_WT2_fpr); +FGEN32(gen_op_store_fpr_WT2, gen_op_store_fpr_WT2_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); +FGEN32(gen_op_load_fpr_DT0, gen_op_load_fpr_DT0_fpr); +FGEN32(gen_op_store_fpr_DT0, gen_op_store_fpr_DT0_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); +FGEN32(gen_op_load_fpr_DT1, gen_op_load_fpr_DT1_fpr); +FGEN32(gen_op_store_fpr_DT1, gen_op_store_fpr_DT1_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); +FGEN32(gen_op_load_fpr_DT2, gen_op_load_fpr_DT2_fpr); +FGEN32(gen_op_store_fpr_DT2, gen_op_store_fpr_DT2_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); +FGEN32(gen_op_load_fpr_WTH0, gen_op_load_fpr_WTH0_fpr); +FGEN32(gen_op_store_fpr_WTH0, gen_op_store_fpr_WTH0_fpr); + +FGEN32(gen_op_load_fpr_WTH1, gen_op_load_fpr_WTH1_fpr); +FGEN32(gen_op_store_fpr_WTH1, gen_op_store_fpr_WTH1_fpr); + +FGEN32(gen_op_load_fpr_WTH2, gen_op_load_fpr_WTH2_fpr); +FGEN32(gen_op_store_fpr_WTH2, gen_op_store_fpr_WTH2_fpr); #define FOP_CONDS(fmt) \ -static GenOpFunc * cond_ ## fmt ## _table[16] = { \ +static GenOpFunc1 * cond_ ## fmt ## _table[16] = { \ gen_op_cmp_ ## fmt ## _f, \ gen_op_cmp_ ## fmt ## _un, \ gen_op_cmp_ ## fmt ## _eq, \ @@ -503,18 +509,20 @@ static GenOpFunc * cond_ ## fmt ## _table[16] = { \ gen_op_cmp_ ## fmt ## _le, \ gen_op_cmp_ ## fmt ## _ngt, \ }; \ -static inline void gen_cmp_ ## fmt(int n) \ +static inline void gen_cmp_ ## fmt(int n, long cc) \ { \ - cond_ ## fmt ## _table[n](); \ + cond_ ## fmt ## _table[n](cc); \ } FOP_CONDS(d) FOP_CONDS(s) +FOP_CONDS(ps) typedef struct DisasContext { struct TranslationBlock *tb; target_ulong pc, saved_pc; uint32_t opcode; + uint32_t fp_status, saved_fp_status; /* Routine used to access memory */ int mem_idx; uint32_t hflags, saved_hflags; @@ -600,17 +608,31 @@ static inline void save_cpu_state (DisasContext *ctx, int do_save_pc) if (ctx->hflags != ctx->saved_hflags) { gen_op_save_state(ctx->hflags); ctx->saved_hflags = ctx->hflags; - if (ctx->hflags & MIPS_HFLAG_BR) { + switch (ctx->hflags & MIPS_HFLAG_BMASK) { + case MIPS_HFLAG_BR: gen_op_save_breg_target(); - } else if (ctx->hflags & MIPS_HFLAG_B) { - gen_op_save_btarget(ctx->btarget); - } else if (ctx->hflags & MIPS_HFLAG_BMASK) { + break; + case MIPS_HFLAG_BC: gen_op_save_bcond(); + /* fall through */ + case MIPS_HFLAG_BL: + /* bcond was already saved by the BL insn */ + /* fall through */ + case MIPS_HFLAG_B: gen_op_save_btarget(ctx->btarget); + break; } } } +static inline void save_fpu_state (DisasContext *ctx) +{ + if (ctx->fp_status != ctx->saved_fp_status) { + gen_op_save_fp_status(ctx->fp_status); + ctx->saved_fp_status = ctx->fp_status; + } +} + static inline void generate_exception_err (DisasContext *ctx, int excp, int err) { #if defined MIPS_DEBUG_DISAS @@ -677,6 +699,12 @@ OP_LD_TABLE(wc1); OP_ST_TABLE(wc1); OP_LD_TABLE(dc1); OP_ST_TABLE(dc1); +OP_LD_TABLE(wxc1); +OP_ST_TABLE(wxc1); +OP_LD_TABLE(dxc1); +OP_ST_TABLE(dxc1); +OP_LD_TABLE(uxc1); +OP_ST_TABLE(uxc1); /* Load and store */ static void gen_ldst (DisasContext *ctx, uint32_t opc, int rt, @@ -1472,7 +1500,7 @@ static void gen_compute_branch (DisasContext *ctx, uint32_t opc, if (ctx->hflags & MIPS_HFLAG_BMASK) { if (loglevel & CPU_LOG_TB_IN_ASM) { fprintf(logfile, - "undefined branch in delay slot at PC " TARGET_FMT_lx "\n", + "Branch in delay slot at PC 0x" TARGET_FMT_lx "\n", ctx->pc); } MIPS_INVAL("branch/jump in bdelay slot"); @@ -1672,6 +1700,7 @@ static void gen_compute_branch (DisasContext *ctx, uint32_t opc, MIPS_DEBUG("bltzal %s, %08x", regnames[rs], btarget); not_likely: ctx->hflags |= MIPS_HFLAG_BC; + gen_op_set_bcond(); break; case OPC_BLTZALL: gen_op_ltz(); @@ -1679,13 +1708,14 @@ static void gen_compute_branch (DisasContext *ctx, uint32_t opc, MIPS_DEBUG("bltzall %s, %08x", regnames[rs], btarget); likely: ctx->hflags |= MIPS_HFLAG_BL; + gen_op_set_bcond(); + gen_op_save_bcond(); break; default: MIPS_INVAL("conditional branch/jump"); generate_exception(ctx, EXCP_RI); return; } - gen_op_set_bcond(); } MIPS_DEBUG("enter ds: link %d cond %02x target %08x", blink, ctx->hflags, btarget); @@ -4220,7 +4250,7 @@ static void gen_cp0 (DisasContext *ctx, uint32_t opc, int rt, int rd) /* CP1 Branches (before delay slot) */ static void gen_compute_branch1 (DisasContext *ctx, uint32_t op, - int32_t offset) + int32_t cc, int32_t offset) { target_ulong btarget; @@ -4228,31 +4258,49 @@ static void gen_compute_branch1 (DisasContext *ctx, uint32_t op, switch (op) { case OPC_BC1F: - gen_op_bc1f(); + gen_op_bc1f(cc); MIPS_DEBUG("bc1f " TARGET_FMT_lx, btarget); goto not_likely; case OPC_BC1FL: - gen_op_bc1f(); + gen_op_bc1f(cc); MIPS_DEBUG("bc1fl " TARGET_FMT_lx, btarget); goto likely; case OPC_BC1T: - gen_op_bc1t(); + gen_op_bc1t(cc); MIPS_DEBUG("bc1t " TARGET_FMT_lx, btarget); - not_likely: - ctx->hflags |= MIPS_HFLAG_BC; - break; + goto not_likely; case OPC_BC1TL: - gen_op_bc1t(); + gen_op_bc1t(cc); MIPS_DEBUG("bc1tl " TARGET_FMT_lx, btarget); likely: ctx->hflags |= MIPS_HFLAG_BL; + gen_op_set_bcond(); + gen_op_save_bcond(); break; - default: - MIPS_INVAL("cp1 branch/jump"); + case OPC_BC1FANY2: + gen_op_bc1fany2(cc); + MIPS_DEBUG("bc1fany2 " TARGET_FMT_lx, btarget); + goto not_likely; + case OPC_BC1TANY2: + gen_op_bc1tany2(cc); + MIPS_DEBUG("bc1tany2 " TARGET_FMT_lx, btarget); + goto not_likely; + case OPC_BC1FANY4: + gen_op_bc1fany4(cc); + MIPS_DEBUG("bc1fany4 " TARGET_FMT_lx, btarget); + goto not_likely; + case OPC_BC1TANY4: + gen_op_bc1tany4(cc); + MIPS_DEBUG("bc1tany4 " TARGET_FMT_lx, btarget); + not_likely: + ctx->hflags |= MIPS_HFLAG_BC; + gen_op_set_bcond(); + break; + default: + MIPS_INVAL("cp1 branch"); generate_exception (ctx, EXCP_RI); return; } - gen_op_set_bcond(); MIPS_DEBUG("enter ds: cond %02x target " TARGET_FMT_lx, ctx->hflags, btarget); @@ -4262,6 +4310,29 @@ static void gen_compute_branch1 (DisasContext *ctx, uint32_t op, } /* Coprocessor 1 (FPU) */ + +/* 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. This limitation exists only for 64 bit wide (d,l,ps) registers. + * + * Multiple 64 bit wide registers can be checked by calling + * CHECK_FR(ctx, freg1 | freg2 | ... | fregN); + * + * FIXME: This is broken for R2, it needs to be checked at runtime, not + * at translation time. + */ +#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_cp1 (DisasContext *ctx, uint32_t opc, int rt, int fs) { const char *opn = "unk"; @@ -4280,30 +4351,43 @@ static void gen_cp1 (DisasContext *ctx, uint32_t opc, int rt, int fs) 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; case OPC_DMFC1: + GEN_LOAD_FREG_FTN(DT0, fs); + gen_op_dmfc1(); + GEN_STORE_TN_REG(rt, T0); + opn = "dmfc1"; + break; case OPC_DMTC1: - /* Not implemented, fallthrough. */ + GEN_LOAD_REG_TN(T0, rt); + gen_op_dmtc1(); + GEN_STORE_FTN_FREG(fs, DT0); + opn = "dmtc1"; + break; + case OPC_MFHC1: + CHECK_FR(ctx, fs); + GEN_LOAD_FREG_FTN(WTH0, fs); + gen_op_mfhc1(); + GEN_STORE_TN_REG(rt, T0); + opn = "mfhc1"; + break; + case OPC_MTHC1: + CHECK_FR(ctx, fs); + GEN_LOAD_REG_TN(T0, rt); + gen_op_mthc1(); + GEN_STORE_FTN_FREG(fs, WTH0); + opn = "mthc1"; + break; default: if (loglevel & CPU_LOG_TB_IN_ASM) { fprintf(logfile, "Invalid CP1 opcode: %08x %03x %03x %03x\n", @@ -4316,26 +4400,44 @@ static void gen_cp1 (DisasContext *ctx, uint32_t opc, int rt, int fs) 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. This limitation exists only for 64 bit wide (d,l) registers. - * - * Multiple 64 bit wide registers can be checked by calling - * CHECK_FR(ctx, freg1 | freg2 | ... | fregN); - */ -#define CHECK_FR(ctx, freg) do { \ - if (!((ctx)->CP0_Status & (1<opcode & 0x3f; switch (ctx->opcode & FOP(0x3f, 0x1f)) { + case FOP(0, 16): + 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): + 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): + 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): + 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): + 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): + 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): + 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): + GEN_LOAD_FREG_FTN(WT0, fs); + gen_op_float_chs_s(); + GEN_STORE_FTN_FREG(fd, WT2); + opn = "neg.s"; + break; + case FOP(8, 16): + CHECK_FR(ctx, fs); + GEN_LOAD_FREG_FTN(WT0, fs); + gen_op_float_roundl_s(); + GEN_STORE_FTN_FREG(fd, DT2); + opn = "round.l.s"; + break; + case FOP(9, 16): + CHECK_FR(ctx, fs); + GEN_LOAD_FREG_FTN(WT0, fs); + gen_op_float_truncl_s(); + GEN_STORE_FTN_FREG(fd, DT2); + opn = "trunc.l.s"; + break; + case FOP(10, 16): + CHECK_FR(ctx, fs); + GEN_LOAD_FREG_FTN(WT0, fs); + gen_op_float_ceill_s(); + GEN_STORE_FTN_FREG(fd, DT2); + opn = "ceil.l.s"; + break; + case FOP(11, 16): + CHECK_FR(ctx, fs); + GEN_LOAD_FREG_FTN(WT0, fs); + gen_op_float_floorl_s(); + GEN_STORE_FTN_FREG(fd, DT2); + opn = "floor.l.s"; + break; + case FOP(12, 16): + 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): + GEN_LOAD_FREG_FTN(WT0, fs); + gen_op_float_truncw_s(); + GEN_STORE_FTN_FREG(fd, WT2); + opn = "trunc.w.s"; + break; + case FOP(14, 16): + GEN_LOAD_FREG_FTN(WT0, fs); + gen_op_float_ceilw_s(); + GEN_STORE_FTN_FREG(fd, WT2); + opn = "ceil.w.s"; + break; + case FOP(15, 16): + GEN_LOAD_FREG_FTN(WT0, fs); + gen_op_float_floorw_s(); + GEN_STORE_FTN_FREG(fd, WT2); + opn = "floor.w.s"; + break; + case FOP(17, 16): + GEN_LOAD_REG_TN(T0, ft); + GEN_LOAD_FREG_FTN(WT0, fs); + GEN_LOAD_FREG_FTN(WT2, fd); + gen_movcf_s(ctx, (ft >> 2) & 0x7, ft & 0x1); + GEN_STORE_FTN_FREG(fd, WT2); + opn = "movcf.s"; + break; + case FOP(18, 16): + GEN_LOAD_REG_TN(T0, ft); + GEN_LOAD_FREG_FTN(WT0, fs); + GEN_LOAD_FREG_FTN(WT2, fd); + gen_op_float_movz_s(); + GEN_STORE_FTN_FREG(fd, WT2); + opn = "movz.s"; + break; + case FOP(19, 16): + GEN_LOAD_REG_TN(T0, ft); + GEN_LOAD_FREG_FTN(WT0, fs); + GEN_LOAD_FREG_FTN(WT2, fd); + gen_op_float_movn_s(); + GEN_STORE_FTN_FREG(fd, WT2); + opn = "movn.s"; + break; + case FOP(33, 16): + CHECK_FR(ctx, fd); + GEN_LOAD_FREG_FTN(WT0, fs); + gen_op_float_cvtd_s(); + GEN_STORE_FTN_FREG(fd, DT2); + opn = "cvt.d.s"; + break; + case FOP(36, 16): + GEN_LOAD_FREG_FTN(WT0, fs); + gen_op_float_cvtw_s(); + GEN_STORE_FTN_FREG(fd, WT2); + opn = "cvt.w.s"; + break; + case FOP(37, 16): + CHECK_FR(ctx, fs | fd); + GEN_LOAD_FREG_FTN(WT0, fs); + gen_op_float_cvtl_s(); + GEN_STORE_FTN_FREG(fd, DT2); + opn = "cvt.l.s"; + break; + case FOP(38, 16): + CHECK_FR(ctx, fs | ft | fd); + GEN_LOAD_FREG_FTN(WT1, fs); + GEN_LOAD_FREG_FTN(WT0, ft); + gen_op_float_cvtps_s(); + GEN_STORE_FTN_FREG(fd, DT2); + opn = "cvt.ps.s"; + 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): + GEN_LOAD_FREG_FTN(WT0, fs); + GEN_LOAD_FREG_FTN(WT1, ft); + gen_cmp_s(func-48, cc); + opn = condnames[func-48]; + break; case FOP(0, 17): CHECK_FR(ctx, fs | ft | fd); GEN_LOAD_FREG_FTN(DT0, fs); @@ -4424,10 +4707,34 @@ static void gen_farith (DisasContext *ctx, uint32_t op1, int ft, int fs, int fd) GEN_STORE_FTN_FREG(fd, DT2); opn = "neg.d"; break; - /* 8 - round.l */ - /* 9 - trunc.l */ - /* 10 - ceil.l */ - /* 11 - floor.l */ + case FOP(8, 17): + CHECK_FR(ctx, fs); + GEN_LOAD_FREG_FTN(DT0, fs); + gen_op_float_roundl_d(); + GEN_STORE_FTN_FREG(fd, DT2); + opn = "round.l.d"; + break; + case FOP(9, 17): + CHECK_FR(ctx, fs); + GEN_LOAD_FREG_FTN(DT0, fs); + gen_op_float_truncl_d(); + GEN_STORE_FTN_FREG(fd, DT2); + opn = "trunc.l.d"; + break; + case FOP(10, 17): + CHECK_FR(ctx, fs); + GEN_LOAD_FREG_FTN(DT0, fs); + gen_op_float_ceill_d(); + GEN_STORE_FTN_FREG(fd, DT2); + opn = "ceil.l.d"; + break; + case FOP(11, 17): + CHECK_FR(ctx, fs); + GEN_LOAD_FREG_FTN(DT0, fs); + gen_op_float_floorl_d(); + GEN_STORE_FTN_FREG(fd, DT2); + opn = "floor.l.d"; + break; case FOP(12, 17): CHECK_FR(ctx, fs); GEN_LOAD_FREG_FTN(DT0, fs); @@ -4456,19 +4763,29 @@ static void gen_farith (DisasContext *ctx, uint32_t op1, int ft, int fs, int fd) GEN_STORE_FTN_FREG(fd, WT2); opn = "floor.w.d"; break; - case FOP(33, 16): - CHECK_FR(ctx, fd); - GEN_LOAD_FREG_FTN(WT0, fs); - gen_op_float_cvtd_s(); + case FOP(17, 17): + GEN_LOAD_REG_TN(T0, ft); + GEN_LOAD_FREG_FTN(DT0, fs); + GEN_LOAD_FREG_FTN(DT2, fd); + gen_movcf_d(ctx, (ft >> 2) & 0x7, ft & 0x1); GEN_STORE_FTN_FREG(fd, DT2); - opn = "cvt.d.s"; + opn = "movcf.d"; break; - case FOP(33, 20): - CHECK_FR(ctx, fd); - GEN_LOAD_FREG_FTN(WT0, fs); - gen_op_float_cvtd_w(); + case FOP(18, 17): + GEN_LOAD_REG_TN(T0, ft); + GEN_LOAD_FREG_FTN(DT0, fs); + GEN_LOAD_FREG_FTN(DT2, fd); + gen_op_float_movz_d(); GEN_STORE_FTN_FREG(fd, DT2); - opn = "cvt.d.w"; + opn = "movz.d"; + break; + case FOP(19, 17): + GEN_LOAD_REG_TN(T0, ft); + GEN_LOAD_FREG_FTN(DT0, fs); + GEN_LOAD_FREG_FTN(DT2, fd); + gen_op_float_movn_d(); + GEN_STORE_FTN_FREG(fd, DT2); + opn = "movn.d"; break; case FOP(48, 17): case FOP(49, 17): @@ -4489,77 +4806,9 @@ static void gen_farith (DisasContext *ctx, uint32_t op1, int ft, int fs, int fd) CHECK_FR(ctx, fs | ft); GEN_LOAD_FREG_FTN(DT0, fs); GEN_LOAD_FREG_FTN(DT1, ft); - gen_cmp_d(func-48); + gen_cmp_d(func-48, cc); opn = condnames[func-48]; break; - case FOP(0, 16): - 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): - 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): - 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): - 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): - 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): - 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): - 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): - 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): - 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): - 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, 17): CHECK_FR(ctx, fs); GEN_LOAD_FREG_FTN(DT0, fs); @@ -4567,18 +4816,6 @@ static void gen_farith (DisasContext *ctx, uint32_t op1, int ft, int fs, int fd) GEN_STORE_FTN_FREG(fd, WT2); opn = "cvt.s.d"; break; - case FOP(32, 20): - 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): - 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): CHECK_FR(ctx, fs); GEN_LOAD_FREG_FTN(DT0, fs); @@ -4586,28 +4823,223 @@ static void gen_farith (DisasContext *ctx, uint32_t op1, int ft, int fs, int fd) 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): + case FOP(37, 17): + CHECK_FR(ctx, fs | fd); + GEN_LOAD_FREG_FTN(DT0, fs); + gen_op_float_cvtl_d(); + GEN_STORE_FTN_FREG(fd, DT2); + opn = "cvt.l.d"; + break; + case FOP(32, 20): + GEN_LOAD_FREG_FTN(WT0, fs); + gen_op_float_cvts_w(); + GEN_STORE_FTN_FREG(fd, WT2); + opn = "cvt.s.w"; + break; + case FOP(33, 20): + CHECK_FR(ctx, 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(32, 21): + CHECK_FR(ctx, fs); + GEN_LOAD_FREG_FTN(DT0, fs); + gen_op_float_cvts_l(); + GEN_STORE_FTN_FREG(fd, WT2); + opn = "cvt.s.l"; + break; + case FOP(33, 21): + CHECK_FR(ctx, fs | fd); + GEN_LOAD_FREG_FTN(DT0, fs); + gen_op_float_cvtd_l(); + GEN_STORE_FTN_FREG(fd, DT2); + opn = "cvt.d.l"; + break; + case FOP(38, 20): + case FOP(38, 21): + CHECK_FR(ctx, fs | fd); + GEN_LOAD_FREG_FTN(WT0, fs); + GEN_LOAD_FREG_FTN(WTH0, fs); + gen_op_float_cvtps_pw(); + GEN_STORE_FTN_FREG(fd, WT2); + GEN_STORE_FTN_FREG(fd, WTH2); + opn = "cvt.ps.pw"; + break; + case FOP(0, 22): + CHECK_FR(ctx, fs | ft | fd); + GEN_LOAD_FREG_FTN(WT0, fs); + GEN_LOAD_FREG_FTN(WTH0, fs); + GEN_LOAD_FREG_FTN(WT1, ft); + GEN_LOAD_FREG_FTN(WTH1, ft); + gen_op_float_add_ps(); + GEN_STORE_FTN_FREG(fd, WT2); + GEN_STORE_FTN_FREG(fd, WTH2); + opn = "add.ps"; + break; + case FOP(1, 22): + CHECK_FR(ctx, fs | ft | fd); + GEN_LOAD_FREG_FTN(WT0, fs); + GEN_LOAD_FREG_FTN(WTH0, fs); + GEN_LOAD_FREG_FTN(WT1, ft); + GEN_LOAD_FREG_FTN(WTH1, ft); + gen_op_float_sub_ps(); + GEN_STORE_FTN_FREG(fd, WT2); + GEN_STORE_FTN_FREG(fd, WTH2); + opn = "sub.ps"; + break; + case FOP(2, 22): + CHECK_FR(ctx, fs | ft | fd); + GEN_LOAD_FREG_FTN(WT0, fs); + GEN_LOAD_FREG_FTN(WTH0, fs); + GEN_LOAD_FREG_FTN(WT1, ft); + GEN_LOAD_FREG_FTN(WTH1, ft); + gen_op_float_mul_ps(); + GEN_STORE_FTN_FREG(fd, WT2); + GEN_STORE_FTN_FREG(fd, WTH2); + opn = "mul.ps"; + break; + case FOP(5, 22): + CHECK_FR(ctx, fs | fd); + GEN_LOAD_FREG_FTN(WT0, fs); + GEN_LOAD_FREG_FTN(WTH0, fs); + gen_op_float_abs_ps(); + GEN_STORE_FTN_FREG(fd, WT2); + GEN_STORE_FTN_FREG(fd, WTH2); + opn = "abs.ps"; + break; + case FOP(6, 22): + CHECK_FR(ctx, fs | fd); + GEN_LOAD_FREG_FTN(WT0, fs); + GEN_LOAD_FREG_FTN(WTH0, fs); + gen_op_float_mov_ps(); + GEN_STORE_FTN_FREG(fd, WT2); + GEN_STORE_FTN_FREG(fd, WTH2); + opn = "mov.ps"; + break; + case FOP(7, 22): + CHECK_FR(ctx, fs | fd); + GEN_LOAD_FREG_FTN(WT0, fs); + GEN_LOAD_FREG_FTN(WTH0, fs); + gen_op_float_chs_ps(); + GEN_STORE_FTN_FREG(fd, WT2); + GEN_STORE_FTN_FREG(fd, WTH2); + opn = "neg.ps"; + break; + case FOP(17, 22): + GEN_LOAD_REG_TN(T0, ft); + GEN_LOAD_FREG_FTN(WT0, fs); + GEN_LOAD_FREG_FTN(WTH0, fs); + GEN_LOAD_FREG_FTN(WT2, fd); + GEN_LOAD_FREG_FTN(WTH2, fd); + gen_movcf_ps(ctx, (ft >> 2) & 0x7, ft & 0x1); + GEN_STORE_FTN_FREG(fd, WT2); + GEN_STORE_FTN_FREG(fd, WTH2); + opn = "movcf.ps"; + break; + case FOP(18, 22): + GEN_LOAD_REG_TN(T0, ft); + GEN_LOAD_FREG_FTN(WT0, fs); + GEN_LOAD_FREG_FTN(WTH0, fs); + GEN_LOAD_FREG_FTN(WT2, fd); + GEN_LOAD_FREG_FTN(WTH2, fd); + gen_op_float_movz_ps(); + GEN_STORE_FTN_FREG(fd, WT2); + GEN_STORE_FTN_FREG(fd, WTH2); + opn = "movz.ps"; + break; + case FOP(19, 22): + GEN_LOAD_REG_TN(T0, ft); + GEN_LOAD_FREG_FTN(WT0, fs); + GEN_LOAD_FREG_FTN(WTH0, fs); + GEN_LOAD_FREG_FTN(WT2, fd); + GEN_LOAD_FREG_FTN(WTH2, fd); + gen_op_float_movn_ps(); + GEN_STORE_FTN_FREG(fd, WT2); + GEN_STORE_FTN_FREG(fd, WTH2); + opn = "movn.ps"; + break; + case FOP(32, 22): + CHECK_FR(ctx, fs); + GEN_LOAD_FREG_FTN(WTH0, fs); + gen_op_float_cvts_pu(); + GEN_STORE_FTN_FREG(fd, WT2); + opn = "cvt.s.pu"; + break; + case FOP(36, 22): + CHECK_FR(ctx, fs | fd); + GEN_LOAD_FREG_FTN(WT0, fs); + GEN_LOAD_FREG_FTN(WTH0, fs); + gen_op_float_cvtpw_ps(); + GEN_STORE_FTN_FREG(fd, WT2); + GEN_STORE_FTN_FREG(fd, WTH2); + opn = "cvt.pw.ps"; + break; + case FOP(40, 22): + CHECK_FR(ctx, fs); + GEN_LOAD_FREG_FTN(WT0, fs); + gen_op_float_cvts_pl(); + GEN_STORE_FTN_FREG(fd, WT2); + opn = "cvt.s.pl"; + break; + case FOP(44, 22): + CHECK_FR(ctx, fs | ft | fd); GEN_LOAD_FREG_FTN(WT0, fs); GEN_LOAD_FREG_FTN(WT1, ft); - gen_cmp_s(func-48); + gen_op_float_pll_ps(); + GEN_STORE_FTN_FREG(fd, DT2); + opn = "pll.ps"; + break; + case FOP(45, 22): + CHECK_FR(ctx, fs | ft | fd); + GEN_LOAD_FREG_FTN(WT0, fs); + GEN_LOAD_FREG_FTN(WTH1, ft); + gen_op_float_plu_ps(); + GEN_STORE_FTN_FREG(fd, DT2); + opn = "plu.ps"; + break; + case FOP(46, 22): + CHECK_FR(ctx, fs | ft | fd); + GEN_LOAD_FREG_FTN(WTH0, fs); + GEN_LOAD_FREG_FTN(WT1, ft); + gen_op_float_pul_ps(); + GEN_STORE_FTN_FREG(fd, DT2); + opn = "pul.ps"; + break; + case FOP(47, 22): + CHECK_FR(ctx, fs | ft | fd); + GEN_LOAD_FREG_FTN(WTH0, fs); + GEN_LOAD_FREG_FTN(WTH1, ft); + gen_op_float_puu_ps(); + GEN_STORE_FTN_FREG(fd, DT2); + opn = "puu.ps"; + break; + case FOP(48, 22): + case FOP(49, 22): + case FOP(50, 22): + case FOP(51, 22): + case FOP(52, 22): + case FOP(53, 22): + case FOP(54, 22): + case FOP(55, 22): + case FOP(56, 22): + case FOP(57, 22): + case FOP(58, 22): + case FOP(59, 22): + case FOP(60, 22): + case FOP(61, 22): + case FOP(62, 22): + case FOP(63, 22): + CHECK_FR(ctx, fs | ft); + GEN_LOAD_FREG_FTN(WT0, fs); + GEN_LOAD_FREG_FTN(WTH0, fs); + GEN_LOAD_FREG_FTN(WT1, ft); + GEN_LOAD_FREG_FTN(WTH1, ft); + gen_cmp_ps(func-48, cc); opn = condnames[func-48]; break; - default: + default: if (loglevel & CPU_LOG_TB_IN_ASM) { fprintf(logfile, "Invalid FP arith function: %08x %03x %03x %03x\n", ctx->opcode, ctx->opcode >> 26, ctx->opcode & 0x3F, @@ -4622,18 +5054,134 @@ static void gen_farith (DisasContext *ctx, uint32_t op1, int ft, int fs, int fd) MIPS_DEBUG("%s %s,%s", opn, fregnames[fd], fregnames[fs]); } -static void gen_movci (DisasContext *ctx, int rd, int rs, int cc, int tf) +/* Coprocessor 3 (FPU) */ +static void gen_flt3_ldst (DisasContext *ctx, uint32_t opc, int fd, + int base, int index) { - uint32_t ccbit; + const char *opn = "unk"; - if (cc) - ccbit = 1 << (24 + cc); - else - ccbit = 1 << 23; - if (!tf) - gen_op_movf(ccbit, rd, rs); - else - gen_op_movt(ccbit, rd, rs); + GEN_LOAD_REG_TN(T0, base); + GEN_LOAD_REG_TN(T1, index); + /* Don't do NOP if destination is zero: we must perform the actual + * memory access + */ + switch (opc) { + case OPC_LWXC1: + op_ldst(lwxc1); + GEN_STORE_FTN_FREG(fd, WT0); + opn = "lwxc1"; + break; + case OPC_LDXC1: + op_ldst(ldxc1); + GEN_STORE_FTN_FREG(fd, DT0); + opn = "ldxc1"; + break; + case OPC_LUXC1: + op_ldst(luxc1); + GEN_STORE_FTN_FREG(fd, DT0); + opn = "luxc1"; + break; + case OPC_SWXC1: + GEN_LOAD_FREG_FTN(WT0, fd); + op_ldst(swxc1); + opn = "swxc1"; + break; + case OPC_SDXC1: + GEN_LOAD_FREG_FTN(DT0, fd); + op_ldst(sdxc1); + opn = "sdxc1"; + break; + case OPC_SUXC1: + GEN_LOAD_FREG_FTN(DT0, fd); + op_ldst(suxc1); + opn = "suxc1"; + break; + default: + MIPS_INVAL("extended float load/store"); + generate_exception(ctx, EXCP_RI); + return; + } + MIPS_DEBUG("%s %s, %s(%s)", opn, fregnames[fd],regnames[index], regnames[base]); +} + +static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, int fd, + int fr, int fs, int ft) +{ + const char *opn = "unk"; + + /* All of those work only on 64bit FPUs. */ + CHECK_FR(ctx, fd | fr | fs | ft); + switch (opc) { + case OPC_ALNV_PS: + GEN_LOAD_REG_TN(T0, fr); + GEN_LOAD_FREG_FTN(DT0, fs); + GEN_LOAD_FREG_FTN(DT1, ft); + gen_op_float_alnv_ps(); + GEN_STORE_FTN_FREG(fd, DT2); + opn = "alnv.ps"; + break; + case OPC_MADD_S: + GEN_LOAD_FREG_FTN(WT0, fs); + GEN_LOAD_FREG_FTN(WT1, ft); + GEN_LOAD_FREG_FTN(WT2, fr); + gen_op_float_muladd_s(); + GEN_STORE_FTN_FREG(fd, WT2); + opn = "madd.s"; + break; + case OPC_MADD_D: + generate_exception (ctx, EXCP_RI); + opn = "madd.d"; + break; + case OPC_MADD_PS: + generate_exception (ctx, EXCP_RI); + opn = "madd.ps"; + break; + case OPC_MSUB_S: + generate_exception (ctx, EXCP_RI); + opn = "msub.s"; + break; + case OPC_MSUB_D: + generate_exception (ctx, EXCP_RI); + opn = "msub.d"; + break; + case OPC_MSUB_PS: + generate_exception (ctx, EXCP_RI); + opn = "msub.ps"; + break; + case OPC_NMADD_S: + generate_exception (ctx, EXCP_RI); + opn = "nmadd.s"; + break; + case OPC_NMADD_D: + generate_exception (ctx, EXCP_RI); + opn = "nmadd.d"; + break; + case OPC_NMADD_PS: + generate_exception (ctx, EXCP_RI); + opn = "nmadd.ps"; + break; + case OPC_NMSUB_S: + generate_exception (ctx, EXCP_RI); + opn = "nmsub.s"; + break; + case OPC_NMSUB_D: + generate_exception (ctx, EXCP_RI); + opn = "nmsub.d"; + break; + case OPC_NMSUB_PS: + generate_exception (ctx, EXCP_RI); + opn = "nmsub.ps"; + break; + default: + if (loglevel & CPU_LOG_TB_IN_ASM) { + fprintf(logfile, "Invalid extended FP arith function: %08x %03x %03x\n", + ctx->opcode, ctx->opcode >> 26, ctx->opcode & 0x3F); + } + generate_exception (ctx, EXCP_RI); + return; + } + MIPS_DEBUG("%s %s, %s, %s, %s", opn, fregnames[fd], fregnames[fr], + fregnames[fs], fregnames[ft]); } /* ISA extensions (ASEs) */ @@ -4641,23 +5189,12 @@ static void gen_movci (DisasContext *ctx, int rd, int rs, int cc, int tf) /* SmartMIPS extension to MIPS32 */ #ifdef TARGET_MIPS64 -/* Coprocessor 3 (FPU) */ /* MDMX extension to MIPS64 */ /* MIPS-3D extension to MIPS64 */ #endif -static void gen_blikely(DisasContext *ctx) -{ - int l1; - l1 = gen_new_label(); - gen_op_jnz_T2(l1); - gen_op_save_state(ctx->hflags & ~MIPS_HFLAG_BMASK); - gen_goto_tb(ctx, 1, ctx->pc + 4); - gen_set_label(l1); -} - static void decode_opc (CPUState *env, DisasContext *ctx) { int32_t offset; @@ -4673,9 +5210,14 @@ static void decode_opc (CPUState *env, DisasContext *ctx) } if ((ctx->hflags & MIPS_HFLAG_BMASK) == MIPS_HFLAG_BL) { + int l1; /* Handle blikely not taken case */ MIPS_DEBUG("blikely condition (" TARGET_FMT_lx ")", ctx->pc + 4); - gen_blikely(ctx); + l1 = gen_new_label(); + gen_op_jnz_T2(l1); + gen_op_save_state(ctx->hflags & ~MIPS_HFLAG_BMASK); + gen_goto_tb(ctx, 1, ctx->pc + 4); + gen_set_label(l1); } op = MASK_OP_MAJOR(ctx->opcode); rs = (ctx->opcode >> 21) & 0x1f; @@ -5024,16 +5566,21 @@ static void decode_opc (CPUState *env, DisasContext *ctx) case OPC_DMFC1: case OPC_DMTC1: #endif + case OPC_MFHC1: + case OPC_MTHC1: gen_cp1(ctx, op1, rt, rd); break; case OPC_BC1: - gen_compute_branch1(ctx, MASK_CP1_BCOND(ctx->opcode), imm << 2); + gen_compute_branch1(ctx, MASK_BC1(ctx->opcode), + (rt >> 2) & 0x7, imm << 2); return; case OPC_S_FMT: case OPC_D_FMT: case OPC_W_FMT: case OPC_L_FMT: - gen_farith(ctx, MASK_CP1_FUNC(ctx->opcode), rt, rd, sa); + case OPC_PS_FMT: + gen_farith(ctx, MASK_CP1_FUNC(ctx->opcode), rt, rd, sa, + (imm >> 8) & 0x7); break; default: generate_exception (ctx, EXCP_RI); @@ -5060,10 +5607,32 @@ static void decode_opc (CPUState *env, DisasContext *ctx) gen_op_cp1_enabled(); op1 = MASK_CP3(ctx->opcode); switch (op1) { + case OPC_LWXC1: + case OPC_LDXC1: + case OPC_LUXC1: + case OPC_SWXC1: + case OPC_SDXC1: + case OPC_SUXC1: + gen_flt3_ldst(ctx, op1, sa, rs, rt); + break; case OPC_PREFX: /* treat as noop */ break; - /* Not implemented */ + case OPC_ALNV_PS: + case OPC_MADD_S: + case OPC_MADD_D: + case OPC_MADD_PS: + case OPC_MSUB_S: + case OPC_MSUB_D: + case OPC_MSUB_PS: + case OPC_NMADD_S: + case OPC_NMADD_D: + case OPC_NMADD_PS: + case OPC_NMSUB_S: + case OPC_NMSUB_D: + case OPC_NMSUB_PS: + gen_flt3_arith(ctx, op1, sa, rs, rd, rt); + break; default: generate_exception (ctx, EXCP_RI); break; @@ -5107,7 +5676,7 @@ static void decode_opc (CPUState *env, DisasContext *ctx) ctx->hflags &= ~MIPS_HFLAG_BMASK; ctx->bstate = BS_BRANCH; save_cpu_state(ctx, 0); - switch (hflags & MIPS_HFLAG_BMASK) { + switch (hflags) { case MIPS_HFLAG_B: /* unconditional branch */ MIPS_DEBUG("unconditional branch"); @@ -5134,6 +5703,8 @@ static void decode_opc (CPUState *env, DisasContext *ctx) /* unconditional branch to register */ MIPS_DEBUG("branch to register"); gen_op_breg(); + gen_op_reset_T0(); + gen_op_exit_tb(); break; default: MIPS_DEBUG("unknown branch"); @@ -5166,16 +5737,18 @@ gen_intermediate_code_internal (CPUState *env, TranslationBlock *tb, /* Restore delay slot state from the tb context. */ ctx.hflags = tb->flags; ctx.saved_hflags = ctx.hflags; - if (ctx.hflags & MIPS_HFLAG_BR) { + switch (ctx.hflags & MIPS_HFLAG_BMASK) { + case MIPS_HFLAG_BR: gen_op_restore_breg_target(); - } else if (ctx.hflags & MIPS_HFLAG_B) { + break; + case MIPS_HFLAG_B: ctx.btarget = env->btarget; - } else if (ctx.hflags & MIPS_HFLAG_BMASK) { - /* If we are in the delay slot of a conditional branch, - * restore the branch condition from env->bcond to T2 - */ + break; + case MIPS_HFLAG_BC: + case MIPS_HFLAG_BL: ctx.btarget = env->btarget; gen_op_restore_bcond(); + break; } #if defined(CONFIG_USER_ONLY) ctx.mem_idx = 0; @@ -5237,12 +5810,6 @@ gen_intermediate_code_internal (CPUState *env, TranslationBlock *tb, gen_op_debug(); } else { switch (ctx.bstate) { - case BS_EXCP: - gen_op_interrupt_restart(); - gen_op_reset_T0(); - /* Generate the return instruction. */ - gen_op_exit_tb(); - break; case BS_STOP: gen_op_interrupt_restart(); /* Fall through. */ @@ -5250,11 +5817,13 @@ gen_intermediate_code_internal (CPUState *env, TranslationBlock *tb, save_cpu_state(ctxp, 0); gen_goto_tb(&ctx, 0, ctx.pc); break; + case BS_EXCP: + gen_op_interrupt_restart(); + gen_op_reset_T0(); + gen_op_exit_tb(); + break; case BS_BRANCH: default: - gen_op_reset_T0(); - /* Generate the return instruction. */ - gen_op_exit_tb(); break; } } @@ -5307,21 +5876,33 @@ void fpu_dump_state(CPUState *env, FILE *f, int flags) { int i; + int is_fpu64 = !!(env->CP0_Status & (1 << CP0St_FR)); -# 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]); \ +#define printfpr(fp) \ + do { \ + if (is_fpu64) \ + fpu_fprintf(f, "w:%08x d:%016lx fd:%13g fs:%13g psu: %13g\n", \ + (fp)->w[FP_ENDIAN_IDX], (fp)->d, (fp)->fd, \ + (fp)->fs[FP_ENDIAN_IDX], (fp)->fs[!FP_ENDIAN_IDX]); \ + else { \ + fpr_t tmp; \ + tmp.w[FP_ENDIAN_IDX] = (fp)->w[FP_ENDIAN_IDX]; \ + tmp.w[!FP_ENDIAN_IDX] = ((fp) + 1)->w[FP_ENDIAN_IDX]; \ + fpu_fprintf(f, "w:%08x d:%016lx fd:%13g fs:%13g psu:%13g\n", \ + tmp.w[FP_ENDIAN_IDX], tmp.d, tmp.fd, \ + tmp.fs[FP_ENDIAN_IDX], tmp.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, "CP1 FCR0 0x%08x FCR31 0x%08x SR.FR %d fp_status 0x%08x(0x%02x)\n", + env->fcr0, env->fcr31, is_fpu64, env->fp_status, get_float_exception_flags(&env->fp_status)); 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, "%s: ", fregnames[i]); - printfpr(FPR(env, i)); + for (i = 0; i < 32; (is_fpu64) ? i++ : (i += 2)) { + fpu_fprintf(f, "%3s: ", fregnames[i]); + printfpr(&env->fpr[i]); } #undef printfpr diff --git a/target-mips/translate_init.c b/target-mips/translate_init.c index 51c571d0e6..a166bdd5e6 100644 --- a/target-mips/translate_init.c +++ b/target-mips/translate_init.c @@ -55,7 +55,7 @@ /* Define a implementation number of 1. Define a major version 1, minor version 0. */ -#define MIPS_FCR0 ((0 << 16) | (1 << 8) | (1 << 4) | 0) +#define MIPS_FCR0 ((0 << FCR0_S) | (0x1 << FCR0_PRID) | (0x10 << FCR0_REV)) struct mips_def_t { @@ -69,6 +69,7 @@ struct mips_def_t { int32_t CP0_Config7; int32_t SYNCI_Step; int32_t CCRes; + int32_t Status_rw_bitmask; int32_t CP1_fcr0; }; @@ -86,7 +87,7 @@ static mips_def_t mips_defs[] = .CP0_Config3 = MIPS_CONFIG3, .SYNCI_Step = 32, .CCRes = 2, - .CP1_fcr0 = MIPS_FCR0, + .Status_rw_bitmask = 0x3278FF17, }, { .name = "4KEcR1", @@ -97,7 +98,6 @@ static mips_def_t mips_defs[] = .CP0_Config3 = MIPS_CONFIG3, .SYNCI_Step = 32, .CCRes = 2, - .CP1_fcr0 = MIPS_FCR0, }, { .name = "4KEc", @@ -108,7 +108,7 @@ static mips_def_t mips_defs[] = .CP0_Config3 = MIPS_CONFIG3, .SYNCI_Step = 32, .CCRes = 2, - .CP1_fcr0 = MIPS_FCR0, + .Status_rw_bitmask = 0x3278FF17, }, { .name = "24Kc", @@ -119,7 +119,7 @@ static mips_def_t mips_defs[] = .CP0_Config3 = MIPS_CONFIG3, .SYNCI_Step = 32, .CCRes = 2, - .CP1_fcr0 = MIPS_FCR0, + .Status_rw_bitmask = 0x3278FF17, }, { .name = "24Kf", @@ -130,7 +130,9 @@ static mips_def_t mips_defs[] = .CP0_Config3 = MIPS_CONFIG3, .SYNCI_Step = 32, .CCRes = 2, - .CP1_fcr0 = MIPS_FCR0, + .Status_rw_bitmask = 0x3678FF17, + .CP1_fcr0 = (1 << FCR0_F64) | (1 << FCR0_L) | (1 << FCR0_W) | + (1 << FCR0_D) | (1 << FCR0_S) | (0x93 << FCR0_PRID), }, #else { @@ -142,7 +144,10 @@ static mips_def_t mips_defs[] = .CP0_Config3 = MIPS_CONFIG3, .SYNCI_Step = 16, .CCRes = 2, - .CP1_fcr0 = MIPS_FCR0, + .Status_rw_bitmask = 0x3678FFFF, + .CP1_fcr0 = (1 << FCR0_F64) | (1 << FCR0_L) | (1 << FCR0_W) | + (1 << FCR0_D) | (1 << FCR0_S) | + (0x4 << FCR0_PRID) | (0x0 << FCR0_REV), }, #endif }; @@ -191,6 +196,7 @@ int cpu_mips_register (CPUMIPSState *env, mips_def_t *def) env->CP0_Config7 = def->CP0_Config7; env->SYNCI_Step = def->SYNCI_Step; env->CCRes = def->CCRes; + env->Status_rw_bitmask = def->Status_rw_bitmask; env->fcr0 = def->CP1_fcr0; #if defined (MIPS_USES_R4K_TLB) env->nb_tlb = 1 + ((def->CP0_Config1 >> CP0C1_MMU) & 63);