mirror of
https://github.com/yuzu-emu/unicorn.git
synced 2025-01-23 17:11:09 +00:00
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. Backports commit 5204ea79ea739b557f47fc4db96c94edcb33a5d6 from qemu
This commit is contained in:
parent
59865351e0
commit
8743ec8b6d
|
@ -474,6 +474,7 @@ struct CPUMIPSState {
|
||||||
#define CP0C5_UFE 9
|
#define CP0C5_UFE 9
|
||||||
#define CP0C5_FRE 8
|
#define CP0C5_FRE 8
|
||||||
#define CP0C5_SBRI 6
|
#define CP0C5_SBRI 6
|
||||||
|
#define CP0C5_MVH 5
|
||||||
#define CP0C5_LLB 4
|
#define CP0C5_LLB 4
|
||||||
#define CP0C5_UFR 2
|
#define CP0C5_UFR 2
|
||||||
#define CP0C5_NFExists 0
|
#define CP0C5_NFExists 0
|
||||||
|
|
|
@ -865,8 +865,10 @@ enum {
|
||||||
enum {
|
enum {
|
||||||
OPC_MFC0 = (0x00 << 21) | OPC_CP0,
|
OPC_MFC0 = (0x00 << 21) | OPC_CP0,
|
||||||
OPC_DMFC0 = (0x01 << 21) | OPC_CP0,
|
OPC_DMFC0 = (0x01 << 21) | OPC_CP0,
|
||||||
|
OPC_MFHC0 = (0x02 << 21) | OPC_CP0,
|
||||||
OPC_MTC0 = (0x04 << 21) | OPC_CP0,
|
OPC_MTC0 = (0x04 << 21) | OPC_CP0,
|
||||||
OPC_DMTC0 = (0x05 << 21) | OPC_CP0,
|
OPC_DMTC0 = (0x05 << 21) | OPC_CP0,
|
||||||
|
OPC_MTHC0 = (0x06 << 21) | OPC_CP0,
|
||||||
OPC_MFTR = (0x08 << 21) | OPC_CP0,
|
OPC_MFTR = (0x08 << 21) | OPC_CP0,
|
||||||
OPC_RDPGPR = (0x0A << 21) | OPC_CP0,
|
OPC_RDPGPR = (0x0A << 21) | OPC_CP0,
|
||||||
OPC_MFMC0 = (0x0B << 21) | OPC_CP0,
|
OPC_MFMC0 = (0x0B << 21) | OPC_CP0,
|
||||||
|
@ -1407,6 +1409,9 @@ typedef struct DisasContext {
|
||||||
int ie;
|
int ie;
|
||||||
bool bi;
|
bool bi;
|
||||||
bool bp;
|
bool bp;
|
||||||
|
uint64_t PAMask;
|
||||||
|
bool mvh;
|
||||||
|
int CP0_LLAddr_shift;
|
||||||
// Unicorn engine
|
// Unicorn engine
|
||||||
struct uc_struct *uc;
|
struct uc_struct *uc;
|
||||||
} DisasContext;
|
} DisasContext;
|
||||||
|
@ -1823,6 +1828,15 @@ static inline void check_mips_64(DisasContext *ctx)
|
||||||
}
|
}
|
||||||
#endif
|
#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
|
/* 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
|
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
|
all callers for gen_load_fpr32 when we need the CTX parameter for
|
||||||
|
@ -4901,6 +4915,60 @@ static inline void gen_move_low32(TCGContext *s, TCGv ret, TCGv_i64 arg)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void gen_mthc0_entrylo(TCGContext *s, TCGv arg, target_ulong off)
|
||||||
|
{
|
||||||
|
TCGv_i64 t0 = tcg_temp_new_i64(s);
|
||||||
|
TCGv_i64 t1 = tcg_temp_new_i64(s);
|
||||||
|
|
||||||
|
tcg_gen_ext_tl_i64(s, t0, arg);
|
||||||
|
tcg_gen_ld_i64(s, t1, s->cpu_env, off);
|
||||||
|
#if defined(TARGET_MIPS64)
|
||||||
|
tcg_gen_deposit_i64(s, t1, t1, t0, 30, 32);
|
||||||
|
#else
|
||||||
|
tcg_gen_concat32_i64(s, t1, t1, t0);
|
||||||
|
#endif
|
||||||
|
tcg_gen_st_i64(s, t1, s->cpu_env, off);
|
||||||
|
tcg_temp_free_i64(s, t1);
|
||||||
|
tcg_temp_free_i64(s, t0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void gen_mthc0_store64(TCGContext *s, TCGv arg, target_ulong off)
|
||||||
|
{
|
||||||
|
TCGv_i64 t0 = tcg_temp_new_i64(s);
|
||||||
|
TCGv_i64 t1 = tcg_temp_new_i64(s);
|
||||||
|
|
||||||
|
tcg_gen_ext_tl_i64(s, t0, arg);
|
||||||
|
tcg_gen_ld_i64(s, t1, s->cpu_env, off);
|
||||||
|
tcg_gen_concat32_i64(s, t1, t1, t0);
|
||||||
|
tcg_gen_st_i64(s, t1, s->cpu_env, off);
|
||||||
|
tcg_temp_free_i64(s, t1);
|
||||||
|
tcg_temp_free_i64(s, t0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void gen_mfhc0_entrylo(TCGContext *s, TCGv arg, target_ulong off)
|
||||||
|
{
|
||||||
|
TCGv_i64 t0 = tcg_temp_new_i64(s);
|
||||||
|
|
||||||
|
tcg_gen_ld_i64(s, t0, s->cpu_env, off);
|
||||||
|
#if defined(TARGET_MIPS64)
|
||||||
|
tcg_gen_shri_i64(s, t0, t0, 30);
|
||||||
|
#else
|
||||||
|
tcg_gen_shri_i64(s, t0, t0, 32);
|
||||||
|
#endif
|
||||||
|
gen_move_low32(s, arg, t0);
|
||||||
|
tcg_temp_free_i64(s, t0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void gen_mfhc0_load64(TCGContext *s, TCGv arg, target_ulong off, int shift)
|
||||||
|
{
|
||||||
|
TCGv_i64 t0 = tcg_temp_new_i64(s);
|
||||||
|
|
||||||
|
tcg_gen_ld_i64(s, t0, s->cpu_env, off);
|
||||||
|
tcg_gen_shri_i64(s, t0, t0, 32 + shift);
|
||||||
|
gen_move_low32(s, arg, t0);
|
||||||
|
tcg_temp_free_i64(s, t0);
|
||||||
|
}
|
||||||
|
|
||||||
static inline void gen_mfc0_load32 (DisasContext *ctx, TCGv arg, target_ulong off)
|
static inline void gen_mfc0_load32 (DisasContext *ctx, TCGv arg, target_ulong off)
|
||||||
{
|
{
|
||||||
TCGContext *tcg_ctx = ctx->uc->tcg_ctx;
|
TCGContext *tcg_ctx = ctx->uc->tcg_ctx;
|
||||||
|
@ -4935,6 +5003,142 @@ static inline void gen_mtc0_store64 (DisasContext *ctx, TCGv arg, target_ulong o
|
||||||
tcg_gen_st_tl(tcg_ctx, arg, tcg_ctx->cpu_env, off);
|
tcg_gen_st_tl(tcg_ctx, arg, tcg_ctx->cpu_env, off);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void gen_mfhc0(DisasContext *ctx, TCGv arg, int reg, int sel)
|
||||||
|
{
|
||||||
|
TCGContext *s = ctx->uc->tcg_ctx;
|
||||||
|
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(s, arg, offsetof(CPUMIPSState, CP0_EntryLo0));
|
||||||
|
rn = "EntryLo0";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
goto mfhc0_read_zero;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
switch (sel) {
|
||||||
|
case 0:
|
||||||
|
gen_mfhc0_entrylo(s, arg, offsetof(CPUMIPSState, CP0_EntryLo1));
|
||||||
|
rn = "EntryLo1";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
goto mfhc0_read_zero;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 17:
|
||||||
|
switch (sel) {
|
||||||
|
case 0:
|
||||||
|
gen_mfhc0_load64(s, 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(s, 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(s, arg, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gen_mthc0(DisasContext *ctx, TCGv arg, int reg, int sel)
|
||||||
|
{
|
||||||
|
TCGContext *s = ctx->uc->tcg_ctx;
|
||||||
|
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(s, arg, arg, mask);
|
||||||
|
gen_mthc0_entrylo(s, arg, offsetof(CPUMIPSState, CP0_EntryLo0));
|
||||||
|
rn = "EntryLo0";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
goto mthc0_nop;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
switch (sel) {
|
||||||
|
case 0:
|
||||||
|
tcg_gen_andi_tl(s, arg, arg, mask);
|
||||||
|
gen_mthc0_entrylo(s, 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(s, arg, arg, mask);
|
||||||
|
gen_mthc0_store64(s, 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)
|
static inline void gen_mfc0_unimplemented(DisasContext *ctx, TCGv arg)
|
||||||
{
|
{
|
||||||
TCGContext *tcg_ctx = ctx->uc->tcg_ctx;
|
TCGContext *tcg_ctx = ctx->uc->tcg_ctx;
|
||||||
|
@ -7949,6 +8153,25 @@ static void gen_cp0 (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, int rt,
|
||||||
opn = "dmtc0";
|
opn = "dmtc0";
|
||||||
break;
|
break;
|
||||||
#endif
|
#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(tcg_ctx);
|
||||||
|
gen_load_gpr(ctx, t0, rt);
|
||||||
|
gen_mthc0(ctx, t0, rd, ctx->opcode & 0x7);
|
||||||
|
tcg_temp_free(tcg_ctx, t0);
|
||||||
|
}
|
||||||
|
opn = "mthc0";
|
||||||
|
break;
|
||||||
case OPC_MFTR:
|
case OPC_MFTR:
|
||||||
check_insn(ctx, ASE_MT);
|
check_insn(ctx, ASE_MT);
|
||||||
if (rd == 0) {
|
if (rd == 0) {
|
||||||
|
|
Loading…
Reference in a new issue