Initial version
This commit is contained in:
commit
6f405265a5
102 changed files with 14486 additions and 0 deletions
234
code/machine/console.cc
Normal file
234
code/machine/console.cc
Normal file
|
@ -0,0 +1,234 @@
|
|||
// console.cc
|
||||
// Routines to simulate a serial port to a console device.
|
||||
// A console has input (a keyboard) and output (a display).
|
||||
// These are each simulated by operations on UNIX files.
|
||||
// The simulated device is asynchronous,
|
||||
// so we have to invoke the interrupt handler (after a simulated
|
||||
// delay), to signal that a byte has arrived and/or that a written
|
||||
// byte has departed.
|
||||
//
|
||||
// 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 "console.h"
|
||||
#include "system.h"
|
||||
#include <langinfo.h>
|
||||
|
||||
#define NOCHAR (-42)
|
||||
|
||||
// Dummy functions because C++ is weird about pointers to member functions
|
||||
static void ConsoleReadPoll(void *c)
|
||||
{ Console *console = (Console *)c; console->CheckCharAvail(); }
|
||||
static void ConsoleWriteDone(void *c)
|
||||
{ Console *console = (Console *)c; console->WriteDone(); }
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Console::Console
|
||||
// Initialize the simulation of a hardware console device.
|
||||
//
|
||||
// "readFile" -- UNIX file simulating the keyboard (NULL -> use stdin)
|
||||
// "writeFile" -- UNIX file simulating the display (NULL -> use stdout)
|
||||
// "readAvailHandler" is the interrupt handler called when a character arrives
|
||||
// from the keyboard
|
||||
// "writeDoneHandler" is the interrupt handler called when a character has
|
||||
// been output, so that it is ok to request the next char be
|
||||
// output
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
int Console::stdin_busy;
|
||||
|
||||
Console::Console(const char *readFile, const char *writeFile, VoidFunctionPtr readAvailHandler,
|
||||
VoidFunctionPtr writeDoneHandler, void *callArg)
|
||||
{
|
||||
if (readFile == NULL)
|
||||
{
|
||||
ASSERT(!stdin_busy);
|
||||
stdin_busy = 1;
|
||||
readFileNo = 0; // keyboard = stdin
|
||||
}
|
||||
else
|
||||
readFileNo = OpenForReadWrite(readFile, TRUE); // should be read-only
|
||||
if (writeFile == NULL)
|
||||
writeFileNo = 1; // display = stdout
|
||||
else
|
||||
writeFileNo = OpenForWrite(writeFile);
|
||||
|
||||
// set up the stuff to emulate asynchronous interrupts
|
||||
writeHandler = writeDoneHandler;
|
||||
readHandler = readAvailHandler;
|
||||
handlerArg = callArg;
|
||||
putBusy = FALSE;
|
||||
incoming = NOCHAR;
|
||||
|
||||
// start polling for incoming packets
|
||||
interrupt->Schedule(ConsoleReadPoll, this, ConsoleTime, ConsoleReadInt);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Console::~Console
|
||||
// Clean up console emulation
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
Console::~Console()
|
||||
{
|
||||
if (readFileNo != 0)
|
||||
Close(readFileNo);
|
||||
else
|
||||
stdin_busy = 0;
|
||||
readFileNo = -1;
|
||||
if (writeFileNo != 1)
|
||||
Close(writeFileNo);
|
||||
writeFileNo = -1;
|
||||
|
||||
/* Wait for last interrupts to happen */
|
||||
while (readFileNo != -2)
|
||||
currentThread->Yield();
|
||||
while (putBusy)
|
||||
currentThread->Yield();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Console::CheckCharAvail()
|
||||
// Periodically called to check if a character is available for
|
||||
// input from the simulated keyboard (eg, has it been typed?).
|
||||
//
|
||||
// Only read it in if there is buffer space for it (if the previous
|
||||
// character has been grabbed out of the buffer by the Nachos kernel).
|
||||
// Invoke the "read" interrupt handler, once the character has been
|
||||
// put into the buffer.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
Console::CheckCharAvail()
|
||||
{
|
||||
unsigned char c, d;
|
||||
int n;
|
||||
int cont = 1;
|
||||
|
||||
if (readFileNo == -1) {
|
||||
// Termination, don't schedule any other interrupt
|
||||
readFileNo = -2;
|
||||
n = 0;
|
||||
cont = 0;
|
||||
} else if ((incoming != NOCHAR) || !PollFile(readFileNo)) {
|
||||
// do nothing if character is already buffered, or none to be read
|
||||
n = 0;
|
||||
} else {
|
||||
// otherwise, read character and tell user about it
|
||||
n = ReadPartial(readFileNo, &c, sizeof(c));
|
||||
if (n == 0) {
|
||||
incoming = EOF;
|
||||
(*readHandler)(handlerArg);
|
||||
} else if (strcmp(nl_langinfo(CODESET),"UTF-8")) {
|
||||
/* Not UTF-8, assume 8bit locale */
|
||||
incoming = c;
|
||||
} else
|
||||
/* UTF-8, decode */
|
||||
if (!(c & 0x80)) {
|
||||
/* ASCII */
|
||||
incoming = c;
|
||||
} else {
|
||||
if ((c & 0xe0) != 0xc0)
|
||||
/* continuation char or more than three bytes, drop */
|
||||
return;
|
||||
if (c & 0x1c)
|
||||
/* Not latin1, drop */
|
||||
return;
|
||||
/* latin1 UTF-8 char, read second char */
|
||||
n = ReadPartial(readFileNo, &d, sizeof(d));
|
||||
if (n == 0) {
|
||||
incoming = EOF;
|
||||
(*readHandler)(handlerArg);
|
||||
} else if ((d & 0xc0) != 0x80) {
|
||||
/* Odd, drop */
|
||||
return;
|
||||
} else {
|
||||
incoming = (c & 0x03) << 6 | d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cont)
|
||||
// schedule the next time to poll for a packet
|
||||
interrupt->Schedule(ConsoleReadPoll, this, ConsoleTime,
|
||||
ConsoleReadInt);
|
||||
|
||||
if (n) {
|
||||
stats->numConsoleCharsRead++;
|
||||
(*readHandler)(handlerArg);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Console::WriteDone()
|
||||
// Internal routine called when it is time to invoke the interrupt
|
||||
// handler to tell the Nachos kernel that the output character has
|
||||
// completed.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
Console::WriteDone()
|
||||
{
|
||||
putBusy = FALSE;
|
||||
stats->numConsoleCharsWritten++;
|
||||
(*writeHandler)(handlerArg);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Console::RX()
|
||||
// Read a character from the input buffer, if there is any there.
|
||||
// Either return the character, or EOF if none buffered or the end of the
|
||||
// input file was reached.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
int
|
||||
Console::RX()
|
||||
{
|
||||
int ch = incoming;
|
||||
|
||||
// We should not be reading anything if no character was received yet
|
||||
ASSERT(incoming != NOCHAR);
|
||||
|
||||
incoming = NOCHAR;
|
||||
return ch;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Console::TX()
|
||||
// Write a character to the simulated display, schedule an interrupt
|
||||
// to occur in the future, and return.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
Console::TX(int ch)
|
||||
{
|
||||
unsigned char c;
|
||||
|
||||
// Make sure that we are not already transferring a character
|
||||
ASSERT(putBusy == FALSE);
|
||||
|
||||
// Compensate when given a non-ascii latin1 character passed as signed char
|
||||
if (ch < 0 && ch >= -128)
|
||||
ch += 256;
|
||||
|
||||
if (ch < 0x80 || strcmp(nl_langinfo(CODESET),"UTF-8")) {
|
||||
/* Not UTF-8 or ASCII, assume 8bit locale */
|
||||
c = ch;
|
||||
WriteFile(writeFileNo, &c, sizeof(c));
|
||||
} else if (ch < 0x100) {
|
||||
/* Non-ASCII UTF-8, thus two bytes */
|
||||
c = ((ch & 0xc0) >> 6) | 0xc0;
|
||||
WriteFile(writeFileNo, &c, sizeof(c));
|
||||
c = (ch & 0x3f) | 0x80;
|
||||
WriteFile(writeFileNo, &c, sizeof(c));
|
||||
} /* Else not latin1, drop */
|
||||
putBusy = TRUE;
|
||||
interrupt->Schedule(ConsoleWriteDone, this, ConsoleTime,
|
||||
ConsoleWriteInt);
|
||||
}
|
85
code/machine/console.h
Normal file
85
code/machine/console.h
Normal file
|
@ -0,0 +1,85 @@
|
|||
// console.h
|
||||
// Data structures to simulate the behavior of a terminal
|
||||
// I/O device. A terminal has two parts -- a keyboard input,
|
||||
// and a display output, each of which produces/accepts
|
||||
// characters sequentially.
|
||||
//
|
||||
// The console hardware device is asynchronous. When a character is
|
||||
// written to the device, the routine returns immediately, and an
|
||||
// interrupt handler is called later when the I/O completes.
|
||||
// For reads, an interrupt handler is called when a character arrives.
|
||||
//
|
||||
// The user of the device can specify the routines to be called when
|
||||
// the read/write interrupts occur. There is a separate interrupt
|
||||
// for read and write, and the device is "duplex" -- a character
|
||||
// can be outgoing and incoming at the same time.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef CONSOLE_H
|
||||
#define CONSOLE_H
|
||||
|
||||
#include "copyright.h"
|
||||
#include "utility.h"
|
||||
#include <stdio.h>
|
||||
|
||||
// The following class defines a hardware console device.
|
||||
// Input and output to the device is simulated by reading
|
||||
// and writing to UNIX files ("readFile" and "writeFile").
|
||||
//
|
||||
// Since the device is asynchronous, the interrupt handler "readAvailHandler"
|
||||
// is called when a character has arrived, ready to be read by calling
|
||||
// RX().
|
||||
// The interrupt handler "writeDone" is called when an output character written
|
||||
// by calling TX() has been "put", so that the next character can be
|
||||
// written.
|
||||
|
||||
class Console:public dontcopythis {
|
||||
public:
|
||||
Console(const char *readFile, const char *writeFile, VoidFunctionPtr readAvailHandler,
|
||||
VoidFunctionPtr writeDoneHandler, void *callArg);
|
||||
// initialize the hardware console device,
|
||||
// registers the readAvailHandler and writeDoneHandler
|
||||
// callbacks
|
||||
|
||||
~Console(); // clean up console emulation
|
||||
|
||||
// external interface -- Nachos kernel code can call these
|
||||
void TX(int ch); // Write "ch" to the console display,
|
||||
// and return immediately. "writeDone"
|
||||
// is called when the I/O completes.
|
||||
|
||||
int RX(); // Poll the console input. If a char is
|
||||
// available, return it. Otherwise, crash.
|
||||
// EOF is returned if the end of the input
|
||||
// file was reached.
|
||||
// "readDone" is called whenever there is
|
||||
// a char to be gotten
|
||||
|
||||
// internal emulation routines -- DO NOT call these.
|
||||
void WriteDone(); // internal routines to signal I/O completion
|
||||
void CheckCharAvail();
|
||||
|
||||
private:
|
||||
int readFileNo; // UNIX file emulating the keyboard
|
||||
int writeFileNo; // UNIX file emulating the display
|
||||
VoidFunctionPtr writeHandler; // Interrupt handler to call when
|
||||
// the TX I/O completes
|
||||
VoidFunctionPtr readHandler; // Interrupt handler to call when
|
||||
// a character arrives from the keyboard
|
||||
void *handlerArg; // argument to be passed to the
|
||||
// interrupt handlers
|
||||
bool putBusy; // Is a TX operation in progress?
|
||||
// If so, you can't do another one!
|
||||
int incoming; // Contains the character to be read,
|
||||
// if there is one available.
|
||||
// Otherwise contains EOF.
|
||||
static int stdin_busy; // Whether stdin is already read from
|
||||
// by a console.
|
||||
};
|
||||
|
||||
#endif // CONSOLE_H
|
272
code/machine/disk.cc
Normal file
272
code/machine/disk.cc
Normal file
|
@ -0,0 +1,272 @@
|
|||
// disk.cc
|
||||
// Routines to simulate a physical disk device; reading and writing
|
||||
// to the disk is simulated as reading and writing to a UNIX file.
|
||||
// See disk.h for details about the behavior of disks (and
|
||||
// therefore about the behavior of this simulation).
|
||||
//
|
||||
// Disk operations are asynchronous, so we have to invoke an interrupt
|
||||
// handler when the simulated operation completes.
|
||||
//
|
||||
// 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 "disk.h"
|
||||
#include "system.h"
|
||||
|
||||
// We put this at the front of the UNIX file representing the
|
||||
// disk, to make it less likely we will accidentally treat a useful file
|
||||
// as a disk (which would probably trash the file's contents).
|
||||
#define MagicNumber 0x456789ab
|
||||
#define MagicSize sizeof(int)
|
||||
|
||||
#define DiskSize (MagicSize + (NumSectors * SectorSize))
|
||||
|
||||
// dummy procedure because we can't take a pointer of a member function
|
||||
static void DiskDone(void *arg) { ((Disk *)arg)->HandleInterrupt(); }
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Disk::Disk()
|
||||
// Initialize a simulated disk. Open the UNIX file (creating it
|
||||
// if it doesn't exist), and check the magic number to make sure it's
|
||||
// ok to treat it as Nachos disk storage.
|
||||
//
|
||||
// "name" -- text name of the file simulating the Nachos disk
|
||||
// "callWhenDone" -- interrupt handler to be called when disk read/write
|
||||
// request completes
|
||||
// "callArg" -- argument to pass the interrupt handler
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
Disk::Disk(const char* name, VoidFunctionPtr callWhenDone, void *callArg)
|
||||
{
|
||||
int magicNum;
|
||||
int tmp = 0;
|
||||
|
||||
DEBUG('d', "Initializing the disk, 0x%x 0x%x\n", callWhenDone, callArg);
|
||||
handler = callWhenDone;
|
||||
handlerArg = callArg;
|
||||
lastSector = 0;
|
||||
bufferInit = 0;
|
||||
|
||||
fileno = OpenForReadWrite(name, FALSE);
|
||||
if (fileno >= 0) { // file exists, check magic number
|
||||
Read(fileno, &magicNum, MagicSize);
|
||||
ASSERT(magicNum == MagicNumber);
|
||||
} else { // file doesn't exist, create it
|
||||
fileno = OpenForWrite(name);
|
||||
magicNum = MagicNumber;
|
||||
WriteFile(fileno, &magicNum, MagicSize); // write magic number
|
||||
|
||||
// need to write at end of file, so that reads will not return EOF
|
||||
Lseek(fileno, DiskSize - sizeof(int), SEEK_SET);
|
||||
WriteFile(fileno, &tmp, sizeof(int));
|
||||
}
|
||||
active = FALSE;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Disk::~Disk()
|
||||
// Clean up disk simulation, by closing the UNIX file representing the
|
||||
// disk.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
Disk::~Disk()
|
||||
{
|
||||
Close(fileno);
|
||||
fileno = -1;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Disk::PrintSector()
|
||||
// Dump the data in a disk read/write request, for debugging.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
static void
|
||||
PrintSector (bool writing, int sector, const void *data)
|
||||
{
|
||||
const int *p = (const int *) data;
|
||||
|
||||
if (writing)
|
||||
printf("Writing sector: %d\n", sector);
|
||||
else
|
||||
printf("Reading sector: %d\n", sector);
|
||||
for (unsigned int i = 0; i < (SectorSize/sizeof(int)); i++)
|
||||
printf("%x ", p[i]);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Disk::ReadRequest/WriteRequest
|
||||
// Simulate a request to read/write a single disk sector
|
||||
// Do the read/write immediately to the UNIX file
|
||||
// Set up an interrupt handler to be called later,
|
||||
// that will notify the caller when the simulator says
|
||||
// the operation has completed.
|
||||
//
|
||||
// Note that a disk only allows an entire sector to be read/written,
|
||||
// not part of a sector.
|
||||
//
|
||||
// "sectorNumber" -- the disk sector to read/write
|
||||
// "data" -- the bytes to be written, the buffer to hold the incoming bytes
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
Disk::ReadRequest(int sectorNumber, void* data)
|
||||
{
|
||||
int ticks = ComputeLatency(sectorNumber, FALSE);
|
||||
|
||||
ASSERT(!active); // only one request at a time
|
||||
ASSERT((sectorNumber >= 0) && (sectorNumber < NumSectors));
|
||||
|
||||
DEBUG('d', "Reading from sector %d\n", sectorNumber);
|
||||
Lseek(fileno, SectorSize * sectorNumber + MagicSize, SEEK_SET);
|
||||
Read(fileno, data, SectorSize);
|
||||
if (DebugIsEnabled('d'))
|
||||
PrintSector(FALSE, sectorNumber, data);
|
||||
|
||||
active = TRUE;
|
||||
UpdateLast(sectorNumber);
|
||||
stats->numDiskReads++;
|
||||
interrupt->Schedule(DiskDone, this, ticks, DiskInt);
|
||||
}
|
||||
|
||||
void
|
||||
Disk::WriteRequest(int sectorNumber, const void* data)
|
||||
{
|
||||
int ticks = ComputeLatency(sectorNumber, TRUE);
|
||||
|
||||
ASSERT(!active);
|
||||
ASSERT((sectorNumber >= 0) && (sectorNumber < NumSectors));
|
||||
|
||||
DEBUG('d', "Writing to sector %d\n", sectorNumber);
|
||||
Lseek(fileno, SectorSize * sectorNumber + MagicSize, SEEK_SET);
|
||||
WriteFile(fileno, data, SectorSize);
|
||||
if (DebugIsEnabled('d'))
|
||||
PrintSector(TRUE, sectorNumber, data);
|
||||
|
||||
active = TRUE;
|
||||
UpdateLast(sectorNumber);
|
||||
stats->numDiskWrites++;
|
||||
interrupt->Schedule(DiskDone, this, ticks, DiskInt);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Disk::HandleInterrupt()
|
||||
// Called when it is time to invoke the disk interrupt handler,
|
||||
// to tell the Nachos kernel that the disk request is done.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
Disk::HandleInterrupt ()
|
||||
{
|
||||
active = FALSE;
|
||||
(*handler)(handlerArg);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Disk::TimeToSeek()
|
||||
// Returns how long it will take to position the disk head over the correct
|
||||
// track on the disk. Since when we finish seeking, we are likely
|
||||
// to be in the middle of a sector that is rotating past the head,
|
||||
// we also return how long until the head is at the next sector boundary.
|
||||
//
|
||||
// Disk seeks at one track per SeekTime ticks (cf. stats.h)
|
||||
// and rotates at one sector per RotationTime ticks
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
int
|
||||
Disk::TimeToSeek(int newSector, int *rotation)
|
||||
{
|
||||
int newTrack = newSector / SectorsPerTrack;
|
||||
int oldTrack = lastSector / SectorsPerTrack;
|
||||
int seek = abs(newTrack - oldTrack) * SeekTime;
|
||||
// how long will seek take?
|
||||
int over = (stats->totalTicks + seek) % RotationTime;
|
||||
// will we be in the middle of a sector when
|
||||
// we finish the seek?
|
||||
|
||||
*rotation = 0;
|
||||
if (over > 0) // if so, need to round up to next full sector
|
||||
*rotation = RotationTime - over;
|
||||
return seek;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Disk::ModuloDiff()
|
||||
// Return number of sectors of rotational delay between target sector
|
||||
// "to" and current sector position "from"
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
int
|
||||
Disk::ModuloDiff(int to, int from)
|
||||
{
|
||||
int toOffset = to % SectorsPerTrack;
|
||||
int fromOffset = from % SectorsPerTrack;
|
||||
|
||||
return ((toOffset - fromOffset) + SectorsPerTrack) % SectorsPerTrack;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Disk::ComputeLatency()
|
||||
// Return how long will it take to read/write a disk sector, from
|
||||
// the current position of the disk head.
|
||||
//
|
||||
// Latency = seek time + rotational latency + transfer time
|
||||
// Disk seeks at one track per SeekTime ticks (cf. stats.h)
|
||||
// and rotates at one sector per RotationTime ticks
|
||||
//
|
||||
// To find the rotational latency, we first must figure out where the
|
||||
// disk head will be after the seek (if any). We then figure out
|
||||
// how long it will take to rotate completely past newSector after
|
||||
// that point.
|
||||
//
|
||||
// The disk also has a "track buffer"; the disk continuously reads
|
||||
// the contents of the current disk track into the buffer. This allows
|
||||
// read requests to the current track to be satisfied more quickly.
|
||||
// The contents of the track buffer are discarded after every seek to
|
||||
// a new track.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
int
|
||||
Disk::ComputeLatency(int newSector, bool writing)
|
||||
{
|
||||
int rotation;
|
||||
int seek = TimeToSeek(newSector, &rotation);
|
||||
int timeAfter = stats->totalTicks + seek + rotation;
|
||||
|
||||
#ifndef NOTRACKBUF // turn this on if you don't want the track buffer stuff
|
||||
// check if track buffer applies
|
||||
if ((writing == FALSE) && (seek == 0)
|
||||
&& (((timeAfter - bufferInit) / RotationTime)
|
||||
> ModuloDiff(newSector, bufferInit / RotationTime))) {
|
||||
DEBUG('d', "Request latency = %d\n", RotationTime);
|
||||
return RotationTime; // time to transfer sector from the track buffer
|
||||
}
|
||||
#endif
|
||||
|
||||
rotation += ModuloDiff(newSector, timeAfter / RotationTime) * RotationTime;
|
||||
|
||||
DEBUG('d', "Request latency = %d\n", seek + rotation + RotationTime);
|
||||
return(seek + rotation + RotationTime);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Disk::UpdateLast
|
||||
// Keep track of the most recently requested sector. So we can know
|
||||
// what is in the track buffer.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
Disk::UpdateLast(int newSector)
|
||||
{
|
||||
int rotate;
|
||||
int seek = TimeToSeek(newSector, &rotate);
|
||||
|
||||
if (seek != 0)
|
||||
bufferInit = stats->totalTicks + seek + rotate;
|
||||
lastSector = newSector;
|
||||
DEBUG('d', "Updating last sector = %d, %d\n", lastSector, bufferInit);
|
||||
}
|
93
code/machine/disk.h
Normal file
93
code/machine/disk.h
Normal file
|
@ -0,0 +1,93 @@
|
|||
// disk.h
|
||||
// Data structures to emulate a physical disk. A physical disk
|
||||
// can accept (one at a time) requests to read/write a disk sector;
|
||||
// when the request is satisfied, the CPU gets an interrupt, and
|
||||
// the next request can be sent to the disk.
|
||||
//
|
||||
// Disk contents are preserved across machine crashes, but if
|
||||
// a file system operation (eg, create a file) is in progress when the
|
||||
// system shuts down, the file system may be corrupted.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef DISK_H
|
||||
#define DISK_H
|
||||
|
||||
#include "copyright.h"
|
||||
#include "utility.h"
|
||||
|
||||
// The following class defines a physical disk I/O device. The disk
|
||||
// has a single surface, split up into "tracks", and each track split
|
||||
// up into "sectors" (the same number of sectors on each track, and each
|
||||
// sector has the same number of bytes of storage).
|
||||
//
|
||||
// Addressing is by sector number -- each sector on the disk is given
|
||||
// a unique number: track * SectorsPerTrack + offset within a track.
|
||||
//
|
||||
// As with other I/O devices, the raw physical disk is an asynchronous device --
|
||||
// requests to read or write portions of the disk return immediately,
|
||||
// and an interrupt is invoked later to signal that the operation completed.
|
||||
//
|
||||
// The physical disk is in fact simulated via operations on a UNIX file.
|
||||
//
|
||||
// To make life a little more realistic, the simulated time for
|
||||
// each operation reflects a "track buffer" -- RAM to store the contents
|
||||
// of the current track as the disk head passes by. The idea is that the
|
||||
// disk always transfers to the track buffer, in case that data is requested
|
||||
// later on. This has the benefit of eliminating the need for
|
||||
// "skip-sector" scheduling -- a read request which comes in shortly after
|
||||
// the head has passed the beginning of the sector can be satisfied more
|
||||
// quickly, because its contents are in the track buffer. Most
|
||||
// disks these days now come with a track buffer.
|
||||
//
|
||||
// The track buffer simulation can be disabled by compiling with -DNOTRACKBUF
|
||||
|
||||
#define SectorSize 128 // number of bytes per disk sector
|
||||
#define SectorsPerTrack 32 // number of sectors per disk track
|
||||
#define NumTracks 32 // number of tracks per disk
|
||||
#define NumSectors (SectorsPerTrack * NumTracks)
|
||||
// total # of sectors per disk
|
||||
|
||||
class Disk:public dontcopythis {
|
||||
public:
|
||||
Disk(const char* name, VoidFunctionPtr callWhenDone, void *callArg);
|
||||
// Create a simulated disk.
|
||||
// Invoke (*callWhenDone)(callArg)
|
||||
// every time a request completes.
|
||||
~Disk(); // Deallocate the disk.
|
||||
|
||||
void ReadRequest(int sectorNumber, void* data);
|
||||
// Read/write an single disk sector.
|
||||
// These routines send a request to
|
||||
// the disk and return immediately.
|
||||
// Only one request allowed at a time!
|
||||
void WriteRequest(int sectorNumber, const void* data);
|
||||
|
||||
void HandleInterrupt(); // Interrupt handler, invoked when
|
||||
// disk request finishes.
|
||||
|
||||
int ComputeLatency(int newSector, bool writing);
|
||||
// Return how long a request to
|
||||
// newSector will take:
|
||||
// (seek + rotational delay + transfer)
|
||||
|
||||
private:
|
||||
int fileno; // UNIX file number for simulated disk
|
||||
VoidFunctionPtr handler; // Interrupt handler, to be invoked
|
||||
// when any disk request finishes
|
||||
void *handlerArg; // Argument to interrupt handler
|
||||
bool active; // Is a disk operation in progress?
|
||||
int lastSector; // The previous disk request
|
||||
int bufferInit; // When the track buffer started
|
||||
// being loaded
|
||||
|
||||
int TimeToSeek(int newSector, int *rotate); // time to get to the new track
|
||||
int ModuloDiff(int to, int from); // # sectors between to and from
|
||||
void UpdateLast(int newSector);
|
||||
};
|
||||
|
||||
#endif // DISK_H
|
385
code/machine/interrupt.cc
Normal file
385
code/machine/interrupt.cc
Normal file
|
@ -0,0 +1,385 @@
|
|||
// interrupt.cc
|
||||
// Routines to simulate hardware interrupts.
|
||||
//
|
||||
// The hardware provides a routine (SetLevel) to enable or disable
|
||||
// interrupts.
|
||||
//
|
||||
// In order to emulate the hardware, we need to keep track of all
|
||||
// interrupts the hardware devices would cause, and when they
|
||||
// are supposed to occur.
|
||||
//
|
||||
// This module also keeps track of simulated time. Time advances
|
||||
// only when the following occur:
|
||||
// interrupts are re-enabled
|
||||
// a user instruction is executed
|
||||
// there is nothing in the ready queue
|
||||
//
|
||||
// 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 "interrupt.h"
|
||||
#include "system.h"
|
||||
#include "sysdep.h"
|
||||
|
||||
// String definitions for debugging messages
|
||||
|
||||
static const char *intLevelNames[] = { "off", "on"};
|
||||
static const char *intTypeNames[] = { "timer", "disk", "console write",
|
||||
"console read", "network send", "network recv"};
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// PendingInterrupt::PendingInterrupt
|
||||
// Initialize a hardware device interrupt that is to be scheduled
|
||||
// to occur in the near future.
|
||||
//
|
||||
// "func" is the procedure to call when the interrupt occurs
|
||||
// "param" is the argument to pass to the procedure
|
||||
// "time" is when (in simulated time) the interrupt is to occur
|
||||
// "kind" is the hardware device that generated the interrupt
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
PendingInterrupt::PendingInterrupt(VoidFunctionPtr func, void *param, long long time,
|
||||
IntType kind)
|
||||
{
|
||||
handler = func;
|
||||
arg = param;
|
||||
when = time;
|
||||
type = kind;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Interrupt::Interrupt
|
||||
// Initialize the simulation of hardware device interrupts.
|
||||
//
|
||||
// Interrupts start disabled, with no interrupts pending, etc.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
Interrupt::Interrupt()
|
||||
{
|
||||
level = IntOff;
|
||||
pending = new List();
|
||||
inHandler = FALSE;
|
||||
yieldOnReturn = FALSE;
|
||||
status = SystemMode;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Interrupt::~Interrupt
|
||||
// De-allocate the data structures needed by the interrupt simulation.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
Interrupt::~Interrupt()
|
||||
{
|
||||
while (!pending->IsEmpty())
|
||||
delete (PendingInterrupt *)(pending->Remove());
|
||||
delete pending;
|
||||
pending = NULL;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Interrupt::ChangeLevel
|
||||
// Change interrupts to be enabled or disabled, without advancing
|
||||
// the simulated time (normally, enabling interrupts advances the time).
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Interrupt::ChangeLevel
|
||||
// Change interrupts to be enabled or disabled, without advancing
|
||||
// the simulated time (normally, enabling interrupts advances the time).
|
||||
//
|
||||
// Used internally.
|
||||
//
|
||||
// "old" -- the old interrupt status
|
||||
// "now" -- the new interrupt status
|
||||
//----------------------------------------------------------------------
|
||||
void
|
||||
Interrupt::ChangeLevel(IntStatus old, IntStatus now)
|
||||
{
|
||||
if (now == IntOff)
|
||||
BlockUserAbort();
|
||||
level = now;
|
||||
DEBUG('i',"\tinterrupts: %s -> %s\n",intLevelNames[old],intLevelNames[now]);
|
||||
if (now == IntOn)
|
||||
UnBlockUserAbort();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Interrupt::SetLevel
|
||||
// Change interrupts to be enabled or disabled, and if interrupts
|
||||
// are being enabled, advance simulated time by calling OneTick().
|
||||
//
|
||||
// Returns:
|
||||
// The old interrupt status.
|
||||
// Parameters:
|
||||
// "now" -- the new interrupt status
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
IntStatus
|
||||
Interrupt::SetLevel(IntStatus now)
|
||||
{
|
||||
IntStatus old = level;
|
||||
|
||||
ASSERT((now == IntOff) || (inHandler == FALSE));// interrupt handlers are
|
||||
// prohibited from enabling
|
||||
// interrupts
|
||||
|
||||
ChangeLevel(old, now); // change to new state
|
||||
if ((now == IntOn) && (old == IntOff))
|
||||
OneTick(); // advance simulated time
|
||||
return old;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Interrupt::Enable
|
||||
// Turn interrupts on. Who cares what they used to be?
|
||||
// Used in ThreadRoot, to turn interrupts on when first starting up
|
||||
// a thread.
|
||||
//----------------------------------------------------------------------
|
||||
void
|
||||
Interrupt::Enable()
|
||||
{
|
||||
(void) SetLevel(IntOn);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Interrupt::OneTick
|
||||
// Advance simulated time and check if there are any pending
|
||||
// interrupts to be called.
|
||||
//
|
||||
// Two things can cause OneTick to be called:
|
||||
// interrupts are re-enabled
|
||||
// a user instruction is executed
|
||||
//----------------------------------------------------------------------
|
||||
void
|
||||
Interrupt::OneTick()
|
||||
{
|
||||
MachineStatus old = status;
|
||||
|
||||
// advance simulated time
|
||||
if (status == SystemMode) {
|
||||
stats->totalTicks += SystemTick;
|
||||
stats->systemTicks += SystemTick;
|
||||
} else { // USER_PROGRAM
|
||||
stats->totalTicks += UserTick;
|
||||
stats->userTicks += UserTick;
|
||||
}
|
||||
DEBUG('i', "\n== Tick %lld ==\n", stats->totalTicks);
|
||||
|
||||
// check any pending interrupts are now ready to fire
|
||||
ChangeLevel(IntOn, IntOff); // first, turn off interrupts
|
||||
// (interrupt handlers run with
|
||||
// interrupts disabled)
|
||||
while (CheckIfDue(FALSE)) // check for pending interrupts
|
||||
;
|
||||
ChangeLevel(IntOff, IntOn); // re-enable interrupts
|
||||
if (yieldOnReturn) { // if the timer device handler asked
|
||||
// for a context switch, ok to do it now
|
||||
yieldOnReturn = FALSE;
|
||||
status = SystemMode; // yield is a kernel routine
|
||||
currentThread->Yield();
|
||||
status = old;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Interrupt::YieldOnReturn
|
||||
// Called from within an interrupt handler, to cause a context switch
|
||||
// (for example, on a time slice) in the interrupted thread,
|
||||
// when the handler returns.
|
||||
//
|
||||
// We can't do the context switch here, because that would switch
|
||||
// out the interrupt handler, and we want to switch out the
|
||||
// interrupted thread.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
Interrupt::YieldOnReturn()
|
||||
{
|
||||
ASSERT(inHandler == TRUE);
|
||||
yieldOnReturn = TRUE;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Interrupt::Idle
|
||||
// Routine called when there is nothing in the ready queue.
|
||||
//
|
||||
// Since something has to be running in order to put a thread
|
||||
// on the ready queue, the only thing to do is to advance
|
||||
// simulated time until the next scheduled hardware interrupt.
|
||||
//
|
||||
// If there are no pending interrupts, stop. There's nothing
|
||||
// more for us to do.
|
||||
//----------------------------------------------------------------------
|
||||
void
|
||||
Interrupt::Idle()
|
||||
{
|
||||
DEBUG('i', "Machine idling; checking for interrupts.\n");
|
||||
status = IdleMode;
|
||||
if (CheckIfDue(TRUE)) { // check for any pending interrupts
|
||||
while (CheckIfDue(FALSE)) // check for any other pending
|
||||
; // interrupts
|
||||
yieldOnReturn = FALSE; // since there's nothing in the
|
||||
// ready queue, the yield is automatic
|
||||
status = SystemMode;
|
||||
return; // return in case there's now
|
||||
// a runnable thread
|
||||
}
|
||||
|
||||
// if there are no pending interrupts, and nothing is on the ready
|
||||
// queue, it is time to stop. If the console or the network is
|
||||
// operating, there are *always* pending interrupts, so this code
|
||||
// is not reached. Instead, the halt must be invoked by the user program.
|
||||
|
||||
DEBUG('i', "Machine idle. No interrupts to do.\n");
|
||||
printf("No threads ready or runnable, and no pending interrupts.\n");
|
||||
printf("Assuming the program completed.\n");
|
||||
Powerdown();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Interrupt::Powerdown
|
||||
// Shut down Nachos cleanly, printing out performance statistics.
|
||||
//----------------------------------------------------------------------
|
||||
void
|
||||
Interrupt::Powerdown()
|
||||
{
|
||||
printf("Machine going down!\n\n");
|
||||
stats->Print();
|
||||
Cleanup(); // Never returns.
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Interrupt::Schedule
|
||||
// Arrange for the CPU to be interrupted when simulated time
|
||||
// reaches "now + when".
|
||||
//
|
||||
// Implementation: just put it on a sorted list.
|
||||
//
|
||||
// NOTE: the Nachos kernel should not call this routine directly.
|
||||
// Instead, it is only called by the hardware device simulators.
|
||||
//
|
||||
// "handler" is the procedure to call when the interrupt occurs
|
||||
// "arg" is the argument to pass to the procedure
|
||||
// "fromNow" is how far in the future (in simulated time) the
|
||||
// interrupt is to occur
|
||||
// "type" is the hardware device that generated the interrupt
|
||||
//----------------------------------------------------------------------
|
||||
void
|
||||
Interrupt::Schedule(VoidFunctionPtr handler, void *arg, long long fromNow, IntType type)
|
||||
{
|
||||
long long when = stats->totalTicks + fromNow;
|
||||
PendingInterrupt *toOccur = new PendingInterrupt(handler, arg, when, type);
|
||||
|
||||
DEBUG('i', "Scheduling interrupt handler the %s at time = %lld\n",
|
||||
intTypeNames[type], when);
|
||||
ASSERT(fromNow > 0);
|
||||
|
||||
pending->SortedInsert(toOccur, when);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Interrupt::CheckIfDue
|
||||
// Check if an interrupt is scheduled to occur, and if so, fire it off.
|
||||
//
|
||||
// Returns:
|
||||
// TRUE, if we fired off any interrupt handlers
|
||||
// Params:
|
||||
// "advanceClock" -- if TRUE, there is nothing in the ready queue,
|
||||
// so we should simply advance the clock to when the next
|
||||
// pending interrupt would occur (if any). If the pending
|
||||
// interrupt is just the time-slice daemon, however, then
|
||||
// we're done!
|
||||
//----------------------------------------------------------------------
|
||||
bool
|
||||
Interrupt::CheckIfDue(bool advanceClock)
|
||||
{
|
||||
MachineStatus old = status;
|
||||
long long when;
|
||||
|
||||
ASSERT(level == IntOff); // interrupts need to be disabled,
|
||||
// to invoke an interrupt handler
|
||||
|
||||
UnBlockUserAbort(); // Here it is safe to let the User abort
|
||||
BlockUserAbort();
|
||||
|
||||
if (DebugIsEnabled('i'))
|
||||
DumpState();
|
||||
PendingInterrupt *toOccur =
|
||||
(PendingInterrupt *)pending->SortedRemove(&when);
|
||||
|
||||
if (toOccur == NULL) // no pending interrupts
|
||||
return FALSE;
|
||||
|
||||
if (advanceClock && when > stats->totalTicks) { // advance the clock
|
||||
stats->idleTicks += (when - stats->totalTicks);
|
||||
stats->totalTicks = when;
|
||||
} else if (when > stats->totalTicks) { // not time yet, put it back
|
||||
pending->SortedInsert(toOccur, when);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Check if there is nothing more to do, and if so, quit
|
||||
if ((status == IdleMode) && (toOccur->type == TimerInt)
|
||||
&& pending->IsEmpty()) {
|
||||
pending->SortedInsert(toOccur, when);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
DEBUG('i', "Invoking interrupt handler for the %s at time %lld\n",
|
||||
intTypeNames[toOccur->type], toOccur->when);
|
||||
#ifdef USER_PROGRAM
|
||||
if (machine != NULL && status == UserMode)
|
||||
machine->DelayedLoad(0, 0);
|
||||
#endif
|
||||
inHandler = TRUE;
|
||||
status = SystemMode; // whatever we were doing,
|
||||
// we are now going to be
|
||||
// running in the kernel
|
||||
(*(toOccur->handler))(toOccur->arg); // call the interrupt handler
|
||||
status = old; // restore the machine status
|
||||
inHandler = FALSE;
|
||||
delete toOccur;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// PrintPending
|
||||
// Print information about an interrupt that is scheduled to occur.
|
||||
// When, where, why, etc.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
static void
|
||||
PrintPending(void *arg)
|
||||
{
|
||||
PendingInterrupt *pend = (PendingInterrupt *)arg;
|
||||
|
||||
printf("Interrupt handler %s, scheduled at %lld\n",
|
||||
intTypeNames[pend->type], pend->when);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// DumpState
|
||||
// Print the complete interrupt state - the status, and all interrupts
|
||||
// that are scheduled to occur in the future.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
Interrupt::DumpState()
|
||||
{
|
||||
// LB: Print format adapted after the promotion of tick type
|
||||
// from int to long long
|
||||
// printf("Time: %d, interrupts %s\n", stats->totalTicks,
|
||||
// intLevelNames[level]);
|
||||
printf("Time: %lld, interrupts %s\n", stats->totalTicks,
|
||||
intLevelNames[level]);
|
||||
// End of correction
|
||||
|
||||
printf("Pending interrupts:\n");
|
||||
fflush(stdout);
|
||||
pending->Mapcar(PrintPending);
|
||||
printf("End of pending interrupts\n");
|
||||
fflush(stdout);
|
||||
}
|
134
code/machine/interrupt.h
Normal file
134
code/machine/interrupt.h
Normal file
|
@ -0,0 +1,134 @@
|
|||
// interrupt.h
|
||||
// Data structures to emulate low-level interrupt hardware.
|
||||
//
|
||||
// The hardware provides a routine (SetLevel) to enable or disable
|
||||
// interrupts.
|
||||
//
|
||||
// In order to emulate the hardware, we need to keep track of all
|
||||
// interrupts the hardware devices would cause, and when they
|
||||
// are supposed to occur.
|
||||
//
|
||||
// This module also keeps track of simulated time. Time advances
|
||||
// only when the following occur:
|
||||
// interrupts are re-enabled
|
||||
// a user instruction is executed
|
||||
// there is nothing in the ready queue
|
||||
//
|
||||
// As a result, unlike real hardware, interrupts (and thus time-slice
|
||||
// context switches) cannot occur anywhere in the code where interrupts
|
||||
// are enabled, but rather only at those places in the code where
|
||||
// simulated time advances (so that it becomes time to invoke an
|
||||
// interrupt in the hardware simulation).
|
||||
//
|
||||
// NOTE: this means that incorrectly synchronized code may work
|
||||
// fine on this hardware simulation (even with randomized time slices),
|
||||
// but it wouldn't work on real hardware. (Just because we can't
|
||||
// always detect when your program would fail in real life, does not
|
||||
// mean it's ok to write incorrectly synchronized code!)
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef INTERRUPT_H
|
||||
#define INTERRUPT_H
|
||||
|
||||
#include "copyright.h"
|
||||
#include "list.h"
|
||||
|
||||
// Interrupts can be disabled (IntOff) or enabled (IntOn)
|
||||
enum IntStatus { IntOff, IntOn };
|
||||
|
||||
// Nachos can be running kernel code (SystemMode), user code (UserMode),
|
||||
// or there can be no runnable thread, because the ready list
|
||||
// is empty (IdleMode).
|
||||
enum MachineStatus {IdleMode, SystemMode, UserMode};
|
||||
|
||||
// IntType records which hardware device generated an interrupt.
|
||||
// In Nachos, we support a hardware timer device, a disk, a console
|
||||
// display and keyboard, and a network.
|
||||
enum IntType { TimerInt, DiskInt, ConsoleWriteInt, ConsoleReadInt,
|
||||
NetworkSendInt, NetworkRecvInt};
|
||||
|
||||
// The following class defines an interrupt that is scheduled
|
||||
// to occur in the future. The internal data structures are
|
||||
// left public to make it simpler to manipulate.
|
||||
|
||||
class PendingInterrupt {
|
||||
public:
|
||||
PendingInterrupt(VoidFunctionPtr func, void *param,
|
||||
long long time, IntType kind);
|
||||
// initialize an interrupt that will
|
||||
// occur in the future
|
||||
|
||||
VoidFunctionPtr handler; // The function (in the hardware device
|
||||
// emulator) to call when the interrupt occurs
|
||||
void *arg; // The argument to the function.
|
||||
long long when; // When the interrupt is supposed to fire
|
||||
IntType type; // for debugging
|
||||
};
|
||||
|
||||
// The following class defines the data structures for the simulation
|
||||
// of hardware interrupts. We record whether interrupts are enabled
|
||||
// or disabled, and any hardware interrupts that are scheduled to occur
|
||||
// in the future.
|
||||
|
||||
class Interrupt:public dontcopythis {
|
||||
public:
|
||||
Interrupt(); // initialize the interrupt simulation
|
||||
~Interrupt(); // de-allocate data structures
|
||||
|
||||
IntStatus SetLevel(IntStatus level);// Disable or enable interrupts
|
||||
// and return previous setting.
|
||||
|
||||
void Enable(); // Enable interrupts.
|
||||
IntStatus getLevel() {return level;}// Return whether interrupts
|
||||
// are enabled or disabled
|
||||
|
||||
void Idle(); // The ready queue is empty, roll
|
||||
// simulated time forward until the
|
||||
// next interrupt
|
||||
|
||||
void Powerdown(); // quit and print out stats
|
||||
|
||||
void YieldOnReturn(); // cause a context switch on return
|
||||
// from an interrupt handler
|
||||
|
||||
MachineStatus getStatus() { return status; } // idle, kernel, user
|
||||
void setStatus(MachineStatus st) { status = st; }
|
||||
|
||||
void DumpState(); // Print interrupt state
|
||||
|
||||
|
||||
// NOTE: the following are internal to the hardware simulation code.
|
||||
// DO NOT call these directly. I should make them "private",
|
||||
// but they need to be public since they are called by the
|
||||
// hardware device simulators.
|
||||
|
||||
void Schedule(VoidFunctionPtr handler,// Schedule an interrupt to occur
|
||||
void *arg, long long when, IntType type);// at time ``when''. This is called
|
||||
// by the hardware device simulators.
|
||||
|
||||
void OneTick(); // Advance simulated time
|
||||
|
||||
private:
|
||||
IntStatus level; // are interrupts enabled or disabled?
|
||||
List *pending; // the list of interrupts scheduled
|
||||
// to occur in the future
|
||||
bool inHandler; // TRUE if we are running an interrupt handler
|
||||
bool yieldOnReturn; // TRUE if we are to context switch
|
||||
// on return from the interrupt handler
|
||||
MachineStatus status; // idle, kernel mode, user mode
|
||||
|
||||
// these functions are internal to the interrupt simulation code
|
||||
|
||||
bool CheckIfDue(bool advanceClock); // Check if an interrupt is supposed
|
||||
// to occur now
|
||||
|
||||
void ChangeLevel(IntStatus old, // SetLevel, without advancing the
|
||||
IntStatus now); // simulated time
|
||||
};
|
||||
|
||||
#endif // INTERRRUPT_H
|
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;
|
||||
}
|
||||
|
239
code/machine/machine.h
Normal file
239
code/machine/machine.h
Normal file
|
@ -0,0 +1,239 @@
|
|||
// machine.h
|
||||
// Data structures for simulating the execution of user programs
|
||||
// running on top of Nachos.
|
||||
//
|
||||
// User programs are loaded into "mainMemory"; to Nachos,
|
||||
// this looks just like an array of bytes. Of course, the Nachos
|
||||
// kernel is in memory too -- but as in most machines these days,
|
||||
// the kernel is loaded into a separate memory region from user
|
||||
// programs, and accesses to kernel memory are not translated or paged.
|
||||
//
|
||||
// In Nachos, user programs are executed one instruction at a time,
|
||||
// by the simulator. Each memory reference is translated, checked
|
||||
// for errors, etc.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef MACHINE_H
|
||||
#define MACHINE_H
|
||||
|
||||
#include "copyright.h"
|
||||
#include "utility.h"
|
||||
#include "translate.h"
|
||||
#include "disk.h"
|
||||
|
||||
// Definitions related to the size, and format of user memory
|
||||
|
||||
#define PageSize SectorSize // set the page size equal to
|
||||
// the disk sector size, for
|
||||
// simplicity
|
||||
|
||||
#define NumPhysPages 64 // Increase this as necessary!
|
||||
#define MemorySize (NumPhysPages * PageSize)
|
||||
#define TLBSize 4 // if there is a TLB, make it small
|
||||
|
||||
enum ExceptionType { NoException, // Everything ok!
|
||||
SyscallException, // A program executed a system call.
|
||||
PageFaultException, // No valid translation found
|
||||
ReadOnlyException, // Write attempted to page marked
|
||||
// "read-only"
|
||||
BusErrorException, // Translation resulted in an
|
||||
// invalid physical address
|
||||
AddressErrorException, // Unaligned reference or one that
|
||||
// was beyond the end of the
|
||||
// address space
|
||||
OverflowException, // Integer overflow in add or sub.
|
||||
IllegalInstrException, // Unimplemented or reserved instr.
|
||||
|
||||
NumExceptionTypes
|
||||
};
|
||||
|
||||
// User program CPU state. The full set of MIPS registers, plus a few
|
||||
// more because we need to be able to start/stop a user program between
|
||||
// any two instructions (thus we need to keep track of things like load
|
||||
// delay slots, etc.)
|
||||
|
||||
#define StackReg 29 // User's stack pointer
|
||||
#define RetAddrReg 31 // Holds return address for procedure calls
|
||||
#define NumGPRegs 32 // 32 general purpose registers on MIPS
|
||||
#define HiReg 32 // Double register to hold multiply result
|
||||
#define LoReg 33
|
||||
#define PCReg 34 // Current program counter
|
||||
#define NextPCReg 35 // Next program counter (for branch delay)
|
||||
#define PrevPCReg 36 // Previous program counter (for debugging)
|
||||
#define LoadReg 37 // The register target of a delayed load.
|
||||
#define LoadValueReg 38 // The value to be loaded by a delayed load.
|
||||
#define BadVAddrReg 39 // The failing virtual address on an exception
|
||||
|
||||
#define NumTotalRegs 40
|
||||
|
||||
// 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 value
|
||||
|
||||
class Instruction {
|
||||
public:
|
||||
void Decode(); // decode the binary representation of the instruction
|
||||
|
||||
unsigned int value; // binary representation of the instruction
|
||||
|
||||
// Type of instruction. This is NOT the same as the
|
||||
// opcode field from the instruction: see defs in mips.h
|
||||
unsigned char opCode;
|
||||
// Three registers from instruction.
|
||||
unsigned char rs, rt, rd;
|
||||
// Immediate or target or shamt field or offset.
|
||||
// Immediates are sign-extended.
|
||||
unsigned int extra;
|
||||
};
|
||||
|
||||
// The following class defines the simulated host workstation hardware, as
|
||||
// seen by user programs -- the CPU registers, main memory, etc.
|
||||
// User programs shouldn't be able to tell that they are running on our
|
||||
// simulator or on the real hardware, except
|
||||
// we don't support floating point instructions
|
||||
// the system call interface to Nachos is not the same as UNIX
|
||||
// (10 system calls in Nachos vs. 200 in UNIX!)
|
||||
// If we were to implement more of the UNIX system calls, we ought to be
|
||||
// able to run Nachos on top of Nachos!
|
||||
//
|
||||
// The procedures in this class are defined in machine.cc, mipssim.cc, and
|
||||
// translate.cc.
|
||||
|
||||
class Machine:public dontcopythis {
|
||||
public:
|
||||
Machine(bool debug); // Initialize the simulation of the hardware
|
||||
// for running user programs
|
||||
~Machine(); // De-allocate the data structures
|
||||
|
||||
// Routines callable by the Nachos kernel
|
||||
void Run(); // Run a user program
|
||||
|
||||
int ReadRegister(int num); // read the contents of a CPU register
|
||||
|
||||
void WriteRegister(int num, int value);
|
||||
// store a value into a CPU register
|
||||
|
||||
|
||||
// Routines internal to the machine simulation -- DO NOT call these
|
||||
|
||||
void OneInstruction(Instruction *instr);
|
||||
// Run one instruction of a user program.
|
||||
void DelayedLoad(int nextReg, int nextVal);
|
||||
// Do a pending delayed load (modifying a reg)
|
||||
|
||||
bool ReadMem(int addr, int size, int* value);
|
||||
bool ReadMem(int addr, int size, int* value, bool debug);
|
||||
bool WriteMem(int addr, int size, int value);
|
||||
// Read or write 1, 2, or 4 bytes of virtual
|
||||
// memory (at addr). Return FALSE if a
|
||||
// correct translation couldn't be found.
|
||||
|
||||
ExceptionType Translate(int virtAddr, int* physAddr, int size, bool writing, bool debug);
|
||||
// Translate an address, and check for
|
||||
// alignment. Set the use and dirty bits in
|
||||
// the translation entry appropriately,
|
||||
// and return an exception code if the
|
||||
// translation couldn't be completed.
|
||||
|
||||
void RaiseException(ExceptionType which, int badVAddr);
|
||||
// Trap to the Nachos kernel, because of a
|
||||
// system call or other exception.
|
||||
|
||||
void Debugger(); // invoke the user program debugger
|
||||
void DumpState(); // print the user CPU and memory state
|
||||
void DumpMem(const char *name); // Draw the memory state
|
||||
void DumpReg(FILE *output, int val, const char *name, const char *color,
|
||||
int ptr_x, int ptr_y, unsigned virtual_x,
|
||||
unsigned y, unsigned blocksize);
|
||||
// Dump a register
|
||||
void DumpRegs(FILE *output, int ptr_x, int ptr_y, unsigned virtual_x,
|
||||
unsigned y, unsigned blocksize);
|
||||
// Dump the machine registers
|
||||
unsigned PageTableRoom(unsigned numPages, unsigned blocksize);
|
||||
// Return how much room is needed for a page table
|
||||
unsigned 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);
|
||||
// Dump a pagetable
|
||||
|
||||
|
||||
// Data structures -- all of these are accessible to Nachos kernel code.
|
||||
// "public" for convenience.
|
||||
//
|
||||
// Note that *all* communication between the user program and the kernel
|
||||
// are in terms of these data structures.
|
||||
|
||||
char *mainMemory; // physical memory to store user program,
|
||||
// code and data, while executing
|
||||
int registers[NumTotalRegs]; // CPU registers, for executing user programs
|
||||
|
||||
|
||||
// NOTE: the hardware translation of virtual addresses in the user program
|
||||
// to physical addresses (relative to the beginning of "mainMemory")
|
||||
// can be controlled by one of:
|
||||
// a traditional linear page table
|
||||
// a software-loaded translation lookaside buffer (tlb) -- a cache of
|
||||
// mappings of virtual page #'s to physical page #'s
|
||||
//
|
||||
// If "tlb" is NULL, the linear page table is used
|
||||
// If "tlb" is non-NULL, the Nachos kernel is responsible for managing
|
||||
// the contents of the TLB. But the kernel can use any data structure
|
||||
// it wants (eg, segmented paging) for handling TLB cache misses.
|
||||
//
|
||||
// For simplicity, both the page table pointer and the TLB pointer are
|
||||
// public. However, while there can be multiple page tables (one per address
|
||||
// space, stored in memory), there is only one TLB (implemented in hardware).
|
||||
// Thus the TLB pointer should be considered as *read-only*, although
|
||||
// the contents of the TLB are free to be modified by the kernel software.
|
||||
|
||||
TranslationEntry *tlb; // this pointer should be considered
|
||||
// "read-only" to Nachos kernel code
|
||||
|
||||
TranslationEntry *currentPageTable;
|
||||
unsigned int currentPageTableSize;
|
||||
|
||||
private:
|
||||
bool singleStep; // drop back into the debugger after each
|
||||
// simulated instruction
|
||||
int runUntilTime; // drop back into the debugger when simulated
|
||||
// time reaches this value
|
||||
};
|
||||
|
||||
extern void ExceptionHandler(ExceptionType which);
|
||||
// Entry point into Nachos for handling
|
||||
// user system calls and exceptions
|
||||
// Defined in exception.cc
|
||||
|
||||
|
||||
// Routines for converting Words and Short Words to and from the
|
||||
// simulated machine's format of little endian. If the host machine
|
||||
// is little endian (DEC and Intel), these end up being NOPs.
|
||||
//
|
||||
// What is stored in each format:
|
||||
// host byte ordering:
|
||||
// kernel data structures
|
||||
// user registers
|
||||
// simulated machine byte ordering:
|
||||
// contents of main memory
|
||||
|
||||
unsigned int WordToHost(unsigned int word);
|
||||
unsigned short ShortToHost(unsigned short shortword);
|
||||
unsigned int WordToMachine(unsigned int word);
|
||||
unsigned short ShortToMachine(unsigned short shortword);
|
||||
|
||||
extern unsigned AddrSpacesRoom(unsigned blocksize);
|
||||
extern void DumpAddrSpaces(FILE *output,
|
||||
unsigned addr_x, unsigned sections_x, unsigned virtual_x, unsigned virtual_width,
|
||||
unsigned physical_x, unsigned y, unsigned blocksize);
|
||||
|
||||
#endif // MACHINE_H
|
703
code/machine/mipssim.cc
Normal file
703
code/machine/mipssim.cc
Normal file
|
@ -0,0 +1,703 @@
|
|||
// 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.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
Machine::Run()
|
||||
{
|
||||
// LB: Using a dynamic instr is right here as one never exits this
|
||||
// function.
|
||||
// Instruction *instr = new Instruction; // storage for decoded instruction
|
||||
Instruction the_instr;
|
||||
Instruction *instr = &the_instr;
|
||||
// End of Modification
|
||||
|
||||
if(DebugIsEnabled('m'))
|
||||
|
||||
// LB: Update the print format after the promotion of tick types
|
||||
// from int to long long
|
||||
// printf("Starting thread \"%s\" at time %d\n",
|
||||
// currentThread->getName(), stats->totalTicks);
|
||||
printf("Starting thread \"%s\" at %d and %d at time %lld\n",
|
||||
currentThread->getName(),
|
||||
machine->ReadRegister(PCReg),
|
||||
machine->ReadRegister(NextPCReg),
|
||||
stats->totalTicks);
|
||||
// End of correction
|
||||
|
||||
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.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
Machine::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;
|
||||
|
||||
unsigned tmp_unsigned;
|
||||
|
||||
// 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;
|
||||
/* FALLTHRU */
|
||||
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;
|
||||
/* FALLTHRU */
|
||||
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;
|
||||
/* FALLTHRU */
|
||||
case OP_J:
|
||||
pcAfter = (pcAfter & 0xf0000000) | IndexToAddr(instr->extra);
|
||||
break;
|
||||
|
||||
case OP_JALR:
|
||||
registers[instr->rd] = registers[NextPCReg] + 4;
|
||||
/* FALLTHRU */
|
||||
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_LWR:
|
||||
tmp = registers[instr->rs] + instr->extra;
|
||||
|
||||
// ReadMem assumes all 4 byte requests are aligned on an even
|
||||
// word boundary.
|
||||
|
||||
if (!machine->ReadMem(tmp & ~0x3, 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 & 0xff000000)
|
||||
| ((value >> 8) & 0xffffff);
|
||||
break;
|
||||
case 2:
|
||||
nextLoadValue = (nextLoadValue & 0xffff0000)
|
||||
| ((value >> 16) & 0xffff);
|
||||
break;
|
||||
case 3:
|
||||
nextLoadValue = (nextLoadValue & 0xffffff00)
|
||||
| ((value >> 24) & 0xff);
|
||||
break;
|
||||
}
|
||||
nextLoadReg = instr->rt;
|
||||
break;
|
||||
|
||||
case OP_LWL:
|
||||
tmp = registers[instr->rs] + instr->extra;
|
||||
|
||||
// ReadMem assumes all 4 byte requests are aligned on an even
|
||||
// word boundary.
|
||||
if (!machine->ReadMem(tmp & ~0x3, 4, &value))
|
||||
return;
|
||||
if (registers[LoadReg] == instr->rt)
|
||||
nextLoadValue = registers[LoadValueReg];
|
||||
else
|
||||
nextLoadValue = registers[instr->rt];
|
||||
switch (tmp & 0x3) {
|
||||
case 0:
|
||||
nextLoadValue = (nextLoadValue & 0xff) | (value << 8);
|
||||
break;
|
||||
case 1:
|
||||
nextLoadValue = (nextLoadValue & 0xffff) | (value << 16);
|
||||
break;
|
||||
case 2:
|
||||
nextLoadValue = (nextLoadValue & 0xffffff) | (value << 24);
|
||||
break;
|
||||
case 3:
|
||||
nextLoadValue = value;
|
||||
break;
|
||||
}
|
||||
nextLoadReg = instr->rt;
|
||||
break;
|
||||
|
||||
case OP_MFHI:
|
||||
registers[instr->rd] = registers[HiReg];
|
||||
break;
|
||||
|
||||
case OP_MFLO:
|
||||
registers[instr->rd] = registers[LoReg];
|
||||
break;
|
||||
|
||||
case OP_MTHI:
|
||||
registers[HiReg] = registers[instr->rs];
|
||||
break;
|
||||
|
||||
case OP_MTLO:
|
||||
registers[LoReg] = registers[instr->rs];
|
||||
break;
|
||||
|
||||
case OP_MULT:
|
||||
Mult(registers[instr->rs], registers[instr->rt], TRUE,
|
||||
®isters[HiReg], ®isters[LoReg]);
|
||||
break;
|
||||
|
||||
case OP_MULTU:
|
||||
Mult(registers[instr->rs], registers[instr->rt], FALSE,
|
||||
®isters[HiReg], ®isters[LoReg]);
|
||||
break;
|
||||
|
||||
case OP_NOR:
|
||||
registers[instr->rd] = ~(registers[instr->rs] | registers[instr->rt]);
|
||||
break;
|
||||
|
||||
case OP_OR:
|
||||
registers[instr->rd] = registers[instr->rs] | registers[instr->rt];
|
||||
break;
|
||||
|
||||
case OP_ORI:
|
||||
registers[instr->rt] = registers[instr->rs] | (instr->extra & 0xffff);
|
||||
break;
|
||||
|
||||
case OP_SB:
|
||||
if (!machine->WriteMem((unsigned)
|
||||
(registers[instr->rs] + instr->extra), 1, registers[instr->rt]))
|
||||
return;
|
||||
break;
|
||||
|
||||
case OP_SH:
|
||||
if (!machine->WriteMem((unsigned)
|
||||
(registers[instr->rs] + instr->extra), 2, registers[instr->rt]))
|
||||
return;
|
||||
break;
|
||||
|
||||
case OP_SLL:
|
||||
registers[instr->rd] = (int) (((unsigned) registers[instr->rt]) << instr->extra);
|
||||
break;
|
||||
|
||||
case OP_SLLV:
|
||||
registers[instr->rd] = (int) (((unsigned) registers[instr->rt]) <<
|
||||
(registers[instr->rs] & 0x1f));
|
||||
break;
|
||||
|
||||
case OP_SLT:
|
||||
if (registers[instr->rs] < registers[instr->rt])
|
||||
registers[instr->rd] = 1;
|
||||
else
|
||||
registers[instr->rd] = 0;
|
||||
break;
|
||||
|
||||
case OP_SLTI:
|
||||
if (registers[instr->rs] < (int) instr->extra)
|
||||
registers[instr->rt] = 1;
|
||||
else
|
||||
registers[instr->rt] = 0;
|
||||
break;
|
||||
|
||||
case OP_SLTIU:
|
||||
rs = registers[instr->rs];
|
||||
imm = instr->extra;
|
||||
if (rs < imm)
|
||||
registers[instr->rt] = 1;
|
||||
else
|
||||
registers[instr->rt] = 0;
|
||||
break;
|
||||
|
||||
case OP_SLTU:
|
||||
rs = registers[instr->rs];
|
||||
rt = registers[instr->rt];
|
||||
if (rs < rt)
|
||||
registers[instr->rd] = 1;
|
||||
else
|
||||
registers[instr->rd] = 0;
|
||||
break;
|
||||
|
||||
case OP_SRA:
|
||||
registers[instr->rd] = registers[instr->rt] >> instr->extra;
|
||||
break;
|
||||
|
||||
case OP_SRAV:
|
||||
registers[instr->rd] = registers[instr->rt] >>
|
||||
(registers[instr->rs] & 0x1f);
|
||||
break;
|
||||
|
||||
case OP_SRL:
|
||||
tmp_unsigned = registers[instr->rt];
|
||||
tmp_unsigned >>= instr->extra;
|
||||
registers[instr->rd] = tmp_unsigned;
|
||||
break;
|
||||
|
||||
case OP_SRLV:
|
||||
tmp_unsigned = registers[instr->rt];
|
||||
tmp_unsigned >>= (registers[instr->rs] & 0x1f);
|
||||
registers[instr->rd] = tmp_unsigned;
|
||||
|
||||
// End of correction
|
||||
//------------------------------------------------------------
|
||||
break;
|
||||
|
||||
case OP_SUB:
|
||||
diff = registers[instr->rs] - registers[instr->rt];
|
||||
if (((registers[instr->rs] ^ registers[instr->rt]) & SIGN_BIT) &&
|
||||
((registers[instr->rs] ^ diff) & SIGN_BIT)) {
|
||||
RaiseException(OverflowException, 0);
|
||||
return;
|
||||
}
|
||||
registers[instr->rd] = diff;
|
||||
break;
|
||||
|
||||
case OP_SUBU:
|
||||
registers[instr->rd] = registers[instr->rs] - registers[instr->rt];
|
||||
break;
|
||||
|
||||
case OP_SW:
|
||||
if (!machine->WriteMem((unsigned)
|
||||
(registers[instr->rs] + instr->extra), 4, registers[instr->rt]))
|
||||
return;
|
||||
break;
|
||||
|
||||
case OP_SWR:
|
||||
tmp = registers[instr->rs] + instr->extra;
|
||||
|
||||
if (!machine->ReadMem((tmp & ~0x3), 4, &value))
|
||||
return;
|
||||
switch (tmp & 0x3) {
|
||||
case 0:
|
||||
value = registers[instr->rt];
|
||||
break;
|
||||
case 1:
|
||||
value = (value & 0xff) | (registers[instr->rt] << 8);
|
||||
break;
|
||||
case 2:
|
||||
value = (value & 0xffff) | (registers[instr->rt] << 16);
|
||||
break;
|
||||
case 3:
|
||||
value = (value & 0xffffff) | (registers[instr->rt] << 24);
|
||||
break;
|
||||
}
|
||||
if (!machine->WriteMem((tmp & ~0x3), 4, value))
|
||||
return;
|
||||
break;
|
||||
|
||||
case OP_SWL:
|
||||
tmp = registers[instr->rs] + instr->extra;
|
||||
|
||||
if (!machine->ReadMem((tmp & ~0x3), 4, &value))
|
||||
return;
|
||||
switch (tmp & 0x3) {
|
||||
case 0:
|
||||
value = (value & 0xffffff00) | ((registers[instr->rt] >> 24) &
|
||||
0xff);
|
||||
break;
|
||||
case 1:
|
||||
value = (value & 0xffff0000) | ((registers[instr->rt] >> 16) &
|
||||
0xffff);
|
||||
break;
|
||||
case 2:
|
||||
value = (value & 0xff000000) | ((registers[instr->rt] >> 8) &
|
||||
0xffffff);
|
||||
break;
|
||||
case 3:
|
||||
value = registers[instr->rt];
|
||||
break;
|
||||
}
|
||||
if (!machine->WriteMem((tmp & ~0x3), 4, value))
|
||||
return;
|
||||
break;
|
||||
|
||||
case OP_SYSCALL:
|
||||
RaiseException(SyscallException, 0);
|
||||
return;
|
||||
|
||||
case OP_XOR:
|
||||
registers[instr->rd] = registers[instr->rs] ^ registers[instr->rt];
|
||||
break;
|
||||
|
||||
case OP_XORI:
|
||||
registers[instr->rt] = registers[instr->rs] ^ (instr->extra & 0xffff);
|
||||
break;
|
||||
|
||||
case OP_RES:
|
||||
case OP_UNIMP:
|
||||
RaiseException(IllegalInstrException, 0);
|
||||
return;
|
||||
|
||||
default:
|
||||
ASSERT(FALSE);
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Machine::DelayedLoad
|
||||
// Simulate effects of a delayed load.
|
||||
//
|
||||
// NOTE -- RaiseException/CheckInterrupts must also call DelayedLoad,
|
||||
// since any delayed load must get applied before we trap to the kernel.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
Machine::DelayedLoad(int nextReg, int nextValue)
|
||||
{
|
||||
registers[registers[LoadReg]] = registers[LoadValueReg];
|
||||
registers[LoadReg] = nextReg;
|
||||
registers[LoadValueReg] = nextValue;
|
||||
registers[0] = 0; // and always make sure R0 stays zero.
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Instruction::Decode
|
||||
// Decode a MIPS instruction
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
Instruction::Decode()
|
||||
{
|
||||
OpInfo *opPtr;
|
||||
|
||||
rs = (value >> 21) & 0x1f;
|
||||
rt = (value >> 16) & 0x1f;
|
||||
rd = (value >> 11) & 0x1f;
|
||||
opPtr = &opTable[(value >> 26) & 0x3f];
|
||||
opCode = opPtr->opCode;
|
||||
if (opPtr->format == IFMT) {
|
||||
extra = value & 0xffff;
|
||||
if (extra & 0x8000) {
|
||||
extra |= 0xffff0000;
|
||||
}
|
||||
} else if (opPtr->format == RFMT) {
|
||||
extra = (value >> 6) & 0x1f;
|
||||
} else {
|
||||
extra = value & 0x3ffffff;
|
||||
}
|
||||
if (opCode == SPECIAL) {
|
||||
opCode = specialTable[value & 0x3f];
|
||||
} else if (opCode == BCOND) {
|
||||
int i = value & 0x1f0000;
|
||||
|
||||
if (i == 0) {
|
||||
opCode = OP_BLTZ;
|
||||
} else if (i == 0x10000) {
|
||||
opCode = OP_BGEZ;
|
||||
} else if (i == 0x100000) {
|
||||
opCode = OP_BLTZAL;
|
||||
} else if (i == 0x110000) {
|
||||
opCode = OP_BGEZAL;
|
||||
} else {
|
||||
opCode = OP_UNIMP;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Mult
|
||||
// Simulate R2000 multiplication.
|
||||
// The words at *hiPtr and *loPtr are overwritten with the
|
||||
// double-length result of the multiplication.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
static void
|
||||
Mult(int a, int b, bool signedArith, int* hiPtr, int* loPtr)
|
||||
{
|
||||
if ((a == 0) || (b == 0)) {
|
||||
*hiPtr = *loPtr = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Compute the sign of the result, then make everything positive
|
||||
// so unsigned computation can be done in the main loop.
|
||||
bool negative = FALSE;
|
||||
if (signedArith) {
|
||||
if (a < 0) {
|
||||
negative = !negative;
|
||||
a = -a;
|
||||
}
|
||||
if (b < 0) {
|
||||
negative = !negative;
|
||||
b = -b;
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the result in unsigned arithmetic (check a's bits one at
|
||||
// a time, and add in a shifted value of b).
|
||||
unsigned int bLo = b;
|
||||
unsigned int bHi = 0;
|
||||
unsigned int lo = 0;
|
||||
unsigned int hi = 0;
|
||||
for (int i = 0; i < 32; i++) {
|
||||
if (a & 1) {
|
||||
lo += bLo;
|
||||
if (lo < bLo) // Carry out of the low bits?
|
||||
hi += 1;
|
||||
hi += bHi;
|
||||
if ((a & 0xfffffffe) == 0)
|
||||
break;
|
||||
}
|
||||
bHi <<= 1;
|
||||
if (bLo & 0x80000000)
|
||||
bHi |= 1;
|
||||
|
||||
bLo <<= 1;
|
||||
a >>= 1;
|
||||
}
|
||||
|
||||
// If the result is supposed to be negative, compute the two's
|
||||
// complement of the double-word result.
|
||||
if (negative) {
|
||||
hi = ~hi;
|
||||
lo = ~lo;
|
||||
lo++;
|
||||
if (lo == 0)
|
||||
hi++;
|
||||
}
|
||||
|
||||
*hiPtr = (int) hi;
|
||||
*loPtr = (int) lo;
|
||||
}
|
229
code/machine/mipssim.h
Normal file
229
code/machine/mipssim.h
Normal file
|
@ -0,0 +1,229 @@
|
|||
// mipssim.h
|
||||
// Internal data structures for simulating the MIPS instruction set.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef MIPSSIM_H
|
||||
#define MIPSSIM_H
|
||||
|
||||
#include "copyright.h"
|
||||
|
||||
/*
|
||||
* OpCode values. The names are straight from the MIPS
|
||||
* manual except for the following special ones:
|
||||
*
|
||||
* OP_UNIMP - means that this instruction is legal, but hasn't
|
||||
* been implemented in the simulator yet.
|
||||
* OP_RES - means that this is a reserved opcode (it isn't
|
||||
* supported by the architecture).
|
||||
*/
|
||||
|
||||
#define OP_ADD 1
|
||||
#define OP_ADDI 2
|
||||
#define OP_ADDIU 3
|
||||
#define OP_ADDU 4
|
||||
#define OP_AND 5
|
||||
#define OP_ANDI 6
|
||||
#define OP_BEQ 7
|
||||
#define OP_BGEZ 8
|
||||
#define OP_BGEZAL 9
|
||||
#define OP_BGTZ 10
|
||||
#define OP_BLEZ 11
|
||||
#define OP_BLTZ 12
|
||||
#define OP_BLTZAL 13
|
||||
#define OP_BNE 14
|
||||
|
||||
#define OP_DIV 16
|
||||
#define OP_DIVU 17
|
||||
#define OP_J 18
|
||||
#define OP_JAL 19
|
||||
#define OP_JALR 20
|
||||
#define OP_JR 21
|
||||
#define OP_LB 22
|
||||
#define OP_LBU 23
|
||||
#define OP_LH 24
|
||||
#define OP_LHU 25
|
||||
#define OP_LUI 26
|
||||
#define OP_LW 27
|
||||
#define OP_LWL 28
|
||||
#define OP_LWR 29
|
||||
|
||||
#define OP_MFHI 31
|
||||
#define OP_MFLO 32
|
||||
|
||||
#define OP_MTHI 34
|
||||
#define OP_MTLO 35
|
||||
#define OP_MULT 36
|
||||
#define OP_MULTU 37
|
||||
#define OP_NOR 38
|
||||
#define OP_OR 39
|
||||
#define OP_ORI 40
|
||||
#define OP_RFE 41
|
||||
#define OP_SB 42
|
||||
#define OP_SH 43
|
||||
#define OP_SLL 44
|
||||
#define OP_SLLV 45
|
||||
#define OP_SLT 46
|
||||
#define OP_SLTI 47
|
||||
#define OP_SLTIU 48
|
||||
#define OP_SLTU 49
|
||||
#define OP_SRA 50
|
||||
#define OP_SRAV 51
|
||||
#define OP_SRL 52
|
||||
#define OP_SRLV 53
|
||||
#define OP_SUB 54
|
||||
#define OP_SUBU 55
|
||||
#define OP_SW 56
|
||||
#define OP_SWL 57
|
||||
#define OP_SWR 58
|
||||
#define OP_XOR 59
|
||||
#define OP_XORI 60
|
||||
#define OP_SYSCALL 61
|
||||
#define OP_UNIMP 62
|
||||
#define OP_RES 63
|
||||
#define MaxOpcode 63
|
||||
|
||||
/*
|
||||
* Miscellaneous definitions:
|
||||
*/
|
||||
|
||||
#define IndexToAddr(x) ((x) << 2)
|
||||
|
||||
#define SIGN_BIT 0x80000000
|
||||
#define R31 31
|
||||
|
||||
/*
|
||||
* The table below is used to translate bits 31:26 of the instruction
|
||||
* into a value suitable for the "opCode" field of a MemWord structure,
|
||||
* or into a special value for further decoding.
|
||||
*/
|
||||
|
||||
#define SPECIAL 100
|
||||
#define BCOND 101
|
||||
|
||||
#define IFMT 1
|
||||
#define JFMT 2
|
||||
#define RFMT 3
|
||||
|
||||
struct OpInfo {
|
||||
int opCode; /* Translated op code. */
|
||||
int format; /* Format type (IFMT or JFMT or RFMT) */
|
||||
};
|
||||
|
||||
static OpInfo opTable[] = {
|
||||
{SPECIAL, RFMT}, {BCOND, IFMT}, {OP_J, JFMT}, {OP_JAL, JFMT},
|
||||
{OP_BEQ, IFMT}, {OP_BNE, IFMT}, {OP_BLEZ, IFMT}, {OP_BGTZ, IFMT},
|
||||
{OP_ADDI, IFMT}, {OP_ADDIU, IFMT}, {OP_SLTI, IFMT}, {OP_SLTIU, IFMT},
|
||||
{OP_ANDI, IFMT}, {OP_ORI, IFMT}, {OP_XORI, IFMT}, {OP_LUI, IFMT},
|
||||
{OP_UNIMP, IFMT}, {OP_UNIMP, IFMT}, {OP_UNIMP, IFMT}, {OP_UNIMP, IFMT},
|
||||
{OP_RES, IFMT}, {OP_RES, IFMT}, {OP_RES, IFMT}, {OP_RES, IFMT},
|
||||
{OP_RES, IFMT}, {OP_RES, IFMT}, {OP_RES, IFMT}, {OP_RES, IFMT},
|
||||
{OP_RES, IFMT}, {OP_RES, IFMT}, {OP_RES, IFMT}, {OP_RES, IFMT},
|
||||
{OP_LB, IFMT}, {OP_LH, IFMT}, {OP_LWL, IFMT}, {OP_LW, IFMT},
|
||||
{OP_LBU, IFMT}, {OP_LHU, IFMT}, {OP_LWR, IFMT}, {OP_RES, IFMT},
|
||||
{OP_SB, IFMT}, {OP_SH, IFMT}, {OP_SWL, IFMT}, {OP_SW, IFMT},
|
||||
{OP_RES, IFMT}, {OP_RES, IFMT}, {OP_SWR, IFMT}, {OP_RES, IFMT},
|
||||
{OP_UNIMP, IFMT}, {OP_UNIMP, IFMT}, {OP_UNIMP, IFMT}, {OP_UNIMP, IFMT},
|
||||
{OP_RES, IFMT}, {OP_RES, IFMT}, {OP_RES, IFMT}, {OP_RES, IFMT},
|
||||
{OP_UNIMP, IFMT}, {OP_UNIMP, IFMT}, {OP_UNIMP, IFMT}, {OP_UNIMP, IFMT},
|
||||
{OP_RES, IFMT}, {OP_RES, IFMT}, {OP_RES, IFMT}, {OP_RES, IFMT}
|
||||
};
|
||||
|
||||
/*
|
||||
* The table below is used to convert the "funct" field of SPECIAL
|
||||
* instructions into the "opCode" field of a MemWord.
|
||||
*/
|
||||
|
||||
static int specialTable[] = {
|
||||
OP_SLL, OP_RES, OP_SRL, OP_SRA, OP_SLLV, OP_RES, OP_SRLV, OP_SRAV,
|
||||
OP_JR, OP_JALR, OP_RES, OP_RES, OP_SYSCALL, OP_UNIMP, OP_RES, OP_RES,
|
||||
OP_MFHI, OP_MTHI, OP_MFLO, OP_MTLO, OP_RES, OP_RES, OP_RES, OP_RES,
|
||||
OP_MULT, OP_MULTU, OP_DIV, OP_DIVU, OP_RES, OP_RES, OP_RES, OP_RES,
|
||||
OP_ADD, OP_ADDU, OP_SUB, OP_SUBU, OP_AND, OP_OR, OP_XOR, OP_NOR,
|
||||
OP_RES, OP_RES, OP_SLT, OP_SLTU, OP_RES, OP_RES, OP_RES, OP_RES,
|
||||
OP_RES, OP_RES, OP_RES, OP_RES, OP_RES, OP_RES, OP_RES, OP_RES,
|
||||
OP_RES, OP_RES, OP_RES, OP_RES, OP_RES, OP_RES, OP_RES, OP_RES
|
||||
};
|
||||
|
||||
|
||||
// Stuff to help print out each instruction, for debugging
|
||||
|
||||
enum RegType { NONE, RS, RT, RD, EXTRA };
|
||||
|
||||
struct OpString {
|
||||
const char *string; // Printed version of instruction
|
||||
RegType args[3];
|
||||
};
|
||||
|
||||
static struct OpString opStrings[] = {
|
||||
{"Shouldn't happen", {NONE, NONE, NONE}},
|
||||
{"ADD r%d,r%d,r%d", {RD, RS, RT}},
|
||||
{"ADDI r%d,r%d,%d", {RT, RS, EXTRA}},
|
||||
{"ADDIU r%d,r%d,%d", {RT, RS, EXTRA}},
|
||||
{"ADDU r%d,r%d,r%d", {RD, RS, RT}},
|
||||
{"AND r%d,r%d,r%d", {RD, RS, RT}},
|
||||
{"ANDI r%d,r%d,%d", {RT, RS, EXTRA}},
|
||||
{"BEQ r%d,r%d,%d", {RS, RT, EXTRA}},
|
||||
{"BGEZ r%d,%d", {RS, EXTRA, NONE}},
|
||||
{"BGEZAL r%d,%d", {RS, EXTRA, NONE}},
|
||||
{"BGTZ r%d,%d", {RS, EXTRA, NONE}},
|
||||
{"BLEZ r%d,%d", {RS, EXTRA, NONE}},
|
||||
{"BLTZ r%d,%d", {RS, EXTRA, NONE}},
|
||||
{"BLTZAL r%d,%d", {RS, EXTRA, NONE}},
|
||||
{"BNE r%d,r%d,%d", {RS, RT, EXTRA}},
|
||||
{"Shouldn't happen", {NONE, NONE, NONE}},
|
||||
{"DIV r%d,r%d", {RS, RT, NONE}},
|
||||
{"DIVU r%d,r%d", {RS, RT, NONE}},
|
||||
{"J 4*%d", {EXTRA, NONE, NONE}},
|
||||
{"JAL 4*%d", {EXTRA, NONE, NONE}},
|
||||
{"JALR r%d,r%d", {RD, RS, NONE}},
|
||||
{"JR r%d,r%d", {RD, RS, NONE}},
|
||||
{"LB r%d,%d(r%d)", {RT, EXTRA, RS}},
|
||||
{"LBU r%d,%d(r%d)", {RT, EXTRA, RS}},
|
||||
{"LH r%d,%d(r%d)", {RT, EXTRA, RS}},
|
||||
{"LHU r%d,%d(r%d)", {RT, EXTRA, RS}},
|
||||
{"LUI r%d,%d", {RT, EXTRA, NONE}},
|
||||
{"LW r%d,%d(r%d)", {RT, EXTRA, RS}},
|
||||
{"LWL r%d,%d(r%d)", {RT, EXTRA, RS}},
|
||||
{"LWR r%d,%d(r%d)", {RT, EXTRA, RS}},
|
||||
{"Shouldn't happen", {NONE, NONE, NONE}},
|
||||
{"MFHI r%d", {RD, NONE, NONE}},
|
||||
{"MFLO r%d", {RD, NONE, NONE}},
|
||||
{"Shouldn't happen", {NONE, NONE, NONE}},
|
||||
{"MTHI r%d", {RS, NONE, NONE}},
|
||||
{"MTLO r%d", {RS, NONE, NONE}},
|
||||
{"MULT r%d,r%d", {RS, RT, NONE}},
|
||||
{"MULTU r%d,r%d", {RS, RT, NONE}},
|
||||
{"NOR r%d,r%d,r%d", {RD, RS, RT}},
|
||||
{"OR r%d,r%d,r%d", {RD, RS, RT}},
|
||||
{"ORI r%d,r%d,%d", {RT, RS, EXTRA}},
|
||||
{"RFE", {NONE, NONE, NONE}},
|
||||
{"SB r%d,%d(r%d)", {RT, EXTRA, RS}},
|
||||
{"SH r%d,%d(r%d)", {RT, EXTRA, RS}},
|
||||
{"SLL r%d,r%d,%d", {RD, RT, EXTRA}},
|
||||
{"SLLV r%d,r%d,r%d", {RD, RT, RS}},
|
||||
{"SLT r%d,r%d,r%d", {RD, RS, RT}},
|
||||
{"SLTI r%d,r%d,%d", {RT, RS, EXTRA}},
|
||||
{"SLTIU r%d,r%d,%d", {RT, RS, EXTRA}},
|
||||
{"SLTU r%d,r%d,r%d", {RD, RS, RT}},
|
||||
{"SRA r%d,r%d,%d", {RD, RT, EXTRA}},
|
||||
{"SRAV r%d,r%d,r%d", {RD, RT, RS}},
|
||||
{"SRL r%d,r%d,%d", {RD, RT, EXTRA}},
|
||||
{"SRLV r%d,r%d,r%d", {RD, RT, RS}},
|
||||
{"SUB r%d,r%d,r%d", {RD, RS, RT}},
|
||||
{"SUBU r%d,r%d,r%d", {RD, RS, RT}},
|
||||
{"SW r%d,%d(r%d)", {RT, EXTRA, RS}},
|
||||
{"SWL r%d,%d(r%d)", {RT, EXTRA, RS}},
|
||||
{"SWR r%d,%d(r%d)", {RT, EXTRA, RS}},
|
||||
{"XOR r%d,r%d,r%d", {RD, RS, RT}},
|
||||
{"XORI r%d,r%d,%d", {RT, RS, EXTRA}},
|
||||
{"SYSCALL", {NONE, NONE, NONE}},
|
||||
{"Unimplemented", {NONE, NONE, NONE}},
|
||||
{"Reserved", {NONE, NONE, NONE}}
|
||||
};
|
||||
|
||||
#endif // MIPSSIM_H
|
139
code/machine/network.cc
Normal file
139
code/machine/network.cc
Normal file
|
@ -0,0 +1,139 @@
|
|||
// network.cc
|
||||
// Routines to simulate a network interface, using UNIX sockets
|
||||
// to deliver packets between multiple invocations of nachos.
|
||||
//
|
||||
// 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 "system.h"
|
||||
|
||||
#include <strings.h> /* for bzero */
|
||||
|
||||
// Dummy functions because C++ can't call member functions indirectly
|
||||
static void NetworkReadPoll(void *arg)
|
||||
{ Network *net = (Network *)arg; net->CheckPktAvail(); }
|
||||
static void NetworkSendDone(void *arg)
|
||||
{ Network *net = (Network *)arg; net->SendDone(); }
|
||||
|
||||
// Initialize the network emulation
|
||||
// addr is used to generate the socket name
|
||||
// reliability says whether we drop packets to emulate unreliable links
|
||||
// readAvailHandler, writeDoneHandler, callArg -- analogous to console
|
||||
Network::Network(NetworkAddress addr, double reliability,
|
||||
VoidFunctionPtr readAvailHandler, VoidFunctionPtr writeDoneHandler, void *callArg)
|
||||
{
|
||||
ident = addr;
|
||||
if (reliability < 0) chanceToWork = 0;
|
||||
else if (reliability > 1) chanceToWork = 1;
|
||||
else chanceToWork = reliability;
|
||||
|
||||
// set up the stuff to emulate asynchronous interrupts
|
||||
writeHandler = writeDoneHandler;
|
||||
readHandler = readAvailHandler;
|
||||
handlerArg = callArg;
|
||||
sendBusy = FALSE;
|
||||
inHdr.length = 0;
|
||||
|
||||
sock = OpenSocket();
|
||||
sprintf(sockName, "SOCKET_%d", (int)addr);
|
||||
AssignNameToSocket(sockName, sock); // Bind socket to a filename
|
||||
// in the current directory.
|
||||
|
||||
// start polling for incoming packets
|
||||
interrupt->Schedule(NetworkReadPoll, this, NetworkTime, NetworkRecvInt);
|
||||
}
|
||||
|
||||
Network::~Network()
|
||||
{
|
||||
CloseSocket(sock);
|
||||
sock = -1;
|
||||
DeAssignNameToSocket(sockName);
|
||||
}
|
||||
|
||||
// if a packet is already buffered, we simply delay reading
|
||||
// the incoming packet. In real life, the incoming
|
||||
// packet might be dropped if we can't read it in time.
|
||||
void
|
||||
Network::CheckPktAvail()
|
||||
{
|
||||
// schedule the next time to poll for a packet
|
||||
interrupt->Schedule(NetworkReadPoll, this, NetworkTime, NetworkRecvInt);
|
||||
|
||||
if (inHdr.length != 0) // do nothing if packet is already buffered
|
||||
return;
|
||||
if (!PollSocket(sock)) // do nothing if no packet to be read
|
||||
return;
|
||||
|
||||
// otherwise, read packet in
|
||||
char *buffer = new char[MaxWireSize];
|
||||
ReadFromSocket(sock, buffer, MaxWireSize);
|
||||
|
||||
// divide packet into header and data
|
||||
inHdr = *(PacketHeader *)buffer;
|
||||
ASSERT((inHdr.to == ident) && (inHdr.length <= MaxPacketSize));
|
||||
bcopy(buffer + sizeof(PacketHeader), inbox, inHdr.length);
|
||||
delete []buffer ;
|
||||
|
||||
DEBUG('n', "Network received packet from %d, length %d...\n",
|
||||
(int) inHdr.from, inHdr.length);
|
||||
stats->numPacketsRecvd++;
|
||||
|
||||
// tell post office that the packet has arrived
|
||||
(*readHandler)(handlerArg);
|
||||
}
|
||||
|
||||
// notify user that another packet can be sent
|
||||
void
|
||||
Network::SendDone()
|
||||
{
|
||||
sendBusy = FALSE;
|
||||
stats->numPacketsSent++;
|
||||
(*writeHandler)(handlerArg);
|
||||
}
|
||||
|
||||
// send a packet by concatenating hdr and data, and schedule
|
||||
// an interrupt to tell the user when the next packet can be sent
|
||||
//
|
||||
// Note we always pad out a packet to MaxWireSize before putting it into
|
||||
// the socket, because it's simpler at the receive end.
|
||||
void
|
||||
Network::Send(PacketHeader hdr, const void* data)
|
||||
{
|
||||
char toName[32];
|
||||
|
||||
sprintf(toName, "SOCKET_%d", (int)hdr.to);
|
||||
|
||||
ASSERT((sendBusy == FALSE) && (hdr.length > 0)
|
||||
&& (hdr.length <= MaxPacketSize) && (hdr.from == ident));
|
||||
DEBUG('n', "Sending to addr %d, %d bytes... ", hdr.to, hdr.length);
|
||||
|
||||
interrupt->Schedule(NetworkSendDone, this, NetworkTime, NetworkSendInt);
|
||||
|
||||
if (Random() % 100 >= chanceToWork * 100) { // emulate a lost packet
|
||||
DEBUG('n', "oops, lost it!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// concatenate hdr and data into a single buffer, and send it out
|
||||
char *buffer = new char[MaxWireSize];
|
||||
*(PacketHeader *)buffer = hdr;
|
||||
bcopy(data, buffer + sizeof(PacketHeader), hdr.length);
|
||||
SendToSocket(sock, buffer, MaxWireSize, toName);
|
||||
delete []buffer;
|
||||
}
|
||||
|
||||
// read a packet, if one is buffered
|
||||
PacketHeader
|
||||
Network::Receive(void* data)
|
||||
{
|
||||
PacketHeader hdr = inHdr;
|
||||
|
||||
inHdr.length = 0;
|
||||
if (hdr.length != 0)
|
||||
bcopy(inbox, data, hdr.length);
|
||||
return hdr;
|
||||
}
|
101
code/machine/network.h
Normal file
101
code/machine/network.h
Normal file
|
@ -0,0 +1,101 @@
|
|||
// network.h
|
||||
// Data structures to emulate a physical network connection.
|
||||
// The network provides the abstraction of ordered, unreliable,
|
||||
// fixed-size packet delivery to other machines on the network.
|
||||
//
|
||||
// You may note that the interface to the network is similar to
|
||||
// the console device -- both are full duplex channels.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef NETWORK_H
|
||||
#define NETWORK_H
|
||||
|
||||
#include "copyright.h"
|
||||
#include "utility.h"
|
||||
|
||||
// Network address -- uniquely identifies a machine. This machine's ID
|
||||
// is given on the command line.
|
||||
typedef int NetworkAddress;
|
||||
|
||||
// The following class defines the network packet header.
|
||||
// The packet header is prepended to the data payload by the Network driver,
|
||||
// before the packet is sent over the wire. The format on the wire is:
|
||||
// packet header (PacketHeader)
|
||||
// data (containing MailHeader from the PostOffice!)
|
||||
|
||||
class PacketHeader {
|
||||
public:
|
||||
NetworkAddress to; // Destination machine ID
|
||||
NetworkAddress from; // source machine ID
|
||||
unsigned length; // bytes of packet data, excluding the
|
||||
// packet header (but including the
|
||||
// MailHeader prepended by the post office)
|
||||
};
|
||||
|
||||
#define MaxWireSize 64 // largest packet that can go out on the wire
|
||||
#define MaxPacketSize (MaxWireSize - sizeof(struct PacketHeader))
|
||||
// data "payload" of the largest packet
|
||||
|
||||
|
||||
// The following class defines a physical network device. The network
|
||||
// is capable of delivering fixed sized packets, in order but unreliably,
|
||||
// to other machines connected to the network.
|
||||
//
|
||||
// The "reliability" of the network can be specified to the constructor.
|
||||
// This number, between 0 and 1, is the chance that the network will lose
|
||||
// a packet. Note that you can change the seed for the random number
|
||||
// generator, by changing the arguments to RandomInit() in Initialize().
|
||||
// The random number generator is used to choose which packets to drop.
|
||||
|
||||
class Network {
|
||||
public:
|
||||
Network(NetworkAddress addr, double reliability,
|
||||
VoidFunctionPtr readAvailHandler, VoidFunctionPtr writeDoneHandler, void *callArg);
|
||||
// Allocate and initialize network driver
|
||||
~Network(); // De-allocate the network driver data
|
||||
|
||||
void Send(PacketHeader hdr, const void* data);
|
||||
// Send the packet data to a remote machine,
|
||||
// specified by "hdr". Returns immediately.
|
||||
// "writeHandler" is invoked once the next
|
||||
// packet can be sent. Note that writeHandler
|
||||
// is called whether or not the packet is
|
||||
// dropped, and note that the "from" field of
|
||||
// the PacketHeader is filled in automatically
|
||||
// by Send().
|
||||
|
||||
PacketHeader Receive(void* data);
|
||||
// Poll the network for incoming messages.
|
||||
// If there is a packet waiting, copy the
|
||||
// packet into "data" and return the header.
|
||||
// If no packet is waiting, return a header
|
||||
// with length 0.
|
||||
|
||||
void SendDone(); // Interrupt handler, called when message is
|
||||
// sent
|
||||
void CheckPktAvail(); // Check if there is an incoming packet
|
||||
|
||||
private:
|
||||
NetworkAddress ident; // This machine's network address
|
||||
double chanceToWork; // Likelihood packet will be dropped
|
||||
int sock; // UNIX socket number for incoming packets
|
||||
char sockName[32]; // File name corresponding to UNIX socket
|
||||
VoidFunctionPtr writeHandler; // Interrupt handler, signalling next packet
|
||||
// can be sent.
|
||||
VoidFunctionPtr readHandler; // Interrupt handler, signalling packet has
|
||||
// arrived.
|
||||
void *handlerArg; // Argument to be passed to interrupt handler
|
||||
// (pointer to post office)
|
||||
bool sendBusy; // Packet is being sent.
|
||||
bool packetAvail; // Packet has arrived, can be pulled off of
|
||||
// network
|
||||
PacketHeader inHdr; // Information about arrived packet
|
||||
char inbox[MaxPacketSize]; // Data for arrived packet
|
||||
};
|
||||
|
||||
#endif // NETWORK_H
|
49
code/machine/stats.cc
Normal file
49
code/machine/stats.cc
Normal file
|
@ -0,0 +1,49 @@
|
|||
// stats.h
|
||||
// Routines for managing statistics about Nachos performance.
|
||||
//
|
||||
// DO NOT CHANGE -- these stats are maintained by 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 "utility.h"
|
||||
#include "stats.h"
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Statistics::Statistics
|
||||
// Initialize performance metrics to zero, at system startup.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
Statistics::Statistics()
|
||||
{
|
||||
totalTicks = idleTicks = systemTicks = userTicks = 0;
|
||||
numDiskReads = numDiskWrites = 0;
|
||||
numConsoleCharsRead = numConsoleCharsWritten = 0;
|
||||
numPageFaults = numPacketsSent = numPacketsRecvd = 0;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Statistics::Print
|
||||
// Print performance metrics, when we've finished everything
|
||||
// at system shutdown.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
Statistics::Print()
|
||||
{
|
||||
// LB: format adapted to long long tick type
|
||||
// printf("Ticks: total %d, idle %d, system %d, user %d\n", totalTicks,
|
||||
// idleTicks, systemTicks, userTicks);
|
||||
printf("Ticks: total %lld, idle %lld, system %lld, user %lld\n",
|
||||
totalTicks, idleTicks, systemTicks, userTicks);
|
||||
// End of correction
|
||||
|
||||
printf("Disk I/O: reads %d, writes %d\n", numDiskReads, numDiskWrites);
|
||||
printf("Console I/O: reads %d, writes %d\n", numConsoleCharsRead,
|
||||
numConsoleCharsWritten);
|
||||
printf("Paging: faults %d\n", numPageFaults);
|
||||
printf("Network I/O: packets received %d, sent %d\n", numPacketsRecvd,
|
||||
numPacketsSent);
|
||||
}
|
70
code/machine/stats.h
Normal file
70
code/machine/stats.h
Normal file
|
@ -0,0 +1,70 @@
|
|||
// stats.h
|
||||
// Data structures for gathering statistics about Nachos performance.
|
||||
//
|
||||
// DO NOT CHANGE -- these stats are maintained by 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.
|
||||
|
||||
#ifndef STATS_H
|
||||
#define STATS_H
|
||||
|
||||
#include "copyright.h"
|
||||
|
||||
// The following class defines the statistics that are to be kept
|
||||
// about Nachos behavior -- how much time (ticks) elapsed, how
|
||||
// many user instructions executed, etc.
|
||||
//
|
||||
// The fields in this class are public to make it easier to update.
|
||||
|
||||
class Statistics {
|
||||
public:
|
||||
// LB: type of ticks promoted from 32 bit int to 64 bit long long
|
||||
// to cope with long runs
|
||||
// int totalTicks; // Total time running Nachos
|
||||
// int idleTicks; // Time spent idle (no threads to run)
|
||||
// int systemTicks; // Time spent executing system code
|
||||
// int userTicks; // Time spent executing user code
|
||||
// (this is also equal to # of
|
||||
// user instructions executed)
|
||||
long long totalTicks; // Total time running Nachos
|
||||
long long idleTicks; // Time spent idle (no threads to run)
|
||||
long long systemTicks; // Time spent executing system code
|
||||
long long userTicks; // Time spent executing user code
|
||||
// (this is also equal to # of
|
||||
// user instructions executed)
|
||||
// End of correction
|
||||
|
||||
|
||||
int numDiskReads; // number of disk read requests
|
||||
int numDiskWrites; // number of disk write requests
|
||||
int numConsoleCharsRead; // number of characters read from the keyboard
|
||||
int numConsoleCharsWritten; // number of characters written to the display
|
||||
int numPageFaults; // number of virtual memory page faults
|
||||
int numPacketsSent; // number of packets sent over the network
|
||||
int numPacketsRecvd; // number of packets received over the network
|
||||
|
||||
Statistics(); // initialize everything to zero
|
||||
|
||||
void Print(); // print collected statistics
|
||||
};
|
||||
|
||||
// Constants used to reflect the relative time an operation would
|
||||
// take in a real system. A "tick" is a just a unit of time -- if you
|
||||
// like, a microsecond.
|
||||
//
|
||||
// Since Nachos kernel code is directly executed, and the time spent
|
||||
// in the kernel measured by the number of calls to enable interrupts,
|
||||
// these time constants are none too exact.
|
||||
|
||||
#define UserTick 1 // advance for each user-level instruction
|
||||
#define SystemTick 10 // advance each time interrupts are enabled
|
||||
#define RotationTime 500 // time disk takes to rotate one sector
|
||||
#define SeekTime 500 // time disk takes to seek past one track
|
||||
#define ConsoleTime 100 // time to read or write one character
|
||||
#define NetworkTime 100 // time to send or receive one packet
|
||||
#define TimerTicks 100 // (average) time between timer interrupts
|
||||
|
||||
#endif // STATS_H
|
537
code/machine/sysdep.cc
Normal file
537
code/machine/sysdep.cc
Normal file
|
@ -0,0 +1,537 @@
|
|||
// sysdep.cc
|
||||
// Implementation of system-dependent interface. Nachos uses the
|
||||
// routines defined here, rather than directly calling the UNIX library,
|
||||
// to simplify porting between versions of UNIX, and even to
|
||||
// other systems, such as MSDOS.
|
||||
//
|
||||
// On UNIX, almost all of these routines are simple wrappers
|
||||
// for the underlying UNIX system calls.
|
||||
//
|
||||
// NOTE: all of these routines refer to operations on the underlying
|
||||
// host machine (e.g., the DECstation, SPARC, etc.), supporting the
|
||||
// Nachos simulation code. Nachos implements similar operations,
|
||||
// (such as opening a file), but those are implemented in terms
|
||||
// of hardware devices, which are simulated by calls to the underlying
|
||||
// routines in the host workstation OS.
|
||||
//
|
||||
// This file includes lots of calls to C routines. C++ requires
|
||||
// us to wrap all C definitions with a "extern "C" block".
|
||||
// This prevents the internal forms of the names from being
|
||||
// changed by the C++ compiler.
|
||||
//
|
||||
// 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"
|
||||
|
||||
extern "C" {
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
// UNIX routines called by procedures in this file
|
||||
|
||||
#ifdef HOST_SNAKE
|
||||
// int creat(char *name, unsigned short mode);
|
||||
// int open(const char *name, int flags, ...);
|
||||
#else
|
||||
#if !defined(SOLARIS) && !defined(LINUX) && !defined(MAC_OS)
|
||||
int creat(const char *name, unsigned short mode);
|
||||
int open(const char *name, int flags, ...);
|
||||
// void signal(int sig, VoidFunctionPtr func); -- this may work now!
|
||||
#ifdef HOST_i386
|
||||
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
|
||||
struct timeval *timeout);
|
||||
#else
|
||||
int select(int numBits, void *readFds, void *writeFds, void *exceptFds,
|
||||
struct timeval *timeout);
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(SOLARIS) && !defined(LINUX) && !defined(MAC_OS)
|
||||
int unlink(char *name);
|
||||
int read(int filedes, char *buf, int numBytes);
|
||||
int write(int filedes, char *buf, int numBytes);
|
||||
int lseek(int filedes, int offset, int whence);
|
||||
int tell(int filedes);
|
||||
int close(int filedes);
|
||||
int unlink(char *name);
|
||||
|
||||
// definition varies slightly from platform to platform, so don't
|
||||
// define unless gcc complains
|
||||
// extern int recvfrom(int s, void *buf, int len, int flags, void *from, int *fromlen);
|
||||
// extern int sendto(int s, void *msg, int len, int flags, void *to, int tolen);
|
||||
|
||||
|
||||
void srand(unsigned seed);
|
||||
int rand(void);
|
||||
unsigned sleep(unsigned);
|
||||
void abort();
|
||||
void exit();
|
||||
int mprotect(char *addr, int len, int prot);
|
||||
|
||||
int socket(int, int, int);
|
||||
int bind (int, const void*, int);
|
||||
int recvfrom (int, void*, int, int, void*, int *);
|
||||
int sendto (int, const void*, int, int, void*, int);
|
||||
#endif
|
||||
}
|
||||
|
||||
#include "interrupt.h"
|
||||
#include "system.h"
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// PollFile
|
||||
// Check open file or open socket to see if there are any
|
||||
// characters that can be read immediately. If so, read them
|
||||
// in, and return TRUE.
|
||||
//
|
||||
// In the network case, if there are no threads for us to run,
|
||||
// and no characters to be read,
|
||||
// we need to give the other side a chance to get our host's CPU
|
||||
// (otherwise, we'll go really slowly, since UNIX time-slices
|
||||
// infrequently, and this would be like busy-waiting). So we
|
||||
// delay for a short fixed time, before allowing ourselves to be
|
||||
// re-scheduled (sort of like a Yield, but cast in terms of UNIX).
|
||||
//
|
||||
// "fd" -- the file descriptor of the file to be polled
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
bool
|
||||
PollFile(int fd)
|
||||
{
|
||||
#if defined(SOLARIS) || defined(LINUX) || defined(MAC_OS)
|
||||
fd_set rfd;
|
||||
int retVal;
|
||||
#else
|
||||
int rfd = (1 << fd), wfd = 0, xfd = 0, retVal;
|
||||
#endif
|
||||
struct timeval pollTime;
|
||||
|
||||
// decide how long to wait if there are no characters on the file
|
||||
pollTime.tv_sec = 0;
|
||||
if (interrupt->getStatus() == IdleMode)
|
||||
pollTime.tv_usec = 20000; // delay to let other nachos run
|
||||
else
|
||||
pollTime.tv_usec = 0; // no delay
|
||||
|
||||
// poll file or socket
|
||||
#if defined(SOLARIS) || defined(LINUX) || defined(MAC_OS)
|
||||
FD_ZERO(&rfd);
|
||||
FD_SET(fd, &rfd);
|
||||
retVal = select(fd + 1, &rfd, NULL, NULL, &pollTime);
|
||||
#else
|
||||
retVal = select(32, &rfd, &wfd, &xfd, &pollTime);
|
||||
#endif
|
||||
|
||||
ASSERT((retVal == 0) || (retVal == 1));
|
||||
if (retVal == 0)
|
||||
return FALSE; // no char waiting to be read
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// OpenForWrite
|
||||
// Open a file for writing. Create it if it doesn't exist; truncate it
|
||||
// if it does already exist. Return the file descriptor.
|
||||
//
|
||||
// "name" -- file name
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
int
|
||||
OpenForWrite(const char *name)
|
||||
{
|
||||
int fd = open(name, O_RDWR|O_CREAT|O_TRUNC, 0666);
|
||||
|
||||
ASSERT(fd >= 0);
|
||||
return fd;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// OpenForReadWrite
|
||||
// Open a file for reading or writing.
|
||||
// Return the file descriptor, or error if it doesn't exist.
|
||||
//
|
||||
// "name" -- file name
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
int
|
||||
OpenForReadWrite(const char *name, bool crashOnError)
|
||||
{
|
||||
int fd = open(name, O_RDWR, 0);
|
||||
|
||||
ASSERT(!crashOnError || fd >= 0);
|
||||
return fd;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Read
|
||||
// Read characters from an open file. Abort if read fails.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
Read(int fd, void *buffer, int nBytes)
|
||||
{
|
||||
int retVal = read(fd, buffer, nBytes);
|
||||
ASSERT(retVal == nBytes);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// ReadPartial
|
||||
// Read characters from an open file, returning as many as are
|
||||
// available.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
int
|
||||
ReadPartial(int fd, void *buffer, int nBytes)
|
||||
{
|
||||
return read(fd, buffer, nBytes);
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// WriteFile
|
||||
// Write characters to an open file. Abort if write fails.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
WriteFile(int fd, const void *buffer, int nBytes)
|
||||
{
|
||||
int retVal = write(fd, buffer, nBytes);
|
||||
ASSERT(retVal == nBytes);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Lseek
|
||||
// Change the location within an open file. Abort on error.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
Lseek(int fd, int offset, int whence)
|
||||
{
|
||||
int retVal = lseek(fd, offset, whence);
|
||||
ASSERT(retVal >= 0);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Tell
|
||||
// Report the current location within an open file.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
int
|
||||
Tell(int fd)
|
||||
{
|
||||
#if defined(SOLARIS) || defined(LINUX) || defined(MAC_OS)
|
||||
return lseek(fd,0,SEEK_CUR); // 386BSD doesn't have the tell() system call
|
||||
#else
|
||||
return tell(fd);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Close
|
||||
// Close a file. Abort on error.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
Close(int fd)
|
||||
{
|
||||
int retVal = close(fd);
|
||||
ASSERT(retVal >= 0);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Unlink
|
||||
// Delete a file.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
bool
|
||||
Unlink(const char *name)
|
||||
{
|
||||
return unlink(name);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// OpenSocket
|
||||
// Open an interprocess communication (IPC) connection. For now,
|
||||
// just open a datagram port where other Nachos (simulating
|
||||
// workstations on a network) can send messages to this Nachos.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
int
|
||||
OpenSocket()
|
||||
{
|
||||
int sockID;
|
||||
|
||||
sockID = socket(AF_UNIX, SOCK_DGRAM, 0);
|
||||
ASSERT(sockID >= 0);
|
||||
|
||||
return sockID;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// CloseSocket
|
||||
// Close the IPC connection.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
CloseSocket(int sockID)
|
||||
{
|
||||
(void) close(sockID);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// InitSocketName
|
||||
// Initialize a UNIX socket address -- magical!
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
static void
|
||||
InitSocketName(struct sockaddr_un *uname, const char *name)
|
||||
{
|
||||
uname->sun_family = AF_UNIX;
|
||||
strcpy(uname->sun_path, name);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// AssignNameToSocket
|
||||
// Give a UNIX file name to the IPC port, so other instances of Nachos
|
||||
// can locate the port.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
AssignNameToSocket(const char *socketName, int sockID)
|
||||
{
|
||||
struct sockaddr_un uName;
|
||||
int retVal;
|
||||
|
||||
(void) unlink(socketName); // in case it's still around from last time
|
||||
|
||||
InitSocketName(&uName, socketName);
|
||||
retVal = bind(sockID, (struct sockaddr *) &uName, sizeof(uName));
|
||||
ASSERT(retVal >= 0);
|
||||
DEBUG('n', "Created socket %s\n", socketName);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// DeAssignNameToSocket
|
||||
// Delete the UNIX file name we assigned to our IPC port, on cleanup.
|
||||
//----------------------------------------------------------------------
|
||||
void
|
||||
DeAssignNameToSocket(const char *socketName)
|
||||
{
|
||||
(void) unlink(socketName);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// PollSocket
|
||||
// Return TRUE if there are any messages waiting to arrive on the
|
||||
// IPC port.
|
||||
//----------------------------------------------------------------------
|
||||
bool
|
||||
PollSocket(int sockID)
|
||||
{
|
||||
return PollFile(sockID); // on UNIX, socket ID's are just file ID's
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// ReadFromSocket
|
||||
// Read a fixed size packet off the IPC port. Abort on error.
|
||||
//----------------------------------------------------------------------
|
||||
void
|
||||
ReadFromSocket(int sockID, void *buffer, int packetSize)
|
||||
{
|
||||
int retVal;
|
||||
struct sockaddr_un uName;
|
||||
|
||||
// LB: Signedness problem on Solaris 5.6/SPARC, as the last
|
||||
// parameter of recvfrom is specified as a int *. In the later
|
||||
// versions, it is specified as a void *. Casting size to int instead
|
||||
// of unsigned seems to fix the problem, but it is admittingly
|
||||
// rather ad-hoc...
|
||||
#ifndef SOLARIS
|
||||
unsigned int size = sizeof(uName);
|
||||
#else
|
||||
int size = (int) sizeof(uName);
|
||||
#endif
|
||||
// End of correction.
|
||||
|
||||
retVal = recvfrom(sockID, buffer, packetSize, 0,
|
||||
(struct sockaddr *) &uName, &size);
|
||||
|
||||
if (retVal != packetSize) {
|
||||
perror("in recvfrom");
|
||||
}
|
||||
ASSERT(retVal == packetSize);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// SendToSocket
|
||||
// Transmit a fixed size packet to another Nachos' IPC port.
|
||||
// Abort on error.
|
||||
//----------------------------------------------------------------------
|
||||
void
|
||||
SendToSocket(int sockID, const void *buffer, int packetSize, const char *toName)
|
||||
{
|
||||
struct sockaddr_un uName;
|
||||
int retVal;
|
||||
|
||||
InitSocketName(&uName, toName);
|
||||
retVal = sendto(sockID, buffer, packetSize, 0,
|
||||
(sockaddr *) &uName, sizeof(uName));
|
||||
ASSERT(retVal == packetSize);
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// CallOnUserAbort
|
||||
// Arrange that "func" will be called when the user aborts (e.g., by
|
||||
// hitting ctl-C.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
CallOnUserAbort(VoidNoArgFunctionPtr func)
|
||||
{
|
||||
(void)signal(SIGINT, (void (*)(int)) func);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// BlockUserAbort
|
||||
// Prevent from abortion (e.g. ctl-C)
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
BlockUserAbort(void)
|
||||
{
|
||||
sighold(SIGINT);
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// UnBlockUserAbort
|
||||
// Re-allow abortion (e.g. ctl-C)
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
UnBlockUserAbort(void)
|
||||
{
|
||||
sigrelse(SIGINT);
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Sleep
|
||||
// Put the UNIX process running Nachos to sleep for x seconds,
|
||||
// to give the user time to start up another invocation of Nachos
|
||||
// in a different UNIX shell.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
Delay(int seconds)
|
||||
{
|
||||
(void) sleep((unsigned) seconds);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Abort
|
||||
// Quit and drop core.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
Abort()
|
||||
{
|
||||
#ifdef USER_PROGRAM
|
||||
if (machine)
|
||||
machine->DumpMem("abort.svg");
|
||||
#endif
|
||||
abort();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Exit
|
||||
// Quit without dropping core.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
Exit(int exitCode)
|
||||
{
|
||||
exit(exitCode);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// RandomInit
|
||||
// Initialize the pseudo-random number generator. We use the
|
||||
// now obsolete "srand" and "rand" because they are more portable!
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
RandomInit(unsigned seed)
|
||||
{
|
||||
srand(seed);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Random
|
||||
// Return a pseudo-random number.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
int
|
||||
Random()
|
||||
{
|
||||
return rand();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// AllocBoundedArray
|
||||
// Return an array, with the two pages just before
|
||||
// and after the array unmapped, to catch illegal references off
|
||||
// the end of the array. Particularly useful for catching overflow
|
||||
// beyond fixed-size thread execution stacks.
|
||||
//
|
||||
// Note: Just return the useful part!
|
||||
//
|
||||
// "size" -- amount of useful space needed (in bytes)
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
char *
|
||||
AllocBoundedArray(int size)
|
||||
{
|
||||
int pgSize = getpagesize();
|
||||
char *ptr = new char[pgSize * 2 + size];
|
||||
|
||||
mprotect(ptr, pgSize, 0);
|
||||
mprotect(ptr + pgSize + size, pgSize, 0);
|
||||
return ptr + pgSize;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// DeallocBoundedArray
|
||||
// Deallocate an array of integers, unprotecting its two boundary pages.
|
||||
//
|
||||
// "ptr" -- the array to be deallocated
|
||||
// "size" -- amount of useful space in the array (in bytes)
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
DeallocBoundedArray(char *ptr, int size)
|
||||
{
|
||||
int pgSize = getpagesize();
|
||||
|
||||
mprotect(ptr - pgSize, pgSize, PROT_READ | PROT_WRITE | PROT_EXEC);
|
||||
mprotect(ptr + size, pgSize, PROT_READ | PROT_WRITE | PROT_EXEC);
|
||||
delete [] (ptr - pgSize);
|
||||
}
|
68
code/machine/sysdep.h
Normal file
68
code/machine/sysdep.h
Normal file
|
@ -0,0 +1,68 @@
|
|||
// sysdep.h
|
||||
// System-dependent interface. Nachos uses the routines defined
|
||||
// here, rather than directly calling the UNIX library functions, to
|
||||
// simplify porting between versions of UNIX, and even to
|
||||
// other systems, such as MSDOS and the Macintosh.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef SYSDEP_H
|
||||
#define SYSDEP_H
|
||||
|
||||
#include "copyright.h"
|
||||
|
||||
// Check file to see if there are any characters to be read.
|
||||
// If no characters in the file, return without waiting.
|
||||
extern bool PollFile(int fd);
|
||||
|
||||
// File operations: open/read/write/lseek/close, and check for error
|
||||
// For simulating the disk and the console devices.
|
||||
extern int OpenForWrite(const char *name);
|
||||
extern int OpenForReadWrite(const char *name, bool crashOnError);
|
||||
extern void Read(int fd, void *buffer, int nBytes);
|
||||
extern int ReadPartial(int fd, void *buffer, int nBytes);
|
||||
extern void WriteFile(int fd, const void *buffer, int nBytes);
|
||||
extern void Lseek(int fd, int offset, int whence);
|
||||
extern int Tell(int fd);
|
||||
extern void Close(int fd);
|
||||
extern bool Unlink(const char *name);
|
||||
|
||||
// Interprocess communication operations, for simulating the network
|
||||
extern int OpenSocket();
|
||||
extern void CloseSocket(int sockID);
|
||||
extern void AssignNameToSocket(const char *socketName, int sockID);
|
||||
extern void DeAssignNameToSocket(const char *socketName);
|
||||
extern bool PollSocket(int sockID);
|
||||
extern void ReadFromSocket(int sockID, void *buffer, int packetSize);
|
||||
extern void SendToSocket(int sockID, const void *buffer, int packetSize,const char *toName);
|
||||
|
||||
// Process control: abort, exit, and sleep
|
||||
extern void Abort();
|
||||
extern void Exit(int exitCode);
|
||||
extern void Delay(int seconds);
|
||||
|
||||
// Initialize system so that cleanUp routine is called when user hits ctl-C
|
||||
extern void CallOnUserAbort(VoidNoArgFunctionPtr cleanUp);
|
||||
extern void BlockUserAbort(void);
|
||||
extern void UnBlockUserAbort(void);
|
||||
|
||||
// Initialize the pseudo random number generator
|
||||
extern void RandomInit(unsigned seed);
|
||||
extern int Random();
|
||||
|
||||
// Allocate, de-allocate an array, such that de-referencing
|
||||
// just beyond either end of the array will cause an error
|
||||
extern char *AllocBoundedArray(int size);
|
||||
extern void DeallocBoundedArray(char *p, int size);
|
||||
|
||||
// Other C library routines that are used by Nachos.
|
||||
// These are assumed to be portable, so we don't include a wrapper.
|
||||
extern "C" {
|
||||
#include <stdlib.h> // for atoi, atof, abs
|
||||
#include <stdio.h> // for printf, fprintf
|
||||
#include <string.h> // for DEBUG, etc.
|
||||
}
|
||||
|
||||
#endif // SYSDEP_H
|
85
code/machine/timer.cc
Normal file
85
code/machine/timer.cc
Normal file
|
@ -0,0 +1,85 @@
|
|||
// timer.cc
|
||||
// Routines to emulate a hardware timer device.
|
||||
//
|
||||
// A hardware timer generates a CPU interrupt every X milliseconds.
|
||||
// This means it can be used for implementing time-slicing.
|
||||
//
|
||||
// We emulate a hardware timer by scheduling an interrupt to occur
|
||||
// every time stats->totalTicks has increased by TimerTicks.
|
||||
//
|
||||
// In order to introduce some randomness into time-slicing, if "doRandom"
|
||||
// is set, then the interrupt is comes after a random number of ticks.
|
||||
//
|
||||
// Remember -- nothing in here is part of Nachos. It is just
|
||||
// an emulation for the hardware that Nachos is running on top of.
|
||||
//
|
||||
// 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 "timer.h"
|
||||
#include "system.h"
|
||||
|
||||
// dummy function because C++ does not allow pointers to member functions
|
||||
static void TimerHandler(void *arg)
|
||||
{ Timer *p = (Timer *)arg; p->TimerExpired(); }
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Timer::Timer
|
||||
// Initialize a hardware timer device. Save the place to call
|
||||
// on each interrupt, and then arrange for the timer to start
|
||||
// generating interrupts.
|
||||
//
|
||||
// "timerHandler" is the interrupt handler for the timer device.
|
||||
// It is called with interrupts disabled every time the
|
||||
// the timer expires.
|
||||
// "callArg" is the parameter to be passed to the interrupt handler.
|
||||
// "doRandom" -- if true, arrange for the interrupts to occur
|
||||
// at random, instead of fixed, intervals.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
Timer::Timer(VoidFunctionPtr timerHandler, void *callArg, bool doRandom)
|
||||
{
|
||||
randomize = doRandom;
|
||||
handler = timerHandler;
|
||||
arg = callArg;
|
||||
|
||||
// schedule the first interrupt from the timer device
|
||||
interrupt->Schedule(TimerHandler, this, TimeOfNextInterrupt(),
|
||||
TimerInt);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Timer::TimerExpired
|
||||
// Routine to simulate the interrupt generated by the hardware
|
||||
// timer device. Schedule the next interrupt, and invoke the
|
||||
// interrupt handler.
|
||||
//----------------------------------------------------------------------
|
||||
void
|
||||
Timer::TimerExpired()
|
||||
{
|
||||
// schedule the next timer device interrupt
|
||||
interrupt->Schedule(TimerHandler, this, TimeOfNextInterrupt(),
|
||||
TimerInt);
|
||||
|
||||
// invoke the Nachos interrupt handler for this device
|
||||
(*handler)(arg);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Timer::TimeOfNextInterrupt
|
||||
// Return when the hardware timer device will next cause an interrupt.
|
||||
// If randomize is turned on, make it a (pseudo-)random delay.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
int
|
||||
Timer::TimeOfNextInterrupt()
|
||||
{
|
||||
if (randomize)
|
||||
return 1 + (Random() % (TimerTicks * 2));
|
||||
else
|
||||
return TimerTicks;
|
||||
}
|
49
code/machine/timer.h
Normal file
49
code/machine/timer.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
// timer.h
|
||||
// Data structures to emulate a hardware timer.
|
||||
//
|
||||
// A hardware timer generates a CPU interrupt every X milliseconds.
|
||||
// This means it can be used for implementing time-slicing, or for
|
||||
// having a thread go to sleep for a specific period of time.
|
||||
//
|
||||
// We emulate a hardware timer by scheduling an interrupt to occur
|
||||
// every time stats->totalTicks has increased by TimerTicks.
|
||||
//
|
||||
// In order to introduce some randomness into time-slicing, if "doRandom"
|
||||
// is set, then the interrupt comes after a random number of ticks.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef TIMER_H
|
||||
#define TIMER_H
|
||||
|
||||
#include "copyright.h"
|
||||
#include "utility.h"
|
||||
|
||||
// The following class defines a hardware timer.
|
||||
class Timer {
|
||||
public:
|
||||
Timer(VoidFunctionPtr timerHandler, void *callArg, bool doRandom);
|
||||
// Initialize the timer, to call the interrupt
|
||||
// handler "timerHandler" every time slice.
|
||||
~Timer() {}
|
||||
|
||||
// Internal routines to the timer emulation -- DO NOT call these
|
||||
|
||||
void TimerExpired(); // called internally when the hardware
|
||||
// timer generates an interrupt
|
||||
|
||||
int TimeOfNextInterrupt(); // figure out when the timer will generate
|
||||
// its next interrupt
|
||||
|
||||
private:
|
||||
bool randomize; // set if we need to use a random timeout delay
|
||||
VoidFunctionPtr handler; // timer interrupt handler
|
||||
void *arg; // argument to pass to interrupt handler
|
||||
|
||||
};
|
||||
|
||||
#endif // TIMER_H
|
267
code/machine/translate.cc
Normal file
267
code/machine/translate.cc
Normal file
|
@ -0,0 +1,267 @@
|
|||
// translate.cc
|
||||
// Routines to translate virtual addresses to physical addresses.
|
||||
// Software sets up a table of legal translations. We look up
|
||||
// in the table on every memory reference to find the true physical
|
||||
// memory location.
|
||||
//
|
||||
// Two types of translation are supported here.
|
||||
//
|
||||
// Linear page table -- the virtual page # is used as an index
|
||||
// into the table, to find the physical page #.
|
||||
//
|
||||
// Translation lookaside buffer -- associative lookup in the table
|
||||
// to find an entry with the same virtual page #. If found,
|
||||
// this entry is used for the translation.
|
||||
// If not, it traps to software with an exception.
|
||||
//
|
||||
// In practice, the TLB is much smaller than the amount of physical
|
||||
// memory (16 entries is common on a machine that has 1000's of
|
||||
// pages). Thus, there must also be a backup translation scheme
|
||||
// (such as page tables), but the hardware doesn't need to know
|
||||
// anything at all about that.
|
||||
//
|
||||
// Note that the contents of the TLB are specific to an address space.
|
||||
// If the address space changes, so does the contents of the TLB!
|
||||
//
|
||||
// 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 "addrspace.h"
|
||||
#include "system.h"
|
||||
|
||||
// Routines for converting Words and Short Words to and from the
|
||||
// simulated machine's format of little endian. These end up
|
||||
// being NOPs when the host machine is also little endian (DEC and Intel).
|
||||
|
||||
unsigned int
|
||||
WordToHost(unsigned int word) {
|
||||
#ifdef HOST_IS_BIG_ENDIAN
|
||||
register unsigned long result;
|
||||
result = (word >> 24) & 0x000000ff;
|
||||
result |= (word >> 8) & 0x0000ff00;
|
||||
result |= (word << 8) & 0x00ff0000;
|
||||
result |= (word << 24) & 0xff000000;
|
||||
return result;
|
||||
#else
|
||||
return word;
|
||||
#endif /* HOST_IS_BIG_ENDIAN */
|
||||
}
|
||||
|
||||
unsigned short
|
||||
ShortToHost(unsigned short shortword) {
|
||||
#ifdef HOST_IS_BIG_ENDIAN
|
||||
register unsigned short result;
|
||||
result = (shortword << 8) & 0xff00;
|
||||
result |= (shortword >> 8) & 0x00ff;
|
||||
return result;
|
||||
#else
|
||||
return shortword;
|
||||
#endif /* HOST_IS_BIG_ENDIAN */
|
||||
}
|
||||
|
||||
unsigned int
|
||||
WordToMachine(unsigned int word) { return WordToHost(word); }
|
||||
|
||||
unsigned short
|
||||
ShortToMachine(unsigned short shortword) { return ShortToHost(shortword); }
|
||||
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Machine::ReadMem
|
||||
// Read "size" (1, 2, or 4) bytes of virtual memory at "addr" into
|
||||
// the location pointed to by "value".
|
||||
//
|
||||
// Returns FALSE if the translation step from virtual to physical memory
|
||||
// failed.
|
||||
//
|
||||
// "addr" -- the virtual address to read from
|
||||
// "size" -- the number of bytes to read (1, 2, or 4)
|
||||
// "value" -- the place to write the result
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
bool
|
||||
Machine::ReadMem(int addr, int size, int *value, bool debug)
|
||||
{
|
||||
int data;
|
||||
ExceptionType exception;
|
||||
int physicalAddress;
|
||||
|
||||
if (debug)
|
||||
DEBUG('a', "Reading VA 0x%x, size %d\n", addr, size);
|
||||
|
||||
exception = Translate(addr, &physicalAddress, size, FALSE, debug);
|
||||
if (exception != NoException) {
|
||||
machine->RaiseException(exception, addr);
|
||||
return FALSE;
|
||||
}
|
||||
switch (size) {
|
||||
case 1:
|
||||
data = machine->mainMemory[physicalAddress];
|
||||
*value = data;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
data = *(unsigned short *) &machine->mainMemory[physicalAddress];
|
||||
*value = ShortToHost(data);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
data = *(unsigned int *) &machine->mainMemory[physicalAddress];
|
||||
*value = WordToHost(data);
|
||||
break;
|
||||
|
||||
default: ASSERT(FALSE);
|
||||
}
|
||||
|
||||
if (debug)
|
||||
DEBUG('a', "\tvalue read = %8.8x\n", *value);
|
||||
return (TRUE);
|
||||
}
|
||||
|
||||
bool
|
||||
Machine::ReadMem(int addr, int size, int *value)
|
||||
{
|
||||
return ReadMem(addr, size, value, TRUE);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Machine::WriteMem
|
||||
// Write "size" (1, 2, or 4) bytes of the contents of "value" into
|
||||
// virtual memory at location "addr".
|
||||
//
|
||||
// Returns FALSE if the translation step from virtual to physical memory
|
||||
// failed.
|
||||
//
|
||||
// "addr" -- the virtual address to write to
|
||||
// "size" -- the number of bytes to be written (1, 2, or 4)
|
||||
// "value" -- the data to be written
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
bool
|
||||
Machine::WriteMem(int addr, int size, int value)
|
||||
{
|
||||
ExceptionType exception;
|
||||
int physicalAddress;
|
||||
|
||||
DEBUG('a', "Writing VA 0x%x, size %d, value 0x%x\n", addr, size, value);
|
||||
|
||||
exception = Translate(addr, &physicalAddress, size, TRUE, TRUE);
|
||||
if (exception != NoException) {
|
||||
machine->RaiseException(exception, addr);
|
||||
return FALSE;
|
||||
}
|
||||
switch (size) {
|
||||
case 1:
|
||||
machine->mainMemory[physicalAddress] = (unsigned char) (value & 0xff);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
*(unsigned short *) &machine->mainMemory[physicalAddress]
|
||||
= ShortToMachine((unsigned short) (value & 0xffff));
|
||||
break;
|
||||
|
||||
case 4:
|
||||
*(unsigned int *) &machine->mainMemory[physicalAddress]
|
||||
= WordToMachine((unsigned int) value);
|
||||
break;
|
||||
|
||||
default: ASSERT(FALSE);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Machine::Translate
|
||||
// Translate a virtual address into a physical address, using
|
||||
// either a page table or a TLB. Check for alignment and all sorts
|
||||
// of other errors, and if everything is ok, set the use/dirty bits in
|
||||
// the translation table entry, and store the translated physical
|
||||
// address in "physAddr". If there was an error, returns the type
|
||||
// of the exception.
|
||||
//
|
||||
// "virtAddr" -- the virtual address to translate
|
||||
// "physAddr" -- the place to store the physical address
|
||||
// "size" -- the amount of memory being read or written
|
||||
// "writing" -- if TRUE, check the "read-only" bit in the TLB
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
ExceptionType
|
||||
Machine::Translate(int virtAddr, int* physAddr, int size, bool writing, bool debug)
|
||||
{
|
||||
int i;
|
||||
unsigned int vpn, offset;
|
||||
TranslationEntry *entry;
|
||||
unsigned int pageFrame;
|
||||
|
||||
if (debug) DEBUG('a', "\tTranslate 0x%x, %s: ", virtAddr, writing ? "write" : "read");
|
||||
|
||||
// check for alignment errors
|
||||
if (((size == 4) && (virtAddr & 0x3)) || ((size == 2) && (virtAddr & 0x1))){
|
||||
if (debug) DEBUG('a', "alignment problem at %d, size %d!\n", virtAddr, size);
|
||||
return AddressErrorException;
|
||||
}
|
||||
|
||||
// we must have either a TLB or a page table, but not both!
|
||||
ASSERT(tlb == NULL || currentPageTable == NULL);
|
||||
ASSERT(tlb != NULL || currentPageTable != NULL);
|
||||
|
||||
// calculate the virtual page number, and offset within the page,
|
||||
// from the virtual address
|
||||
vpn = (unsigned) virtAddr / PageSize;
|
||||
offset = (unsigned) virtAddr % PageSize;
|
||||
|
||||
if (tlb == NULL) { // => page table => vpn is index into table
|
||||
if (vpn >= currentPageTableSize) {
|
||||
if (debug) DEBUG('a', "virtual page # %d too large for page table size %d!\n",
|
||||
virtAddr, currentPageTableSize);
|
||||
return AddressErrorException;
|
||||
} else if (!currentPageTable[vpn].valid) {
|
||||
if (debug) DEBUG('a', "virtual page # %d : page %d is invalid !\n",
|
||||
virtAddr, vpn);
|
||||
return PageFaultException;
|
||||
}
|
||||
entry = ¤tPageTable[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
|
||||
if (debug) DEBUG('a', "*** no valid TLB entry found for this virtual page!\n");
|
||||
return PageFaultException; // really, this is a TLB fault,
|
||||
// the page may be in memory,
|
||||
// but not in the TLB
|
||||
}
|
||||
}
|
||||
|
||||
if (entry->readOnly && writing) { // trying to write to a read-only page
|
||||
if (tlb == NULL) {
|
||||
if (debug) DEBUG('a', "%d mapped read-only in page table!\n", virtAddr);
|
||||
} else {
|
||||
if (debug) DEBUG('a', "%d mapped read-only at %d in TLB!\n", virtAddr, i);
|
||||
}
|
||||
return 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) {
|
||||
if (debug) DEBUG('a', "*** frame %d > %d!\n", pageFrame, NumPhysPages);
|
||||
return BusErrorException;
|
||||
}
|
||||
entry->use = TRUE; // set the use, dirty bits
|
||||
if (writing)
|
||||
entry->dirty = TRUE;
|
||||
*physAddr = pageFrame * PageSize + offset;
|
||||
ASSERT((*physAddr >= 0) && ((*physAddr + size) <= MemorySize));
|
||||
if (debug) DEBUG('a', "phys addr = 0x%x\n", *physAddr);
|
||||
return NoException;
|
||||
}
|
46
code/machine/translate.h
Normal file
46
code/machine/translate.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
// translate.h
|
||||
// Data structures for managing the translation from
|
||||
// virtual page # -> physical page #, used for managing
|
||||
// physical memory on behalf of user programs.
|
||||
//
|
||||
// The data structures in this file are "dual-use" - they
|
||||
// serve both as a page table entry, and as an entry in
|
||||
// a software-managed translation lookaside buffer (TLB).
|
||||
// Either way, each entry is of the form:
|
||||
// <virtual page #, physical page #>.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef TLB_H
|
||||
#define TLB_H
|
||||
|
||||
#include "copyright.h"
|
||||
#include "utility.h"
|
||||
|
||||
// The following class defines an entry in a translation table -- either
|
||||
// in a page table or a TLB. Each entry defines a mapping from one
|
||||
// virtual page to one physical page.
|
||||
// In addition, there are some extra bits for access control (valid and
|
||||
// read-only) and some bits for usage information (use and dirty).
|
||||
|
||||
class TranslationEntry {
|
||||
public:
|
||||
unsigned int virtualPage; // The page number in virtual memory, only when
|
||||
// using a TLB
|
||||
unsigned int physicalPage; // The page number in real memory (relative to the
|
||||
// start of "mainMemory"
|
||||
bool valid; // If this bit is cleared, the translation is ignored.
|
||||
// (In other words, the entry hasn't been initialized.)
|
||||
bool readOnly; // If this bit is set, the user program is not allowed
|
||||
// to modify the contents of the page.
|
||||
bool use; // This bit is set by the hardware every time the
|
||||
// page is referenced or modified.
|
||||
bool dirty; // This bit is set by the hardware every time the
|
||||
// page is modified.
|
||||
};
|
||||
|
||||
#endif
|
23
code/machine/valgrind.h
Normal file
23
code/machine/valgrind.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
// valgrind.h
|
||||
// Valgrind hooks to announce stack allocation/deallocation
|
||||
//
|
||||
// Copyright (c) 2009 Samuel Thibault
|
||||
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||
// of liability and disclaimer of warranty provisions.
|
||||
|
||||
#ifndef VALGRIND_H
|
||||
#define VALGRIND_H
|
||||
|
||||
#ifdef HAVE_VALGRIND
|
||||
#include <valgrind/valgrind.h>
|
||||
#endif
|
||||
|
||||
#ifndef VALGRIND_STACK_REGISTER
|
||||
#define VALGRIND_STACK_REGISTER(start, end) 0
|
||||
#endif
|
||||
|
||||
#ifndef VALGRIND_STACK_DEREGISTER
|
||||
#define VALGRIND_STACK_DEREGISTER(id) ((void)0)
|
||||
#endif
|
||||
|
||||
#endif // VALGRIND_H
|
Loading…
Add table
Add a link
Reference in a new issue