📄 r4k_cp0.c
字号:
/***************************************************************** * WriteC0Register * * All writes to coprocessor come here for handling. This allows * us to do whatever we want with read-only bits, etc. *****************************************************************/static voidWriteC0Register(CPUState *P, int c0RegNum, Reg value, int is64bit){ if (cp0RegCtl[c0RegNum].read_only) { return; } if (cp0RegCtl[c0RegNum].size == -1) { CPUWarning("WriteC0Reg write to invalid reg %d\n", c0RegNum); } if ((cp0RegCtl[c0RegNum].zero_mask & value) != 0) { /* Trying to write must be zero bits, force them to zero. */ value &= ~cp0RegCtl[c0RegNum].zero_mask; /* Print a warning message for the programmer. Special * case 32bit signed-extention bits that set high zero bits * of 64 bit registers (IRIX 6.2 does this). */ if (is64bit || (cp0RegCtl[c0RegNum].size != 1) || ((cp0RegCtl[c0RegNum].zero_mask & value & 0xffffffff) != 0)) { #if defined(TORNADO) || defined(IRIX6_4) /* for now, tornado sometimes writes funny values for tlb stuff */ /* So does IRIX 6.4 that that matter */ int warning = (c0RegNum != C0_TLBLO_0) && (c0RegNum != C0_TLBLO_1);#else int warning = 1;#endif if (warning) { CPUWarning("WriteC0Reg zero bits set during write to %d @%#lx RA %#lx, clearing\n", (int)c0RegNum, (long)P->PC, (long)P->R[31]); /* got rid of the evil %llx -- PZ */ } } } if (is64bit && (cp0RegCtl[c0RegNum].size == 0)) {#ifdef IRIX6_4 if (c0RegNum != C0_SR) #endif CPUWarning("WriteC0Reg 64bit write to 32bit reg %d\n", c0RegNum); } if (c0RegNum == C0_CAUSE){ P->CP0[C0_CAUSE] &= ~(CAUSE_SW2|CAUSE_SW1); P->CP0[C0_CAUSE] |= (value & (CAUSE_SW2|CAUSE_SW1)); } else { P->CP0[c0RegNum] = value; } if (c0RegNum == C0_COMPARE) { CompareWritten(P); } if (c0RegNum == C0_COUNT) { CountWritten(P); } if (c0RegNum == C0_TLBHI) { /* Clear the fill bits */ P->CP0[c0RegNum] &= TLBHI_FILLMASK; /* Might be changing the ASID */ P->pcVPNcache = 0; TraceCheckASID(P); } if (c0RegNum == C0_SR){ StatusReg statusReg; statusReg.ts_data = P->CP0[C0_SR];#if defined(SIM_MIPS32) if ((statusReg.s32.ts_ux) || (statusReg.s32.ts_sx) || (statusReg.s32.ts_kx)) { static int warned_64 = 0; if (!warned_64) { CPUWarning("MIPSY: 64-bit mode not implemented, ignoring SR write\n"); warned_64 = 1; } statusReg.s32.ts_ux = 0; statusReg.s32.ts_sx = 0; statusReg.s32.ts_kx = 0; P->CP0[C0_SR] = statusReg.ts_data; }#endif /* Don't allow reverse endian either. */ if (statusReg.s32.ts_re) { static int warned_re = 0; if (!warned_re) { CPUWarning("MIPSY: reverse-endian mode not implemented, ignore SR write\n"); warned_re = 1; } statusReg.s32.ts_re = 0; } UpdateCPUMode(P); MipsyCheckForInterrupts(P); } if (c0RegNum == C0_CAUSE){ MipsyCheckForInterrupts(P); }} /***************************************************************** * R4000 Timer support *****************************************************************/ static EventCallbackHdr timerCallbackHdr[MIPSY_MAX_CPUS];static void MipsySetTimerCallback(int cpuNum);/***************************************************************** * Things that need doing when the compare register is written. * Right now just timer related actions. *****************************************************************/static voidCompareWritten(CPUState *P){ int cpuNum = P->myNum; /* make sure upper bits are zero */ P->CP0[C0_COMPARE] &= 0xffffffff; PE[cpuNum].CP0[C0_CAUSE] &= ~CAUSE_IP8; if (EventCallbackActive(&(timerCallbackHdr[cpuNum]))) { EventCallbackRemove(&(timerCallbackHdr[cpuNum])); } MipsySetTimerCallback(cpuNum); }/***************************************************************** * Things that need doing when the count register is written. * Right now just timer related actions. *****************************************************************/static voidCountWritten(CPUState *P) { int cpuNum = P->myNum; /* make sure upper bits are zero */ P->CP0[C0_COUNT] &= 0xffffffff; P->timerCycleCount = MipsyReadTime(P->myNum); if (EventCallbackActive(&(timerCallbackHdr[cpuNum]))) { EventCallbackRemove(&(timerCallbackHdr[cpuNum])); } MipsySetTimerCallback(cpuNum);}/**************************************************************** * TMipsyimerCallback * * This is the routine called back on the event that * the C0_COMPARE == C0_COUNT, it is set when the * COMPARE register is written. Raise IP(7) in the * C0_CAUSE register to signal a timer interrupt is * pending. Note: IP(7) is cleared when when the * C0_COMPARE register is written. use DEV_IEC_MAGICERR * as it corresponds to hw interrupt bit #5 which raises * ip7 in the cause register. ****************************************************************/static void MipsyTimerCallback(int cpuNum, EventCallbackHdr *ECBhdr, void *empty) { if (simosCPUType != MIPSY) { return; } PE[cpuNum].CP0[C0_CAUSE] |= CAUSE_IP8; MipsyCheckForInterrupts(&PE[cpuNum]);}/***************************************************************** * MipsySetTimerCallback * * Each processor has its own r4kTimerInfo and r4k_timerHdr * associated with it. When the Compare register is written to * we check current cycle count, and, projecting into the future the * diff btwn the new Compare reg value and the current cycle count, a * callback is set to fire in that calculate amt of time. It is tricky * only when they decide to write to the count reg, which is permited on * system initialization or to synchronize processors. Since we do not * update the Count register continually, our cycle count and the Count * register value could then be out-of-synch. The r4kTimerInfo variable * is used to track this discrepency. ****************************************************************/static void MipsySetTimerCallback(int cpuNum){ CPUState *P = &PE[cpuNum]; SimTime timeInFuture; P->CP0[C0_COUNT] += (MipsyReadTime(cpuNum) - P->timerCycleCount)/ COUNTER_FREQUENCY_DIVIDER; P->timerCycleCount = MipsyReadTime(cpuNum); P->CP0[C0_COUNT] &= 0xffffffff; if (P->CP0[C0_COMPARE] >= P->CP0[C0_COUNT]) { timeInFuture = (P->CP0[C0_COMPARE] - P->CP0[C0_COUNT]); timeInFuture *= COUNTER_FREQUENCY_DIVIDER; } else { timeInFuture = 0x100000000LL - (P->CP0[C0_COUNT] - P->CP0[C0_COMPARE]); timeInFuture *= COUNTER_FREQUENCY_DIVIDER; } EventDoCallback(cpuNum, MipsyTimerCallback, &(timerCallbackHdr[cpuNum]), NULL, timeInFuture);}/***************************************************************** * CHECKING OF INTERRUPTS *****************************************************************//***************************************************************** * CheckForInterrupts callback * This callback is in charge of polling for interrupts and checking * if mipsy should be exited. You can exit by running the set number * of cycles or by attaching with a debugger and setting "mipsyExit" * to true. *****************************************************************/static struct CheckForInterruptsCallbackHdr { EventCallbackHdr hdr; SimTime interval;} checkForInterruptsEvent[SIM_MAXCPUS];extern bool exitMipsy;static voidCheckForInterruptsCallback(int cpuNum, EventCallbackHdr *hdr, void *empty){ int cpu; if (simosCPUType != MIPSY) { return; } for (cpu = 0; cpu < TOTAL_CPUS; cpu++) { MipsyCheckForInterrupts(&PE[cpu]); } EventDoCallback(cpuNum, CheckForInterruptsCallback, hdr, 0, checkForInterruptsEvent[cpuNum].interval); /* Another way of leaving mipsy is to attach to it with the */ /* debugger and set the exitMipsy flag to TRUE */ if (exitMipsy) { CPUPrint("exitMipsy set to true (by debugger)\n"); MipsyExit(BASE); }}void EnableInterruptCheck(CPUState *P, int interval){ checkForInterruptsEvent[P->myNum].interval = interval; EventDoCallback(P->myNum, CheckForInterruptsCallback, &checkForInterruptsEvent[P->myNum].hdr, 0, interval);}#ifdef MIPSY_MXS/***************************************************************** * DoPrivInst - Perform a priviledged instruction for MXS. * MXS passes us the instr and any source register * values that come from the GP register set. We return * to MXS any destination registers. *****************************************************************/ boolDoPrivInst(struct s_cpu_state *st, uint instr, uint srcreg, uint *dstreg, bool *hack){ CPUState *P = (CPUState *) (st->mipsyPtr); uint rs = RS(instr); uint rd = RD(instr); uint op = MAJOR_OP(instr); (*hack) = FALSE; if (!IS_KERNEL_MODE(P)) { /* CP0 is unusable */ CPUWarning("Using CP0 inst in user mode!\n"); RECORD_EXCEPTION(P, EXC_CPU, E_VEC, 0, P->CP0[C0_TLBHI], P->CP0[C0_CTXT],P->CP0[C0_XCTXT]); return TRUE; } if (op == cop0_op) { /* First handle the rs instructions */ switch(rs) { case bc_op: CPUError("BCC0 is not valid\n"); break; case mfc_op: (*dstreg) = P->CP0[rd]; break; case mtc_op: /*BAN: BEGIN*/ if (rs == dmtc_op) { WriteC0Register(P, rd, srcreg, 1); } else { WriteC0Register(P, rd, (Reg32_s)srcreg, 0); } /*BAN: END*/ break; default: /* It wasn't any of these, so it's a CO function */ CopyFromMXS(P); switch(FUNC(instr)) { case eret_op: P->LLbit = 0;#ifdef BANREMOVE if (P->numInstructions >= P->nextInstrSample) { INST_SAMPLE_EVENT(MipsyReadTime(P->myNum), P->myNum, P->PC); P->nextInstrSample += MS_SAMPLE_INSTR_INTERVAL; }#endif /*BAN: BEGIN*/ ExceptionReturn(P); /*BAN: END*/ (*hack) = TRUE; break; case rfe_op: CPUError("MIPSY: Hit an RFE on an R4000\n"); break; case tlbp_op: ProbeTLB(P); break; case tlbr_op: ReadTLBEntry(P); break; case tlbwi_op: WriteTLBEntry(P); break; case tlbwr_op: WriteRandomTLBEntry(P); break; default: CPUWarning("COP0 instruction not found at %#x\n", P->PC); break; } /* cp0 functions */ } /* rs values */ } else if (op == cache_op) { /* Be careful with this one. Mipsy's caches are physically indexed. */ uint targetCache; targetCache = TARGET_CACHE(instr); switch(targetCache) { case CACH_PI: case CACH_PD: case CACH_SI: CPUWarning("Bad op for cache instruction\n"); break; case CACH_SD: CPUWarning("Bad op for cache instruction\n"); break; default: CPUWarning("BAD TARGET CACHE at %#x\n", P->PC); break; } } else { CPUError("Unknown CP0 opcode in DoPrivInst\n"); } return FALSE;}#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -