mirror of https://github.com/xemu-project/xemu.git
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:
parent
e117f52636
commit
5204ea79ea
|
@ -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 },
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue