Initial version
This commit is contained in:
commit
6f405265a5
102 changed files with 14486 additions and 0 deletions
437
code/machine/machine.cc
Normal file
437
code/machine/machine.cc
Normal file
|
@ -0,0 +1,437 @@
|
|||
// 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 const 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;
|
||||
#else // use linear page table
|
||||
tlb = NULL;
|
||||
#endif
|
||||
currentPageTable = NULL;
|
||||
currentPageTableSize = 0;
|
||||
|
||||
singleStep = debug;
|
||||
runUntilTime = 0;
|
||||
CheckEndian();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Machine::~Machine
|
||||
// De-allocate the data structures used to simulate user program execution.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
Machine::~Machine()
|
||||
{
|
||||
delete [] mainMemory;
|
||||
mainMemory = NULL;
|
||||
if (tlb != NULL)
|
||||
{
|
||||
delete [] tlb;
|
||||
tlb = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// 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)
|
||||
{
|
||||
enum MachineStatus oldStatus = interrupt->getStatus();
|
||||
DEBUG('m', "Exception: %s\n", exceptionNames[which]);
|
||||
|
||||
registers[BadVAddrReg] = badVAddr;
|
||||
DelayedLoad(0, 0); // finish anything in progress
|
||||
interrupt->setStatus(SystemMode);
|
||||
ExceptionHandler(which); // interrupts are enabled at this point
|
||||
interrupt->setStatus(oldStatus);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// 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();
|
||||
|
||||
// LB: Update the print format after the promotion of tick types
|
||||
// from int to long long
|
||||
// printf("%d> ", stats->totalTicks);
|
||||
printf("%lld> ", stats->totalTicks);
|
||||
// End of correction
|
||||
|
||||
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(" <return> execute one instruction\n");
|
||||
printf(" <number> 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");
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// DumpReg
|
||||
// Draw a pointer register in the virtual address space
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
Machine::DumpReg(FILE *output, int val, const char *name, const char *color,
|
||||
int ptr_x, int ptr_y, unsigned virtual_x,
|
||||
unsigned y, unsigned blocksize)
|
||||
{
|
||||
unsigned page = val / PageSize;
|
||||
unsigned offset = val % PageSize;
|
||||
|
||||
fprintf(output, "<text x=\"%d\" y=\"%u\" stroke=\"%s\" fill=\"%s\" font-size=\"%u\">%s</text>\n",
|
||||
ptr_x, y - page * blocksize, color, color, blocksize, name);
|
||||
fprintf(output, "<line x1=\"%d\" y1=\"%u\" x2=\"%u\" y2=\"%u\" "
|
||||
"stroke=\"#808080\" stroke-width=\"5\"/>\n",
|
||||
ptr_x + 3*blocksize/2,
|
||||
ptr_y - page * blocksize - blocksize/2,
|
||||
virtual_x + offset * blocksize + blocksize/2,
|
||||
ptr_y - page * blocksize - blocksize/2);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// DumpRegs
|
||||
// Draw machine pointer registers in the virtual address space
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
Machine::DumpRegs(FILE *output, int ptr_x, int ptr_y, unsigned virtual_x,
|
||||
unsigned y, unsigned blocksize)
|
||||
{
|
||||
DumpReg(output, registers[PCReg], "PC", "#FF0000", ptr_x, ptr_y, virtual_x, y, blocksize);
|
||||
DumpReg(output, registers[StackReg], "SP", "#FF0000", ptr_x, ptr_y, virtual_x, y, blocksize);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// PageTableRoom
|
||||
// Return how much room would be needed for showing this page table
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
unsigned
|
||||
Machine::PageTableRoom(unsigned numPages, unsigned blocksize)
|
||||
{
|
||||
return (numPages+1) * blocksize;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// get_RGB
|
||||
// Turn a byte into a representative color of the byte
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
static void
|
||||
get_RGB(unsigned char value, unsigned char *r, unsigned char *g, unsigned char *b)
|
||||
{
|
||||
*r = (value & 0x7) << 5;
|
||||
*g = (value & 0x38) << 2;
|
||||
*b = (value & 0xc0) << 0; // Humans don't see blue that well
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// DumpPageTable
|
||||
// Draw a page table and its mapping to physical address space
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
unsigned
|
||||
Machine::DumpPageTable(FILE *output,
|
||||
TranslationEntry *_pageTable, unsigned _pageTableSize,
|
||||
unsigned addr_x, unsigned virtual_x, unsigned virtual_width,
|
||||
unsigned physical_x, unsigned virtual_y, unsigned y,
|
||||
unsigned blocksize)
|
||||
{
|
||||
unsigned page, offset;
|
||||
|
||||
for (page = 0; page < _pageTableSize; page++) {
|
||||
TranslationEntry *e = &_pageTable[page];
|
||||
|
||||
fprintf(output, "<text x=\"%u\" y=\"%u\" font-size=\"%u\">0x%04x</text>\n",
|
||||
addr_x, virtual_y - page * blocksize, blocksize, page * PageSize);
|
||||
|
||||
if (e->valid) {
|
||||
for (offset = 0; offset < PageSize; offset++) {
|
||||
int value;
|
||||
unsigned char r, g, b;
|
||||
int virt = page * PageSize + offset;
|
||||
int phys;
|
||||
|
||||
TranslationEntry *save_pageTable = currentPageTable;
|
||||
unsigned save_pageTableSize = currentPageTableSize;
|
||||
|
||||
currentPageTable = _pageTable;
|
||||
currentPageTableSize = _pageTableSize;
|
||||
|
||||
ExceptionType res = Translate(virt, &phys, 1, FALSE, FALSE);
|
||||
if (res == NoException)
|
||||
ReadMem(virt, 1, &value, FALSE);
|
||||
else
|
||||
value = -1;
|
||||
|
||||
currentPageTable = save_pageTable;
|
||||
currentPageTableSize = save_pageTableSize;
|
||||
|
||||
get_RGB(value, &r, &g, &b);
|
||||
|
||||
fprintf(output, "<rect x=\"%u\" y=\"%u\" "
|
||||
"width=\"%u\" height=\"%u\" "
|
||||
"fill=\"#%02x%02x%02x\" "
|
||||
"stroke=\"#000000\" "
|
||||
"stroke-width=\"1\"/>\n",
|
||||
virtual_x + offset * blocksize,
|
||||
virtual_y - page * blocksize - blocksize,
|
||||
blocksize, blocksize,
|
||||
r, g, b);
|
||||
}
|
||||
|
||||
fprintf(output, "<line x1=\"%u\" y1=\"%u\" "
|
||||
"x2=\"%u\" y2=\"%u\" "
|
||||
"stroke=\"#000000\" "
|
||||
"stroke-width=\"1\"/>\n",
|
||||
virtual_x + virtual_width,
|
||||
virtual_y - page * blocksize - blocksize/2,
|
||||
physical_x,
|
||||
y - e->physicalPage * blocksize - blocksize/2);
|
||||
}
|
||||
}
|
||||
|
||||
if (_pageTable == currentPageTable) {
|
||||
fprintf(output, "<rect x=\"%u\" y=\"%u\" "
|
||||
"width=\"%u\" height=\"%u\" "
|
||||
"fill-opacity=\"0.0\" "
|
||||
"stroke=\"#FF0000\" "
|
||||
"stroke-width=\"2\"/>\n",
|
||||
virtual_x,
|
||||
virtual_y - _pageTableSize * blocksize,
|
||||
virtual_width, _pageTableSize * blocksize);
|
||||
}
|
||||
|
||||
return PageTableRoom(_pageTableSize, blocksize);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Machine::DumpMem
|
||||
// Draw the user program's memory layout.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
Machine::DumpMem(const char *name)
|
||||
{
|
||||
FILE *output = fopen(name, "w+");
|
||||
|
||||
const unsigned blocksize = 32;
|
||||
|
||||
const unsigned addr_x = 0;
|
||||
const unsigned addr_width = 4*blocksize;
|
||||
|
||||
const unsigned ptr_x = addr_x + addr_width;
|
||||
const unsigned ptr_width = 6*blocksize;
|
||||
|
||||
const unsigned virtual_x = ptr_x + ptr_width;
|
||||
const unsigned virtual_width = PageSize * blocksize;
|
||||
|
||||
const unsigned physical_x = virtual_x + virtual_width + 30 * blocksize;
|
||||
const unsigned physical_width = PageSize * blocksize;
|
||||
|
||||
const unsigned width = physical_x + physical_width;
|
||||
unsigned height;
|
||||
unsigned page, offset;
|
||||
|
||||
unsigned virtual_height = AddrSpacesRoom(blocksize);
|
||||
unsigned physical_height = NumPhysPages * blocksize;
|
||||
|
||||
height = virtual_height > physical_height ? virtual_height : physical_height;
|
||||
|
||||
fprintf(output, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
|
||||
fprintf(output, "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" viewBox = \"0 0 %u %u\" version = \"1.1\">\n", width, height);
|
||||
|
||||
fprintf(output, "<rect x=\"%u\" y=\"%u\" "
|
||||
"width=\"%u\" height=\"%u\" "
|
||||
"fill=\"#ffffff\" "
|
||||
"stroke=\"#000000\" "
|
||||
"stroke-width=\"1\"/>\n",
|
||||
virtual_x,
|
||||
height - currentPageTableSize * blocksize,
|
||||
virtual_width,
|
||||
currentPageTableSize * blocksize);
|
||||
|
||||
DumpAddrSpaces(output, addr_x, ptr_x, virtual_x, virtual_width, physical_x, height, blocksize);
|
||||
|
||||
for (page = 0; page < NumPhysPages; page++) {
|
||||
for (offset = 0; offset < PageSize; offset++) {
|
||||
int value;
|
||||
unsigned char r, g, b;
|
||||
|
||||
value = machine->mainMemory[page * PageSize + offset];
|
||||
get_RGB(value, &r, &g, &b);
|
||||
|
||||
fprintf(output, "<rect x=\"%u\" y=\"%u\" "
|
||||
"width=\"%u\" height=\"%u\" "
|
||||
"fill=\"#%02x%02x%02x\" "
|
||||
"stroke=\"#000000\" "
|
||||
"stroke-width=\"1\"/>\n",
|
||||
physical_x + offset * blocksize,
|
||||
height - page * blocksize - blocksize,
|
||||
blocksize, blocksize,
|
||||
r, g, b);
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(output, "</svg>\n");
|
||||
fclose(output);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// 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;
|
||||
}
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue