--- trunk/src/cpus/cpu_mips_coproc.c 2007/10/08 16:20:26 28 +++ trunk/src/cpus/cpu_mips_coproc.c 2007/10/08 16:22:11 40 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2006 Anders Gavare. All rights reserved. + * Copyright (C) 2003-2007 Anders Gavare. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -25,7 +25,7 @@ * SUCH DAMAGE. * * - * $Id: cpu_mips_coproc.c,v 1.49 2006/07/21 20:39:40 debug Exp $ + * $Id: cpu_mips_coproc.c,v 1.64 2007/04/28 09:19:51 debug Exp $ * * Emulation of MIPS coprocessors. */ @@ -45,6 +45,7 @@ #include "mips_cpu_types.h" #include "misc.h" #include "opcodes_mips.h" +#include "timer.h" #ifndef ENABLE_MIPS @@ -438,6 +439,23 @@ /* + * mips_timer_tick(): + */ +static void mips_timer_tick(struct timer *timer, void *extra) +{ + struct cpu *cpu = (struct cpu *) extra; + + cpu->cd.mips.compare_interrupts_pending ++; + + if ((int32_t) (cpu->cd.mips.coproc[0]->reg[COP0_COUNT] - + cpu->cd.mips.coproc[0]->reg[COP0_COMPARE]) < 0) { + cpu->cd.mips.coproc[0]->reg[COP0_COUNT] = + cpu->cd.mips.coproc[0]->reg[COP0_COMPARE]; + } +} + + +/* * mips_coproc_tlb_set_entry(): * * Used by machine setup code, if a specific machine emulation starts up @@ -517,10 +535,10 @@ * * Note: In the R3000 case, the asid argument is shifted 6 bits. */ -static void invalidate_asid(struct cpu *cpu, int asid) +static void invalidate_asid(struct cpu *cpu, unsigned int asid) { struct mips_coproc *cp = cpu->cd.mips.coproc[0]; - int i, ntlbs = cp->nr_of_tlbs; + unsigned int i, ntlbs = cp->nr_of_tlbs; struct mips_tlb *tlb = cp->tlbs; if (cpu->cd.mips.cpu_type.mmu_model == MMU3K) { @@ -536,7 +554,10 @@ int non4kpages = 0; uint64_t topbit = 1, fillmask = 0xffffff0000000000ULL; - if (cpu->cd.mips.cpu_type.mmu_model == MMU10K) { + if (cpu->is_32bit) { + topbit = 0x80000000; + fillmask = 0xffffffff00000000ULL; + } else if (cpu->cd.mips.cpu_type.mmu_model == MMU10K) { topbit <<= 43; fillmask <<= 4; } else { @@ -592,7 +613,11 @@ 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) { + /* TODO: Increase count in a more meaningful way! */ + cp->reg[COP0_COUNT] = (int32_t) (cp->reg[COP0_COUNT] + 1); + 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; @@ -658,7 +683,9 @@ int readonly = 0; uint64_t tmp = *ptr; uint64_t tmp2 = 0, old; - int inval = 0, old_asid, oldmode; + int inval = 0; + unsigned int old_asid; + uint64_t oldmode; switch (cp->coproc_nr) { case 0: @@ -750,13 +777,49 @@ unimpl = 0; break; case COP0_COMPARE: - /* Clear the timer interrupt bit (bit 7): */ - cpu->cd.mips.compare_register_set = 1; - mips_cpu_interrupt_ack(cpu, 7); + if (cpu->machine->emulated_hz > 0) { + int32_t compare_diff = tmp - + cp->reg[COP0_COMPARE]; + double hz; + + if (compare_diff < 0) + hz = tmp - cp->reg[COP0_COUNT]; + + if (compare_diff == 0) + hz = 0; + else + hz = (double)cpu->machine->emulated_hz + / (double)compare_diff; +/* + * TODO: DON'T HARDCODE THIS! + */ +hz = 100.0; + + /* Initialize or re-set the periodic timer: */ + if (hz > 0) { + if (cpu->cd.mips.timer == NULL) + cpu->cd.mips.timer = timer_add( + hz, mips_timer_tick, cpu); + else + timer_update_frequency( + cpu->cd.mips.timer, hz); + } + } + + /* Ack the periodic timer, if it was asserted: */ + if (cp->reg[COP0_CAUSE] & 0x8000 && + cpu->cd.mips.compare_interrupts_pending > 0) + cpu->cd.mips.compare_interrupts_pending --; + + /* Clear the timer interrupt assertion (bit 7): */ + cp->reg[COP0_CAUSE] &= ~0x8000; + if (tmp != (uint64_t)(int64_t)(int32_t)tmp) fatal("WARNING: trying to write a 64-bit value" " to the COMPARE register!\n"); + tmp = (int64_t)(int32_t)tmp; + cpu->cd.mips.compare_register_set = 1; unimpl = 0; break; case COP0_ENTRYHI: @@ -1201,7 +1264,7 @@ /* bc1f, bc1t, bc1fl, bc1tl: */ if ((function & 0x03e00000) == 0x01000000) { - int nd, tf, imm, cond_true; + int nd, tf, imm; char *instr_mnem; /* cc are bits 20..18: */ @@ -1224,36 +1287,10 @@ if (unassemble_only) return 1; - if (cpu->delay_slot) { - fatal("%s: jump inside a jump's delay slot, " - "or similar. TODO\n", instr_mnem); - cpu->running = 0; - 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; + fatal("INTERNAL ERROR: MIPS coprocessor branches should not" + " be implemented in cpu_mips_coproc.c, but in" + " cpu_mips_instr.c!\n"); + exit(1); } /* add.fmt: Floating-point add */ @@ -1467,19 +1504,13 @@ R2K3K_INDEX_SHIFT; if (i >= cp->nr_of_tlbs) { /* TODO: exception? */ - fatal("warning: tlbr from index %i (too " - "high)\n", i); + fatal("[ warning: tlbr from index %i (too " + "high) ]\n", i); return; } - /* - * TODO: Hm. Earlier I had an & ~0x3f on the high - * 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; */ + cp->reg[COP0_ENTRYHI] = cp->tlbs[i].hi; + cp->reg[COP0_ENTRYLO0] = cp->tlbs[i].lo0; } else { /* R4000: */ i = cp->reg[COP0_INDEX] & INDEX_MASK; @@ -1651,29 +1682,28 @@ (cp->reg[COP0_ENTRYHI] & R2K3K_ENTRYHI_ASID_MASK) )) cpu->invalidate_translation_caches(cpu, oldvaddr, INVALIDATE_VADDR); + break; default:if (cpu->cd.mips.cpu_type.mmu_model == MMU10K) { - oldvaddr = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK_R10K; + oldvaddr = cp->tlbs[index].hi & + (ENTRYHI_VPN2_MASK_R10K | ENTRYHI_R_MASK); /* 44 addressable bits: */ if (oldvaddr & 0x80000000000ULL) - oldvaddr |= 0xfffff00000000000ULL; + oldvaddr |= 0x3ffff00000000000ULL; } else if (cpu->is_32bit) { /* MIPS32 etc.: */ oldvaddr = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK; oldvaddr = (int32_t)oldvaddr; } else { /* Assume MMU4K */ - oldvaddr = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK; + oldvaddr = cp->tlbs[index].hi & + (ENTRYHI_R_MASK | ENTRYHI_VPN2_MASK); /* 40 addressable bits: */ if (oldvaddr & 0x8000000000ULL) - oldvaddr |= 0xffffff0000000000ULL; + oldvaddr |= 0x3fffff0000000000ULL; } -#if 0 - /* TODO: FIX THIS! It shouldn't be needed! */ - cpu->invalidate_translation_caches(cpu, 0, INVALIDATE_ALL); -#else /* * TODO: non-4KB page sizes! */ @@ -1683,7 +1713,6 @@ if (cp->tlbs[index].lo1 & ENTRYLO_V) cpu->invalidate_translation_caches(cpu, oldvaddr|0x1000, INVALIDATE_VADDR); -#endif } #if 0 @@ -1701,7 +1730,8 @@ int i; unsigned int asid; - vaddr1 = cp->reg[COP0_ENTRYHI] & ENTRYHI_VPN2_MASK_R10K; + vaddr1 = cp->reg[COP0_ENTRYHI] & + (ENTRYHI_R_MASK | 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. */ @@ -1714,7 +1744,8 @@ (cp->tlbs[i].hi & ENTRYHI_ASID) != asid) continue; - vaddr2 = cp->tlbs[i].hi & ENTRYHI_VPN2_MASK_R10K; + vaddr2 = cp->tlbs[i].hi & + (ENTRYHI_R_MASK | ENTRYHI_VPN2_MASK_R10K); if (vaddr1 == vaddr2 && ((cp->tlbs[i].lo0 & ENTRYLO_V) || (cp->tlbs[i].lo1 & ENTRYLO_V))) fatal("\n[ WARNING! tlbw%s to index 0x%02x " @@ -1748,6 +1779,9 @@ INVALIDATE_PADDR); } + /* Set new last_written_tlb_index hint: */ + cpu->cd.mips.last_written_tlb_index = index; + if (cp->reg[COP0_STATUS] & MIPS1_ISOL_CACHES) { fatal("Wow! Interesting case; tlbw* while caches" " are isolated. TODO\n"); @@ -1768,6 +1802,7 @@ int pfn_shift = 12, vpn_shift = 12; int wf0, wf1, mask; uint64_t vaddr0, vaddr1, paddr0, paddr1, ptmp; + uint64_t psize; cp->tlbs[index].mask = cp->reg[COP0_PAGEMASK]; cp->tlbs[index].hi = cp->reg[COP0_ENTRYHI]; @@ -1806,25 +1841,29 @@ } paddr0 = ((cp->tlbs[index].lo0 & ENTRYLO_PFN_MASK) - >> ENTRYLO_PFN_SHIFT) << pfn_shift; + >> ENTRYLO_PFN_SHIFT) << pfn_shift + >> vpn_shift << vpn_shift; paddr1 = ((cp->tlbs[index].lo1 & ENTRYLO_PFN_MASK) - >> ENTRYLO_PFN_SHIFT) << pfn_shift; + >> ENTRYLO_PFN_SHIFT) << pfn_shift + >> vpn_shift << vpn_shift; if (cpu->cd.mips.cpu_type.mmu_model == MMU10K) { - vaddr0 = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK_R10K; + vaddr0 = cp->tlbs[index].hi & + (ENTRYHI_R_MASK | ENTRYHI_VPN2_MASK_R10K); /* 44 addressable bits: */ if (vaddr0 & 0x80000000000ULL) - vaddr0 |= 0xfffff00000000000ULL; + vaddr0 |= 0x3ffff00000000000ULL; } else if (cpu->is_32bit) { /* MIPS32 etc.: */ vaddr0 = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK; vaddr0 = (int32_t)vaddr0; } else { /* Assume MMU4K */ - vaddr0 = cp->tlbs[index].hi & ENTRYHI_VPN2_MASK; + vaddr0 = cp->tlbs[index].hi & + (ENTRYHI_R_MASK | ENTRYHI_VPN2_MASK); /* 40 addressable bits: */ if (vaddr0 & 0x8000000000ULL) - vaddr0 |= 0xffffff0000000000ULL; + vaddr0 |= 0x3fffff0000000000ULL; } vaddr1 = vaddr0 | (1 << vpn_shift); @@ -1849,7 +1888,8 @@ * Invalidate any code translations, if we are writing Dirty * pages to the TLB: (TODO: 4KB hardcoded... ugly) */ - for (ptmp = 0; ptmp < (1 << pfn_shift); ptmp += 0x1000) { + psize = 1 << pfn_shift; + for (ptmp = 0; ptmp < psize; ptmp += 0x1000) { if (wf0) cpu->invalidate_code_translation(cpu, paddr0 + ptmp, INVALIDATE_PADDR); @@ -1876,6 +1916,9 @@ if (memblock != NULL && cp->reg[COP0_ENTRYLO1] & ENTRYLO_V) cpu->update_translation_table(cpu, vaddr1, memblock, wf1, paddr1); + + /* Set new last_written_tlb_index hint: */ + cpu->cd.mips.last_written_tlb_index = index; } } @@ -2098,108 +2141,73 @@ /* TLB operations and other things: */ if (cp->coproc_nr == 0) { + if (!unassemble_only) { + fatal("FATAL INTERNAL ERROR: Should be implemented" + " with dyntrans instead.\n"); + exit(1); + } + op = (function) & 0xff; switch (co_bit) { case 1: switch (op) { case COP0_TLBR: /* Read indexed TLB entry */ - if (unassemble_only) { - debug("tlbr\n"); - return; - } - coproc_tlbpr(cpu, 1); + debug("tlbr\n"); return; case COP0_TLBWI: /* Write indexed */ case COP0_TLBWR: /* Write random */ - if (unassemble_only) { - if (op == COP0_TLBWI) - debug("tlbwi"); - else - debug("tlbwr"); - if (!running) { - 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]); + if (op == COP0_TLBWI) + debug("tlbwi"); + else + debug("tlbwr"); + if (!running) { + debug("\n"); return; } - coproc_tlbwri(cpu, op == COP0_TLBWR); + 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]); return; case COP0_TLBP: /* Probe TLB for matching entry */ - if (unassemble_only) { - debug("tlbp\n"); - return; - } - coproc_tlbpr(cpu, 0); + debug("tlbp\n"); return; case COP0_RFE: /* R2000/R3000 only: Return from Exception */ - if (unassemble_only) { - debug("rfe\n"); - return; - } - fatal("Internal error (rfe): Should be " - "implemented in dyntrans instead.\n"); - exit(1); + debug("rfe\n"); + return; case COP0_ERET: /* R4000: Return from exception */ - if (unassemble_only) { - debug("eret\n"); - return; - } - fatal("Internal error (eret): Should be " - "implemented in dyntrans instead.\n"); - exit(1); + debug("eret\n"); + return; case COP0_DERET: - if (unassemble_only) { - debug("deret\n"); - return; + debug("deret\n"); + return; + case COP0_WAIT: + { + int code = (function >> 6) & 0x7ffff; + debug("wait"); + if (code > 0) + debug("\t0x%x", code); + debug("\n"); } - /* - * 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; return; case COP0_STANDBY: - if (unassemble_only) { - debug("standby\n"); - return; - } - /* TODO: Hm. Do something here? */ + debug("standby\n"); return; case COP0_SUSPEND: - if (unassemble_only) { - debug("suspend\n"); - return; - } - /* TODO: Hm. Do something here? */ + debug("suspend\n"); return; case COP0_HIBERNATE: - if (unassemble_only) { - debug("hibernate\n"); - return; - } - /* TODO: Hm. Do something here? */ + debug("hibernate\n"); return; default: ; @@ -2219,17 +2227,6 @@ return; } - /* 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; - } - if (unassemble_only) { debug("cop%i\t0x%08x (unimplemented)\n", cpnr, (int)function); return;