📄 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-1996 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.// Simulation fixes done by Peter E Reissner, class of Winter 1994/95 (York)// I've not been able to test this extensively.// Ported to newer version of Nachos at Waterloo by Scott Graham (Mar 99).#include "copyright.h"#include "debug.h"#include "machine.h"#include "mipssim.h"#include "main.h"static void Mult(int a, int b, bool signedArith, int* hiPtr, int* loPtr);// The following class defines an instruction, represented in both// undecoded binary form// decoded to identify// operation to do// registers to act on// any immediate operand valueclass Instruction { public: void Decode(); // decode the binary representation of the instruction unsigned int value; // binary representation of the instruction char opCode; // Type of instruction. This is NOT the same as the // opcode field from the instruction: see defs in mips.h char rs, rt, rd; // Three registers from instruction. int extra; // Immediate or target or shamt field or offset. // Immediates are sign-extended.};//----------------------------------------------------------------------// 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 (debug->IsEnabled('m')) { cout << "Starting program in thread: " << kernel->currentThread->getName(); cout << ", at time: " << kernel->stats->totalTicks << "\n"; } kernel->interrupt->setStatus(UserMode); for (;;) { OneInstruction(instr); kernel->interrupt->OneTick(); if (singleStep && (runUntilTime <= kernel->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){#ifdef SIM_FIX int byte; // described in Kane for LWL,LWR,...#endif int raw; int nextLoadReg = 0; int nextLoadValue = 0; // record delayed load operation, to apply // in the future // Fetch instruction if (!ReadMem(registers[PCReg], 4, &raw)) return; // exception occurred instr->value = raw; instr->Decode(); if (debug->IsEnabled('m')) { struct OpString *str = &opStrings[instr->opCode]; char buf[80]; ASSERT(instr->opCode <= MaxOpcode); cout << "At PC = " << registers[PCReg]; sprintf(buf, str->format, TypeToReg(str->args[0], instr), TypeToReg(str->args[1], instr), TypeToReg(str->args[2], instr)); cout << "\t" << buf << "\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 (!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 (!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(dbgMach, "Executing: LUI r" << 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 (!ReadMem(tmp, 4, &value)) return; nextLoadReg = instr->rt; nextLoadValue = value; break; case OP_LWL: tmp = registers[instr->rs] + instr->extra;#ifdef SIM_FIX // The only difference between this code and the BIG ENDIAN code // is that the ReadMem call is guaranteed an aligned access as it // should be (Kane's book hides the fact that all memory access // are done using aligned loads - what the instruction asks for // is a arbitrary) This is the whole purpose of LWL and LWR etc. // Then the switch uses 3 - (tmp & 0x3) instead of (tmp & 0x3) byte = tmp & 0x3; // DEBUG('P', "Addr 0x%X\n",tmp-byte); if (!ReadMem(tmp-byte, 4, &value)) return;#else // 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 (!ReadMem(tmp, 4, &value)) return;#endif if (registers[LoadReg] == instr->rt) nextLoadValue = registers[LoadValueReg]; else nextLoadValue = registers[instr->rt];#ifdef SIM_FIX switch (3 - byte) #else switch (tmp & 0x3)#endif { 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;#ifdef SIM_FIX // The only difference between this code and the BIG ENDIAN code // is that the ReadMem call is guaranteed an aligned access as it // should be (Kane's book hides the fact that all memory access // are done using aligned loads - what the instruction asks // for is a arbitrary) This is the whole purpose of LWL and LWR etc. // Then the switch uses 3 - (tmp & 0x3) instead of (tmp & 0x3) byte = tmp & 0x3; // DEBUG('P', "Addr 0x%X\n",tmp-byte); if (!ReadMem(tmp-byte, 4, &value)) return;#else // 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 (!ReadMem(tmp, 4, &value)) return;#endif if (registers[LoadReg] == instr->rt) nextLoadValue = registers[LoadValueReg]; else nextLoadValue = registers[instr->rt];#ifdef SIM_FIX switch (3 - byte) #else switch (tmp & 0x3)#endif { 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -