public static void raiseException(int which, int badVAddr) { Debug.println('m', "Exception: " + exceptionNames[which]); Debug.ASSERT(Interrupt.getStatus() == Interrupt.UserMode); registers[BadVAddrReg] = badVAddr; delayedLoad(0, 0); // finish anything in progress Interrupt.setStatus(Interrupt.SystemMode); Nachos.exceptionHandler(which); // interrupts are enabled at this point Interrupt.setStatus(Interrupt.UserMode); }
static int readMem(int addr, int size) throws MachineException { int data = 0; int exception; int physicalAddress; if (Debug.isEnabled('a')) { Debug.printf('a', "Reading VA 0x%x, size %d\n", new Integer(addr), new Integer(size)); } try { //////////////System.out.println("try to read from vir "+addr); physicalAddress = translate(addr, size, false); } catch (MachineException e) { //System.out.println("read mem exe"+e.getMessage()); raiseException(e.exception, addr); throw e; } switch (size) { case 1: data = (mainMemory[physicalAddress] & 0xff); break; case 2: data = ((mainMemory[physicalAddress] & 0xff) << 8) | (mainMemory[physicalAddress+1] & 0xff); break; case 4: data = (mainMemory[physicalAddress+3] << 24) | ((mainMemory[physicalAddress+2] & 0xff) << 16) | ((mainMemory[physicalAddress+1] & 0xff) << 8) | (mainMemory[physicalAddress] & 0xff); break; default: Debug.ASSERT(false); } if (Debug.isEnabled('a')) { Debug.printf('a', "\tvalue read = 0x%x\n", new Long(data & LOW32BITS)); } return data; }
static boolean writeMem(int addr, int size, int value) { int exception; int physicalAddress; if (Debug.isEnabled('a')) { Debug.printf('a', "Writing VA 0x%x, size %d, value 0x%x\n", new Integer(addr), new Integer(size), new Integer(value)); } try { physicalAddress = translate(addr, size, true); } catch (MachineException e) { raiseException(e.exception, addr); return false; } switch (size) { case 1: mainMemory[physicalAddress] = (byte) (value & 0xff); break; case 2: mainMemory[physicalAddress] = (byte) ((value >> 8) & 0xff); mainMemory[physicalAddress+1] = (byte) (value & 0xff); break; case 4: mainMemory[physicalAddress+3] = (byte) ((value >> 24) & 0xff); mainMemory[physicalAddress+2] = (byte) ((value >> 16) & 0xff); mainMemory[physicalAddress+1] = (byte) ((value >> 8) & 0xff); mainMemory[physicalAddress] = (byte) (value & 0xff); break; default: Debug.ASSERT(false); } return true; }
static private void oneInstruction(Instruction instr) { int raw; int nextLoadReg = 0; int nextLoadValue = 0; // record delayed load operation, to apply // in the future // Fetch instruction try { instr.value = readMem(registers[PCReg], 4); } catch (MachineException e) { return; // exception occurred } instr.decode(); String str = Instruction.opStrings[instr.opCode]; byte args[] = Instruction.opRegs[instr.opCode]; Debug.ASSERT(instr.opCode <= Instruction.MaxOpcode); if (Debug.isEnabled('m')) { Debug.printf('m', "At PC = 0x%x: ", new Integer(registers[PCReg])); Debug.printf('m', str + "\n", new Integer(instr.typeToReg(args[0])), new Integer(instr.typeToReg(args[1])), new Integer(instr.typeToReg(args[2]))); } // 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; long rs, rt, imm; // Execute the instruction (cf. Kane's book) switch (instr.opCode) { case Instruction.OP_ADD: sum = registers[instr.rs] + registers[instr.rt]; if (((registers[instr.rs] ^ registers[instr.rt]) & SIGN_BIT) == 0 && ((registers[instr.rs] ^ sum) & SIGN_BIT) != 0) { raiseException(OverflowException, 0); return; } registers[instr.rd] = sum; break; case Instruction.OP_ADDI: sum = registers[instr.rs] + instr.extra; if (((registers[instr.rs] ^ instr.extra) & SIGN_BIT) == 0 && ((instr.extra ^ sum) & SIGN_BIT) != 0) { raiseException(OverflowException, 0); return; } registers[instr.rt] = sum; break; case Instruction.OP_ADDIU: // the registers are int, so we have to do them unsigned. // Now, precisely *WHY* didn't java give us unsigned types, again? rs = registers[instr.rs] & LOW32BITS; imm = instr.extra & LOW32BITS; rt = rs + imm; registers[instr.rt] = (int)rt; break; case Instruction.OP_ADDU: rs = registers[instr.rs] & LOW32BITS; rt = registers[instr.rt] & LOW32BITS; registers[instr.rd] = (int)(rs + rt); break; case Instruction.OP_AND: registers[instr.rd] = registers[instr.rs] & registers[instr.rt]; break; case Instruction.OP_ANDI: registers[instr.rt] = registers[instr.rs] & (instr.extra & 0xffff); break; case Instruction.OP_BEQ: if (registers[instr.rs] == registers[instr.rt]) pcAfter = registers[NextPCReg] + (instr.extra << 2); break; case Instruction.OP_BGEZAL: registers[R31] = registers[NextPCReg] + 4; case Instruction.OP_BGEZ: if ((registers[instr.rs] & SIGN_BIT) == 0) pcAfter = registers[NextPCReg] + (instr.extra << 2); break; case Instruction.OP_BGTZ: if (registers[instr.rs] > 0) pcAfter = registers[NextPCReg] + (instr.extra << 2); break; case Instruction.OP_BLEZ: if (registers[instr.rs] <= 0) pcAfter = registers[NextPCReg] + (instr.extra << 2); break; case Instruction.OP_BLTZAL: registers[R31] = registers[NextPCReg] + 4; case Instruction.OP_BLTZ: if ((registers[instr.rs] & SIGN_BIT) != 0) pcAfter = registers[NextPCReg] + (instr.extra << 2); break; case Instruction.OP_BNE: if (registers[instr.rs] != registers[instr.rt]) pcAfter = registers[NextPCReg] + (instr.extra << 2); break; case Instruction.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 Instruction.OP_DIVU: // don't sign extend rs = (registers[instr.rs] & LOW32BITS); rt = (registers[instr.rt] & LOW32BITS); if (rt == 0) { registers[LoReg] = 0; registers[HiReg] = 0; } else { tmp = (int) (rs / rt); registers[LoReg] = tmp; tmp = (int) (rs % rt); registers[HiReg] = tmp; } break; case Instruction.OP_JAL: registers[R31] = registers[NextPCReg] + 4; case Instruction.OP_J: pcAfter = (pcAfter & 0xf0000000) | instr.extra << 2; break; case Instruction.OP_JALR: registers[instr.rd] = registers[NextPCReg] + 4; case Instruction.OP_JR: pcAfter = registers[instr.rs]; break; case Instruction.OP_LB: case Instruction.OP_LBU: tmp = registers[instr.rs] + instr.extra; try { value = readMem(tmp, 1); } catch (MachineException e) { return; // exception occurred } if ((value & 0x80) != 0 && (instr.opCode == Instruction.OP_LB)) value |= 0xffffff00; else value &= 0xff; nextLoadReg = instr.rt; nextLoadValue = value; break; case Instruction.OP_LH: case Instruction.OP_LHU: tmp = registers[instr.rs] + instr.extra; if ((tmp & 0x1) != 0) { raiseException(AddressErrorException, tmp); return; } try { value = readMem(tmp, 2); } catch (MachineException e) { return; // exception occurred } if ((value & 0x8000) != 0 && (instr.opCode == Instruction.OP_LH)) value |= 0xffff0000; else value &= 0xffff; nextLoadReg = instr.rt; nextLoadValue = value; break; case Instruction.OP_LUI: if (Debug.isEnabled('m')) Debug.printf('m', "Executing: LUI r%d,%d\n", new Integer(instr.rt), new Integer(instr.extra)); registers[instr.rt] = instr.extra << 16; break; case Instruction.OP_LW: tmp = registers[instr.rs] + instr.extra; if ((tmp & 0x3) != 0) { raiseException(AddressErrorException, tmp); return; } try { value = readMem(tmp, 4); } catch (MachineException e) { return; // exception occurred } nextLoadReg = instr.rt; nextLoadValue = value; break; case Instruction.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. Debug.ASSERT((tmp & 0x3) == 0); try { value = readMem(tmp, 4); } catch (MachineException e) { return; // exception occurred } 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 Instruction.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. Debug.ASSERT((tmp & 0x3) == 0); try { value = readMem(tmp, 4); } catch (MachineException e) { return; // exception occurred } 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 Instruction.OP_MFHI: registers[instr.rd] = registers[HiReg]; break; case Instruction.OP_MFLO: registers[instr.rd] = registers[LoReg]; break; case Instruction.OP_MTHI: registers[HiReg] = registers[instr.rs]; break; case Instruction.OP_MTLO: registers[LoReg] = registers[instr.rs]; break; case Instruction.OP_MULT: mult(registers[instr.rs], registers[instr.rt], true, mresult); registers[HiReg] = mresult[0]; registers[LoReg] = mresult[1]; break; case Instruction.OP_MULTU: mult(registers[instr.rs], registers[instr.rt], false, mresult); registers[HiReg] = mresult[0]; registers[LoReg] = mresult[1]; break; case Instruction.OP_NOR: registers[instr.rd] = ~(registers[instr.rs] | registers[instr.rt]); break; case Instruction.OP_OR: registers[instr.rd] = registers[instr.rs] | registers[instr.rt]; break; case Instruction.OP_ORI: registers[instr.rt] = registers[instr.rs] | (instr.extra & 0xffff); break; case Instruction.OP_SB: if (!writeMem( (registers[instr.rs] + instr.extra), 1, registers[instr.rt])) return; break; case Instruction.OP_SH: if (!writeMem( (registers[instr.rs] + instr.extra), 2, registers[instr.rt])) return; break; case Instruction.OP_SLL: registers[instr.rd] = registers[instr.rt] << instr.extra; break; case Instruction.OP_SLLV: registers[instr.rd] = registers[instr.rt] << (registers[instr.rs] & 0x1f); break; case Instruction.OP_SLT: if (registers[instr.rs] < registers[instr.rt]) registers[instr.rd] = 1; else registers[instr.rd] = 0; break; case Instruction.OP_SLTI: if (registers[instr.rs] < instr.extra) registers[instr.rt] = 1; else registers[instr.rt] = 0; break; case Instruction.OP_SLTIU: rs = (registers[instr.rs] & LOW32BITS); imm = (instr.extra & LOW32BITS); if (rs < imm) registers[instr.rt] = 1; else registers[instr.rt] = 0; break; case Instruction.OP_SLTU: rs = registers[instr.rs] & LOW32BITS; rt = registers[instr.rt] & LOW32BITS; if (rs < rt) registers[instr.rd] = 1; else registers[instr.rd] = 0; break; case Instruction.OP_SRA: registers[instr.rd] = registers[instr.rt] >> instr.extra; break; case Instruction.OP_SRAV: registers[instr.rd] = registers[instr.rt] >> (registers[instr.rs] & 0x1f); break; case Instruction.OP_SRL: tmp = registers[instr.rt]; tmp >>= instr.extra; registers[instr.rd] = tmp; break; case Instruction.OP_SRLV: tmp = registers[instr.rt]; tmp >>= (registers[instr.rs] & 0x1f); registers[instr.rd] = tmp; break; case Instruction.OP_SUB: diff = registers[instr.rs] - registers[instr.rt]; if (((registers[instr.rs] ^ registers[instr.rt]) & SIGN_BIT) != 0 && ((registers[instr.rs] ^ diff) & SIGN_BIT) != 0) { raiseException(OverflowException, 0); return; } registers[instr.rd] = diff; break; case Instruction.OP_SUBU: rs = (registers[instr.rs] & LOW32BITS); rt = (registers[instr.rt] & LOW32BITS); registers[instr.rd] = (int)(rs - rt); break; case Instruction.OP_SW: if (!writeMem( (registers[instr.rs] + instr.extra), 4, registers[instr.rt])) return; break; case Instruction.OP_SWL: tmp = registers[instr.rs] + instr.extra; // The little endian/big endian swap code would // fail (I think) if the other cases are ever exercised. Debug.ASSERT((tmp & 0x3) == 0); try { value = readMem((tmp & ~0x3), 4); } catch (MachineException e) { return; // exception occurred } switch (tmp & 0x3) { case 0: value = registers[instr.rt]; break; case 1: value = (value & 0xff000000) | ((registers[instr.rt] >> 8) & 0xffffff); break; case 2: value = (value & 0xffff0000) | ((registers[instr.rt] >> 16) & 0xffff); break; case 3: value = (value & 0xffffff00) | ((registers[instr.rt] >> 24) & 0xff); break; } if (!writeMem((tmp & ~0x3), 4, value)) return; break; case Instruction.OP_SWR: tmp = registers[instr.rs] + instr.extra; // The little endian/big endian swap code would // fail (I think) if the other cases are ever exercised. Debug.ASSERT((tmp & 0x3) == 0); try { value = readMem((tmp & ~0x3), 4); } catch (MachineException e) { return; // exception occurred } switch (tmp & 0x3) { case 0: value = (value & 0xffffff) | (registers[instr.rt] << 24); break; case 1: value = (value & 0xffff) | (registers[instr.rt] << 16); break; case 2: value = (value & 0xff) | (registers[instr.rt] << 8); break; case 3: value = registers[instr.rt]; break; } if (!writeMem((tmp & ~0x3), 4, value)) return; break; case Instruction.OP_SYSCALL: raiseException(SyscallException, 0); return; case Instruction.OP_XOR: registers[instr.rd] = registers[instr.rs] ^ registers[instr.rt]; break; case Instruction.OP_XORI: registers[instr.rt] = registers[instr.rs] ^ (instr.extra & 0xffff); break; case Instruction.OP_RES: case Instruction.OP_UNIMP: raiseException(IllegalInstrException, 0); return; default: System.out.println("Bogus opcode, should not happen"); return; } // Now we have successfully executed the instruction. // Do any delayed load operation delayedLoad(nextLoadReg, nextLoadValue); // Advance program counters. registers[PrevPCReg] = registers[PCReg]; // for debugging, in case we // are jumping into lala-land registers[PCReg] = registers[NextPCReg]; registers[NextPCReg] = pcAfter; }
static private int translate(int virtAddr, int size, boolean writing) throws MachineException { int i = 0; long vpn, offset; TranslationEntry entry; long pageFrame; int physAddr; if (Debug.isEnabled('a')) { if (writing) Debug.printf('a', "\tTranslate 0x%x, %s: ", new Integer(virtAddr), "write"); else Debug.printf('a', "\tTranslate 0x%x, %s: ", new Integer(virtAddr), "read"); } // check for alignment errors if (((size == 4) && (virtAddr & 0x3) != 0) || ((size == 2) && (virtAddr & 0x1) != 0)) { Debug.println('a', "alignment problem at " + virtAddr + ", size " + size); throw new MachineException(exceptionNames[AddressErrorException], AddressErrorException); } // we must have either a TLB or a page table, but not both! Debug.ASSERT(tlb == null || pageTable == null); Debug.ASSERT(tlb != null || pageTable != null); // calculate the virtual page number, and offset within the page, // from the virtual address vpn = ((long) virtAddr & LOW32BITS) / PageSize; offset = ((long) virtAddr & LOW32BITS) % PageSize; if (tlb == null) { // => page table => vpn is index into table if (vpn >= pageTableSize) { Debug.println('a', "virtual page # " + virtAddr + " too large for page table size " + pageTableSize); throw new MachineException(exceptionNames[AddressErrorException], AddressErrorException); } else if (!pageTable[(int)vpn].valid) { Debug.println('a', "virtual page # " + virtAddr + " too large for page table size " + pageTableSize); throw new MachineException(exceptionNames[PageFaultException], PageFaultException); } entry = pageTable[(int)vpn]; } else { for (entry = null, i = 0; i < TLBSize; i++) if (tlb[i].valid && (tlb[i].virtualPage == vpn)) { entry = tlb[i]; // FOUND! break; } if (entry == null) { // not found Debug.println('a', "** no valid TLB entry found for this virtual page!"); // really, this is a TLB fault, // the page may be in memory, // but not in the TLB throw new MachineException(exceptionNames[PageFaultException], PageFaultException); } } if (entry.readOnly && writing) { // trying to write to a read-only page Debug.println('a', virtAddr + " mapped read-only at " + i + " in TLB!"); throw new MachineException(exceptionNames[ReadOnlyException], ReadOnlyException); } pageFrame = entry.physicalPage; // if the pageFrame is too big, there is something really wrong! // An invalid translation was loaded into the page table or TLB. if (pageFrame >= NumPhysPages) { Debug.println('a', "*** frame " + pageFrame + " > " + NumPhysPages); throw new MachineException(exceptionNames[BusErrorException], BusErrorException); } entry.use = true; // set the use, dirty bits if (writing) entry.dirty = true; physAddr = (int) (pageFrame * PageSize + offset); Debug.ASSERT((physAddr >= 0) && ((physAddr + size) <= MemorySize)); if (Debug.isEnabled('a')) { Debug.printf('a', "phys addr = 0x%x\n", new Integer(physAddr)); } return physAddr; }