mirror of
https://github.com/yuzu-emu/unicorn
synced 2024-11-25 06:18:56 +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
2 changed files with 224 additions and 0 deletions
|
@ -474,6 +474,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
|
||||
|
|
|
@ -865,8 +865,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,
|
||||
|
@ -1407,6 +1409,9 @@ typedef struct DisasContext {
|
|||
int ie;
|
||||
bool bi;
|
||||
bool bp;
|
||||
uint64_t PAMask;
|
||||
bool mvh;
|
||||
int CP0_LLAddr_shift;
|
||||
// Unicorn engine
|
||||
struct uc_struct *uc;
|
||||
} DisasContext;
|
||||
|
@ -1823,6 +1828,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
|
||||
|
@ -4901,6 +4915,60 @@ static inline void gen_move_low32(TCGContext *s, TCGv ret, TCGv_i64 arg)
|
|||
#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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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";
|
||||
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(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:
|
||||
check_insn(ctx, ASE_MT);
|
||||
if (rd == 0) {
|
||||
|
|
Loading…
Reference in a new issue