target-mips: add MTHC0 and MFHC0 instructions

Implement MTHC0 and MFHC0 instructions. In MIPS32 they are used to access
upper word of extended to 64-bits CP0 registers.

In MIPS64, when CP0 destination register specified is the EntryLo0 or
EntryLo1, bits 1:0 of the GPR appear at bits 31:30 of EntryLo0 or
EntryLo1. This is to compensate for RI and XI, which were shifted to bits
63:62 by MTC0 to EntryLo0 or EntryLo1. Therefore creating separate
functions for EntryLo0 and EntryLo1.

Signed-off-by: Leon Alrae <leon.alrae@imgtec.com>
Reviewed-by: Aurelien Jarno <aurelien@aurel32.net>
This commit is contained in:
Leon Alrae 2014-09-11 16:28:17 +01:00
parent e117f52636
commit 5204ea79ea
3 changed files with 229 additions and 0 deletions

View File

@ -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 },

View File

@ -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

View File

@ -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;