--- trunk/src/cpu_mips_coproc.c 2007/10/08 16:17:52 3 +++ trunk/src/cpu_mips_coproc.c 2007/10/08 16:18:00 4 @@ -25,7 +25,7 @@ * SUCH DAMAGE. * * - * $Id: cpu_mips_coproc.c,v 1.11 2005/03/15 06:52:14 debug Exp $ + * $Id: cpu_mips_coproc.c,v 1.17 2005/04/22 16:03:43 debug Exp $ * * Emulation of MIPS coprocessors. */ @@ -684,8 +684,11 @@ for (a=0; a<0x400; a++) { tbl1 = cpu->cd.mips.vaddr_to_hostaddr_table0_kernel[a]; if (tbl1 != cpu->cd.mips.vaddr_to_hostaddr_nulltable) { - for (b=0; b<0x400; b++) + for (b=0; b<0x400; b++) { + tbl1->haddr_entry[b] = NULL; + tbl1->paddr_entry[b] = 0; tbl1->bintrans_chunks[b] = NULL; + } } } } @@ -949,7 +952,30 @@ if (cp->coproc_nr==0 && reg_nr==COP0_PAGEMASK) unimpl = 0; if (cp->coproc_nr==0 && reg_nr==COP0_WIRED) unimpl = 0; if (cp->coproc_nr==0 && reg_nr==COP0_BADVADDR) unimpl = 0; - if (cp->coproc_nr==0 && reg_nr==COP0_COUNT) unimpl = 0; + if (cp->coproc_nr==0 && reg_nr==COP0_COUNT) { + /* + * This speeds up delay-loops that just read the count + * register until it has reached a certain value. (Only for + * R4000 etc.) + * + * TODO: Maybe this should be optional? + */ + if (cpu->cd.mips.cpu_type.exc_model != EXC3K) { + int increase = 500; + int32_t x = cp->reg[COP0_COUNT]; + int32_t y = cp->reg[COP0_COMPARE]; + int32_t diff = x - y; + if (diff < 0 && diff + increase >= 0 + && cpu->cd.mips.compare_register_set) { + mips_cpu_interrupt(cpu, 7); + cpu->cd.mips.compare_register_set = 0; + } + cp->reg[COP0_COUNT] = (int64_t) + (int32_t)(cp->reg[COP0_COUNT] + increase); + } + + unimpl = 0; + } if (cp->coproc_nr==0 && reg_nr==COP0_ENTRYHI) unimpl = 0; if (cp->coproc_nr==0 && reg_nr==COP0_COMPARE) unimpl = 0; if (cp->coproc_nr==0 && reg_nr==COP0_STATUS) unimpl = 0; @@ -1172,6 +1198,9 @@ case COP0_STATUS: oldmode = cp->reg[COP0_STATUS]; tmp &= ~(1 << 21); /* bit 21 is read-only */ +#if 0 +/* Why was this here? It should not be necessary. */ + /* Changing from kernel to user mode? Then invalidate some translation caches: */ if (cpu->cd.mips.cpu_type.mmu_model == MMU3K) { @@ -1185,6 +1214,34 @@ invalidate_translation_caches( cpu, 0, 0, 1, 0); } +#endif + +#ifdef BINTRANS + if (cpu->cd.mips.cpu_type.mmu_model == MMU3K && + (oldmode & MIPS1_ISOL_CACHES) != + (tmp & MIPS1_ISOL_CACHES)) { + /* R3000-style caches when isolated are + treated in bintrans mode by changing + the vaddr_to_hostaddr_table0 pointer: */ + if (tmp & MIPS1_ISOL_CACHES) { + /* cpu->cd.mips. + dont_run_next_bintrans = 1; */ + cpu->cd.mips.vaddr_to_hostaddr_table0 = + tmp & MIPS1_SWAP_CACHES? + cpu->cd.mips. + vaddr_to_hostaddr_table0_cacheisol_i + : cpu->cd.mips. + vaddr_to_hostaddr_table0_cacheisol_d; + } else { + cpu->cd.mips.vaddr_to_hostaddr_table0 = + cpu->cd.mips. + vaddr_to_hostaddr_table0_kernel; + + /* TODO: cpu->cd.mips. + vaddr_to_hostaddr_table0_user; */ + } + } +#endif unimpl = 0; break; case COP0_CAUSE: @@ -2207,7 +2264,7 @@ /* TODO: Bug? Why does this if need to be commented out? */ - /* if (cp->tlbs[index].lo0 & ENTRYLO_V) */ + /* if (cp->tlbs[index].lo0 & ENTRYLO_V) */ invalidate_translation_caches(cpu, 0, oldvaddr, 0, 0); break; default: @@ -2236,6 +2293,42 @@ } + /* + * Check for duplicate entries. (There should not be two mappings + * from one virtual address to physical addresses.) + * + * TODO: Do this for MMU3K and R4100 too. + * + * TODO: Make this detection more robust. + */ + if (cpu->cd.mips.cpu_type.mmu_model != MMU3K && + cpu->cd.mips.cpu_type.rev != MIPS_R4100) { + uint64_t vaddr1, vaddr2; + int i, asid; + + vaddr1 = cp->reg[COP0_ENTRYHI] & ENTRYHI_VPN2_MASK_R10K; + asid = cp->reg[COP0_ENTRYHI] & ENTRYHI_ASID; + /* Since this is just a warning, it's probably not necessary + to use R4000 masks etc. */ + + for (i=0; inr_of_tlbs; i++) { + if (i == index && !randomflag) + continue; + + if (!(cp->tlbs[i].hi & TLB_G) && + (cp->tlbs[i].hi & ENTRYHI_ASID) != asid) + continue; + + vaddr2 = cp->tlbs[i].hi & ENTRYHI_VPN2_MASK_R10K; + if (vaddr1 == vaddr2 && ((cp->tlbs[i].lo0 & + ENTRYLO_V) || (cp->tlbs[i].lo1 & ENTRYLO_V))) + fatal("\n[ WARNING! tlbw%s vaddr=0x%llx is " + "already in the TLB! ]\n\n", randomflag? + "r" : "i", (long long)vaddr1); + } + } + + /* Write the new entry: */ if (cpu->cd.mips.cpu_type.mmu_model == MMU3K) { @@ -2369,10 +2462,12 @@ (cpu->cd.mips.coproc[0]->reg[COP0_STATUS] & 1) == 0) newmode = 1; +#if 0 /* Changing from kernel to user mode? Then this is necessary: TODO */ if (oldmode && !newmode) invalidate_translation_caches(cpu, 0, 0, 1, 0); +#endif }