Dynamically translate MIPS mtc0 instructions.

git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2223 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
ths 2006-12-06 18:19:33 +00:00
parent 873eb01234
commit 8c0fdd856c
4 changed files with 423 additions and 220 deletions

View File

@ -68,7 +68,8 @@ void do_msubu (void);
#endif #endif
void do_mfc0_random(void); void do_mfc0_random(void);
void do_mfc0_count(void); void do_mfc0_count(void);
void do_mtc0(int reg, int sel); void do_mtc0_status_debug(uint32_t old, uint32_t val);
void do_mtc0_status_irqraise_debug(void);
void do_tlbwi (void); void do_tlbwi (void);
void do_tlbwr (void); void do_tlbwr (void);
void do_tlbp (void); void do_tlbp (void);

View File

@ -852,9 +852,185 @@ void op_mfc0_desave (void)
RETURN(); RETURN();
} }
void op_mtc0 (void) void op_mtc0_index (void)
{ {
CALL_FROM_TB2(do_mtc0, PARAM1, PARAM2); env->CP0_index = (env->CP0_index & 0x80000000) | (T0 & 0x0000000F);
RETURN();
}
void op_mtc0_entrylo0 (void)
{
env->CP0_EntryLo0 = T0 & 0x3FFFFFFF;
RETURN();
}
void op_mtc0_entrylo1 (void)
{
env->CP0_EntryLo1 = T0 & 0x3FFFFFFF;
RETURN();
}
void op_mtc0_context (void)
{
env->CP0_Context = (env->CP0_Context & 0xFF800000) | (T0 & 0x007FFFF0);
RETURN();
}
void op_mtc0_pagemask (void)
{
env->CP0_PageMask = T0 & 0x01FFE000;
RETURN();
}
void op_mtc0_wired (void)
{
env->CP0_Wired = T0 & 0x0000000F;
RETURN();
}
void op_mtc0_count (void)
{
CALL_FROM_TB2(cpu_mips_store_count, env, T0);
RETURN();
}
void op_mtc0_entryhi (void)
{
uint32_t old, val;
val = T0 & 0xFFFFE0FF;
old = env->CP0_EntryHi;
env->CP0_EntryHi = val;
/* If the ASID changes, flush qemu's TLB. */
if ((old & 0xFF) != (val & 0xFF))
CALL_FROM_TB2(cpu_mips_tlb_flush, env, 1);
RETURN();
}
void op_mtc0_compare (void)
{
CALL_FROM_TB2(cpu_mips_store_compare, env, T0);
RETURN();
}
void op_mtc0_status (void)
{
uint32_t val, old, mask;
val = T0 & 0xFA78FF01;
old = env->CP0_Status;
if (T0 & (1 << CP0St_UM))
env->hflags |= MIPS_HFLAG_UM;
else
env->hflags &= ~MIPS_HFLAG_UM;
if (T0 & (1 << CP0St_ERL))
env->hflags |= MIPS_HFLAG_ERL;
else
env->hflags &= ~MIPS_HFLAG_ERL;
if (T0 & (1 << CP0St_EXL))
env->hflags |= MIPS_HFLAG_EXL;
else
env->hflags &= ~MIPS_HFLAG_EXL;
env->CP0_Status = val;
/* If we unmasked an asserted IRQ, raise it */
mask = 0x0000FF00;
if (loglevel & CPU_LOG_TB_IN_ASM)
CALL_FROM_TB2(do_mtc0_status_debug, old, val);
if ((val & (1 << CP0St_IE)) && !(old & (1 << CP0St_IE)) &&
!(env->hflags & MIPS_HFLAG_EXL) &&
!(env->hflags & MIPS_HFLAG_ERL) &&
!(env->hflags & MIPS_HFLAG_DM) &&
(env->CP0_Status & env->CP0_Cause & mask)) {
env->interrupt_request |= CPU_INTERRUPT_HARD;
if (logfile)
CALL_FROM_TB0(do_mtc0_status_irqraise_debug);
} else if (!(val & (1 << CP0St_IE)) && (old & (1 << CP0St_IE))) {
env->interrupt_request &= ~CPU_INTERRUPT_HARD;
}
RETURN();
}
void op_mtc0_cause (void)
{
uint32_t val, old;
val = (env->CP0_Cause & 0xB000F87C) | (T0 & 0x000C00300);
old = env->CP0_Cause;
env->CP0_Cause = val;
#if 0
{
int i, mask;
/* Check if we ever asserted a software IRQ */
for (i = 0; i < 2; i++) {
mask = 0x100 << i;
if ((val & mask) & !(old & mask))
CALL_FROM_TB1(mips_set_irq, i);
}
}
#endif
RETURN();
}
void op_mtc0_epc (void)
{
env->CP0_EPC = T0;
RETURN();
}
void op_mtc0_config0 (void)
{
#if defined(MIPS_USES_R4K_TLB)
env->CP0_Config0 = (env->CP0_Config0 & 0x8017FF80) | (T0 & 0x7E000001);
#else
env->CP0_Config0 = (env->CP0_Config0 & 0xFE17FF80) | (T0 & 0x00000001);
#endif
RETURN();
}
void op_mtc0_watchlo (void)
{
env->CP0_WatchLo = T0;
RETURN();
}
void op_mtc0_watchhi (void)
{
env->CP0_WatchHi = T0 & 0x40FF0FF8;
RETURN();
}
void op_mtc0_debug (void)
{
env->CP0_Debug = (env->CP0_Debug & 0x8C03FC1F) | (T0 & 0x13300120);
if (T0 & (1 << CP0DB_DM))
env->hflags |= MIPS_HFLAG_DM;
else
env->hflags &= ~MIPS_HFLAG_DM;
RETURN();
}
void op_mtc0_depc (void)
{
env->CP0_DEPC = T0;
RETURN();
}
void op_mtc0_taglo (void)
{
env->CP0_TagLo = T0 & 0xFFFFFCF6;
RETURN();
}
void op_mtc0_errorepc (void)
{
env->CP0_ErrorEPC = T0;
RETURN();
}
void op_mtc0_desave (void)
{
env->CP0_DESAVE = T0;
RETURN(); RETURN();
} }

View File

@ -141,9 +141,24 @@ void do_mfc0_count (void)
cpu_abort(env, "mfc0 count\n"); cpu_abort(env, "mfc0 count\n");
} }
void do_mtc0 (int reg, int sel) void cpu_mips_store_count(CPUState *env, uint32_t value)
{ {
cpu_abort(env, "mtc0 reg=%d sel=%d\n", reg, sel); cpu_abort(env, "mtc0 count\n");
}
void cpu_mips_store_compare(CPUState *env, uint32_t value)
{
cpu_abort(env, "mtc0 compare\n");
}
void do_mtc0_status_debug(uint32_t old, uint32_t val)
{
cpu_abort(env, "mtc0 status\n");
}
void do_mtc0_status_irqraise_debug(void)
{
cpu_abort(env, "mtc0 status\n");
} }
void do_tlbwi (void) void do_tlbwi (void)
@ -166,6 +181,11 @@ void do_tlbr (void)
cpu_abort(env, "tlbr\n"); cpu_abort(env, "tlbr\n");
} }
void cpu_mips_tlb_flush (CPUState *env, int flush_global)
{
cpu_abort(env, "mips_tlb_flush\n");
}
#else #else
/* CP0 helpers */ /* CP0 helpers */
@ -179,222 +199,17 @@ void do_mfc0_count (void)
T0 = cpu_mips_get_count(env); T0 = cpu_mips_get_count(env);
} }
void do_mtc0 (int reg, int sel) void do_mtc0_status_debug(uint32_t old, uint32_t val)
{ {
const unsigned char *rn; const uint32_t mask = 0x0000FF00;
uint32_t val, old, mask; fprintf(logfile, "Status %08x => %08x Cause %08x (%08x %08x %08x)\n",
old, val, env->CP0_Cause, old & mask, val & mask,
env->CP0_Cause & mask);
}
if (sel != 0 && reg != 16 && reg != 28) { void do_mtc0_status_irqraise_debug(void)
val = -1; {
old = -1; fprintf(logfile, "Raise pending IRQs\n");
rn = "invalid";
goto print;
}
switch (reg) {
case 0:
val = (env->CP0_index & 0x80000000) | (T0 & 0x0000000F);
old = env->CP0_index;
env->CP0_index = val;
rn = "Index";
break;
case 2:
val = T0 & 0x3FFFFFFF;
old = env->CP0_EntryLo0;
env->CP0_EntryLo0 = val;
rn = "EntryLo0";
break;
case 3:
val = T0 & 0x3FFFFFFF;
old = env->CP0_EntryLo1;
env->CP0_EntryLo1 = val;
rn = "EntryLo1";
break;
case 4:
val = (env->CP0_Context & 0xFF800000) | (T0 & 0x007FFFF0);
old = env->CP0_Context;
env->CP0_Context = val;
rn = "Context";
break;
case 5:
val = T0 & 0x01FFE000;
old = env->CP0_PageMask;
env->CP0_PageMask = val;
rn = "PageMask";
break;
case 6:
val = T0 & 0x0000000F;
old = env->CP0_Wired;
env->CP0_Wired = val;
rn = "Wired";
break;
case 9:
val = T0;
old = cpu_mips_get_count(env);
cpu_mips_store_count(env, val);
rn = "Count";
break;
case 10:
val = T0 & 0xFFFFE0FF;
old = env->CP0_EntryHi;
env->CP0_EntryHi = val;
/* If the ASID changes, flush qemu's TLB. */
if ((old & 0xFF) != (val & 0xFF))
cpu_mips_tlb_flush (env, 1);
rn = "EntryHi";
break;
case 11:
val = T0;
old = env->CP0_Compare;
cpu_mips_store_compare(env, val);
rn = "Compare";
break;
case 12:
val = T0 & 0xFA78FF01;
if (T0 & (1 << CP0St_UM))
env->hflags |= MIPS_HFLAG_UM;
else
env->hflags &= ~MIPS_HFLAG_UM;
if (T0 & (1 << CP0St_ERL))
env->hflags |= MIPS_HFLAG_ERL;
else
env->hflags &= ~MIPS_HFLAG_ERL;
if (T0 & (1 << CP0St_EXL))
env->hflags |= MIPS_HFLAG_EXL;
else
env->hflags &= ~MIPS_HFLAG_EXL;
old = env->CP0_Status;
env->CP0_Status = val;
/* If we unmasked an asserted IRQ, raise it */
mask = 0x0000FF00;
if (loglevel & CPU_LOG_TB_IN_ASM) {
fprintf(logfile, "Status %08x => %08x Cause %08x (%08x %08x %08x)\n",
old, val, env->CP0_Cause, old & mask, val & mask,
env->CP0_Cause & mask);
}
if ((val & (1 << CP0St_IE)) && !(old & (1 << CP0St_IE)) &&
!(env->hflags & MIPS_HFLAG_EXL) &&
!(env->hflags & MIPS_HFLAG_ERL) &&
!(env->hflags & MIPS_HFLAG_DM) &&
(env->CP0_Status & env->CP0_Cause & mask)) {
if (logfile)
fprintf(logfile, "Raise pending IRQs\n");
env->interrupt_request |= CPU_INTERRUPT_HARD;
} else if (!(val & (1 << CP0St_IE)) && (old & (1 << CP0St_IE))) {
env->interrupt_request &= ~CPU_INTERRUPT_HARD;
}
rn = "Status";
break;
case 13:
val = (env->CP0_Cause & 0xB000F87C) | (T0 & 0x000C00300);
old = env->CP0_Cause;
env->CP0_Cause = val;
#if 0
{
int i;
/* Check if we ever asserted a software IRQ */
for (i = 0; i < 2; i++) {
mask = 0x100 << i;
if ((val & mask) & !(old & mask))
mips_set_irq(i);
}
}
#endif
rn = "Cause";
break;
case 14:
val = T0;
old = env->CP0_EPC;
env->CP0_EPC = val;
rn = "EPC";
break;
case 16:
switch (sel) {
case 0:
#if defined(MIPS_USES_R4K_TLB)
val = (env->CP0_Config0 & 0x8017FF80) | (T0 & 0x7E000001);
#else
val = (env->CP0_Config0 & 0xFE17FF80) | (T0 & 0x00000001);
#endif
old = env->CP0_Config0;
env->CP0_Config0 = val;
rn = "Config0";
break;
default:
val = -1;
old = -1;
rn = "bad config selector";
break;
}
break;
case 18:
val = T0;
old = env->CP0_WatchLo;
env->CP0_WatchLo = val;
rn = "WatchLo";
break;
case 19:
val = T0 & 0x40FF0FF8;
old = env->CP0_WatchHi;
env->CP0_WatchHi = val;
rn = "WatchHi";
break;
case 23:
val = (env->CP0_Debug & 0x8C03FC1F) | (T0 & 0x13300120);
if (T0 & (1 << CP0DB_DM))
env->hflags |= MIPS_HFLAG_DM;
else
env->hflags &= ~MIPS_HFLAG_DM;
old = env->CP0_Debug;
env->CP0_Debug = val;
rn = "Debug";
break;
case 24:
val = T0;
old = env->CP0_DEPC;
env->CP0_DEPC = val;
rn = "DEPC";
break;
case 28:
switch (sel) {
case 0:
val = T0 & 0xFFFFFCF6;
old = env->CP0_TagLo;
env->CP0_TagLo = val;
rn = "TagLo";
break;
default:
val = -1;
old = -1;
rn = "invalid sel";
break;
}
break;
case 30:
val = T0;
old = env->CP0_ErrorEPC;
env->CP0_ErrorEPC = val;
rn = "EPC";
break;
case 31:
val = T0;
old = env->CP0_DESAVE;
env->CP0_DESAVE = val;
rn = "DESAVE";
break;
default:
val = -1;
old = -1;
rn = "unknown";
break;
}
print:
#if defined MIPS_DEBUG_DISAS
if (loglevel & CPU_LOG_TB_IN_ASM) {
fprintf(logfile, "%08x mtc0 %s %08x => %08x (%d %d %08x)\n",
env->PC, rn, T0, val, reg, sel, old);
}
#endif
return;
} }
#ifdef MIPS_USES_FPU #ifdef MIPS_USES_FPU

View File

@ -1371,6 +1371,7 @@ static void gen_mfc0 (DisasContext *ctx, int reg, int sel)
rn = "EntryLo0"; rn = "EntryLo0";
break; break;
case 3: case 3:
/* also CONF */
gen_op_mfc0_entrylo1(); gen_op_mfc0_entrylo1();
rn = "EntryLo1"; rn = "EntryLo1";
break; break;
@ -1386,6 +1387,10 @@ static void gen_mfc0 (DisasContext *ctx, int reg, int sel)
gen_op_mfc0_wired(); gen_op_mfc0_wired();
rn = "Wired"; rn = "Wired";
break; break;
case 7:
// gen_op_mfc0_info();
rn = "Info";
break;
case 8: case 8:
gen_op_mfc0_badvaddr(); gen_op_mfc0_badvaddr();
rn = "BadVaddr"; rn = "BadVaddr";
@ -1445,6 +1450,19 @@ static void gen_mfc0 (DisasContext *ctx, int reg, int sel)
gen_op_mfc0_watchhi(); gen_op_mfc0_watchhi();
rn = "WatchHi"; rn = "WatchHi";
break; break;
case 20:
/* 64 bit only */
// gen_op_mfc0_xcontext();
rn = "XContext";
break;
case 21:
// gen_op_mfc0_framemask();
rn = "Framemask";
break;
case 22:
// gen_op_mfc0_diagnostic();
rn = "'Diagnostic";
break;
case 23: case 23:
gen_op_mfc0_debug(); gen_op_mfc0_debug();
rn = "Debug"; rn = "Debug";
@ -1453,6 +1471,18 @@ static void gen_mfc0 (DisasContext *ctx, int reg, int sel)
gen_op_mfc0_depc(); gen_op_mfc0_depc();
rn = "DEPC"; rn = "DEPC";
break; break;
case 25:
// gen_op_mfc0_performance();
rn = "Performance";
break;
case 26:
// gen_op_mfc0_ecc();
rn = "ECC";
break;
case 27:
// gen_op_mfc0_cacheerr();
rn = "CacheErr";
break;
case 28: case 28:
switch (sel) { switch (sel) {
case 0: case 0:
@ -1468,6 +1498,10 @@ static void gen_mfc0 (DisasContext *ctx, int reg, int sel)
goto die; goto die;
} }
break; break;
case 29:
// gen_op_mfc0_taghi();
rn = "TagHi";
break;
case 30: case 30:
gen_op_mfc0_errorepc(); gen_op_mfc0_errorepc();
rn = "ErrorEPC"; rn = "ErrorEPC";
@ -1498,6 +1532,183 @@ die:
generate_exception(ctx, EXCP_RI); generate_exception(ctx, EXCP_RI);
} }
static void gen_mtc0 (DisasContext *ctx, int reg, int sel)
{
const unsigned char *rn;
uint32_t val, old;
if (sel != 0 && reg != 16 && reg != 28) {
val = -1;
old = -1;
rn = "invalid";
goto die;
}
switch (reg) {
case 0:
gen_op_mtc0_index();
rn = "Index";
break;
case 1:
// ignore or except?
rn = "Random";
break;
case 2:
gen_op_mtc0_entrylo0();
rn = "EntryLo0";
break;
case 3:
gen_op_mtc0_entrylo1();
rn = "EntryLo1";
break;
case 4:
gen_op_mtc0_context();
rn = "Context";
break;
case 5:
gen_op_mtc0_pagemask();
rn = "PageMask";
break;
case 6:
gen_op_mtc0_wired();
rn = "Wired";
break;
case 7:
// ignore or except?
rn = "Info";
break;
case 8:
// ignore or except?
rn = "BadVaddr";
break;
case 9:
gen_op_mtc0_count();
rn = "Count";
break;
case 10:
gen_op_mtc0_entryhi();
rn = "EntryHi";
break;
case 11:
gen_op_mtc0_compare();
rn = "Compare";
break;
case 12:
gen_op_mtc0_status();
rn = "Status";
break;
case 13:
gen_op_mtc0_cause();
rn = "Cause";
break;
case 14:
gen_op_mtc0_epc();
rn = "EPC";
break;
case 15:
// ignore or except?
rn = "PRid";
break;
case 16:
switch (sel) {
case 0:
gen_op_mtc0_config0();
rn = "Config0";
break;
default:
rn = "Invalid config selector";
goto die;
}
break;
case 17:
// ignore or except?
rn = "LLaddr";
break;
case 18:
gen_op_mtc0_watchlo();
rn = "WatchLo";
break;
case 19:
gen_op_mtc0_watchhi();
rn = "WatchHi";
break;
case 20:
/* 64 bit only */
// gen_op_mtc0_xcontext();
rn = "XContext";
break;
case 21:
// gen_op_mtc0_framemask();
rn = "Framemask";
break;
case 22:
// ignore or except?
rn = "Diagnostic";
break;
case 23:
gen_op_mtc0_debug();
rn = "Debug";
break;
case 24:
gen_op_mtc0_depc();
rn = "DEPC";
break;
case 25:
// ignore or except?
rn = "Performance";
break;
case 26:
// ignore or except?
rn = "ECC";
break;
case 27:
// ignore or except?
rn = "CacheErr";
break;
case 28:
switch (sel) {
case 0:
gen_op_mtc0_taglo();
rn = "TagLo";
break;
default:
rn = "invalid sel";
goto die;
}
break;
case 29:
// gen_op_mtc0_taghi();
rn = "TagHi";
break;
case 30:
gen_op_mtc0_errorepc();
rn = "ErrorEPC";
break;
case 31:
gen_op_mtc0_desave();
rn = "DESAVE";
break;
default:
rn = "unknown";
goto die;
}
#if defined MIPS_DEBUG_DISAS
if (loglevel & CPU_LOG_TB_IN_ASM) {
fprintf(logfile, "%08x mtc0 %s => %08x (%d %d)\n",
env->PC, rn, T0, reg, sel);
}
#endif
return;
die:
#if defined MIPS_DEBUG_DISAS
if (loglevel & CPU_LOG_TB_IN_ASM) {
fprintf(logfile, "%08x mtc0 %s => %08x (%d %d)\n",
env->PC, rn, T0, reg, sel);
}
#endif
generate_exception(ctx, EXCP_RI);
}
static void gen_cp0 (DisasContext *ctx, uint16_t opc, int rt, int rd) static void gen_cp0 (DisasContext *ctx, uint16_t opc, int rt, int rd)
{ {
const unsigned char *opn = "unk"; const unsigned char *opn = "unk";
@ -1529,7 +1740,7 @@ static void gen_cp0 (DisasContext *ctx, uint16_t opc, int rt, int rd)
save_cpu_state(ctx, 1); save_cpu_state(ctx, 1);
ctx->pc -= 4; ctx->pc -= 4;
GEN_LOAD_REG_TN(T0, rt); GEN_LOAD_REG_TN(T0, rt);
gen_op_mtc0(rd, ctx->opcode & 0x7); gen_mtc0(ctx, rd, ctx->opcode & 0x7);
/* Stop translation as we may have switched the execution mode */ /* Stop translation as we may have switched the execution mode */
ctx->bstate = BS_STOP; ctx->bstate = BS_STOP;
opn = "mtc0"; opn = "mtc0";