code/machine/machine.cc
// machine.cc
// Routines for simulating the execution of user programs.
//
// 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 "system.h"
// Textual names of the exceptions that can be generated by user program
// execution, for debugging.
static char* exceptionNames[] = { "no exception", "syscall",
"page fault/no TLB entry", "page read only",
"bus error", "address error", "overflow",
"illegal instruction" };
//----------------------------------------------------------------------
// CheckEndian
// Check to be sure that the host really uses the format it says it
// does, for storing the bytes of an integer. Stop on error.
//----------------------------------------------------------------------
static
void CheckEndian()
{
union checkit {
char charword[4];
unsigned int intword;
} check;
check.charword[0] = 1;
check.charword[1] = 2;
check.charword[2] = 3;
check.charword[3] = 4;
#ifdef HOST_IS_BIG_ENDIAN
ASSERT (check.intword == 0x01020304);
#else
ASSERT (check.intword == 0x04030201);
#endif
}
//----------------------------------------------------------------------
// Machine::Machine
// Initialize the simulation of user program execution.
//
// "debug" -- if TRUE, drop into the debugger after each user instruction
// is executed.
//----------------------------------------------------------------------
Machine::Machine(bool debug)
{
int i;
for (i = 0; i < NumTotalRegs; i++)
registers[i] = 0;
mainMemory = new char[MemorySize];
for (i = 0; i < MemorySize; i++)
mainMemory[i] = 0;
#ifdef USE_TLB
tlb = new TranslationEntry[TLBSize];
for (i = 0; i < TLBSize; i++)
tlb[i].valid = FALSE;
pageTable = NULL;
#else // use linear page table
tlb = NULL;
pageTable = NULL;
#endif
singleStep = debug;
CheckEndian();
}
//----------------------------------------------------------------------
// Machine::~Machine
// De-allocate the data structures used to simulate user program execution.
//----------------------------------------------------------------------
Machine::~Machine()
{
delete [] mainMemory;
if (tlb != NULL)
delete [] tlb;
}
//----------------------------------------------------------------------
// Machine::RaiseException
// Transfer control to the Nachos kernel from user mode, because
// the user program either invoked a system call, or some exception
// occured (such as the address translation failed).
//
// "which" -- the cause of the kernel trap
// "badVaddr" -- the virtual address causing the trap, if appropriate
//----------------------------------------------------------------------
void
Machine::RaiseException(ExceptionType which, int badVAddr)
{
DEBUG('m', "Exception: %s\n", exceptionNames[which]);
// ASSERT(interrupt->getStatus() == UserMode);
registers[BadVAddrReg] = badVAddr;
DelayedLoad(0, 0); // finish anything in progress
interrupt->setStatus(SystemMode);
ExceptionHandler(which); // interrupts are enabled at this point
interrupt->setStatus(UserMode);
}
//----------------------------------------------------------------------
// Machine::Debugger
// Primitive debugger for user programs. Note that we can't use
// gdb to debug user programs, since gdb doesn't run on top of Nachos.
// It could, but you'd have to implement *a lot* more system calls
// to get it to work!
//
// So just allow single-stepping, and printing the contents of memory.
//----------------------------------------------------------------------
void Machine::Debugger()
{
char *buf = new char[80];
int num;
interrupt->DumpState();
DumpState();
printf("%d> ", stats->totalTicks);
fflush(stdout);
fgets(buf, 80, stdin);
if (sscanf(buf, "%d", &num) == 1)
runUntilTime = num;
else {
runUntilTime = 0;
switch (*buf) {
case '\n':
break;
case 'c':
singleStep = FALSE;
break;
case '?':
printf("Machine commands:\n");
printf(" execute one instruction\n");
printf(" run until the given timer tick\n");
printf(" c run until completion\n");
printf(" ? print help message\n");
break;
}
}
delete [] buf;
}
//----------------------------------------------------------------------
// Machine::DumpState
// Print the user program's CPU state. We might print the contents
// of memory, but that seemed like overkill.
//----------------------------------------------------------------------
void
Machine::DumpState()
{
int i;
printf("Machine registers:\n");
for (i = 0; i < NumGPRegs; i++)
switch (i) {
case StackReg:
printf("\tSP(%d):\t0x%x%s", i, registers[i],
((i % 4) == 3) ? "\n" : "");
break;
case RetAddrReg:
printf("\tRA(%d):\t0x%x%s", i, registers[i],
((i % 4) == 3) ? "\n" : "");
break;
default:
printf("\t%d:\t0x%x%s", i, registers[i],
((i % 4) == 3) ? "\n" : "");
break;
}
printf("\tHi:\t0x%x", registers[HiReg]);
printf("\tLo:\t0x%x\n", registers[LoReg]);
printf("\tPC:\t0x%x", registers[PCReg]);
printf("\tNextPC:\t0x%x", registers[NextPCReg]);
printf("\tPrevPC:\t0x%x\n", registers[PrevPCReg]);
printf("\tLoad:\t0x%x", registers[LoadReg]);
printf("\tLoadV:\t0x%x\n", registers[LoadValueReg]);
printf("\n");
}
//----------------------------------------------------------------------
// Machine::ReadRegister/WriteRegister
// Fetch or write the contents of a user program register.
//----------------------------------------------------------------------
int Machine::ReadRegister(int num)
{
ASSERT((num >= 0) && (num < NumTotalRegs));
return registers[num];
}
void Machine::WriteRegister(int num, int value)
{
ASSERT((num >= 0) && (num < NumTotalRegs));
// DEBUG('m', "WriteRegister %d, value %d\n", num, value);
registers[num] = value;
}