📄 mipssim.cc
字号:
// mipssim.cc -- simulate a MIPS R2/3000 processor//// This code has been adapted from Ousterhout's MIPSSIM package.// Byte ordering is little-endian, so we can be compatible with// DEC RISC systems.//// DO NOT CHANGE -- part of the machine emulation//// Copyright (c) 1992-1993 The Regents of the University of California.// All rights reserved. See copyright.h for copyright notice and limitation // of liability and disclaimer of warranty provisions.#include "copyright.h"#include "machine.h"#include "mipssim.h"#include "system.h"static void Mult(int a, int b, bool signedArith, int* hiPtr, int* loPtr);//----------------------------------------------------------------------// Machine::Run// Simulate the execution of a user-level program on Nachos.// Called by the kernel when the program starts up; never returns.//// This routine is re-entrant, in that it can be called multiple// times concurrently -- one for each thread executing user code.//----------------------------------------------------------------------voidMachine::Run(){ Instruction *instr = new Instruction; // storage for decoded instruction if(DebugIsEnabled('m')) printf("Starting thread \"%s\" at time %d\n", currentThread->getName(), stats->totalTicks); interrupt->setStatus(UserMode); for (;;) { OneInstruction(instr); interrupt->OneTick(); if (singleStep && (runUntilTime <= stats->totalTicks)) Debugger(); }}//----------------------------------------------------------------------// TypeToReg// Retrieve the register # referred to in an instruction. //----------------------------------------------------------------------static int TypeToReg(RegType reg, Instruction *instr){ switch (reg) { case RS: return instr->rs; case RT: return instr->rt; case RD: return instr->rd; case EXTRA: return instr->extra; default: return -1; }}//----------------------------------------------------------------------// Machine::OneInstruction// Execute one instruction from a user-level program//// If there is any kind of exception or interrupt, we invoke the // exception handler, and when it returns, we return to Run(), which// will re-invoke us in a loop. This allows us to// re-start the instruction execution from the beginning, in// case any of our state has changed. On a syscall,// the OS software must increment the PC so execution begins// at the instruction immediately after the syscall. //// This routine is re-entrant, in that it can be called multiple// times concurrently -- one for each thread executing user code.// We get re-entrancy by never caching any data -- we always re-start the// simulation from scratch each time we are called (or after trapping// back to the Nachos kernel on an exception or interrupt), and we always// store all data back to the machine registers and memory before// leaving. This allows the Nachos kernel to control our behavior// by controlling the contents of memory, the translation table,// and the register set.//----------------------------------------------------------------------voidMachine::OneInstruction(Instruction *instr){ int raw; int nextLoadReg = 0; int nextLoadValue = 0; // record delayed load operation, to apply // in the future // Fetch instruction if (!machine->ReadMem(registers[PCReg], 4, &raw)) return; // exception occurred instr->value = raw; instr->Decode(); if (DebugIsEnabled('m')) { struct OpString *str = &opStrings[instr->opCode]; ASSERT(instr->opCode <= MaxOpcode); printf("At PC = 0x%x: ", registers[PCReg]); printf(str->string, TypeToReg(str->args[0], instr), TypeToReg(str->args[1], instr), TypeToReg(str->args[2], instr)); printf("\n"); } // Compute next pc, but don't install in case there's an error or branch. int pcAfter = registers[NextPCReg] + 4; int sum, diff, tmp, value; unsigned int rs, rt, imm; // Execute the instruction (cf. Kane's book) switch (instr->opCode) { case OP_ADD: sum = registers[instr->rs] + registers[instr->rt]; if (!((registers[instr->rs] ^ registers[instr->rt]) & SIGN_BIT) && ((registers[instr->rs] ^ sum) & SIGN_BIT)) { RaiseException(OverflowException, 0); return; } registers[instr->rd] = sum; break; case OP_ADDI: sum = registers[instr->rs] + instr->extra; if (!((registers[instr->rs] ^ instr->extra) & SIGN_BIT) && ((instr->extra ^ sum) & SIGN_BIT)) { RaiseException(OverflowException, 0); return; } registers[instr->rt] = sum; break; case OP_ADDIU: registers[instr->rt] = registers[instr->rs] + instr->extra; break; case OP_ADDU: registers[instr->rd] = registers[instr->rs] + registers[instr->rt]; break; case OP_AND: registers[instr->rd] = registers[instr->rs] & registers[instr->rt]; break; case OP_ANDI: registers[instr->rt] = registers[instr->rs] & (instr->extra & 0xffff); break; case OP_BEQ: if (registers[instr->rs] == registers[instr->rt]) pcAfter = registers[NextPCReg] + IndexToAddr(instr->extra); break; case OP_BGEZAL: registers[R31] = registers[NextPCReg] + 4; case OP_BGEZ: if (!(registers[instr->rs] & SIGN_BIT)) pcAfter = registers[NextPCReg] + IndexToAddr(instr->extra); break; case OP_BGTZ: if (registers[instr->rs] > 0) pcAfter = registers[NextPCReg] + IndexToAddr(instr->extra); break; case OP_BLEZ: if (registers[instr->rs] <= 0) pcAfter = registers[NextPCReg] + IndexToAddr(instr->extra); break; case OP_BLTZAL: registers[R31] = registers[NextPCReg] + 4; case OP_BLTZ: if (registers[instr->rs] & SIGN_BIT) pcAfter = registers[NextPCReg] + IndexToAddr(instr->extra); break; case OP_BNE: if (registers[instr->rs] != registers[instr->rt]) pcAfter = registers[NextPCReg] + IndexToAddr(instr->extra); break; case OP_DIV: if (registers[instr->rt] == 0) { registers[LoReg] = 0; registers[HiReg] = 0; } else { registers[LoReg] = registers[instr->rs] / registers[instr->rt]; registers[HiReg] = registers[instr->rs] % registers[instr->rt]; } break; case OP_DIVU: rs = (unsigned int) registers[instr->rs]; rt = (unsigned int) registers[instr->rt]; if (rt == 0) { registers[LoReg] = 0; registers[HiReg] = 0; } else { tmp = rs / rt; registers[LoReg] = (int) tmp; tmp = rs % rt; registers[HiReg] = (int) tmp; } break; case OP_JAL: registers[R31] = registers[NextPCReg] + 4; case OP_J: pcAfter = (pcAfter & 0xf0000000) | IndexToAddr(instr->extra); break; case OP_JALR: registers[instr->rd] = registers[NextPCReg] + 4; case OP_JR: pcAfter = registers[instr->rs]; break; case OP_LB: case OP_LBU: tmp = registers[instr->rs] + instr->extra; if (!machine->ReadMem(tmp, 1, &value)) return; if ((value & 0x80) && (instr->opCode == OP_LB)) value |= 0xffffff00; else value &= 0xff; nextLoadReg = instr->rt; nextLoadValue = value; break; case OP_LH: case OP_LHU: tmp = registers[instr->rs] + instr->extra; if (tmp & 0x1) { RaiseException(AddressErrorException, tmp); return; } if (!machine->ReadMem(tmp, 2, &value)) return; if ((value & 0x8000) && (instr->opCode == OP_LH)) value |= 0xffff0000; else value &= 0xffff; nextLoadReg = instr->rt; nextLoadValue = value; break; case OP_LUI: DEBUG('m', "Executing: LUI r%d,%d\n", instr->rt, instr->extra); registers[instr->rt] = instr->extra << 16; break; case OP_LW: tmp = registers[instr->rs] + instr->extra; if (tmp & 0x3) { RaiseException(AddressErrorException, tmp); return; } if (!machine->ReadMem(tmp, 4, &value)) return; nextLoadReg = instr->rt; nextLoadValue = value; break; case OP_LWL: tmp = registers[instr->rs] + instr->extra; // ReadMem assumes all 4 byte requests are aligned on an even // word boundary. Also, the little endian/big endian swap code would // fail (I think) if the other cases are ever exercised. ASSERT((tmp & 0x3) == 0); if (!machine->ReadMem(tmp, 4, &value)) return; if (registers[LoadReg] == instr->rt) nextLoadValue = registers[LoadValueReg]; else nextLoadValue = registers[instr->rt]; switch (tmp & 0x3) { case 0: nextLoadValue = value; break; case 1: nextLoadValue = (nextLoadValue & 0xff) | (value << 8); break; case 2: nextLoadValue = (nextLoadValue & 0xffff) | (value << 16); break; case 3: nextLoadValue = (nextLoadValue & 0xffffff) | (value << 24); break; } nextLoadReg = instr->rt; break; case OP_LWR: tmp = registers[instr->rs] + instr->extra; // ReadMem assumes all 4 byte requests are aligned on an even // word boundary. Also, the little endian/big endian swap code would // fail (I think) if the other cases are ever exercised. ASSERT((tmp & 0x3) == 0); if (!machine->ReadMem(tmp, 4, &value)) return; if (registers[LoadReg] == instr->rt) nextLoadValue = registers[LoadValueReg]; else nextLoadValue = registers[instr->rt]; switch (tmp & 0x3) { case 0: nextLoadValue = (nextLoadValue & 0xffffff00) | ((value >> 24) & 0xff); break; case 1: nextLoadValue = (nextLoadValue & 0xffff0000) | ((value >> 16) & 0xffff); break; case 2: nextLoadValue = (nextLoadValue & 0xff000000) | ((value >> 8) & 0xffffff); break; case 3: nextLoadValue = value; break; } nextLoadReg = instr->rt; break; case OP_MFHI: registers[instr->rd] = registers[HiReg]; break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -