25 |
* SUCH DAMAGE. |
* SUCH DAMAGE. |
26 |
* |
* |
27 |
* |
* |
28 |
* $Id: cpu_mips_coproc.c,v 1.49 2006/07/21 20:39:40 debug Exp $ |
* $Id: cpu_mips_coproc.c,v 1.53 2006/08/11 17:43:30 debug Exp $ |
29 |
* |
* |
30 |
* Emulation of MIPS coprocessors. |
* Emulation of MIPS coprocessors. |
31 |
*/ |
*/ |
536 |
int non4kpages = 0; |
int non4kpages = 0; |
537 |
uint64_t topbit = 1, fillmask = 0xffffff0000000000ULL; |
uint64_t topbit = 1, fillmask = 0xffffff0000000000ULL; |
538 |
|
|
539 |
if (cpu->cd.mips.cpu_type.mmu_model == MMU10K) { |
if (cpu->is_32bit) { |
540 |
|
topbit = 0x80000000; |
541 |
|
fillmask = 0xffffffff00000000ULL; |
542 |
|
} else if (cpu->cd.mips.cpu_type.mmu_model == MMU10K) { |
543 |
topbit <<= 43; |
topbit <<= 43; |
544 |
fillmask <<= 4; |
fillmask <<= 4; |
545 |
} else { |
} else { |
1204 |
|
|
1205 |
/* bc1f, bc1t, bc1fl, bc1tl: */ |
/* bc1f, bc1t, bc1fl, bc1tl: */ |
1206 |
if ((function & 0x03e00000) == 0x01000000) { |
if ((function & 0x03e00000) == 0x01000000) { |
1207 |
int nd, tf, imm, cond_true; |
int nd, tf, imm; |
1208 |
char *instr_mnem; |
char *instr_mnem; |
1209 |
|
|
1210 |
/* cc are bits 20..18: */ |
/* cc are bits 20..18: */ |
1227 |
if (unassemble_only) |
if (unassemble_only) |
1228 |
return 1; |
return 1; |
1229 |
|
|
1230 |
if (cpu->delay_slot) { |
fatal("INTERNAL ERROR: MIPS coprocessor branches should not" |
1231 |
fatal("%s: jump inside a jump's delay slot, " |
" be implemented in cpu_mips_coproc.c, but in" |
1232 |
"or similar. TODO\n", instr_mnem); |
" cpu_mips_instr.c!\n"); |
1233 |
cpu->running = 0; |
exit(1); |
|
return 1; |
|
|
} |
|
|
|
|
|
/* Both the FCCR and FCSR contain condition code bits... */ |
|
|
if (cc == 0) |
|
|
cond_true = (cp->fcr[MIPS_FPU_FCSR] >> |
|
|
MIPS_FCSR_FCC0_SHIFT) & 1; |
|
|
else |
|
|
cond_true = (cp->fcr[MIPS_FPU_FCSR] >> |
|
|
(MIPS_FCSR_FCC1_SHIFT + cc-1)) & 1; |
|
|
|
|
|
if (!tf) |
|
|
cond_true = !cond_true; |
|
|
|
|
|
if (cond_true) { |
|
|
cpu->delay_slot = TO_BE_DELAYED; |
|
|
cpu->delay_jmpaddr = cpu->pc + (imm << 2); |
|
|
} else { |
|
|
/* "likely": */ |
|
|
if (nd) { |
|
|
/* nullify the delay slot */ |
|
|
cpu->cd.mips.nullify_next = 1; |
|
|
} |
|
|
} |
|
|
|
|
|
return 1; |
|
1234 |
} |
} |
1235 |
|
|
1236 |
/* add.fmt: Floating-point add */ |
/* add.fmt: Floating-point add */ |
1444 |
R2K3K_INDEX_SHIFT; |
R2K3K_INDEX_SHIFT; |
1445 |
if (i >= cp->nr_of_tlbs) { |
if (i >= cp->nr_of_tlbs) { |
1446 |
/* TODO: exception? */ |
/* TODO: exception? */ |
1447 |
fatal("warning: tlbr from index %i (too " |
fatal("[ warning: tlbr from index %i (too " |
1448 |
"high)\n", i); |
"high) ]\n", i); |
1449 |
return; |
return; |
1450 |
} |
} |
1451 |
|
|
1452 |
/* |
cp->reg[COP0_ENTRYHI] = cp->tlbs[i].hi; |
1453 |
* TODO: Hm. Earlier I had an & ~0x3f on the high |
cp->reg[COP0_ENTRYLO0] = cp->tlbs[i].lo0; |
|
* assignment and an & ~0xff on the lo0 assignment. |
|
|
* I wonder why. |
|
|
*/ |
|
|
|
|
|
cp->reg[COP0_ENTRYHI] = cp->tlbs[i].hi; /* & ~0x3f; */ |
|
|
cp->reg[COP0_ENTRYLO0] = cp->tlbs[i].lo0;/* & ~0xff; */ |
|
1454 |
} else { |
} else { |
1455 |
/* R4000: */ |
/* R4000: */ |
1456 |
i = cp->reg[COP0_INDEX] & INDEX_MASK; |
i = cp->reg[COP0_INDEX] & INDEX_MASK; |
2069 |
|
|
2070 |
/* TLB operations and other things: */ |
/* TLB operations and other things: */ |
2071 |
if (cp->coproc_nr == 0) { |
if (cp->coproc_nr == 0) { |
2072 |
|
if (!unassemble_only) { |
2073 |
|
fatal("FATAL INTERNAL ERROR: Should be implemented" |
2074 |
|
" with dyntrans instead.\n"); |
2075 |
|
exit(1); |
2076 |
|
} |
2077 |
|
|
2078 |
op = (function) & 0xff; |
op = (function) & 0xff; |
2079 |
switch (co_bit) { |
switch (co_bit) { |
2080 |
case 1: |
case 1: |
2081 |
switch (op) { |
switch (op) { |
2082 |
case COP0_TLBR: /* Read indexed TLB entry */ |
case COP0_TLBR: /* Read indexed TLB entry */ |
2083 |
if (unassemble_only) { |
debug("tlbr\n"); |
|
debug("tlbr\n"); |
|
|
return; |
|
|
} |
|
|
coproc_tlbpr(cpu, 1); |
|
2084 |
return; |
return; |
2085 |
case COP0_TLBWI: /* Write indexed */ |
case COP0_TLBWI: /* Write indexed */ |
2086 |
case COP0_TLBWR: /* Write random */ |
case COP0_TLBWR: /* Write random */ |
2087 |
if (unassemble_only) { |
if (op == COP0_TLBWI) |
2088 |
if (op == COP0_TLBWI) |
debug("tlbwi"); |
2089 |
debug("tlbwi"); |
else |
2090 |
else |
debug("tlbwr"); |
2091 |
debug("tlbwr"); |
if (!running) { |
2092 |
if (!running) { |
debug("\n"); |
|
debug("\n"); |
|
|
return; |
|
|
} |
|
|
debug("\tindex=%08llx", |
|
|
(long long)cp->reg[COP0_INDEX]); |
|
|
debug(", random=%08llx", |
|
|
(long long)cp->reg[COP0_RANDOM]); |
|
|
debug(", mask=%016llx", |
|
|
(long long)cp->reg[COP0_PAGEMASK]); |
|
|
debug(", hi=%016llx", |
|
|
(long long)cp->reg[COP0_ENTRYHI]); |
|
|
debug(", lo0=%016llx", |
|
|
(long long)cp->reg[COP0_ENTRYLO0]); |
|
|
debug(", lo1=%016llx\n", |
|
|
(long long)cp->reg[COP0_ENTRYLO1]); |
|
2093 |
return; |
return; |
2094 |
} |
} |
2095 |
coproc_tlbwri(cpu, op == COP0_TLBWR); |
debug("\tindex=%08llx", |
2096 |
|
(long long)cp->reg[COP0_INDEX]); |
2097 |
|
debug(", random=%08llx", |
2098 |
|
(long long)cp->reg[COP0_RANDOM]); |
2099 |
|
debug(", mask=%016llx", |
2100 |
|
(long long)cp->reg[COP0_PAGEMASK]); |
2101 |
|
debug(", hi=%016llx", |
2102 |
|
(long long)cp->reg[COP0_ENTRYHI]); |
2103 |
|
debug(", lo0=%016llx", |
2104 |
|
(long long)cp->reg[COP0_ENTRYLO0]); |
2105 |
|
debug(", lo1=%016llx\n", |
2106 |
|
(long long)cp->reg[COP0_ENTRYLO1]); |
2107 |
return; |
return; |
2108 |
case COP0_TLBP: /* Probe TLB for |
case COP0_TLBP: /* Probe TLB for |
2109 |
matching entry */ |
matching entry */ |
2110 |
if (unassemble_only) { |
debug("tlbp\n"); |
|
debug("tlbp\n"); |
|
|
return; |
|
|
} |
|
|
coproc_tlbpr(cpu, 0); |
|
2111 |
return; |
return; |
2112 |
case COP0_RFE: /* R2000/R3000 only: |
case COP0_RFE: /* R2000/R3000 only: |
2113 |
Return from Exception */ |
Return from Exception */ |
2114 |
if (unassemble_only) { |
debug("rfe\n"); |
2115 |
debug("rfe\n"); |
return; |
|
return; |
|
|
} |
|
|
fatal("Internal error (rfe): Should be " |
|
|
"implemented in dyntrans instead.\n"); |
|
|
exit(1); |
|
2116 |
case COP0_ERET: /* R4000: Return from exception */ |
case COP0_ERET: /* R4000: Return from exception */ |
2117 |
if (unassemble_only) { |
debug("eret\n"); |
2118 |
debug("eret\n"); |
return; |
|
return; |
|
|
} |
|
|
fatal("Internal error (eret): Should be " |
|
|
"implemented in dyntrans instead.\n"); |
|
|
exit(1); |
|
2119 |
case COP0_DERET: |
case COP0_DERET: |
2120 |
if (unassemble_only) { |
debug("deret\n"); |
2121 |
debug("deret\n"); |
return; |
2122 |
return; |
case COP0_WAIT: |
2123 |
|
{ |
2124 |
|
int code = (function >> 6) & 0x7ffff; |
2125 |
|
debug("wait"); |
2126 |
|
if (code > 0) |
2127 |
|
debug("\t0x%x", code); |
2128 |
|
debug("\n"); |
2129 |
} |
} |
|
/* |
|
|
* According to the MIPS64 manual, deret |
|
|
* loads PC from the DEPC cop0 register, and |
|
|
* jumps there immediately. No delay slot. |
|
|
* |
|
|
* TODO: This instruction is only available |
|
|
* if the processor is in debug mode. (What |
|
|
* does that mean?) TODO: This instruction |
|
|
* is undefined in a delay slot. |
|
|
*/ |
|
|
cpu->pc = cp->reg[COP0_DEPC]; |
|
|
cpu->delay_slot = 0; |
|
|
cp->reg[COP0_STATUS] &= ~STATUS_EXL; |
|
2130 |
return; |
return; |
2131 |
case COP0_STANDBY: |
case COP0_STANDBY: |
2132 |
if (unassemble_only) { |
debug("standby\n"); |
|
debug("standby\n"); |
|
|
return; |
|
|
} |
|
|
/* TODO: Hm. Do something here? */ |
|
2133 |
return; |
return; |
2134 |
case COP0_SUSPEND: |
case COP0_SUSPEND: |
2135 |
if (unassemble_only) { |
debug("suspend\n"); |
|
debug("suspend\n"); |
|
|
return; |
|
|
} |
|
|
/* TODO: Hm. Do something here? */ |
|
2136 |
return; |
return; |
2137 |
case COP0_HIBERNATE: |
case COP0_HIBERNATE: |
2138 |
if (unassemble_only) { |
debug("hibernate\n"); |
|
debug("hibernate\n"); |
|
|
return; |
|
|
} |
|
|
/* TODO: Hm. Do something here? */ |
|
2139 |
return; |
return; |
2140 |
default: |
default: |
2141 |
; |
; |
2155 |
return; |
return; |
2156 |
} |
} |
2157 |
|
|
|
/* TODO: RM5200 idle (?) */ |
|
|
if ((cp->coproc_nr==0 || cp->coproc_nr==3) && function == 0x02000020) { |
|
|
if (unassemble_only) { |
|
|
debug("idle(?)\n"); /* TODO */ |
|
|
return; |
|
|
} |
|
|
|
|
|
/* Idle? TODO */ |
|
|
return; |
|
|
} |
|
|
|
|
2158 |
if (unassemble_only) { |
if (unassemble_only) { |
2159 |
debug("cop%i\t0x%08x (unimplemented)\n", cpnr, (int)function); |
debug("cop%i\t0x%08x (unimplemented)\n", cpnr, (int)function); |
2160 |
return; |
return; |