/// <summary> /// Switches the CPU into an exception state. Each of the 7 exceptions that can be /// raised are unique. The CPU is switched into a new cpu mode, and interrupts /// may be disabled. The PC is set to the exception vector. /// Vector address is initially set to the "low" values. However of the HighVectors flag /// is set, then ARM exception vectors start at 0xffff0000 /// </summary> /// <param name="armException"></param> private void SetExceptionState(ARMExceptions armException) { uint newPC; switch (armException) { case ARMExceptions.Reset: mCPSR.SwitchCPUMode(CPSR.CPUModeEnum.Supervisor); mCPSR.IRQDisable = true; mCPSR.FIQDisable = true; newPC = 0x00000000; break; case ARMExceptions.UndefinedInstruction: mCPSR.SwitchCPUMode(CPSR.CPUModeEnum.Undefined); mCPSR.IRQDisable = true; newPC = 0x00000004; break; case ARMExceptions.SoftwareInterrupt: mCPSR.SwitchCPUMode(CPSR.CPUModeEnum.Supervisor); mCPSR.IRQDisable = true; newPC = 0x00000008; break; case ARMExceptions.PreFetchAbort: mCPSR.SwitchCPUMode(CPSR.CPUModeEnum.Abort); mCPSR.IRQDisable = true; newPC = 0x0000000c; break; case ARMExceptions.DataAbort: mCPSR.SwitchCPUMode(CPSR.CPUModeEnum.Abort); mCPSR.IRQDisable = true; newPC = 0x00000010; break; case ARMExceptions.IRQ: mCPSR.SwitchCPUMode(CPSR.CPUModeEnum.IRQ); mCPSR.IRQDisable = true; newPC = 0x00000018; break; case ARMExceptions.FIQ: mCPSR.SwitchCPUMode(CPSR.CPUModeEnum.FIQ); mCPSR.IRQDisable = true; mCPSR.FIQDisable = true; newPC = 0x0000001c; break; default: return; }//switch //save the PC into the new modes banked lr mGPR.LR = mGPR.PC; //if the "high" vectors mode is on, then set the top 16bits to a 1 of the new pc. mGPR.PC = this.HighVectors ? (newPC | 0xffff0000) : newPC; //always process the exception in ARM mode. When the exception returns and reloads //the cpsr from the scpsr, thumb mode will be reenabled if that was the starting mode. mCPSR.tf = false; }//SetExceptionState
/// <summary> /// Execute the next instruction pointed to by the PC. /// Can be in either ARM or Thumb mode. /// </summary> public void Execute() { uint pcBefore = mGPR.PC; // PC before op is executed try { ARMPluginInterfaces.MemorySize opcodeSize = mCPSR.tf ? ARMPluginInterfaces.MemorySize.HalfWord : ARMPluginInterfaces.MemorySize.Word; //this should never happen as the PC is checked after the opcode is executed, //but just being careful ... if (!this.InRange(pcBefore, opcodeSize)) { pcOutofRange(uint.MaxValue); // we don't know the old address?? return; } //track the number of instructions executed. ++InstructionCount; //get the opcode and increment the PC. Fetch through the cache system if available. uint opcode = this.mL1InstructionCache.GetMemory(pcBefore, opcodeSize); //increment the PC by the current opcode size (ARM or Thumb mode). mGPR.PC = pcBefore + (uint)opcodeSize; uint cycleCount; bool swiInstruction; try { cycleCount = ExecuteInstruction(opcode, out swiInstruction); } catch (UnalignedAccessException e) { ReportRuntimeError(pcBefore, "{0}", e.Message); HaltSimulation(); mGPR.PC = pcBefore; // undo any advance of the PC return; } catch (MemoryAccessException e) { ReportRuntimeError(pcBefore, "{0}", e.Message); HaltSimulation(); mGPR.PC = pcBefore; // undo any advance of the PC return; } // a cycle count of zero indicates an unknown instruction (including an swi instruction) if (cycleCount == 0) { //let plugins have first crack at the unknown instruction. //a return of 0 indicates it cannot handle it. cycleCount = UnknownOpCode(opcode); }//if //if the cycle count is still 0 and it's an swi instruction, it wasn't handled by a plugin //invoke an ARM swi exception if (cycleCount == 0 && swiInstruction) { this.RequestException(ARMExceptions.SoftwareInterrupt); }//if //otherwise its an unknown instruction that is not an swi instruction //so raise an ARM undefined instruction exception. else if (cycleCount == 0) { this.RequestException(ARMExceptions.UndefinedInstruction); }//else if //if we actually expended cycles, let the plugins know and update local cycle count if (cycleCount > 0) { CycleCount += cycleCount; //todo - check overflow HandleCycles((ushort)cycleCount); }//if //test the PC against valid memory. If it is not within the memory block, we have a problem if (!this.InRange(mGPR.PC, opcodeSize)) { pcOutofRange(pcBefore); }//if //check if any exceptions have been raised. //it is assumed that only 1 exception can be raised in a single instruction cycle. if (mRequestedException != 0) { if ((mRequestedException & (uint)ARMExceptions.SoftwareInterrupt) != 0) { SetExceptionState(ARMExceptions.SoftwareInterrupt); }//if //FIQ has highest priority else if (((mRequestedException & (uint)ARMExceptions.FIQ) != 0) && !mCPSR.FIQDisable) { SetExceptionState(ARMExceptions.FIQ); } else if (((mRequestedException & (uint)ARMExceptions.IRQ) != 0) && !mCPSR.IRQDisable) { SetExceptionState(ARMExceptions.IRQ); } else if ((mRequestedException & (uint)ARMExceptions.UndefinedInstruction) != 0) { SetExceptionState(ARMExceptions.UndefinedInstruction); } else if ((mRequestedException & (uint)ARMExceptions.Reset) != 0) { SetExceptionState(ARMExceptions.Reset); } mRequestedException = 0; }//if //test the PC against valid memory. If it is not within the memory block, we have a problem if (!this.InRange(mGPR.PC, opcodeSize)) { ARMExceptions possibleReason = interruptKind(mGPR.PC); if (possibleReason != ARMExceptions.None) { if (possibleReason == ARMExceptions.SoftwareInterrupt) { ReportRuntimeError(pcBefore, "Unimplemented SWI code: (0x{0:X6})\n" + "[Check File/Preferences/Plugins to see which SWI sets have been enabled]", opcode & 0xFFFFFF); } else { ReportRuntimeError(pcBefore, "Unhandled interrupt of kind {0}", interruptKind(mGPR.PC)); } HaltSimulation(); mGPR.PC = pcBefore; // leave PC stuck on the interrupting instruction } else { pcOutofRange(pcBefore); } } }//try catch (ARMPluginInterfaces.MemoryAccessException ex) { ReportRuntimeError(pcBefore, "Attempt to access memory out of valid range: 0x{0:X8}", ex.Address); HaltSimulation(); mGPR.PC = pcBefore; // leave PC stuck on the bad instruction }//catch }//Execute
public void RequestException(ARMExceptions exception) { mRequestedException |= (uint)exception; }