aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--disas/mips.c2
-rw-r--r--target-mips/cpu.h1
-rw-r--r--target-mips/translate.c226
3 files changed, 229 insertions, 0 deletions
diff --git a/disas/mips.c b/disas/mips.c
index 832468c0a7..32940feb95 100644
--- a/disas/mips.c
+++ b/disas/mips.c
@@ -2238,6 +2238,8 @@ const struct mips_opcode mips_builtin_opcodes[] =
{"ceil.l.s", "D,S", 0x4600000a, 0xffff003f, WR_D|RD_S|FP_S|FP_D, 0, I3|I33 },
{"ceil.w.d", "D,S", 0x4620000e, 0xffff003f, WR_D|RD_S|FP_S|FP_D, 0, I2 },
{"ceil.w.s", "D,S", 0x4600000e, 0xffff003f, WR_D|RD_S|FP_S, 0, I2 },
+{"mfhc0", "t,G,H", 0x40400000, 0xffe007f8, LCD|WR_t|RD_C0, 0, I33},
+{"mthc0", "t,G,H", 0x40c00000, 0xffe007f8, COD|RD_t|WR_C0|WR_CC, 0, I33},
{"cfc0", "t,G", 0x40400000, 0xffe007ff, LCD|WR_t|RD_C0, 0, I1 },
{"cfc1", "t,G", 0x44400000, 0xffe007ff, LCD|WR_t|RD_C1|FP_S, 0, I1 },
{"cfc1", "t,S", 0x44400000, 0xffe007ff, LCD|WR_t|RD_C1|FP_S, 0, I1 },
diff --git a/target-mips/cpu.h b/target-mips/cpu.h
index c266e9ff71..474a0e327d 100644
--- a/target-mips/cpu.h
+++ b/target-mips/cpu.h
@@ -473,6 +473,7 @@ struct CPUMIPSState {
#define CP0C5_UFE 9
#define CP0C5_FRE 8
#define CP0C5_SBRI 6
+#define CP0C5_MVH 5
#define CP0C5_LLB 4
#define CP0C5_UFR 2
#define CP0C5_NFExists 0
diff --git a/target-mips/translate.c b/target-mips/translate.c
index 6a39ef0e5a..1d128eef02 100644
--- a/target-mips/translate.c
+++ b/target-mips/translate.c
@@ -868,8 +868,10 @@ enum {
enum {
OPC_MFC0 = (0x00 << 21) | OPC_CP0,
OPC_DMFC0 = (0x01 << 21) | OPC_CP0,
+ OPC_MFHC0 = (0x02 << 21) | OPC_CP0,
OPC_MTC0 = (0x04 << 21) | OPC_CP0,
OPC_DMTC0 = (0x05 << 21) | OPC_CP0,
+ OPC_MTHC0 = (0x06 << 21) | OPC_CP0,
OPC_MFTR = (0x08 << 21) | OPC_CP0,
OPC_RDPGPR = (0x0A << 21) | OPC_CP0,
OPC_MFMC0 = (0x0B << 21) | OPC_CP0,
@@ -1424,6 +1426,9 @@ typedef struct DisasContext {
int ie;
bool bi;
bool bp;
+ uint64_t PAMask;
+ bool mvh;
+ int CP0_LLAddr_shift;
} DisasContext;
enum {
@@ -1821,6 +1826,15 @@ static inline void check_mips_64(DisasContext *ctx)
}
#endif
+#ifndef CONFIG_USER_ONLY
+static inline void check_mvh(DisasContext *ctx)
+{
+ if (unlikely(!ctx->mvh)) {
+ generate_exception(ctx, EXCP_RI);
+ }
+}
+#endif
+
/* Define small wrappers for gen_load_fpr* so that we have a uniform
calling interface for 32 and 64-bit FPRs. No sense in changing
all callers for gen_load_fpr32 when we need the CTX parameter for
@@ -4842,6 +4856,60 @@ static inline void gen_move_low32(TCGv ret, TCGv_i64 arg)
#endif
}
+static inline void gen_mthc0_entrylo(TCGv arg, target_ulong off)
+{
+ TCGv_i64 t0 = tcg_temp_new_i64();
+ TCGv_i64 t1 = tcg_temp_new_i64();
+
+ tcg_gen_ext_tl_i64(t0, arg);
+ tcg_gen_ld_i64(t1, cpu_env, off);
+#if defined(TARGET_MIPS64)
+ tcg_gen_deposit_i64(t1, t1, t0, 30, 32);
+#else
+ tcg_gen_concat32_i64(t1, t1, t0);
+#endif
+ tcg_gen_st_i64(t1, cpu_env, off);
+ tcg_temp_free_i64(t1);
+ tcg_temp_free_i64(t0);
+}
+
+static inline void gen_mthc0_store64(TCGv arg, target_ulong off)
+{
+ TCGv_i64 t0 = tcg_temp_new_i64();
+ TCGv_i64 t1 = tcg_temp_new_i64();
+
+ tcg_gen_ext_tl_i64(t0, arg);
+ tcg_gen_ld_i64(t1, cpu_env, off);
+ tcg_gen_concat32_i64(t1, t1, t0);
+ tcg_gen_st_i64(t1, cpu_env, off);
+ tcg_temp_free_i64(t1);
+ tcg_temp_free_i64(t0);
+}
+
+static inline void gen_mfhc0_entrylo(TCGv arg, target_ulong off)
+{
+ TCGv_i64 t0 = tcg_temp_new_i64();
+
+ tcg_gen_ld_i64(t0, cpu_env, off);
+#if defined(TARGET_MIPS64)
+ tcg_gen_shri_i64(t0, t0, 30);
+#else
+ tcg_gen_shri_i64(t0, t0, 32);
+#endif
+ gen_move_low32(arg, t0);
+ tcg_temp_free_i64(t0);
+}
+
+static inline void gen_mfhc0_load64(TCGv arg, target_ulong off, int shift)
+{
+ TCGv_i64 t0 = tcg_temp_new_i64();
+
+ tcg_gen_ld_i64(t0, cpu_env, off);
+ tcg_gen_shri_i64(t0, t0, 32 + shift);
+ gen_move_low32(arg, t0);
+ tcg_temp_free_i64(t0);
+}
+
static inline void gen_mfc0_load32 (TCGv arg, target_ulong off)
{
TCGv_i32 t0 = tcg_temp_new_i32();
@@ -4872,6 +4940,140 @@ static inline void gen_mtc0_store64 (TCGv arg, target_ulong off)
tcg_gen_st_tl(arg, cpu_env, off);
}
+static void gen_mfhc0(DisasContext *ctx, TCGv arg, int reg, int sel)
+{
+ const char *rn = "invalid";
+
+ if (!(ctx->hflags & MIPS_HFLAG_ELPA)) {
+ goto mfhc0_read_zero;
+ }
+
+ switch (reg) {
+ case 2:
+ switch (sel) {
+ case 0:
+ gen_mfhc0_entrylo(arg, offsetof(CPUMIPSState, CP0_EntryLo0));
+ rn = "EntryLo0";
+ break;
+ default:
+ goto mfhc0_read_zero;
+ }
+ break;
+ case 3:
+ switch (sel) {
+ case 0:
+ gen_mfhc0_entrylo(arg, offsetof(CPUMIPSState, CP0_EntryLo1));
+ rn = "EntryLo1";
+ break;
+ default:
+ goto mfhc0_read_zero;
+ }
+ break;
+ case 17:
+ switch (sel) {
+ case 0:
+ gen_mfhc0_load64(arg, offsetof(CPUMIPSState, lladdr),
+ ctx->CP0_LLAddr_shift);
+ rn = "LLAddr";
+ break;
+ default:
+ goto mfhc0_read_zero;
+ }
+ break;
+ case 28:
+ switch (sel) {
+ case 0:
+ case 2:
+ case 4:
+ case 6:
+ gen_mfhc0_load64(arg, offsetof(CPUMIPSState, CP0_TagLo), 0);
+ rn = "TagLo";
+ break;
+ default:
+ goto mfhc0_read_zero;
+ }
+ break;
+ default:
+ goto mfhc0_read_zero;
+ }
+
+ (void)rn; /* avoid a compiler warning */
+ LOG_DISAS("mfhc0 %s (reg %d sel %d)\n", rn, reg, sel);
+ return;
+
+mfhc0_read_zero:
+ LOG_DISAS("mfhc0 %s (reg %d sel %d)\n", rn, reg, sel);
+ tcg_gen_movi_tl(arg, 0);
+}
+
+static void gen_mthc0(DisasContext *ctx, TCGv arg, int reg, int sel)
+{
+ const char *rn = "invalid";
+ uint64_t mask = ctx->PAMask >> 36;
+
+ if (!(ctx->hflags & MIPS_HFLAG_ELPA)) {
+ goto mthc0_nop;
+ }
+
+ switch (reg) {
+ case 2:
+ switch (sel) {
+ case 0:
+ tcg_gen_andi_tl(arg, arg, mask);
+ gen_mthc0_entrylo(arg, offsetof(CPUMIPSState, CP0_EntryLo0));
+ rn = "EntryLo0";
+ break;
+ default:
+ goto mthc0_nop;
+ }
+ break;
+ case 3:
+ switch (sel) {
+ case 0:
+ tcg_gen_andi_tl(arg, arg, mask);
+ gen_mthc0_entrylo(arg, offsetof(CPUMIPSState, CP0_EntryLo1));
+ rn = "EntryLo1";
+ break;
+ default:
+ goto mthc0_nop;
+ }
+ break;
+ case 17:
+ switch (sel) {
+ case 0:
+ /* LLAddr is read-only (the only exception is bit 0 if LLB is
+ supported); the CP0_LLAddr_rw_bitmask does not seem to be
+ relevant for modern MIPS cores supporting MTHC0, therefore
+ treating MTHC0 to LLAddr as NOP. */
+ rn = "LLAddr";
+ break;
+ default:
+ goto mthc0_nop;
+ }
+ break;
+ case 28:
+ switch (sel) {
+ case 0:
+ case 2:
+ case 4:
+ case 6:
+ tcg_gen_andi_tl(arg, arg, mask);
+ gen_mthc0_store64(arg, offsetof(CPUMIPSState, CP0_TagLo));
+ rn = "TagLo";
+ break;
+ default:
+ goto mthc0_nop;
+ }
+ break;
+ default:
+ goto mthc0_nop;
+ }
+
+ (void)rn; /* avoid a compiler warning */
+mthc0_nop:
+ LOG_DISAS("mthc0 %s (reg %d sel %d)\n", rn, reg, sel);
+}
+
static inline void gen_mfc0_unimplemented(DisasContext *ctx, TCGv arg)
{
if (ctx->insn_flags & ISA_MIPS32R6) {
@@ -7880,6 +8082,25 @@ static void gen_cp0 (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, int rt,
opn = "dmtc0";
break;
#endif
+ case OPC_MFHC0:
+ check_mvh(ctx);
+ if (rt == 0) {
+ /* Treat as NOP. */
+ return;
+ }
+ gen_mfhc0(ctx, cpu_gpr[rt], rd, ctx->opcode & 0x7);
+ opn = "mfhc0";
+ break;
+ case OPC_MTHC0:
+ check_mvh(ctx);
+ {
+ TCGv t0 = tcg_temp_new();
+ gen_load_gpr(t0, rt);
+ gen_mthc0(ctx, t0, rd, ctx->opcode & 0x7);
+ tcg_temp_free(t0);
+ }
+ opn = "mthc0";
+ break;
case OPC_MFTR:
check_insn(ctx, ASE_MT);
if (rd == 0) {
@@ -18621,6 +18842,8 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx)
case OPC_MTC0:
case OPC_MFTR:
case OPC_MTTR:
+ case OPC_MFHC0:
+ case OPC_MTHC0:
#if defined(TARGET_MIPS64)
case OPC_DMFC0:
case OPC_DMTC0:
@@ -19191,6 +19414,9 @@ gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb,
ctx.ie = (env->CP0_Config4 >> CP0C4_IE) & 3;
ctx.bi = (env->CP0_Config3 >> CP0C3_BI) & 1;
ctx.bp = (env->CP0_Config3 >> CP0C3_BP) & 1;
+ ctx.PAMask = env->PAMask;
+ ctx.mvh = (env->CP0_Config5 >> CP0C5_MVH) & 1;
+ ctx.CP0_LLAddr_shift = env->CP0_LLAddr_shift;
/* Restore delay slot state from the tb context. */
ctx.hflags = (uint32_t)tb->flags; /* FIXME: maybe use 64 bits here? */
ctx.ulri = (env->CP0_Config3 >> CP0C3_ULRI) & 1;