init: init nachos hw01
This commit is contained in:
18
COPYRIGHT
Executable file
18
COPYRIGHT
Executable file
@@ -0,0 +1,18 @@
|
|||||||
|
Copyright (c) 1992-1993 The Regents of the University of California.
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and distribute this software and its
|
||||||
|
documentation for any purpose, without fee, and without written agreement is
|
||||||
|
hereby granted, provided that the above copyright notice and the following
|
||||||
|
two paragraphs appear in all copies of this software.
|
||||||
|
|
||||||
|
IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
|
||||||
|
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
|
||||||
|
OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
|
||||||
|
CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
|
||||||
|
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
||||||
|
ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
|
||||||
|
PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
||||||
16
c++example/Makefile
Executable file
16
c++example/Makefile
Executable file
@@ -0,0 +1,16 @@
|
|||||||
|
PREFIX=decstation-ultrix- # add crosscompiler prefix here i.e. decstation-ultrix-
|
||||||
|
INCLUDEDIR= # add path to include directories for crosscompiler environment here:
|
||||||
|
# don't forget the -I tag before the directory
|
||||||
|
# i.e: -I/usr/lcoal/nachosxdev/include -I/usr/local/nachosxdev/include/g++-3
|
||||||
|
|
||||||
|
|
||||||
|
all: stack inheritstack templatestack
|
||||||
|
|
||||||
|
stack: stack.h stack.cc
|
||||||
|
$(PREFIX)g++ $(INCLUDEDIR) -o stack stack.cc
|
||||||
|
|
||||||
|
inheritstack: inheritstack.h inheritstack.cc list.h list.cc
|
||||||
|
$(PREFIX)g++ $(INCLUDEDIR) -o inheritstack inheritstack.cc list.cc
|
||||||
|
|
||||||
|
templatestack: templatestack.h templatestack.cc
|
||||||
|
$(PREFIX)g++ $(INCLUDEDIR) -o templatestack templatestack.cc
|
||||||
2760
c++example/c++.ps
Executable file
2760
c++example/c++.ps
Executable file
File diff suppressed because it is too large
Load Diff
1576
c++example/c++.tex
Executable file
1576
c++example/c++.tex
Executable file
File diff suppressed because it is too large
Load Diff
27
c++example/copyright.h
Executable file
27
c++example/copyright.h
Executable file
@@ -0,0 +1,27 @@
|
|||||||
|
#ifndef COPYRIGHT_H
|
||||||
|
#define COPYRIGHT_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright (c) 1992,1993,1995 The Regents of the University of California.
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and distribute this software and its
|
||||||
|
documentation for any purpose, without fee, and without written agreement is
|
||||||
|
hereby granted, provided that the above copyright notice and the following
|
||||||
|
two paragraphs appear in all copies of this software.
|
||||||
|
|
||||||
|
IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
|
||||||
|
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
|
||||||
|
OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
|
||||||
|
CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
|
||||||
|
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
||||||
|
ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
|
||||||
|
PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static char *copyright = "Copyright (c) 1992,1993,1995 The Regents of the University of California. All rights reserved.";
|
||||||
|
|
||||||
|
#endif /* COPYRIGHT_H */
|
||||||
230
c++example/inheritstack.cc
Executable file
230
c++example/inheritstack.cc
Executable file
@@ -0,0 +1,230 @@
|
|||||||
|
// inheritstack.cc
|
||||||
|
// Routines for two implementions of a LIFO stack of integers,
|
||||||
|
// one as an array, the other as a list.
|
||||||
|
//
|
||||||
|
// Copyright (c) 1992,1993,1995 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.
|
||||||
|
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#define ASSERT(expression) assert(expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool FALSE = false;
|
||||||
|
const bool TRUE = true;
|
||||||
|
|
||||||
|
#include <iostream.h>
|
||||||
|
#include "copyright.h"
|
||||||
|
#include "list.h"
|
||||||
|
#include "inheritstack.h"
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Stack::Stack, Stack::~Stack
|
||||||
|
// constructor and destructor for the Stack class; no data
|
||||||
|
// to initialize!
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
Stack::Stack() {}
|
||||||
|
Stack::~Stack() {}
|
||||||
|
|
||||||
|
|
||||||
|
// IMPLEMENTATION #1: AS AN ARRAY
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// ArrayStack::ArrayStack
|
||||||
|
// The constructor for the ArrayStack class.
|
||||||
|
//
|
||||||
|
// "sz" -- maximum number of elements on the ArrayStack at any time
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
ArrayStack::ArrayStack(int sz) : Stack() {
|
||||||
|
|
||||||
|
ASSERT(sz >= 1);
|
||||||
|
|
||||||
|
// Initialize the data members of the stack object.
|
||||||
|
size = sz;
|
||||||
|
top = 0;
|
||||||
|
stack = new int[size]; // allocate an array of integers.
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// ArrayStack::~ArrayStack
|
||||||
|
// The destructor for the ArrayStack class. Just get rid of the array we
|
||||||
|
// allocated in the constructor.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
ArrayStack::~ArrayStack() {
|
||||||
|
|
||||||
|
delete [] stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// ArrayStack::Push
|
||||||
|
// Put an integer on the top of the stack; error on overflow.
|
||||||
|
//
|
||||||
|
// "value" -- the value to put on the stack
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
ArrayStack::Push(int value) {
|
||||||
|
ASSERT(!Full());
|
||||||
|
|
||||||
|
stack[top++] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// ArrayStack::Pop
|
||||||
|
// Remove an integer from the top of the stack, returning its value.
|
||||||
|
// Error if the stack is empty.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
int
|
||||||
|
ArrayStack::Pop() {
|
||||||
|
|
||||||
|
ASSERT(!Empty());
|
||||||
|
|
||||||
|
return (stack[--top]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// ArrayStack::Full
|
||||||
|
// Return TRUE if the stack has no more room.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
bool
|
||||||
|
ArrayStack::Full() {
|
||||||
|
return (top == size);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// ArrayStack::Empty
|
||||||
|
// Return TRUE if the stack has nothing on it.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
bool
|
||||||
|
ArrayStack::Empty() {
|
||||||
|
return (top == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// IMPLEMENTATION #2: AS A LIST
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// ListStack::ListStack
|
||||||
|
// The constructor for the ListStack class.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
ListStack::ListStack() : Stack() {
|
||||||
|
|
||||||
|
stack = new List; // allocate an empty list of integers.
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// ListStack::~ListStack
|
||||||
|
// The destructor for the ListStack class. Just get rid of the list we
|
||||||
|
// allocated in the constructor.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
ListStack::~ListStack() {
|
||||||
|
|
||||||
|
delete stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// ListStack::Push
|
||||||
|
// Put an integer on the top of the stack.
|
||||||
|
//
|
||||||
|
// "value" -- the value to put on the stack
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
ListStack::Push(int value) {
|
||||||
|
stack->Prepend(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// ListStack::Pop
|
||||||
|
// Remove an integer from the top of the stack, returning its value.
|
||||||
|
// Error if the stack is empty.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
int
|
||||||
|
ListStack::Pop() {
|
||||||
|
|
||||||
|
ASSERT(!Empty());
|
||||||
|
|
||||||
|
return stack->Remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// ListStack::Full
|
||||||
|
// Return FALSE, because a liststack can never overflow
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
bool
|
||||||
|
ListStack::Full() {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// ListStack::Empty
|
||||||
|
// Return TRUE if the stack has nothing on it.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
bool
|
||||||
|
ListStack::Empty() {
|
||||||
|
return stack->Empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Stack::SelfTest
|
||||||
|
// Test our stack implementation by pushing 10 numbers onto the
|
||||||
|
// stack, and then print them as it pops them off.
|
||||||
|
//
|
||||||
|
// Note this code is generic between the two versions --
|
||||||
|
// it doesn't matter whether this is an ArrayStack or a ListStack!
|
||||||
|
//
|
||||||
|
// "numToPush" is the number of items to put on the stack in the
|
||||||
|
// selftest.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
Stack::SelfTest(int numToPush) {
|
||||||
|
int count = 17;
|
||||||
|
|
||||||
|
// Put a bunch of stuff in the stack...
|
||||||
|
for (int i = 0; i < numToPush; i++) {
|
||||||
|
ASSERT(!Full());
|
||||||
|
cout << "pushing " << count << "\n";
|
||||||
|
Push(count++);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ... and take it out again.
|
||||||
|
while (!Empty()) {
|
||||||
|
cout << "popping " << Pop() << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// main
|
||||||
|
// Run the test code for the stack implementation.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
int
|
||||||
|
main() {
|
||||||
|
Stack *s1 = new ArrayStack(10); // Constructor with an argument.
|
||||||
|
Stack *s2 = new ListStack();
|
||||||
|
|
||||||
|
cout << "Testing ArrayStack\n";
|
||||||
|
s1->SelfTest(10);
|
||||||
|
|
||||||
|
cout << "Testing ListStack\n";
|
||||||
|
s2->SelfTest(10);
|
||||||
|
|
||||||
|
delete s1; // always delete what you allocate
|
||||||
|
delete s2; // always delete what you allocate
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
86
c++example/inheritstack.h
Executable file
86
c++example/inheritstack.h
Executable file
@@ -0,0 +1,86 @@
|
|||||||
|
// inheritstack.h
|
||||||
|
// Data structures for a "stack" -- a Last-In-First-Out list of integers.
|
||||||
|
//
|
||||||
|
// We define two separate implementations of stacks, to
|
||||||
|
// illustrate C++ inheritance.
|
||||||
|
//
|
||||||
|
// Copyright (c) 1992,1993,1995 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 INHERITSTACK_H // to prevent recursive includes
|
||||||
|
#define INHERITSTACK_H
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
#include "list.h"
|
||||||
|
|
||||||
|
|
||||||
|
// The following defines an "abstract" stack of integers.
|
||||||
|
// This class is abstract because no one is allowed to create
|
||||||
|
// instances of it; instead, you make instances of the derived
|
||||||
|
// classes that inherit from it.
|
||||||
|
|
||||||
|
class Stack {
|
||||||
|
public:
|
||||||
|
virtual ~Stack(); // Destructor
|
||||||
|
|
||||||
|
virtual void Push(int value) = 0; // Push an integer on the stack
|
||||||
|
virtual int Pop() = 0; // Pop an integer off the stack
|
||||||
|
|
||||||
|
virtual bool Full() = 0; // Returns TRUE if the stack is full
|
||||||
|
virtual bool Empty() = 0; // Returns TRUE if the stack is empty
|
||||||
|
|
||||||
|
void SelfTest(int numToPush); // Test whether the implementation works.
|
||||||
|
// Note that the test routine is shared among
|
||||||
|
// all derived classes because it shouldn't
|
||||||
|
// matter to the test code which version we're using!
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Stack(); // Constructor is protected to prevent anyone but
|
||||||
|
// derived classes from calling constructor.
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// The following defines an implementation of Stack using arrays.
|
||||||
|
// This is the same as the original implementation in stack.h,
|
||||||
|
// except we don't need a SelfTest() because that's defined above by Stack!
|
||||||
|
|
||||||
|
class ArrayStack : public Stack {
|
||||||
|
public:
|
||||||
|
ArrayStack(int sz); // Constructor: initialize variables, allocate space.
|
||||||
|
~ArrayStack(); // Destructor: deallocate space allocated above.
|
||||||
|
|
||||||
|
void Push(int value); // Push an integer on the stack
|
||||||
|
int Pop(); // Pop an integer off the stack
|
||||||
|
|
||||||
|
bool Full(); // Returns TRUE if the stack is full
|
||||||
|
bool Empty(); // Returns TRUE if the stack is empty
|
||||||
|
|
||||||
|
private:
|
||||||
|
int size; // The maximum capacity of the stack.
|
||||||
|
int top; // Index of the next position to be used.
|
||||||
|
int *stack; // A pointer to an array that holds the contents.
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// The following defines an implementation of Stack using lists.
|
||||||
|
//
|
||||||
|
// Note that a list implementation can't overflow, so we don't
|
||||||
|
// need to pass a maximum size into the constructor.
|
||||||
|
|
||||||
|
class ListStack : public Stack {
|
||||||
|
public:
|
||||||
|
ListStack(); // Constructor: initialize variables, allocate space.
|
||||||
|
~ListStack(); // Destructor: deallocate space allocated above.
|
||||||
|
|
||||||
|
void Push(int value); // Push an integer on the stack
|
||||||
|
int Pop(); // Pop an integer off the stack
|
||||||
|
|
||||||
|
bool Full(); // Always return FALSE, this implementation never overflows
|
||||||
|
bool Empty(); // Returns TRUE if the stack is empty
|
||||||
|
|
||||||
|
private:
|
||||||
|
List *stack;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif INHERITSTACK_H
|
||||||
133
c++example/list.cc
Executable file
133
c++example/list.cc
Executable file
@@ -0,0 +1,133 @@
|
|||||||
|
// list.cc
|
||||||
|
// Routines to manage a singly-linked list of integers.
|
||||||
|
//
|
||||||
|
// A "ListElement" is allocated for each item to be put on the
|
||||||
|
// list; it is de-allocated when the item is removed. This means
|
||||||
|
// we don't need to keep a "next" pointer in every object we
|
||||||
|
// want to put on a list.
|
||||||
|
//
|
||||||
|
// Copyright (c) 1992,1993,1995 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.
|
||||||
|
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#define ASSERT(expression) assert(expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
#include "list.h"
|
||||||
|
|
||||||
|
const int NULL = 0;
|
||||||
|
|
||||||
|
|
||||||
|
// The following class defines a "list element" -- which is
|
||||||
|
// used to keep track of one item on a list. It is equivalent to a
|
||||||
|
// LISP cell, with a "car" ("next") pointing to the next element on the list,
|
||||||
|
// and a "cdr" ("item") containing the item on the list.
|
||||||
|
//
|
||||||
|
// Class defined in list.cc, because only the List class can be allocating
|
||||||
|
// and accessing ListElements.
|
||||||
|
|
||||||
|
class ListElement {
|
||||||
|
public:
|
||||||
|
ListElement(int value) { item = value; next = NULL;};
|
||||||
|
// constructor for list element
|
||||||
|
|
||||||
|
ListElement *next; // next element on list,
|
||||||
|
// NULL if this is the last
|
||||||
|
int item; // value of this element
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// List::List
|
||||||
|
// Initialize a list, empty to start with.
|
||||||
|
// Elements can now be added to the list.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
List::List() {
|
||||||
|
|
||||||
|
first = last = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// List::~List
|
||||||
|
// Prepare a list for deallocation. If the list still contains any
|
||||||
|
// ListElements, de-allocate them.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
List::~List() {
|
||||||
|
|
||||||
|
while (!Empty())
|
||||||
|
(void) Remove(); // delete all the list elements
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// List::Prepend
|
||||||
|
// Put an integer on the front of the list.
|
||||||
|
//
|
||||||
|
// Allocate a ListElement to keep track of the integer.
|
||||||
|
// If the list is empty, then this will be the only element.
|
||||||
|
// Otherwise, put it at the beginning.
|
||||||
|
//
|
||||||
|
// "value" is the integer to be put on the list.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
List::Prepend(int value) {
|
||||||
|
ListElement *element = new ListElement(value);
|
||||||
|
|
||||||
|
if (Empty()) { // list is empty
|
||||||
|
first = element;
|
||||||
|
last = element;
|
||||||
|
} else { // else put it before first
|
||||||
|
element->next = first;
|
||||||
|
first = element;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// List::Remove
|
||||||
|
// Remove the first integer from the front of the list.
|
||||||
|
// Error if nothing on the list.
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// The removed integer.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
int
|
||||||
|
List::Remove() {
|
||||||
|
ListElement *element = first;
|
||||||
|
int value;
|
||||||
|
|
||||||
|
ASSERT(!Empty());
|
||||||
|
|
||||||
|
element = first;
|
||||||
|
value = first->item;
|
||||||
|
|
||||||
|
if (first == last) { // list had one item, now has none
|
||||||
|
first = NULL;
|
||||||
|
last = NULL;
|
||||||
|
} else {
|
||||||
|
first = element->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete element; // deallocate list element -- no longer needed
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// List::Empty
|
||||||
|
// Returns TRUE if the list is empty (has no items).
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
bool
|
||||||
|
List::Empty() {
|
||||||
|
return (first == NULL);
|
||||||
|
}
|
||||||
35
c++example/list.h
Executable file
35
c++example/list.h
Executable file
@@ -0,0 +1,35 @@
|
|||||||
|
// list.h
|
||||||
|
// Data structures to manage LISP-like lists.
|
||||||
|
//
|
||||||
|
// Copyright (c) 1992,1993,1995 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 LIST_H
|
||||||
|
#define LIST_H
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
|
||||||
|
class ListElement;
|
||||||
|
|
||||||
|
// The following class defines a "list" -- a singly linked list of
|
||||||
|
// list elements, each of which contains an integer.
|
||||||
|
|
||||||
|
class List {
|
||||||
|
public:
|
||||||
|
List(); // initialize the list
|
||||||
|
~List(); // de-allocate the list
|
||||||
|
|
||||||
|
void Prepend(int value); // Put item at the beginning of the list
|
||||||
|
int Remove(); // Take item off the front of the list
|
||||||
|
|
||||||
|
bool Empty(); // is the list empty?
|
||||||
|
|
||||||
|
void SelfTest();
|
||||||
|
|
||||||
|
private:
|
||||||
|
ListElement *first; // Head of the list, NULL if list is empty
|
||||||
|
ListElement *last; // Last element of list
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // LIST_H
|
||||||
135
c++example/stack.cc
Executable file
135
c++example/stack.cc
Executable file
@@ -0,0 +1,135 @@
|
|||||||
|
// stack.cc
|
||||||
|
// Routines to implement a LIFO stack of integers.
|
||||||
|
//
|
||||||
|
// The stack is represented as an array; we return an error
|
||||||
|
// if the caller tries to push more things onto the stack than we have
|
||||||
|
// room for.
|
||||||
|
//
|
||||||
|
// Copyright (c) 1992,1993,1995 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.
|
||||||
|
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#define ASSERT(expression) assert(expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <iostream.h>
|
||||||
|
#include "copyright.h"
|
||||||
|
#include "stack.h"
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Stack::Stack
|
||||||
|
// The constructor for the Stack class. Note that it doesn't have a
|
||||||
|
// return type.
|
||||||
|
//
|
||||||
|
// "sz" -- maximum number of elements on the Stack at any time
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
Stack::Stack(int sz) {
|
||||||
|
|
||||||
|
ASSERT(sz >= 1);
|
||||||
|
|
||||||
|
// Initialize the data members of the stack object.
|
||||||
|
size = sz;
|
||||||
|
top = 0;
|
||||||
|
stack = new int[size]; // allocate an array of integers.
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Stack::~Stack
|
||||||
|
// The destructor for the Stack class. Just get rid of the array we
|
||||||
|
// allocated in the constructor.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
Stack::~Stack() {
|
||||||
|
|
||||||
|
delete [] stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Stack::Push
|
||||||
|
// Put an integer on the top of the stack; error on overflow.
|
||||||
|
//
|
||||||
|
// "value" -- the value to put on the stack
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
Stack::Push(int value) {
|
||||||
|
ASSERT(!Full());
|
||||||
|
|
||||||
|
stack[top++] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Stack::Pop
|
||||||
|
// Remove an integer from the top of the stack, returning its value.
|
||||||
|
// Error if the stack is empty.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
int
|
||||||
|
Stack::Pop() {
|
||||||
|
|
||||||
|
ASSERT(!Empty());
|
||||||
|
|
||||||
|
return (stack[--top]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Stack::Full
|
||||||
|
// Return TRUE if the stack has no more room.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
bool
|
||||||
|
Stack::Full() {
|
||||||
|
return (top == size);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Stack::Empty
|
||||||
|
// Return TRUE if the stack has nothing on it.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
bool
|
||||||
|
Stack::Empty() {
|
||||||
|
return (top == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Stack::SelfTest
|
||||||
|
// Test our stack implementation by pushing 10 numbers onto the
|
||||||
|
// stack, and then print them as it pops them off.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
Stack::SelfTest() {
|
||||||
|
int count = 17;
|
||||||
|
|
||||||
|
// Put a bunch of stuff in the stack...
|
||||||
|
while (!Full()) {
|
||||||
|
cout << "pushing " << count << "\n";
|
||||||
|
Push(count++);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ... and take it out again.
|
||||||
|
while (!Empty()) {
|
||||||
|
cout << "popping " << Pop() << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// main
|
||||||
|
// Run the test code for the stack implementation.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
int
|
||||||
|
main() {
|
||||||
|
Stack *stack = new Stack(10); // Constructor with an argument.
|
||||||
|
|
||||||
|
stack->SelfTest();
|
||||||
|
|
||||||
|
delete stack; // always delete what you allocate
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
38
c++example/stack.h
Executable file
38
c++example/stack.h
Executable file
@@ -0,0 +1,38 @@
|
|||||||
|
// stack.h
|
||||||
|
// Data structures for a "stack" -- a Last-In-First-Out list of integers.
|
||||||
|
//
|
||||||
|
// Copyright (c) 1992,1993,1995 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 STACK_H // to prevent recursive includes
|
||||||
|
#define STACK_H
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
|
||||||
|
// The following defines the Stack class. The functions are
|
||||||
|
// implemented in the file stack.cc.
|
||||||
|
//
|
||||||
|
// The constructor (initializer) for the Stack is passed the number
|
||||||
|
// of elements (integers) in the stack.
|
||||||
|
|
||||||
|
class Stack {
|
||||||
|
public:
|
||||||
|
Stack(int sz); // Constructor: initialize variables, allocate space.
|
||||||
|
~Stack(); // Destructor: deallocate space allocated above.
|
||||||
|
|
||||||
|
void Push(int value); // Push an integer on the stack, checking for overflow
|
||||||
|
int Pop(); // Pop an integer off the stack, checking for underflow.
|
||||||
|
|
||||||
|
bool Full(); // Returns TRUE if the stack is full, FALSE otherwise.
|
||||||
|
bool Empty(); // Returns TRUE if the stack is empty, FALSE otherwise.
|
||||||
|
|
||||||
|
void SelfTest(); // Test whether the implementation works.
|
||||||
|
|
||||||
|
private:
|
||||||
|
int size; // The maximum capacity of the stack.
|
||||||
|
int top; // Index of the next position to be used.
|
||||||
|
int *stack; // A pointer to an array that holds the contents.
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // STACK_H
|
||||||
149
c++example/templatestack.cc
Executable file
149
c++example/templatestack.cc
Executable file
@@ -0,0 +1,149 @@
|
|||||||
|
// templatestack.cc
|
||||||
|
// Routines to implement a LIFO stack of arbitrary things.
|
||||||
|
//
|
||||||
|
// The stack is represented as an array; we return an error
|
||||||
|
// if the caller tries to push more things onto the stack than we have
|
||||||
|
// room for.
|
||||||
|
//
|
||||||
|
// Copyright (c) 1992,1993,1995 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.
|
||||||
|
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#define ASSERT(expression) assert(expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <iostream.h>
|
||||||
|
#include "copyright.h"
|
||||||
|
#include "templatestack.h"
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Stack<T>::Stack
|
||||||
|
// The constructor for the Stack class. Note that it doesn't have a
|
||||||
|
// return type.
|
||||||
|
//
|
||||||
|
// "sz" -- maximum number of elements on the Stack at any time
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
Stack<T>::Stack(int sz) {
|
||||||
|
|
||||||
|
ASSERT(sz >= 1);
|
||||||
|
|
||||||
|
// Initialize the data members of the stack object.
|
||||||
|
size = sz;
|
||||||
|
top = 0;
|
||||||
|
stack = new T[size]; // allocate an array of integers.
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Stack<T>::~Stack
|
||||||
|
// The destructor for the Stack class. Just get rid of the array we
|
||||||
|
// allocated in the constructor.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
Stack<T>::~Stack() {
|
||||||
|
|
||||||
|
delete [] stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Stack<T>::Push
|
||||||
|
// Put a T on the top of the stack; error on overflow.
|
||||||
|
//
|
||||||
|
// "value" -- the value to put on the stack
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void
|
||||||
|
Stack<T>::Push(T value) {
|
||||||
|
ASSERT(!Full());
|
||||||
|
|
||||||
|
stack[top++] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Stack<T>::Pop
|
||||||
|
// Remove a T from the top of the stack, returning its value.
|
||||||
|
// Error if the stack is empty.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
T
|
||||||
|
Stack<T>::Pop() {
|
||||||
|
|
||||||
|
ASSERT(!Empty());
|
||||||
|
|
||||||
|
return (stack[--top]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Stack<T>::Full
|
||||||
|
// Return TRUE if the stack has no more room.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
bool
|
||||||
|
Stack<T>::Full() {
|
||||||
|
return (top == size);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Stack<T>::Empty
|
||||||
|
// Return TRUE if the stack has nothing on it.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
bool
|
||||||
|
Stack<T>::Empty() {
|
||||||
|
return (top == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Stack<T>::SelfTest
|
||||||
|
// Test our stack implementation by pushing 10 T's onto the
|
||||||
|
// stack, and then print them as it pops them off.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void
|
||||||
|
Stack<T>::SelfTest(T start) {
|
||||||
|
T count = start;
|
||||||
|
|
||||||
|
// Put a bunch of stuff in the stack...
|
||||||
|
while (!Full()) {
|
||||||
|
cout << "pushing " << count << "\n";
|
||||||
|
Push(count++);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ... and take it out again.
|
||||||
|
while (!Empty()) {
|
||||||
|
cout << "popping " << Pop() << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// main
|
||||||
|
// Run the test code for the stack implementation.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
int
|
||||||
|
main() {
|
||||||
|
Stack<int> *s1 = new Stack<int>(10);
|
||||||
|
Stack<char> *s2 = new Stack<char>(10);
|
||||||
|
|
||||||
|
cout << "Testing Stack<int>\n";
|
||||||
|
s1->SelfTest(17);
|
||||||
|
|
||||||
|
cout << "Testing Stack<char>\n";
|
||||||
|
s2->SelfTest('a');
|
||||||
|
|
||||||
|
delete s1; // always delete what you allocate
|
||||||
|
delete s2; // always delete what you allocate
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
39
c++example/templatestack.h
Executable file
39
c++example/templatestack.h
Executable file
@@ -0,0 +1,39 @@
|
|||||||
|
// templatestack.h
|
||||||
|
// Data structures for a stack" -- a Last-In-First-Out list --
|
||||||
|
// of arbitrary things.
|
||||||
|
//
|
||||||
|
// Copyright (c) 1992,1993,1995 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 TEMPLATESTACK_H // to prevent recursive includes
|
||||||
|
#define TEMPLATESTACK_H
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
|
||||||
|
// The following defines the Stack class. The functions are
|
||||||
|
// implemented in the file templatestack.cc.
|
||||||
|
//
|
||||||
|
// T is the type of the thing we want to put on the stack.
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
class Stack {
|
||||||
|
public:
|
||||||
|
Stack(int sz); // Constructor
|
||||||
|
~Stack(); // Destructor
|
||||||
|
|
||||||
|
void Push(T value); // Push a T on the stack
|
||||||
|
T Pop(); // Pop a T off the stack
|
||||||
|
|
||||||
|
bool Full(); // Returns TRUE if the stack is full
|
||||||
|
bool Empty(); // Returns TRUE if the stack is empty
|
||||||
|
|
||||||
|
void SelfTest(T start); // Test whether the implementation works.
|
||||||
|
|
||||||
|
private:
|
||||||
|
int size; // The maximum capacity of the stack.
|
||||||
|
int top; // Index of the next position to be used.
|
||||||
|
T *stack; // A pointer to an array that holds the contents.
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // TEMPLATESTACK_H
|
||||||
BIN
code/.DS_Store
vendored
Normal file
BIN
code/.DS_Store
vendored
Normal file
Binary file not shown.
12
code/README
Executable file
12
code/README
Executable file
@@ -0,0 +1,12 @@
|
|||||||
|
Building Instructions:
|
||||||
|
* got to the directory build.<host>, where <host> is your working OS
|
||||||
|
* do a "make depend" to build depenencies (DO IT!)
|
||||||
|
* do a "make" to build NachOS
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
see "nachos -u" for all command line options
|
||||||
|
|
||||||
|
Building and starting user-level programs in NachOS:
|
||||||
|
* use Mips cross-compiler to build and link coff-binaries
|
||||||
|
* use coff2noff to translate the binaries to the NachOS-format
|
||||||
|
* start binary with nachos -x <path_to_file/file>
|
||||||
367
code/build.cygwin/Makefile
Executable file
367
code/build.cygwin/Makefile
Executable file
@@ -0,0 +1,367 @@
|
|||||||
|
# Copyright (c) 1992-1996 The Regents of the University of California.
|
||||||
|
# All rights reserved. See copyright.h for copyright notice and limitation
|
||||||
|
# of liability and disclaimer of warranty provisions.
|
||||||
|
#
|
||||||
|
# This is a GNU Makefile. It must be used with the GNU make program.
|
||||||
|
# At UW, the GNU make program is /software/gnu/bin/make.
|
||||||
|
# In many other places it is known as "gmake".
|
||||||
|
# You may wish to include /software/gnu/bin/ early in your command
|
||||||
|
# search path, so that you will be using GNU make when you type "make".
|
||||||
|
#
|
||||||
|
# About this Makefile:
|
||||||
|
# --------------------
|
||||||
|
#
|
||||||
|
# This Makefile is used to build the Nachos system, which includes
|
||||||
|
# the MIPS machine simulation and a simple operating system.
|
||||||
|
#
|
||||||
|
# There is a separate Makefile, in the "test" directory, that is
|
||||||
|
# used to build the Nachos test programs (which run on the
|
||||||
|
# simulated machine).
|
||||||
|
#
|
||||||
|
# There are several "build" directories, one for each type
|
||||||
|
# of machine in the MFCF computing environment
|
||||||
|
# (build.solaris, build.sunos, and build.ultrix), as well
|
||||||
|
# as a build directory for Linux (build.linux) and a generic
|
||||||
|
# build directory (build.other) for those who wish to try
|
||||||
|
# building Nachos on other platforms.
|
||||||
|
#
|
||||||
|
# This Makefile appears to be located in all of the build directories.
|
||||||
|
# If you edit it in one directory, the copies in all of the other
|
||||||
|
# directories appear to change as well. This is the desired behaviour,
|
||||||
|
# since this file is machine independent. (The file actually lives
|
||||||
|
# in build.solaris, with symbolic links from the other build directories.)
|
||||||
|
#
|
||||||
|
# The platform-dependent parts of make's instructions are located
|
||||||
|
# in the file Makefile.dep.
|
||||||
|
# There is a different Makefile.dep in each build directory.
|
||||||
|
#
|
||||||
|
# If you are in the MFCF environment, you should not have to edit
|
||||||
|
# the Makefile.dep files by hand. Any changes to the make instructions
|
||||||
|
# can be made in this file (see the instructions below) - they will
|
||||||
|
# apply no matter where you build Nachos.
|
||||||
|
# If you are not in the MFCF environment, e.g., if you are trying
|
||||||
|
# to build Nachos on Linux at home, you will probably need
|
||||||
|
# to edit Makefile.dep (in the appropriate build directory) to
|
||||||
|
# customize the make procedure to your environment.
|
||||||
|
#
|
||||||
|
# How to build Nachos for the first time:
|
||||||
|
# ---------------------------------------
|
||||||
|
#
|
||||||
|
# (1) Make sure than you are in the build directory for the
|
||||||
|
# type of machine you are logged in to (the "host" machine):
|
||||||
|
#
|
||||||
|
# host type examples build directory
|
||||||
|
# ----------- ----------- ----------------
|
||||||
|
#
|
||||||
|
# sparc/SunOS cayley,napier, build.sunos
|
||||||
|
# (SunOS 4.1.3) descartes
|
||||||
|
#
|
||||||
|
# sparc/Solaris picard.math, build.solaris
|
||||||
|
# (SunOS 5.x) hermite.math,
|
||||||
|
# markov.math,
|
||||||
|
# hypatia.math,
|
||||||
|
# hume.math
|
||||||
|
#
|
||||||
|
# mips/ULTRIX cantor.math build.ultrix
|
||||||
|
# (ULTRIX 4.2) noether.math
|
||||||
|
#
|
||||||
|
# If you are not sure what type of machine you are on,
|
||||||
|
# try the command "uname -a".
|
||||||
|
#
|
||||||
|
# (2) Type "make depend"
|
||||||
|
# - this computes file dependencies and records them
|
||||||
|
# at the end of the file Makefile.dep in
|
||||||
|
# your build directory. Have a look...
|
||||||
|
#
|
||||||
|
# (3) Type "make nachos" (or just "make").
|
||||||
|
# - make echos the commands it is executing, so that
|
||||||
|
# you can observe its progress. When the
|
||||||
|
# build is finished, you should have an
|
||||||
|
# executable "nachos" in the build directory.
|
||||||
|
#
|
||||||
|
# (4) There is no 4th step. You are done. Try running "./nachos -u".
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# How to Re-build Nachos after you have changed the code:
|
||||||
|
#--------------------------------------------------------
|
||||||
|
#
|
||||||
|
# - The Nachos source code is located in the code subdirectories:
|
||||||
|
# threads, userprog, filesys, network, and lib. You may
|
||||||
|
# change the files in any of these directories, and you can
|
||||||
|
# add new files and/or remove files. The "machine" subdirectory
|
||||||
|
# contains the hardware simulation (which is also part of
|
||||||
|
# Nachos. You may look at it, but
|
||||||
|
# you may not change it, except as noted in machine/machine.h
|
||||||
|
# - When you want to re-make Nachos, always do it in the
|
||||||
|
# "build" directory that is appropriate for the machine
|
||||||
|
# type that you are running on.
|
||||||
|
# DO NOT TRY TO MAKE NACHOS IN THE SOURCE CODE DIRECTORIES.
|
||||||
|
#
|
||||||
|
# - IF all you have done is changed C++ code in existing files
|
||||||
|
# (since the last time you made Nachos in this build directory),
|
||||||
|
# THEN all you need to do to re-make Nachos is to type
|
||||||
|
#
|
||||||
|
# "make nachos"
|
||||||
|
#
|
||||||
|
# in the build directory.
|
||||||
|
#
|
||||||
|
# - IF you have done any of the following since the last build in
|
||||||
|
# this directory:
|
||||||
|
# added new .cc files or new .h files
|
||||||
|
# added or deleted #include's from existing files
|
||||||
|
# THEN
|
||||||
|
# you must do
|
||||||
|
# "make depend"
|
||||||
|
# followed by
|
||||||
|
# "make nachos"
|
||||||
|
#
|
||||||
|
# in the build directory.
|
||||||
|
#
|
||||||
|
# Note that is is always safe to do "make depend" followed by
|
||||||
|
# "make nachos", so if you are not sure what changes you have
|
||||||
|
# made, do "make depend".
|
||||||
|
#
|
||||||
|
# - IF you have added new files (.cc or .h) since the last build,
|
||||||
|
# you should edit this Makefile before running "make depend"
|
||||||
|
# and "make nachos".
|
||||||
|
|
||||||
|
# For new .h files, simply update the appropriate "_H" list below.
|
||||||
|
# For example, if you create a file called
|
||||||
|
# "bigfile.h" in the filesys subdirectory, you should add
|
||||||
|
# "../filesys/bigfile.h" to FILESYS_H, which is defined below
|
||||||
|
|
||||||
|
# For new .cc files, update the appropriate "_C" and "_O" lists.
|
||||||
|
# For example, if you create a file called "filetable.cc" in
|
||||||
|
# the directory "userprog", you should add
|
||||||
|
# "../userprog/filetable.cc" to USERPROG_C,
|
||||||
|
# and you should add "filetable.o" to USERPROG_O.
|
||||||
|
# Note that the entry in the "_C" list includes the subdirectory
|
||||||
|
# name, while the entry on the "_O" list does not.
|
||||||
|
#
|
||||||
|
# Some Important Notes:
|
||||||
|
# ---------------------
|
||||||
|
#
|
||||||
|
# * You can clean up all of the .o and other files left behind
|
||||||
|
# by make by typeing "make clean" in the build directory.
|
||||||
|
# * You can clean up .o and other files, as well as the nachos
|
||||||
|
# executable, DISK, core, SOCKET, and other files by typing
|
||||||
|
# make "distclean"
|
||||||
|
#
|
||||||
|
# These are good ways to save space, but the next build that
|
||||||
|
# you do after cleaning will take longer than usual, since
|
||||||
|
# much of the stuff you cleaned will need to be rebuilt.
|
||||||
|
#
|
||||||
|
# * When you build Nachos on an ULTRIX machine (in build.ultrix),
|
||||||
|
# you will get lots of warning messages like this:
|
||||||
|
#
|
||||||
|
# openfile.o: does not have gp tables for all it's sectons
|
||||||
|
#
|
||||||
|
# from the loader. Ignore them. Or better yet, figure out
|
||||||
|
# how to make them go away.
|
||||||
|
#
|
||||||
|
# The Most Important Note:
|
||||||
|
# -----------------------
|
||||||
|
#
|
||||||
|
# * If "make" is behaving strangely and you cannot figure out
|
||||||
|
# why, you should REBUILD the program FROM SCRATCH.
|
||||||
|
# Yes, it is slow.
|
||||||
|
# But, there are lots of little things that can go wrong, especially
|
||||||
|
# with all of these different types of machines available.
|
||||||
|
# Rebuilding from scratch at least gives you a known starting
|
||||||
|
# place. To rebuild from scratch, go to the appropriate
|
||||||
|
# build directory and do:
|
||||||
|
#
|
||||||
|
# make distclean
|
||||||
|
# make depend
|
||||||
|
# make nachos
|
||||||
|
#
|
||||||
|
################################################################
|
||||||
|
# READ THIS: CONFIGURING NACHOS
|
||||||
|
#
|
||||||
|
# Change DEFINES (below) to
|
||||||
|
# DEFINES = -DUSE_TLB -DFILESYS_STUB
|
||||||
|
# if you want the simulated machine to use its TLB
|
||||||
|
#
|
||||||
|
# If you want to use the real Nachos file system (based on
|
||||||
|
# the simulated disk), rather than the stub, remove
|
||||||
|
# the -DFILESYS_STUB from DEFINES.
|
||||||
|
#
|
||||||
|
# There is a a fix to the MIPS simulator to enable it to properly
|
||||||
|
# handle unaligned data access. This fix is enabled by the addition
|
||||||
|
# of "-DSIM_FIX" to the DEFINES. This should be enabled by default
|
||||||
|
# and eventually will not require the symbol definition
|
||||||
|
################################################################
|
||||||
|
DEFINES = -DFILESYS_STUB -DRDATA -DSIM_FIX
|
||||||
|
|
||||||
|
|
||||||
|
#####################################################################
|
||||||
|
#
|
||||||
|
# You might want to play with the CFLAGS, but if you use -O it may
|
||||||
|
# break the thread system. You might want to use -fno-inline if
|
||||||
|
# you need to call some inline functions from the debugger.
|
||||||
|
|
||||||
|
CFLAGS = -g -Wall -fwritable-strings $(INCPATH) $(DEFINES) $(HOSTCFLAGS) -DCHANGED
|
||||||
|
LDFLAGS =
|
||||||
|
|
||||||
|
#####################################################################
|
||||||
|
CPP= cpp
|
||||||
|
CC = g++
|
||||||
|
LD = g++
|
||||||
|
AS = as
|
||||||
|
RM = rm
|
||||||
|
|
||||||
|
INCPATH = -I../network -I../filesys -I../userprog -I../threads -I../machine -I../lib
|
||||||
|
|
||||||
|
PROGRAM = nachos
|
||||||
|
|
||||||
|
#
|
||||||
|
# Edit these lists as if you add files to the source directories.
|
||||||
|
# See the instructions at the top of the file for more information.
|
||||||
|
#
|
||||||
|
|
||||||
|
LIB_H = ../lib/bitmap.h\
|
||||||
|
../lib/copyright.h\
|
||||||
|
../lib/debug.h\
|
||||||
|
../lib/hash.h\
|
||||||
|
../lib/libtest.h\
|
||||||
|
../lib/list.h\
|
||||||
|
../lib/sysdep.h\
|
||||||
|
../lib/utility.h
|
||||||
|
|
||||||
|
LIB_C = ../lib/bitmap.cc\
|
||||||
|
../lib/debug.cc\
|
||||||
|
../lib/hash.cc\
|
||||||
|
../lib/libtest.cc\
|
||||||
|
../lib/list.cc\
|
||||||
|
../lib/sysdep.cc
|
||||||
|
|
||||||
|
LIB_O = bitmap.o debug.o libtest.o sysdep.o
|
||||||
|
|
||||||
|
|
||||||
|
MACHINE_H = ../machine/callback.h\
|
||||||
|
../machine/interrupt.h\
|
||||||
|
../machine/stats.h\
|
||||||
|
../machine/timer.h\
|
||||||
|
../machine/console.h\
|
||||||
|
../machine/machine.h\
|
||||||
|
../machine/mipssim.h\
|
||||||
|
../machine/translate.h\
|
||||||
|
../machine/network.h\
|
||||||
|
../machine/disk.h
|
||||||
|
|
||||||
|
MACHINE_C = ../machine/interrupt.cc\
|
||||||
|
../machine/stats.cc\
|
||||||
|
../machine/timer.cc\
|
||||||
|
../machine/console.cc\
|
||||||
|
../machine/machine.cc\
|
||||||
|
../machine/mipssim.cc\
|
||||||
|
../machine/translate.cc\
|
||||||
|
../machine/network.cc\
|
||||||
|
../machine/disk.cc
|
||||||
|
|
||||||
|
MACHINE_O = interrupt.o stats.o timer.o console.o machine.o mipssim.o\
|
||||||
|
translate.o network.o disk.o
|
||||||
|
|
||||||
|
THREAD_H = ../threads/alarm.h\
|
||||||
|
../threads/kernel.h\
|
||||||
|
../threads/main.h\
|
||||||
|
../threads/scheduler.h\
|
||||||
|
../threads/switch.h\
|
||||||
|
../threads/synch.h\
|
||||||
|
../threads/synchlist.h\
|
||||||
|
../threads/thread.h
|
||||||
|
|
||||||
|
THREAD_C = ../threads/alarm.cc\
|
||||||
|
../threads/kernel.cc\
|
||||||
|
../threads/main.cc\
|
||||||
|
../threads/scheduler.cc\
|
||||||
|
../threads/synch.cc\
|
||||||
|
../threads/synchlist.cc\
|
||||||
|
../threads/thread.cc
|
||||||
|
|
||||||
|
THREAD_O = alarm.o kernel.o main.o scheduler.o synch.o thread.o
|
||||||
|
|
||||||
|
USERPROG_H = ../userprog/addrspace.h\
|
||||||
|
../userprog/syscall.h\
|
||||||
|
../userprog/synchconsole.h\
|
||||||
|
../userprog/noff.h
|
||||||
|
|
||||||
|
USERPROG_C = ../userprog/addrspace.cc\
|
||||||
|
../userprog/exception.cc\
|
||||||
|
../userprog/synchconsole.cc
|
||||||
|
|
||||||
|
USERPROG_O = addrspace.o exception.o synchconsole.o
|
||||||
|
|
||||||
|
FILESYS_H =../filesys/directory.h \
|
||||||
|
../filesys/filehdr.h\
|
||||||
|
../filesys/filesys.h \
|
||||||
|
../filesys/openfile.h\
|
||||||
|
../filesys/pbitmap.h\
|
||||||
|
../filesys/synchdisk.h
|
||||||
|
|
||||||
|
FILESYS_C =../filesys/directory.cc\
|
||||||
|
../filesys/filehdr.cc\
|
||||||
|
../filesys/filesys.cc\
|
||||||
|
../filesys/pbitmap.cc\
|
||||||
|
../filesys/openfile.cc\
|
||||||
|
../filesys/synchdisk.cc\
|
||||||
|
|
||||||
|
FILESYS_O =directory.o filehdr.o filesys.o pbitmap.o openfile.o synchdisk.o
|
||||||
|
|
||||||
|
NETWORK_H = ../network/post.h
|
||||||
|
|
||||||
|
NETWORK_C = ../network/post.cc
|
||||||
|
|
||||||
|
NETWORK_O = post.o
|
||||||
|
|
||||||
|
##################################################################
|
||||||
|
# You probably don't want to change anything below this point in
|
||||||
|
# the file unless you are comfortable with GNU make and know what
|
||||||
|
# you are doing...
|
||||||
|
##################################################################
|
||||||
|
|
||||||
|
THREAD_S = ../threads/switch.s
|
||||||
|
|
||||||
|
HFILES = $(LIB_H) $(MACHINE_H) $(THREAD_H) $(USERPROG_H) $(FILESYS_H) $(NETWORK_H)
|
||||||
|
CFILES = $(LIB_C) $(MACHINE_C) $(THREAD_C) $(USERPROG_C) $(FILESYS_C) $(NETWORK_C)
|
||||||
|
|
||||||
|
C_OFILES = $(LIB_O) $(MACHINE_O) $(THREAD_O) $(USERPROG_O) $(FILESYS_O) $(NETWORK_O)
|
||||||
|
|
||||||
|
S_OFILES = switch.o
|
||||||
|
OFILES = $(C_OFILES) $(S_OFILES)
|
||||||
|
|
||||||
|
$(PROGRAM): $(OFILES)
|
||||||
|
$(LD) $(OFILES) $(LDFLAGS) -o $(PROGRAM)
|
||||||
|
|
||||||
|
$(C_OFILES): %.o:
|
||||||
|
$(CC) $(CFLAGS) -c $<
|
||||||
|
|
||||||
|
switch.o: ../threads/switch.s
|
||||||
|
$(CPP) $(CPP_AS_FLAGS) -P $(INCPATH) $(HOSTCFLAGS) ../threads/switch.s > swtch.s
|
||||||
|
$(AS) -o switch.o swtch.s
|
||||||
|
|
||||||
|
depend: $(CFILES) $(HFILES)
|
||||||
|
$(CC) $(INCPATH) $(DEFINES) $(HOSTCFLAGS) -DCHANGED -M $(CFILES) > makedep
|
||||||
|
@echo '/^# DO NOT DELETE THIS LINE/+2,$$d' >eddep
|
||||||
|
@echo '$$r makedep' >>eddep
|
||||||
|
@echo 'w' >>eddep
|
||||||
|
@echo 'q' >>eddep
|
||||||
|
ed - Makefile.dep < eddep
|
||||||
|
rm eddep makedep
|
||||||
|
@echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.dep
|
||||||
|
@echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.dep
|
||||||
|
@echo '# see make depend above' >> Makefile.dep
|
||||||
|
|
||||||
|
clean:
|
||||||
|
$(RM) -f $(OFILES)
|
||||||
|
$(RM) -f swtch.s
|
||||||
|
$(RM) -f *.s *.ii
|
||||||
|
|
||||||
|
distclean: clean
|
||||||
|
$(RM) -f $(PROGRAM)
|
||||||
|
$(RM) -f $(PROGRAM).exe
|
||||||
|
$(RM) -f DISK_?
|
||||||
|
$(RM) -f core
|
||||||
|
$(RM) -f SOCKET_?
|
||||||
|
|
||||||
|
include Makefile.dep
|
||||||
544
code/build.cygwin/Makefile.dep
Executable file
544
code/build.cygwin/Makefile.dep
Executable file
@@ -0,0 +1,544 @@
|
|||||||
|
##################################################################
|
||||||
|
# Machine Dependencies - this file is included automatically
|
||||||
|
# into the main Makefile
|
||||||
|
#
|
||||||
|
# This file contains definitions below for x86 running Linux
|
||||||
|
# It has *not* been tested!
|
||||||
|
##################################################################
|
||||||
|
|
||||||
|
HOSTCFLAGS = -Dx86 -DLINUX -DCYGWIN
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------
|
||||||
|
# Do not put anything below this point - it will be destroyed by
|
||||||
|
# "make depend"
|
||||||
|
#
|
||||||
|
# DO NOT DELETE THIS LINE -- make depend uses it
|
||||||
|
# DEPENDENCIES MUST END AT END OF FILE
|
||||||
|
bitmap.o: ../lib/bitmap.cc ../lib/copyright.h ../lib/debug.h \
|
||||||
|
../lib/utility.h ../lib/sysdep.h /usr/include/g++-3/iostream.h \
|
||||||
|
/usr/include/g++-3/streambuf.h /usr/include/g++-3/libio.h \
|
||||||
|
/usr/include/_G_config.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
|
||||||
|
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
|
||||||
|
/usr/include/sys/config.h /usr/include/sys/reent.h \
|
||||||
|
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
|
||||||
|
/usr/include/alloca.h /usr/include/stdio.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
|
||||||
|
/usr/include/sys/types.h /usr/include/machine/types.h \
|
||||||
|
/usr/include/sys/features.h /usr/include/cygwin/types.h \
|
||||||
|
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
|
||||||
|
/usr/include/string.h ../lib/bitmap.h
|
||||||
|
debug.o: ../lib/debug.cc ../lib/copyright.h ../lib/utility.h \
|
||||||
|
../lib/debug.h ../lib/sysdep.h /usr/include/g++-3/iostream.h \
|
||||||
|
/usr/include/g++-3/streambuf.h /usr/include/g++-3/libio.h \
|
||||||
|
/usr/include/_G_config.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
|
||||||
|
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
|
||||||
|
/usr/include/sys/config.h /usr/include/sys/reent.h \
|
||||||
|
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
|
||||||
|
/usr/include/alloca.h /usr/include/stdio.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
|
||||||
|
/usr/include/sys/types.h /usr/include/machine/types.h \
|
||||||
|
/usr/include/sys/features.h /usr/include/cygwin/types.h \
|
||||||
|
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
|
||||||
|
/usr/include/string.h
|
||||||
|
hash.o: ../lib/hash.cc ../lib/copyright.h
|
||||||
|
libtest.o: ../lib/libtest.cc ../lib/copyright.h ../lib/libtest.h \
|
||||||
|
../lib/bitmap.h ../lib/utility.h ../lib/list.h ../lib/debug.h \
|
||||||
|
../lib/sysdep.h /usr/include/g++-3/iostream.h \
|
||||||
|
/usr/include/g++-3/streambuf.h /usr/include/g++-3/libio.h \
|
||||||
|
/usr/include/_G_config.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
|
||||||
|
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
|
||||||
|
/usr/include/sys/config.h /usr/include/sys/reent.h \
|
||||||
|
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
|
||||||
|
/usr/include/alloca.h /usr/include/stdio.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
|
||||||
|
/usr/include/sys/types.h /usr/include/machine/types.h \
|
||||||
|
/usr/include/sys/features.h /usr/include/cygwin/types.h \
|
||||||
|
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
|
||||||
|
/usr/include/string.h ../lib/list.cc ../lib/hash.h ../lib/hash.cc
|
||||||
|
list.o: ../lib/list.cc ../lib/copyright.h
|
||||||
|
sysdep.o: ../lib/sysdep.cc ../lib/copyright.h ../lib/debug.h \
|
||||||
|
../lib/utility.h ../lib/sysdep.h /usr/include/g++-3/iostream.h \
|
||||||
|
/usr/include/g++-3/streambuf.h /usr/include/g++-3/libio.h \
|
||||||
|
/usr/include/_G_config.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
|
||||||
|
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
|
||||||
|
/usr/include/sys/config.h /usr/include/sys/reent.h \
|
||||||
|
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
|
||||||
|
/usr/include/alloca.h /usr/include/stdio.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
|
||||||
|
/usr/include/sys/types.h /usr/include/machine/types.h \
|
||||||
|
/usr/include/sys/features.h /usr/include/cygwin/types.h \
|
||||||
|
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
|
||||||
|
/usr/include/string.h /usr/include/unistd.h /usr/include/sys/unistd.h \
|
||||||
|
/usr/include/getopt.h /usr/include/sys/time.h \
|
||||||
|
/usr/include/sys/select.h /usr/include/time.h \
|
||||||
|
/usr/include/machine/time.h /usr/include/sys/file.h \
|
||||||
|
/usr/include/fcntl.h /usr/include/sys/fcntl.h /usr/include/sys/stat.h \
|
||||||
|
/usr/include/cygwin/stat.h /usr/include/sys/socket.h \
|
||||||
|
/usr/include/features.h /usr/include/cygwin/socket.h \
|
||||||
|
/usr/include/asm/socket.h /usr/include/cygwin/if.h \
|
||||||
|
/usr/include/cygwin/sockios.h /usr/include/cygwin/uio.h \
|
||||||
|
/usr/include/sys/un.h /usr/include/signal.h /usr/include/sys/signal.h
|
||||||
|
interrupt.o: ../machine/interrupt.cc ../lib/copyright.h \
|
||||||
|
../machine/interrupt.h ../lib/list.h ../lib/debug.h ../lib/utility.h \
|
||||||
|
../lib/sysdep.h /usr/include/g++-3/iostream.h \
|
||||||
|
/usr/include/g++-3/streambuf.h /usr/include/g++-3/libio.h \
|
||||||
|
/usr/include/_G_config.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
|
||||||
|
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
|
||||||
|
/usr/include/sys/config.h /usr/include/sys/reent.h \
|
||||||
|
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
|
||||||
|
/usr/include/alloca.h /usr/include/stdio.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
|
||||||
|
/usr/include/sys/types.h /usr/include/machine/types.h \
|
||||||
|
/usr/include/sys/features.h /usr/include/cygwin/types.h \
|
||||||
|
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
|
||||||
|
/usr/include/string.h ../lib/list.cc ../machine/callback.h \
|
||||||
|
../threads/main.h ../threads/kernel.h ../threads/thread.h \
|
||||||
|
../machine/machine.h ../machine/translate.h ../userprog/addrspace.h \
|
||||||
|
../filesys/filesys.h ../filesys/openfile.h ../threads/scheduler.h \
|
||||||
|
../machine/stats.h ../threads/alarm.h ../machine/timer.h
|
||||||
|
stats.o: ../machine/stats.cc ../lib/copyright.h ../lib/debug.h \
|
||||||
|
../lib/utility.h ../lib/sysdep.h /usr/include/g++-3/iostream.h \
|
||||||
|
/usr/include/g++-3/streambuf.h /usr/include/g++-3/libio.h \
|
||||||
|
/usr/include/_G_config.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
|
||||||
|
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
|
||||||
|
/usr/include/sys/config.h /usr/include/sys/reent.h \
|
||||||
|
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
|
||||||
|
/usr/include/alloca.h /usr/include/stdio.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
|
||||||
|
/usr/include/sys/types.h /usr/include/machine/types.h \
|
||||||
|
/usr/include/sys/features.h /usr/include/cygwin/types.h \
|
||||||
|
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
|
||||||
|
/usr/include/string.h ../machine/stats.h
|
||||||
|
timer.o: ../machine/timer.cc ../lib/copyright.h ../machine/timer.h \
|
||||||
|
../lib/utility.h ../machine/callback.h ../threads/main.h \
|
||||||
|
../lib/debug.h ../lib/sysdep.h /usr/include/g++-3/iostream.h \
|
||||||
|
/usr/include/g++-3/streambuf.h /usr/include/g++-3/libio.h \
|
||||||
|
/usr/include/_G_config.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
|
||||||
|
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
|
||||||
|
/usr/include/sys/config.h /usr/include/sys/reent.h \
|
||||||
|
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
|
||||||
|
/usr/include/alloca.h /usr/include/stdio.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
|
||||||
|
/usr/include/sys/types.h /usr/include/machine/types.h \
|
||||||
|
/usr/include/sys/features.h /usr/include/cygwin/types.h \
|
||||||
|
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
|
||||||
|
/usr/include/string.h ../threads/kernel.h ../threads/thread.h \
|
||||||
|
../machine/machine.h ../machine/translate.h ../userprog/addrspace.h \
|
||||||
|
../filesys/filesys.h ../filesys/openfile.h ../threads/scheduler.h \
|
||||||
|
../lib/list.h ../lib/list.cc ../machine/interrupt.h \
|
||||||
|
../machine/stats.h ../threads/alarm.h
|
||||||
|
console.o: ../machine/console.cc ../lib/copyright.h \
|
||||||
|
../machine/console.h ../lib/utility.h ../machine/callback.h \
|
||||||
|
../threads/main.h ../lib/debug.h ../lib/sysdep.h \
|
||||||
|
/usr/include/g++-3/iostream.h /usr/include/g++-3/streambuf.h \
|
||||||
|
/usr/include/g++-3/libio.h /usr/include/_G_config.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
|
||||||
|
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
|
||||||
|
/usr/include/sys/config.h /usr/include/sys/reent.h \
|
||||||
|
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
|
||||||
|
/usr/include/alloca.h /usr/include/stdio.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
|
||||||
|
/usr/include/sys/types.h /usr/include/machine/types.h \
|
||||||
|
/usr/include/sys/features.h /usr/include/cygwin/types.h \
|
||||||
|
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
|
||||||
|
/usr/include/string.h ../threads/kernel.h ../threads/thread.h \
|
||||||
|
../machine/machine.h ../machine/translate.h ../userprog/addrspace.h \
|
||||||
|
../filesys/filesys.h ../filesys/openfile.h ../threads/scheduler.h \
|
||||||
|
../lib/list.h ../lib/list.cc ../machine/interrupt.h \
|
||||||
|
../machine/stats.h ../threads/alarm.h ../machine/timer.h
|
||||||
|
machine.o: ../machine/machine.cc ../lib/copyright.h \
|
||||||
|
../machine/machine.h ../lib/utility.h ../machine/translate.h \
|
||||||
|
../threads/main.h ../lib/debug.h ../lib/sysdep.h \
|
||||||
|
/usr/include/g++-3/iostream.h /usr/include/g++-3/streambuf.h \
|
||||||
|
/usr/include/g++-3/libio.h /usr/include/_G_config.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
|
||||||
|
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
|
||||||
|
/usr/include/sys/config.h /usr/include/sys/reent.h \
|
||||||
|
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
|
||||||
|
/usr/include/alloca.h /usr/include/stdio.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
|
||||||
|
/usr/include/sys/types.h /usr/include/machine/types.h \
|
||||||
|
/usr/include/sys/features.h /usr/include/cygwin/types.h \
|
||||||
|
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
|
||||||
|
/usr/include/string.h ../threads/kernel.h ../threads/thread.h \
|
||||||
|
../userprog/addrspace.h ../filesys/filesys.h ../filesys/openfile.h \
|
||||||
|
../threads/scheduler.h ../lib/list.h ../lib/list.cc \
|
||||||
|
../machine/interrupt.h ../machine/callback.h ../machine/stats.h \
|
||||||
|
../threads/alarm.h ../machine/timer.h
|
||||||
|
mipssim.o: ../machine/mipssim.cc ../lib/copyright.h ../lib/debug.h \
|
||||||
|
../lib/utility.h ../lib/sysdep.h /usr/include/g++-3/iostream.h \
|
||||||
|
/usr/include/g++-3/streambuf.h /usr/include/g++-3/libio.h \
|
||||||
|
/usr/include/_G_config.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
|
||||||
|
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
|
||||||
|
/usr/include/sys/config.h /usr/include/sys/reent.h \
|
||||||
|
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
|
||||||
|
/usr/include/alloca.h /usr/include/stdio.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
|
||||||
|
/usr/include/sys/types.h /usr/include/machine/types.h \
|
||||||
|
/usr/include/sys/features.h /usr/include/cygwin/types.h \
|
||||||
|
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
|
||||||
|
/usr/include/string.h ../machine/machine.h ../machine/translate.h \
|
||||||
|
../machine/mipssim.h ../threads/main.h ../threads/kernel.h \
|
||||||
|
../threads/thread.h ../userprog/addrspace.h ../filesys/filesys.h \
|
||||||
|
../filesys/openfile.h ../threads/scheduler.h ../lib/list.h \
|
||||||
|
../lib/list.cc ../machine/interrupt.h ../machine/callback.h \
|
||||||
|
../machine/stats.h ../threads/alarm.h ../machine/timer.h
|
||||||
|
translate.o: ../machine/translate.cc ../lib/copyright.h \
|
||||||
|
../threads/main.h ../lib/debug.h ../lib/utility.h ../lib/sysdep.h \
|
||||||
|
/usr/include/g++-3/iostream.h /usr/include/g++-3/streambuf.h \
|
||||||
|
/usr/include/g++-3/libio.h /usr/include/_G_config.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
|
||||||
|
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
|
||||||
|
/usr/include/sys/config.h /usr/include/sys/reent.h \
|
||||||
|
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
|
||||||
|
/usr/include/alloca.h /usr/include/stdio.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
|
||||||
|
/usr/include/sys/types.h /usr/include/machine/types.h \
|
||||||
|
/usr/include/sys/features.h /usr/include/cygwin/types.h \
|
||||||
|
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
|
||||||
|
/usr/include/string.h ../threads/kernel.h ../threads/thread.h \
|
||||||
|
../machine/machine.h ../machine/translate.h ../userprog/addrspace.h \
|
||||||
|
../filesys/filesys.h ../filesys/openfile.h ../threads/scheduler.h \
|
||||||
|
../lib/list.h ../lib/list.cc ../machine/interrupt.h \
|
||||||
|
../machine/callback.h ../machine/stats.h ../threads/alarm.h \
|
||||||
|
../machine/timer.h
|
||||||
|
network.o: ../machine/network.cc ../lib/copyright.h \
|
||||||
|
../machine/network.h ../lib/utility.h ../machine/callback.h \
|
||||||
|
../threads/main.h ../lib/debug.h ../lib/sysdep.h \
|
||||||
|
/usr/include/g++-3/iostream.h /usr/include/g++-3/streambuf.h \
|
||||||
|
/usr/include/g++-3/libio.h /usr/include/_G_config.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
|
||||||
|
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
|
||||||
|
/usr/include/sys/config.h /usr/include/sys/reent.h \
|
||||||
|
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
|
||||||
|
/usr/include/alloca.h /usr/include/stdio.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
|
||||||
|
/usr/include/sys/types.h /usr/include/machine/types.h \
|
||||||
|
/usr/include/sys/features.h /usr/include/cygwin/types.h \
|
||||||
|
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
|
||||||
|
/usr/include/string.h ../threads/kernel.h ../threads/thread.h \
|
||||||
|
../machine/machine.h ../machine/translate.h ../userprog/addrspace.h \
|
||||||
|
../filesys/filesys.h ../filesys/openfile.h ../threads/scheduler.h \
|
||||||
|
../lib/list.h ../lib/list.cc ../machine/interrupt.h \
|
||||||
|
../machine/stats.h ../threads/alarm.h ../machine/timer.h
|
||||||
|
disk.o: ../machine/disk.cc ../lib/copyright.h ../machine/disk.h \
|
||||||
|
../lib/utility.h ../machine/callback.h ../lib/debug.h ../lib/sysdep.h \
|
||||||
|
/usr/include/g++-3/iostream.h /usr/include/g++-3/streambuf.h \
|
||||||
|
/usr/include/g++-3/libio.h /usr/include/_G_config.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
|
||||||
|
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
|
||||||
|
/usr/include/sys/config.h /usr/include/sys/reent.h \
|
||||||
|
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
|
||||||
|
/usr/include/alloca.h /usr/include/stdio.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
|
||||||
|
/usr/include/sys/types.h /usr/include/machine/types.h \
|
||||||
|
/usr/include/sys/features.h /usr/include/cygwin/types.h \
|
||||||
|
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
|
||||||
|
/usr/include/string.h ../threads/main.h ../threads/kernel.h \
|
||||||
|
../threads/thread.h ../machine/machine.h ../machine/translate.h \
|
||||||
|
../userprog/addrspace.h ../filesys/filesys.h ../filesys/openfile.h \
|
||||||
|
../threads/scheduler.h ../lib/list.h ../lib/list.cc \
|
||||||
|
../machine/interrupt.h ../machine/stats.h ../threads/alarm.h \
|
||||||
|
../machine/timer.h
|
||||||
|
alarm.o: ../threads/alarm.cc ../lib/copyright.h ../threads/alarm.h \
|
||||||
|
../lib/utility.h ../machine/callback.h ../machine/timer.h \
|
||||||
|
../threads/main.h ../lib/debug.h ../lib/sysdep.h \
|
||||||
|
/usr/include/g++-3/iostream.h /usr/include/g++-3/streambuf.h \
|
||||||
|
/usr/include/g++-3/libio.h /usr/include/_G_config.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
|
||||||
|
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
|
||||||
|
/usr/include/sys/config.h /usr/include/sys/reent.h \
|
||||||
|
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
|
||||||
|
/usr/include/alloca.h /usr/include/stdio.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
|
||||||
|
/usr/include/sys/types.h /usr/include/machine/types.h \
|
||||||
|
/usr/include/sys/features.h /usr/include/cygwin/types.h \
|
||||||
|
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
|
||||||
|
/usr/include/string.h ../threads/kernel.h ../threads/thread.h \
|
||||||
|
../machine/machine.h ../machine/translate.h ../userprog/addrspace.h \
|
||||||
|
../filesys/filesys.h ../filesys/openfile.h ../threads/scheduler.h \
|
||||||
|
../lib/list.h ../lib/list.cc ../machine/interrupt.h \
|
||||||
|
../machine/stats.h
|
||||||
|
kernel.o: ../threads/kernel.cc ../lib/copyright.h ../lib/debug.h \
|
||||||
|
../lib/utility.h ../lib/sysdep.h /usr/include/g++-3/iostream.h \
|
||||||
|
/usr/include/g++-3/streambuf.h /usr/include/g++-3/libio.h \
|
||||||
|
/usr/include/_G_config.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
|
||||||
|
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
|
||||||
|
/usr/include/sys/config.h /usr/include/sys/reent.h \
|
||||||
|
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
|
||||||
|
/usr/include/alloca.h /usr/include/stdio.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
|
||||||
|
/usr/include/sys/types.h /usr/include/machine/types.h \
|
||||||
|
/usr/include/sys/features.h /usr/include/cygwin/types.h \
|
||||||
|
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
|
||||||
|
/usr/include/string.h ../threads/main.h ../threads/kernel.h \
|
||||||
|
../threads/thread.h ../machine/machine.h ../machine/translate.h \
|
||||||
|
../userprog/addrspace.h ../filesys/filesys.h ../filesys/openfile.h \
|
||||||
|
../threads/scheduler.h ../lib/list.h ../lib/list.cc \
|
||||||
|
../machine/interrupt.h ../machine/callback.h ../machine/stats.h \
|
||||||
|
../threads/alarm.h ../machine/timer.h ../threads/synch.h \
|
||||||
|
../threads/synchlist.h ../threads/synchlist.cc ../lib/libtest.h \
|
||||||
|
../userprog/synchconsole.h ../machine/console.h \
|
||||||
|
../filesys/synchdisk.h ../machine/disk.h ../network/post.h \
|
||||||
|
../machine/network.h
|
||||||
|
main.o: ../threads/main.cc ../lib/copyright.h ../threads/main.h \
|
||||||
|
../lib/debug.h ../lib/utility.h ../lib/sysdep.h \
|
||||||
|
/usr/include/g++-3/iostream.h /usr/include/g++-3/streambuf.h \
|
||||||
|
/usr/include/g++-3/libio.h /usr/include/_G_config.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
|
||||||
|
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
|
||||||
|
/usr/include/sys/config.h /usr/include/sys/reent.h \
|
||||||
|
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
|
||||||
|
/usr/include/alloca.h /usr/include/stdio.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
|
||||||
|
/usr/include/sys/types.h /usr/include/machine/types.h \
|
||||||
|
/usr/include/sys/features.h /usr/include/cygwin/types.h \
|
||||||
|
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
|
||||||
|
/usr/include/string.h ../threads/kernel.h ../threads/thread.h \
|
||||||
|
../machine/machine.h ../machine/translate.h ../userprog/addrspace.h \
|
||||||
|
../filesys/filesys.h ../filesys/openfile.h ../threads/scheduler.h \
|
||||||
|
../lib/list.h ../lib/list.cc ../machine/interrupt.h \
|
||||||
|
../machine/callback.h ../machine/stats.h ../threads/alarm.h \
|
||||||
|
../machine/timer.h
|
||||||
|
scheduler.o: ../threads/scheduler.cc ../lib/copyright.h ../lib/debug.h \
|
||||||
|
../lib/utility.h ../lib/sysdep.h /usr/include/g++-3/iostream.h \
|
||||||
|
/usr/include/g++-3/streambuf.h /usr/include/g++-3/libio.h \
|
||||||
|
/usr/include/_G_config.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
|
||||||
|
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
|
||||||
|
/usr/include/sys/config.h /usr/include/sys/reent.h \
|
||||||
|
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
|
||||||
|
/usr/include/alloca.h /usr/include/stdio.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
|
||||||
|
/usr/include/sys/types.h /usr/include/machine/types.h \
|
||||||
|
/usr/include/sys/features.h /usr/include/cygwin/types.h \
|
||||||
|
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
|
||||||
|
/usr/include/string.h ../threads/scheduler.h ../lib/list.h \
|
||||||
|
../lib/list.cc ../threads/thread.h ../machine/machine.h \
|
||||||
|
../machine/translate.h ../userprog/addrspace.h ../filesys/filesys.h \
|
||||||
|
../filesys/openfile.h ../threads/main.h ../threads/kernel.h \
|
||||||
|
../machine/interrupt.h ../machine/callback.h ../machine/stats.h \
|
||||||
|
../threads/alarm.h ../machine/timer.h
|
||||||
|
synch.o: ../threads/synch.cc ../lib/copyright.h ../threads/synch.h \
|
||||||
|
../threads/thread.h ../lib/utility.h ../lib/sysdep.h \
|
||||||
|
/usr/include/g++-3/iostream.h /usr/include/g++-3/streambuf.h \
|
||||||
|
/usr/include/g++-3/libio.h /usr/include/_G_config.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
|
||||||
|
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
|
||||||
|
/usr/include/sys/config.h /usr/include/sys/reent.h \
|
||||||
|
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
|
||||||
|
/usr/include/alloca.h /usr/include/stdio.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
|
||||||
|
/usr/include/sys/types.h /usr/include/machine/types.h \
|
||||||
|
/usr/include/sys/features.h /usr/include/cygwin/types.h \
|
||||||
|
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
|
||||||
|
/usr/include/string.h ../machine/machine.h ../machine/translate.h \
|
||||||
|
../userprog/addrspace.h ../filesys/filesys.h ../filesys/openfile.h \
|
||||||
|
../lib/list.h ../lib/debug.h ../lib/list.cc ../threads/main.h \
|
||||||
|
../threads/kernel.h ../threads/scheduler.h ../machine/interrupt.h \
|
||||||
|
../machine/callback.h ../machine/stats.h ../threads/alarm.h \
|
||||||
|
../machine/timer.h
|
||||||
|
synchlist.o: ../threads/synchlist.cc ../lib/copyright.h \
|
||||||
|
../threads/synchlist.h ../lib/list.h ../lib/debug.h ../lib/utility.h \
|
||||||
|
../lib/sysdep.h /usr/include/g++-3/iostream.h \
|
||||||
|
/usr/include/g++-3/streambuf.h /usr/include/g++-3/libio.h \
|
||||||
|
/usr/include/_G_config.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
|
||||||
|
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
|
||||||
|
/usr/include/sys/config.h /usr/include/sys/reent.h \
|
||||||
|
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
|
||||||
|
/usr/include/alloca.h /usr/include/stdio.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
|
||||||
|
/usr/include/sys/types.h /usr/include/machine/types.h \
|
||||||
|
/usr/include/sys/features.h /usr/include/cygwin/types.h \
|
||||||
|
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
|
||||||
|
/usr/include/string.h ../lib/list.cc ../threads/synch.h \
|
||||||
|
../threads/thread.h ../machine/machine.h ../machine/translate.h \
|
||||||
|
../userprog/addrspace.h ../filesys/filesys.h ../filesys/openfile.h \
|
||||||
|
../threads/main.h ../threads/kernel.h ../threads/scheduler.h \
|
||||||
|
../machine/interrupt.h ../machine/callback.h ../machine/stats.h \
|
||||||
|
../threads/alarm.h ../machine/timer.h ../threads/synchlist.cc
|
||||||
|
thread.o: ../threads/thread.cc ../lib/copyright.h ../threads/thread.h \
|
||||||
|
../lib/utility.h ../lib/sysdep.h /usr/include/g++-3/iostream.h \
|
||||||
|
/usr/include/g++-3/streambuf.h /usr/include/g++-3/libio.h \
|
||||||
|
/usr/include/_G_config.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
|
||||||
|
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
|
||||||
|
/usr/include/sys/config.h /usr/include/sys/reent.h \
|
||||||
|
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
|
||||||
|
/usr/include/alloca.h /usr/include/stdio.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
|
||||||
|
/usr/include/sys/types.h /usr/include/machine/types.h \
|
||||||
|
/usr/include/sys/features.h /usr/include/cygwin/types.h \
|
||||||
|
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
|
||||||
|
/usr/include/string.h ../machine/machine.h ../machine/translate.h \
|
||||||
|
../userprog/addrspace.h ../filesys/filesys.h ../filesys/openfile.h \
|
||||||
|
../threads/switch.h ../threads/synch.h ../lib/list.h ../lib/debug.h \
|
||||||
|
../lib/list.cc ../threads/main.h ../threads/kernel.h \
|
||||||
|
../threads/scheduler.h ../machine/interrupt.h ../machine/callback.h \
|
||||||
|
../machine/stats.h ../threads/alarm.h ../machine/timer.h
|
||||||
|
addrspace.o: ../userprog/addrspace.cc ../lib/copyright.h \
|
||||||
|
../threads/main.h ../lib/debug.h ../lib/utility.h ../lib/sysdep.h \
|
||||||
|
/usr/include/g++-3/iostream.h /usr/include/g++-3/streambuf.h \
|
||||||
|
/usr/include/g++-3/libio.h /usr/include/_G_config.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
|
||||||
|
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
|
||||||
|
/usr/include/sys/config.h /usr/include/sys/reent.h \
|
||||||
|
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
|
||||||
|
/usr/include/alloca.h /usr/include/stdio.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
|
||||||
|
/usr/include/sys/types.h /usr/include/machine/types.h \
|
||||||
|
/usr/include/sys/features.h /usr/include/cygwin/types.h \
|
||||||
|
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
|
||||||
|
/usr/include/string.h ../threads/kernel.h ../threads/thread.h \
|
||||||
|
../machine/machine.h ../machine/translate.h ../userprog/addrspace.h \
|
||||||
|
../filesys/filesys.h ../filesys/openfile.h ../threads/scheduler.h \
|
||||||
|
../lib/list.h ../lib/list.cc ../machine/interrupt.h \
|
||||||
|
../machine/callback.h ../machine/stats.h ../threads/alarm.h \
|
||||||
|
../machine/timer.h ../userprog/noff.h
|
||||||
|
exception.o: ../userprog/exception.cc ../lib/copyright.h \
|
||||||
|
../threads/main.h ../lib/debug.h ../lib/utility.h ../lib/sysdep.h \
|
||||||
|
/usr/include/g++-3/iostream.h /usr/include/g++-3/streambuf.h \
|
||||||
|
/usr/include/g++-3/libio.h /usr/include/_G_config.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
|
||||||
|
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
|
||||||
|
/usr/include/sys/config.h /usr/include/sys/reent.h \
|
||||||
|
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
|
||||||
|
/usr/include/alloca.h /usr/include/stdio.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
|
||||||
|
/usr/include/sys/types.h /usr/include/machine/types.h \
|
||||||
|
/usr/include/sys/features.h /usr/include/cygwin/types.h \
|
||||||
|
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
|
||||||
|
/usr/include/string.h ../threads/kernel.h ../threads/thread.h \
|
||||||
|
../machine/machine.h ../machine/translate.h ../userprog/addrspace.h \
|
||||||
|
../filesys/filesys.h ../filesys/openfile.h ../threads/scheduler.h \
|
||||||
|
../lib/list.h ../lib/list.cc ../machine/interrupt.h \
|
||||||
|
../machine/callback.h ../machine/stats.h ../threads/alarm.h \
|
||||||
|
../machine/timer.h ../userprog/syscall.h ../userprog/errno.h \
|
||||||
|
../userprog/ksyscall.h
|
||||||
|
synchconsole.o: ../userprog/synchconsole.cc ../lib/copyright.h \
|
||||||
|
../userprog/synchconsole.h ../lib/utility.h ../machine/callback.h \
|
||||||
|
../machine/console.h ../threads/synch.h ../threads/thread.h \
|
||||||
|
../lib/sysdep.h /usr/include/g++-3/iostream.h \
|
||||||
|
/usr/include/g++-3/streambuf.h /usr/include/g++-3/libio.h \
|
||||||
|
/usr/include/_G_config.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
|
||||||
|
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
|
||||||
|
/usr/include/sys/config.h /usr/include/sys/reent.h \
|
||||||
|
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
|
||||||
|
/usr/include/alloca.h /usr/include/stdio.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
|
||||||
|
/usr/include/sys/types.h /usr/include/machine/types.h \
|
||||||
|
/usr/include/sys/features.h /usr/include/cygwin/types.h \
|
||||||
|
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
|
||||||
|
/usr/include/string.h ../machine/machine.h ../machine/translate.h \
|
||||||
|
../userprog/addrspace.h ../filesys/filesys.h ../filesys/openfile.h \
|
||||||
|
../lib/list.h ../lib/debug.h ../lib/list.cc ../threads/main.h \
|
||||||
|
../threads/kernel.h ../threads/scheduler.h ../machine/interrupt.h \
|
||||||
|
../machine/stats.h ../threads/alarm.h ../machine/timer.h
|
||||||
|
directory.o: ../filesys/directory.cc ../lib/copyright.h \
|
||||||
|
../lib/utility.h ../filesys/filehdr.h ../machine/disk.h \
|
||||||
|
../machine/callback.h ../filesys/pbitmap.h ../lib/bitmap.h \
|
||||||
|
../filesys/openfile.h ../lib/sysdep.h /usr/include/g++-3/iostream.h \
|
||||||
|
/usr/include/g++-3/streambuf.h /usr/include/g++-3/libio.h \
|
||||||
|
/usr/include/_G_config.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
|
||||||
|
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
|
||||||
|
/usr/include/sys/config.h /usr/include/sys/reent.h \
|
||||||
|
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
|
||||||
|
/usr/include/alloca.h /usr/include/stdio.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
|
||||||
|
/usr/include/sys/types.h /usr/include/machine/types.h \
|
||||||
|
/usr/include/sys/features.h /usr/include/cygwin/types.h \
|
||||||
|
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
|
||||||
|
/usr/include/string.h ../filesys/directory.h
|
||||||
|
filehdr.o: ../filesys/filehdr.cc ../lib/copyright.h \
|
||||||
|
../filesys/filehdr.h ../machine/disk.h ../lib/utility.h \
|
||||||
|
../machine/callback.h ../filesys/pbitmap.h ../lib/bitmap.h \
|
||||||
|
../filesys/openfile.h ../lib/sysdep.h /usr/include/g++-3/iostream.h \
|
||||||
|
/usr/include/g++-3/streambuf.h /usr/include/g++-3/libio.h \
|
||||||
|
/usr/include/_G_config.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
|
||||||
|
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
|
||||||
|
/usr/include/sys/config.h /usr/include/sys/reent.h \
|
||||||
|
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
|
||||||
|
/usr/include/alloca.h /usr/include/stdio.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
|
||||||
|
/usr/include/sys/types.h /usr/include/machine/types.h \
|
||||||
|
/usr/include/sys/features.h /usr/include/cygwin/types.h \
|
||||||
|
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
|
||||||
|
/usr/include/string.h ../lib/debug.h ../filesys/synchdisk.h \
|
||||||
|
../threads/synch.h ../threads/thread.h ../machine/machine.h \
|
||||||
|
../machine/translate.h ../userprog/addrspace.h ../filesys/filesys.h \
|
||||||
|
../lib/list.h ../lib/list.cc ../threads/main.h ../threads/kernel.h \
|
||||||
|
../threads/scheduler.h ../machine/interrupt.h ../machine/stats.h \
|
||||||
|
../threads/alarm.h ../machine/timer.h
|
||||||
|
filesys.o: ../filesys/filesys.cc
|
||||||
|
pbitmap.o: ../filesys/pbitmap.cc ../lib/copyright.h \
|
||||||
|
../filesys/pbitmap.h ../lib/bitmap.h ../lib/utility.h \
|
||||||
|
../filesys/openfile.h ../lib/sysdep.h /usr/include/g++-3/iostream.h \
|
||||||
|
/usr/include/g++-3/streambuf.h /usr/include/g++-3/libio.h \
|
||||||
|
/usr/include/_G_config.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
|
||||||
|
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
|
||||||
|
/usr/include/sys/config.h /usr/include/sys/reent.h \
|
||||||
|
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
|
||||||
|
/usr/include/alloca.h /usr/include/stdio.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
|
||||||
|
/usr/include/sys/types.h /usr/include/machine/types.h \
|
||||||
|
/usr/include/sys/features.h /usr/include/cygwin/types.h \
|
||||||
|
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
|
||||||
|
/usr/include/string.h
|
||||||
|
openfile.o: ../filesys/openfile.cc
|
||||||
|
synchdisk.o: ../filesys/synchdisk.cc ../lib/copyright.h \
|
||||||
|
../filesys/synchdisk.h ../machine/disk.h ../lib/utility.h \
|
||||||
|
../machine/callback.h ../threads/synch.h ../threads/thread.h \
|
||||||
|
../lib/sysdep.h /usr/include/g++-3/iostream.h \
|
||||||
|
/usr/include/g++-3/streambuf.h /usr/include/g++-3/libio.h \
|
||||||
|
/usr/include/_G_config.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
|
||||||
|
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
|
||||||
|
/usr/include/sys/config.h /usr/include/sys/reent.h \
|
||||||
|
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
|
||||||
|
/usr/include/alloca.h /usr/include/stdio.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
|
||||||
|
/usr/include/sys/types.h /usr/include/machine/types.h \
|
||||||
|
/usr/include/sys/features.h /usr/include/cygwin/types.h \
|
||||||
|
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
|
||||||
|
/usr/include/string.h ../machine/machine.h ../machine/translate.h \
|
||||||
|
../userprog/addrspace.h ../filesys/filesys.h ../filesys/openfile.h \
|
||||||
|
../lib/list.h ../lib/debug.h ../lib/list.cc ../threads/main.h \
|
||||||
|
../threads/kernel.h ../threads/scheduler.h ../machine/interrupt.h \
|
||||||
|
../machine/stats.h ../threads/alarm.h ../machine/timer.h
|
||||||
|
post.o: ../network/post.cc ../lib/copyright.h ../network/post.h \
|
||||||
|
../lib/utility.h ../machine/callback.h ../machine/network.h \
|
||||||
|
../threads/synchlist.h ../lib/list.h ../lib/debug.h ../lib/sysdep.h \
|
||||||
|
/usr/include/g++-3/iostream.h /usr/include/g++-3/streambuf.h \
|
||||||
|
/usr/include/g++-3/libio.h /usr/include/_G_config.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
|
||||||
|
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
|
||||||
|
/usr/include/sys/config.h /usr/include/sys/reent.h \
|
||||||
|
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
|
||||||
|
/usr/include/alloca.h /usr/include/stdio.h \
|
||||||
|
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
|
||||||
|
/usr/include/sys/types.h /usr/include/machine/types.h \
|
||||||
|
/usr/include/sys/features.h /usr/include/cygwin/types.h \
|
||||||
|
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
|
||||||
|
/usr/include/string.h ../lib/list.cc ../threads/synch.h \
|
||||||
|
../threads/thread.h ../machine/machine.h ../machine/translate.h \
|
||||||
|
../userprog/addrspace.h ../filesys/filesys.h ../filesys/openfile.h \
|
||||||
|
../threads/main.h ../threads/kernel.h ../threads/scheduler.h \
|
||||||
|
../machine/interrupt.h ../machine/stats.h ../threads/alarm.h \
|
||||||
|
../machine/timer.h ../threads/synchlist.cc
|
||||||
|
# DEPENDENCIES MUST END AT END OF FILE
|
||||||
|
# IF YOU PUT STUFF HERE IT WILL GO AWAY
|
||||||
|
# see make depend above
|
||||||
BIN
code/build.linux/DISK_0
Executable file
BIN
code/build.linux/DISK_0
Executable file
Binary file not shown.
372
code/build.linux/Makefile
Executable file
372
code/build.linux/Makefile
Executable file
@@ -0,0 +1,372 @@
|
|||||||
|
# Copyright (c) 1992-1996 The Regents of the University of California.
|
||||||
|
# All rights reserved. See copyright.h for copyright notice and limitation
|
||||||
|
# of liability and disclaimer of warranty provisions.
|
||||||
|
#
|
||||||
|
# This is a GNU Makefile. It must be used with the GNU make program.
|
||||||
|
# At UW, the GNU make program is /software/gnu/bin/make.
|
||||||
|
# In many other places it is known as "gmake".
|
||||||
|
# You may wish to include /software/gnu/bin/ early in your command
|
||||||
|
# search path, so that you will be using GNU make when you type "make".
|
||||||
|
#
|
||||||
|
# About this Makefile:
|
||||||
|
# --------------------
|
||||||
|
#
|
||||||
|
# This Makefile is used to build the Nachos system, which includes
|
||||||
|
# the MIPS machine simulation and a simple operating system.
|
||||||
|
#
|
||||||
|
# There is a separate Makefile, in the "test" directory, that is
|
||||||
|
# used to build the Nachos test programs (which run on the
|
||||||
|
# simulated machine).
|
||||||
|
#
|
||||||
|
# There are several "build" directories, one for each type
|
||||||
|
# of machine in the MFCF computing environment
|
||||||
|
# (build.solaris, build.sunos, and build.ultrix), as well
|
||||||
|
# as a build directory for Linux (build.linux) and a generic
|
||||||
|
# build directory (build.other) for those who wish to try
|
||||||
|
# building Nachos on other platforms.
|
||||||
|
#
|
||||||
|
# This Makefile appears to be located in all of the build directories.
|
||||||
|
# If you edit it in one directory, the copies in all of the other
|
||||||
|
# directories appear to change as well. This is the desired behaviour,
|
||||||
|
# since this file is machine independent. (The file actually lives
|
||||||
|
# in build.solaris, with symbolic links from the other build directories.)
|
||||||
|
#
|
||||||
|
# The platform-dependent parts of make's instructions are located
|
||||||
|
# in the file Makefile.dep.
|
||||||
|
# There is a different Makefile.dep in each build directory.
|
||||||
|
#
|
||||||
|
# If you are in the MFCF environment, you should not have to edit
|
||||||
|
# the Makefile.dep files by hand. Any changes to the make instructions
|
||||||
|
# can be made in this file (see the instructions below) - they will
|
||||||
|
# apply no matter where you build Nachos.
|
||||||
|
# If you are not in the MFCF environment, e.g., if you are trying
|
||||||
|
# to build Nachos on Linux at home, you will probably need
|
||||||
|
# to edit Makefile.dep (in the appropriate build directory) to
|
||||||
|
# customize the make procedure to your environment.
|
||||||
|
#
|
||||||
|
# How to build Nachos for the first time:
|
||||||
|
# ---------------------------------------
|
||||||
|
#
|
||||||
|
# (1) Make sure than you are in the build directory for the
|
||||||
|
# type of machine you are logged in to (the "host" machine):
|
||||||
|
#
|
||||||
|
# host type examples build directory
|
||||||
|
# ----------- ----------- ----------------
|
||||||
|
#
|
||||||
|
# sparc/SunOS cayley,napier, build.sunos
|
||||||
|
# (SunOS 4.1.3) descartes
|
||||||
|
#
|
||||||
|
# sparc/Solaris picard.math, build.solaris
|
||||||
|
# (SunOS 5.x) hermite.math,
|
||||||
|
# markov.math,
|
||||||
|
# hypatia.math,
|
||||||
|
# hume.math
|
||||||
|
#
|
||||||
|
# mips/ULTRIX cantor.math build.ultrix
|
||||||
|
# (ULTRIX 4.2) noether.math
|
||||||
|
#
|
||||||
|
# If you are not sure what type of machine you are on,
|
||||||
|
# try the command "uname -a".
|
||||||
|
#
|
||||||
|
# (2) Type "make depend"
|
||||||
|
# - this computes file dependencies and records them
|
||||||
|
# at the end of the file Makefile.dep in
|
||||||
|
# your build directory. Have a look...
|
||||||
|
#
|
||||||
|
# (3) Type "make nachos" (or just "make").
|
||||||
|
# - make echos the commands it is executing, so that
|
||||||
|
# you can observe its progress. When the
|
||||||
|
# build is finished, you should have an
|
||||||
|
# executable "nachos" in the build directory.
|
||||||
|
#
|
||||||
|
# (4) There is no 4th step. You are done. Try running "./nachos -u".
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# How to Re-build Nachos after you have changed the code:
|
||||||
|
#--------------------------------------------------------
|
||||||
|
#
|
||||||
|
# - The Nachos source code is located in the code subdirectories:
|
||||||
|
# threads, userprog, filesys, network, and lib. You may
|
||||||
|
# change the files in any of these directories, and you can
|
||||||
|
# add new files and/or remove files. The "machine" subdirectory
|
||||||
|
# contains the hardware simulation (which is also part of
|
||||||
|
# Nachos. You may look at it, but
|
||||||
|
# you may not change it, except as noted in machine/machine.h
|
||||||
|
# - When you want to re-make Nachos, always do it in the
|
||||||
|
# "build" directory that is appropriate for the machine
|
||||||
|
# type that you are running on.
|
||||||
|
# DO NOT TRY TO MAKE NACHOS IN THE SOURCE CODE DIRECTORIES.
|
||||||
|
#
|
||||||
|
# - IF all you have done is changed C++ code in existing files
|
||||||
|
# (since the last time you made Nachos in this build directory),
|
||||||
|
# THEN all you need to do to re-make Nachos is to type
|
||||||
|
#
|
||||||
|
# "make nachos"
|
||||||
|
#
|
||||||
|
# in the build directory.
|
||||||
|
#
|
||||||
|
# - IF you have done any of the following since the last build in
|
||||||
|
# this directory:
|
||||||
|
# added new .cc files or new .h files
|
||||||
|
# added or deleted #include's from existing files
|
||||||
|
# THEN
|
||||||
|
# you must do
|
||||||
|
# "make depend"
|
||||||
|
# followed by
|
||||||
|
# "make nachos"
|
||||||
|
#
|
||||||
|
# in the build directory.
|
||||||
|
#
|
||||||
|
# Note that is is always safe to do "make depend" followed by
|
||||||
|
# "make nachos", so if you are not sure what changes you have
|
||||||
|
# made, do "make depend".
|
||||||
|
#
|
||||||
|
# - IF you have added new files (.cc or .h) since the last build,
|
||||||
|
# you should edit this Makefile before running "make depend"
|
||||||
|
# and "make nachos".
|
||||||
|
|
||||||
|
# For new .h files, simply update the appropriate "_H" list below.
|
||||||
|
# For example, if you create a file called
|
||||||
|
# "bigfile.h" in the filesys subdirectory, you should add
|
||||||
|
# "../filesys/bigfile.h" to FILESYS_H, which is defined below
|
||||||
|
|
||||||
|
# For new .cc files, update the appropriate "_C" and "_O" lists.
|
||||||
|
# For example, if you create a file called "filetable.cc" in
|
||||||
|
# the directory "userprog", you should add
|
||||||
|
# "../userprog/filetable.cc" to USERPROG_C,
|
||||||
|
# and you should add "filetable.o" to USERPROG_O.
|
||||||
|
# Note that the entry in the "_C" list includes the subdirectory
|
||||||
|
# name, while the entry on the "_O" list does not.
|
||||||
|
#
|
||||||
|
# Some Important Notes:
|
||||||
|
# ---------------------
|
||||||
|
#
|
||||||
|
# * You can clean up all of the .o and other files left behind
|
||||||
|
# by make by typeing "make clean" in the build directory.
|
||||||
|
# * You can clean up .o and other files, as well as the nachos
|
||||||
|
# executable, DISK, core, SOCKET, and other files by typing
|
||||||
|
# make "distclean"
|
||||||
|
#
|
||||||
|
# These are good ways to save space, but the next build that
|
||||||
|
# you do after cleaning will take longer than usual, since
|
||||||
|
# much of the stuff you cleaned will need to be rebuilt.
|
||||||
|
#
|
||||||
|
# * When you build Nachos on an ULTRIX machine (in build.ultrix),
|
||||||
|
# you will get lots of warning messages like this:
|
||||||
|
#
|
||||||
|
# openfile.o: does not have gp tables for all it's sectons
|
||||||
|
#
|
||||||
|
# from the loader. Ignore them. Or better yet, figure out
|
||||||
|
# how to make them go away.
|
||||||
|
#
|
||||||
|
# The Most Important Note:
|
||||||
|
# -----------------------
|
||||||
|
#
|
||||||
|
# * If "make" is behaving strangely and you cannot figure out
|
||||||
|
# why, you should REBUILD the program FROM SCRATCH.
|
||||||
|
# Yes, it is slow.
|
||||||
|
# But, there are lots of little things that can go wrong, especially
|
||||||
|
# with all of these different types of machines available.
|
||||||
|
# Rebuilding from scratch at least gives you a known starting
|
||||||
|
# place. To rebuild from scratch, go to the appropriate
|
||||||
|
# build directory and do:
|
||||||
|
#
|
||||||
|
# make distclean
|
||||||
|
# make depend
|
||||||
|
# make nachos
|
||||||
|
#
|
||||||
|
################################################################
|
||||||
|
# READ THIS: CONFIGURING NACHOS
|
||||||
|
#
|
||||||
|
# Change DEFINES (below) to
|
||||||
|
# DEFINES = -DUSE_TLB -DFILESYS_STUB
|
||||||
|
# if you want the simulated machine to use its TLB
|
||||||
|
#
|
||||||
|
# If you want to use the real Nachos file system (based on
|
||||||
|
# the simulated disk), rather than the stub, remove
|
||||||
|
# the -DFILESYS_STUB from DEFINES.
|
||||||
|
#
|
||||||
|
# There is a a fix to the MIPS simulator to enable it to properly
|
||||||
|
# handle unaligned data access. This fix is enabled by the addition
|
||||||
|
# of "-DSIM_FIX" to the DEFINES. This should be enabled by default
|
||||||
|
# and eventually will not require the symbol definition
|
||||||
|
################################################################
|
||||||
|
DEFINES = -DFILESYS_STUB -DRDATA -DSIM_FIX
|
||||||
|
|
||||||
|
|
||||||
|
#####################################################################
|
||||||
|
#
|
||||||
|
# You might want to play with the CFLAGS, but if you use -O it may
|
||||||
|
# break the thread system. You might want to use -fno-inline if
|
||||||
|
# you need to call some inline functions from the debugger.
|
||||||
|
|
||||||
|
CFLAGS = -g -Wall $(INCPATH) $(DEFINES) $(HOSTCFLAGS) -DCHANGED -m32
|
||||||
|
LDFLAGS = -m32
|
||||||
|
CPP_AS_FLAGS= -m32
|
||||||
|
|
||||||
|
#####################################################################
|
||||||
|
CPP=/lib/cpp
|
||||||
|
CC = g++ -m32 -Wno-deprecated
|
||||||
|
LD = g++ -m32 -Wno-deprecated
|
||||||
|
AS = as --32
|
||||||
|
RM = /bin/rm
|
||||||
|
|
||||||
|
INCPATH = -I../network -I../filesys -I../userprog -I../threads -I../machine -I../lib -I-
|
||||||
|
|
||||||
|
PROGRAM = nachos
|
||||||
|
|
||||||
|
#
|
||||||
|
# Edit these lists as if you add files to the source directories.
|
||||||
|
# See the instructions at the top of the file for more information.
|
||||||
|
#
|
||||||
|
|
||||||
|
LIB_H = ../lib/bitmap.h\
|
||||||
|
../lib/copyright.h\
|
||||||
|
../lib/debug.h\
|
||||||
|
../lib/hash.h\
|
||||||
|
../lib/libtest.h\
|
||||||
|
../lib/list.h\
|
||||||
|
../lib/sysdep.h\
|
||||||
|
../lib/utility.h
|
||||||
|
|
||||||
|
LIB_C = ../lib/bitmap.cc\
|
||||||
|
../lib/debug.cc\
|
||||||
|
../lib/hash.cc\
|
||||||
|
../lib/libtest.cc\
|
||||||
|
../lib/list.cc\
|
||||||
|
../lib/sysdep.cc
|
||||||
|
|
||||||
|
LIB_O = bitmap.o debug.o libtest.o sysdep.o
|
||||||
|
|
||||||
|
|
||||||
|
MACHINE_H = ../machine/callback.h\
|
||||||
|
../machine/interrupt.h\
|
||||||
|
../machine/stats.h\
|
||||||
|
../machine/timer.h\
|
||||||
|
../machine/console.h\
|
||||||
|
../machine/machine.h\
|
||||||
|
../machine/mipssim.h\
|
||||||
|
../machine/translate.h\
|
||||||
|
../machine/network.h\
|
||||||
|
../machine/disk.h
|
||||||
|
|
||||||
|
MACHINE_C = ../machine/interrupt.cc\
|
||||||
|
../machine/stats.cc\
|
||||||
|
../machine/timer.cc\
|
||||||
|
../machine/console.cc\
|
||||||
|
../machine/machine.cc\
|
||||||
|
../machine/mipssim.cc\
|
||||||
|
../machine/translate.cc\
|
||||||
|
../machine/network.cc\
|
||||||
|
../machine/disk.cc
|
||||||
|
|
||||||
|
MACHINE_O = interrupt.o stats.o timer.o console.o machine.o mipssim.o\
|
||||||
|
translate.o network.o disk.o
|
||||||
|
|
||||||
|
THREAD_H = ../threads/alarm.h\
|
||||||
|
../threads/kernel.h\
|
||||||
|
../threads/main.h\
|
||||||
|
../threads/scheduler.h\
|
||||||
|
../threads/switch.h\
|
||||||
|
../threads/synch.h\
|
||||||
|
../threads/synchlist.h\
|
||||||
|
../threads/thread.h
|
||||||
|
|
||||||
|
THREAD_C = ../threads/alarm.cc\
|
||||||
|
../threads/kernel.cc\
|
||||||
|
../threads/main.cc\
|
||||||
|
../threads/scheduler.cc\
|
||||||
|
../threads/synch.cc\
|
||||||
|
../threads/synchlist.cc\
|
||||||
|
../threads/thread.cc
|
||||||
|
|
||||||
|
THREAD_O = alarm.o kernel.o main.o scheduler.o synch.o thread.o
|
||||||
|
|
||||||
|
USERPROG_H = ../userprog/addrspace.h\
|
||||||
|
../userprog/syscall.h\
|
||||||
|
../userprog/synchconsole.h\
|
||||||
|
../userprog/noff.h
|
||||||
|
|
||||||
|
USERPROG_C = ../userprog/addrspace.cc\
|
||||||
|
../userprog/exception.cc\
|
||||||
|
../userprog/synchconsole.cc
|
||||||
|
|
||||||
|
USERPROG_O = addrspace.o exception.o synchconsole.o
|
||||||
|
|
||||||
|
FILESYS_H =../filesys/directory.h \
|
||||||
|
../filesys/filehdr.h\
|
||||||
|
../filesys/filesys.h \
|
||||||
|
../filesys/openfile.h\
|
||||||
|
../filesys/pbitmap.h\
|
||||||
|
../filesys/synchdisk.h
|
||||||
|
|
||||||
|
FILESYS_C =../filesys/directory.cc\
|
||||||
|
../filesys/filehdr.cc\
|
||||||
|
../filesys/filesys.cc\
|
||||||
|
../filesys/pbitmap.cc\
|
||||||
|
../filesys/openfile.cc\
|
||||||
|
../filesys/synchdisk.cc\
|
||||||
|
|
||||||
|
FILESYS_O =directory.o filehdr.o filesys.o pbitmap.o openfile.o synchdisk.o
|
||||||
|
|
||||||
|
NETWORK_H = ../network/post.h
|
||||||
|
|
||||||
|
NETWORK_C = ../network/post.cc
|
||||||
|
|
||||||
|
NETWORK_O = post.o
|
||||||
|
|
||||||
|
##################################################################
|
||||||
|
# You probably don't want to change anything below this point in
|
||||||
|
# the file unless you are comfortable with GNU make and know what
|
||||||
|
# you are doing...
|
||||||
|
##################################################################
|
||||||
|
|
||||||
|
THREAD_S = ../threads/switch.s
|
||||||
|
|
||||||
|
HFILES = $(LIB_H) $(MACHINE_H) $(THREAD_H) $(USERPROG_H) $(FILESYS_H) $(NETWORK_H)
|
||||||
|
CFILES = $(LIB_C) $(MACHINE_C) $(THREAD_C) $(USERPROG_C) $(FILESYS_C) $(NETWORK_C)
|
||||||
|
|
||||||
|
C_OFILES = $(LIB_O) $(MACHINE_O) $(THREAD_O) $(USERPROG_O) $(FILESYS_O) $(NETWORK_O)
|
||||||
|
|
||||||
|
S_OFILES = switch.o
|
||||||
|
OFILES = $(C_OFILES) $(S_OFILES)
|
||||||
|
|
||||||
|
$(PROGRAM): $(OFILES)
|
||||||
|
$(LD) $(OFILES) $(LDFLAGS) -o $(PROGRAM)
|
||||||
|
|
||||||
|
$(C_OFILES): %.o:
|
||||||
|
$(CC) $(CFLAGS) -c $<
|
||||||
|
|
||||||
|
switch.o: ../threads/switch.S
|
||||||
|
$(CC) $(CPP_AS_FLAGS) -P $(INCPATH) $(HOSTCFLAGS) -c ../threads/switch.S
|
||||||
|
|
||||||
|
depend: $(CFILES) $(HFILES)
|
||||||
|
$(CC) $(INCPATH) $(DEFINES) $(HOSTCFLAGS) -DCHANGED -M $(CFILES) > makedep
|
||||||
|
@echo '/^# DO NOT DELETE THIS LINE/+1,$$d' >eddep
|
||||||
|
@echo '$$r makedep' >>eddep
|
||||||
|
@echo 'w' >>eddep
|
||||||
|
@echo 'q' >>eddep
|
||||||
|
ed - Makefile.dep < eddep
|
||||||
|
rm eddep makedep
|
||||||
|
@echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.dep
|
||||||
|
@echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.dep
|
||||||
|
@echo '# see make depend above' >> Makefile.dep
|
||||||
|
|
||||||
|
clean:
|
||||||
|
$(RM) -f $(OFILES)
|
||||||
|
|
||||||
|
distclean: clean
|
||||||
|
$(RM) -f $(PROGRAM)
|
||||||
|
$(RM) -f DISK_?
|
||||||
|
$(RM) -f core
|
||||||
|
$(RM) -f SOCKET_?
|
||||||
|
@echo '/^# DO NOT DELETE THIS LINE/+1,$$d' >eddep
|
||||||
|
@echo 'w' >>eddep
|
||||||
|
@echo 'q' >>eddep
|
||||||
|
ed - Makefile.dep < eddep
|
||||||
|
rm eddep
|
||||||
|
@echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.dep
|
||||||
|
@echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.dep
|
||||||
|
@echo '# see make depend above' >> Makefile.dep
|
||||||
|
|
||||||
|
include Makefile.dep
|
||||||
3703
code/build.linux/Makefile.dep
Executable file
3703
code/build.linux/Makefile.dep
Executable file
File diff suppressed because it is too large
Load Diff
BIN
code/build.linux/nachos
Executable file
BIN
code/build.linux/nachos
Executable file
Binary file not shown.
365
code/build.macosx/Makefile
Executable file
365
code/build.macosx/Makefile
Executable file
@@ -0,0 +1,365 @@
|
|||||||
|
# Copyright (c) 1992-1996 The Regents of the University of California.
|
||||||
|
# All rights reserved. See copyright.h for copyright notice and limitation
|
||||||
|
# of liability and disclaimer of warranty provisions.
|
||||||
|
#
|
||||||
|
# This is a GNU Makefile. It must be used with the GNU make program.
|
||||||
|
# At UW, the GNU make program is /software/gnu/bin/make.
|
||||||
|
# In many other places it is known as "gmake".
|
||||||
|
# You may wish to include /software/gnu/bin/ early in your command
|
||||||
|
# search path, so that you will be using GNU make when you type "make".
|
||||||
|
#
|
||||||
|
# About this Makefile:
|
||||||
|
# --------------------
|
||||||
|
#
|
||||||
|
# This Makefile is used to build the Nachos system, which includes
|
||||||
|
# the MIPS machine simulation and a simple operating system.
|
||||||
|
#
|
||||||
|
# There is a separate Makefile, in the "test" directory, that is
|
||||||
|
# used to build the Nachos test programs (which run on the
|
||||||
|
# simulated machine).
|
||||||
|
#
|
||||||
|
# There are several "build" directories, one for each type
|
||||||
|
# of machine in the MFCF computing environment
|
||||||
|
# (build.solaris, build.sunos, and build.ultrix), as well
|
||||||
|
# as a build directory for Linux (build.linux) and a generic
|
||||||
|
# build directory (build.other) for those who wish to try
|
||||||
|
# building Nachos on other platforms.
|
||||||
|
#
|
||||||
|
# This Makefile appears to be located in all of the build directories.
|
||||||
|
# If you edit it in one directory, the copies in all of the other
|
||||||
|
# directories appear to change as well. This is the desired behaviour,
|
||||||
|
# since this file is machine independent. (The file actually lives
|
||||||
|
# in build.solaris, with symbolic links from the other build directories.)
|
||||||
|
#
|
||||||
|
# The platform-dependent parts of make's instructions are located
|
||||||
|
# in the file Makefile.dep.
|
||||||
|
# There is a different Makefile.dep in each build directory.
|
||||||
|
#
|
||||||
|
# If you are in the MFCF environment, you should not have to edit
|
||||||
|
# the Makefile.dep files by hand. Any changes to the make instructions
|
||||||
|
# can be made in this file (see the instructions below) - they will
|
||||||
|
# apply no matter where you build Nachos.
|
||||||
|
# If you are not in the MFCF environment, e.g., if you are trying
|
||||||
|
# to build Nachos on Linux at home, you will probably need
|
||||||
|
# to edit Makefile.dep (in the appropriate build directory) to
|
||||||
|
# customize the make procedure to your environment.
|
||||||
|
#
|
||||||
|
# How to build Nachos for the first time:
|
||||||
|
# ---------------------------------------
|
||||||
|
#
|
||||||
|
# (1) Make sure than you are in the build directory for the
|
||||||
|
# type of machine you are logged in to (the "host" machine):
|
||||||
|
#
|
||||||
|
# host type examples build directory
|
||||||
|
# ----------- ----------- ----------------
|
||||||
|
#
|
||||||
|
# sparc/SunOS cayley,napier, build.sunos
|
||||||
|
# (SunOS 4.1.3) descartes
|
||||||
|
#
|
||||||
|
# sparc/Solaris picard.math, build.solaris
|
||||||
|
# (SunOS 5.x) hermite.math,
|
||||||
|
# markov.math,
|
||||||
|
# hypatia.math,
|
||||||
|
# hume.math
|
||||||
|
#
|
||||||
|
# mips/ULTRIX cantor.math build.ultrix
|
||||||
|
# (ULTRIX 4.2) noether.math
|
||||||
|
#
|
||||||
|
# If you are not sure what type of machine you are on,
|
||||||
|
# try the command "uname -a".
|
||||||
|
#
|
||||||
|
# (2) Type "make depend"
|
||||||
|
# - this computes file dependencies and records them
|
||||||
|
# at the end of the file Makefile.dep in
|
||||||
|
# your build directory. Have a look...
|
||||||
|
#
|
||||||
|
# (3) Type "make nachos" (or just "make").
|
||||||
|
# - make echos the commands it is executing, so that
|
||||||
|
# you can observe its progress. When the
|
||||||
|
# build is finished, you should have an
|
||||||
|
# executable "nachos" in the build directory.
|
||||||
|
#
|
||||||
|
# (4) There is no 4th step. You are done. Try running "./nachos -u".
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# How to Re-build Nachos after you have changed the code:
|
||||||
|
#--------------------------------------------------------
|
||||||
|
#
|
||||||
|
# - The Nachos source code is located in the code subdirectories:
|
||||||
|
# threads, userprog, filesys, network, and lib. You may
|
||||||
|
# change the files in any of these directories, and you can
|
||||||
|
# add new files and/or remove files. The "machine" subdirectory
|
||||||
|
# contains the hardware simulation (which is also part of
|
||||||
|
# Nachos. You may look at it, but
|
||||||
|
# you may not change it, except as noted in machine/machine.h
|
||||||
|
# - When you want to re-make Nachos, always do it in the
|
||||||
|
# "build" directory that is appropriate for the machine
|
||||||
|
# type that you are running on.
|
||||||
|
# DO NOT TRY TO MAKE NACHOS IN THE SOURCE CODE DIRECTORIES.
|
||||||
|
#
|
||||||
|
# - IF all you have done is changed C++ code in existing files
|
||||||
|
# (since the last time you made Nachos in this build directory),
|
||||||
|
# THEN all you need to do to re-make Nachos is to type
|
||||||
|
#
|
||||||
|
# "make nachos"
|
||||||
|
#
|
||||||
|
# in the build directory.
|
||||||
|
#
|
||||||
|
# - IF you have done any of the following since the last build in
|
||||||
|
# this directory:
|
||||||
|
# added new .cc files or new .h files
|
||||||
|
# added or deleted #include's from existing files
|
||||||
|
# THEN
|
||||||
|
# you must do
|
||||||
|
# "make depend"
|
||||||
|
# followed by
|
||||||
|
# "make nachos"
|
||||||
|
#
|
||||||
|
# in the build directory.
|
||||||
|
#
|
||||||
|
# Note that is is always safe to do "make depend" followed by
|
||||||
|
# "make nachos", so if you are not sure what changes you have
|
||||||
|
# made, do "make depend".
|
||||||
|
#
|
||||||
|
# - IF you have added new files (.cc or .h) since the last build,
|
||||||
|
# you should edit this Makefile before running "make depend"
|
||||||
|
# and "make nachos".
|
||||||
|
|
||||||
|
# For new .h files, simply update the appropriate "_H" list below.
|
||||||
|
# For example, if you create a file called
|
||||||
|
# "bigfile.h" in the filesys subdirectory, you should add
|
||||||
|
# "../filesys/bigfile.h" to FILESYS_H, which is defined below
|
||||||
|
|
||||||
|
# For new .cc files, update the appropriate "_C" and "_O" lists.
|
||||||
|
# For example, if you create a file called "filetable.cc" in
|
||||||
|
# the directory "userprog", you should add
|
||||||
|
# "../userprog/filetable.cc" to USERPROG_C,
|
||||||
|
# and you should add "filetable.o" to USERPROG_O.
|
||||||
|
# Note that the entry in the "_C" list includes the subdirectory
|
||||||
|
# name, while the entry on the "_O" list does not.
|
||||||
|
#
|
||||||
|
# Some Important Notes:
|
||||||
|
# ---------------------
|
||||||
|
#
|
||||||
|
# * You can clean up all of the .o and other files left behind
|
||||||
|
# by make by typeing "make clean" in the build directory.
|
||||||
|
# * You can clean up .o and other files, as well as the nachos
|
||||||
|
# executable, DISK, core, SOCKET, and other files by typing
|
||||||
|
# make "distclean"
|
||||||
|
#
|
||||||
|
# These are good ways to save space, but the next build that
|
||||||
|
# you do after cleaning will take longer than usual, since
|
||||||
|
# much of the stuff you cleaned will need to be rebuilt.
|
||||||
|
#
|
||||||
|
# * When you build Nachos on an ULTRIX machine (in build.ultrix),
|
||||||
|
# you will get lots of warning messages like this:
|
||||||
|
#
|
||||||
|
# openfile.o: does not have gp tables for all it's sectons
|
||||||
|
#
|
||||||
|
# from the loader. Ignore them. Or better yet, figure out
|
||||||
|
# how to make them go away.
|
||||||
|
#
|
||||||
|
# The Most Important Note:
|
||||||
|
# -----------------------
|
||||||
|
#
|
||||||
|
# * If "make" is behaving strangely and you cannot figure out
|
||||||
|
# why, you should REBUILD the program FROM SCRATCH.
|
||||||
|
# Yes, it is slow.
|
||||||
|
# But, there are lots of little things that can go wrong, especially
|
||||||
|
# with all of these different types of machines available.
|
||||||
|
# Rebuilding from scratch at least gives you a known starting
|
||||||
|
# place. To rebuild from scratch, go to the appropriate
|
||||||
|
# build directory and do:
|
||||||
|
#
|
||||||
|
# make distclean
|
||||||
|
# make depend
|
||||||
|
# make nachos
|
||||||
|
#
|
||||||
|
################################################################
|
||||||
|
# READ THIS: CONFIGURING NACHOS
|
||||||
|
#
|
||||||
|
# Change DEFINES (below) to
|
||||||
|
# DEFINES = -DUSE_TLB -DFILESYS_STUB
|
||||||
|
# if you want the simulated machine to use its TLB
|
||||||
|
#
|
||||||
|
# If you want to use the real Nachos file system (based on
|
||||||
|
# the simulated disk), rather than the stub, remove
|
||||||
|
# the -DFILESYS_STUB from DEFINES.
|
||||||
|
#
|
||||||
|
# There is a a fix to the MIPS simulator to enable it to properly
|
||||||
|
# handle unaligned data access. This fix is enabled by the addition
|
||||||
|
# of "-DSIM_FIX" to the DEFINES. This should be enabled by default
|
||||||
|
# and eventually will not require the symbol definition
|
||||||
|
################################################################
|
||||||
|
DEFINES = -DFILESYS_STUB -DRDATA -DSIM_FIX
|
||||||
|
|
||||||
|
|
||||||
|
#####################################################################
|
||||||
|
#
|
||||||
|
# You might want to play with the CFLAGS, but if you use -O it may
|
||||||
|
# break the thread system. You might want to use -fno-inline if
|
||||||
|
# you need to call some inline functions from the debugger.
|
||||||
|
|
||||||
|
CFLAGS = -g -Wall -fwritable-strings $(INCPATH) $(DEFINES) $(HOSTCFLAGS) -DCHANGED
|
||||||
|
LDFLAGS =
|
||||||
|
|
||||||
|
#####################################################################
|
||||||
|
CPP=/lib/cpp
|
||||||
|
CC = g++
|
||||||
|
LD = g++
|
||||||
|
AS = as
|
||||||
|
RM = /bin/rm
|
||||||
|
|
||||||
|
INCPATH = -I../network -I../filesys -I../userprog -I../threads -I../machine -I../lib
|
||||||
|
|
||||||
|
PROGRAM = nachos
|
||||||
|
|
||||||
|
#
|
||||||
|
# Edit these lists as if you add files to the source directories.
|
||||||
|
# See the instructions at the top of the file for more information.
|
||||||
|
#
|
||||||
|
|
||||||
|
LIB_H = ../lib/bitmap.h\
|
||||||
|
../lib/copyright.h\
|
||||||
|
../lib/debug.h\
|
||||||
|
../lib/hash.h\
|
||||||
|
../lib/libtest.h\
|
||||||
|
../lib/list.h\
|
||||||
|
../lib/sysdep.h\
|
||||||
|
../lib/utility.h
|
||||||
|
|
||||||
|
LIB_C = ../lib/bitmap.cc\
|
||||||
|
../lib/debug.cc\
|
||||||
|
../lib/hash.cc\
|
||||||
|
../lib/libtest.cc\
|
||||||
|
../lib/list.cc\
|
||||||
|
../lib/sysdep.cc
|
||||||
|
|
||||||
|
LIB_O = bitmap.o debug.o libtest.o sysdep.o
|
||||||
|
|
||||||
|
|
||||||
|
MACHINE_H = ../machine/callback.h\
|
||||||
|
../machine/interrupt.h\
|
||||||
|
../machine/stats.h\
|
||||||
|
../machine/timer.h\
|
||||||
|
../machine/console.h\
|
||||||
|
../machine/machine.h\
|
||||||
|
../machine/mipssim.h\
|
||||||
|
../machine/translate.h\
|
||||||
|
../machine/network.h\
|
||||||
|
../machine/disk.h
|
||||||
|
|
||||||
|
MACHINE_C = ../machine/interrupt.cc\
|
||||||
|
../machine/stats.cc\
|
||||||
|
../machine/timer.cc\
|
||||||
|
../machine/console.cc\
|
||||||
|
../machine/machine.cc\
|
||||||
|
../machine/mipssim.cc\
|
||||||
|
../machine/translate.cc\
|
||||||
|
../machine/network.cc\
|
||||||
|
../machine/disk.cc
|
||||||
|
|
||||||
|
MACHINE_O = interrupt.o stats.o timer.o console.o machine.o mipssim.o\
|
||||||
|
translate.o network.o disk.o
|
||||||
|
|
||||||
|
THREAD_H = ../threads/alarm.h\
|
||||||
|
../threads/kernel.h\
|
||||||
|
../threads/main.h\
|
||||||
|
../threads/scheduler.h\
|
||||||
|
../threads/switch.h\
|
||||||
|
../threads/synch.h\
|
||||||
|
../threads/synchlist.h\
|
||||||
|
../threads/thread.h
|
||||||
|
|
||||||
|
THREAD_C = ../threads/alarm.cc\
|
||||||
|
../threads/kernel.cc\
|
||||||
|
../threads/main.cc\
|
||||||
|
../threads/scheduler.cc\
|
||||||
|
../threads/synch.cc\
|
||||||
|
../threads/synchlist.cc\
|
||||||
|
../threads/thread.cc
|
||||||
|
|
||||||
|
THREAD_O = alarm.o kernel.o main.o scheduler.o synch.o thread.o
|
||||||
|
|
||||||
|
USERPROG_H = ../userprog/addrspace.h\
|
||||||
|
../userprog/syscall.h\
|
||||||
|
../userprog/synchconsole.h\
|
||||||
|
../userprog/noff.h
|
||||||
|
|
||||||
|
USERPROG_C = ../userprog/addrspace.cc\
|
||||||
|
../userprog/exception.cc\
|
||||||
|
../userprog/synchconsole.cc
|
||||||
|
|
||||||
|
USERPROG_O = addrspace.o exception.o synchconsole.o
|
||||||
|
|
||||||
|
FILESYS_H =../filesys/directory.h \
|
||||||
|
../filesys/filehdr.h\
|
||||||
|
../filesys/filesys.h \
|
||||||
|
../filesys/openfile.h\
|
||||||
|
../filesys/pbitmap.h\
|
||||||
|
../filesys/synchdisk.h
|
||||||
|
|
||||||
|
FILESYS_C =../filesys/directory.cc\
|
||||||
|
../filesys/filehdr.cc\
|
||||||
|
../filesys/filesys.cc\
|
||||||
|
../filesys/pbitmap.cc\
|
||||||
|
../filesys/openfile.cc\
|
||||||
|
../filesys/synchdisk.cc\
|
||||||
|
|
||||||
|
FILESYS_O =directory.o filehdr.o filesys.o pbitmap.o openfile.o synchdisk.o
|
||||||
|
|
||||||
|
NETWORK_H = ../network/post.h
|
||||||
|
|
||||||
|
NETWORK_C = ../network/post.cc
|
||||||
|
|
||||||
|
NETWORK_O = post.o
|
||||||
|
|
||||||
|
##################################################################
|
||||||
|
# You probably don't want to change anything below this point in
|
||||||
|
# the file unless you are comfortable with GNU make and know what
|
||||||
|
# you are doing...
|
||||||
|
##################################################################
|
||||||
|
|
||||||
|
THREAD_S = ../threads/switch.s
|
||||||
|
|
||||||
|
HFILES = $(LIB_H) $(MACHINE_H) $(THREAD_H) $(USERPROG_H) $(FILESYS_H) $(NETWORK_H)
|
||||||
|
CFILES = $(LIB_C) $(MACHINE_C) $(THREAD_C) $(USERPROG_C) $(FILESYS_C) $(NETWORK_C)
|
||||||
|
|
||||||
|
C_OFILES = $(LIB_O) $(MACHINE_O) $(THREAD_O) $(USERPROG_O) $(FILESYS_O) $(NETWORK_O)
|
||||||
|
|
||||||
|
S_OFILES = switch.o
|
||||||
|
OFILES = $(C_OFILES) $(S_OFILES)
|
||||||
|
|
||||||
|
$(PROGRAM): $(OFILES)
|
||||||
|
$(LD) $(OFILES) $(LDFLAGS) -o $(PROGRAM)
|
||||||
|
|
||||||
|
$(C_OFILES): %.o:
|
||||||
|
$(CC) $(CFLAGS) -c $<
|
||||||
|
|
||||||
|
switch.o: ../threads/switch.s
|
||||||
|
$(CPP) $(CPP_AS_FLAGS) -P $(INCPATH) $(HOSTCFLAGS) ../threads/switch.s > swtch.s
|
||||||
|
$(AS) -o switch.o swtch.s
|
||||||
|
|
||||||
|
depend: $(CFILES) $(HFILES)
|
||||||
|
$(CC) $(INCPATH) $(DEFINES) $(HOSTCFLAGS) -DCHANGED -M $(CFILES) > makedep
|
||||||
|
@echo '/^# DO NOT DELETE THIS LINE/+2,$$d' >eddep
|
||||||
|
@echo '$$r makedep' >>eddep
|
||||||
|
@echo 'w' >>eddep
|
||||||
|
@echo 'q' >>eddep
|
||||||
|
ed - Makefile.dep < eddep
|
||||||
|
rm eddep makedep
|
||||||
|
@echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.dep
|
||||||
|
@echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.dep
|
||||||
|
@echo '# see make depend above' >> Makefile.dep
|
||||||
|
|
||||||
|
clean:
|
||||||
|
$(RM) -f $(OFILES)
|
||||||
|
$(RM) -f swtch.s
|
||||||
|
|
||||||
|
distclean: clean
|
||||||
|
$(RM) -f $(PROGRAM)
|
||||||
|
$(RM) -f DISK_?
|
||||||
|
$(RM) -f core
|
||||||
|
$(RM) -f SOCKET_?
|
||||||
|
|
||||||
|
include Makefile.dep
|
||||||
18
code/build.macosx/Makefile.dep
Executable file
18
code/build.macosx/Makefile.dep
Executable file
@@ -0,0 +1,18 @@
|
|||||||
|
##################################################################
|
||||||
|
# Machine Dependencies - this file is included automatically
|
||||||
|
# into the main Makefile
|
||||||
|
#
|
||||||
|
##################################################################
|
||||||
|
|
||||||
|
HOSTCFLAGS = -DHOST_IS_BIG_ENDIAN -DPowerPC -DBSD -DAIX -DApplePowerPC
|
||||||
|
CPP_AS_FLAGS = -D_ASM
|
||||||
|
CPP = cpp
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------
|
||||||
|
# Do not put anything below this point - it will be destroyed by
|
||||||
|
# "make depend"
|
||||||
|
#
|
||||||
|
# DO NOT DELETE THIS LINE -- make depend uses it
|
||||||
|
# DEPENDENCIES MUST END AT END OF FILE
|
||||||
|
# IF YOU PUT STUFF HERE IT WILL GO AWAY
|
||||||
|
# see make depend above
|
||||||
196
code/filesys/directory.cc
Executable file
196
code/filesys/directory.cc
Executable file
@@ -0,0 +1,196 @@
|
|||||||
|
// directory.cc
|
||||||
|
// Routines to manage a directory of file names.
|
||||||
|
//
|
||||||
|
// The directory is a table of fixed length entries; each
|
||||||
|
// entry represents a single file, and contains the file name,
|
||||||
|
// and the location of the file header on disk. The fixed size
|
||||||
|
// of each directory entry means that we have the restriction
|
||||||
|
// of a fixed maximum size for file names.
|
||||||
|
//
|
||||||
|
// The constructor initializes an empty directory of a certain size;
|
||||||
|
// we use ReadFrom/WriteBack to fetch the contents of the directory
|
||||||
|
// from disk, and to write back any modifications back to disk.
|
||||||
|
//
|
||||||
|
// Also, this implementation has the restriction that the size
|
||||||
|
// of the directory cannot expand. In other words, once all the
|
||||||
|
// entries in the directory are used, no more files can be created.
|
||||||
|
//
|
||||||
|
// 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 "filehdr.h"
|
||||||
|
#include "directory.h"
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Directory::Directory
|
||||||
|
// Initialize a directory; initially, the directory is completely
|
||||||
|
// empty. If the disk is being formatted, an empty directory
|
||||||
|
// is all we need, but otherwise, we need to call FetchFrom in order
|
||||||
|
// to initialize it from disk.
|
||||||
|
//
|
||||||
|
// "size" is the number of entries in the directory
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
Directory::Directory(int size)
|
||||||
|
{
|
||||||
|
table = new DirectoryEntry[size];
|
||||||
|
tableSize = size;
|
||||||
|
for (int i = 0; i < tableSize; i++)
|
||||||
|
table[i].inUse = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Directory::~Directory
|
||||||
|
// De-allocate directory data structure.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
Directory::~Directory()
|
||||||
|
{
|
||||||
|
delete [] table;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Directory::FetchFrom
|
||||||
|
// Read the contents of the directory from disk.
|
||||||
|
//
|
||||||
|
// "file" -- file containing the directory contents
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
Directory::FetchFrom(OpenFile *file)
|
||||||
|
{
|
||||||
|
(void) file->ReadAt((char *)table, tableSize * sizeof(DirectoryEntry), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Directory::WriteBack
|
||||||
|
// Write any modifications to the directory back to disk
|
||||||
|
//
|
||||||
|
// "file" -- file to contain the new directory contents
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
Directory::WriteBack(OpenFile *file)
|
||||||
|
{
|
||||||
|
(void) file->WriteAt((char *)table, tableSize * sizeof(DirectoryEntry), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Directory::FindIndex
|
||||||
|
// Look up file name in directory, and return its location in the table of
|
||||||
|
// directory entries. Return -1 if the name isn't in the directory.
|
||||||
|
//
|
||||||
|
// "name" -- the file name to look up
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
int
|
||||||
|
Directory::FindIndex(char *name)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < tableSize; i++)
|
||||||
|
if (table[i].inUse && !strncmp(table[i].name, name, FileNameMaxLen))
|
||||||
|
return i;
|
||||||
|
return -1; // name not in directory
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Directory::Find
|
||||||
|
// Look up file name in directory, and return the disk sector number
|
||||||
|
// where the file's header is stored. Return -1 if the name isn't
|
||||||
|
// in the directory.
|
||||||
|
//
|
||||||
|
// "name" -- the file name to look up
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
int
|
||||||
|
Directory::Find(char *name)
|
||||||
|
{
|
||||||
|
int i = FindIndex(name);
|
||||||
|
|
||||||
|
if (i != -1)
|
||||||
|
return table[i].sector;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Directory::Add
|
||||||
|
// Add a file into the directory. Return TRUE if successful;
|
||||||
|
// return FALSE if the file name is already in the directory, or if
|
||||||
|
// the directory is completely full, and has no more space for
|
||||||
|
// additional file names.
|
||||||
|
//
|
||||||
|
// "name" -- the name of the file being added
|
||||||
|
// "newSector" -- the disk sector containing the added file's header
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
bool
|
||||||
|
Directory::Add(char *name, int newSector)
|
||||||
|
{
|
||||||
|
if (FindIndex(name) != -1)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
for (int i = 0; i < tableSize; i++)
|
||||||
|
if (!table[i].inUse) {
|
||||||
|
table[i].inUse = TRUE;
|
||||||
|
strncpy(table[i].name, name, FileNameMaxLen);
|
||||||
|
table[i].sector = newSector;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
return FALSE; // no space. Fix when we have extensible files.
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Directory::Remove
|
||||||
|
// Remove a file name from the directory. Return TRUE if successful;
|
||||||
|
// return FALSE if the file isn't in the directory.
|
||||||
|
//
|
||||||
|
// "name" -- the file name to be removed
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
bool
|
||||||
|
Directory::Remove(char *name)
|
||||||
|
{
|
||||||
|
int i = FindIndex(name);
|
||||||
|
|
||||||
|
if (i == -1)
|
||||||
|
return FALSE; // name not in directory
|
||||||
|
table[i].inUse = FALSE;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Directory::List
|
||||||
|
// List all the file names in the directory.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
Directory::List()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < tableSize; i++)
|
||||||
|
if (table[i].inUse)
|
||||||
|
printf("%s\n", table[i].name);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Directory::Print
|
||||||
|
// List all the file names in the directory, their FileHeader locations,
|
||||||
|
// and the contents of each file. For debugging.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
Directory::Print()
|
||||||
|
{
|
||||||
|
FileHeader *hdr = new FileHeader;
|
||||||
|
|
||||||
|
printf("Directory contents:\n");
|
||||||
|
for (int i = 0; i < tableSize; i++)
|
||||||
|
if (table[i].inUse) {
|
||||||
|
printf("Name: %s, Sector: %d\n", table[i].name, table[i].sector);
|
||||||
|
hdr->FetchFrom(table[i].sector);
|
||||||
|
hdr->Print();
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
delete hdr;
|
||||||
|
}
|
||||||
83
code/filesys/directory.h
Executable file
83
code/filesys/directory.h
Executable file
@@ -0,0 +1,83 @@
|
|||||||
|
// directory.h
|
||||||
|
// Data structures to manage a UNIX-like directory of file names.
|
||||||
|
//
|
||||||
|
// A directory is a table of pairs: <file name, sector #>,
|
||||||
|
// giving the name of each file in the directory, and
|
||||||
|
// where to find its file header (the data structure describing
|
||||||
|
// where to find the file's data blocks) on disk.
|
||||||
|
//
|
||||||
|
// We assume mutual exclusion is provided by the caller.
|
||||||
|
//
|
||||||
|
// 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"
|
||||||
|
|
||||||
|
#ifndef DIRECTORY_H
|
||||||
|
#define DIRECTORY_H
|
||||||
|
|
||||||
|
#include "openfile.h"
|
||||||
|
|
||||||
|
#define FileNameMaxLen 9 // for simplicity, we assume
|
||||||
|
// file names are <= 9 characters long
|
||||||
|
|
||||||
|
// The following class defines a "directory entry", representing a file
|
||||||
|
// in the directory. Each entry gives the name of the file, and where
|
||||||
|
// the file's header is to be found on disk.
|
||||||
|
//
|
||||||
|
// Internal data structures kept public so that Directory operations can
|
||||||
|
// access them directly.
|
||||||
|
|
||||||
|
class DirectoryEntry {
|
||||||
|
public:
|
||||||
|
bool inUse; // Is this directory entry in use?
|
||||||
|
int sector; // Location on disk to find the
|
||||||
|
// FileHeader for this file
|
||||||
|
char name[FileNameMaxLen + 1]; // Text name for file, with +1 for
|
||||||
|
// the trailing '\0'
|
||||||
|
};
|
||||||
|
|
||||||
|
// The following class defines a UNIX-like "directory". Each entry in
|
||||||
|
// the directory describes a file, and where to find it on disk.
|
||||||
|
//
|
||||||
|
// The directory data structure can be stored in memory, or on disk.
|
||||||
|
// When it is on disk, it is stored as a regular Nachos file.
|
||||||
|
//
|
||||||
|
// The constructor initializes a directory structure in memory; the
|
||||||
|
// FetchFrom/WriteBack operations shuffle the directory information
|
||||||
|
// from/to disk.
|
||||||
|
|
||||||
|
class Directory {
|
||||||
|
public:
|
||||||
|
Directory(int size); // Initialize an empty directory
|
||||||
|
// with space for "size" files
|
||||||
|
~Directory(); // De-allocate the directory
|
||||||
|
|
||||||
|
void FetchFrom(OpenFile *file); // Init directory contents from disk
|
||||||
|
void WriteBack(OpenFile *file); // Write modifications to
|
||||||
|
// directory contents back to disk
|
||||||
|
|
||||||
|
int Find(char *name); // Find the sector number of the
|
||||||
|
// FileHeader for file: "name"
|
||||||
|
|
||||||
|
bool Add(char *name, int newSector); // Add a file name into the directory
|
||||||
|
|
||||||
|
bool Remove(char *name); // Remove a file from the directory
|
||||||
|
|
||||||
|
void List(); // Print the names of all the files
|
||||||
|
// in the directory
|
||||||
|
void Print(); // Verbose print of the contents
|
||||||
|
// of the directory -- all the file
|
||||||
|
// names and their contents.
|
||||||
|
|
||||||
|
private:
|
||||||
|
int tableSize; // Number of directory entries
|
||||||
|
DirectoryEntry *table; // Table of pairs:
|
||||||
|
// <file name, file header location>
|
||||||
|
|
||||||
|
int FindIndex(char *name); // Find the index into the directory
|
||||||
|
// table corresponding to "name"
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // DIRECTORY_H
|
||||||
156
code/filesys/filehdr.cc
Executable file
156
code/filesys/filehdr.cc
Executable file
@@ -0,0 +1,156 @@
|
|||||||
|
// filehdr.cc
|
||||||
|
// Routines for managing the disk file header (in UNIX, this
|
||||||
|
// would be called the i-node).
|
||||||
|
//
|
||||||
|
// The file header is used to locate where on disk the
|
||||||
|
// file's data is stored. We implement this as a fixed size
|
||||||
|
// table of pointers -- each entry in the table points to the
|
||||||
|
// disk sector containing that portion of the file data
|
||||||
|
// (in other words, there are no indirect or doubly indirect
|
||||||
|
// blocks). The table size is chosen so that the file header
|
||||||
|
// will be just big enough to fit in one disk sector,
|
||||||
|
//
|
||||||
|
// Unlike in a real system, we do not keep track of file permissions,
|
||||||
|
// ownership, last modification date, etc., in the file header.
|
||||||
|
//
|
||||||
|
// A file header can be initialized in two ways:
|
||||||
|
// for a new file, by modifying the in-memory data structure
|
||||||
|
// to point to the newly allocated data blocks
|
||||||
|
// for a file already on disk, by reading the file header from disk
|
||||||
|
//
|
||||||
|
// 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 "filehdr.h"
|
||||||
|
#include "debug.h"
|
||||||
|
#include "synchdisk.h"
|
||||||
|
#include "main.h"
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// FileHeader::Allocate
|
||||||
|
// Initialize a fresh file header for a newly created file.
|
||||||
|
// Allocate data blocks for the file out of the map of free disk blocks.
|
||||||
|
// Return FALSE if there are not enough free blocks to accomodate
|
||||||
|
// the new file.
|
||||||
|
//
|
||||||
|
// "freeMap" is the bit map of free disk sectors
|
||||||
|
// "fileSize" is the bit map of free disk sectors
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
bool
|
||||||
|
FileHeader::Allocate(PersistentBitmap *freeMap, int fileSize)
|
||||||
|
{
|
||||||
|
numBytes = fileSize;
|
||||||
|
numSectors = divRoundUp(fileSize, SectorSize);
|
||||||
|
if (freeMap->NumClear() < numSectors)
|
||||||
|
return FALSE; // not enough space
|
||||||
|
|
||||||
|
for (int i = 0; i < numSectors; i++) {
|
||||||
|
dataSectors[i] = freeMap->FindAndSet();
|
||||||
|
// since we checked that there was enough free space,
|
||||||
|
// we expect this to succeed
|
||||||
|
ASSERT(dataSectors[i] >= 0);
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// FileHeader::Deallocate
|
||||||
|
// De-allocate all the space allocated for data blocks for this file.
|
||||||
|
//
|
||||||
|
// "freeMap" is the bit map of free disk sectors
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
FileHeader::Deallocate(PersistentBitmap *freeMap)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < numSectors; i++) {
|
||||||
|
ASSERT(freeMap->Test((int) dataSectors[i])); // ought to be marked!
|
||||||
|
freeMap->Clear((int) dataSectors[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// FileHeader::FetchFrom
|
||||||
|
// Fetch contents of file header from disk.
|
||||||
|
//
|
||||||
|
// "sector" is the disk sector containing the file header
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
FileHeader::FetchFrom(int sector)
|
||||||
|
{
|
||||||
|
kernel->synchDisk->ReadSector(sector, (char *)this);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// FileHeader::WriteBack
|
||||||
|
// Write the modified contents of the file header back to disk.
|
||||||
|
//
|
||||||
|
// "sector" is the disk sector to contain the file header
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
FileHeader::WriteBack(int sector)
|
||||||
|
{
|
||||||
|
kernel->synchDisk->WriteSector(sector, (char *)this);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// FileHeader::ByteToSector
|
||||||
|
// Return which disk sector is storing a particular byte within the file.
|
||||||
|
// This is essentially a translation from a virtual address (the
|
||||||
|
// offset in the file) to a physical address (the sector where the
|
||||||
|
// data at the offset is stored).
|
||||||
|
//
|
||||||
|
// "offset" is the location within the file of the byte in question
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
int
|
||||||
|
FileHeader::ByteToSector(int offset)
|
||||||
|
{
|
||||||
|
return(dataSectors[offset / SectorSize]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// FileHeader::FileLength
|
||||||
|
// Return the number of bytes in the file.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
int
|
||||||
|
FileHeader::FileLength()
|
||||||
|
{
|
||||||
|
return numBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// FileHeader::Print
|
||||||
|
// Print the contents of the file header, and the contents of all
|
||||||
|
// the data blocks pointed to by the file header.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
FileHeader::Print()
|
||||||
|
{
|
||||||
|
int i, j, k;
|
||||||
|
char *data = new char[SectorSize];
|
||||||
|
|
||||||
|
printf("FileHeader contents. File size: %d. File blocks:\n", numBytes);
|
||||||
|
for (i = 0; i < numSectors; i++)
|
||||||
|
printf("%d ", dataSectors[i]);
|
||||||
|
printf("\nFile contents:\n");
|
||||||
|
for (i = k = 0; i < numSectors; i++) {
|
||||||
|
kernel->synchDisk->ReadSector(dataSectors[i], data);
|
||||||
|
for (j = 0; (j < SectorSize) && (k < numBytes); j++, k++) {
|
||||||
|
if ('\040' <= data[j] && data[j] <= '\176') // isprint(data[j])
|
||||||
|
printf("%c", data[j]);
|
||||||
|
else
|
||||||
|
printf("\\%x", (unsigned char)data[j]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
delete [] data;
|
||||||
|
}
|
||||||
66
code/filesys/filehdr.h
Executable file
66
code/filesys/filehdr.h
Executable file
@@ -0,0 +1,66 @@
|
|||||||
|
// filehdr.h
|
||||||
|
// Data structures for managing a disk file header.
|
||||||
|
//
|
||||||
|
// A file header describes where on disk to find the data in a file,
|
||||||
|
// along with other information about the file (for instance, its
|
||||||
|
// length, owner, etc.)
|
||||||
|
//
|
||||||
|
// 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"
|
||||||
|
|
||||||
|
#ifndef FILEHDR_H
|
||||||
|
#define FILEHDR_H
|
||||||
|
|
||||||
|
#include "disk.h"
|
||||||
|
#include "pbitmap.h"
|
||||||
|
|
||||||
|
#define NumDirect ((SectorSize - 2 * sizeof(int)) / sizeof(int))
|
||||||
|
#define MaxFileSize (NumDirect * SectorSize)
|
||||||
|
|
||||||
|
// The following class defines the Nachos "file header" (in UNIX terms,
|
||||||
|
// the "i-node"), describing where on disk to find all of the data in the file.
|
||||||
|
// The file header is organized as a simple table of pointers to
|
||||||
|
// data blocks.
|
||||||
|
//
|
||||||
|
// The file header data structure can be stored in memory or on disk.
|
||||||
|
// When it is on disk, it is stored in a single sector -- this means
|
||||||
|
// that we assume the size of this data structure to be the same
|
||||||
|
// as one disk sector. Without indirect addressing, this
|
||||||
|
// limits the maximum file length to just under 4K bytes.
|
||||||
|
//
|
||||||
|
// There is no constructor; rather the file header can be initialized
|
||||||
|
// by allocating blocks for the file (if it is a new file), or by
|
||||||
|
// reading it from disk.
|
||||||
|
|
||||||
|
class FileHeader {
|
||||||
|
public:
|
||||||
|
bool Allocate(PersistentBitmap *bitMap, int fileSize);// Initialize a file header,
|
||||||
|
// including allocating space
|
||||||
|
// on disk for the file data
|
||||||
|
void Deallocate(PersistentBitmap *bitMap); // De-allocate this file's
|
||||||
|
// data blocks
|
||||||
|
|
||||||
|
void FetchFrom(int sectorNumber); // Initialize file header from disk
|
||||||
|
void WriteBack(int sectorNumber); // Write modifications to file header
|
||||||
|
// back to disk
|
||||||
|
|
||||||
|
int ByteToSector(int offset); // Convert a byte offset into the file
|
||||||
|
// to the disk sector containing
|
||||||
|
// the byte
|
||||||
|
|
||||||
|
int FileLength(); // Return the length of the file
|
||||||
|
// in bytes
|
||||||
|
|
||||||
|
void Print(); // Print the contents of the file.
|
||||||
|
|
||||||
|
private:
|
||||||
|
int numBytes; // Number of bytes in the file
|
||||||
|
int numSectors; // Number of data sectors in the file
|
||||||
|
int dataSectors[NumDirect]; // Disk sector numbers for each data
|
||||||
|
// block in the file
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // FILEHDR_H
|
||||||
340
code/filesys/filesys.cc
Executable file
340
code/filesys/filesys.cc
Executable file
@@ -0,0 +1,340 @@
|
|||||||
|
// filesys.cc
|
||||||
|
// Routines to manage the overall operation of the file system.
|
||||||
|
// Implements routines to map from textual file names to files.
|
||||||
|
//
|
||||||
|
// Each file in the file system has:
|
||||||
|
// A file header, stored in a sector on disk
|
||||||
|
// (the size of the file header data structure is arranged
|
||||||
|
// to be precisely the size of 1 disk sector)
|
||||||
|
// A number of data blocks
|
||||||
|
// An entry in the file system directory
|
||||||
|
//
|
||||||
|
// The file system consists of several data structures:
|
||||||
|
// A bitmap of free disk sectors (cf. bitmap.h)
|
||||||
|
// A directory of file names and file headers
|
||||||
|
//
|
||||||
|
// Both the bitmap and the directory are represented as normal
|
||||||
|
// files. Their file headers are located in specific sectors
|
||||||
|
// (sector 0 and sector 1), so that the file system can find them
|
||||||
|
// on bootup.
|
||||||
|
//
|
||||||
|
// The file system assumes that the bitmap and directory files are
|
||||||
|
// kept "open" continuously while Nachos is running.
|
||||||
|
//
|
||||||
|
// For those operations (such as Create, Remove) that modify the
|
||||||
|
// directory and/or bitmap, if the operation succeeds, the changes
|
||||||
|
// are written immediately back to disk (the two files are kept
|
||||||
|
// open during all this time). If the operation fails, and we have
|
||||||
|
// modified part of the directory and/or bitmap, we simply discard
|
||||||
|
// the changed version, without writing it back to disk.
|
||||||
|
//
|
||||||
|
// Our implementation at this point has the following restrictions:
|
||||||
|
//
|
||||||
|
// there is no synchronization for concurrent accesses
|
||||||
|
// files have a fixed size, set when the file is created
|
||||||
|
// files cannot be bigger than about 3KB in size
|
||||||
|
// there is no hierarchical directory structure, and only a limited
|
||||||
|
// number of files can be added to the system
|
||||||
|
// there is no attempt to make the system robust to failures
|
||||||
|
// (if Nachos exits in the middle of an operation that modifies
|
||||||
|
// the file system, it may corrupt the disk)
|
||||||
|
//
|
||||||
|
// 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 FILESYS_STUB
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
#include "debug.h"
|
||||||
|
#include "disk.h"
|
||||||
|
#include "pbitmap.h"
|
||||||
|
#include "directory.h"
|
||||||
|
#include "filehdr.h"
|
||||||
|
#include "filesys.h"
|
||||||
|
|
||||||
|
// Sectors containing the file headers for the bitmap of free sectors,
|
||||||
|
// and the directory of files. These file headers are placed in well-known
|
||||||
|
// sectors, so that they can be located on boot-up.
|
||||||
|
#define FreeMapSector 0
|
||||||
|
#define DirectorySector 1
|
||||||
|
|
||||||
|
// Initial file sizes for the bitmap and directory; until the file system
|
||||||
|
// supports extensible files, the directory size sets the maximum number
|
||||||
|
// of files that can be loaded onto the disk.
|
||||||
|
#define FreeMapFileSize (NumSectors / BitsInByte)
|
||||||
|
#define NumDirEntries 10
|
||||||
|
#define DirectoryFileSize (sizeof(DirectoryEntry) * NumDirEntries)
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// FileSystem::FileSystem
|
||||||
|
// Initialize the file system. If format = TRUE, the disk has
|
||||||
|
// nothing on it, and we need to initialize the disk to contain
|
||||||
|
// an empty directory, and a bitmap of free sectors (with almost but
|
||||||
|
// not all of the sectors marked as free).
|
||||||
|
//
|
||||||
|
// If format = FALSE, we just have to open the files
|
||||||
|
// representing the bitmap and the directory.
|
||||||
|
//
|
||||||
|
// "format" -- should we initialize the disk?
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
FileSystem::FileSystem(bool format)
|
||||||
|
{
|
||||||
|
DEBUG(dbgFile, "Initializing the file system.");
|
||||||
|
if (format) {
|
||||||
|
PersistentBitmap *freeMap = new PersistentBitmap(NumSectors);
|
||||||
|
Directory *directory = new Directory(NumDirEntries);
|
||||||
|
FileHeader *mapHdr = new FileHeader;
|
||||||
|
FileHeader *dirHdr = new FileHeader;
|
||||||
|
|
||||||
|
DEBUG(dbgFile, "Formatting the file system.");
|
||||||
|
|
||||||
|
// First, allocate space for FileHeaders for the directory and bitmap
|
||||||
|
// (make sure no one else grabs these!)
|
||||||
|
freeMap->Mark(FreeMapSector);
|
||||||
|
freeMap->Mark(DirectorySector);
|
||||||
|
|
||||||
|
// Second, allocate space for the data blocks containing the contents
|
||||||
|
// of the directory and bitmap files. There better be enough space!
|
||||||
|
|
||||||
|
ASSERT(mapHdr->Allocate(freeMap, FreeMapFileSize));
|
||||||
|
ASSERT(dirHdr->Allocate(freeMap, DirectoryFileSize));
|
||||||
|
|
||||||
|
// Flush the bitmap and directory FileHeaders back to disk
|
||||||
|
// We need to do this before we can "Open" the file, since open
|
||||||
|
// reads the file header off of disk (and currently the disk has garbage
|
||||||
|
// on it!).
|
||||||
|
|
||||||
|
DEBUG(dbgFile, "Writing headers back to disk.");
|
||||||
|
mapHdr->WriteBack(FreeMapSector);
|
||||||
|
dirHdr->WriteBack(DirectorySector);
|
||||||
|
|
||||||
|
// OK to open the bitmap and directory files now
|
||||||
|
// The file system operations assume these two files are left open
|
||||||
|
// while Nachos is running.
|
||||||
|
|
||||||
|
freeMapFile = new OpenFile(FreeMapSector);
|
||||||
|
directoryFile = new OpenFile(DirectorySector);
|
||||||
|
|
||||||
|
// Once we have the files "open", we can write the initial version
|
||||||
|
// of each file back to disk. The directory at this point is completely
|
||||||
|
// empty; but the bitmap has been changed to reflect the fact that
|
||||||
|
// sectors on the disk have been allocated for the file headers and
|
||||||
|
// to hold the file data for the directory and bitmap.
|
||||||
|
|
||||||
|
DEBUG(dbgFile, "Writing bitmap and directory back to disk.");
|
||||||
|
freeMap->WriteBack(freeMapFile); // flush changes to disk
|
||||||
|
directory->WriteBack(directoryFile);
|
||||||
|
|
||||||
|
if (debug->IsEnabled('f')) {
|
||||||
|
freeMap->Print();
|
||||||
|
directory->Print();
|
||||||
|
}
|
||||||
|
delete freeMap;
|
||||||
|
delete directory;
|
||||||
|
delete mapHdr;
|
||||||
|
delete dirHdr;
|
||||||
|
} else {
|
||||||
|
// if we are not formatting the disk, just open the files representing
|
||||||
|
// the bitmap and directory; these are left open while Nachos is running
|
||||||
|
freeMapFile = new OpenFile(FreeMapSector);
|
||||||
|
directoryFile = new OpenFile(DirectorySector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// FileSystem::Create
|
||||||
|
// Create a file in the Nachos file system (similar to UNIX create).
|
||||||
|
// Since we can't increase the size of files dynamically, we have
|
||||||
|
// to give Create the initial size of the file.
|
||||||
|
//
|
||||||
|
// The steps to create a file are:
|
||||||
|
// Make sure the file doesn't already exist
|
||||||
|
// Allocate a sector for the file header
|
||||||
|
// Allocate space on disk for the data blocks for the file
|
||||||
|
// Add the name to the directory
|
||||||
|
// Store the new file header on disk
|
||||||
|
// Flush the changes to the bitmap and the directory back to disk
|
||||||
|
//
|
||||||
|
// Return TRUE if everything goes ok, otherwise, return FALSE.
|
||||||
|
//
|
||||||
|
// Create fails if:
|
||||||
|
// file is already in directory
|
||||||
|
// no free space for file header
|
||||||
|
// no free entry for file in directory
|
||||||
|
// no free space for data blocks for the file
|
||||||
|
//
|
||||||
|
// Note that this implementation assumes there is no concurrent access
|
||||||
|
// to the file system!
|
||||||
|
//
|
||||||
|
// "name" -- name of file to be created
|
||||||
|
// "initialSize" -- size of file to be created
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
bool
|
||||||
|
FileSystem::Create(char *name, int initialSize)
|
||||||
|
{
|
||||||
|
Directory *directory;
|
||||||
|
PersistentBitmap *freeMap;
|
||||||
|
FileHeader *hdr;
|
||||||
|
int sector;
|
||||||
|
bool success;
|
||||||
|
|
||||||
|
DEBUG(dbgFile, "Creating file " << name << " size " << initialSize);
|
||||||
|
|
||||||
|
directory = new Directory(NumDirEntries);
|
||||||
|
directory->FetchFrom(directoryFile);
|
||||||
|
|
||||||
|
if (directory->Find(name) != -1)
|
||||||
|
success = FALSE; // file is already in directory
|
||||||
|
else {
|
||||||
|
freeMap = new PersistentBitmap(freeMapFile,NumSectors);
|
||||||
|
sector = freeMap->FindAndSet(); // find a sector to hold the file header
|
||||||
|
if (sector == -1)
|
||||||
|
success = FALSE; // no free block for file header
|
||||||
|
else if (!directory->Add(name, sector))
|
||||||
|
success = FALSE; // no space in directory
|
||||||
|
else {
|
||||||
|
hdr = new FileHeader;
|
||||||
|
if (!hdr->Allocate(freeMap, initialSize))
|
||||||
|
success = FALSE; // no space on disk for data
|
||||||
|
else {
|
||||||
|
success = TRUE;
|
||||||
|
// everthing worked, flush all changes back to disk
|
||||||
|
hdr->WriteBack(sector);
|
||||||
|
directory->WriteBack(directoryFile);
|
||||||
|
freeMap->WriteBack(freeMapFile);
|
||||||
|
}
|
||||||
|
delete hdr;
|
||||||
|
}
|
||||||
|
delete freeMap;
|
||||||
|
}
|
||||||
|
delete directory;
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// FileSystem::Open
|
||||||
|
// Open a file for reading and writing.
|
||||||
|
// To open a file:
|
||||||
|
// Find the location of the file's header, using the directory
|
||||||
|
// Bring the header into memory
|
||||||
|
//
|
||||||
|
// "name" -- the text name of the file to be opened
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
OpenFile *
|
||||||
|
FileSystem::Open(char *name)
|
||||||
|
{
|
||||||
|
Directory *directory = new Directory(NumDirEntries);
|
||||||
|
OpenFile *openFile = NULL;
|
||||||
|
int sector;
|
||||||
|
|
||||||
|
DEBUG(dbgFile, "Opening file" << name);
|
||||||
|
directory->FetchFrom(directoryFile);
|
||||||
|
sector = directory->Find(name);
|
||||||
|
if (sector >= 0)
|
||||||
|
openFile = new OpenFile(sector); // name was found in directory
|
||||||
|
delete directory;
|
||||||
|
return openFile; // return NULL if not found
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// FileSystem::Remove
|
||||||
|
// Delete a file from the file system. This requires:
|
||||||
|
// Remove it from the directory
|
||||||
|
// Delete the space for its header
|
||||||
|
// Delete the space for its data blocks
|
||||||
|
// Write changes to directory, bitmap back to disk
|
||||||
|
//
|
||||||
|
// Return TRUE if the file was deleted, FALSE if the file wasn't
|
||||||
|
// in the file system.
|
||||||
|
//
|
||||||
|
// "name" -- the text name of the file to be removed
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
bool
|
||||||
|
FileSystem::Remove(char *name)
|
||||||
|
{
|
||||||
|
Directory *directory;
|
||||||
|
PersistentBitmap *freeMap;
|
||||||
|
FileHeader *fileHdr;
|
||||||
|
int sector;
|
||||||
|
|
||||||
|
directory = new Directory(NumDirEntries);
|
||||||
|
directory->FetchFrom(directoryFile);
|
||||||
|
sector = directory->Find(name);
|
||||||
|
if (sector == -1) {
|
||||||
|
delete directory;
|
||||||
|
return FALSE; // file not found
|
||||||
|
}
|
||||||
|
fileHdr = new FileHeader;
|
||||||
|
fileHdr->FetchFrom(sector);
|
||||||
|
|
||||||
|
freeMap = new PersistentBitmap(freeMapFile,NumSectors);
|
||||||
|
|
||||||
|
fileHdr->Deallocate(freeMap); // remove data blocks
|
||||||
|
freeMap->Clear(sector); // remove header block
|
||||||
|
directory->Remove(name);
|
||||||
|
|
||||||
|
freeMap->WriteBack(freeMapFile); // flush to disk
|
||||||
|
directory->WriteBack(directoryFile); // flush to disk
|
||||||
|
delete fileHdr;
|
||||||
|
delete directory;
|
||||||
|
delete freeMap;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// FileSystem::List
|
||||||
|
// List all the files in the file system directory.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
FileSystem::List()
|
||||||
|
{
|
||||||
|
Directory *directory = new Directory(NumDirEntries);
|
||||||
|
|
||||||
|
directory->FetchFrom(directoryFile);
|
||||||
|
directory->List();
|
||||||
|
delete directory;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// FileSystem::Print
|
||||||
|
// Print everything about the file system:
|
||||||
|
// the contents of the bitmap
|
||||||
|
// the contents of the directory
|
||||||
|
// for each file in the directory,
|
||||||
|
// the contents of the file header
|
||||||
|
// the data in the file
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
FileSystem::Print()
|
||||||
|
{
|
||||||
|
FileHeader *bitHdr = new FileHeader;
|
||||||
|
FileHeader *dirHdr = new FileHeader;
|
||||||
|
PersistentBitmap *freeMap = new PersistentBitmap(freeMapFile,NumSectors);
|
||||||
|
Directory *directory = new Directory(NumDirEntries);
|
||||||
|
|
||||||
|
printf("Bit map file header:\n");
|
||||||
|
bitHdr->FetchFrom(FreeMapSector);
|
||||||
|
bitHdr->Print();
|
||||||
|
|
||||||
|
printf("Directory file header:\n");
|
||||||
|
dirHdr->FetchFrom(DirectorySector);
|
||||||
|
dirHdr->Print();
|
||||||
|
|
||||||
|
freeMap->Print();
|
||||||
|
|
||||||
|
directory->FetchFrom(directoryFile);
|
||||||
|
directory->Print();
|
||||||
|
|
||||||
|
delete bitHdr;
|
||||||
|
delete dirHdr;
|
||||||
|
delete freeMap;
|
||||||
|
delete directory;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // FILESYS_STUB
|
||||||
98
code/filesys/filesys.h
Executable file
98
code/filesys/filesys.h
Executable file
@@ -0,0 +1,98 @@
|
|||||||
|
// filesys.h
|
||||||
|
// Data structures to represent the Nachos file system.
|
||||||
|
//
|
||||||
|
// A file system is a set of files stored on disk, organized
|
||||||
|
// into directories. Operations on the file system have to
|
||||||
|
// do with "naming" -- creating, opening, and deleting files,
|
||||||
|
// given a textual file name. Operations on an individual
|
||||||
|
// "open" file (read, write, close) are to be found in the OpenFile
|
||||||
|
// class (openfile.h).
|
||||||
|
//
|
||||||
|
// We define two separate implementations of the file system.
|
||||||
|
// The "STUB" version just re-defines the Nachos file system
|
||||||
|
// operations as operations on the native UNIX file system on the machine
|
||||||
|
// running the Nachos simulation.
|
||||||
|
//
|
||||||
|
// The other version is a "real" file system, built on top of
|
||||||
|
// a disk simulator. The disk is simulated using the native UNIX
|
||||||
|
// file system (in a file named "DISK").
|
||||||
|
//
|
||||||
|
// In the "real" implementation, there are two key data structures used
|
||||||
|
// in the file system. There is a single "root" directory, listing
|
||||||
|
// all of the files in the file system; unlike UNIX, the baseline
|
||||||
|
// system does not provide a hierarchical directory structure.
|
||||||
|
// In addition, there is a bitmap for allocating
|
||||||
|
// disk sectors. Both the root directory and the bitmap are themselves
|
||||||
|
// stored as files in the Nachos file system -- this causes an interesting
|
||||||
|
// bootstrap problem when the simulated disk is initialized.
|
||||||
|
//
|
||||||
|
// 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 FS_H
|
||||||
|
#define FS_H
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
#include "sysdep.h"
|
||||||
|
#include "openfile.h"
|
||||||
|
|
||||||
|
#ifdef FILESYS_STUB // Temporarily implement file system calls as
|
||||||
|
// calls to UNIX, until the real file system
|
||||||
|
// implementation is available
|
||||||
|
class FileSystem {
|
||||||
|
public:
|
||||||
|
FileSystem() { for (int i = 0; i < 20; i++) fileDescriptorTable[i] = NULL; }
|
||||||
|
|
||||||
|
bool Create(char *name) {
|
||||||
|
int fileDescriptor = OpenForWrite(name);
|
||||||
|
|
||||||
|
if (fileDescriptor == -1) return FALSE;
|
||||||
|
Close(fileDescriptor);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenFile* Open(char *name) {
|
||||||
|
int fileDescriptor = OpenForReadWrite(name, FALSE);
|
||||||
|
|
||||||
|
if (fileDescriptor == -1) return NULL;
|
||||||
|
return new OpenFile(fileDescriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Remove(char *name) { return Unlink(name) == 0; }
|
||||||
|
|
||||||
|
OpenFile *fileDescriptorTable[20];
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#else // FILESYS
|
||||||
|
class FileSystem {
|
||||||
|
public:
|
||||||
|
FileSystem(bool format); // Initialize the file system.
|
||||||
|
// Must be called *after* "synchDisk"
|
||||||
|
// has been initialized.
|
||||||
|
// If "format", there is nothing on
|
||||||
|
// the disk, so initialize the directory
|
||||||
|
// and the bitmap of free blocks.
|
||||||
|
|
||||||
|
bool Create(char *name, int initialSize);
|
||||||
|
// Create a file (UNIX creat)
|
||||||
|
|
||||||
|
OpenFile* Open(char *name); // Open a file (UNIX open)
|
||||||
|
|
||||||
|
bool Remove(char *name); // Delete a file (UNIX unlink)
|
||||||
|
|
||||||
|
void List(); // List all the files in the file system
|
||||||
|
|
||||||
|
void Print(); // List all the files and their contents
|
||||||
|
|
||||||
|
private:
|
||||||
|
OpenFile* freeMapFile; // Bit map of free disk blocks,
|
||||||
|
// represented as a file
|
||||||
|
OpenFile* directoryFile; // "Root" directory -- list of
|
||||||
|
// file names, represented as a file
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // FILESYS
|
||||||
|
|
||||||
|
#endif // FS_H
|
||||||
196
code/filesys/openfile.cc
Executable file
196
code/filesys/openfile.cc
Executable file
@@ -0,0 +1,196 @@
|
|||||||
|
// openfile.cc
|
||||||
|
// Routines to manage an open Nachos file. As in UNIX, a
|
||||||
|
// file must be open before we can read or write to it.
|
||||||
|
// Once we're all done, we can close it (in Nachos, by deleting
|
||||||
|
// the OpenFile data structure).
|
||||||
|
//
|
||||||
|
// Also as in UNIX, for convenience, we keep the file header in
|
||||||
|
// memory while the file is open.
|
||||||
|
//
|
||||||
|
// 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 FILESYS_STUB
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
#include "main.h"
|
||||||
|
#include "filehdr.h"
|
||||||
|
#include "openfile.h"
|
||||||
|
#include "synchdisk.h"
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// OpenFile::OpenFile
|
||||||
|
// Open a Nachos file for reading and writing. Bring the file header
|
||||||
|
// into memory while the file is open.
|
||||||
|
//
|
||||||
|
// "sector" -- the location on disk of the file header for this file
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
OpenFile::OpenFile(int sector)
|
||||||
|
{
|
||||||
|
hdr = new FileHeader;
|
||||||
|
hdr->FetchFrom(sector);
|
||||||
|
seekPosition = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// OpenFile::~OpenFile
|
||||||
|
// Close a Nachos file, de-allocating any in-memory data structures.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
OpenFile::~OpenFile()
|
||||||
|
{
|
||||||
|
delete hdr;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// OpenFile::Seek
|
||||||
|
// Change the current location within the open file -- the point at
|
||||||
|
// which the next Read or Write will start from.
|
||||||
|
//
|
||||||
|
// "position" -- the location within the file for the next Read/Write
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
OpenFile::Seek(int position)
|
||||||
|
{
|
||||||
|
seekPosition = position;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// OpenFile::Read/Write
|
||||||
|
// Read/write a portion of a file, starting from seekPosition.
|
||||||
|
// Return the number of bytes actually written or read, and as a
|
||||||
|
// side effect, increment the current position within the file.
|
||||||
|
//
|
||||||
|
// Implemented using the more primitive ReadAt/WriteAt.
|
||||||
|
//
|
||||||
|
// "into" -- the buffer to contain the data to be read from disk
|
||||||
|
// "from" -- the buffer containing the data to be written to disk
|
||||||
|
// "numBytes" -- the number of bytes to transfer
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
int
|
||||||
|
OpenFile::Read(char *into, int numBytes)
|
||||||
|
{
|
||||||
|
int result = ReadAt(into, numBytes, seekPosition);
|
||||||
|
seekPosition += result;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
OpenFile::Write(char *into, int numBytes)
|
||||||
|
{
|
||||||
|
int result = WriteAt(into, numBytes, seekPosition);
|
||||||
|
seekPosition += result;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// OpenFile::ReadAt/WriteAt
|
||||||
|
// Read/write a portion of a file, starting at "position".
|
||||||
|
// Return the number of bytes actually written or read, but has
|
||||||
|
// no side effects (except that Write modifies the file, of course).
|
||||||
|
//
|
||||||
|
// There is no guarantee the request starts or ends on an even disk sector
|
||||||
|
// boundary; however the disk only knows how to read/write a whole disk
|
||||||
|
// sector at a time. Thus:
|
||||||
|
//
|
||||||
|
// For ReadAt:
|
||||||
|
// We read in all of the full or partial sectors that are part of the
|
||||||
|
// request, but we only copy the part we are interested in.
|
||||||
|
// For WriteAt:
|
||||||
|
// We must first read in any sectors that will be partially written,
|
||||||
|
// so that we don't overwrite the unmodified portion. We then copy
|
||||||
|
// in the data that will be modified, and write back all the full
|
||||||
|
// or partial sectors that are part of the request.
|
||||||
|
//
|
||||||
|
// "into" -- the buffer to contain the data to be read from disk
|
||||||
|
// "from" -- the buffer containing the data to be written to disk
|
||||||
|
// "numBytes" -- the number of bytes to transfer
|
||||||
|
// "position" -- the offset within the file of the first byte to be
|
||||||
|
// read/written
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
int
|
||||||
|
OpenFile::ReadAt(char *into, int numBytes, int position)
|
||||||
|
{
|
||||||
|
int fileLength = hdr->FileLength();
|
||||||
|
int i, firstSector, lastSector, numSectors;
|
||||||
|
char *buf;
|
||||||
|
|
||||||
|
if ((numBytes <= 0) || (position >= fileLength))
|
||||||
|
return 0; // check request
|
||||||
|
if ((position + numBytes) > fileLength)
|
||||||
|
numBytes = fileLength - position;
|
||||||
|
DEBUG(dbgFile, "Reading " << numBytes << " bytes at " << position << " from file of length " << fileLength);
|
||||||
|
|
||||||
|
firstSector = divRoundDown(position, SectorSize);
|
||||||
|
lastSector = divRoundDown(position + numBytes - 1, SectorSize);
|
||||||
|
numSectors = 1 + lastSector - firstSector;
|
||||||
|
|
||||||
|
// read in all the full and partial sectors that we need
|
||||||
|
buf = new char[numSectors * SectorSize];
|
||||||
|
for (i = firstSector; i <= lastSector; i++)
|
||||||
|
kernel->synchDisk->ReadSector(hdr->ByteToSector(i * SectorSize),
|
||||||
|
&buf[(i - firstSector) * SectorSize]);
|
||||||
|
|
||||||
|
// copy the part we want
|
||||||
|
bcopy(&buf[position - (firstSector * SectorSize)], into, numBytes);
|
||||||
|
delete [] buf;
|
||||||
|
return numBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
OpenFile::WriteAt(char *from, int numBytes, int position)
|
||||||
|
{
|
||||||
|
int fileLength = hdr->FileLength();
|
||||||
|
int i, firstSector, lastSector, numSectors;
|
||||||
|
bool firstAligned, lastAligned;
|
||||||
|
char *buf;
|
||||||
|
|
||||||
|
if ((numBytes <= 0) || (position >= fileLength))
|
||||||
|
return 0; // check request
|
||||||
|
if ((position + numBytes) > fileLength)
|
||||||
|
numBytes = fileLength - position;
|
||||||
|
DEBUG(dbgFile, "Writing " << numBytes << " bytes at " << position << " from file of length " << fileLength);
|
||||||
|
|
||||||
|
firstSector = divRoundDown(position, SectorSize);
|
||||||
|
lastSector = divRoundDown(position + numBytes - 1, SectorSize);
|
||||||
|
numSectors = 1 + lastSector - firstSector;
|
||||||
|
|
||||||
|
buf = new char[numSectors * SectorSize];
|
||||||
|
|
||||||
|
firstAligned = (position == (firstSector * SectorSize));
|
||||||
|
lastAligned = ((position + numBytes) == ((lastSector + 1) * SectorSize));
|
||||||
|
|
||||||
|
// read in first and last sector, if they are to be partially modified
|
||||||
|
if (!firstAligned)
|
||||||
|
ReadAt(buf, SectorSize, firstSector * SectorSize);
|
||||||
|
if (!lastAligned && ((firstSector != lastSector) || firstAligned))
|
||||||
|
ReadAt(&buf[(lastSector - firstSector) * SectorSize],
|
||||||
|
SectorSize, lastSector * SectorSize);
|
||||||
|
|
||||||
|
// copy in the bytes we want to change
|
||||||
|
bcopy(from, &buf[position - (firstSector * SectorSize)], numBytes);
|
||||||
|
|
||||||
|
// write modified sectors back
|
||||||
|
for (i = firstSector; i <= lastSector; i++)
|
||||||
|
kernel->synchDisk->WriteSector(hdr->ByteToSector(i * SectorSize),
|
||||||
|
&buf[(i - firstSector) * SectorSize]);
|
||||||
|
delete [] buf;
|
||||||
|
return numBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// OpenFile::Length
|
||||||
|
// Return the number of bytes in the file.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
int
|
||||||
|
OpenFile::Length()
|
||||||
|
{
|
||||||
|
return hdr->FileLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //FILESYS_STUB
|
||||||
97
code/filesys/openfile.h
Executable file
97
code/filesys/openfile.h
Executable file
@@ -0,0 +1,97 @@
|
|||||||
|
// openfile.h
|
||||||
|
// Data structures for opening, closing, reading and writing to
|
||||||
|
// individual files. The operations supported are similar to
|
||||||
|
// the UNIX ones -- type 'man open' to the UNIX prompt.
|
||||||
|
//
|
||||||
|
// There are two implementations. One is a "STUB" that directly
|
||||||
|
// turns the file operations into the underlying UNIX operations.
|
||||||
|
// (cf. comment in filesys.h).
|
||||||
|
//
|
||||||
|
// The other is the "real" implementation, that turns these
|
||||||
|
// operations into read and write disk sector requests.
|
||||||
|
// In this baseline implementation of the file system, we don't
|
||||||
|
// worry about concurrent accesses to the file system
|
||||||
|
// by different threads.
|
||||||
|
//
|
||||||
|
// 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 OPENFILE_H
|
||||||
|
#define OPENFILE_H
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
#include "utility.h"
|
||||||
|
#include "sysdep.h"
|
||||||
|
|
||||||
|
#ifdef FILESYS_STUB // Temporarily implement calls to
|
||||||
|
// Nachos file system as calls to UNIX!
|
||||||
|
// See definitions listed under #else
|
||||||
|
class OpenFile {
|
||||||
|
public:
|
||||||
|
OpenFile(int f) { file = f; currentOffset = 0; } // open the file
|
||||||
|
~OpenFile() { Close(file); } // close the file
|
||||||
|
|
||||||
|
int ReadAt(char *into, int numBytes, int position) {
|
||||||
|
Lseek(file, position, 0);
|
||||||
|
return ReadPartial(file, into, numBytes);
|
||||||
|
}
|
||||||
|
int WriteAt(char *from, int numBytes, int position) {
|
||||||
|
Lseek(file, position, 0);
|
||||||
|
WriteFile(file, from, numBytes);
|
||||||
|
return numBytes;
|
||||||
|
}
|
||||||
|
int Read(char *into, int numBytes) {
|
||||||
|
int numRead = ReadAt(into, numBytes, currentOffset);
|
||||||
|
currentOffset += numRead;
|
||||||
|
return numRead;
|
||||||
|
}
|
||||||
|
int Write(char *from, int numBytes) {
|
||||||
|
int numWritten = WriteAt(from, numBytes, currentOffset);
|
||||||
|
currentOffset += numWritten;
|
||||||
|
return numWritten;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Length() { Lseek(file, 0, 2); return Tell(file); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
int file;
|
||||||
|
int currentOffset;
|
||||||
|
};
|
||||||
|
|
||||||
|
#else // FILESYS
|
||||||
|
class FileHeader;
|
||||||
|
|
||||||
|
class OpenFile {
|
||||||
|
public:
|
||||||
|
OpenFile(int sector); // Open a file whose header is located
|
||||||
|
// at "sector" on the disk
|
||||||
|
~OpenFile(); // Close the file
|
||||||
|
|
||||||
|
void Seek(int position); // Set the position from which to
|
||||||
|
// start reading/writing -- UNIX lseek
|
||||||
|
|
||||||
|
int Read(char *into, int numBytes); // Read/write bytes from the file,
|
||||||
|
// starting at the implicit position.
|
||||||
|
// Return the # actually read/written,
|
||||||
|
// and increment position in file.
|
||||||
|
int Write(char *from, int numBytes);
|
||||||
|
|
||||||
|
int ReadAt(char *into, int numBytes, int position);
|
||||||
|
// Read/write bytes from the file,
|
||||||
|
// bypassing the implicit position.
|
||||||
|
int WriteAt(char *from, int numBytes, int position);
|
||||||
|
|
||||||
|
int Length(); // Return the number of bytes in the
|
||||||
|
// file (this interface is simpler
|
||||||
|
// than the UNIX idiom -- lseek to
|
||||||
|
// end of file, tell, lseek back
|
||||||
|
|
||||||
|
private:
|
||||||
|
FileHeader *hdr; // Header for this file
|
||||||
|
int seekPosition; // Current position within the file
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // FILESYS
|
||||||
|
|
||||||
|
#endif // OPENFILE_H
|
||||||
79
code/filesys/pbitmap.cc
Executable file
79
code/filesys/pbitmap.cc
Executable file
@@ -0,0 +1,79 @@
|
|||||||
|
// pbitmap.c
|
||||||
|
// Routines to manage a persistent bitmap -- a bitmap that is
|
||||||
|
// stored on disk.
|
||||||
|
//
|
||||||
|
// Copyright (c) 1992,1993,1995 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 "pbitmap.h"
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// PersistentBitmap::PersistentBitmap(int)
|
||||||
|
// Initialize a bitmap with "numItems" bits, so that every bit is clear.
|
||||||
|
// it can be added somewhere on a list.
|
||||||
|
//
|
||||||
|
// "numItems" is the number of bits in the bitmap.
|
||||||
|
//
|
||||||
|
// This constructor does not initialize the bitmap from a disk file
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
PersistentBitmap::PersistentBitmap(int numItems):Bitmap(numItems)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// PersistentBitmap::PersistentBitmap(OpenFile*,int)
|
||||||
|
// Initialize a persistent bitmap with "numItems" bits,
|
||||||
|
// so that every bit is clear.
|
||||||
|
//
|
||||||
|
// "numItems" is the number of bits in the bitmap.
|
||||||
|
// "file" refers to an open file containing the bitmap (written
|
||||||
|
// by a previous call to PersistentBitmap::WriteBack
|
||||||
|
//
|
||||||
|
// This constructor initializes the bitmap from a disk file
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
PersistentBitmap::PersistentBitmap(OpenFile *file, int numItems):Bitmap(numItems)
|
||||||
|
{
|
||||||
|
// map has already been initialized by the BitMap constructor,
|
||||||
|
// but we will just overwrite that with the contents of the
|
||||||
|
// map found in the file
|
||||||
|
file->ReadAt((char *)map, numWords * sizeof(unsigned), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// PersistentBitmap::~PersistentBitmap
|
||||||
|
// De-allocate a persistent bitmap.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
PersistentBitmap::~PersistentBitmap()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// PersistentBitmap::FetchFrom
|
||||||
|
// Initialize the contents of a persistent bitmap from a Nachos file.
|
||||||
|
//
|
||||||
|
// "file" is the place to read the bitmap from
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
PersistentBitmap::FetchFrom(OpenFile *file)
|
||||||
|
{
|
||||||
|
file->ReadAt((char *)map, numWords * sizeof(unsigned), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// PersistentBitmap::WriteBack
|
||||||
|
// Store the contents of a persistent bitmap to a Nachos file.
|
||||||
|
//
|
||||||
|
// "file" is the place to write the bitmap to
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
PersistentBitmap::WriteBack(OpenFile *file)
|
||||||
|
{
|
||||||
|
file->WriteAt((char *)map, numWords * sizeof(unsigned), 0);
|
||||||
|
}
|
||||||
35
code/filesys/pbitmap.h
Executable file
35
code/filesys/pbitmap.h
Executable file
@@ -0,0 +1,35 @@
|
|||||||
|
// pbitmap.h
|
||||||
|
// Data structures defining a "persistent" bitmap -- a bitmap
|
||||||
|
// that can be stored and fetched off of disk
|
||||||
|
//
|
||||||
|
// A persistent bitmap can either be initialized from the disk
|
||||||
|
// when it is created, or it can be initialized later using
|
||||||
|
// the FetchFrom method
|
||||||
|
//
|
||||||
|
// Copyright (c) 1992,1993,1995 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 PBITMAP_H
|
||||||
|
#define PBITMAP_H
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
#include "bitmap.h"
|
||||||
|
#include "openfile.h"
|
||||||
|
|
||||||
|
// The following class defines a persistent bitmap. It inherits all
|
||||||
|
// the behavior of a bitmap (see bitmap.h), adding the ability to
|
||||||
|
// be read from and stored to the disk.
|
||||||
|
|
||||||
|
class PersistentBitmap : public Bitmap {
|
||||||
|
public:
|
||||||
|
PersistentBitmap(OpenFile *file,int numItems); //initialize bitmap from disk
|
||||||
|
PersistentBitmap(int numItems); // or don't...
|
||||||
|
|
||||||
|
~PersistentBitmap(); // deallocate bitmap
|
||||||
|
|
||||||
|
void FetchFrom(OpenFile *file); // read bitmap from the disk
|
||||||
|
void WriteBack(OpenFile *file); // write bitmap contents to disk
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // PBITMAP_H
|
||||||
94
code/filesys/synchdisk.cc
Executable file
94
code/filesys/synchdisk.cc
Executable file
@@ -0,0 +1,94 @@
|
|||||||
|
// synchdisk.cc
|
||||||
|
// Routines to synchronously access the disk. The physical disk
|
||||||
|
// is an asynchronous device (disk requests return immediately, and
|
||||||
|
// an interrupt happens later on). This is a layer on top of
|
||||||
|
// the disk providing a synchronous interface (requests wait until
|
||||||
|
// the request completes).
|
||||||
|
//
|
||||||
|
// Use a semaphore to synchronize the interrupt handlers with the
|
||||||
|
// pending requests. And, because the physical disk can only
|
||||||
|
// handle one operation at a time, use a lock to enforce mutual
|
||||||
|
// exclusion.
|
||||||
|
//
|
||||||
|
// 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 "synchdisk.h"
|
||||||
|
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// SynchDisk::SynchDisk
|
||||||
|
// Initialize the synchronous interface to the physical disk, in turn
|
||||||
|
// initializing the physical disk.
|
||||||
|
//
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
SynchDisk::SynchDisk()
|
||||||
|
{
|
||||||
|
semaphore = new Semaphore("synch disk", 0);
|
||||||
|
lock = new Lock("synch disk lock");
|
||||||
|
disk = new Disk(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// SynchDisk::~SynchDisk
|
||||||
|
// De-allocate data structures needed for the synchronous disk
|
||||||
|
// abstraction.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
SynchDisk::~SynchDisk()
|
||||||
|
{
|
||||||
|
delete disk;
|
||||||
|
delete lock;
|
||||||
|
delete semaphore;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// SynchDisk::ReadSector
|
||||||
|
// Read the contents of a disk sector into a buffer. Return only
|
||||||
|
// after the data has been read.
|
||||||
|
//
|
||||||
|
// "sectorNumber" -- the disk sector to read
|
||||||
|
// "data" -- the buffer to hold the contents of the disk sector
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
SynchDisk::ReadSector(int sectorNumber, char* data)
|
||||||
|
{
|
||||||
|
lock->Acquire(); // only one disk I/O at a time
|
||||||
|
disk->ReadRequest(sectorNumber, data);
|
||||||
|
semaphore->P(); // wait for interrupt
|
||||||
|
lock->Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// SynchDisk::WriteSector
|
||||||
|
// Write the contents of a buffer into a disk sector. Return only
|
||||||
|
// after the data has been written.
|
||||||
|
//
|
||||||
|
// "sectorNumber" -- the disk sector to be written
|
||||||
|
// "data" -- the new contents of the disk sector
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
SynchDisk::WriteSector(int sectorNumber, char* data)
|
||||||
|
{
|
||||||
|
lock->Acquire(); // only one disk I/O at a time
|
||||||
|
disk->WriteRequest(sectorNumber, data);
|
||||||
|
semaphore->P(); // wait for interrupt
|
||||||
|
lock->Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// SynchDisk::CallBack
|
||||||
|
// Disk interrupt handler. Wake up any thread waiting for the disk
|
||||||
|
// request to finish.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
SynchDisk::CallBack()
|
||||||
|
{
|
||||||
|
semaphore->V();
|
||||||
|
}
|
||||||
55
code/filesys/synchdisk.h
Executable file
55
code/filesys/synchdisk.h
Executable file
@@ -0,0 +1,55 @@
|
|||||||
|
// synchdisk.h
|
||||||
|
// Data structures to export a synchronous interface to the raw
|
||||||
|
// disk device.
|
||||||
|
//
|
||||||
|
// 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"
|
||||||
|
|
||||||
|
#ifndef SYNCHDISK_H
|
||||||
|
#define SYNCHDISK_H
|
||||||
|
|
||||||
|
#include "disk.h"
|
||||||
|
#include "synch.h"
|
||||||
|
#include "callback.h"
|
||||||
|
|
||||||
|
// The following class defines a "synchronous" disk abstraction.
|
||||||
|
// 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 occurs later to signal that the operation completed.
|
||||||
|
// (Also, the physical characteristics of the disk device assume that
|
||||||
|
// only one operation can be requested at a time).
|
||||||
|
//
|
||||||
|
// This class provides the abstraction that for any individual thread
|
||||||
|
// making a request, it waits around until the operation finishes before
|
||||||
|
// returning.
|
||||||
|
|
||||||
|
class SynchDisk : public CallBackObj {
|
||||||
|
public:
|
||||||
|
SynchDisk(); // Initialize a synchronous disk,
|
||||||
|
// by initializing the raw Disk.
|
||||||
|
~SynchDisk(); // De-allocate the synch disk data
|
||||||
|
|
||||||
|
void ReadSector(int sectorNumber, char* data);
|
||||||
|
// Read/write a disk sector, returning
|
||||||
|
// only once the data is actually read
|
||||||
|
// or written. These call
|
||||||
|
// Disk::ReadRequest/WriteRequest and
|
||||||
|
// then wait until the request is done.
|
||||||
|
void WriteSector(int sectorNumber, char* data);
|
||||||
|
|
||||||
|
void CallBack(); // Called by the disk device interrupt
|
||||||
|
// handler, to signal that the
|
||||||
|
// current disk operation is complete.
|
||||||
|
|
||||||
|
private:
|
||||||
|
Disk *disk; // Raw disk device
|
||||||
|
Semaphore *semaphore; // To synchronize requesting thread
|
||||||
|
// with the interrupt handler
|
||||||
|
Lock *lock; // Only one read/write request
|
||||||
|
// can be sent to the disk at a time
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SYNCHDISK_H
|
||||||
191
code/lib/bitmap.cc
Executable file
191
code/lib/bitmap.cc
Executable file
@@ -0,0 +1,191 @@
|
|||||||
|
// bitmap.cc
|
||||||
|
// Routines to manage a bitmap -- an array of bits each of which
|
||||||
|
// can be either on or off. Represented as an array of integers.
|
||||||
|
//
|
||||||
|
// Copyright (c) 1992-1996 The Regents of the University of California.
|
||||||
|
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||||
|
// of liability and disclaimer of warranty provisions.
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
#include "debug.h"
|
||||||
|
#include "bitmap.h"
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// BitMap::BitMap
|
||||||
|
// Initialize a bitmap with "numItems" bits, so that every bit is clear.
|
||||||
|
// it can be added somewhere on a list.
|
||||||
|
//
|
||||||
|
// "numItems" is the number of bits in the bitmap.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
Bitmap::Bitmap(int numItems)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
ASSERT(numItems > 0);
|
||||||
|
|
||||||
|
numBits = numItems;
|
||||||
|
numWords = divRoundUp(numBits, BitsInWord);
|
||||||
|
map = new unsigned int[numWords];
|
||||||
|
for (i = 0; i < numWords; i++) {
|
||||||
|
map[i] = 0; // initialize map to keep Purify happy
|
||||||
|
}
|
||||||
|
for (i = 0; i < numBits; i++) {
|
||||||
|
Clear(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Bitmap::~Bitmap
|
||||||
|
// De-allocate a bitmap.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
Bitmap::~Bitmap()
|
||||||
|
{
|
||||||
|
delete map;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Bitmap::Set
|
||||||
|
// Set the "nth" bit in a bitmap.
|
||||||
|
//
|
||||||
|
// "which" is the number of the bit to be set.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
Bitmap::Mark(int which)
|
||||||
|
{
|
||||||
|
ASSERT(which >= 0 && which < numBits);
|
||||||
|
|
||||||
|
map[which / BitsInWord] |= 1 << (which % BitsInWord);
|
||||||
|
|
||||||
|
ASSERT(Test(which));
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Bitmap::Clear
|
||||||
|
// Clear the "nth" bit in a bitmap.
|
||||||
|
//
|
||||||
|
// "which" is the number of the bit to be cleared.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
Bitmap::Clear(int which)
|
||||||
|
{
|
||||||
|
ASSERT(which >= 0 && which < numBits);
|
||||||
|
|
||||||
|
map[which / BitsInWord] &= ~(1 << (which % BitsInWord));
|
||||||
|
|
||||||
|
ASSERT(!Test(which));
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Bitmap::Test
|
||||||
|
// Return TRUE if the "nth" bit is set.
|
||||||
|
//
|
||||||
|
// "which" is the number of the bit to be tested.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
bool
|
||||||
|
Bitmap::Test(int which) const
|
||||||
|
{
|
||||||
|
ASSERT(which >= 0 && which < numBits);
|
||||||
|
|
||||||
|
if (map[which / BitsInWord] & (1 << (which % BitsInWord))) {
|
||||||
|
return TRUE;
|
||||||
|
} else {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Bitmap::FindAndSet
|
||||||
|
// Return the number of the first bit which is clear.
|
||||||
|
// As a side effect, set the bit (mark it as in use).
|
||||||
|
// (In other words, find and allocate a bit.)
|
||||||
|
//
|
||||||
|
// If no bits are clear, return -1.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
int
|
||||||
|
Bitmap::FindAndSet()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < numBits; i++) {
|
||||||
|
if (!Test(i)) {
|
||||||
|
Mark(i);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Bitmap::NumClear
|
||||||
|
// Return the number of clear bits in the bitmap.
|
||||||
|
// (In other words, how many bits are unallocated?)
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
int
|
||||||
|
Bitmap::NumClear() const
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < numBits; i++) {
|
||||||
|
if (!Test(i)) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Bitmap::Print
|
||||||
|
// Print the contents of the bitmap, for debugging.
|
||||||
|
//
|
||||||
|
// Could be done in a number of ways, but we just print the #'s of
|
||||||
|
// all the bits that are set in the bitmap.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
Bitmap::Print() const
|
||||||
|
{
|
||||||
|
cout << "Bitmap set:\n";
|
||||||
|
for (int i = 0; i < numBits; i++) {
|
||||||
|
if (Test(i)) {
|
||||||
|
cout << i << ", ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cout << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Bitmap::SelfTest
|
||||||
|
// Test whether this module is working.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
Bitmap::SelfTest()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
ASSERT(numBits >= BitsInWord); // bitmap must be big enough
|
||||||
|
|
||||||
|
ASSERT(NumClear() == numBits); // bitmap must be empty
|
||||||
|
ASSERT(FindAndSet() == 0);
|
||||||
|
Mark(31);
|
||||||
|
ASSERT(Test(0) && Test(31));
|
||||||
|
|
||||||
|
ASSERT(FindAndSet() == 1);
|
||||||
|
Clear(0);
|
||||||
|
Clear(1);
|
||||||
|
Clear(31);
|
||||||
|
|
||||||
|
for (i = 0; i < numBits; i++) {
|
||||||
|
Mark(i);
|
||||||
|
}
|
||||||
|
ASSERT(FindAndSet() == -1); // bitmap should be full!
|
||||||
|
for (i = 0; i < numBits; i++) {
|
||||||
|
Clear(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
59
code/lib/bitmap.h
Executable file
59
code/lib/bitmap.h
Executable file
@@ -0,0 +1,59 @@
|
|||||||
|
// bitmap.h
|
||||||
|
// Data structures defining a bitmap -- an array of bits each of which
|
||||||
|
// can be either on or off.
|
||||||
|
//
|
||||||
|
// Represented as an array of unsigned integers, on which we do
|
||||||
|
// modulo arithmetic to find the bit we are interested in.
|
||||||
|
//
|
||||||
|
// The bitmap can be parameterized with with the number of bits being
|
||||||
|
// managed.
|
||||||
|
//
|
||||||
|
// Copyright (c) 1992-1996 The Regents of the University of California.
|
||||||
|
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||||
|
// of liability and disclaimer of warranty provisions.
|
||||||
|
|
||||||
|
#ifndef BITMAP_H
|
||||||
|
#define BITMAP_H
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
#include "utility.h"
|
||||||
|
|
||||||
|
// Definitions helpful for representing a bitmap as an array of integers
|
||||||
|
const int BitsInByte = 8;
|
||||||
|
const int BitsInWord = sizeof(unsigned int) * BitsInByte;
|
||||||
|
|
||||||
|
// The following class defines a "bitmap" -- an array of bits,
|
||||||
|
// each of which can be independently set, cleared, and tested.
|
||||||
|
//
|
||||||
|
// Most useful for managing the allocation of the elements of an array --
|
||||||
|
// for instance, disk sectors, or main memory pages.
|
||||||
|
// Each bit represents whether the corresponding sector or page is
|
||||||
|
// in use or free.
|
||||||
|
|
||||||
|
class Bitmap {
|
||||||
|
public:
|
||||||
|
Bitmap(int numItems); // Initialize a bitmap, with "numItems" bits
|
||||||
|
// initially, all bits are cleared.
|
||||||
|
~Bitmap(); // De-allocate bitmap
|
||||||
|
|
||||||
|
void Mark(int which); // Set the "nth" bit
|
||||||
|
void Clear(int which); // Clear the "nth" bit
|
||||||
|
bool Test(int which) const; // Is the "nth" bit set?
|
||||||
|
int FindAndSet(); // Return the # of a clear bit, and as a side
|
||||||
|
// effect, set the bit.
|
||||||
|
// If no bits are clear, return -1.
|
||||||
|
int NumClear() const; // Return the number of clear bits
|
||||||
|
|
||||||
|
void Print() const; // Print contents of bitmap
|
||||||
|
void SelfTest(); // Test whether bitmap is working
|
||||||
|
|
||||||
|
protected:
|
||||||
|
int numBits; // number of bits in the bitmap
|
||||||
|
int numWords; // number of words of bitmap storage
|
||||||
|
// (rounded up if numBits is not a
|
||||||
|
// multiple of the number of bits in
|
||||||
|
// a word)
|
||||||
|
unsigned int *map; // bit storage
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // BITMAP_H
|
||||||
24
code/lib/copyright.h
Executable file
24
code/lib/copyright.h
Executable file
@@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 1992-1996 The Regents of the University of California.
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and distribute this software and its
|
||||||
|
documentation for any purpose, without fee, and without written agreement is
|
||||||
|
hereby granted, provided that the above copyright notice and the following
|
||||||
|
two paragraphs appear in all copies of this software.
|
||||||
|
|
||||||
|
IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
|
||||||
|
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
|
||||||
|
OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
|
||||||
|
CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
|
||||||
|
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
||||||
|
ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
|
||||||
|
PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef MAIN /* include the copyright message in every executable */
|
||||||
|
static char *copyright = "Copyright (c) 1992-1993 The Regents of the University of California. All rights reserved.";
|
||||||
|
#endif // MAIN
|
||||||
45
code/lib/debug.cc
Executable file
45
code/lib/debug.cc
Executable file
@@ -0,0 +1,45 @@
|
|||||||
|
// debug.cc
|
||||||
|
// Debugging routines. Allows users to control whether to
|
||||||
|
// print DEBUG statements, based on a command line argument.
|
||||||
|
//
|
||||||
|
// Copyright (c) 1992-1996 The Regents of the University of California.
|
||||||
|
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||||
|
// of liability and disclaimer of warranty provisions.
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
#include "utility.h"
|
||||||
|
#include "debug.h"
|
||||||
|
#include "string.h"
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Debug::Debug
|
||||||
|
// Initialize so that only DEBUG messages with a flag in flagList
|
||||||
|
// will be printed.
|
||||||
|
//
|
||||||
|
// If the flag is "+", we enable all DEBUG messages.
|
||||||
|
//
|
||||||
|
// "flagList" is a string of characters for whose DEBUG messages are
|
||||||
|
// to be enabled.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
Debug::Debug(char *flagList)
|
||||||
|
{
|
||||||
|
enableFlags = flagList;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Debug::IsEnabled
|
||||||
|
// Return TRUE if DEBUG messages with "flag" are to be printed.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
bool
|
||||||
|
Debug::IsEnabled(char flag)
|
||||||
|
{
|
||||||
|
if (enableFlags != NULL) {
|
||||||
|
return ((strchr(enableFlags, flag) != 0)
|
||||||
|
|| (strchr(enableFlags, '+') != 0));
|
||||||
|
} else {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
96
code/lib/debug.h
Executable file
96
code/lib/debug.h
Executable file
@@ -0,0 +1,96 @@
|
|||||||
|
// debug.h
|
||||||
|
// Data structures for debugging routines.
|
||||||
|
//
|
||||||
|
// The debugging routines allow the user to turn on selected
|
||||||
|
// debugging messages, controllable from the command line arguments
|
||||||
|
// passed to Nachos (-d). You are encouraged to add your own
|
||||||
|
// debugging flags. Please....
|
||||||
|
//
|
||||||
|
// Copyright (c) 1992-1996 The Regents of the University of California.
|
||||||
|
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||||
|
// of liability and disclaimer of warranty provisions.
|
||||||
|
|
||||||
|
#ifndef DEBUG_H
|
||||||
|
#define DEBUG_H
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
#include "utility.h"
|
||||||
|
#include "sysdep.h"
|
||||||
|
|
||||||
|
// The pre-defined debugging flags are:
|
||||||
|
|
||||||
|
const char dbgAll = '+'; // turn on all debug messages
|
||||||
|
const char dbgThread = 't'; // threads
|
||||||
|
const char dbgSynch = 's'; // locks, semaphores, condition vars
|
||||||
|
const char dbgInt = 'i'; // interrupt emulation
|
||||||
|
const char dbgMach = 'm'; // machine emulation
|
||||||
|
const char dbgDisk = 'd'; // disk emulation
|
||||||
|
const char dbgFile = 'f'; // file system
|
||||||
|
const char dbgAddr = 'a'; // address spaces
|
||||||
|
const char dbgNet = 'n'; // network emulation
|
||||||
|
const char dbgSys = 'u'; // systemcall
|
||||||
|
|
||||||
|
class Debug {
|
||||||
|
public:
|
||||||
|
Debug(char *flagList);
|
||||||
|
|
||||||
|
bool IsEnabled(char flag);
|
||||||
|
|
||||||
|
private:
|
||||||
|
char *enableFlags; // controls which DEBUG messages are printed
|
||||||
|
};
|
||||||
|
|
||||||
|
extern Debug *debug;
|
||||||
|
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// DEBUG
|
||||||
|
// If flag is enabled, print a message.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
#define DEBUG(flag,expr) \
|
||||||
|
if (!debug->IsEnabled(flag)) {} else { \
|
||||||
|
cerr << expr << "\n"; \
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// ASSERT
|
||||||
|
// If condition is false, print a message and dump core.
|
||||||
|
// Useful for documenting assumptions in the code.
|
||||||
|
//
|
||||||
|
// NOTE: needs to be a #define, to be able to print the location
|
||||||
|
// where the error occurred.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
#define ASSERT(condition) \
|
||||||
|
if (condition) {} else { \
|
||||||
|
cerr << "Assertion failed: line " << __LINE__ << " file " << __FILE__ << "\n"; \
|
||||||
|
Abort(); \
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// ASSERTNOTREACHED
|
||||||
|
// Print a message and dump core (equivalent to ASSERT(FALSE) without
|
||||||
|
// making the compiler whine). Useful for documenting when
|
||||||
|
// code should not be reached.
|
||||||
|
//
|
||||||
|
// NOTE: needs to be a #define, to be able to print the location
|
||||||
|
// where the error occurred.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
#define ASSERTNOTREACHED() \
|
||||||
|
{ \
|
||||||
|
cerr << "Assertion failed: line " << __LINE__ << " file " << __FILE__ << "\n"; \
|
||||||
|
Abort(); \
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// ASSERTUNIMPLEMENTED
|
||||||
|
// Print a message that unimplemented code is executed and dump core
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
#define UNIMPLEMENTED() \
|
||||||
|
{ \
|
||||||
|
cerr << "Reached UNIMPLEMENTED function " << __FUNCTION__ << " in file: " \
|
||||||
|
<< __FILE__ << " line: " << __LINE__ << ".\n"; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // DEBUG_H
|
||||||
360
code/lib/hash.cc
Executable file
360
code/lib/hash.cc
Executable file
@@ -0,0 +1,360 @@
|
|||||||
|
// hash.cc
|
||||||
|
// Routines to manage a self-expanding hash table of arbitrary things.
|
||||||
|
// The hashing function is supplied by the objects being put into
|
||||||
|
// the table; we use chaining to resolve hash conflicts.
|
||||||
|
//
|
||||||
|
// The hash table is implemented as an array of sorted lists,
|
||||||
|
// and we expand the hash table if the number of elements in the table
|
||||||
|
// gets too big.
|
||||||
|
//
|
||||||
|
// NOTE: Mutual exclusion must be provided by the caller.
|
||||||
|
//
|
||||||
|
// Copyright (c) 1992-1996 The Regents of the University of California.
|
||||||
|
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||||
|
// of liability and disclaimer of warranty provisions.
|
||||||
|
|
||||||
|
const int InitialBuckets = 4; // how big a hash table do we start with
|
||||||
|
const int ResizeRatio = 3; // when do we grow the hash table?
|
||||||
|
const int IncreaseSizeBy = 4; // how much do we grow table when needed?
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// HashTable<Key,T>::HashTable
|
||||||
|
// Initialize a hash table, empty to start with.
|
||||||
|
// Elements can now be added to the table.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class Key, class T>
|
||||||
|
HashTable<Key,T>::HashTable(Key (*get)(T x), unsigned (*hFunc)(Key x))
|
||||||
|
{
|
||||||
|
numItems = 0;
|
||||||
|
InitBuckets(InitialBuckets);
|
||||||
|
getKey = get;
|
||||||
|
hash = hFunc;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// HashTable<Key,T>::InitBuckets
|
||||||
|
// Initialize the bucket array for a hash table.
|
||||||
|
// Called by the constructor and by ReHash().
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class Key, class T>
|
||||||
|
void
|
||||||
|
HashTable<Key,T>::InitBuckets(int sz)
|
||||||
|
{
|
||||||
|
numBuckets = sz;
|
||||||
|
buckets = new Bucket[numBuckets];
|
||||||
|
for (int i = 0; i < sz; i++) {
|
||||||
|
buckets[i] = new List<T>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// HashTable<T>::~HashTable
|
||||||
|
// Prepare a hash table for deallocation.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class Key, class T>
|
||||||
|
HashTable<Key,T>::~HashTable()
|
||||||
|
{
|
||||||
|
ASSERT(IsEmpty()); // make sure table is empty
|
||||||
|
DeleteBuckets(buckets, numBuckets);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// HashTable<Key,T>::DeleteBuckets
|
||||||
|
// De-Initialize the bucket array for a hash table.
|
||||||
|
// Called by the destructor and by ReHash().
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class Key, class T>
|
||||||
|
void
|
||||||
|
HashTable<Key,T>::DeleteBuckets(List<T> **table, int sz)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < sz; i++) {
|
||||||
|
delete table[i];
|
||||||
|
}
|
||||||
|
delete [] table;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// HashTable<Key,T>::HashValue
|
||||||
|
// Return hash table bucket that would contain key.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class Key, class T>
|
||||||
|
int
|
||||||
|
HashTable<Key, T>::HashValue(Key key) const
|
||||||
|
{
|
||||||
|
int result = (*hash)(key) % numBuckets;
|
||||||
|
ASSERT(result >= 0 && result < numBuckets);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// HashTable<Key,T>::Insert
|
||||||
|
// Put an item into the hashtable.
|
||||||
|
//
|
||||||
|
// Resize the table if the # of elements / # of buckets is too big.
|
||||||
|
// Then allocate a HashElement to keep track of the key, item pair,
|
||||||
|
// and add it to the right bucket.
|
||||||
|
//
|
||||||
|
// "key" is the key we'll use to find this item.
|
||||||
|
// "item" is the thing to put in the table.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class Key, class T>
|
||||||
|
void
|
||||||
|
HashTable<Key,T>::Insert(T item)
|
||||||
|
{
|
||||||
|
Key key = getKey(item);
|
||||||
|
|
||||||
|
ASSERT(!IsInTable(key));
|
||||||
|
|
||||||
|
if ((numItems / numBuckets) >= ResizeRatio) {
|
||||||
|
ReHash();
|
||||||
|
}
|
||||||
|
|
||||||
|
buckets[HashValue(key)]->Append(item);
|
||||||
|
numItems++;
|
||||||
|
|
||||||
|
ASSERT(IsInTable(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// HashTable<Key,T>::ReHash
|
||||||
|
// Increase the size of the hashtable, by
|
||||||
|
// (i) making a new table
|
||||||
|
// (ii) moving all the elements into the new table
|
||||||
|
// (iii) deleting the old table
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class Key, class T>
|
||||||
|
void
|
||||||
|
HashTable<Key,T>::ReHash()
|
||||||
|
{
|
||||||
|
Bucket *oldTable = buckets;
|
||||||
|
int oldSize = numBuckets;
|
||||||
|
T item;
|
||||||
|
|
||||||
|
SanityCheck();
|
||||||
|
InitBuckets(numBuckets * IncreaseSizeBy);
|
||||||
|
|
||||||
|
for (int i = 0; i < oldSize; i++) {
|
||||||
|
while (!oldTable[i]->IsEmpty()) {
|
||||||
|
item = oldTable[i]->RemoveFront();
|
||||||
|
buckets[HashValue(getKey(item))]->Append(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DeleteBuckets(oldTable, oldSize);
|
||||||
|
SanityCheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// HashTable<Key,T>::FindInBucket
|
||||||
|
// Find an item in a hash table bucket, from it's key
|
||||||
|
//
|
||||||
|
// "bucket" -- the list storing the item, if it's in the table
|
||||||
|
// "key" -- the key uniquely identifying the item
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// Whether item is found, and if found, the item.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class Key, class T>
|
||||||
|
bool
|
||||||
|
HashTable<Key,T>::FindInBucket(int bucket,
|
||||||
|
Key key, T *itemPtr) const
|
||||||
|
{
|
||||||
|
ListIterator<T> iterator(buckets[bucket]);
|
||||||
|
|
||||||
|
for (; !iterator.IsDone(); iterator.Next()) {
|
||||||
|
if (key == getKey(iterator.Item())) { // found!
|
||||||
|
*itemPtr = iterator.Item();
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*itemPtr = NULL;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// HashTable<Key,T>::Find
|
||||||
|
// Find an item from the hash table.
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// The item or NULL if not found.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class Key, class T>
|
||||||
|
bool
|
||||||
|
HashTable<Key,T>::Find(Key key, T *itemPtr) const
|
||||||
|
{
|
||||||
|
int bucket = HashValue(key);
|
||||||
|
|
||||||
|
return FindInBucket(bucket, key, itemPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// HashTable<Key,T>::Remove
|
||||||
|
// Remove an item from the hash table. The item must be in the table.
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// The removed item.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class Key, class T>
|
||||||
|
T
|
||||||
|
HashTable<Key,T>::Remove(Key key)
|
||||||
|
{
|
||||||
|
int bucket = HashValue(key);
|
||||||
|
T item;
|
||||||
|
bool found = FindInBucket(bucket, key, &item);
|
||||||
|
|
||||||
|
ASSERT(found); // item must be in table
|
||||||
|
|
||||||
|
buckets[bucket]->Remove(item);
|
||||||
|
numItems--;
|
||||||
|
|
||||||
|
ASSERT(!IsInTable(key));
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// HashTable<Key,T>::Apply
|
||||||
|
// Apply function to every item in the hash table.
|
||||||
|
//
|
||||||
|
// "func" -- the function to apply
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class Key,class T>
|
||||||
|
void
|
||||||
|
HashTable<Key,T>::Apply(void (*func)(T)) const
|
||||||
|
{
|
||||||
|
for (int bucket = 0; bucket < numBuckets; bucket++) {
|
||||||
|
buckets[bucket]->Apply(func);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// HashTable<Key,T>::FindNextFullBucket
|
||||||
|
// Find the next bucket in the hash table that has any items in it.
|
||||||
|
//
|
||||||
|
// "bucket" -- where to start looking for full buckets
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class Key,class T>
|
||||||
|
int
|
||||||
|
HashTable<Key,T>::FindNextFullBucket(int bucket) const
|
||||||
|
{
|
||||||
|
for (; bucket < numBuckets; bucket++) {
|
||||||
|
if (!buckets[bucket]->IsEmpty()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bucket;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// HashTable<Key,T>::SanityCheck
|
||||||
|
// Test whether this is still a legal hash table.
|
||||||
|
//
|
||||||
|
// Tests: are all the buckets legal?
|
||||||
|
// does the table have the right # of elements?
|
||||||
|
// do all the elements hash to where they are stored?
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class Key, class T>
|
||||||
|
void
|
||||||
|
HashTable<Key,T>::SanityCheck() const
|
||||||
|
{
|
||||||
|
int numFound = 0;
|
||||||
|
ListIterator<T> *iterator;
|
||||||
|
|
||||||
|
for (int i = 0; i < numBuckets; i++) {
|
||||||
|
buckets[i]->SanityCheck();
|
||||||
|
numFound += buckets[i]->NumInList();
|
||||||
|
iterator = new ListIterator<T>(buckets[i]);
|
||||||
|
for (; !iterator->IsDone(); iterator->Next()) {
|
||||||
|
ASSERT(i == HashValue(getKey(iterator->Item())));
|
||||||
|
}
|
||||||
|
delete iterator;
|
||||||
|
}
|
||||||
|
ASSERT(numItems == numFound);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// HashTable<Key,T>::SelfTest
|
||||||
|
// Test whether this module is working.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class Key, class T>
|
||||||
|
void
|
||||||
|
HashTable<Key,T>::SelfTest(T *p, int numEntries)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
HashIterator<Key, T> *iterator = new HashIterator<Key,T>(this);
|
||||||
|
|
||||||
|
SanityCheck();
|
||||||
|
ASSERT(IsEmpty()); // check that table is empty in various ways
|
||||||
|
for (; !iterator->IsDone(); iterator->Next()) {
|
||||||
|
ASSERTNOTREACHED();
|
||||||
|
}
|
||||||
|
delete iterator;
|
||||||
|
|
||||||
|
for (i = 0; i < numEntries; i++) {
|
||||||
|
Insert(p[i]);
|
||||||
|
ASSERT(IsInTable(getKey(p[i])));
|
||||||
|
ASSERT(!IsEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
// should be able to get out everything we put in
|
||||||
|
for (i = 0; i < numEntries; i++) {
|
||||||
|
ASSERT(Remove(getKey(p[i])) == p[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT(IsEmpty());
|
||||||
|
SanityCheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// HashIterator<Key,T>::HashIterator
|
||||||
|
// Initialize a data structure to allow us to step through
|
||||||
|
// every entry in a has table.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class Key, class T>
|
||||||
|
HashIterator<Key,T>::HashIterator(HashTable<Key,T> *tbl)
|
||||||
|
{
|
||||||
|
table = tbl;
|
||||||
|
bucket = table->FindNextFullBucket(0);
|
||||||
|
bucketIter = NULL;
|
||||||
|
if (bucket < table->numBuckets) {
|
||||||
|
bucketIter = new ListIterator<T>(table->buckets[bucket]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// HashIterator<Key,T>::Next
|
||||||
|
// Update iterator to point to the next item in the table.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class Key,class T>
|
||||||
|
void
|
||||||
|
HashIterator<Key,T>::Next()
|
||||||
|
{
|
||||||
|
bucketIter->Next();
|
||||||
|
if (bucketIter->IsDone()) {
|
||||||
|
delete bucketIter;
|
||||||
|
bucketIter = NULL;
|
||||||
|
bucket = table->FindNextFullBucket(++bucket);
|
||||||
|
if (bucket < table->numBuckets) {
|
||||||
|
bucketIter = new ListIterator<T>(table->buckets[bucket]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
123
code/lib/hash.h
Executable file
123
code/lib/hash.h
Executable file
@@ -0,0 +1,123 @@
|
|||||||
|
// hash.h
|
||||||
|
// Data structures to manage a hash table to relate arbitrary
|
||||||
|
// keys to arbitrary values. A hash table allows efficient lookup
|
||||||
|
// for the value given the key.
|
||||||
|
//
|
||||||
|
// I've only tested this implementation when both the key and the
|
||||||
|
// value are primitive types (ints or pointers). There is no
|
||||||
|
// guarantee that it will work in general. In particular, it
|
||||||
|
// assumes that the "==" operator works for both keys and values.
|
||||||
|
//
|
||||||
|
// In addition, the key must have Hash() defined:
|
||||||
|
// unsigned Hash(Key k);
|
||||||
|
// returns a randomized # based on value of key
|
||||||
|
//
|
||||||
|
// The value must have a function defined to retrieve the key:
|
||||||
|
// Key GetKey(T x);
|
||||||
|
//
|
||||||
|
// The hash table automatically resizes itself as items are
|
||||||
|
// put into the table. The implementation uses chaining
|
||||||
|
// to resolve hash conflicts.
|
||||||
|
//
|
||||||
|
// Allocation and deallocation of the items in the table are to
|
||||||
|
// be done by the caller.
|
||||||
|
//
|
||||||
|
// Copyright (c) 1992-1996 The Regents of the University of California.
|
||||||
|
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||||
|
// of liability and disclaimer of warranty provisions.
|
||||||
|
|
||||||
|
#ifndef HASH_H
|
||||||
|
#define HASH_H
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
#include "list.h"
|
||||||
|
|
||||||
|
// The following class defines a "hash table" -- allowing quick
|
||||||
|
// lookup according to the hash function defined for the items
|
||||||
|
// being put into the table.
|
||||||
|
|
||||||
|
template <class Key,class T> class HashIterator;
|
||||||
|
|
||||||
|
template <class Key, class T>
|
||||||
|
class HashTable {
|
||||||
|
public:
|
||||||
|
HashTable(Key (*get)(T x), unsigned (*hFunc)(Key x));
|
||||||
|
// initialize a hash table
|
||||||
|
~HashTable(); // deallocate a hash table
|
||||||
|
|
||||||
|
void Insert(T item); // Put item into hash table
|
||||||
|
T Remove(Key key); // Remove item from hash table.
|
||||||
|
|
||||||
|
bool Find(Key key, T *itemPtr) const;
|
||||||
|
// Find an item from its key
|
||||||
|
bool IsInTable(Key key) { T dummy; return Find(key, &dummy); }
|
||||||
|
// Is the item in the table?
|
||||||
|
|
||||||
|
bool IsEmpty() { return numItems == 0; }
|
||||||
|
// does the table have anything in it
|
||||||
|
|
||||||
|
void Apply(void (*f)(T)) const;
|
||||||
|
// apply function to all elements in table
|
||||||
|
|
||||||
|
void SanityCheck() const;// is this still a legal hash table?
|
||||||
|
void SelfTest(T *p, int numItems);
|
||||||
|
// is the module working?
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef List<T> *Bucket;
|
||||||
|
|
||||||
|
Bucket *buckets; // the array of hash buckets
|
||||||
|
int numBuckets; // the number of buckets
|
||||||
|
int numItems; // the number of items in the table
|
||||||
|
|
||||||
|
Key (*getKey)(T x); // get Key from value
|
||||||
|
unsigned (*hash)(Key x); // the hash function
|
||||||
|
|
||||||
|
void InitBuckets(int size);// initialize bucket array
|
||||||
|
void DeleteBuckets(Bucket *table, int size);
|
||||||
|
// deallocate bucket array
|
||||||
|
|
||||||
|
int HashValue(Key key) const;
|
||||||
|
// which bucket does the key hash to?
|
||||||
|
|
||||||
|
void ReHash(); // expand the hash table
|
||||||
|
|
||||||
|
bool FindInBucket(int bucket, Key key, T *itemPtr) const;
|
||||||
|
// find item in bucket
|
||||||
|
int FindNextFullBucket(int start) const;
|
||||||
|
// find next full bucket starting from this one
|
||||||
|
|
||||||
|
friend class HashIterator<Key,T>;
|
||||||
|
};
|
||||||
|
|
||||||
|
// The following class can be used to step through a hash table --
|
||||||
|
// same interface as ListIterator. Example code:
|
||||||
|
// HashIterator<Key, T> iter(table);
|
||||||
|
//
|
||||||
|
// for (; !iter->IsDone(); iter->Next()) {
|
||||||
|
// Operation on iter->Item()
|
||||||
|
// }
|
||||||
|
|
||||||
|
template <class Key,class T>
|
||||||
|
class HashIterator {
|
||||||
|
public:
|
||||||
|
HashIterator(HashTable<Key,T> *table); // initialize an iterator
|
||||||
|
~HashIterator() { if (bucketIter != NULL) delete bucketIter;};
|
||||||
|
// destruct an iterator
|
||||||
|
|
||||||
|
bool IsDone() { return (bucket == table->numBuckets); };
|
||||||
|
// return TRUE if no more items in table
|
||||||
|
T Item() { ASSERT(!IsDone()); return bucketIter->Item(); };
|
||||||
|
// return current item in table
|
||||||
|
void Next(); // update iterator to point to next
|
||||||
|
|
||||||
|
private:
|
||||||
|
HashTable<Key,T> *table; // the hash table we're stepping through
|
||||||
|
int bucket; // current bucket we are in
|
||||||
|
ListIterator<T> *bucketIter; // where we are in the bucket
|
||||||
|
};
|
||||||
|
|
||||||
|
#include "hash.cc" // templates are really like macros
|
||||||
|
// so needs to be included in every
|
||||||
|
// file that uses the template
|
||||||
|
#endif // HASH_H
|
||||||
85
code/lib/libtest.cc
Executable file
85
code/lib/libtest.cc
Executable file
@@ -0,0 +1,85 @@
|
|||||||
|
// libtest.cc
|
||||||
|
// Driver code to call self-test routines for standard library
|
||||||
|
// classes -- bitmaps, lists, sorted lists, and hash tables.
|
||||||
|
//
|
||||||
|
// Copyright (c) 1992-1996 The Regents of the University of California.
|
||||||
|
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||||
|
// of liability and disclaimer of warranty provisions.
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
#include "libtest.h"
|
||||||
|
#include "bitmap.h"
|
||||||
|
#include "list.h"
|
||||||
|
#include "hash.h"
|
||||||
|
#include "sysdep.h"
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// IntCompare
|
||||||
|
// Compare two integers together. Serves as the comparison
|
||||||
|
// function for testing SortedLists
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
static int
|
||||||
|
IntCompare(int x, int y) {
|
||||||
|
if (x < y) return -1;
|
||||||
|
else if (x == y) return 0;
|
||||||
|
else return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// HashInt, HashKey
|
||||||
|
// Compute a hash function on an integer. Serves as the
|
||||||
|
// hashing function for testing HashTables.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
static unsigned int
|
||||||
|
HashInt(int key) {
|
||||||
|
return (unsigned int) key;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// HashKey
|
||||||
|
// Convert a string into an integer. Serves as the function
|
||||||
|
// to retrieve the key from the item in the hash table, for
|
||||||
|
// testing HashTables. Should be able to use "atoi" directly,
|
||||||
|
// but some compilers complain about that.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
static int
|
||||||
|
HashKey(char *str) {
|
||||||
|
return atoi(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Array of values to be inserted into a List or SortedList.
|
||||||
|
static int listTestVector[] = { 9, 5, 7 };
|
||||||
|
|
||||||
|
// Array of values to be inserted into the HashTable
|
||||||
|
// There are enough here to force a ReHash().
|
||||||
|
static char *hashTestVector[] = { "0", "1", "2", "3", "4", "5", "6",
|
||||||
|
"7", "8", "9", "10", "11", "12", "13", "14"};
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// LibSelfTest
|
||||||
|
// Run self tests on bitmaps, lists, sorted lists, and
|
||||||
|
// hash tables.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
LibSelfTest () {
|
||||||
|
Bitmap *map = new Bitmap(200);
|
||||||
|
List<int> *list = new List<int>;
|
||||||
|
SortedList<int> *sortList = new SortedList<int>(IntCompare);
|
||||||
|
HashTable<int, char *> *hashTable =
|
||||||
|
new HashTable<int, char *>(HashKey, HashInt);
|
||||||
|
|
||||||
|
|
||||||
|
map->SelfTest();
|
||||||
|
list->SelfTest(listTestVector, sizeof(listTestVector)/sizeof(int));
|
||||||
|
sortList->SelfTest(listTestVector, sizeof(listTestVector)/sizeof(int));
|
||||||
|
hashTable->SelfTest(hashTestVector, sizeof(hashTestVector)/sizeof(char *));
|
||||||
|
|
||||||
|
delete map;
|
||||||
|
delete list;
|
||||||
|
delete sortList;
|
||||||
|
delete hashTable;
|
||||||
|
}
|
||||||
15
code/lib/libtest.h
Executable file
15
code/lib/libtest.h
Executable file
@@ -0,0 +1,15 @@
|
|||||||
|
// libtest.h
|
||||||
|
// Defines self test module for standard library routines.
|
||||||
|
//
|
||||||
|
// Copyright (c) 1992-1996 The Regents of the University of California.
|
||||||
|
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||||
|
// of liability and disclaimer of warranty provisions.
|
||||||
|
|
||||||
|
#ifndef LIBTEST_H
|
||||||
|
#define LIBTEST_H
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
|
||||||
|
extern void LibSelfTest();
|
||||||
|
|
||||||
|
#endif // LIBTEST_H
|
||||||
383
code/lib/list.cc
Executable file
383
code/lib/list.cc
Executable file
@@ -0,0 +1,383 @@
|
|||||||
|
// list.cc
|
||||||
|
// Routines to manage a singly linked list of "things".
|
||||||
|
// Lists are implemented as templates so that we can store
|
||||||
|
// anything on the list in a type-safe manner.
|
||||||
|
//
|
||||||
|
// A "ListElement" is allocated for each item to be put on the
|
||||||
|
// list; it is de-allocated when the item is removed. This means
|
||||||
|
// we don't need to keep a "next" pointer in every object we
|
||||||
|
// want to put on a list.
|
||||||
|
//
|
||||||
|
// NOTE: Mutual exclusion must be provided by the caller.
|
||||||
|
// If you want a synchronized list, you must use the routines
|
||||||
|
// in synchlist.cc.
|
||||||
|
//
|
||||||
|
// Copyright (c) 1992-1996 The Regents of the University of California.
|
||||||
|
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||||
|
// of liability and disclaimer of warranty provisions.
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// ListElement<T>::ListElement
|
||||||
|
// Initialize a list element, so it can be added somewhere on a list.
|
||||||
|
//
|
||||||
|
// "itm" is the thing to be put on the list.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
ListElement<T>::ListElement(T itm)
|
||||||
|
{
|
||||||
|
item = itm;
|
||||||
|
next = NULL; // always initialize to something!
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// List<T>::List
|
||||||
|
// Initialize a list, empty to start with.
|
||||||
|
// Elements can now be added to the list.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
List<T>::List()
|
||||||
|
{
|
||||||
|
first = last = NULL;
|
||||||
|
numInList = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// List<T>::~List
|
||||||
|
// Prepare a list for deallocation.
|
||||||
|
// This does *NOT* free list elements, nor does it
|
||||||
|
// free the data those elements point to.
|
||||||
|
// Normally, the list should be empty when this is called.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
List<T>::~List()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// List<T>::Append
|
||||||
|
// Append an "item" to the end of the list.
|
||||||
|
//
|
||||||
|
// Allocate a ListElement to keep track of the item.
|
||||||
|
// If the list is empty, then this will be the only element.
|
||||||
|
// Otherwise, put it at the end.
|
||||||
|
//
|
||||||
|
// "item" is the thing to put on the list.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void
|
||||||
|
List<T>::Append(T item)
|
||||||
|
{
|
||||||
|
ListElement<T> *element = new ListElement<T>(item);
|
||||||
|
|
||||||
|
ASSERT(!this->IsInList(item));
|
||||||
|
if (IsEmpty()) { // list is empty
|
||||||
|
first = element;
|
||||||
|
last = element;
|
||||||
|
} else { // else put it after last
|
||||||
|
last->next = element;
|
||||||
|
last = element;
|
||||||
|
}
|
||||||
|
numInList++;
|
||||||
|
ASSERT(this->IsInList(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// List<T>::Prepend
|
||||||
|
// Same as Append, only put "item" on the front.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void
|
||||||
|
List<T>::Prepend(T item)
|
||||||
|
{
|
||||||
|
ListElement<T> *element = new ListElement<T>(item);
|
||||||
|
|
||||||
|
ASSERT(!this->IsInList(item));
|
||||||
|
if (IsEmpty()) { // list is empty
|
||||||
|
first = element;
|
||||||
|
last = element;
|
||||||
|
} else { // else put it before first
|
||||||
|
element->next = first;
|
||||||
|
first = element;
|
||||||
|
}
|
||||||
|
numInList++;
|
||||||
|
ASSERT(this->IsInList(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// List<T>::RemoveFront
|
||||||
|
// Remove the first "item" from the front of the list.
|
||||||
|
// List must not be empty.
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// The removed item.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
T
|
||||||
|
List<T>::RemoveFront()
|
||||||
|
{
|
||||||
|
ListElement<T> *element = first;
|
||||||
|
T thing;
|
||||||
|
|
||||||
|
ASSERT(!IsEmpty());
|
||||||
|
|
||||||
|
thing = first->item;
|
||||||
|
if (first == last) { // list had one item, now has none
|
||||||
|
first = NULL;
|
||||||
|
last = NULL;
|
||||||
|
} else {
|
||||||
|
first = element->next;
|
||||||
|
}
|
||||||
|
numInList--;
|
||||||
|
delete element;
|
||||||
|
return thing;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// List<T>::Remove
|
||||||
|
// Remove a specific item from the list. Must be in the list!
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void
|
||||||
|
List<T>::Remove(T item)
|
||||||
|
{
|
||||||
|
ListElement<T> *prev, *ptr;
|
||||||
|
T removed;
|
||||||
|
|
||||||
|
ASSERT(this->IsInList(item));
|
||||||
|
|
||||||
|
// if first item on list is match, then remove from front
|
||||||
|
if (item == first->item) {
|
||||||
|
removed = RemoveFront();
|
||||||
|
ASSERT(item == removed);
|
||||||
|
} else {
|
||||||
|
prev = first;
|
||||||
|
for (ptr = first->next; ptr != NULL; prev = ptr, ptr = ptr->next) {
|
||||||
|
if (item == ptr->item) {
|
||||||
|
prev->next = ptr->next;
|
||||||
|
if (prev->next == NULL) {
|
||||||
|
last = prev;
|
||||||
|
}
|
||||||
|
delete ptr;
|
||||||
|
numInList--;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ASSERT(ptr != NULL); // should always find item!
|
||||||
|
}
|
||||||
|
ASSERT(!this->IsInList(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// List<T>::IsInList
|
||||||
|
// Return TRUE if the item is in the list.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
bool
|
||||||
|
List<T>::IsInList(T item) const
|
||||||
|
{
|
||||||
|
ListElement<T> *ptr;
|
||||||
|
|
||||||
|
for (ptr = first; ptr != NULL; ptr = ptr->next) {
|
||||||
|
if (item == ptr->item) {
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// List<T>::Apply
|
||||||
|
// Apply function to every item on a list.
|
||||||
|
//
|
||||||
|
// "func" -- the function to apply
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void
|
||||||
|
List<T>::Apply(void (*func)(T)) const
|
||||||
|
{
|
||||||
|
ListElement<T> *ptr;
|
||||||
|
|
||||||
|
for (ptr = first; ptr != NULL; ptr = ptr->next) {
|
||||||
|
(*func)(ptr->item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// SortedList::Insert
|
||||||
|
// Insert an "item" into a list, so that the list elements are
|
||||||
|
// sorted in increasing order.
|
||||||
|
//
|
||||||
|
// Allocate a ListElement to keep track of the item.
|
||||||
|
// If the list is empty, then this will be the only element.
|
||||||
|
// Otherwise, walk through the list, one element at a time,
|
||||||
|
// to find where the new item should be placed.
|
||||||
|
//
|
||||||
|
// "item" is the thing to put on the list.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void
|
||||||
|
SortedList<T>::Insert(T item)
|
||||||
|
{
|
||||||
|
ListElement<T> *element = new ListElement<T>(item);
|
||||||
|
ListElement<T> *ptr; // keep track
|
||||||
|
|
||||||
|
ASSERT(!this->IsInList(item));
|
||||||
|
if (this->IsEmpty()) { // if list is empty, put at front
|
||||||
|
this->first = element;
|
||||||
|
this->last = element;
|
||||||
|
} else if (compare(item, this->first->item) < 0) { // item goes at front
|
||||||
|
element->next = this->first;
|
||||||
|
this->first = element;
|
||||||
|
} else { // look for first elt in list bigger than item
|
||||||
|
for (ptr = this->first; ptr->next != NULL; ptr = ptr->next) {
|
||||||
|
if (compare(item, ptr->next->item) < 0) {
|
||||||
|
element->next = ptr->next;
|
||||||
|
ptr->next = element;
|
||||||
|
this->numInList++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this->last->next = element; // item goes at end of list
|
||||||
|
this->last = element;
|
||||||
|
}
|
||||||
|
this->numInList++;
|
||||||
|
ASSERT(this->IsInList(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// List::SanityCheck
|
||||||
|
// Test whether this is still a legal list.
|
||||||
|
//
|
||||||
|
// Tests: do I get to last starting from first?
|
||||||
|
// does the list have the right # of elements?
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void
|
||||||
|
List<T>::SanityCheck() const
|
||||||
|
{
|
||||||
|
ListElement<T> *ptr;
|
||||||
|
int numFound;
|
||||||
|
|
||||||
|
if (first == NULL) {
|
||||||
|
ASSERT((numInList == 0) && (last == NULL));
|
||||||
|
} else if (first == last) {
|
||||||
|
ASSERT((numInList == 1) && (last->next == NULL));
|
||||||
|
} else {
|
||||||
|
for (numFound = 1, ptr = first; ptr != last; ptr = ptr->next) {
|
||||||
|
numFound++;
|
||||||
|
ASSERT(numFound <= numInList); // prevent infinite loop
|
||||||
|
}
|
||||||
|
ASSERT(numFound == numInList);
|
||||||
|
ASSERT(last->next == NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// List::SelfTest
|
||||||
|
// Test whether this module is working.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void
|
||||||
|
List<T>::SelfTest(T *p, int numEntries)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
ListIterator<T> *iterator = new ListIterator<T>(this);
|
||||||
|
|
||||||
|
SanityCheck();
|
||||||
|
// check various ways that list is empty
|
||||||
|
ASSERT(IsEmpty() && (first == NULL));
|
||||||
|
for (; !iterator->IsDone(); iterator->Next()) {
|
||||||
|
ASSERTNOTREACHED(); // nothing on list
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < numEntries; i++) {
|
||||||
|
Append(p[i]);
|
||||||
|
ASSERT(this->IsInList(p[i]));
|
||||||
|
ASSERT(!IsEmpty());
|
||||||
|
}
|
||||||
|
SanityCheck();
|
||||||
|
|
||||||
|
// should be able to get out everything we put in
|
||||||
|
for (i = 0; i < numEntries; i++) {
|
||||||
|
Remove(p[i]);
|
||||||
|
ASSERT(!this->IsInList(p[i]));
|
||||||
|
}
|
||||||
|
ASSERT(IsEmpty());
|
||||||
|
SanityCheck();
|
||||||
|
delete iterator;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// SortedList::SanityCheck
|
||||||
|
// Test whether this is still a legal sorted list.
|
||||||
|
//
|
||||||
|
// Test: is the list sorted?
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void
|
||||||
|
SortedList<T>::SanityCheck() const
|
||||||
|
{
|
||||||
|
ListElement<T> *prev, *ptr;
|
||||||
|
|
||||||
|
List<T>::SanityCheck();
|
||||||
|
if (this->first != this->last) {
|
||||||
|
for (prev = this->first, ptr = this->first->next; ptr != NULL;
|
||||||
|
prev = ptr, ptr = ptr->next) {
|
||||||
|
ASSERT(compare(prev->item, ptr->item) <= 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// SortedList::SelfTest
|
||||||
|
// Test whether this module is working.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void
|
||||||
|
SortedList<T>::SelfTest(T *p, int numEntries)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
T *q = new T[numEntries];
|
||||||
|
|
||||||
|
List<T>::SelfTest(p, numEntries);
|
||||||
|
|
||||||
|
for (i = 0; i < numEntries; i++) {
|
||||||
|
Insert(p[i]);
|
||||||
|
ASSERT(this->IsInList(p[i]));
|
||||||
|
}
|
||||||
|
SanityCheck();
|
||||||
|
|
||||||
|
// should be able to get out everything we put in
|
||||||
|
for (i = 0; i < numEntries; i++) {
|
||||||
|
q[i] = this->RemoveFront();
|
||||||
|
ASSERT(!this->IsInList(q[i]));
|
||||||
|
}
|
||||||
|
ASSERT(this->IsEmpty());
|
||||||
|
|
||||||
|
// make sure everything came out in the right order
|
||||||
|
for (i = 0; i < (numEntries - 1); i++) {
|
||||||
|
ASSERT(compare(q[i], q[i + 1]) <= 0);
|
||||||
|
}
|
||||||
|
SanityCheck();
|
||||||
|
|
||||||
|
delete q;
|
||||||
|
}
|
||||||
142
code/lib/list.h
Executable file
142
code/lib/list.h
Executable file
@@ -0,0 +1,142 @@
|
|||||||
|
// list.h
|
||||||
|
// Data structures to manage LISP-like lists.
|
||||||
|
//
|
||||||
|
// As in LISP, a list can contain any type of data structure
|
||||||
|
// as an item on the list: thread control blocks,
|
||||||
|
// pending interrupts, etc. Allocation and deallocation of the
|
||||||
|
// items on the list are to be done by the caller.
|
||||||
|
//
|
||||||
|
// Copyright (c) 1992-1996 The Regents of the University of California.
|
||||||
|
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||||
|
// of liability and disclaimer of warranty provisions.
|
||||||
|
|
||||||
|
#ifndef LIST_H
|
||||||
|
#define LIST_H
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
#include "debug.h"
|
||||||
|
|
||||||
|
// The following class defines a "list element" -- which is
|
||||||
|
// used to keep track of one item on a list. It is equivalent to a
|
||||||
|
// LISP cell, with a "car" ("next") pointing to the next element on the list,
|
||||||
|
// and a "cdr" ("item") pointing to the item on the list.
|
||||||
|
//
|
||||||
|
// This class is private to this module (and classes that inherit
|
||||||
|
// from this module). Made public for notational convenience.
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
class ListElement {
|
||||||
|
public:
|
||||||
|
ListElement(T itm); // initialize a list element
|
||||||
|
ListElement *next; // next element on list, NULL if this is last
|
||||||
|
T item; // item on the list
|
||||||
|
};
|
||||||
|
|
||||||
|
// The following class defines a "list" -- a singly linked list of
|
||||||
|
// list elements, each of which points to a single item on the list.
|
||||||
|
// The class has been tested only for primitive types (ints, pointers);
|
||||||
|
// no guarantees it will work in general. For instance, all types
|
||||||
|
// to be inserted into a list must have a "==" operator defined.
|
||||||
|
|
||||||
|
template <class T> class ListIterator;
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
class List {
|
||||||
|
public:
|
||||||
|
List(); // initialize the list
|
||||||
|
virtual ~List(); // de-allocate the list
|
||||||
|
|
||||||
|
virtual void Prepend(T item);// Put item at the beginning of the list
|
||||||
|
virtual void Append(T item); // Put item at the end of the list
|
||||||
|
|
||||||
|
T Front() { return first->item; }
|
||||||
|
// Return first item on list
|
||||||
|
// without removing it
|
||||||
|
T RemoveFront(); // Take item off the front of the list
|
||||||
|
void Remove(T item); // Remove specific item from list
|
||||||
|
|
||||||
|
bool IsInList(T item) const;// is the item in the list?
|
||||||
|
|
||||||
|
unsigned int NumInList() { return numInList;};
|
||||||
|
// how many items in the list?
|
||||||
|
bool IsEmpty() { return (numInList == 0); };
|
||||||
|
// is the list empty?
|
||||||
|
|
||||||
|
void Apply(void (*f)(T)) const;
|
||||||
|
// apply function to all elements in list
|
||||||
|
|
||||||
|
virtual void SanityCheck() const;
|
||||||
|
// has this list been corrupted?
|
||||||
|
void SelfTest(T *p, int numEntries);
|
||||||
|
// verify module is working
|
||||||
|
|
||||||
|
protected:
|
||||||
|
ListElement<T> *first; // Head of the list, NULL if list is empty
|
||||||
|
ListElement<T> *last; // Last element of list
|
||||||
|
int numInList; // number of elements in list
|
||||||
|
|
||||||
|
friend class ListIterator<T>;
|
||||||
|
};
|
||||||
|
|
||||||
|
// The following class defines a "sorted list" -- a singly linked list of
|
||||||
|
// list elements, arranged so that "Remove" always returns the smallest
|
||||||
|
// element.
|
||||||
|
// All types to be inserted onto a sorted list must have a "Compare"
|
||||||
|
// function defined:
|
||||||
|
// int Compare(T x, T y)
|
||||||
|
// returns -1 if x < y
|
||||||
|
// returns 0 if x == y
|
||||||
|
// returns 1 if x > y
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
class SortedList : public List<T> {
|
||||||
|
public:
|
||||||
|
SortedList(int (*comp)(T x, T y)) : List<T>() { compare = comp;};
|
||||||
|
~SortedList() {}; // base class destructor called automatically
|
||||||
|
|
||||||
|
void Insert(T item); // insert an item onto the list in sorted order
|
||||||
|
|
||||||
|
void SanityCheck() const; // has this list been corrupted?
|
||||||
|
void SelfTest(T *p, int numEntries);
|
||||||
|
// verify module is working
|
||||||
|
|
||||||
|
private:
|
||||||
|
int (*compare)(T x, T y); // function for sorting list elements
|
||||||
|
|
||||||
|
void Prepend(T item) { Insert(item); } // *pre*pending has no meaning
|
||||||
|
// in a sorted list
|
||||||
|
void Append(T item) { Insert(item); } // neither does *ap*pend
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// The following class can be used to step through a list.
|
||||||
|
// Example code:
|
||||||
|
// ListIterator<T> *iter(list);
|
||||||
|
//
|
||||||
|
// for (; !iter->IsDone(); iter->Next()) {
|
||||||
|
// Operation on iter->Item()
|
||||||
|
// }
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
class ListIterator {
|
||||||
|
public:
|
||||||
|
ListIterator(List<T> *list) { current = list->first; }
|
||||||
|
// initialize an iterator
|
||||||
|
|
||||||
|
bool IsDone() { return current == NULL; };
|
||||||
|
// return TRUE if we are at the end of the list
|
||||||
|
|
||||||
|
T Item() { ASSERT(!IsDone()); return current->item; };
|
||||||
|
// return current element on list
|
||||||
|
|
||||||
|
void Next() { current = current->next; };
|
||||||
|
// update iterator to point to next
|
||||||
|
|
||||||
|
private:
|
||||||
|
ListElement<T> *current; // where we are in the list
|
||||||
|
};
|
||||||
|
|
||||||
|
#include "list.cc" // templates are really like macros
|
||||||
|
// so needs to be included in every
|
||||||
|
// file that uses the template
|
||||||
|
#endif // LIST_H
|
||||||
571
code/lib/sysdep.cc
Executable file
571
code/lib/sysdep.cc
Executable file
@@ -0,0 +1,571 @@
|
|||||||
|
// 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-1996 The Regents of the University of California.
|
||||||
|
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||||
|
// of liability and disclaimer of warranty provisions.
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
#include "debug.h"
|
||||||
|
#include "sysdep.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/file.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
#include <cerrno>
|
||||||
|
|
||||||
|
#ifdef SOLARIS
|
||||||
|
// KMS
|
||||||
|
// for open()
|
||||||
|
#include <fcntl.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef LINUX // at this point, linux doesn't support mprotect
|
||||||
|
#define NO_MPROT
|
||||||
|
#endif
|
||||||
|
#ifdef DOS // neither does DOS
|
||||||
|
#define NO_MPROT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <signal.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#ifndef NO_MPROT
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// UNIX routines called by procedures in this file
|
||||||
|
|
||||||
|
#if defined CYGWIN
|
||||||
|
size_t getpagesize(void);
|
||||||
|
#else
|
||||||
|
int getpagesize(void);
|
||||||
|
#endif
|
||||||
|
unsigned sleep(unsigned);
|
||||||
|
//#ifdef SOLARIS
|
||||||
|
//int usleep(useconds_t);
|
||||||
|
//#else
|
||||||
|
//void usleep(unsigned int); // rcgood - to avoid spinning processes.
|
||||||
|
//#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef NO_MPROT
|
||||||
|
|
||||||
|
#ifdef OSF
|
||||||
|
#define OSF_OR_AIX
|
||||||
|
#endif
|
||||||
|
#ifdef AIX
|
||||||
|
#define OSF_OR_AIX
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef OSF_OR_AIX
|
||||||
|
int mprotect(const void *, long unsigned int, int);
|
||||||
|
#else
|
||||||
|
int mprotect(char *, unsigned int, int);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(BSD) || defined(SOLARIS) || defined(LINUX)
|
||||||
|
//KMS
|
||||||
|
// added Solaris and LINUX
|
||||||
|
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
|
||||||
|
|
||||||
|
int socket(int, int, int);
|
||||||
|
|
||||||
|
#if defined(SUNOS) || defined(ULTRIX)
|
||||||
|
long tell(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
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// CallOnUserAbort
|
||||||
|
// Arrange that "func" will be called when the user aborts (e.g., by
|
||||||
|
// hitting ctl-C.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
CallOnUserAbort(void (*func)(int))
|
||||||
|
{
|
||||||
|
(void)signal(SIGINT, func);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Delay
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// UDelay
|
||||||
|
// Put the UNIX process running Nachos to sleep for x microseconds,
|
||||||
|
// to prevent an idle Nachos process from spinning...
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
UDelay(unsigned int useconds)
|
||||||
|
{
|
||||||
|
//#ifdef SOLARIS
|
||||||
|
// usleep(useconds_t useconds);
|
||||||
|
//#else
|
||||||
|
// usleep(useconds);
|
||||||
|
//#endif /* SOLARIS */
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Abort
|
||||||
|
// Quit and drop core.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
Abort()
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// RandomNumber
|
||||||
|
// Return a pseudo-random number.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
unsigned int
|
||||||
|
RandomNumber()
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
#ifdef NO_MPROT
|
||||||
|
return new char[size];
|
||||||
|
#else
|
||||||
|
int pgSize = getpagesize();
|
||||||
|
char *ptr = new char[pgSize * 2 + size];
|
||||||
|
|
||||||
|
mprotect(ptr, pgSize, 0);
|
||||||
|
mprotect(ptr + pgSize + size, pgSize, 0);
|
||||||
|
return ptr + pgSize;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// 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)
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifdef NO_MPROT
|
||||||
|
void
|
||||||
|
DeallocBoundedArray(char *ptr, int /* size */)
|
||||||
|
{
|
||||||
|
delete [] ptr;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
// "fd" -- the file descriptor of the file to be polled
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
bool
|
||||||
|
PollFile(int fd)
|
||||||
|
{
|
||||||
|
#if defined(SOLARIS) || defined(LINUX)
|
||||||
|
// KMS
|
||||||
|
fd_set rfd,wfd,xfd;
|
||||||
|
#else
|
||||||
|
int rfd = (1 << fd), wfd = 0, xfd = 0;
|
||||||
|
#endif
|
||||||
|
int retVal;
|
||||||
|
struct timeval pollTime;
|
||||||
|
|
||||||
|
#if defined(SOLARIS) || defined(LINUX)
|
||||||
|
// KMS
|
||||||
|
FD_ZERO(&rfd);
|
||||||
|
FD_ZERO(&wfd);
|
||||||
|
FD_ZERO(&xfd);
|
||||||
|
FD_SET(fd,&rfd);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// don't wait if there are no characters on the file
|
||||||
|
pollTime.tv_sec = 0;
|
||||||
|
pollTime.tv_usec = 0;
|
||||||
|
|
||||||
|
// poll file or socket
|
||||||
|
#if defined(BSD)
|
||||||
|
retVal = select(32, (fd_set*)&rfd, (fd_set*)&wfd, (fd_set*)&xfd, &pollTime);
|
||||||
|
#elif defined(SOLARIS) || defined(LINUX)
|
||||||
|
// KMS
|
||||||
|
retVal = select(32, &rfd, &wfd, &xfd, &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(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(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, char *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, char *buffer, int nBytes)
|
||||||
|
{
|
||||||
|
return read(fd, buffer, nBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// WriteFile
|
||||||
|
// Write characters to an open file. Abort if write fails.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
WriteFile(int fd, char *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(BSD) || defined(SOLARIS) || defined(LINUX)
|
||||||
|
return lseek(fd,0,SEEK_CUR); // 386BSD doesn't have the tell() system call
|
||||||
|
// neither do Solaris and Linux -KMS
|
||||||
|
#else
|
||||||
|
return tell(fd);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Close
|
||||||
|
// Close a file. Abort on error.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
int
|
||||||
|
Close(int fd)
|
||||||
|
{
|
||||||
|
int retVal = close(fd);
|
||||||
|
ASSERT(retVal >= 0);
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Unlink
|
||||||
|
// Delete a file.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
bool
|
||||||
|
Unlink(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, 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(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(dbgNet, "Created socket " << socketName);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// DeAssignNameToSocket
|
||||||
|
// Delete the UNIX file name we assigned to our IPC port, on cleanup.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
void
|
||||||
|
DeAssignNameToSocket(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, char *buffer, int packetSize)
|
||||||
|
{
|
||||||
|
int retVal;
|
||||||
|
struct sockaddr_un uName;
|
||||||
|
#ifdef LINUX
|
||||||
|
socklen_t size = sizeof(uName);
|
||||||
|
#else
|
||||||
|
int size = sizeof(uName);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
retVal = recvfrom(sockID, buffer, packetSize, 0,
|
||||||
|
(struct sockaddr *) &uName, &size);
|
||||||
|
|
||||||
|
if (retVal != packetSize) {
|
||||||
|
perror("in recvfrom");
|
||||||
|
#if defined CYGWIN
|
||||||
|
cerr << "called with " << packetSize << ", got back " << retVal
|
||||||
|
<< ", and " << "\n";
|
||||||
|
#else
|
||||||
|
cerr << "called with " << packetSize << ", got back " << retVal
|
||||||
|
<< ", and " << "\n";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
ASSERT(retVal == packetSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// modified by KMS to add retry...
|
||||||
|
// SendToSocket
|
||||||
|
// Transmit a fixed size packet to another Nachos' IPC port.
|
||||||
|
// Try 10 times with a one second delay between attempts.
|
||||||
|
// This is useful, e.g., to give the other socket a chance
|
||||||
|
// to get set up.
|
||||||
|
// Terminate if we still fail after 10 tries.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
void
|
||||||
|
SendToSocket(int sockID, char *buffer, int packetSize, char *toName)
|
||||||
|
{
|
||||||
|
struct sockaddr_un uName;
|
||||||
|
int retVal;
|
||||||
|
int retryCount;
|
||||||
|
|
||||||
|
InitSocketName(&uName, toName);
|
||||||
|
|
||||||
|
for(retryCount=0;retryCount < 10;retryCount++) {
|
||||||
|
retVal = sendto(sockID, buffer, packetSize, 0,
|
||||||
|
(struct sockaddr *) &uName, sizeof(uName));
|
||||||
|
if (retVal == packetSize) return;
|
||||||
|
// if we did not succeed, we should see a negative
|
||||||
|
// return value indicating complete failure. If we
|
||||||
|
// don't, something fishy is going on...
|
||||||
|
ASSERT(retVal < 0);
|
||||||
|
// wait a second before trying again
|
||||||
|
Delay(1);
|
||||||
|
}
|
||||||
|
// At this point, we have failed many times
|
||||||
|
// The most common reason for this is that the target machine
|
||||||
|
// has halted and its socket no longer exists.
|
||||||
|
// We simply do nothing (drop the packet).
|
||||||
|
// This may mask other kinds of failures, but it is the
|
||||||
|
// right thing to do in the common case.
|
||||||
|
}
|
||||||
75
code/lib/sysdep.h
Executable file
75
code/lib/sysdep.h
Executable file
@@ -0,0 +1,75 @@
|
|||||||
|
// 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-1996 The Regents of the University of California.
|
||||||
|
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||||
|
// of liability and disclaimer of warranty provisions.
|
||||||
|
|
||||||
|
#ifndef SYSDEP_H
|
||||||
|
#define SYSDEP_H
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
#include <iostream>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
// Process control: abort, exit, and sleep
|
||||||
|
extern void Abort();
|
||||||
|
extern void Exit(int exitCode);
|
||||||
|
extern void Delay(int seconds);
|
||||||
|
extern void UDelay(unsigned int usec);// rcgood - to avoid spinners.
|
||||||
|
|
||||||
|
// Initialize system so that cleanUp routine is called when user hits ctl-C
|
||||||
|
extern void CallOnUserAbort(void (*cleanup)(int));
|
||||||
|
|
||||||
|
// Initialize the pseudo random number generator
|
||||||
|
extern void RandomInit(unsigned seed);
|
||||||
|
extern unsigned int RandomNumber();
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
// 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(char *name);
|
||||||
|
extern int OpenForReadWrite(char *name, bool crashOnError);
|
||||||
|
extern void Read(int fd, char *buffer, int nBytes);
|
||||||
|
extern int ReadPartial(int fd, char *buffer, int nBytes);
|
||||||
|
extern void WriteFile(int fd, char *buffer, int nBytes);
|
||||||
|
extern void Lseek(int fd, int offset, int whence);
|
||||||
|
extern int Tell(int fd);
|
||||||
|
extern int Close(int fd);
|
||||||
|
extern bool Unlink(char *name);
|
||||||
|
|
||||||
|
// Other C library routines that are used by Nachos.
|
||||||
|
// These are assumed to be portable, so we don't include a wrapper.
|
||||||
|
extern "C" {
|
||||||
|
int atoi(const char *str);
|
||||||
|
double atof(const char *str);
|
||||||
|
int abs(int i);
|
||||||
|
void bcopy(const void *s1, void *s2, size_t n);
|
||||||
|
void bzero(void *s, size_t n);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interprocess communication operations, for simulating the network
|
||||||
|
extern int OpenSocket();
|
||||||
|
extern void CloseSocket(int sockID);
|
||||||
|
extern void AssignNameToSocket(char *socketName, int sockID);
|
||||||
|
extern void DeAssignNameToSocket(char *socketName);
|
||||||
|
extern bool PollSocket(int sockID);
|
||||||
|
extern void ReadFromSocket(int sockID, char *buffer, int packetSize);
|
||||||
|
extern void SendToSocket(int sockID, char *buffer, int packetSize,char *toName);
|
||||||
|
|
||||||
|
#endif // SYSDEP_H
|
||||||
38
code/lib/utility.h
Executable file
38
code/lib/utility.h
Executable file
@@ -0,0 +1,38 @@
|
|||||||
|
// utility.h
|
||||||
|
// Miscellaneous useful definitions.
|
||||||
|
//
|
||||||
|
// Copyright (c) 1992-1996 The Regents of the University of California.
|
||||||
|
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||||
|
// of liability and disclaimer of warranty provisions.
|
||||||
|
|
||||||
|
#ifndef UTILITY_H
|
||||||
|
#define UTILITY_H
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
|
||||||
|
// Miscellaneous useful routines
|
||||||
|
|
||||||
|
#define NULL 0
|
||||||
|
#define TRUE true
|
||||||
|
#define FALSE false
|
||||||
|
// #define bool int // necessary on the Mac?
|
||||||
|
|
||||||
|
#define min(a,b) (((a) < (b)) ? (a) : (b))
|
||||||
|
#define max(a,b) (((a) > (b)) ? (a) : (b))
|
||||||
|
|
||||||
|
// Divide and either round up or down
|
||||||
|
#define divRoundDown(n,s) ((n) / (s))
|
||||||
|
#define divRoundUp(n,s) (((n) / (s)) + ((((n) % (s)) > 0) ? 1 : 0))
|
||||||
|
|
||||||
|
// This declares the type "VoidFunctionPtr" to be a "pointer to a
|
||||||
|
// function taking an arbitrary pointer argument and returning nothing". With
|
||||||
|
// such a function pointer (say it is "func"), we can call it like this:
|
||||||
|
//
|
||||||
|
// (*func) ("help!");
|
||||||
|
//
|
||||||
|
// This is used by Thread::Fork as well as a couple of other places.
|
||||||
|
|
||||||
|
typedef void (*VoidFunctionPtr)(void *arg);
|
||||||
|
typedef void (*VoidNoArgFunctionPtr)();
|
||||||
|
|
||||||
|
#endif // UTILITY_H
|
||||||
44
code/machine/callback.h
Executable file
44
code/machine/callback.h
Executable file
@@ -0,0 +1,44 @@
|
|||||||
|
// callback.h
|
||||||
|
// Data structure to allow an object to register a "callback".
|
||||||
|
// On an asynchronous operation, the call to start the operation
|
||||||
|
// returns immediately. When the operation completes, the called
|
||||||
|
// object must somehow notify the caller of the completion.
|
||||||
|
// In the general case, the called object doesn't know the type
|
||||||
|
// of the caller.
|
||||||
|
//
|
||||||
|
// We implement this using virtual functions in C++. An object
|
||||||
|
// that needs to register a callback is set up as a derived class of
|
||||||
|
// the abstract base class "CallbackObj". When we pass a
|
||||||
|
// pointer to the object to a lower level module, that module
|
||||||
|
// calls back via "obj->CallBack()", without knowing the
|
||||||
|
// type of the object being called back.
|
||||||
|
//
|
||||||
|
// Note that this isn't a general-purpose mechanism,
|
||||||
|
// because a class can only register a single callback.
|
||||||
|
//
|
||||||
|
// DO NOT CHANGE -- part of the machine emulation
|
||||||
|
//
|
||||||
|
// Copyright (c) 1992-1996 The Regents of the University of California.
|
||||||
|
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||||
|
// of liability and disclaimer of warranty provisions.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef CALLBACK_H
|
||||||
|
#define CALLBACK_H
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
|
||||||
|
// Abstract base class for objects that register callbacks
|
||||||
|
|
||||||
|
class CallBackObj {
|
||||||
|
public:
|
||||||
|
virtual void CallBack() = 0;
|
||||||
|
protected:
|
||||||
|
CallBackObj() {}; // to prevent anyone from creating
|
||||||
|
// an instance of this class. Only
|
||||||
|
// allow creation of instances of
|
||||||
|
// classes derived from this class.
|
||||||
|
virtual ~CallBackObj() {};
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
174
code/machine/console.cc
Executable file
174
code/machine/console.cc
Executable file
@@ -0,0 +1,174 @@
|
|||||||
|
// 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-1996 The Regents of the University of California.
|
||||||
|
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||||
|
// of liability and disclaimer of warranty provisions.
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
#include "console.h"
|
||||||
|
#include "main.h"
|
||||||
|
#include "stdio.h"
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// ConsoleInput::ConsoleInput
|
||||||
|
// Initialize the simulation of the input for a hardware console device.
|
||||||
|
//
|
||||||
|
// "readFile" -- UNIX file simulating the keyboard (NULL -> use stdin)
|
||||||
|
// "toCall" is the interrupt handler to call when a character arrives
|
||||||
|
// from the keyboard
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
ConsoleInput::ConsoleInput(char *readFile, CallBackObj *toCall)
|
||||||
|
{
|
||||||
|
if (readFile == NULL)
|
||||||
|
readFileNo = 0; // keyboard = stdin
|
||||||
|
else
|
||||||
|
readFileNo = OpenForReadWrite(readFile, TRUE); // should be read-only
|
||||||
|
|
||||||
|
// set up the stuff to emulate asynchronous interrupts
|
||||||
|
callWhenAvail = toCall;
|
||||||
|
incoming = EOF;
|
||||||
|
|
||||||
|
// start polling for incoming keystrokes
|
||||||
|
kernel->interrupt->Schedule(this, ConsoleTime, ConsoleReadInt);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// ConsoleInput::~ConsoleInput
|
||||||
|
// Clean up console input emulation
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
ConsoleInput::~ConsoleInput()
|
||||||
|
{
|
||||||
|
if (readFileNo != 0)
|
||||||
|
Close(readFileNo);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// ConsoleInput::CallBack()
|
||||||
|
// Simulator calls this when a character may be available to be
|
||||||
|
// read in from the simulated keyboard (eg, the user typed something).
|
||||||
|
//
|
||||||
|
// First check to make sure character is available.
|
||||||
|
// Then invoke the "callBack" registered by whoever wants the character.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
ConsoleInput::CallBack()
|
||||||
|
{
|
||||||
|
char c;
|
||||||
|
int readCount;
|
||||||
|
|
||||||
|
ASSERT(incoming == EOF);
|
||||||
|
if (!PollFile(readFileNo)) { // nothing to be read
|
||||||
|
// schedule the next time to poll for a packet
|
||||||
|
kernel->interrupt->Schedule(this, ConsoleTime, ConsoleReadInt);
|
||||||
|
} else {
|
||||||
|
// otherwise, try to read a character
|
||||||
|
readCount = ReadPartial(readFileNo, &c, sizeof(char));
|
||||||
|
if (readCount == 0) {
|
||||||
|
// this seems to happen at end of file, when the
|
||||||
|
// console input is a regular file
|
||||||
|
// don't schedule an interrupt, since there will never
|
||||||
|
// be any more input
|
||||||
|
// just do nothing....
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// save the character and notify the OS that
|
||||||
|
// it is available
|
||||||
|
ASSERT(readCount == sizeof(char));
|
||||||
|
incoming = c;
|
||||||
|
kernel->stats->numConsoleCharsRead++;
|
||||||
|
}
|
||||||
|
callWhenAvail->CallBack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// ConsoleInput::GetChar()
|
||||||
|
// Read a character from the input buffer, if there is any there.
|
||||||
|
// Either return the character, or EOF if none buffered.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
char
|
||||||
|
ConsoleInput::GetChar()
|
||||||
|
{
|
||||||
|
char ch = incoming;
|
||||||
|
|
||||||
|
if (incoming != EOF) { // schedule when next char will arrive
|
||||||
|
kernel->interrupt->Schedule(this, ConsoleTime, ConsoleReadInt);
|
||||||
|
}
|
||||||
|
incoming = EOF;
|
||||||
|
return ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// ConsoleOutput::ConsoleOutput
|
||||||
|
// Initialize the simulation of the output for a hardware console device.
|
||||||
|
//
|
||||||
|
// "writeFile" -- UNIX file simulating the display (NULL -> use stdout)
|
||||||
|
// "toCall" is the interrupt handler to call when a write to
|
||||||
|
// the display completes.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
ConsoleOutput::ConsoleOutput(char *writeFile, CallBackObj *toCall)
|
||||||
|
{
|
||||||
|
if (writeFile == NULL)
|
||||||
|
writeFileNo = 1; // display = stdout
|
||||||
|
else
|
||||||
|
writeFileNo = OpenForWrite(writeFile);
|
||||||
|
|
||||||
|
callWhenDone = toCall;
|
||||||
|
putBusy = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// ConsoleOutput::~ConsoleOutput
|
||||||
|
// Clean up console output emulation
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
ConsoleOutput::~ConsoleOutput()
|
||||||
|
{
|
||||||
|
if (writeFileNo != 1)
|
||||||
|
Close(writeFileNo);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// ConsoleOutput::CallBack()
|
||||||
|
// Simulator calls this when the next character can be output to the
|
||||||
|
// display.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
ConsoleOutput::CallBack()
|
||||||
|
{
|
||||||
|
putBusy = FALSE;
|
||||||
|
kernel->stats->numConsoleCharsWritten++;
|
||||||
|
callWhenDone->CallBack();
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// ConsoleOutput::PutChar()
|
||||||
|
// Write a character to the simulated display, schedule an interrupt
|
||||||
|
// to occur in the future, and return.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
ConsoleOutput::PutChar(char ch)
|
||||||
|
{
|
||||||
|
ASSERT(putBusy == FALSE);
|
||||||
|
WriteFile(writeFileNo, &ch, sizeof(char));
|
||||||
|
putBusy = TRUE;
|
||||||
|
kernel->interrupt->Schedule(this, ConsoleTime, ConsoleWriteInt);
|
||||||
|
}
|
||||||
|
|
||||||
90
code/machine/console.h
Executable file
90
code/machine/console.h
Executable file
@@ -0,0 +1,90 @@
|
|||||||
|
// 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.
|
||||||
|
//
|
||||||
|
// In either case, the serial line connecting the computer
|
||||||
|
// to the console has limited bandwidth (like a modem!), and so
|
||||||
|
// each character takes measurable time.
|
||||||
|
//
|
||||||
|
// The user of the device registers itself to be called "back" 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-1996 The Regents of the University of California.
|
||||||
|
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||||
|
// of liability and disclaimer of warranty provisions.
|
||||||
|
|
||||||
|
#ifndef CONSOLE_H
|
||||||
|
#define CONSOLE_H
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
#include "utility.h"
|
||||||
|
#include "callback.h"
|
||||||
|
|
||||||
|
// The following two classes define the input (and output) side of a
|
||||||
|
// hardware console device. Input (and output) to the device is simulated
|
||||||
|
// by reading (and writing) to the UNIX file "readFile" (and "writeFile").
|
||||||
|
//
|
||||||
|
// Since input (and output) to the device is asynchronous, the interrupt
|
||||||
|
// handler "callWhenAvail" is called when a character has arrived to be
|
||||||
|
// read in (and "callWhenDone" is called when an output character has been
|
||||||
|
// "put" so that the next character can be written).
|
||||||
|
//
|
||||||
|
// In practice, usually a single hardware thing that does both
|
||||||
|
// serial input and serial output. But conceptually simpler to
|
||||||
|
// use two objects.
|
||||||
|
|
||||||
|
class ConsoleInput : public CallBackObj {
|
||||||
|
public:
|
||||||
|
ConsoleInput(char *readFile, CallBackObj *toCall);
|
||||||
|
// initialize hardware console input
|
||||||
|
~ConsoleInput(); // clean up console emulation
|
||||||
|
|
||||||
|
char GetChar(); // Poll the console input. If a char is
|
||||||
|
// available, return it. Otherwise, return EOF.
|
||||||
|
// "callWhenAvail" is called whenever there is
|
||||||
|
// a char to be gotten
|
||||||
|
|
||||||
|
void CallBack(); // Invoked when a character arrives
|
||||||
|
// from the keyboard.
|
||||||
|
|
||||||
|
private:
|
||||||
|
int readFileNo; // UNIX file emulating the keyboard
|
||||||
|
CallBackObj *callWhenAvail; // Interrupt handler to call when
|
||||||
|
// there is a char to be read
|
||||||
|
char incoming; // Contains the character to be read,
|
||||||
|
// if there is one available.
|
||||||
|
// Otherwise contains EOF.
|
||||||
|
};
|
||||||
|
|
||||||
|
class ConsoleOutput : public CallBackObj {
|
||||||
|
public:
|
||||||
|
ConsoleOutput(char *writeFile, CallBackObj *toCall);
|
||||||
|
// initialize hardware console output
|
||||||
|
~ConsoleOutput(); // clean up console emulation
|
||||||
|
|
||||||
|
void PutChar(char ch); // Write "ch" to the console display,
|
||||||
|
// and return immediately. "callWhenDone"
|
||||||
|
// will called when the I/O completes.
|
||||||
|
void CallBack(); // Invoked when next character can be put
|
||||||
|
// out to the display.
|
||||||
|
|
||||||
|
private:
|
||||||
|
int writeFileNo; // UNIX file emulating the display
|
||||||
|
CallBackObj *callWhenDone; // Interrupt handler to call when
|
||||||
|
// the next char can be put
|
||||||
|
bool putBusy; // Is a PutChar operation in progress?
|
||||||
|
// If so, you can't do another one!
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CONSOLE_H
|
||||||
268
code/machine/disk.cc
Executable file
268
code/machine/disk.cc
Executable file
@@ -0,0 +1,268 @@
|
|||||||
|
// 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 "debug.h"
|
||||||
|
#include "sysdep.h"
|
||||||
|
#include "main.h"
|
||||||
|
|
||||||
|
// We put a magic number 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).
|
||||||
|
|
||||||
|
const int MagicNumber = 0x456789ab;
|
||||||
|
const int MagicSize = sizeof(int);
|
||||||
|
const int DiskSize = (MagicSize + (NumSectors * SectorSize));
|
||||||
|
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
// "toCall" -- object to call when disk read/write request completes
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
Disk::Disk(CallBackObj *toCall)
|
||||||
|
{
|
||||||
|
int magicNum;
|
||||||
|
int tmp = 0;
|
||||||
|
|
||||||
|
DEBUG(dbgDisk, "Initializing the disk.");
|
||||||
|
callWhenDone = toCall;
|
||||||
|
lastSector = 0;
|
||||||
|
bufferInit = 0;
|
||||||
|
|
||||||
|
sprintf(diskname,"DISK_%d",kernel->hostName);
|
||||||
|
fileno = OpenForReadWrite(diskname, FALSE);
|
||||||
|
if (fileno >= 0) { // file exists, check magic number
|
||||||
|
Read(fileno, (char *) &magicNum, MagicSize);
|
||||||
|
ASSERT(magicNum == MagicNumber);
|
||||||
|
} else { // file doesn't exist, create it
|
||||||
|
fileno = OpenForWrite(diskname);
|
||||||
|
magicNum = MagicNumber;
|
||||||
|
WriteFile(fileno, (char *) &magicNum, MagicSize); // write magic number
|
||||||
|
|
||||||
|
// need to write at end of file, so that reads will not return EOF
|
||||||
|
Lseek(fileno, DiskSize - sizeof(int), 0);
|
||||||
|
WriteFile(fileno, (char *)&tmp, sizeof(int));
|
||||||
|
}
|
||||||
|
active = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Disk::~Disk()
|
||||||
|
// Clean up disk simulation, by closing the UNIX file representing the
|
||||||
|
// disk.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
Disk::~Disk()
|
||||||
|
{
|
||||||
|
Close(fileno);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Disk::PrintSector()
|
||||||
|
// Dump the data in a disk read/write request, for debugging.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
static void
|
||||||
|
PrintSector (bool writing, int sector, char *data)
|
||||||
|
{
|
||||||
|
int *p = (int *) data;
|
||||||
|
|
||||||
|
if (writing)
|
||||||
|
cout << "Writing sector: " << sector << "\n";
|
||||||
|
else
|
||||||
|
cout << "Reading sector: " << sector << "\n";
|
||||||
|
for (unsigned int i = 0; i < (SectorSize/sizeof(int)); i++) {
|
||||||
|
cout << p[i] << " ";
|
||||||
|
}
|
||||||
|
cout << "\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, char* data)
|
||||||
|
{
|
||||||
|
int ticks = ComputeLatency(sectorNumber, FALSE);
|
||||||
|
|
||||||
|
ASSERT(!active); // only one request at a time
|
||||||
|
ASSERT((sectorNumber >= 0) && (sectorNumber < NumSectors));
|
||||||
|
|
||||||
|
DEBUG(dbgDisk, "Reading from sector " << sectorNumber);
|
||||||
|
Lseek(fileno, SectorSize * sectorNumber + MagicSize, 0);
|
||||||
|
Read(fileno, data, SectorSize);
|
||||||
|
if (debug->IsEnabled('d'))
|
||||||
|
PrintSector(FALSE, sectorNumber, data);
|
||||||
|
|
||||||
|
active = TRUE;
|
||||||
|
UpdateLast(sectorNumber);
|
||||||
|
kernel->stats->numDiskReads++;
|
||||||
|
kernel->interrupt->Schedule(this, ticks, DiskInt);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Disk::WriteRequest(int sectorNumber, char* data)
|
||||||
|
{
|
||||||
|
int ticks = ComputeLatency(sectorNumber, TRUE);
|
||||||
|
|
||||||
|
ASSERT(!active);
|
||||||
|
ASSERT((sectorNumber >= 0) && (sectorNumber < NumSectors));
|
||||||
|
|
||||||
|
DEBUG(dbgDisk, "Writing to sector " << sectorNumber);
|
||||||
|
Lseek(fileno, SectorSize * sectorNumber + MagicSize, 0);
|
||||||
|
WriteFile(fileno, data, SectorSize);
|
||||||
|
if (debug->IsEnabled('d'))
|
||||||
|
PrintSector(TRUE, sectorNumber, data);
|
||||||
|
|
||||||
|
active = TRUE;
|
||||||
|
UpdateLast(sectorNumber);
|
||||||
|
kernel->stats->numDiskWrites++;
|
||||||
|
kernel->interrupt->Schedule(this, ticks, DiskInt);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Disk::CallBack()
|
||||||
|
// Called by the machine simulation when the disk interrupt occurs.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
Disk::CallBack ()
|
||||||
|
{
|
||||||
|
active = FALSE;
|
||||||
|
callWhenDone->CallBack();
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// 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 = (kernel->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 = kernel->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(dbgDisk, "Request latency = " << RotationTime);
|
||||||
|
return RotationTime; // time to transfer sector from the track buffer
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
rotation += ModuloDiff(newSector, timeAfter / RotationTime) * RotationTime;
|
||||||
|
|
||||||
|
DEBUG(dbgDisk, "Request latency = " << (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 = kernel->stats->totalTicks + seek + rotate;
|
||||||
|
lastSector = newSector;
|
||||||
|
DEBUG(dbgDisk, "Updating last sector = " << lastSector << " , " << bufferInit);
|
||||||
|
}
|
||||||
92
code/machine/disk.h
Executable file
92
code/machine/disk.h
Executable file
@@ -0,0 +1,92 @@
|
|||||||
|
// 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"
|
||||||
|
#include "callback.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
|
||||||
|
|
||||||
|
const int SectorSize = 128; // number of bytes per disk sector
|
||||||
|
const int SectorsPerTrack = 32; // number of sectors per disk track
|
||||||
|
const int NumTracks = 32; // number of tracks per disk
|
||||||
|
const int NumSectors = (SectorsPerTrack * NumTracks);
|
||||||
|
// total # of sectors per disk
|
||||||
|
|
||||||
|
class Disk : public CallBackObj {
|
||||||
|
public:
|
||||||
|
Disk(CallBackObj *toCall); // Create a simulated disk.
|
||||||
|
// Invoke toCall->CallBack()
|
||||||
|
// when each request completes.
|
||||||
|
~Disk(); // Deallocate the disk.
|
||||||
|
|
||||||
|
void ReadRequest(int sectorNumber, char* 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, char* data);
|
||||||
|
|
||||||
|
void CallBack(); // Invoked when disk request
|
||||||
|
// finishes. In turn calls, callWhenDone.
|
||||||
|
|
||||||
|
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
|
||||||
|
char diskname[32]; // name of simulated disk's file
|
||||||
|
CallBackObj *callWhenDone; // Invoke when any disk request finishes
|
||||||
|
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
|
||||||
361
code/machine/interrupt.cc
Executable file
361
code/machine/interrupt.cc
Executable file
@@ -0,0 +1,361 @@
|
|||||||
|
// 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-1996 The Regents of the University of California.
|
||||||
|
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||||
|
// of liability and disclaimer of warranty provisions.
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
#include "interrupt.h"
|
||||||
|
#include "main.h"
|
||||||
|
|
||||||
|
// String definitions for debugging messages
|
||||||
|
|
||||||
|
static char *intLevelNames[] = { "off", "on"};
|
||||||
|
static 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.
|
||||||
|
//
|
||||||
|
// "callOnInt" is the object to call when the interrupt occurs
|
||||||
|
// "time" is when (in simulated time) the interrupt is to occur
|
||||||
|
// "kind" is the hardware device that generated the interrupt
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
PendingInterrupt::PendingInterrupt(CallBackObj *callOnInt,
|
||||||
|
int time, IntType kind)
|
||||||
|
{
|
||||||
|
callOnInterrupt = callOnInt;
|
||||||
|
when = time;
|
||||||
|
type = kind;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// PendingCompare
|
||||||
|
// Compare to interrupts based on which should occur first.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
static int
|
||||||
|
PendingCompare (PendingInterrupt *x, PendingInterrupt *y)
|
||||||
|
{
|
||||||
|
if (x->when < y->when) { return -1; }
|
||||||
|
else if (x->when > y->when) { return 1; }
|
||||||
|
else { return 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Interrupt::Interrupt
|
||||||
|
// Initialize the simulation of hardware device interrupts.
|
||||||
|
//
|
||||||
|
// Interrupts start disabled, with no interrupts pending, etc.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
Interrupt::Interrupt()
|
||||||
|
{
|
||||||
|
level = IntOff;
|
||||||
|
pending = new SortedList<PendingInterrupt *>(PendingCompare);
|
||||||
|
inHandler = FALSE;
|
||||||
|
yieldOnReturn = FALSE;
|
||||||
|
status = SystemMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Interrupt::~Interrupt
|
||||||
|
// De-allocate the data structures needed by the interrupt simulation.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
Interrupt::~Interrupt()
|
||||||
|
{
|
||||||
|
while (!pending->IsEmpty()) {
|
||||||
|
delete pending->RemoveFront();
|
||||||
|
}
|
||||||
|
delete pending;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// 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)
|
||||||
|
{
|
||||||
|
level = now;
|
||||||
|
DEBUG(dbgInt, "\tinterrupts: " << intLevelNames[old] << " -> " << intLevelNames[now]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
// interrupt handlers are prohibited from enabling interrupts
|
||||||
|
ASSERT((now == IntOff) || (inHandler == FALSE));
|
||||||
|
|
||||||
|
ChangeLevel(old, now); // change to new state
|
||||||
|
if ((now == IntOn) && (old == IntOff)) {
|
||||||
|
OneTick(); // advance simulated time
|
||||||
|
}
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// 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 oldStatus = status;
|
||||||
|
Statistics *stats = kernel->stats;
|
||||||
|
|
||||||
|
// advance simulated time
|
||||||
|
if (status == SystemMode) {
|
||||||
|
stats->totalTicks += SystemTick;
|
||||||
|
stats->systemTicks += SystemTick;
|
||||||
|
} else {
|
||||||
|
stats->totalTicks += UserTick;
|
||||||
|
stats->userTicks += UserTick;
|
||||||
|
}
|
||||||
|
DEBUG(dbgInt, "== Tick " << stats->totalTicks << " ==");
|
||||||
|
|
||||||
|
// check any pending interrupts are now ready to fire
|
||||||
|
ChangeLevel(IntOn, IntOff); // first, turn off interrupts
|
||||||
|
// (interrupt handlers run with
|
||||||
|
// interrupts disabled)
|
||||||
|
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
|
||||||
|
kernel->currentThread->Yield();
|
||||||
|
status = oldStatus;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// 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(dbgInt, "Machine idling; checking for interrupts.");
|
||||||
|
status = IdleMode;
|
||||||
|
if (CheckIfDue(TRUE)) { // check for any pending interrupts
|
||||||
|
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(dbgInt, "Machine idle. No interrupts to do.");
|
||||||
|
cout << "No threads ready or runnable, and no pending interrupts.\n";
|
||||||
|
cout << "Assuming the program completed.\n";
|
||||||
|
Halt();
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Interrupt::Halt
|
||||||
|
// Shut down Nachos cleanly, printing out performance statistics.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
void
|
||||||
|
Interrupt::Halt()
|
||||||
|
{
|
||||||
|
cout << "Machine halting!\n\n";
|
||||||
|
cout << "This is halt\n";
|
||||||
|
kernel->stats->Print();
|
||||||
|
delete kernel; // Never returns.
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
Interrupt::CreateFile(char *filename)
|
||||||
|
{
|
||||||
|
return kernel->CreateFile(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
// "toCall" is the object to call when the interrupt occurs
|
||||||
|
// "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(CallBackObj *toCall, int fromNow, IntType type)
|
||||||
|
{
|
||||||
|
int when = kernel->stats->totalTicks + fromNow;
|
||||||
|
PendingInterrupt *toOccur = new PendingInterrupt(toCall, when, type);
|
||||||
|
|
||||||
|
DEBUG(dbgInt, "Scheduling interrupt handler the " << intTypeNames[type] << " at time = " << when);
|
||||||
|
ASSERT(fromNow > 0);
|
||||||
|
|
||||||
|
pending->Insert(toOccur);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Interrupt::CheckIfDue
|
||||||
|
// Check if any interrupts are scheduled to occur, and if so,
|
||||||
|
// fire them 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).
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
bool
|
||||||
|
Interrupt::CheckIfDue(bool advanceClock)
|
||||||
|
{
|
||||||
|
PendingInterrupt *next;
|
||||||
|
Statistics *stats = kernel->stats;
|
||||||
|
|
||||||
|
ASSERT(level == IntOff); // interrupts need to be disabled,
|
||||||
|
// to invoke an interrupt handler
|
||||||
|
if (debug->IsEnabled(dbgInt)) {
|
||||||
|
DumpState();
|
||||||
|
}
|
||||||
|
if (pending->IsEmpty()) { // no pending interrupts
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
next = pending->Front();
|
||||||
|
|
||||||
|
if (next->when > stats->totalTicks) {
|
||||||
|
if (!advanceClock) { // not time yet
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
else { // advance the clock to next interrupt
|
||||||
|
stats->idleTicks += (next->when - stats->totalTicks);
|
||||||
|
stats->totalTicks = next->when;
|
||||||
|
// UDelay(1000L); // rcgood - to stop nachos from spinning.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG(dbgInt, "Invoking interrupt handler for the ");
|
||||||
|
DEBUG(dbgInt, intTypeNames[next->type] << " at time " << next->when);
|
||||||
|
|
||||||
|
if (kernel->machine != NULL) {
|
||||||
|
kernel->machine->DelayedLoad(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
inHandler = TRUE;
|
||||||
|
do {
|
||||||
|
next = pending->RemoveFront(); // pull interrupt off list
|
||||||
|
next->callOnInterrupt->CallBack();// call the interrupt handler
|
||||||
|
delete next;
|
||||||
|
} while (!pending->IsEmpty()
|
||||||
|
&& (pending->Front()->when <= stats->totalTicks));
|
||||||
|
inHandler = FALSE;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// PrintPending
|
||||||
|
// Print information about an interrupt that is scheduled to occur.
|
||||||
|
// When, where, why, etc.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
static void
|
||||||
|
PrintPending (PendingInterrupt *pending)
|
||||||
|
{
|
||||||
|
cout << "Interrupt handler "<< intTypeNames[pending->type];
|
||||||
|
cout << ", scheduled at " << pending->when;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// DumpState
|
||||||
|
// Print the complete interrupt state - the status, and all interrupts
|
||||||
|
// that are scheduled to occur in the future.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
Interrupt::DumpState()
|
||||||
|
{
|
||||||
|
cout << "Time: " << kernel->stats->totalTicks;
|
||||||
|
cout << ", interrupts " << intLevelNames[level] << "\n";
|
||||||
|
cout << "Pending interrupts:\n";
|
||||||
|
pending->Apply(PrintPending);
|
||||||
|
cout << "\nEnd of pending interrupts\n";
|
||||||
|
}
|
||||||
|
|
||||||
145
code/machine/interrupt.h
Executable file
145
code/machine/interrupt.h
Executable file
@@ -0,0 +1,145 @@
|
|||||||
|
// 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.
|
||||||
|
//
|
||||||
|
// DO NOT CHANGE -- part of the machine emulation
|
||||||
|
//
|
||||||
|
// Copyright (c) 1992-1996 The Regents of the University of California.
|
||||||
|
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||||
|
// of liability and disclaimer of warranty provisions.
|
||||||
|
|
||||||
|
#ifndef INTERRUPT_H
|
||||||
|
#define INTERRUPT_H
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
#include "list.h"
|
||||||
|
#include "callback.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(CallBackObj *callOnInt, int time, IntType kind);
|
||||||
|
// initialize an interrupt that will
|
||||||
|
// occur in the future
|
||||||
|
|
||||||
|
CallBackObj *callOnInterrupt;// The object (in the hardware device
|
||||||
|
// emulator) to call when the interrupt occurs
|
||||||
|
|
||||||
|
int 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:
|
||||||
|
Interrupt(); // initialize the interrupt simulation
|
||||||
|
~Interrupt(); // de-allocate data structures
|
||||||
|
|
||||||
|
IntStatus SetLevel(IntStatus level);
|
||||||
|
// Disable or enable interrupts
|
||||||
|
// and return previous setting.
|
||||||
|
|
||||||
|
void Enable() { (void) SetLevel(IntOn); }
|
||||||
|
// 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 Halt(); // quit and print out stats
|
||||||
|
|
||||||
|
void PrintInt(int number);
|
||||||
|
int CreateFile(char *filename);
|
||||||
|
|
||||||
|
void YieldOnReturn(); // cause a context switch on return
|
||||||
|
// from an interrupt handler
|
||||||
|
|
||||||
|
MachineStatus getStatus() { return status; }
|
||||||
|
void setStatus(MachineStatus st) { status = st; }
|
||||||
|
// idle, kernel, user
|
||||||
|
|
||||||
|
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(CallBackObj *callTo, int when, IntType type);
|
||||||
|
// Schedule an interrupt to occur
|
||||||
|
// at time "when". This is called
|
||||||
|
// by the hardware device simulators.
|
||||||
|
|
||||||
|
void OneTick(); // Advance simulated time
|
||||||
|
|
||||||
|
private:
|
||||||
|
IntStatus level; // are interrupts enabled or disabled?
|
||||||
|
SortedList<PendingInterrupt *> *pending;
|
||||||
|
// the list of interrupts scheduled
|
||||||
|
// to occur in the future
|
||||||
|
//int writeFileNo; //UNIX file emulating the display
|
||||||
|
bool inHandler; // TRUE if we are running an interrupt handler
|
||||||
|
//bool putBusy; // Is a PrintInt operation in progress
|
||||||
|
//If so, you cannoot do another one
|
||||||
|
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 any interrupts are supposed
|
||||||
|
// to occur now, and if so, do them
|
||||||
|
|
||||||
|
void ChangeLevel(IntStatus old, // SetLevel, without advancing the
|
||||||
|
IntStatus now); // simulated time
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // INTERRRUPT_H
|
||||||
224
code/machine/machine.cc
Executable file
224
code/machine/machine.cc
Executable file
@@ -0,0 +1,224 @@
|
|||||||
|
// machine.cc
|
||||||
|
// Routines for simulating the execution of user programs.
|
||||||
|
//
|
||||||
|
// DO NOT CHANGE -- part of the machine emulation
|
||||||
|
//
|
||||||
|
// Copyright (c) 1992-1996 The Regents of the University of California.
|
||||||
|
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||||
|
// of liability and disclaimer of warranty provisions.
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
#include "machine.h"
|
||||||
|
#include "main.h"
|
||||||
|
|
||||||
|
// Textual names of the exceptions that can be generated by user program
|
||||||
|
// execution, for debugging.
|
||||||
|
static char* exceptionNames[] = { "no exception", "syscall",
|
||||||
|
"page fault/no TLB entry", "page read only",
|
||||||
|
"bus error", "address error", "overflow",
|
||||||
|
"illegal instruction" };
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// CheckEndian
|
||||||
|
// Check to be sure that the host really uses the format it says it
|
||||||
|
// does, for storing the bytes of an integer. Stop on error.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
static
|
||||||
|
void CheckEndian()
|
||||||
|
{
|
||||||
|
union checkit {
|
||||||
|
char charword[4];
|
||||||
|
unsigned int intword;
|
||||||
|
} check;
|
||||||
|
|
||||||
|
check.charword[0] = 1;
|
||||||
|
check.charword[1] = 2;
|
||||||
|
check.charword[2] = 3;
|
||||||
|
check.charword[3] = 4;
|
||||||
|
|
||||||
|
#ifdef HOST_IS_BIG_ENDIAN
|
||||||
|
ASSERT (check.intword == 0x01020304);
|
||||||
|
#else
|
||||||
|
ASSERT (check.intword == 0x04030201);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Machine::Machine
|
||||||
|
// Initialize the simulation of user program execution.
|
||||||
|
//
|
||||||
|
// "debug" -- if TRUE, drop into the debugger after each user instruction
|
||||||
|
// is executed.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
Machine::Machine(bool debug)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < NumTotalRegs; i++)
|
||||||
|
registers[i] = 0;
|
||||||
|
mainMemory = new char[MemorySize];
|
||||||
|
for (i = 0; i < MemorySize; i++)
|
||||||
|
mainMemory[i] = 0;
|
||||||
|
#ifdef USE_TLB
|
||||||
|
tlb = new TranslationEntry[TLBSize];
|
||||||
|
for (i = 0; i < TLBSize; i++)
|
||||||
|
tlb[i].valid = FALSE;
|
||||||
|
pageTable = NULL;
|
||||||
|
#else // use linear page table
|
||||||
|
tlb = NULL;
|
||||||
|
pageTable = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
singleStep = debug;
|
||||||
|
CheckEndian();
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Machine::~Machine
|
||||||
|
// De-allocate the data structures used to simulate user program execution.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
Machine::~Machine()
|
||||||
|
{
|
||||||
|
delete [] mainMemory;
|
||||||
|
if (tlb != NULL)
|
||||||
|
delete [] tlb;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Machine::RaiseException
|
||||||
|
// Transfer control to the Nachos kernel from user mode, because
|
||||||
|
// the user program either invoked a system call, or some exception
|
||||||
|
// occured (such as the address translation failed).
|
||||||
|
//
|
||||||
|
// "which" -- the cause of the kernel trap
|
||||||
|
// "badVaddr" -- the virtual address causing the trap, if appropriate
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
Machine::RaiseException(ExceptionType which, int badVAddr)
|
||||||
|
{
|
||||||
|
DEBUG(dbgMach, "Exception: " << exceptionNames[which]);
|
||||||
|
registers[BadVAddrReg] = badVAddr;
|
||||||
|
DelayedLoad(0, 0); // finish anything in progress
|
||||||
|
kernel->interrupt->setStatus(SystemMode);
|
||||||
|
ExceptionHandler(which); // interrupts are enabled at this point
|
||||||
|
kernel->interrupt->setStatus(UserMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Machine::Debugger
|
||||||
|
// Primitive debugger for user programs. Note that we can't use
|
||||||
|
// gdb to debug user programs, since gdb doesn't run on top of Nachos.
|
||||||
|
// It could, but you'd have to implement *a lot* more system calls
|
||||||
|
// to get it to work!
|
||||||
|
//
|
||||||
|
// So just allow single-stepping, and printing the contents of memory.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void Machine::Debugger()
|
||||||
|
{
|
||||||
|
char *buf = new char[80];
|
||||||
|
int num;
|
||||||
|
bool done = FALSE;
|
||||||
|
|
||||||
|
kernel->interrupt->DumpState();
|
||||||
|
DumpState();
|
||||||
|
while (!done) {
|
||||||
|
// read commands until we should proceed with more execution
|
||||||
|
// prompt for input, giving current simulation time in the prompt
|
||||||
|
cout << kernel->stats->totalTicks << ">";
|
||||||
|
// read one line of input (80 chars max)
|
||||||
|
cin.get(buf, 80);
|
||||||
|
if (sscanf(buf, "%d", &num) == 1) {
|
||||||
|
runUntilTime = num;
|
||||||
|
done = TRUE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
runUntilTime = 0;
|
||||||
|
switch (*buf) {
|
||||||
|
case '\0':
|
||||||
|
done = TRUE;
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
singleStep = FALSE;
|
||||||
|
done = TRUE;
|
||||||
|
break;
|
||||||
|
case '?':
|
||||||
|
cout << "Machine commands:\n";
|
||||||
|
cout << " <return> execute one instruction\n";
|
||||||
|
cout << " <number> run until the given timer tick\n";
|
||||||
|
cout << " c run until completion\n";
|
||||||
|
cout << " ? print help message\n";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
cout << "Unknown command: " << buf << "\n";
|
||||||
|
cout << "Type ? for help.\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// consume the newline delimiter, which does not get
|
||||||
|
// eaten by cin.get(buf,80) above.
|
||||||
|
buf[0] = cin.get();
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
|
||||||
|
cout << "Machine registers:\n";
|
||||||
|
for (i = 0; i < NumGPRegs; i++) {
|
||||||
|
switch (i) {
|
||||||
|
case StackReg:
|
||||||
|
cout << "\tSP(" << i << "):\t" << registers[i];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RetAddrReg:
|
||||||
|
cout << "\tRA(" << i << "):\t" << registers[i];
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
cout << "\t" << i << ":\t" << registers[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ((i % 4) == 3) { cout << "\n"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
cout << "\tHi:\t" << registers[HiReg];
|
||||||
|
cout << "\tLo:\t" << registers[LoReg];
|
||||||
|
cout << "\tPC:\t" << registers[PCReg];
|
||||||
|
cout << "\tNextPC:\t" << registers[NextPCReg];
|
||||||
|
cout << "\tPrevPC:\t" << registers[PrevPCReg];
|
||||||
|
cout << "\tLoad:\t" << registers[LoadReg];
|
||||||
|
cout << "\tLoadV:\t" << registers[LoadValueReg] << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Machine::ReadRegister/WriteRegister
|
||||||
|
// Fetch or write the contents of a user program register.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
int
|
||||||
|
Machine::ReadRegister(int num)
|
||||||
|
{
|
||||||
|
ASSERT((num >= 0) && (num < NumTotalRegs));
|
||||||
|
return registers[num];
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Machine::WriteRegister(int num, int value)
|
||||||
|
{
|
||||||
|
ASSERT((num >= 0) && (num < NumTotalRegs));
|
||||||
|
registers[num] = value;
|
||||||
|
}
|
||||||
|
|
||||||
206
code/machine/machine.h
Executable file
206
code/machine/machine.h
Executable file
@@ -0,0 +1,206 @@
|
|||||||
|
// 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 EXCEPT AS NOTED BELOW -- part of the machine emulation
|
||||||
|
//
|
||||||
|
// Copyright (c) 1992-1996 The Regents of the University of California.
|
||||||
|
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||||
|
// of liability and disclaimer of warranty provisions.
|
||||||
|
|
||||||
|
#ifndef MACHINE_H
|
||||||
|
#define MACHINE_H
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
#include "utility.h"
|
||||||
|
#include "translate.h"
|
||||||
|
|
||||||
|
// Definitions related to the size, and format of user memory
|
||||||
|
|
||||||
|
const int PageSize = 128; // set the page size equal to
|
||||||
|
// the disk sector size, for simplicity
|
||||||
|
|
||||||
|
//
|
||||||
|
// You are allowed to change this value.
|
||||||
|
// Doing so will change the number of pages of physical memory
|
||||||
|
// available on the simulated machine.
|
||||||
|
//
|
||||||
|
const int NumPhysPages = 128;
|
||||||
|
|
||||||
|
const int MemorySize = (NumPhysPages * PageSize);
|
||||||
|
const int 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 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 Instruction;
|
||||||
|
class Interrupt;
|
||||||
|
|
||||||
|
class Machine {
|
||||||
|
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
|
||||||
|
|
||||||
|
// Data structures accessible to the Nachos kernel -- main memory and the
|
||||||
|
// page table/TLB.
|
||||||
|
//
|
||||||
|
// Note that *all* communication between the user program and the kernel
|
||||||
|
// are in terms of these data structures (plus the CPU registers).
|
||||||
|
|
||||||
|
char *mainMemory; // physical memory to store user program,
|
||||||
|
// code and data, while executing
|
||||||
|
|
||||||
|
// 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 *pageTable;
|
||||||
|
unsigned int pageTableSize;
|
||||||
|
|
||||||
|
bool ReadMem(int addr, int size, int* value);
|
||||||
|
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.
|
||||||
|
private:
|
||||||
|
|
||||||
|
// Routines internal to the machine simulation -- DO NOT call these directly
|
||||||
|
void DelayedLoad(int nextReg, int nextVal);
|
||||||
|
// Do a pending delayed load (modifying a reg)
|
||||||
|
|
||||||
|
void OneInstruction(Instruction *instr);
|
||||||
|
// Run one instruction of a user program.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
ExceptionType Translate(int virtAddr, int* physAddr, int size,bool writing);
|
||||||
|
// 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
|
||||||
|
|
||||||
|
|
||||||
|
// Internal data structures
|
||||||
|
|
||||||
|
int registers[NumTotalRegs]; // CPU registers, for executing user programs
|
||||||
|
|
||||||
|
bool singleStep; // drop back into the debugger after each
|
||||||
|
// simulated instruction
|
||||||
|
int runUntilTime; // drop back into the debugger when simulated
|
||||||
|
// time reaches this value
|
||||||
|
|
||||||
|
friend class Interrupt; // calls DelayedLoad()
|
||||||
|
};
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
#endif // MACHINE_H
|
||||||
828
code/machine/mipssim.cc
Executable file
828
code/machine/mipssim.cc
Executable file
@@ -0,0 +1,828 @@
|
|||||||
|
// mipssim.cc -- simulate a MIPS R2/3000 processor
|
||||||
|
//
|
||||||
|
// This code has been adapted from Ousterhout's MIPSSIM package.
|
||||||
|
// Byte ordering is little-endian, so we can be compatible with
|
||||||
|
// DEC RISC systems.
|
||||||
|
//
|
||||||
|
// DO NOT CHANGE -- part of the machine emulation
|
||||||
|
//
|
||||||
|
// Copyright (c) 1992-1996 The Regents of the University of California.
|
||||||
|
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||||
|
// of liability and disclaimer of warranty provisions.
|
||||||
|
|
||||||
|
// Simulation fixes done by Peter E Reissner, class of Winter 1994/95 (York)
|
||||||
|
// I've not been able to test this extensively.
|
||||||
|
// Ported to newer version of Nachos at Waterloo by Scott Graham (Mar 99).
|
||||||
|
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
|
||||||
|
#include "debug.h"
|
||||||
|
#include "machine.h"
|
||||||
|
#include "mipssim.h"
|
||||||
|
#include "main.h"
|
||||||
|
|
||||||
|
static void Mult(int a, int b, bool signedArith, int* hiPtr, int* loPtr);
|
||||||
|
|
||||||
|
// The following class defines an instruction, represented in both
|
||||||
|
// undecoded binary form
|
||||||
|
// decoded to identify
|
||||||
|
// operation to do
|
||||||
|
// registers to act on
|
||||||
|
// any immediate operand value
|
||||||
|
|
||||||
|
class Instruction {
|
||||||
|
public:
|
||||||
|
void Decode(); // decode the binary representation of the instruction
|
||||||
|
|
||||||
|
unsigned int value; // binary representation of the instruction
|
||||||
|
|
||||||
|
char opCode; // Type of instruction. This is NOT the same as the
|
||||||
|
// opcode field from the instruction: see defs in mips.h
|
||||||
|
char rs, rt, rd; // Three registers from instruction.
|
||||||
|
int extra; // Immediate or target or shamt field or offset.
|
||||||
|
// Immediates are sign-extended.
|
||||||
|
};
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Machine::Run
|
||||||
|
// Simulate the execution of a user-level program on Nachos.
|
||||||
|
// Called by the kernel when the program starts up; never returns.
|
||||||
|
//
|
||||||
|
// This routine is re-entrant, in that it can be called multiple
|
||||||
|
// times concurrently -- one for each thread executing user code.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
Machine::Run()
|
||||||
|
{
|
||||||
|
Instruction *instr = new Instruction; // storage for decoded instruction
|
||||||
|
|
||||||
|
if (debug->IsEnabled('m')) {
|
||||||
|
cout << "Starting program in thread: " << kernel->currentThread->getName();
|
||||||
|
cout << ", at time: " << kernel->stats->totalTicks << "\n";
|
||||||
|
}
|
||||||
|
kernel->interrupt->setStatus(UserMode);
|
||||||
|
for (;;) {
|
||||||
|
OneInstruction(instr);
|
||||||
|
kernel->interrupt->OneTick();
|
||||||
|
if (singleStep && (runUntilTime <= kernel->stats->totalTicks))
|
||||||
|
Debugger();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// TypeToReg
|
||||||
|
// Retrieve the register # referred to in an instruction.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
static int
|
||||||
|
TypeToReg(RegType reg, Instruction *instr)
|
||||||
|
{
|
||||||
|
switch (reg) {
|
||||||
|
case RS:
|
||||||
|
return instr->rs;
|
||||||
|
case RT:
|
||||||
|
return instr->rt;
|
||||||
|
case RD:
|
||||||
|
return instr->rd;
|
||||||
|
case EXTRA:
|
||||||
|
return instr->extra;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Machine::OneInstruction
|
||||||
|
// Execute one instruction from a user-level program
|
||||||
|
//
|
||||||
|
// If there is any kind of exception or interrupt, we invoke the
|
||||||
|
// exception handler, and when it returns, we return to Run(), which
|
||||||
|
// will re-invoke us in a loop. This allows us to
|
||||||
|
// re-start the instruction execution from the beginning, in
|
||||||
|
// case any of our state has changed. On a syscall,
|
||||||
|
// the OS software must increment the PC so execution begins
|
||||||
|
// at the instruction immediately after the syscall.
|
||||||
|
//
|
||||||
|
// This routine is re-entrant, in that it can be called multiple
|
||||||
|
// times concurrently -- one for each thread executing user code.
|
||||||
|
// We get re-entrancy by never caching any data -- we always re-start the
|
||||||
|
// simulation from scratch each time we are called (or after trapping
|
||||||
|
// back to the Nachos kernel on an exception or interrupt), and we always
|
||||||
|
// store all data back to the machine registers and memory before
|
||||||
|
// leaving. This allows the Nachos kernel to control our behavior
|
||||||
|
// by controlling the contents of memory, the translation table,
|
||||||
|
// and the register set.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
Machine::OneInstruction(Instruction *instr)
|
||||||
|
{
|
||||||
|
#ifdef SIM_FIX
|
||||||
|
int byte; // described in Kane for LWL,LWR,...
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int raw;
|
||||||
|
int nextLoadReg = 0;
|
||||||
|
int nextLoadValue = 0; // record delayed load operation, to apply
|
||||||
|
// in the future
|
||||||
|
|
||||||
|
// Fetch instruction
|
||||||
|
if (!ReadMem(registers[PCReg], 4, &raw))
|
||||||
|
return; // exception occurred
|
||||||
|
instr->value = raw;
|
||||||
|
instr->Decode();
|
||||||
|
|
||||||
|
if (debug->IsEnabled('m')) {
|
||||||
|
struct OpString *str = &opStrings[instr->opCode];
|
||||||
|
char buf[80];
|
||||||
|
|
||||||
|
ASSERT(instr->opCode <= MaxOpcode);
|
||||||
|
cout << "At PC = " << registers[PCReg];
|
||||||
|
sprintf(buf, str->format, TypeToReg(str->args[0], instr),
|
||||||
|
TypeToReg(str->args[1], instr), TypeToReg(str->args[2], instr));
|
||||||
|
cout << "\t" << buf << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute next pc, but don't install in case there's an error or branch.
|
||||||
|
int pcAfter = registers[NextPCReg] + 4;
|
||||||
|
int sum, diff, tmp, value;
|
||||||
|
unsigned int rs, rt, imm;
|
||||||
|
|
||||||
|
// Execute the instruction (cf. Kane's book)
|
||||||
|
switch (instr->opCode) {
|
||||||
|
|
||||||
|
case OP_ADD:
|
||||||
|
sum = registers[instr->rs] + registers[instr->rt];
|
||||||
|
if (!((registers[instr->rs] ^ registers[instr->rt]) & SIGN_BIT) &&
|
||||||
|
((registers[instr->rs] ^ sum) & SIGN_BIT)) {
|
||||||
|
RaiseException(OverflowException, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
registers[instr->rd] = sum;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OP_ADDI:
|
||||||
|
sum = registers[instr->rs] + instr->extra;
|
||||||
|
if (!((registers[instr->rs] ^ instr->extra) & SIGN_BIT) &&
|
||||||
|
((instr->extra ^ sum) & SIGN_BIT)) {
|
||||||
|
RaiseException(OverflowException, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
registers[instr->rt] = sum;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OP_ADDIU:
|
||||||
|
registers[instr->rt] = registers[instr->rs] + instr->extra;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OP_ADDU:
|
||||||
|
registers[instr->rd] = registers[instr->rs] + registers[instr->rt];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OP_AND:
|
||||||
|
registers[instr->rd] = registers[instr->rs] & registers[instr->rt];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OP_ANDI:
|
||||||
|
registers[instr->rt] = registers[instr->rs] & (instr->extra & 0xffff);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OP_BEQ:
|
||||||
|
if (registers[instr->rs] == registers[instr->rt])
|
||||||
|
pcAfter = registers[NextPCReg] + IndexToAddr(instr->extra);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OP_BGEZAL:
|
||||||
|
registers[R31] = registers[NextPCReg] + 4;
|
||||||
|
case OP_BGEZ:
|
||||||
|
if (!(registers[instr->rs] & SIGN_BIT))
|
||||||
|
pcAfter = registers[NextPCReg] + IndexToAddr(instr->extra);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OP_BGTZ:
|
||||||
|
if (registers[instr->rs] > 0)
|
||||||
|
pcAfter = registers[NextPCReg] + IndexToAddr(instr->extra);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OP_BLEZ:
|
||||||
|
if (registers[instr->rs] <= 0)
|
||||||
|
pcAfter = registers[NextPCReg] + IndexToAddr(instr->extra);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OP_BLTZAL:
|
||||||
|
registers[R31] = registers[NextPCReg] + 4;
|
||||||
|
case OP_BLTZ:
|
||||||
|
if (registers[instr->rs] & SIGN_BIT)
|
||||||
|
pcAfter = registers[NextPCReg] + IndexToAddr(instr->extra);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OP_BNE:
|
||||||
|
if (registers[instr->rs] != registers[instr->rt])
|
||||||
|
pcAfter = registers[NextPCReg] + IndexToAddr(instr->extra);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OP_DIV:
|
||||||
|
if (registers[instr->rt] == 0) {
|
||||||
|
registers[LoReg] = 0;
|
||||||
|
registers[HiReg] = 0;
|
||||||
|
} else {
|
||||||
|
registers[LoReg] = registers[instr->rs] / registers[instr->rt];
|
||||||
|
registers[HiReg] = registers[instr->rs] % registers[instr->rt];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OP_DIVU:
|
||||||
|
rs = (unsigned int) registers[instr->rs];
|
||||||
|
rt = (unsigned int) registers[instr->rt];
|
||||||
|
if (rt == 0) {
|
||||||
|
registers[LoReg] = 0;
|
||||||
|
registers[HiReg] = 0;
|
||||||
|
} else {
|
||||||
|
tmp = rs / rt;
|
||||||
|
registers[LoReg] = (int) tmp;
|
||||||
|
tmp = rs % rt;
|
||||||
|
registers[HiReg] = (int) tmp;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OP_JAL:
|
||||||
|
registers[R31] = registers[NextPCReg] + 4;
|
||||||
|
case OP_J:
|
||||||
|
pcAfter = (pcAfter & 0xf0000000) | IndexToAddr(instr->extra);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OP_JALR:
|
||||||
|
registers[instr->rd] = registers[NextPCReg] + 4;
|
||||||
|
case OP_JR:
|
||||||
|
pcAfter = registers[instr->rs];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OP_LB:
|
||||||
|
case OP_LBU:
|
||||||
|
tmp = registers[instr->rs] + instr->extra;
|
||||||
|
if (!ReadMem(tmp, 1, &value))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ((value & 0x80) && (instr->opCode == OP_LB))
|
||||||
|
value |= 0xffffff00;
|
||||||
|
else
|
||||||
|
value &= 0xff;
|
||||||
|
nextLoadReg = instr->rt;
|
||||||
|
nextLoadValue = value;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OP_LH:
|
||||||
|
case OP_LHU:
|
||||||
|
tmp = registers[instr->rs] + instr->extra;
|
||||||
|
if (tmp & 0x1) {
|
||||||
|
RaiseException(AddressErrorException, tmp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!ReadMem(tmp, 2, &value))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ((value & 0x8000) && (instr->opCode == OP_LH))
|
||||||
|
value |= 0xffff0000;
|
||||||
|
else
|
||||||
|
value &= 0xffff;
|
||||||
|
nextLoadReg = instr->rt;
|
||||||
|
nextLoadValue = value;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OP_LUI:
|
||||||
|
DEBUG(dbgMach, "Executing: LUI r" << instr->rt << ", " << instr->extra);
|
||||||
|
registers[instr->rt] = instr->extra << 16;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OP_LW:
|
||||||
|
tmp = registers[instr->rs] + instr->extra;
|
||||||
|
if (tmp & 0x3) {
|
||||||
|
RaiseException(AddressErrorException, tmp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!ReadMem(tmp, 4, &value))
|
||||||
|
return;
|
||||||
|
nextLoadReg = instr->rt;
|
||||||
|
nextLoadValue = value;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OP_LWL:
|
||||||
|
tmp = registers[instr->rs] + instr->extra;
|
||||||
|
|
||||||
|
#ifdef SIM_FIX
|
||||||
|
// The only difference between this code and the BIG ENDIAN code
|
||||||
|
// is that the ReadMem call is guaranteed an aligned access as it
|
||||||
|
// should be (Kane's book hides the fact that all memory access
|
||||||
|
// are done using aligned loads - what the instruction asks for
|
||||||
|
// is a arbitrary) This is the whole purpose of LWL and LWR etc.
|
||||||
|
// Then the switch uses 3 - (tmp & 0x3) instead of (tmp & 0x3)
|
||||||
|
|
||||||
|
byte = tmp & 0x3;
|
||||||
|
// DEBUG('P', "Addr 0x%X\n",tmp-byte);
|
||||||
|
|
||||||
|
if (!ReadMem(tmp-byte, 4, &value))
|
||||||
|
return;
|
||||||
|
#else
|
||||||
|
// ReadMem assumes all 4 byte requests are aligned on an even
|
||||||
|
// word boundary. Also, the little endian/big endian swap code would
|
||||||
|
// fail (I think) if the other cases are ever exercised.
|
||||||
|
ASSERT((tmp & 0x3) == 0);
|
||||||
|
|
||||||
|
if (!ReadMem(tmp, 4, &value))
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (registers[LoadReg] == instr->rt)
|
||||||
|
nextLoadValue = registers[LoadValueReg];
|
||||||
|
else
|
||||||
|
nextLoadValue = registers[instr->rt];
|
||||||
|
#ifdef SIM_FIX
|
||||||
|
switch (3 - byte)
|
||||||
|
#else
|
||||||
|
switch (tmp & 0x3)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
nextLoadValue = value;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
nextLoadValue = (nextLoadValue & 0xff) | (value << 8);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
nextLoadValue = (nextLoadValue & 0xffff) | (value << 16);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
nextLoadValue = (nextLoadValue & 0xffffff) | (value << 24);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
nextLoadReg = instr->rt;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OP_LWR:
|
||||||
|
tmp = registers[instr->rs] + instr->extra;
|
||||||
|
|
||||||
|
#ifdef SIM_FIX
|
||||||
|
// The only difference between this code and the BIG ENDIAN code
|
||||||
|
// is that the ReadMem call is guaranteed an aligned access as it
|
||||||
|
// should be (Kane's book hides the fact that all memory access
|
||||||
|
// are done using aligned loads - what the instruction asks
|
||||||
|
// for is a arbitrary) This is the whole purpose of LWL and LWR etc.
|
||||||
|
// Then the switch uses 3 - (tmp & 0x3) instead of (tmp & 0x3)
|
||||||
|
|
||||||
|
byte = tmp & 0x3;
|
||||||
|
// DEBUG('P', "Addr 0x%X\n",tmp-byte);
|
||||||
|
|
||||||
|
if (!ReadMem(tmp-byte, 4, &value))
|
||||||
|
return;
|
||||||
|
#else
|
||||||
|
// ReadMem assumes all 4 byte requests are aligned on an even
|
||||||
|
// word boundary. Also, the little endian/big endian swap code would
|
||||||
|
// fail (I think) if the other cases are ever exercised.
|
||||||
|
ASSERT((tmp & 0x3) == 0);
|
||||||
|
|
||||||
|
if (!ReadMem(tmp, 4, &value))
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (registers[LoadReg] == instr->rt)
|
||||||
|
nextLoadValue = registers[LoadValueReg];
|
||||||
|
else
|
||||||
|
nextLoadValue = registers[instr->rt];
|
||||||
|
|
||||||
|
#ifdef SIM_FIX
|
||||||
|
switch (3 - byte)
|
||||||
|
#else
|
||||||
|
switch (tmp & 0x3)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
nextLoadValue = (nextLoadValue & 0xffffff00) |
|
||||||
|
((value >> 24) & 0xff);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
nextLoadValue = (nextLoadValue & 0xffff0000) |
|
||||||
|
((value >> 16) & 0xffff);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
nextLoadValue = (nextLoadValue & 0xff000000)
|
||||||
|
| ((value >> 8) & 0xffffff);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
nextLoadValue = value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
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 (!WriteMem((unsigned)
|
||||||
|
(registers[instr->rs] + instr->extra), 1, registers[instr->rt]))
|
||||||
|
return;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OP_SH:
|
||||||
|
if (!WriteMem((unsigned)
|
||||||
|
(registers[instr->rs] + instr->extra), 2, registers[instr->rt]))
|
||||||
|
return;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OP_SLL:
|
||||||
|
registers[instr->rd] = registers[instr->rt] << instr->extra;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OP_SLLV:
|
||||||
|
registers[instr->rd] = 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] < 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 = registers[instr->rt];
|
||||||
|
tmp >>= instr->extra;
|
||||||
|
registers[instr->rd] = tmp;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OP_SRLV:
|
||||||
|
tmp = registers[instr->rt];
|
||||||
|
tmp >>= (registers[instr->rs] & 0x1f);
|
||||||
|
registers[instr->rd] = tmp;
|
||||||
|
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 (!WriteMem((unsigned)
|
||||||
|
(registers[instr->rs] + instr->extra), 4, registers[instr->rt]))
|
||||||
|
return;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OP_SWL:
|
||||||
|
tmp = registers[instr->rs] + instr->extra;
|
||||||
|
|
||||||
|
#ifdef SIM_FIX
|
||||||
|
// The only difference between this code and the BIG ENDIAN code
|
||||||
|
// is that the ReadMem call is guaranteed an aligned access as it
|
||||||
|
// should be (Kane's book hides the fact that all memory access
|
||||||
|
// are done using aligned loads - what the instruction asks for
|
||||||
|
// is a arbitrary) This is the whole purpose of LWL and LWR etc.
|
||||||
|
|
||||||
|
byte = tmp & 0x3;
|
||||||
|
// DEBUG('P', "Addr 0x%X\n",tmp-byte);
|
||||||
|
if (!ReadMem(tmp-byte, 4, &value))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// DEBUG('P', "Value 0x%X\n",value);
|
||||||
|
#else
|
||||||
|
|
||||||
|
// The little endian/big endian swap code would
|
||||||
|
// fail (I think) if the other cases are ever exercised.
|
||||||
|
ASSERT((tmp & 0x3) == 0);
|
||||||
|
|
||||||
|
if (!ReadMem((tmp & ~0x3), 4, &value))
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef SIM_FIX
|
||||||
|
switch( 3 - byte )
|
||||||
|
#else
|
||||||
|
switch (tmp & 0x3)
|
||||||
|
#endif // SIM_FIX
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
value = registers[instr->rt];
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
value = (value & 0xff000000) | ((registers[instr->rt] >> 8) &
|
||||||
|
0xffffff);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
value = (value & 0xffff0000) | ((registers[instr->rt] >> 16) &
|
||||||
|
0xffff);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
value = (value & 0xffffff00) | ((registers[instr->rt] >> 24) &
|
||||||
|
0xff);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#ifndef SIM_FIX
|
||||||
|
if (!WriteMem((tmp & ~0x3), 4, value))
|
||||||
|
return;
|
||||||
|
#else
|
||||||
|
// DEBUG('P', "Value 0x%X\n",value);
|
||||||
|
|
||||||
|
if (!WriteMem((tmp - byte), 4, value))
|
||||||
|
return;
|
||||||
|
#endif // SIM_FIX
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OP_SWR:
|
||||||
|
tmp = registers[instr->rs] + instr->extra;
|
||||||
|
|
||||||
|
#ifndef SIM_FIX
|
||||||
|
// The little endian/big endian swap code would
|
||||||
|
// fail (I think) if the other cases are ever exercised.
|
||||||
|
ASSERT((tmp & 0x3) == 0);
|
||||||
|
|
||||||
|
if (!ReadMem((tmp & ~0x3), 4, &value))
|
||||||
|
return;
|
||||||
|
#else
|
||||||
|
// The only difference between this code and the BIG ENDIAN code
|
||||||
|
// is that the ReadMem call is guaranteed an aligned access as
|
||||||
|
// it should be (Kane's book hides the fact that all memory
|
||||||
|
// access are done using aligned loads - what the instruction
|
||||||
|
// asks for is a arbitrary) This is the whole purpose of LWL
|
||||||
|
// and LWR etc.
|
||||||
|
|
||||||
|
byte = tmp & 0x3;
|
||||||
|
// DEBUG('P', "Addr 0x%X\n",tmp-byte);
|
||||||
|
|
||||||
|
if (!ReadMem(tmp-byte, 4, &value))
|
||||||
|
return;
|
||||||
|
// DEBUG('P', "Value 0x%X\n",value);
|
||||||
|
#endif // SIM_FIX
|
||||||
|
|
||||||
|
#ifndef SIM_FIX
|
||||||
|
switch (tmp & 0x3)
|
||||||
|
#else
|
||||||
|
switch( 3 - byte )
|
||||||
|
#endif // SIM_FIX
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
value = (value & 0xffffff) | (registers[instr->rt] << 24);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
value = (value & 0xffff) | (registers[instr->rt] << 16);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
value = (value & 0xff) | (registers[instr->rt] << 8);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
value = registers[instr->rt];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef SIM_FIX
|
||||||
|
if (!WriteMem((tmp & ~0x3), 4, value))
|
||||||
|
return;
|
||||||
|
#else
|
||||||
|
// DEBUG('P', "Value 0x%X\n",value);
|
||||||
|
|
||||||
|
if (!WriteMem((tmp - byte), 4, value))
|
||||||
|
return;
|
||||||
|
#endif // SIM_FIX
|
||||||
|
|
||||||
|
|
||||||
|
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
Executable file
229
code/machine/mipssim.h
Executable 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 {
|
||||||
|
char *format; // 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 %d", {EXTRA, NONE, NONE}},
|
||||||
|
{"JAL %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
|
||||||
182
code/machine/network.cc
Executable file
182
code/machine/network.cc
Executable file
@@ -0,0 +1,182 @@
|
|||||||
|
// 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-1996 The Regents of the University of California.
|
||||||
|
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||||
|
// of liability and disclaimer of warranty provisions.
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
#include "network.h"
|
||||||
|
#include "main.h"
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
// NetworkInput::NetworkInput
|
||||||
|
// Initialize the simulation for the network input
|
||||||
|
//
|
||||||
|
// "toCall" is the interrupt handler to call when packet arrives
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
|
||||||
|
NetworkInput::NetworkInput(CallBackObj *toCall)
|
||||||
|
{
|
||||||
|
// set up the stuff to emulate asynchronous interrupts
|
||||||
|
callWhenAvail = toCall;
|
||||||
|
packetAvail = FALSE;
|
||||||
|
inHdr.length = 0;
|
||||||
|
|
||||||
|
sock = OpenSocket();
|
||||||
|
sprintf(sockName, "SOCKET_%d", kernel->hostName);
|
||||||
|
AssignNameToSocket(sockName, sock); // Bind socket to a filename
|
||||||
|
// in the current directory.
|
||||||
|
|
||||||
|
// start polling for incoming packets
|
||||||
|
kernel->interrupt->Schedule(this, NetworkTime, NetworkRecvInt);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
// NetworkInput::NetworkInput
|
||||||
|
// Deallocate the simulation for the network input
|
||||||
|
// (basically, deallocate the input mailbox)
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
|
||||||
|
NetworkInput::~NetworkInput()
|
||||||
|
{
|
||||||
|
CloseSocket(sock);
|
||||||
|
DeAssignNameToSocket(sockName);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
// NetworkInput::CallBack
|
||||||
|
// Simulator calls this when a packet may be available to
|
||||||
|
// be read in from the simulated network.
|
||||||
|
//
|
||||||
|
// First check to make sure packet is available & there's space to
|
||||||
|
// pull it in. Then invoke the "callBack" registered by whoever
|
||||||
|
// wants the packet.
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
NetworkInput::CallBack()
|
||||||
|
{
|
||||||
|
// schedule the next time to poll for a packet
|
||||||
|
kernel->interrupt->Schedule(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 == kernel->hostName) && (inHdr.length <= MaxPacketSize));
|
||||||
|
bcopy(buffer + sizeof(PacketHeader), inbox, inHdr.length);
|
||||||
|
delete [] buffer ;
|
||||||
|
|
||||||
|
DEBUG(dbgNet, "Network received packet from " << inHdr.from << ", length " << inHdr.length);
|
||||||
|
kernel->stats->numPacketsRecvd++;
|
||||||
|
|
||||||
|
// tell post office that the packet has arrived
|
||||||
|
callWhenAvail->CallBack();
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
// NetworkInput::Receive
|
||||||
|
// Read a packet, if one is buffered
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
|
||||||
|
PacketHeader
|
||||||
|
NetworkInput::Receive(char* data)
|
||||||
|
{
|
||||||
|
PacketHeader hdr = inHdr;
|
||||||
|
|
||||||
|
inHdr.length = 0;
|
||||||
|
if (hdr.length != 0) {
|
||||||
|
bcopy(inbox, data, hdr.length);
|
||||||
|
}
|
||||||
|
return hdr;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
// NetworkOutput::NetworkOutput
|
||||||
|
// Initialize the simulation for sending network packets
|
||||||
|
//
|
||||||
|
// "reliability" says whether we drop packets to emulate unreliable links
|
||||||
|
// "toCall" is the interrupt handler to call when next packet can be sent
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
|
||||||
|
NetworkOutput::NetworkOutput(double reliability, CallBackObj *toCall)
|
||||||
|
{
|
||||||
|
if (reliability < 0) chanceToWork = 0;
|
||||||
|
else if (reliability > 1) chanceToWork = 1;
|
||||||
|
else chanceToWork = reliability;
|
||||||
|
|
||||||
|
// set up the stuff to emulate asynchronous interrupts
|
||||||
|
callWhenDone = toCall;
|
||||||
|
sendBusy = FALSE;
|
||||||
|
sock = OpenSocket();
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
// NetworkOutput::~NetworkOutput
|
||||||
|
// Deallocate the simulation for sending network packets
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
|
||||||
|
NetworkOutput::~NetworkOutput()
|
||||||
|
{
|
||||||
|
CloseSocket(sock);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
// NetworkOutput::CallBack
|
||||||
|
// Called by simulator when another packet can be sent.
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
NetworkOutput::CallBack()
|
||||||
|
{
|
||||||
|
sendBusy = FALSE;
|
||||||
|
kernel->stats->numPacketsSent++;
|
||||||
|
callWhenDone->CallBack();
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
|
// NetworkOutput::Send
|
||||||
|
// Send a packet into the simulated network, to the destination in hdr.
|
||||||
|
// Concatenate 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
|
||||||
|
NetworkOutput::Send(PacketHeader hdr, char* data)
|
||||||
|
{
|
||||||
|
char toName[32];
|
||||||
|
|
||||||
|
sprintf(toName, "SOCKET_%d", (int)hdr.to);
|
||||||
|
|
||||||
|
ASSERT((sendBusy == FALSE) && (hdr.length > 0) &&
|
||||||
|
(hdr.length <= MaxPacketSize) && (hdr.from == kernel->hostName));
|
||||||
|
DEBUG(dbgNet, "Sending to addr " << hdr.to << ", length " << hdr.length);
|
||||||
|
|
||||||
|
kernel->interrupt->Schedule(this, NetworkTime, NetworkSendInt);
|
||||||
|
|
||||||
|
if (RandomNumber() % 100 >= chanceToWork * 100) { // emulate a lost packet
|
||||||
|
DEBUG(dbgNet, "oops, lost it!");
|
||||||
|
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;
|
||||||
|
}
|
||||||
110
code/machine/network.h
Executable file
110
code/machine/network.h
Executable file
@@ -0,0 +1,110 @@
|
|||||||
|
// 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-1996 The Regents of the University of California.
|
||||||
|
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||||
|
// of liability and disclaimer of warranty provisions.
|
||||||
|
|
||||||
|
#ifndef NETWORK_H
|
||||||
|
#define NETWORK_H
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
#include "utility.h"
|
||||||
|
#include "callback.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 two classes 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 NetworkInput : public CallBackObj{
|
||||||
|
public:
|
||||||
|
NetworkInput(CallBackObj *toCall);
|
||||||
|
// Allocate and initialize network input driver
|
||||||
|
~NetworkInput(); // De-allocate the network input driver data
|
||||||
|
|
||||||
|
PacketHeader Receive(char* 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 CallBack(); // A packet may have arrived.
|
||||||
|
|
||||||
|
private:
|
||||||
|
int sock; // UNIX socket number for incoming packets
|
||||||
|
char sockName[32]; // File name corresponding to UNIX socket
|
||||||
|
|
||||||
|
CallBackObj *callWhenAvail; // Interrupt handler, signalling packet has
|
||||||
|
// arrived.
|
||||||
|
bool packetAvail; // Packet has arrived, can be pulled off of
|
||||||
|
// network
|
||||||
|
PacketHeader inHdr; // Information about arrived packet
|
||||||
|
char inbox[MaxPacketSize]; // Data for arrived packet
|
||||||
|
};
|
||||||
|
|
||||||
|
class NetworkOutput : public CallBackObj {
|
||||||
|
public:
|
||||||
|
NetworkOutput(double reliability, CallBackObj *toCall);
|
||||||
|
// Allocate and initialize network output driver
|
||||||
|
~NetworkOutput(); // De-allocate the network input driver data
|
||||||
|
|
||||||
|
void Send(PacketHeader hdr, char* data);
|
||||||
|
// Send the packet data to a remote machine,
|
||||||
|
// specified by "hdr". Returns immediately.
|
||||||
|
// "callWhenDone" is invoked once the next
|
||||||
|
// packet can be sent. Note that callWhenDone
|
||||||
|
// is called whether or not the packet is
|
||||||
|
// dropped, and note that the "from" field of
|
||||||
|
// the PacketHeader is filled in automatically
|
||||||
|
// by Send().
|
||||||
|
|
||||||
|
void CallBack(); // Interrupt handler, called when message is
|
||||||
|
// sent
|
||||||
|
|
||||||
|
private:
|
||||||
|
int sock; // UNIX socket number for outgoing packets
|
||||||
|
double chanceToWork; // Likelihood packet will be dropped
|
||||||
|
CallBackObj *callWhenDone; // Interrupt handler, signalling next packet
|
||||||
|
// can be sent.
|
||||||
|
bool sendBusy; // Packet is being sent.
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // NETWORK_H
|
||||||
45
code/machine/stats.cc
Executable file
45
code/machine/stats.cc
Executable file
@@ -0,0 +1,45 @@
|
|||||||
|
// stats.h
|
||||||
|
// Routines for managing statistics about Nachos performance.
|
||||||
|
//
|
||||||
|
// DO NOT CHANGE -- these stats are maintained by the machine emulation.
|
||||||
|
//
|
||||||
|
// Copyright (c) 1992-1996 The Regents of the University of California.
|
||||||
|
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||||
|
// of liability and disclaimer of warranty provisions.
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
#include "debug.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()
|
||||||
|
{
|
||||||
|
cout << "Ticks: total " << totalTicks << ", idle " << idleTicks;
|
||||||
|
cout << ", system " << systemTicks << ", user " << userTicks <<"\n";
|
||||||
|
cout << "Disk I/O: reads " << numDiskReads;
|
||||||
|
cout << ", writes " << numDiskWrites << "\n";
|
||||||
|
cout << "Console I/O: reads " << numConsoleCharsRead;
|
||||||
|
cout << ", writes " << numConsoleCharsWritten << "\n";
|
||||||
|
cout << "Paging: faults " << numPageFaults << "\n";
|
||||||
|
cout << "Network I/O: packets received " << numPacketsRecvd;
|
||||||
|
cout << ", sent " << numPacketsSent << "\n";
|
||||||
|
}
|
||||||
60
code/machine/stats.h
Executable file
60
code/machine/stats.h
Executable file
@@ -0,0 +1,60 @@
|
|||||||
|
// 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:
|
||||||
|
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)
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
const int UserTick = 1; // advance for each user-level instruction
|
||||||
|
const int SystemTick = 10; // advance each time interrupts are enabled
|
||||||
|
const int RotationTime = 500; // time disk takes to rotate one sector
|
||||||
|
const int SeekTime = 500; // time disk takes to seek past one track
|
||||||
|
const int ConsoleTime = 100; // time to read or write one character
|
||||||
|
const int NetworkTime = 100; // time to send or receive one packet
|
||||||
|
const int TimerTicks = 100; // (average) time between timer interrupts
|
||||||
|
|
||||||
|
#endif // STATS_H
|
||||||
81
code/machine/timer.cc
Executable file
81
code/machine/timer.cc
Executable file
@@ -0,0 +1,81 @@
|
|||||||
|
// 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-1996 The Regents of the University of California.
|
||||||
|
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||||
|
// of liability and disclaimer of warranty provisions.
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
#include "timer.h"
|
||||||
|
#include "main.h"
|
||||||
|
#include "sysdep.h"
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
// "doRandom" -- if true, arrange for the interrupts to occur
|
||||||
|
// at random, instead of fixed, intervals.
|
||||||
|
// "toCall" is the interrupt handler to call when the timer expires.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
Timer::Timer(bool doRandom, CallBackObj *toCall)
|
||||||
|
{
|
||||||
|
randomize = doRandom;
|
||||||
|
callPeriodically = toCall;
|
||||||
|
disable = FALSE;
|
||||||
|
SetInterrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Timer::CallBack
|
||||||
|
// Routine called when interrupt is generated by the hardware
|
||||||
|
// timer device. Schedule the next interrupt, and invoke the
|
||||||
|
// interrupt handler.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
void
|
||||||
|
Timer::CallBack()
|
||||||
|
{
|
||||||
|
// invoke the Nachos interrupt handler for this device
|
||||||
|
callPeriodically->CallBack();
|
||||||
|
|
||||||
|
SetInterrupt(); // do last, to let software interrupt handler
|
||||||
|
// decide if it wants to disable future interrupts
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Timer::SetInterrupt
|
||||||
|
// Cause a timer interrupt to occur in the future, unless
|
||||||
|
// future interrupts have been disabled. The delay is either
|
||||||
|
// fixed or random.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
Timer::SetInterrupt()
|
||||||
|
{
|
||||||
|
if (!disable) {
|
||||||
|
int delay = TimerTicks;
|
||||||
|
|
||||||
|
if (randomize) {
|
||||||
|
delay = 1 + (RandomNumber() % (TimerTicks * 2));
|
||||||
|
}
|
||||||
|
// schedule the next timer device interrupt
|
||||||
|
kernel->interrupt->Schedule(this, delay, TimerInt);
|
||||||
|
}
|
||||||
|
}
|
||||||
53
code/machine/timer.h
Executable file
53
code/machine/timer.h
Executable file
@@ -0,0 +1,53 @@
|
|||||||
|
// 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-1996 The Regents of the University of California.
|
||||||
|
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||||
|
// of liability and disclaimer of warranty provisions.
|
||||||
|
|
||||||
|
#ifndef TIMER_H
|
||||||
|
#define TIMER_H
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
#include "utility.h"
|
||||||
|
#include "callback.h"
|
||||||
|
|
||||||
|
// The following class defines a hardware timer.
|
||||||
|
class Timer : public CallBackObj {
|
||||||
|
public:
|
||||||
|
Timer(bool doRandom, CallBackObj *toCall);
|
||||||
|
// Initialize the timer, and callback to "toCall"
|
||||||
|
// every time slice.
|
||||||
|
virtual ~Timer() {}
|
||||||
|
|
||||||
|
void Disable() { disable = TRUE; }
|
||||||
|
// Turn timer device off, so it doesn't
|
||||||
|
// generate any more interrupts.
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool randomize; // set if we need to use a random timeout delay
|
||||||
|
CallBackObj *callPeriodically; // call this every TimerTicks time units
|
||||||
|
bool disable; // turn off the timer device after next
|
||||||
|
// interrupt.
|
||||||
|
|
||||||
|
void CallBack(); // called internally when the hardware
|
||||||
|
// timer generates an interrupt
|
||||||
|
|
||||||
|
void SetInterrupt(); // cause an interrupt to occur in the
|
||||||
|
// the future after a fixed or random
|
||||||
|
// delay
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // TIMER_H
|
||||||
250
code/machine/translate.cc
Executable file
250
code/machine/translate.cc
Executable file
@@ -0,0 +1,250 @@
|
|||||||
|
// 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-1996 The Regents of the University of California.
|
||||||
|
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||||
|
// of liability and disclaimer of warranty provisions.
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
#include "main.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)
|
||||||
|
{
|
||||||
|
int data;
|
||||||
|
ExceptionType exception;
|
||||||
|
int physicalAddress;
|
||||||
|
|
||||||
|
DEBUG(dbgAddr, "Reading VA " << addr << ", size " << size);
|
||||||
|
|
||||||
|
exception = Translate(addr, &physicalAddress, size, FALSE);
|
||||||
|
if (exception != NoException) {
|
||||||
|
RaiseException(exception, addr);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
switch (size) {
|
||||||
|
case 1:
|
||||||
|
data = mainMemory[physicalAddress];
|
||||||
|
*value = data;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
data = *(unsigned short *) &mainMemory[physicalAddress];
|
||||||
|
*value = ShortToHost(data);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
data = *(unsigned int *) &mainMemory[physicalAddress];
|
||||||
|
*value = WordToHost(data);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: ASSERT(FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG(dbgAddr, "\tvalue read = " << *value);
|
||||||
|
return (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(dbgAddr, "Writing VA " << addr << ", size " << size << ", value " << value);
|
||||||
|
|
||||||
|
exception = Translate(addr, &physicalAddress, size, TRUE);
|
||||||
|
if (exception != NoException) {
|
||||||
|
RaiseException(exception, addr);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
switch (size) {
|
||||||
|
case 1:
|
||||||
|
mainMemory[physicalAddress] = (unsigned char) (value & 0xff);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
*(unsigned short *) &mainMemory[physicalAddress]
|
||||||
|
= ShortToMachine((unsigned short) (value & 0xffff));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
*(unsigned int *) &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)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
unsigned int vpn, offset;
|
||||||
|
TranslationEntry *entry;
|
||||||
|
unsigned int pageFrame;
|
||||||
|
|
||||||
|
DEBUG(dbgAddr, "\tTranslate " << virtAddr << (writing ? " , write" : " , read"));
|
||||||
|
|
||||||
|
// check for alignment errors
|
||||||
|
if (((size == 4) && (virtAddr & 0x3)) || ((size == 2) && (virtAddr & 0x1))){
|
||||||
|
DEBUG(dbgAddr, "Alignment problem at " << virtAddr << ", size " << size);
|
||||||
|
return AddressErrorException;
|
||||||
|
}
|
||||||
|
// we must have either a TLB or a page table, but not both!
|
||||||
|
ASSERT(tlb == NULL || pageTable == NULL);
|
||||||
|
ASSERT(tlb != NULL || pageTable != 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 >= pageTableSize) {
|
||||||
|
DEBUG(dbgAddr, "Illegal virtual page # " << virtAddr);
|
||||||
|
return AddressErrorException;
|
||||||
|
} else if (!pageTable[vpn].valid) {
|
||||||
|
DEBUG(dbgAddr, "Invalid virtual page # " << virtAddr);
|
||||||
|
return PageFaultException;
|
||||||
|
}
|
||||||
|
entry = &pageTable[vpn];
|
||||||
|
} else {
|
||||||
|
for (entry = NULL, i = 0; i < TLBSize; i++)
|
||||||
|
if (tlb[i].valid && (tlb[i].virtualPage == ((int)vpn))) {
|
||||||
|
entry = &tlb[i]; // FOUND!
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (entry == NULL) { // not found
|
||||||
|
DEBUG(dbgAddr, "Invalid TLB entry for this virtual page!");
|
||||||
|
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
|
||||||
|
DEBUG(dbgAddr, "Write to read-only page at " << virtAddr);
|
||||||
|
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) {
|
||||||
|
DEBUG(dbgAddr, "Illegal pageframe " << pageFrame);
|
||||||
|
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));
|
||||||
|
DEBUG(dbgAddr, "phys addr = " << *physAddr);
|
||||||
|
return NoException;
|
||||||
|
}
|
||||||
45
code/machine/translate.h
Executable file
45
code/machine/translate.h
Executable file
@@ -0,0 +1,45 @@
|
|||||||
|
// 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:
|
||||||
|
int virtualPage; // The page number in virtual memory.
|
||||||
|
int physicalPage; // The page number in real memory (relative to the
|
||||||
|
// start of "mainMemory"
|
||||||
|
bool valid; // If this bit is set, 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
|
||||||
344
code/network/post.cc
Executable file
344
code/network/post.cc
Executable file
@@ -0,0 +1,344 @@
|
|||||||
|
// post.cc
|
||||||
|
// Routines to deliver incoming network messages to the correct
|
||||||
|
// "address" -- a mailbox, or a holding area for incoming messages.
|
||||||
|
// This module operates just like the US postal service (in other
|
||||||
|
// words, it works, but it's slow, and you can't really be sure if
|
||||||
|
// your mail really got through!).
|
||||||
|
//
|
||||||
|
// Note that once we prepend the MailHdr to the outgoing message data,
|
||||||
|
// the combination (MailHdr plus data) looks like "data" to the Network
|
||||||
|
// device.
|
||||||
|
//
|
||||||
|
// The implementation synchronizes incoming messages with threads
|
||||||
|
// waiting for those messages.
|
||||||
|
//
|
||||||
|
// Copyright (c) 1992-1996 The Regents of the University of California.
|
||||||
|
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||||
|
// of liability and disclaimer of warranty provisions.
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
#include "post.h"
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Mail::Mail
|
||||||
|
// Initialize a single mail message, by concatenating the headers to
|
||||||
|
// the data.
|
||||||
|
//
|
||||||
|
// "pktH" -- source, destination machine ID's
|
||||||
|
// "mailH" -- source, destination mailbox ID's
|
||||||
|
// "data" -- payload data
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
Mail::Mail(PacketHeader pktH, MailHeader mailH, char *msgData)
|
||||||
|
{
|
||||||
|
ASSERT(mailH.length <= MaxMailSize);
|
||||||
|
|
||||||
|
pktHdr = pktH;
|
||||||
|
mailHdr = mailH;
|
||||||
|
bcopy(msgData, data, mailHdr.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// MailBox::MailBox
|
||||||
|
// Initialize a single mail box within the post office, so that it
|
||||||
|
// can receive incoming messages.
|
||||||
|
//
|
||||||
|
// Just initialize a list of messages, representing the mailbox.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
MailBox::MailBox()
|
||||||
|
{
|
||||||
|
messages = new SynchList<Mail *>();
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// MailBox::~MailBox
|
||||||
|
// De-allocate a single mail box within the post office.
|
||||||
|
//
|
||||||
|
// Just delete the mailbox, and throw away all the queued messages
|
||||||
|
// in the mailbox.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
MailBox::~MailBox()
|
||||||
|
{
|
||||||
|
delete messages;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// PrintHeader
|
||||||
|
// Print the message header -- the destination machine ID and mailbox
|
||||||
|
// #, source machine ID and mailbox #, and message length.
|
||||||
|
//
|
||||||
|
// "pktHdr" -- source, destination machine ID's
|
||||||
|
// "mailHdr" -- source, destination mailbox ID's
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
static void
|
||||||
|
PrintHeader(PacketHeader pktHdr, MailHeader mailHdr)
|
||||||
|
{
|
||||||
|
cout << "From (" << pktHdr.from << ", " << mailHdr.from << ") to (" <<
|
||||||
|
pktHdr.to << ", " << mailHdr.to << ") bytes " << mailHdr.length << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// MailBox::Put
|
||||||
|
// Add a message to the mailbox. If anyone is waiting for message
|
||||||
|
// arrival, wake them up!
|
||||||
|
//
|
||||||
|
// We need to reconstruct the Mail message (by concatenating the headers
|
||||||
|
// to the data), to simplify queueing the message on the SynchList.
|
||||||
|
//
|
||||||
|
// "pktHdr" -- source, destination machine ID's
|
||||||
|
// "mailHdr" -- source, destination mailbox ID's
|
||||||
|
// "data" -- payload message data
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
MailBox::Put(PacketHeader pktHdr, MailHeader mailHdr, char *data)
|
||||||
|
{
|
||||||
|
Mail *mail = new Mail(pktHdr, mailHdr, data);
|
||||||
|
|
||||||
|
messages->Append(mail); // put on the end of the list of
|
||||||
|
// arrived messages, and wake up
|
||||||
|
// any waiters
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// MailBox::Get
|
||||||
|
// Get a message from a mailbox, parsing it into the packet header,
|
||||||
|
// mailbox header, and data.
|
||||||
|
//
|
||||||
|
// The calling thread waits if there are no messages in the mailbox.
|
||||||
|
//
|
||||||
|
// "pktHdr" -- address to put: source, destination machine ID's
|
||||||
|
// "mailHdr" -- address to put: source, destination mailbox ID's
|
||||||
|
// "data" -- address to put: payload message data
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
MailBox::Get(PacketHeader *pktHdr, MailHeader *mailHdr, char *data)
|
||||||
|
{
|
||||||
|
DEBUG(dbgNet, "Waiting for mail in mailbox");
|
||||||
|
Mail *mail = messages->RemoveFront(); // remove message from list;
|
||||||
|
// will wait if list is empty
|
||||||
|
|
||||||
|
*pktHdr = mail->pktHdr;
|
||||||
|
*mailHdr = mail->mailHdr;
|
||||||
|
if (debug->IsEnabled('n')) {
|
||||||
|
cout << "Got mail from mailbox: ";
|
||||||
|
PrintHeader(*pktHdr, *mailHdr);
|
||||||
|
}
|
||||||
|
bcopy(mail->data, data, mail->mailHdr.length);
|
||||||
|
// copy the message data into
|
||||||
|
// the caller's buffer
|
||||||
|
delete mail; // we've copied out the stuff we
|
||||||
|
// need, we can now discard the message
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// PostOfficeInput::PostOfficeInput
|
||||||
|
// Initialize the post office input queues as a collection of mailboxes.
|
||||||
|
// Also initialize the network device, to allow post offices
|
||||||
|
// on different machines to deliver messages to one another.
|
||||||
|
//
|
||||||
|
// We use a separate thread "the postal worker" to wait for messages
|
||||||
|
// to arrive, and deliver them to the correct mailbox. Note that
|
||||||
|
// delivering messages to the mailboxes can't be done directly
|
||||||
|
// by the interrupt handlers, because it requires a Lock.
|
||||||
|
//
|
||||||
|
// "nBoxes" is the number of mail boxes in this Post Office
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
PostOfficeInput::PostOfficeInput(int nBoxes)
|
||||||
|
{
|
||||||
|
messageAvailable = new Semaphore("message available", 0);
|
||||||
|
|
||||||
|
numBoxes = nBoxes;
|
||||||
|
boxes = new MailBox[nBoxes];
|
||||||
|
|
||||||
|
network = new NetworkInput(this);
|
||||||
|
|
||||||
|
Thread *t = new Thread("postal worker", 1);
|
||||||
|
|
||||||
|
t->Fork(PostOfficeInput::PostalDelivery, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// PostOfficeInput::~PostOfficeInput
|
||||||
|
// De-allocate the post office data structures.
|
||||||
|
//
|
||||||
|
// Since the postal helper is waiting on the "messageAvail" semaphore,
|
||||||
|
// we don't deallocate it! This leaves garbage lying about,
|
||||||
|
// but the alternative is worse!
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
PostOfficeInput::~PostOfficeInput()
|
||||||
|
{
|
||||||
|
delete network;
|
||||||
|
delete [] boxes;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// PostOffice::PostalDelivery
|
||||||
|
// Wait for incoming messages, and put them in the right mailbox.
|
||||||
|
//
|
||||||
|
// Incoming messages have had the PacketHeader stripped off,
|
||||||
|
// but the MailHeader is still tacked on the front of the data.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
PostOfficeInput::PostalDelivery(void* data)
|
||||||
|
{
|
||||||
|
PostOfficeInput* _this = (PostOfficeInput*)data;
|
||||||
|
PacketHeader pktHdr;
|
||||||
|
MailHeader mailHdr;
|
||||||
|
char *buffer = new char[MaxPacketSize];
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
// first, wait for a message
|
||||||
|
_this->messageAvailable->P();
|
||||||
|
pktHdr = _this->network->Receive(buffer);
|
||||||
|
|
||||||
|
mailHdr = *(MailHeader *)buffer;
|
||||||
|
if (debug->IsEnabled('n')) {
|
||||||
|
cout << "Putting mail into mailbox: ";
|
||||||
|
PrintHeader(pktHdr, mailHdr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check that arriving message is legal!
|
||||||
|
ASSERT(0 <= mailHdr.to && mailHdr.to < _this->numBoxes);
|
||||||
|
ASSERT(mailHdr.length <= MaxMailSize);
|
||||||
|
|
||||||
|
// put into mailbox
|
||||||
|
_this->boxes[mailHdr.to].Put(pktHdr, mailHdr, buffer + sizeof(MailHeader));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// PostOfficeInput::Receive
|
||||||
|
// Retrieve a message from a specific box if one is available,
|
||||||
|
// otherwise wait for a message to arrive in the box.
|
||||||
|
//
|
||||||
|
// Note that the MailHeader + data looks just like normal payload
|
||||||
|
// data to the Network.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// "box" -- mailbox ID in which to look for message
|
||||||
|
// "pktHdr" -- address to put: source, destination machine ID's
|
||||||
|
// "mailHdr" -- address to put: source, destination mailbox ID's
|
||||||
|
// "data" -- address to put: payload message data
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
PostOfficeInput::Receive(int box, PacketHeader *pktHdr,
|
||||||
|
MailHeader *mailHdr, char* data)
|
||||||
|
{
|
||||||
|
ASSERT((box >= 0) && (box < numBoxes));
|
||||||
|
|
||||||
|
boxes[box].Get(pktHdr, mailHdr, data);
|
||||||
|
ASSERT(mailHdr->length <= MaxMailSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// PostOffice::CallBack
|
||||||
|
// Interrupt handler, called when a packet arrives from the network.
|
||||||
|
//
|
||||||
|
// Signal the PostalDelivery routine that it is time to get to work!
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
PostOfficeInput::CallBack()
|
||||||
|
{
|
||||||
|
messageAvailable->V();
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// PostOfficeOutput::PostOfficeOutput
|
||||||
|
// Initialize the post office output queue.
|
||||||
|
//
|
||||||
|
// "reliability" is the probability that a network packet will
|
||||||
|
// be delivered (e.g., reliability = 1 means the network never
|
||||||
|
// drops any packets; reliability = 0 means the network never
|
||||||
|
// delivers any packets)
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
PostOfficeOutput::PostOfficeOutput(double reliability)
|
||||||
|
{
|
||||||
|
messageSent = new Semaphore("message sent", 0);
|
||||||
|
sendLock = new Lock("message send lock");
|
||||||
|
|
||||||
|
network = new NetworkOutput(reliability, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// PostOfficeOutput::~PostOfficeOutput
|
||||||
|
// De-allocate the post office data structures.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
PostOfficeOutput::~PostOfficeOutput()
|
||||||
|
{
|
||||||
|
delete network;
|
||||||
|
delete messageSent;
|
||||||
|
delete sendLock;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// PostOfficeOutput::Send
|
||||||
|
// Concatenate the MailHeader to the front of the data, and pass
|
||||||
|
// the result to the Network for delivery to the destination machine.
|
||||||
|
//
|
||||||
|
// Note that the MailHeader + data looks just like normal payload
|
||||||
|
// data to the Network.
|
||||||
|
//
|
||||||
|
// "pktHdr" -- source, destination machine ID's
|
||||||
|
// "mailHdr" -- source, destination mailbox ID's
|
||||||
|
// "data" -- payload message data
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
PostOfficeOutput::Send(PacketHeader pktHdr, MailHeader mailHdr, char* data)
|
||||||
|
{
|
||||||
|
char* buffer = new char[MaxPacketSize]; // space to hold concatenated
|
||||||
|
// mailHdr + data
|
||||||
|
|
||||||
|
if (debug->IsEnabled('n')) {
|
||||||
|
cout << "Post send: ";
|
||||||
|
PrintHeader(pktHdr, mailHdr);
|
||||||
|
}
|
||||||
|
ASSERT(mailHdr.length <= MaxMailSize);
|
||||||
|
ASSERT(0 <= mailHdr.to);
|
||||||
|
|
||||||
|
// fill in pktHdr, for the Network layer
|
||||||
|
pktHdr.from = kernel->hostName;
|
||||||
|
pktHdr.length = mailHdr.length + sizeof(MailHeader);
|
||||||
|
|
||||||
|
// concatenate MailHeader and data
|
||||||
|
bcopy((char *)&mailHdr, buffer, sizeof(MailHeader));
|
||||||
|
bcopy(data, buffer + sizeof(MailHeader), mailHdr.length);
|
||||||
|
|
||||||
|
sendLock->Acquire(); // only one message can be sent
|
||||||
|
// to the network at any one time
|
||||||
|
network->Send(pktHdr, buffer);
|
||||||
|
messageSent->P(); // wait for interrupt to tell us
|
||||||
|
// ok to send the next message
|
||||||
|
sendLock->Release();
|
||||||
|
|
||||||
|
delete [] buffer; // we've sent the message, so
|
||||||
|
// we can delete our buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// PostOfficeOutput::CallBack
|
||||||
|
// Interrupt handler, called when the next packet can be put onto the
|
||||||
|
// network.
|
||||||
|
//
|
||||||
|
// Called even if the previous packet was dropped.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
PostOfficeOutput::CallBack()
|
||||||
|
{
|
||||||
|
messageSent->V();
|
||||||
|
}
|
||||||
|
|
||||||
151
code/network/post.h
Executable file
151
code/network/post.h
Executable file
@@ -0,0 +1,151 @@
|
|||||||
|
// post.h
|
||||||
|
// Data structures for providing the abstraction of unreliable,
|
||||||
|
// ordered, fixed-size message delivery to mailboxes on other
|
||||||
|
// (directly connected) machines. Messages can be dropped by
|
||||||
|
// the network, but they are never corrupted.
|
||||||
|
//
|
||||||
|
// The US Post Office (and Canada Post! -KMS)
|
||||||
|
// delivers mail to the addressed mailbox.
|
||||||
|
// By analogy, our post office delivers packets to a specific buffer
|
||||||
|
// (MailBox), based on the mailbox number stored in the packet header.
|
||||||
|
// Mail waits in the box until a thread asks for it; if the mailbox
|
||||||
|
// is empty, threads can wait for mail to arrive in it.
|
||||||
|
//
|
||||||
|
// Thus, the service our post office provides is to de-multiplex
|
||||||
|
// incoming packets, delivering them to the appropriate thread.
|
||||||
|
//
|
||||||
|
// With each message, you get a return address, which consists of a "from
|
||||||
|
// address", which is the id of the machine that sent the message, and
|
||||||
|
// a "from box", which is the number of a mailbox on the sending machine
|
||||||
|
// to which you can send an acknowledgement, if your protocol requires
|
||||||
|
// this.
|
||||||
|
//
|
||||||
|
// Copyright (c) 1992-1996 The Regents of the University of California.
|
||||||
|
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||||
|
// of liability and disclaimer of warranty provisions.
|
||||||
|
|
||||||
|
#ifndef POST_H
|
||||||
|
#define POST_H
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
#include "utility.h"
|
||||||
|
#include "callback.h"
|
||||||
|
#include "network.h"
|
||||||
|
#include "synchlist.h"
|
||||||
|
#include "synch.h"
|
||||||
|
|
||||||
|
// Mailbox address -- uniquely identifies a mailbox on a given machine.
|
||||||
|
// A mailbox is just a place for temporary storage for messages.
|
||||||
|
typedef int MailBoxAddress;
|
||||||
|
|
||||||
|
// The following class defines part of the message header.
|
||||||
|
// This is prepended to the message by the PostOffice, before the message
|
||||||
|
// is sent to the Network.
|
||||||
|
|
||||||
|
class MailHeader {
|
||||||
|
public:
|
||||||
|
MailBoxAddress to; // Destination mail box
|
||||||
|
MailBoxAddress from; // Mail box to reply to
|
||||||
|
unsigned length; // Bytes of message data (excluding the
|
||||||
|
// mail header)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Maximum "payload" -- real data -- that can included in a single message
|
||||||
|
// Excluding the MailHeader and the PacketHeader
|
||||||
|
|
||||||
|
#define MaxMailSize (MaxPacketSize - sizeof(MailHeader))
|
||||||
|
|
||||||
|
|
||||||
|
// The following class defines the format of an incoming/outgoing
|
||||||
|
// "Mail" message. The message format is layered:
|
||||||
|
// network header (PacketHeader)
|
||||||
|
// post office header (MailHeader)
|
||||||
|
// data
|
||||||
|
|
||||||
|
class Mail {
|
||||||
|
public:
|
||||||
|
Mail(PacketHeader pktH, MailHeader mailH, char *msgData);
|
||||||
|
// Initialize a mail message by
|
||||||
|
// concatenating the headers to the data
|
||||||
|
|
||||||
|
PacketHeader pktHdr; // Header appended by Network
|
||||||
|
MailHeader mailHdr; // Header appended by PostOffice
|
||||||
|
char data[MaxMailSize]; // Payload -- message data
|
||||||
|
};
|
||||||
|
|
||||||
|
// The following class defines a single mailbox, or temporary storage
|
||||||
|
// for messages. Incoming messages are put by the PostOffice into the
|
||||||
|
// appropriate mailbox, and these messages can then be retrieved by
|
||||||
|
// threads on this machine.
|
||||||
|
|
||||||
|
class MailBox {
|
||||||
|
public:
|
||||||
|
MailBox(); // Allocate and initialize mail box
|
||||||
|
~MailBox(); // De-allocate mail box
|
||||||
|
|
||||||
|
void Put(PacketHeader pktHdr, MailHeader mailHdr, char *data);
|
||||||
|
// Atomically put a message into the mailbox
|
||||||
|
void Get(PacketHeader *pktHdr, MailHeader *mailHdr, char *data);
|
||||||
|
// Atomically get a message out of the
|
||||||
|
// mailbox (and wait if there is no message
|
||||||
|
// to get!)
|
||||||
|
private:
|
||||||
|
SynchList<Mail *> *messages; // A mailbox is just a list of arrived messages
|
||||||
|
};
|
||||||
|
|
||||||
|
// The following two classes defines a "Post Office", or a collection of
|
||||||
|
// mailboxes. The Post Office provides two main operations:
|
||||||
|
// Send -- send a message to a mailbox on a remote machine
|
||||||
|
// Receive -- wait until a message is in the mailbox, then remove and
|
||||||
|
// return it.
|
||||||
|
//
|
||||||
|
// Incoming messages are put by the PostOffice into the
|
||||||
|
// appropriate mailbox, waking up any threads waiting on Receive.
|
||||||
|
|
||||||
|
class PostOfficeInput : public CallBackObj {
|
||||||
|
public:
|
||||||
|
PostOfficeInput(int nBoxes); // Allocate and initialize Post Office
|
||||||
|
~PostOfficeInput(); // De-allocate Post Office data
|
||||||
|
|
||||||
|
void Receive(int box, PacketHeader *pktHdr,
|
||||||
|
MailHeader *mailHdr, char *data);
|
||||||
|
// Retrieve a message from "box". Wait if
|
||||||
|
// there is no message in the box.
|
||||||
|
|
||||||
|
static void PostalDelivery(void* data);
|
||||||
|
// Wait for incoming messages,
|
||||||
|
// and then put them in the correct mailbox
|
||||||
|
|
||||||
|
void CallBack(); // Called when incoming packet has arrived
|
||||||
|
// and can be pulled off of network
|
||||||
|
// (i.e., time to call PostalDelivery)
|
||||||
|
|
||||||
|
private:
|
||||||
|
NetworkInput *network; // Physical network connection
|
||||||
|
MailBox *boxes; // Table of mail boxes to hold incoming mail
|
||||||
|
int numBoxes; // Number of mail boxes
|
||||||
|
Semaphore *messageAvailable;// V'ed when message has arrived from network
|
||||||
|
};
|
||||||
|
|
||||||
|
class PostOfficeOutput : public CallBackObj {
|
||||||
|
public:
|
||||||
|
PostOfficeOutput(double reliability);
|
||||||
|
// Allocate and initialize output
|
||||||
|
// "reliability" is how many packets
|
||||||
|
// get dropped by the underlying network
|
||||||
|
~PostOfficeOutput(); // De-allocate Post Office data
|
||||||
|
|
||||||
|
void Send(PacketHeader pktHdr, MailHeader mailHdr, char *data);
|
||||||
|
// Send a message to a mailbox on a remote
|
||||||
|
// machine. The fromBox in the MailHeader is
|
||||||
|
// the return box for ack's.
|
||||||
|
|
||||||
|
void CallBack(); // Called when outgoing packet has been
|
||||||
|
// put on network; next packet can now be sent
|
||||||
|
|
||||||
|
private:
|
||||||
|
NetworkOutput *network; // Physical network connection
|
||||||
|
Semaphore *messageSent; // V'ed when next message can be sent to network
|
||||||
|
Lock *sendLock; // Only one outgoing message at a time
|
||||||
|
};
|
||||||
|
#endif
|
||||||
12
code/test/AFS_students.sh
Executable file
12
code/test/AFS_students.sh
Executable file
@@ -0,0 +1,12 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
make clean
|
||||||
|
make
|
||||||
|
|
||||||
|
../build.linux/nachos -e consoleIO_test1
|
||||||
|
echo "====================================="
|
||||||
|
../build.linux/nachos -e consoleIO_test2
|
||||||
|
echo "====================================="
|
||||||
|
../build.linux/nachos -e fileIO_test1
|
||||||
|
echo "====================================="
|
||||||
|
../build.linux/nachos -e fileIO_test2
|
||||||
|
echo "====================================="
|
||||||
BIN
code/test/DISK_0
Executable file
BIN
code/test/DISK_0
Executable file
Binary file not shown.
165
code/test/Makefile
Executable file
165
code/test/Makefile
Executable file
@@ -0,0 +1,165 @@
|
|||||||
|
#
|
||||||
|
# Makefile for building user programs to run on top of Nachos
|
||||||
|
#
|
||||||
|
# Use "make" to build the test executable(s)
|
||||||
|
# Use "make clean" to remove .o files and .coff files
|
||||||
|
# Use "make distclean" to remove all files produced by make, including
|
||||||
|
# the test executables
|
||||||
|
#
|
||||||
|
# This is a GNU Makefile. It must be used with the GNU make program.
|
||||||
|
# At UW, the GNU make program is /software/gnu/bin/make.
|
||||||
|
# In many other places it is known as "gmake".
|
||||||
|
# You may wish to include /software/gnu/bin/ early in your command
|
||||||
|
# search path, so that you will be using GNU make when you type "make".
|
||||||
|
#
|
||||||
|
# Several things to be aware of:
|
||||||
|
#
|
||||||
|
# It should not be necessary to build the test executables for
|
||||||
|
# every type of host machine on which Nachos runs. You should
|
||||||
|
# be able to build them once, and then use them regardless of
|
||||||
|
# the host machine type. That is because the test executables
|
||||||
|
# run on the simulated MIPS machine, and not on the host.
|
||||||
|
#
|
||||||
|
# However:
|
||||||
|
# (1) if you are experiencing problems with the test executables,
|
||||||
|
# it would be prudent to rebuild them on the host machine
|
||||||
|
# on which you are currently running Nachos. To do this,
|
||||||
|
# just type "make distclean", and then "make"
|
||||||
|
#
|
||||||
|
# (2) the procedure used to build the test executables does
|
||||||
|
# depend on the host machine you are on. All of the machine
|
||||||
|
# dependencies are isolated in the Makefile.dep file.
|
||||||
|
# It should be possible to build the test executables on
|
||||||
|
# any MFCF machine. In the MFCF environment, this makefile
|
||||||
|
# should automatically figure out what type of host you are
|
||||||
|
# on, and should use the appropriate procedure.
|
||||||
|
# However, if you are working outside the MFCF environment,
|
||||||
|
# you will need to build a cross-compiler, build coff2noff,
|
||||||
|
# and edit Makefile.dep in this directory before you
|
||||||
|
# can build the test programs.
|
||||||
|
#
|
||||||
|
# Nachos assumes that the location of the program startup routine (the
|
||||||
|
# location the kernel jumps to when the program initially starts up)
|
||||||
|
# is at location 0. This means: start.o must be the first .o passed
|
||||||
|
# to ld, in order for the routine "Start" to be loaded at location 0
|
||||||
|
#
|
||||||
|
# When you make the test programs, you will see messages like these:
|
||||||
|
# numsections 3
|
||||||
|
# Loading 3 sections:
|
||||||
|
# ".text", filepos 0xd0, mempos 0x0, size 0x440
|
||||||
|
# ".data", filepos 0x510, mempos 0x440, size 0x0
|
||||||
|
# ".bss", filepos 0x0, mempos 0x440, size 0x12c0
|
||||||
|
# These messages are normal. They come from the coff2noff program.
|
||||||
|
# They are useful in that they tell you how big the various parts of your
|
||||||
|
# compiled user program are, and where in the address space
|
||||||
|
# coff2noff is going to place them. This information is also
|
||||||
|
# recorded in the header of the executable file that coff2noff
|
||||||
|
# creates. See the method AddrSpace::Load (in userprog/addrspace.cc)
|
||||||
|
# for an example of how this header is used by the Nachos OS to set up the
|
||||||
|
# address space for a new process that will run the executable.
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Adding New Test Programs:
|
||||||
|
#
|
||||||
|
# You are free to write new test programs, and to modify the
|
||||||
|
# existing programs. If you write a new program, you will
|
||||||
|
# need to modify this makefile so that the new program will
|
||||||
|
# get built.
|
||||||
|
# You will need to make the following changes for each program
|
||||||
|
# you add:
|
||||||
|
# (1) add the program's name to PROGRAMS variable definition
|
||||||
|
# (2) add dependencies and build commands for the new
|
||||||
|
# program. The easiest way to do this is to
|
||||||
|
# copy the dependencies and commands for an
|
||||||
|
# existing program, and then change the names.
|
||||||
|
#
|
||||||
|
# For example, if you write a test program in foo.c, for which
|
||||||
|
# the executable is to be called foo, you should do the following:
|
||||||
|
#
|
||||||
|
# change the PROGRAMS definition to look like this:
|
||||||
|
#
|
||||||
|
# PROGRAMS = halt shell matmult sort foo
|
||||||
|
#
|
||||||
|
# add these dependencies/commands:
|
||||||
|
#
|
||||||
|
# foo.o: foo.c
|
||||||
|
# $(CC) $(CFLAGS) -c foo.c
|
||||||
|
# foo: foo.o start.o
|
||||||
|
# $(LD) $(LDFLAGS) start.o foo.o -o foo.coff
|
||||||
|
# $(COFF2NOFF) foo.coff foo
|
||||||
|
#
|
||||||
|
# Be careful when you copy the commands! The commands
|
||||||
|
# must be indented with a *TAB*, not a bunch of spaces.
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#############################################################################
|
||||||
|
# Makefile.dep contains all machine-dependent definitions
|
||||||
|
# If you are trying to build coff2noff somewhere outside
|
||||||
|
# of the MFCF environment, you will almost certainly want
|
||||||
|
# to visit and edit Makefile.dep before doing so
|
||||||
|
#############################################################################
|
||||||
|
|
||||||
|
include Makefile.dep
|
||||||
|
|
||||||
|
CC = $(GCCDIR)gcc
|
||||||
|
AS = $(GCCDIR)as
|
||||||
|
LD = $(GCCDIR)ld
|
||||||
|
|
||||||
|
INCDIR =-I../userprog -I../lib
|
||||||
|
CFLAGS = -G 0 -c $(INCDIR) -B/usr/bin/local/nachos/lib/gcc-lib/decstation-ultrix/2.95.2/ -B/usr/bin/local/nachos/decstation-ultrix/bin/
|
||||||
|
|
||||||
|
ifeq ($(hosttype),unknown)
|
||||||
|
PROGRAMS = unknownhost
|
||||||
|
else
|
||||||
|
# change this if you create a new test program!
|
||||||
|
PROGRAMS = halt consoleIO_test1 consoleIO_test2 fileIO_test1 fileIO_test2
|
||||||
|
endif
|
||||||
|
|
||||||
|
all: $(PROGRAMS)
|
||||||
|
|
||||||
|
start.o: start.S ../userprog/syscall.h
|
||||||
|
$(CC) $(CFLAGS) $(ASFLAGS) -c start.S
|
||||||
|
|
||||||
|
halt.o: halt.c
|
||||||
|
$(CC) $(CFLAGS) -c halt.c
|
||||||
|
halt: halt.o start.o
|
||||||
|
$(LD) $(LDFLAGS) start.o halt.o -o halt.coff
|
||||||
|
$(COFF2NOFF) halt.coff halt
|
||||||
|
|
||||||
|
consoleIO_test1.o: consoleIO_test1.c
|
||||||
|
$(CC) $(CFLAGS) -c consoleIO_test1.c
|
||||||
|
consoleIO_test1: consoleIO_test1.o start.o
|
||||||
|
$(LD) $(LDFLAGS) start.o consoleIO_test1.o -o consoleIO_test1.coff
|
||||||
|
$(COFF2NOFF) consoleIO_test1.coff consoleIO_test1
|
||||||
|
|
||||||
|
consoleIO_test2.o: consoleIO_test2.c
|
||||||
|
$(CC) $(CFLAGS) -c consoleIO_test2.c
|
||||||
|
consoleIO_test2: consoleIO_test2.o start.o
|
||||||
|
$(LD) $(LDFLAGS) start.o consoleIO_test2.o -o consoleIO_test2.coff
|
||||||
|
$(COFF2NOFF) consoleIO_test2.coff consoleIO_test2
|
||||||
|
|
||||||
|
fileIO_test1.o: fileIO_test1.c
|
||||||
|
$(CC) $(CFLAGS) -c fileIO_test1.c
|
||||||
|
fileIO_test1: fileIO_test1.o start.o
|
||||||
|
$(LD) $(LDFLAGS) start.o fileIO_test1.o -o fileIO_test1.coff
|
||||||
|
$(COFF2NOFF) fileIO_test1.coff fileIO_test1
|
||||||
|
|
||||||
|
fileIO_test2.o: fileIO_test2.c
|
||||||
|
$(CC) $(CFLAGS) -c fileIO_test2.c
|
||||||
|
fileIO_test2: fileIO_test2.o start.o
|
||||||
|
$(LD) $(LDFLAGS) start.o fileIO_test2.o -o fileIO_test2.coff
|
||||||
|
$(COFF2NOFF) fileIO_test2.coff fileIO_test2
|
||||||
|
|
||||||
|
|
||||||
|
clean:
|
||||||
|
$(RM) -f *.o *.ii
|
||||||
|
$(RM) -f *.coff
|
||||||
|
|
||||||
|
distclean: clean
|
||||||
|
$(RM) -f $(PROGRAMS)
|
||||||
|
|
||||||
|
unknownhost:
|
||||||
|
@echo Host type could not be determined.
|
||||||
|
@echo make is terminating.
|
||||||
|
@echo If you are on an MFCF machine, contact the instructor to report this problem
|
||||||
|
@echo Otherwise, edit Makefile.dep and try again.
|
||||||
63
code/test/Makefile.dep
Executable file
63
code/test/Makefile.dep
Executable file
@@ -0,0 +1,63 @@
|
|||||||
|
#############################################################################
|
||||||
|
# Machine-specific definitions
|
||||||
|
#
|
||||||
|
# If you are not in the MFCF environment, you can either add a new
|
||||||
|
# automatic test for your machine/OS type, or you should set the
|
||||||
|
# necessary variables "manually" here
|
||||||
|
#############################################################################
|
||||||
|
|
||||||
|
# unfortunately, command line arguments to uname are not
|
||||||
|
# very consistent across UNIX flavours. However, the following
|
||||||
|
# seem to work almost everywhere in MFCF land
|
||||||
|
|
||||||
|
osname = $(shell uname -s)
|
||||||
|
osrelease = $(shell uname -r)
|
||||||
|
|
||||||
|
hosttype = unknown
|
||||||
|
|
||||||
|
# Test for x86 Linux
|
||||||
|
# !!! COMMENT THE FOLLOWING LINES OUT IF BUILDING FOR SOLARIS HOST !!!
|
||||||
|
# !!! ADD PATH TO CPP and CROSS COMPILER
|
||||||
|
|
||||||
|
ifeq ($(osname),Linux)
|
||||||
|
# full path name of your cpp program i.e.:
|
||||||
|
CPP = ../../usr/local/nachos/lib/gcc-lib/decstation-ultrix/2.95.2/cpp
|
||||||
|
# directory in which your gcc cross-compiler lives i.e.:
|
||||||
|
GCCDIR = ../../usr/local/nachos/bin/decstation-ultrix-
|
||||||
|
LDFLAGS = -T script -N
|
||||||
|
ASFLAGS = -mips2
|
||||||
|
CPPFLAGS = $(INCDIR)
|
||||||
|
COFF2NOFF = ../../coff2noff/coff2noff.x86Linux
|
||||||
|
hosttype = x86Linux
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(osname),Windows)
|
||||||
|
CPP = /usr/local/nachosxdev/lib/gcc-lib/decstation-ultrix/2.95.3/cpp0
|
||||||
|
# directory in which your gcc cross-compiler lives i.e.:
|
||||||
|
GCCDIR = /usr/local/nachosxdev/bin/decstation-ultrix-
|
||||||
|
LDFLAGS = -T script -N
|
||||||
|
ASFLAGS = -mips2
|
||||||
|
CPPFLAGS = $(INCDIR)
|
||||||
|
COFF2NOFF = ../../coff2noff/coff2noff.Windows
|
||||||
|
hosttype = Windows
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Note:
|
||||||
|
# If you are trying to build on MacOS X
|
||||||
|
# try something like this, substituting whatever
|
||||||
|
# uname -s returns on your machine for the XXX
|
||||||
|
#
|
||||||
|
#ifeq ($(osname),XXX)
|
||||||
|
#CPP = full path name of your cpp program
|
||||||
|
#GCCDIR = directory in which your gcc cross-compiler lives
|
||||||
|
#LDFLAGS = -T script -N
|
||||||
|
#ASFLAGS = -mips2
|
||||||
|
#CPPFLAGS = $(INCDIR)
|
||||||
|
#COFF2NOFF = full pathname of your coff2noff program
|
||||||
|
# Note: it has been moved to part of the Nachos distribution
|
||||||
|
# COFF2NOFF = ../../coff2noff.mipsUltrix
|
||||||
|
#hosttype = MacOS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
7
code/test/compile.sh
Executable file
7
code/test/compile.sh
Executable file
@@ -0,0 +1,7 @@
|
|||||||
|
cd ../build.linux
|
||||||
|
make depend
|
||||||
|
make clean
|
||||||
|
make
|
||||||
|
cd ../test
|
||||||
|
make clean
|
||||||
|
make
|
||||||
12
code/test/consoleIO_test1.c
Executable file
12
code/test/consoleIO_test1.c
Executable file
@@ -0,0 +1,12 @@
|
|||||||
|
#include "syscall.h"
|
||||||
|
|
||||||
|
int
|
||||||
|
main()
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
for (n=9;n>5;n--) {
|
||||||
|
PrintInt(n);
|
||||||
|
}
|
||||||
|
Halt();
|
||||||
|
}
|
||||||
|
|
||||||
13
code/test/consoleIO_test2.c
Executable file
13
code/test/consoleIO_test2.c
Executable file
@@ -0,0 +1,13 @@
|
|||||||
|
#include "syscall.h"
|
||||||
|
|
||||||
|
int
|
||||||
|
main()
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
for (n=15;n<=19;n++){
|
||||||
|
|
||||||
|
PrintInt(n);
|
||||||
|
}
|
||||||
|
Halt();
|
||||||
|
}
|
||||||
|
|
||||||
20
code/test/fileIO_test1.c
Executable file
20
code/test/fileIO_test1.c
Executable file
@@ -0,0 +1,20 @@
|
|||||||
|
#include "syscall.h"
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
char test[] = "abcdefghijklmnopqrstuvwxyz";
|
||||||
|
int success = Create("file1.test");
|
||||||
|
OpenFileId fid;
|
||||||
|
int i;
|
||||||
|
if (success != 1) MSG("Failed on creating file");
|
||||||
|
fid = Open("file1.test");
|
||||||
|
if (fid <= 0) MSG("Failed on opening file");
|
||||||
|
for (i = 0; i < 26; ++i) {
|
||||||
|
int count = Write(test + i, 1, fid);
|
||||||
|
if (count != 1) MSG("Failed on writing file");
|
||||||
|
}
|
||||||
|
success = Close(fid);
|
||||||
|
if (success != 1) MSG("Failed on closing file");
|
||||||
|
Halt();
|
||||||
|
}
|
||||||
|
|
||||||
22
code/test/fileIO_test2.c
Executable file
22
code/test/fileIO_test2.c
Executable file
@@ -0,0 +1,22 @@
|
|||||||
|
#include "syscall.h"
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
// you should run fileIO_test1 first before running this one
|
||||||
|
char test[26];
|
||||||
|
char check[] = "abcdefghijklmnopqrstuvwxyz";
|
||||||
|
OpenFileId fid;
|
||||||
|
int count, success, i;
|
||||||
|
fid = Open("file1.test");
|
||||||
|
if (fid <= 0) MSG("Failed on opening file");
|
||||||
|
count = Read(test, 26, fid);
|
||||||
|
if (count != 26) MSG("Failed on reading file");
|
||||||
|
success = Close(fid);
|
||||||
|
if (success != 1) MSG("Failed on closing file");
|
||||||
|
for (i = 0; i < 26; ++i) {
|
||||||
|
if (test[i] != check[i]) MSG("Failed: reading wrong result");
|
||||||
|
}
|
||||||
|
MSG("Passed! ^_^");
|
||||||
|
Halt();
|
||||||
|
}
|
||||||
|
|
||||||
20
code/test/halt.c
Executable file
20
code/test/halt.c
Executable file
@@ -0,0 +1,20 @@
|
|||||||
|
/* halt.c
|
||||||
|
* Simple program to test whether running a user program works.
|
||||||
|
*
|
||||||
|
* Just do a "syscall" that shuts down the OS.
|
||||||
|
*
|
||||||
|
* NOTE: for some reason, user programs with global data structures
|
||||||
|
* sometimes haven't worked in the Nachos environment. So be careful
|
||||||
|
* out there! One option is to allocate data structures as
|
||||||
|
* automatics within a procedure, but if you do this, you have to
|
||||||
|
* be careful to allocate a big enough stack to hold the automatics!
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "syscall.h"
|
||||||
|
|
||||||
|
int
|
||||||
|
main()
|
||||||
|
{
|
||||||
|
Halt();
|
||||||
|
/* not reached */
|
||||||
|
}
|
||||||
36
code/test/script
Executable file
36
code/test/script
Executable file
@@ -0,0 +1,36 @@
|
|||||||
|
OUTPUT_FORMAT("ecoff-littlemips")
|
||||||
|
ENTRY(__start)
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
.text 0 : {
|
||||||
|
_ftext = . ;
|
||||||
|
*(.init)
|
||||||
|
eprol = .;
|
||||||
|
*(.text)
|
||||||
|
*(.fini)
|
||||||
|
etext = .;
|
||||||
|
_etext = .;
|
||||||
|
}
|
||||||
|
.rdata . : {
|
||||||
|
*(.rdata)
|
||||||
|
}
|
||||||
|
_fdata = .;
|
||||||
|
.data . : {
|
||||||
|
*(.data)
|
||||||
|
CONSTRUCTORS
|
||||||
|
}
|
||||||
|
edata = .;
|
||||||
|
_edata = .;
|
||||||
|
_fbss = .;
|
||||||
|
.sbss . : {
|
||||||
|
*(.sbss)
|
||||||
|
*(.scommon)
|
||||||
|
}
|
||||||
|
.bss . : {
|
||||||
|
*(.bss)
|
||||||
|
*(COMMON)
|
||||||
|
}
|
||||||
|
end = .;
|
||||||
|
_end = .;
|
||||||
|
}
|
||||||
|
|
||||||
196
code/test/start.S
Executable file
196
code/test/start.S
Executable file
@@ -0,0 +1,196 @@
|
|||||||
|
/* Start.s
|
||||||
|
* Assembly language assist for user programs running on top of Nachos.
|
||||||
|
*
|
||||||
|
* Since we don't want to pull in the entire C library, we define
|
||||||
|
* what we need for a user program here, namely Start and the system
|
||||||
|
* calls.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define IN_ASM
|
||||||
|
#include "syscall.h"
|
||||||
|
|
||||||
|
.text
|
||||||
|
.align 2
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------
|
||||||
|
* __start
|
||||||
|
* Initialize running a C program, by calling "main".
|
||||||
|
*
|
||||||
|
* NOTE: This has to be first, so that it gets loaded at location 0.
|
||||||
|
* The Nachos kernel always starts a program by jumping to location 0.
|
||||||
|
* -------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
.globl __start
|
||||||
|
.ent __start
|
||||||
|
__start:
|
||||||
|
jal main
|
||||||
|
move $4,$0
|
||||||
|
jal Exit /* if we return from main, exit(0) */
|
||||||
|
.end __start
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------
|
||||||
|
* System call stubs:
|
||||||
|
* Assembly language assist to make system calls to the Nachos kernel.
|
||||||
|
* There is one stub per system call, that places the code for the
|
||||||
|
* system call into register r2, and leaves the arguments to the
|
||||||
|
* system call alone (in other words, arg1 is in r4, arg2 is
|
||||||
|
* in r5, arg3 is in r6, arg4 is in r7)
|
||||||
|
*
|
||||||
|
* The return value is in r2. This follows the standard C calling
|
||||||
|
* convention on the MIPS.
|
||||||
|
* -------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
.globl Halt
|
||||||
|
.ent Halt
|
||||||
|
Halt:
|
||||||
|
addiu $2,$0,SC_Halt
|
||||||
|
syscall
|
||||||
|
j $31
|
||||||
|
.end Halt
|
||||||
|
|
||||||
|
.globl MSG
|
||||||
|
.ent MSG
|
||||||
|
MSG:
|
||||||
|
addiu $2,$0,SC_MSG
|
||||||
|
syscall
|
||||||
|
j $31
|
||||||
|
.end MSG
|
||||||
|
|
||||||
|
.globl Add
|
||||||
|
.ent Add
|
||||||
|
Add:
|
||||||
|
addiu $2,$0,SC_Add
|
||||||
|
syscall
|
||||||
|
j $31
|
||||||
|
.end Add
|
||||||
|
|
||||||
|
.globl Exit
|
||||||
|
.ent Exit
|
||||||
|
Exit:
|
||||||
|
addiu $2,$0,SC_Exit
|
||||||
|
syscall
|
||||||
|
j $31
|
||||||
|
.end Exit
|
||||||
|
|
||||||
|
.globl Exec
|
||||||
|
.ent Exec
|
||||||
|
Exec:
|
||||||
|
addiu $2,$0,SC_Exec
|
||||||
|
syscall
|
||||||
|
j $31
|
||||||
|
.end Exec
|
||||||
|
|
||||||
|
.globl ExecV
|
||||||
|
.ent ExecV
|
||||||
|
ExecV:
|
||||||
|
addiu $2,$0,SC_ExecV
|
||||||
|
syscall
|
||||||
|
j $31
|
||||||
|
.end ExecV
|
||||||
|
|
||||||
|
.globl Join
|
||||||
|
.ent Join
|
||||||
|
Join:
|
||||||
|
addiu $2,$0,SC_Join
|
||||||
|
syscall
|
||||||
|
j $31
|
||||||
|
.end Join
|
||||||
|
|
||||||
|
.globl Create
|
||||||
|
.ent Create
|
||||||
|
Create:
|
||||||
|
addiu $2,$0,SC_Create
|
||||||
|
syscall
|
||||||
|
j $31
|
||||||
|
.end Create
|
||||||
|
|
||||||
|
.globl Remove
|
||||||
|
.ent Remove
|
||||||
|
Remove:
|
||||||
|
addiu $2,$0,SC_Remove
|
||||||
|
syscall
|
||||||
|
j $31
|
||||||
|
.end Remove
|
||||||
|
|
||||||
|
.globl Open
|
||||||
|
.ent Open
|
||||||
|
Open:
|
||||||
|
addiu $2,$0,SC_Open
|
||||||
|
syscall
|
||||||
|
j $31
|
||||||
|
.end Open
|
||||||
|
|
||||||
|
.globl Read
|
||||||
|
.ent Read
|
||||||
|
Read:
|
||||||
|
addiu $2,$0,SC_Read
|
||||||
|
syscall
|
||||||
|
j $31
|
||||||
|
.end Read
|
||||||
|
|
||||||
|
.globl Write
|
||||||
|
.ent Write
|
||||||
|
Write:
|
||||||
|
addiu $2,$0,SC_Write
|
||||||
|
syscall
|
||||||
|
j $31
|
||||||
|
.end Write
|
||||||
|
|
||||||
|
.globl Close
|
||||||
|
.ent Close
|
||||||
|
Close:
|
||||||
|
addiu $2,$0,SC_Close
|
||||||
|
syscall
|
||||||
|
j $31
|
||||||
|
.end Close
|
||||||
|
|
||||||
|
.globl Seek
|
||||||
|
.ent Seek
|
||||||
|
Seek:
|
||||||
|
addiu $2,$0,SC_Seek
|
||||||
|
syscall
|
||||||
|
j $31
|
||||||
|
.end Seek
|
||||||
|
|
||||||
|
.globl ThreadFork
|
||||||
|
.ent ThreadFork
|
||||||
|
ThreadFork:
|
||||||
|
addiu $2,$0,SC_ThreadFork
|
||||||
|
syscall
|
||||||
|
j $31
|
||||||
|
.end ThreadFork
|
||||||
|
|
||||||
|
.globl ThreadYield
|
||||||
|
.ent ThreadYield
|
||||||
|
ThreadYield:
|
||||||
|
addiu $2,$0,SC_ThreadYield
|
||||||
|
syscall
|
||||||
|
j $31
|
||||||
|
.end ThreadYield
|
||||||
|
|
||||||
|
.globl ThreadExit
|
||||||
|
.ent ThreadExit
|
||||||
|
ThreadExit:
|
||||||
|
addiu $2, $0, SC_ThreadExit
|
||||||
|
syscall
|
||||||
|
j $31
|
||||||
|
.end ThreadExit
|
||||||
|
|
||||||
|
.globl ThreadJoin
|
||||||
|
.ent ThreadJoin
|
||||||
|
ThreadJoin:
|
||||||
|
addiu $2, $0, SC_ThreadJoin
|
||||||
|
syscall
|
||||||
|
j $31
|
||||||
|
.end ThreadJoin
|
||||||
|
|
||||||
|
|
||||||
|
/* dummy function to keep gcc happy */
|
||||||
|
.globl __main
|
||||||
|
.ent __main
|
||||||
|
__main:
|
||||||
|
j $31
|
||||||
|
.end __main
|
||||||
|
|
||||||
55
code/threads/alarm.cc
Executable file
55
code/threads/alarm.cc
Executable file
@@ -0,0 +1,55 @@
|
|||||||
|
// alarm.cc
|
||||||
|
// Routines to use a hardware timer device to provide a
|
||||||
|
// software alarm clock. For now, we just provide time-slicing.
|
||||||
|
//
|
||||||
|
// Not completely implemented.
|
||||||
|
//
|
||||||
|
// Copyright (c) 1992-1996 The Regents of the University of California.
|
||||||
|
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||||
|
// of liability and disclaimer of warranty provisions.
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
#include "alarm.h"
|
||||||
|
#include "main.h"
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Alarm::Alarm
|
||||||
|
// Initialize a software alarm clock. Start up a timer device
|
||||||
|
//
|
||||||
|
// "doRandom" -- if true, arrange for the hardware interrupts to
|
||||||
|
// occur at random, instead of fixed, intervals.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
Alarm::Alarm(bool doRandom)
|
||||||
|
{
|
||||||
|
timer = new Timer(doRandom, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Alarm::CallBack
|
||||||
|
// Software interrupt handler for the timer device. The timer device is
|
||||||
|
// set up to interrupt the CPU periodically (once every TimerTicks).
|
||||||
|
// This routine is called each time there is a timer interrupt,
|
||||||
|
// with interrupts disabled.
|
||||||
|
//
|
||||||
|
// Note that instead of calling Yield() directly (which would
|
||||||
|
// suspend the interrupt handler, not the interrupted thread
|
||||||
|
// which is what we wanted to context switch), we set a flag
|
||||||
|
// so that once the interrupt handler is done, it will appear as
|
||||||
|
// if the interrupted thread called Yield at the point it is
|
||||||
|
// was interrupted.
|
||||||
|
//
|
||||||
|
// For now, just provide time-slicing. Only need to time slice
|
||||||
|
// if we're currently running something (in other words, not idle).
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
Alarm::CallBack()
|
||||||
|
{
|
||||||
|
Interrupt *interrupt = kernel->interrupt;
|
||||||
|
MachineStatus status = interrupt->getStatus();
|
||||||
|
|
||||||
|
if (status != IdleMode) {
|
||||||
|
interrupt->YieldOnReturn();
|
||||||
|
}
|
||||||
|
}
|
||||||
42
code/threads/alarm.h
Executable file
42
code/threads/alarm.h
Executable file
@@ -0,0 +1,42 @@
|
|||||||
|
// alarm.h
|
||||||
|
// Data structures for a software alarm clock.
|
||||||
|
//
|
||||||
|
// We make use of a hardware timer device, that generates
|
||||||
|
// an interrupt every X time ticks (on real systems, X is
|
||||||
|
// usually between 0.25 - 10 milliseconds).
|
||||||
|
//
|
||||||
|
// From this, we provide the ability for a thread to be
|
||||||
|
// woken up after a delay; we also provide time-slicing.
|
||||||
|
//
|
||||||
|
// NOTE: this abstraction is not completely implemented.
|
||||||
|
//
|
||||||
|
// Copyright (c) 1992-1996 The Regents of the University of California.
|
||||||
|
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||||
|
// of liability and disclaimer of warranty provisions.
|
||||||
|
|
||||||
|
#ifndef ALARM_H
|
||||||
|
#define ALARM_H
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
#include "utility.h"
|
||||||
|
#include "callback.h"
|
||||||
|
#include "timer.h"
|
||||||
|
|
||||||
|
// The following class defines a software alarm clock.
|
||||||
|
class Alarm : public CallBackObj {
|
||||||
|
public:
|
||||||
|
Alarm(bool doRandomYield); // Initialize the timer, and callback
|
||||||
|
// to "toCall" every time slice.
|
||||||
|
~Alarm() { delete timer; }
|
||||||
|
|
||||||
|
void WaitUntil(int x); // suspend execution until time > now + x
|
||||||
|
// this method is not yet implemented
|
||||||
|
|
||||||
|
private:
|
||||||
|
Timer *timer; // the hardware timer device
|
||||||
|
|
||||||
|
void CallBack(); // called when the hardware
|
||||||
|
// timer generates an interrupt
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ALARM_H
|
||||||
311
code/threads/kernel.cc
Executable file
311
code/threads/kernel.cc
Executable file
@@ -0,0 +1,311 @@
|
|||||||
|
// kernel.cc
|
||||||
|
// Initialization and cleanup routines for the Nachos kernel.
|
||||||
|
//
|
||||||
|
// Copyright (c) 1992-1996 The Regents of the University of California.
|
||||||
|
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||||
|
// of liability and disclaimer of warranty provisions.
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
#include "debug.h"
|
||||||
|
#include "main.h"
|
||||||
|
#include "kernel.h"
|
||||||
|
#include "sysdep.h"
|
||||||
|
#include "synch.h"
|
||||||
|
#include "synchlist.h"
|
||||||
|
#include "libtest.h"
|
||||||
|
#include "string.h"
|
||||||
|
#include "synchdisk.h"
|
||||||
|
#include "post.h"
|
||||||
|
#include "synchconsole.h"
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Kernel::Kernel
|
||||||
|
// Interpret command line arguments in order to determine flags
|
||||||
|
// for the initialization (see also comments in main.cc)
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
Kernel::Kernel(int argc, char **argv)
|
||||||
|
{
|
||||||
|
randomSlice = FALSE;
|
||||||
|
debugUserProg = FALSE;
|
||||||
|
consoleIn = NULL; // default is stdin
|
||||||
|
consoleOut = NULL; // default is stdout
|
||||||
|
#ifndef FILESYS_STUB
|
||||||
|
formatFlag = FALSE;
|
||||||
|
#endif
|
||||||
|
reliability = 1; // network reliability, default is 1.0
|
||||||
|
hostName = 0; // machine id, also UNIX socket name
|
||||||
|
// 0 is the default machine id
|
||||||
|
for (int i = 1; i < argc; i++) {
|
||||||
|
if (strcmp(argv[i], "-rs") == 0) {
|
||||||
|
ASSERT(i + 1 < argc);
|
||||||
|
RandomInit(atoi(argv[i + 1]));// initialize pseudo-random
|
||||||
|
// number generator
|
||||||
|
randomSlice = TRUE;
|
||||||
|
i++;
|
||||||
|
} else if (strcmp(argv[i], "-s") == 0) {
|
||||||
|
debugUserProg = TRUE;
|
||||||
|
} else if (strcmp(argv[i], "-e") == 0) {
|
||||||
|
execfile[++execfileNum]= argv[++i];
|
||||||
|
cout << execfile[execfileNum] << endl;
|
||||||
|
} else if (strcmp(argv[i], "-ci") == 0) {
|
||||||
|
ASSERT(i + 1 < argc);
|
||||||
|
consoleIn = argv[i + 1];
|
||||||
|
i++;
|
||||||
|
} else if (strcmp(argv[i], "-co") == 0) {
|
||||||
|
ASSERT(i + 1 < argc);
|
||||||
|
consoleOut = argv[i + 1];
|
||||||
|
i++;
|
||||||
|
#ifndef FILESYS_STUB
|
||||||
|
} else if (strcmp(argv[i], "-f") == 0) {
|
||||||
|
formatFlag = TRUE;
|
||||||
|
#endif
|
||||||
|
} else if (strcmp(argv[i], "-n") == 0) {
|
||||||
|
ASSERT(i + 1 < argc); // next argument is float
|
||||||
|
reliability = atof(argv[i + 1]);
|
||||||
|
i++;
|
||||||
|
} else if (strcmp(argv[i], "-m") == 0) {
|
||||||
|
ASSERT(i + 1 < argc); // next argument is int
|
||||||
|
hostName = atoi(argv[i + 1]);
|
||||||
|
i++;
|
||||||
|
} else if (strcmp(argv[i], "-u") == 0) {
|
||||||
|
cout << "Partial usage: nachos [-rs randomSeed]\n";
|
||||||
|
cout << "Partial usage: nachos [-s]\n";
|
||||||
|
cout << "Partial usage: nachos [-ci consoleIn] [-co consoleOut]\n";
|
||||||
|
#ifndef FILESYS_STUB
|
||||||
|
cout << "Partial usage: nachos [-nf]\n";
|
||||||
|
#endif
|
||||||
|
cout << "Partial usage: nachos [-n #] [-m #]\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Kernel::Initialize
|
||||||
|
// Initialize Nachos global data structures. Separate from the
|
||||||
|
// constructor because some of these refer to earlier initialized
|
||||||
|
// data via the "kernel" global variable.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
Kernel::Initialize()
|
||||||
|
{
|
||||||
|
// We didn't explicitly allocate the current thread we are running in.
|
||||||
|
// But if it ever tries to give up the CPU, we better have a Thread
|
||||||
|
// object to save its state.
|
||||||
|
|
||||||
|
|
||||||
|
currentThread = new Thread("main", threadNum++);
|
||||||
|
currentThread->setStatus(RUNNING);
|
||||||
|
|
||||||
|
stats = new Statistics(); // collect statistics
|
||||||
|
interrupt = new Interrupt; // start up interrupt handling
|
||||||
|
scheduler = new Scheduler(); // initialize the ready queue
|
||||||
|
alarm = new Alarm(randomSlice); // start up time slicing
|
||||||
|
machine = new Machine(debugUserProg);
|
||||||
|
synchConsoleIn = new SynchConsoleInput(consoleIn); // input from stdin
|
||||||
|
synchConsoleOut = new SynchConsoleOutput(consoleOut); // output to stdout
|
||||||
|
synchDisk = new SynchDisk(); //
|
||||||
|
#ifdef FILESYS_STUB
|
||||||
|
fileSystem = new FileSystem();
|
||||||
|
#else
|
||||||
|
fileSystem = new FileSystem(formatFlag);
|
||||||
|
#endif // FILESYS_STUB
|
||||||
|
postOfficeIn = new PostOfficeInput(10);
|
||||||
|
postOfficeOut = new PostOfficeOutput(reliability);
|
||||||
|
|
||||||
|
interrupt->Enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Kernel::~Kernel
|
||||||
|
// Nachos is halting. De-allocate global data structures.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
Kernel::~Kernel()
|
||||||
|
{
|
||||||
|
delete stats;
|
||||||
|
delete interrupt;
|
||||||
|
delete scheduler;
|
||||||
|
delete alarm;
|
||||||
|
delete machine;
|
||||||
|
delete synchConsoleIn;
|
||||||
|
delete synchConsoleOut;
|
||||||
|
delete synchDisk;
|
||||||
|
delete fileSystem;
|
||||||
|
delete postOfficeIn;
|
||||||
|
delete postOfficeOut;
|
||||||
|
|
||||||
|
Exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Kernel::ThreadSelfTest
|
||||||
|
// Test threads, semaphores, synchlists
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
Kernel::ThreadSelfTest() {
|
||||||
|
Semaphore *semaphore;
|
||||||
|
SynchList<int> *synchList;
|
||||||
|
|
||||||
|
LibSelfTest(); // test library routines
|
||||||
|
|
||||||
|
currentThread->SelfTest(); // test thread switching
|
||||||
|
|
||||||
|
// test semaphore operation
|
||||||
|
semaphore = new Semaphore("test", 0);
|
||||||
|
semaphore->SelfTest();
|
||||||
|
delete semaphore;
|
||||||
|
|
||||||
|
// test locks, condition variables
|
||||||
|
// using synchronized lists
|
||||||
|
synchList = new SynchList<int>;
|
||||||
|
synchList->SelfTest(9);
|
||||||
|
delete synchList;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Kernel::ConsoleTest
|
||||||
|
// Test the synchconsole
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
Kernel::ConsoleTest() {
|
||||||
|
char ch;
|
||||||
|
|
||||||
|
cout << "Testing the console device.\n"
|
||||||
|
<< "Typed characters will be echoed, until ^D is typed.\n"
|
||||||
|
<< "Note newlines are needed to flush input through UNIX.\n";
|
||||||
|
cout.flush();
|
||||||
|
|
||||||
|
do {
|
||||||
|
ch = synchConsoleIn->GetChar();
|
||||||
|
if(ch != EOF) synchConsoleOut->PutChar(ch); // echo it!
|
||||||
|
} while (ch != EOF);
|
||||||
|
|
||||||
|
cout << "\n";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Kernel::NetworkTest
|
||||||
|
// Test whether the post office is working. On machines #0 and #1, do:
|
||||||
|
//
|
||||||
|
// 1. send a message to the other machine at mail box #0
|
||||||
|
// 2. wait for the other machine's message to arrive (in our mailbox #0)
|
||||||
|
// 3. send an acknowledgment for the other machine's message
|
||||||
|
// 4. wait for an acknowledgement from the other machine to our
|
||||||
|
// original message
|
||||||
|
//
|
||||||
|
// This test works best if each Nachos machine has its own window
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
Kernel::NetworkTest() {
|
||||||
|
|
||||||
|
if (hostName == 0 || hostName == 1) {
|
||||||
|
// if we're machine 1, send to 0 and vice versa
|
||||||
|
int farHost = (hostName == 0 ? 1 : 0);
|
||||||
|
PacketHeader outPktHdr, inPktHdr;
|
||||||
|
MailHeader outMailHdr, inMailHdr;
|
||||||
|
char *data = "Hello there!";
|
||||||
|
char *ack = "Got it!";
|
||||||
|
char buffer[MaxMailSize];
|
||||||
|
|
||||||
|
// construct packet, mail header for original message
|
||||||
|
// To: destination machine, mailbox 0
|
||||||
|
// From: our machine, reply to: mailbox 1
|
||||||
|
outPktHdr.to = farHost;
|
||||||
|
outMailHdr.to = 0;
|
||||||
|
outMailHdr.from = 1;
|
||||||
|
outMailHdr.length = strlen(data) + 1;
|
||||||
|
|
||||||
|
// Send the first message
|
||||||
|
postOfficeOut->Send(outPktHdr, outMailHdr, data);
|
||||||
|
|
||||||
|
// Wait for the first message from the other machine
|
||||||
|
postOfficeIn->Receive(0, &inPktHdr, &inMailHdr, buffer);
|
||||||
|
cout << "Got: " << buffer << " : from " << inPktHdr.from << ", box "
|
||||||
|
<< inMailHdr.from << "\n";
|
||||||
|
cout.flush();
|
||||||
|
|
||||||
|
// Send acknowledgement to the other machine (using "reply to" mailbox
|
||||||
|
// in the message that just arrived
|
||||||
|
outPktHdr.to = inPktHdr.from;
|
||||||
|
outMailHdr.to = inMailHdr.from;
|
||||||
|
outMailHdr.length = strlen(ack) + 1;
|
||||||
|
postOfficeOut->Send(outPktHdr, outMailHdr, ack);
|
||||||
|
|
||||||
|
// Wait for the ack from the other machine to the first message we sent
|
||||||
|
postOfficeIn->Receive(1, &inPktHdr, &inMailHdr, buffer);
|
||||||
|
cout << "Got: " << buffer << " : from " << inPktHdr.from << ", box "
|
||||||
|
<< inMailHdr.from << "\n";
|
||||||
|
cout.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then we're done!
|
||||||
|
}
|
||||||
|
|
||||||
|
void ForkExecute(Thread *t)
|
||||||
|
{
|
||||||
|
if ( !t->space->Load(t->getName()) ) {
|
||||||
|
return; // executable not found
|
||||||
|
}
|
||||||
|
|
||||||
|
t->space->Execute(t->getName());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Kernel::ExecAll()
|
||||||
|
{
|
||||||
|
for (int i=1;i<=execfileNum;i++) {
|
||||||
|
int a = Exec(execfile[i]);
|
||||||
|
}
|
||||||
|
currentThread->Finish();
|
||||||
|
//Kernel::Exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int Kernel::Exec(char* name)
|
||||||
|
{
|
||||||
|
t[threadNum] = new Thread(name, threadNum);
|
||||||
|
t[threadNum]->space = new AddrSpace();
|
||||||
|
t[threadNum]->Fork((VoidFunctionPtr) &ForkExecute, (void *)t[threadNum]);
|
||||||
|
threadNum++;
|
||||||
|
|
||||||
|
return threadNum-1;
|
||||||
|
/*
|
||||||
|
cout << "Total threads number is " << execfileNum << endl;
|
||||||
|
for (int n=1;n<=execfileNum;n++) {
|
||||||
|
t[n] = new Thread(execfile[n]);
|
||||||
|
t[n]->space = new AddrSpace();
|
||||||
|
t[n]->Fork((VoidFunctionPtr) &ForkExecute, (void *)t[n]);
|
||||||
|
cout << "Thread " << execfile[n] << " is executing." << endl;
|
||||||
|
}
|
||||||
|
cout << "debug Kernel::Run finished.\n";
|
||||||
|
*/
|
||||||
|
// Thread *t1 = new Thread(execfile[1]);
|
||||||
|
// Thread *t1 = new Thread("../test/test1");
|
||||||
|
// Thread *t2 = new Thread("../test/test2");
|
||||||
|
|
||||||
|
// AddrSpace *halt = new AddrSpace();
|
||||||
|
// t1->space = new AddrSpace();
|
||||||
|
// t2->space = new AddrSpace();
|
||||||
|
|
||||||
|
// halt->Execute("../test/halt");
|
||||||
|
// t1->Fork((VoidFunctionPtr) &ForkExecute, (void *)t1);
|
||||||
|
// t2->Fork((VoidFunctionPtr) &ForkExecute, (void *)t2);
|
||||||
|
|
||||||
|
// currentThread->Finish();
|
||||||
|
// Kernel::Run();
|
||||||
|
// cout << "after ThreadedKernel:Run();" << endl; // unreachable
|
||||||
|
}
|
||||||
|
|
||||||
|
int Kernel::CreateFile(char *filename)
|
||||||
|
{
|
||||||
|
return fileSystem->Create(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
86
code/threads/kernel.h
Executable file
86
code/threads/kernel.h
Executable file
@@ -0,0 +1,86 @@
|
|||||||
|
// kernel.h
|
||||||
|
// Global variables for the Nachos kernel.
|
||||||
|
//
|
||||||
|
// Copyright (c) 1992-1996 The Regents of the University of California.
|
||||||
|
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||||
|
// of liability and disclaimer of warranty provisions.
|
||||||
|
|
||||||
|
#ifndef KERNEL_H
|
||||||
|
#define KERNEL_H
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
#include "debug.h"
|
||||||
|
#include "utility.h"
|
||||||
|
#include "thread.h"
|
||||||
|
#include "scheduler.h"
|
||||||
|
#include "interrupt.h"
|
||||||
|
#include "stats.h"
|
||||||
|
#include "alarm.h"
|
||||||
|
#include "filesys.h"
|
||||||
|
#include "machine.h"
|
||||||
|
|
||||||
|
class PostOfficeInput;
|
||||||
|
class PostOfficeOutput;
|
||||||
|
class SynchConsoleInput;
|
||||||
|
class SynchConsoleOutput;
|
||||||
|
class SynchDisk;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Kernel {
|
||||||
|
public:
|
||||||
|
Kernel(int argc, char **argv);
|
||||||
|
// Interpret command line arguments
|
||||||
|
~Kernel(); // deallocate the kernel
|
||||||
|
|
||||||
|
void Initialize(); // initialize the kernel -- separated
|
||||||
|
// from constructor because
|
||||||
|
// refers to "kernel" as a global
|
||||||
|
void ExecAll();
|
||||||
|
int Exec(char* name);
|
||||||
|
void ThreadSelfTest(); // self test of threads and synchronization
|
||||||
|
|
||||||
|
void ConsoleTest(); // interactive console self test
|
||||||
|
void NetworkTest(); // interactive 2-machine network test
|
||||||
|
Thread* getThread(int threadID){return t[threadID];}
|
||||||
|
|
||||||
|
int CreateFile(char* filename); // fileSystem call
|
||||||
|
|
||||||
|
// These are public for notational convenience; really,
|
||||||
|
// they're global variables used everywhere.
|
||||||
|
|
||||||
|
Thread *currentThread; // the thread holding the CPU
|
||||||
|
Scheduler *scheduler; // the ready list
|
||||||
|
Interrupt *interrupt; // interrupt status
|
||||||
|
Statistics *stats; // performance metrics
|
||||||
|
Alarm *alarm; // the software alarm clock
|
||||||
|
Machine *machine; // the simulated CPU
|
||||||
|
SynchConsoleInput *synchConsoleIn;
|
||||||
|
SynchConsoleOutput *synchConsoleOut;
|
||||||
|
SynchDisk *synchDisk;
|
||||||
|
FileSystem *fileSystem;
|
||||||
|
PostOfficeInput *postOfficeIn;
|
||||||
|
PostOfficeOutput *postOfficeOut;
|
||||||
|
|
||||||
|
int hostName; // machine identifier
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
Thread* t[10];
|
||||||
|
char* execfile[10];
|
||||||
|
int execfileNum;
|
||||||
|
int threadNum;
|
||||||
|
bool randomSlice; // enable pseudo-random time slicing
|
||||||
|
bool debugUserProg; // single step user program
|
||||||
|
double reliability; // likelihood messages are dropped
|
||||||
|
char *consoleIn; // file to read console input from
|
||||||
|
char *consoleOut; // file to send console output to
|
||||||
|
#ifndef FILESYS_STUB
|
||||||
|
bool formatFlag; // format the disk if this is true
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // KERNEL_H
|
||||||
|
|
||||||
|
|
||||||
297
code/threads/main.cc
Executable file
297
code/threads/main.cc
Executable file
@@ -0,0 +1,297 @@
|
|||||||
|
// main.cc
|
||||||
|
// Driver code to initialize, selftest, and run the
|
||||||
|
// operating system kernel.
|
||||||
|
//
|
||||||
|
// Usage: nachos -d <debugflags> -rs <random seed #>
|
||||||
|
// -s -x <nachos file> -ci <consoleIn> -co <consoleOut>
|
||||||
|
// -f -cp <unix file> <nachos file>
|
||||||
|
// -p <nachos file> -r <nachos file> -l -D
|
||||||
|
// -n <network reliability> -m <machine id>
|
||||||
|
// -z -K -C -N
|
||||||
|
//
|
||||||
|
// -d causes certain debugging messages to be printed (see debug.h)
|
||||||
|
// -rs causes Yield to occur at random (but repeatable) spots
|
||||||
|
// -z prints the copyright message
|
||||||
|
// -s causes user programs to be executed in single-step mode
|
||||||
|
// -x runs a user program
|
||||||
|
// -ci specify file for console input (stdin is the default)
|
||||||
|
// -co specify file for console output (stdout is the default)
|
||||||
|
// -n sets the network reliability
|
||||||
|
// -m sets this machine's host id (needed for the network)
|
||||||
|
// -K run a simple self test of kernel threads and synchronization
|
||||||
|
// -C run an interactive console test
|
||||||
|
// -N run a two-machine network test (see Kernel::NetworkTest)
|
||||||
|
//
|
||||||
|
// Filesystem-related flags:
|
||||||
|
// -f forces the Nachos disk to be formatted
|
||||||
|
// -cp copies a file from UNIX to Nachos
|
||||||
|
// -p prints a Nachos file to stdout
|
||||||
|
// -r removes a Nachos file from the file system
|
||||||
|
// -l lists the contents of the Nachos directory
|
||||||
|
// -D prints the contents of the entire file system
|
||||||
|
//
|
||||||
|
// Note: the file system flags are not used if the stub filesystem
|
||||||
|
// is being used
|
||||||
|
//
|
||||||
|
// Copyright (c) 1992-1996 The Regents of the University of California.
|
||||||
|
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||||
|
// of liability and disclaimer of warranty provisions.
|
||||||
|
|
||||||
|
#define MAIN
|
||||||
|
#include "copyright.h"
|
||||||
|
#undef MAIN
|
||||||
|
|
||||||
|
#include "main.h"
|
||||||
|
#include "filesys.h"
|
||||||
|
#include "openfile.h"
|
||||||
|
#include "sysdep.h"
|
||||||
|
|
||||||
|
// global variables
|
||||||
|
Kernel *kernel;
|
||||||
|
Debug *debug;
|
||||||
|
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Cleanup
|
||||||
|
// Delete kernel data structures; called when user hits "ctl-C".
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
static void
|
||||||
|
Cleanup(int x)
|
||||||
|
{
|
||||||
|
cerr << "\nCleaning up after signal " << x << "\n";
|
||||||
|
delete kernel;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------
|
||||||
|
// Constant used by "Copy" and "Print"
|
||||||
|
// It is the number of bytes read from the Unix file (for Copy)
|
||||||
|
// or the Nachos file (for Print) by each read operation
|
||||||
|
//-------------------------------------------------------------------
|
||||||
|
static const int TransferSize = 128;
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef FILESYS_STUB
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Copy
|
||||||
|
// Copy the contents of the UNIX file "from" to the Nachos file "to"
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
static void
|
||||||
|
Copy(char *from, char *to)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
OpenFile* openFile;
|
||||||
|
int amountRead, fileLength;
|
||||||
|
char *buffer;
|
||||||
|
|
||||||
|
// Open UNIX file
|
||||||
|
if ((fd = OpenForReadWrite(from,FALSE)) < 0) {
|
||||||
|
printf("Copy: couldn't open input file %s\n", from);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Figure out length of UNIX file
|
||||||
|
Lseek(fd, 0, 2);
|
||||||
|
fileLength = Tell(fd);
|
||||||
|
Lseek(fd, 0, 0);
|
||||||
|
|
||||||
|
// Create a Nachos file of the same length
|
||||||
|
DEBUG('f', "Copying file " << from << " of size " << fileLength << " to file " << to);
|
||||||
|
if (!kernel->fileSystem->Create(to, fileLength)) { // Create Nachos file
|
||||||
|
printf("Copy: couldn't create output file %s\n", to);
|
||||||
|
Close(fd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
openFile = kernel->fileSystem->Open(to);
|
||||||
|
ASSERT(openFile != NULL);
|
||||||
|
|
||||||
|
// Copy the data in TransferSize chunks
|
||||||
|
buffer = new char[TransferSize];
|
||||||
|
while ((amountRead=ReadPartial(fd, buffer, sizeof(char)*TransferSize)) > 0)
|
||||||
|
openFile->Write(buffer, amountRead);
|
||||||
|
delete [] buffer;
|
||||||
|
|
||||||
|
// Close the UNIX and the Nachos files
|
||||||
|
delete openFile;
|
||||||
|
Close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // FILESYS_STUB
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Print
|
||||||
|
// Print the contents of the Nachos file "name".
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
Print(char *name)
|
||||||
|
{
|
||||||
|
OpenFile *openFile;
|
||||||
|
int i, amountRead;
|
||||||
|
char *buffer;
|
||||||
|
|
||||||
|
if ((openFile = kernel->fileSystem->Open(name)) == NULL) {
|
||||||
|
printf("Print: unable to open file %s\n", name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer = new char[TransferSize];
|
||||||
|
while ((amountRead = openFile->Read(buffer, TransferSize)) > 0)
|
||||||
|
for (i = 0; i < amountRead; i++)
|
||||||
|
printf("%c", buffer[i]);
|
||||||
|
delete [] buffer;
|
||||||
|
|
||||||
|
delete openFile; // close the Nachos file
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// main
|
||||||
|
// Bootstrap the operating system kernel.
|
||||||
|
//
|
||||||
|
// Initialize kernel data structures
|
||||||
|
// Call some test routines
|
||||||
|
// Call "Run" to start an initial user program running
|
||||||
|
//
|
||||||
|
// "argc" is the number of command line arguments (including the name
|
||||||
|
// of the command) -- ex: "nachos -d +" -> argc = 3
|
||||||
|
// "argv" is an array of strings, one for each command line argument
|
||||||
|
// ex: "nachos -d +" -> argv = {"nachos", "-d", "+"}
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
char *debugArg = "";
|
||||||
|
char *userProgName = NULL; // default is not to execute a user prog
|
||||||
|
bool threadTestFlag = false;
|
||||||
|
bool consoleTestFlag = false;
|
||||||
|
bool networkTestFlag = false;
|
||||||
|
#ifndef FILESYS_STUB
|
||||||
|
char *copyUnixFileName = NULL; // UNIX file to be copied into Nachos
|
||||||
|
char *copyNachosFileName = NULL; // name of copied file in Nachos
|
||||||
|
char *printFileName = NULL;
|
||||||
|
char *removeFileName = NULL;
|
||||||
|
bool dirListFlag = false;
|
||||||
|
bool dumpFlag = false;
|
||||||
|
#endif //FILESYS_STUB
|
||||||
|
|
||||||
|
// some command line arguments are handled here.
|
||||||
|
// those that set kernel parameters are handled in
|
||||||
|
// the Kernel constructor
|
||||||
|
for (i = 1; i < argc; i++) {
|
||||||
|
if (strcmp(argv[i], "-d") == 0) {
|
||||||
|
ASSERT(i + 1 < argc); // next argument is debug string
|
||||||
|
debugArg = argv[i + 1];
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
else if (strcmp(argv[i], "-z") == 0) {
|
||||||
|
cout << copyright << "\n";
|
||||||
|
}
|
||||||
|
else if (strcmp(argv[i], "-x") == 0) {
|
||||||
|
ASSERT(i + 1 < argc);
|
||||||
|
userProgName = argv[i + 1];
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
else if (strcmp(argv[i], "-K") == 0) {
|
||||||
|
threadTestFlag = TRUE;
|
||||||
|
}
|
||||||
|
else if (strcmp(argv[i], "-C") == 0) {
|
||||||
|
consoleTestFlag = TRUE;
|
||||||
|
}
|
||||||
|
else if (strcmp(argv[i], "-N") == 0) {
|
||||||
|
networkTestFlag = TRUE;
|
||||||
|
}
|
||||||
|
#ifndef FILESYS_STUB
|
||||||
|
else if (strcmp(argv[i], "-cp") == 0) {
|
||||||
|
ASSERT(i + 2 < argc);
|
||||||
|
copyUnixFileName = argv[i + 1];
|
||||||
|
copyNachosFileName = argv[i + 2];
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
else if (strcmp(argv[i], "-p") == 0) {
|
||||||
|
ASSERT(i + 1 < argc);
|
||||||
|
printFileName = argv[i + 1];
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
else if (strcmp(argv[i], "-r") == 0) {
|
||||||
|
ASSERT(i + 1 < argc);
|
||||||
|
removeFileName = argv[i + 1];
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
else if (strcmp(argv[i], "-l") == 0) {
|
||||||
|
dirListFlag = true;
|
||||||
|
}
|
||||||
|
else if (strcmp(argv[i], "-D") == 0) {
|
||||||
|
dumpFlag = true;
|
||||||
|
}
|
||||||
|
#endif //FILESYS_STUB
|
||||||
|
else if (strcmp(argv[i], "-u") == 0) {
|
||||||
|
cout << "Partial usage: nachos [-z -d debugFlags]\n";
|
||||||
|
cout << "Partial usage: nachos [-x programName]\n";
|
||||||
|
cout << "Partial usage: nachos [-K] [-C] [-N]\n";
|
||||||
|
#ifndef FILESYS_STUB
|
||||||
|
cout << "Partial usage: nachos [-cp UnixFile NachosFile]\n";
|
||||||
|
cout << "Partial usage: nachos [-p fileName] [-r fileName]\n";
|
||||||
|
cout << "Partial usage: nachos [-l] [-D]\n";
|
||||||
|
#endif //FILESYS_STUB
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
debug = new Debug(debugArg);
|
||||||
|
|
||||||
|
DEBUG(dbgThread, "Entering main");
|
||||||
|
|
||||||
|
kernel = new Kernel(argc, argv);
|
||||||
|
|
||||||
|
kernel->Initialize();
|
||||||
|
|
||||||
|
CallOnUserAbort(Cleanup); // if user hits ctl-C
|
||||||
|
|
||||||
|
// at this point, the kernel is ready to do something
|
||||||
|
// run some tests, if requested
|
||||||
|
if (threadTestFlag) {
|
||||||
|
kernel->ThreadSelfTest(); // test threads and synchronization
|
||||||
|
}
|
||||||
|
if (consoleTestFlag) {
|
||||||
|
kernel->ConsoleTest(); // interactive test of the synchronized console
|
||||||
|
}
|
||||||
|
if (networkTestFlag) {
|
||||||
|
kernel->NetworkTest(); // two-machine test of the network
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef FILESYS_STUB
|
||||||
|
if (removeFileName != NULL) {
|
||||||
|
kernel->fileSystem->Remove(removeFileName);
|
||||||
|
}
|
||||||
|
if (copyUnixFileName != NULL && copyNachosFileName != NULL) {
|
||||||
|
Copy(copyUnixFileName,copyNachosFileName);
|
||||||
|
}
|
||||||
|
if (dumpFlag) {
|
||||||
|
kernel->fileSystem->Print();
|
||||||
|
}
|
||||||
|
if (dirListFlag) {
|
||||||
|
kernel->fileSystem->List();
|
||||||
|
}
|
||||||
|
if (printFileName != NULL) {
|
||||||
|
Print(printFileName);
|
||||||
|
}
|
||||||
|
#endif // FILESYS_STUB
|
||||||
|
|
||||||
|
// finally, run an initial user program if requested to do so
|
||||||
|
|
||||||
|
kernel->ExecAll();
|
||||||
|
// If we don't run a user program, we may get here.
|
||||||
|
// Calling "return" would terminate the program.
|
||||||
|
// Instead, call Halt, which will first clean up, then
|
||||||
|
// terminate.
|
||||||
|
// kernel->interrupt->Halt();
|
||||||
|
|
||||||
|
ASSERTNOTREACHED();
|
||||||
|
}
|
||||||
|
|
||||||
19
code/threads/main.h
Executable file
19
code/threads/main.h
Executable file
@@ -0,0 +1,19 @@
|
|||||||
|
// main.h
|
||||||
|
// This file defines the Nachos global variables
|
||||||
|
//
|
||||||
|
// Copyright (c) 1992-1996 The Regents of the University of California.
|
||||||
|
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||||
|
// of liability and disclaimer of warranty provisions.
|
||||||
|
|
||||||
|
#ifndef MAIN_H
|
||||||
|
#define MAIN_H
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
#include "debug.h"
|
||||||
|
#include "kernel.h"
|
||||||
|
|
||||||
|
extern Kernel *kernel;
|
||||||
|
extern Debug *debug;
|
||||||
|
|
||||||
|
#endif // MAIN_H
|
||||||
|
|
||||||
179
code/threads/scheduler.cc
Executable file
179
code/threads/scheduler.cc
Executable file
@@ -0,0 +1,179 @@
|
|||||||
|
// scheduler.cc
|
||||||
|
// Routines to choose the next thread to run, and to dispatch to
|
||||||
|
// that thread.
|
||||||
|
//
|
||||||
|
// These routines assume that interrupts are already disabled.
|
||||||
|
// If interrupts are disabled, we can assume mutual exclusion
|
||||||
|
// (since we are on a uniprocessor).
|
||||||
|
//
|
||||||
|
// NOTE: We can't use Locks to provide mutual exclusion here, since
|
||||||
|
// if we needed to wait for a lock, and the lock was busy, we would
|
||||||
|
// end up calling FindNextToRun(), and that would put us in an
|
||||||
|
// infinite loop.
|
||||||
|
//
|
||||||
|
// Very simple implementation -- no priorities, straight FIFO.
|
||||||
|
// Might need to be improved in later assignments.
|
||||||
|
//
|
||||||
|
// Copyright (c) 1992-1996 The Regents of the University of California.
|
||||||
|
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||||
|
// of liability and disclaimer of warranty provisions.
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
#include "debug.h"
|
||||||
|
#include "scheduler.h"
|
||||||
|
#include "main.h"
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Scheduler::Scheduler
|
||||||
|
// Initialize the list of ready but not running threads.
|
||||||
|
// Initially, no ready threads.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
Scheduler::Scheduler()
|
||||||
|
{
|
||||||
|
readyList = new List<Thread *>;
|
||||||
|
toBeDestroyed = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Scheduler::~Scheduler
|
||||||
|
// De-allocate the list of ready threads.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
Scheduler::~Scheduler()
|
||||||
|
{
|
||||||
|
delete readyList;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Scheduler::ReadyToRun
|
||||||
|
// Mark a thread as ready, but not running.
|
||||||
|
// Put it on the ready list, for later scheduling onto the CPU.
|
||||||
|
//
|
||||||
|
// "thread" is the thread to be put on the ready list.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
Scheduler::ReadyToRun (Thread *thread)
|
||||||
|
{
|
||||||
|
ASSERT(kernel->interrupt->getLevel() == IntOff);
|
||||||
|
DEBUG(dbgThread, "Putting thread on ready list: " << thread->getName());
|
||||||
|
//cout << "Putting thread on ready list: " << thread->getName() << endl ;
|
||||||
|
thread->setStatus(READY);
|
||||||
|
readyList->Append(thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Scheduler::FindNextToRun
|
||||||
|
// Return the next thread to be scheduled onto the CPU.
|
||||||
|
// If there are no ready threads, return NULL.
|
||||||
|
// Side effect:
|
||||||
|
// Thread is removed from the ready list.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
Thread *
|
||||||
|
Scheduler::FindNextToRun ()
|
||||||
|
{
|
||||||
|
ASSERT(kernel->interrupt->getLevel() == IntOff);
|
||||||
|
|
||||||
|
if (readyList->IsEmpty()) {
|
||||||
|
return NULL;
|
||||||
|
} else {
|
||||||
|
return readyList->RemoveFront();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Scheduler::Run
|
||||||
|
// Dispatch the CPU to nextThread. Save the state of the old thread,
|
||||||
|
// and load the state of the new thread, by calling the machine
|
||||||
|
// dependent context switch routine, SWITCH.
|
||||||
|
//
|
||||||
|
// Note: we assume the state of the previously running thread has
|
||||||
|
// already been changed from running to blocked or ready (depending).
|
||||||
|
// Side effect:
|
||||||
|
// The global variable kernel->currentThread becomes nextThread.
|
||||||
|
//
|
||||||
|
// "nextThread" is the thread to be put into the CPU.
|
||||||
|
// "finishing" is set if the current thread is to be deleted
|
||||||
|
// once we're no longer running on its stack
|
||||||
|
// (when the next thread starts running)
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
Scheduler::Run (Thread *nextThread, bool finishing)
|
||||||
|
{
|
||||||
|
Thread *oldThread = kernel->currentThread;
|
||||||
|
|
||||||
|
ASSERT(kernel->interrupt->getLevel() == IntOff);
|
||||||
|
|
||||||
|
if (finishing) { // mark that we need to delete current thread
|
||||||
|
ASSERT(toBeDestroyed == NULL);
|
||||||
|
toBeDestroyed = oldThread;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldThread->space != NULL) { // if this thread is a user program,
|
||||||
|
oldThread->SaveUserState(); // save the user's CPU registers
|
||||||
|
oldThread->space->SaveState();
|
||||||
|
}
|
||||||
|
|
||||||
|
oldThread->CheckOverflow(); // check if the old thread
|
||||||
|
// had an undetected stack overflow
|
||||||
|
|
||||||
|
kernel->currentThread = nextThread; // switch to the next thread
|
||||||
|
nextThread->setStatus(RUNNING); // nextThread is now running
|
||||||
|
|
||||||
|
DEBUG(dbgThread, "Switching from: " << oldThread->getName() << " to: " << nextThread->getName());
|
||||||
|
|
||||||
|
// This is a machine-dependent assembly language routine defined
|
||||||
|
// in switch.s. You may have to think
|
||||||
|
// a bit to figure out what happens after this, both from the point
|
||||||
|
// of view of the thread and from the perspective of the "outside world".
|
||||||
|
|
||||||
|
SWITCH(oldThread, nextThread);
|
||||||
|
|
||||||
|
// we're back, running oldThread
|
||||||
|
|
||||||
|
// interrupts are off when we return from switch!
|
||||||
|
ASSERT(kernel->interrupt->getLevel() == IntOff);
|
||||||
|
|
||||||
|
DEBUG(dbgThread, "Now in thread: " << oldThread->getName());
|
||||||
|
|
||||||
|
CheckToBeDestroyed(); // check if thread we were running
|
||||||
|
// before this one has finished
|
||||||
|
// and needs to be cleaned up
|
||||||
|
|
||||||
|
if (oldThread->space != NULL) { // if there is an address space
|
||||||
|
oldThread->RestoreUserState(); // to restore, do it.
|
||||||
|
oldThread->space->RestoreState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Scheduler::CheckToBeDestroyed
|
||||||
|
// If the old thread gave up the processor because it was finishing,
|
||||||
|
// we need to delete its carcass. Note we cannot delete the thread
|
||||||
|
// before now (for example, in Thread::Finish()), because up to this
|
||||||
|
// point, we were still running on the old thread's stack!
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
Scheduler::CheckToBeDestroyed()
|
||||||
|
{
|
||||||
|
if (toBeDestroyed != NULL) {
|
||||||
|
delete toBeDestroyed;
|
||||||
|
toBeDestroyed = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Scheduler::Print
|
||||||
|
// Print the scheduler state -- in other words, the contents of
|
||||||
|
// the ready list. For debugging.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
void
|
||||||
|
Scheduler::Print()
|
||||||
|
{
|
||||||
|
cout << "Ready list contents:\n";
|
||||||
|
readyList->Apply(ThreadPrint);
|
||||||
|
}
|
||||||
44
code/threads/scheduler.h
Executable file
44
code/threads/scheduler.h
Executable file
@@ -0,0 +1,44 @@
|
|||||||
|
// scheduler.h
|
||||||
|
// Data structures for the thread dispatcher and scheduler.
|
||||||
|
// Primarily, the list of threads that are ready to run.
|
||||||
|
//
|
||||||
|
// Copyright (c) 1992-1996 The Regents of the University of California.
|
||||||
|
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||||
|
// of liability and disclaimer of warranty provisions.
|
||||||
|
|
||||||
|
#ifndef SCHEDULER_H
|
||||||
|
#define SCHEDULER_H
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
#include "list.h"
|
||||||
|
#include "thread.h"
|
||||||
|
|
||||||
|
// The following class defines the scheduler/dispatcher abstraction --
|
||||||
|
// the data structures and operations needed to keep track of which
|
||||||
|
// thread is running, and which threads are ready but not running.
|
||||||
|
|
||||||
|
class Scheduler {
|
||||||
|
public:
|
||||||
|
Scheduler(); // Initialize list of ready threads
|
||||||
|
~Scheduler(); // De-allocate ready list
|
||||||
|
|
||||||
|
void ReadyToRun(Thread* thread);
|
||||||
|
// Thread can be dispatched.
|
||||||
|
Thread* FindNextToRun(); // Dequeue first thread on the ready
|
||||||
|
// list, if any, and return thread.
|
||||||
|
void Run(Thread* nextThread, bool finishing);
|
||||||
|
// Cause nextThread to start running
|
||||||
|
void CheckToBeDestroyed();// Check if thread that had been
|
||||||
|
// running needs to be deleted
|
||||||
|
void Print(); // Print contents of ready list
|
||||||
|
|
||||||
|
// SelfTest for scheduler is implemented in class Thread
|
||||||
|
|
||||||
|
private:
|
||||||
|
List<Thread *> *readyList; // queue of threads that are ready to run,
|
||||||
|
// but not running
|
||||||
|
Thread *toBeDestroyed; // finishing thread to be destroyed
|
||||||
|
// by the next thread that runs
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SCHEDULER_H
|
||||||
750
code/threads/switch.S
Executable file
750
code/threads/switch.S
Executable file
@@ -0,0 +1,750 @@
|
|||||||
|
/* switch.s
|
||||||
|
* Machine dependent context switch routines. DO NOT MODIFY THESE!
|
||||||
|
*
|
||||||
|
* Context switching is inherently machine dependent, since
|
||||||
|
* the registers to be saved, how to set up an initial
|
||||||
|
* call frame, etc, are all specific to a processor architecture.
|
||||||
|
*
|
||||||
|
* This file currently supports the following architectures:
|
||||||
|
* DEC MIPS (DECMIPS)
|
||||||
|
* DEC Alpha (ALPHA)
|
||||||
|
* SUN SPARC (SPARC)
|
||||||
|
* HP PA-RISC (PARISC)
|
||||||
|
* Intel 386 (x86)
|
||||||
|
* IBM RS6000 (PowerPC) -- I hope it will also work for Mac PowerPC
|
||||||
|
*
|
||||||
|
* We define two routines for each architecture:
|
||||||
|
*
|
||||||
|
* ThreadRoot(InitialPC, InitialArg, WhenDonePC, StartupPC)
|
||||||
|
* InitialPC - The program counter of the procedure to run
|
||||||
|
* in this thread.
|
||||||
|
* InitialArg - The single argument to the thread.
|
||||||
|
* WhenDonePC - The routine to call when the thread returns.
|
||||||
|
* StartupPC - Routine to call when the thread is started.
|
||||||
|
*
|
||||||
|
* ThreadRoot is called from the SWITCH() routine to start
|
||||||
|
* a thread for the first time.
|
||||||
|
*
|
||||||
|
* SWITCH(oldThread, newThread)
|
||||||
|
* oldThread - The current thread that was running, where the
|
||||||
|
* CPU register state is to be saved.
|
||||||
|
* newThread - The new thread to be run, where the CPU register
|
||||||
|
* state is to be loaded from.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright (c) 1992-1996 The Regents of the University of California.
|
||||||
|
All rights reserved. See copyright.h for copyright notice and limitation
|
||||||
|
of liability and disclaimer of warranty provisions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
#include "switch.h"
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef DECMIPS
|
||||||
|
|
||||||
|
/* Symbolic register names */
|
||||||
|
#define z $0 /* zero register */
|
||||||
|
#define a0 $4 /* argument registers */
|
||||||
|
#define a1 $5
|
||||||
|
#define s0 $16 /* callee saved */
|
||||||
|
#define s1 $17
|
||||||
|
#define s2 $18
|
||||||
|
#define s3 $19
|
||||||
|
#define s4 $20
|
||||||
|
#define s5 $21
|
||||||
|
#define s6 $22
|
||||||
|
#define s7 $23
|
||||||
|
#define sp $29 /* stack pointer */
|
||||||
|
#define fp $30 /* frame pointer */
|
||||||
|
#define ra $31 /* return address */
|
||||||
|
|
||||||
|
.text
|
||||||
|
.align 2
|
||||||
|
|
||||||
|
.globl ThreadRoot
|
||||||
|
.ent ThreadRoot,0
|
||||||
|
ThreadRoot:
|
||||||
|
or fp,z,z # Clearing the frame pointer here
|
||||||
|
# makes gdb backtraces of thread stacks
|
||||||
|
# end here (I hope!)
|
||||||
|
|
||||||
|
jal StartupPC # call startup procedure
|
||||||
|
move a0, InitialArg
|
||||||
|
jal InitialPC # call main procedure
|
||||||
|
jal WhenDonePC # when done, call clean up procedure
|
||||||
|
|
||||||
|
# NEVER REACHED
|
||||||
|
.end ThreadRoot
|
||||||
|
|
||||||
|
# a0 -- pointer to old Thread
|
||||||
|
# a1 -- pointer to new Thread
|
||||||
|
.globl SWITCH
|
||||||
|
.ent SWITCH,0
|
||||||
|
SWITCH:
|
||||||
|
sw sp, SP(a0) # save new stack pointer
|
||||||
|
sw s0, S0(a0) # save all the callee-save registers
|
||||||
|
sw s1, S1(a0)
|
||||||
|
sw s2, S2(a0)
|
||||||
|
sw s3, S3(a0)
|
||||||
|
sw s4, S4(a0)
|
||||||
|
sw s5, S5(a0)
|
||||||
|
sw s6, S6(a0)
|
||||||
|
sw s7, S7(a0)
|
||||||
|
sw fp, FP(a0) # save frame pointer
|
||||||
|
sw ra, PC(a0) # save return address
|
||||||
|
|
||||||
|
lw sp, SP(a1) # load the new stack pointer
|
||||||
|
lw s0, S0(a1) # load the callee-save registers
|
||||||
|
lw s1, S1(a1)
|
||||||
|
lw s2, S2(a1)
|
||||||
|
lw s3, S3(a1)
|
||||||
|
lw s4, S4(a1)
|
||||||
|
lw s5, S5(a1)
|
||||||
|
lw s6, S6(a1)
|
||||||
|
lw s7, S7(a1)
|
||||||
|
lw fp, FP(a1)
|
||||||
|
lw ra, PC(a1) # load the return address
|
||||||
|
|
||||||
|
j ra
|
||||||
|
.end SWITCH
|
||||||
|
|
||||||
|
#endif // DECMIPS
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef SPARC
|
||||||
|
|
||||||
|
/* NOTE! These files appear not to exist on Solaris --
|
||||||
|
* you need to find where (the SPARC-specific) MINFRAME, ST_FLUSH_WINDOWS, ...
|
||||||
|
* are defined. (I don't have a Solaris machine, so I have no way to tell.)
|
||||||
|
*/
|
||||||
|
#ifdef SOLARIS
|
||||||
|
#include <sys/trap.h>
|
||||||
|
#include <sys/asm_linkage.h>
|
||||||
|
#else
|
||||||
|
#include <sun4/trap.h>
|
||||||
|
#include <sun4/asm_linkage.h>
|
||||||
|
#endif
|
||||||
|
.seg "text"
|
||||||
|
|
||||||
|
/* SPECIAL to the SPARC:
|
||||||
|
* The first two instruction of ThreadRoot are skipped because
|
||||||
|
* the address of ThreadRoot is made the return address of SWITCH()
|
||||||
|
* by the routine Thread::StackAllocate. SWITCH() jumps here on the
|
||||||
|
* "ret" instruction which is really at "jmp %o7+8". The 8 skips the
|
||||||
|
* two nops at the beginning of the routine.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef SOLARIS
|
||||||
|
.globl ThreadRoot
|
||||||
|
ThreadRoot:
|
||||||
|
#else
|
||||||
|
.globl _ThreadRoot
|
||||||
|
_ThreadRoot:
|
||||||
|
#endif
|
||||||
|
nop ; nop /* These 2 nops are skipped because we are called
|
||||||
|
* with a jmp+8 instruction. */
|
||||||
|
clr %fp /* Clearing the frame pointer makes gdb backtraces
|
||||||
|
* of thread stacks end here. */
|
||||||
|
/* Currently the arguments are in out registers we
|
||||||
|
* save them into local registers so they won't be
|
||||||
|
* trashed during the calls we make. */
|
||||||
|
mov InitialPC, %l0
|
||||||
|
mov InitialArg, %l1
|
||||||
|
mov WhenDonePC, %l2
|
||||||
|
/* Execute the code:
|
||||||
|
* call StartupPC();
|
||||||
|
* call InitialPC(InitialArg);
|
||||||
|
* call WhenDonePC();
|
||||||
|
*/
|
||||||
|
call StartupPC,0
|
||||||
|
nop
|
||||||
|
call %l0, 1
|
||||||
|
mov %l1, %o0 /* Using delay slot to setup argument to InitialPC */
|
||||||
|
call %l2, 0
|
||||||
|
nop
|
||||||
|
/* WhenDonePC call should never return. If it does
|
||||||
|
* we execute a trap into the debugger. */
|
||||||
|
ta ST_BREAKPOINT
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef SOLARIS
|
||||||
|
.globl SWITCH
|
||||||
|
SWITCH:
|
||||||
|
#else
|
||||||
|
.globl _SWITCH
|
||||||
|
_SWITCH:
|
||||||
|
#endif
|
||||||
|
save %sp, -SA(MINFRAME), %sp
|
||||||
|
st %fp, [%i0]
|
||||||
|
st %i0, [%i0+I0]
|
||||||
|
st %i1, [%i0+I1]
|
||||||
|
st %i2, [%i0+I2]
|
||||||
|
st %i3, [%i0+I3]
|
||||||
|
st %i4, [%i0+I4]
|
||||||
|
st %i5, [%i0+I5]
|
||||||
|
st %i7, [%i0+I7]
|
||||||
|
ta ST_FLUSH_WINDOWS
|
||||||
|
nop
|
||||||
|
mov %i1, %l0
|
||||||
|
ld [%l0+I0], %i0
|
||||||
|
ld [%l0+I1], %i1
|
||||||
|
ld [%l0+I2], %i2
|
||||||
|
ld [%l0+I3], %i3
|
||||||
|
ld [%l0+I4], %i4
|
||||||
|
ld [%l0+I5], %i5
|
||||||
|
ld [%l0+I7], %i7
|
||||||
|
ld [%l0], %i6
|
||||||
|
ret
|
||||||
|
restore
|
||||||
|
|
||||||
|
#endif // SPARC
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef PARISC
|
||||||
|
|
||||||
|
;rp = r2, sp = r30
|
||||||
|
;arg0 = r26, arg1 = r25, arg2 = r24, arg3 = r23
|
||||||
|
|
||||||
|
.SPACE $TEXT$
|
||||||
|
.SUBSPA $CODE$
|
||||||
|
ThreadRoot
|
||||||
|
.PROC
|
||||||
|
.CALLINFO CALLER,FRAME=0
|
||||||
|
.ENTER
|
||||||
|
|
||||||
|
.CALL
|
||||||
|
ble 0(%r6) ;call StartupPC
|
||||||
|
stw %r31, -24(%sp) ;put return address in proper stack
|
||||||
|
;location for StartupPC export stub.
|
||||||
|
|
||||||
|
or %r4, 0, %arg0 ;load InitialArg
|
||||||
|
.CALL ;in=26
|
||||||
|
ble 0(%r3) ;call InitialPC
|
||||||
|
stw %r31, -24(%sp) ;put return address in proper stack
|
||||||
|
;location for InitialPC export stub.
|
||||||
|
|
||||||
|
.CALL
|
||||||
|
ble 0(%r5) ;call WhenDonePC
|
||||||
|
stw %r31, -24(%sp) ;put return address in proper stack
|
||||||
|
;location for StartupPC export stub.
|
||||||
|
.LEAVE
|
||||||
|
|
||||||
|
.PROCEND
|
||||||
|
|
||||||
|
|
||||||
|
SWITCH
|
||||||
|
.PROC
|
||||||
|
.CALLINFO CALLER,FRAME=0
|
||||||
|
.ENTRY
|
||||||
|
|
||||||
|
; save process state of oldThread
|
||||||
|
stw %sp, SP(%arg0) ;save stack pointer
|
||||||
|
stw %r3, S0(%arg0) ;save callee-save registers
|
||||||
|
stw %r4, S1(%arg0)
|
||||||
|
stw %r5, S2(%arg0)
|
||||||
|
stw %r6, S3(%arg0)
|
||||||
|
stw %r7, S4(%arg0)
|
||||||
|
stw %r8, S5(%arg0)
|
||||||
|
stw %r9, S6(%arg0)
|
||||||
|
stw %r10, S7(%arg0)
|
||||||
|
stw %r11, S8(%arg0)
|
||||||
|
stw %r12, S9(%arg0)
|
||||||
|
stw %r13, S10(%arg0)
|
||||||
|
stw %r14, S11(%arg0)
|
||||||
|
stw %r15, S12(%arg0)
|
||||||
|
stw %r16, S13(%arg0)
|
||||||
|
stw %r17, S14(%arg0)
|
||||||
|
stw %r18, S15(%arg0)
|
||||||
|
stw %rp, PC(%arg0) ;save program counter
|
||||||
|
|
||||||
|
; restore process state of nextThread
|
||||||
|
ldw SP(%arg1), %sp ;restore stack pointer
|
||||||
|
ldw S0(%arg1), %r3 ;restore callee-save registers
|
||||||
|
ldw S1(%arg1), %r4
|
||||||
|
ldw S2(%arg1), %r5
|
||||||
|
ldw S3(%arg1), %r6
|
||||||
|
ldw S4(%arg1), %r7
|
||||||
|
ldw S5(%arg1), %r8
|
||||||
|
ldw S6(%arg1), %r9
|
||||||
|
ldw S7(%arg1), %r10
|
||||||
|
ldw S8(%arg1), %r11
|
||||||
|
ldw S9(%arg1), %r12
|
||||||
|
ldw S10(%arg1), %r13
|
||||||
|
ldw S11(%arg1), %r14
|
||||||
|
ldw S12(%arg1), %r15
|
||||||
|
ldw S13(%arg1), %r16
|
||||||
|
ldw S14(%arg1), %r17
|
||||||
|
ldw PC(%arg1), %rp ;save program counter
|
||||||
|
bv 0(%rp)
|
||||||
|
.EXIT
|
||||||
|
ldw S15(%arg1), %r18
|
||||||
|
|
||||||
|
.PROCEND
|
||||||
|
|
||||||
|
.EXPORT SWITCH,ENTRY,PRIV_LEV=3,RTNVAL=GR
|
||||||
|
.EXPORT ThreadRoot,ENTRY,PRIV_LEV=3,RTNVAL=GR
|
||||||
|
|
||||||
|
#endif // PARISC
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef x86
|
||||||
|
|
||||||
|
.text
|
||||||
|
.align 2
|
||||||
|
|
||||||
|
.globl ThreadRoot
|
||||||
|
.globl _ThreadRoot
|
||||||
|
|
||||||
|
/* void ThreadRoot( void )
|
||||||
|
**
|
||||||
|
** expects the following registers to be initialized:
|
||||||
|
** eax points to startup function (interrupt enable)
|
||||||
|
** edx contains inital argument to thread function
|
||||||
|
** esi points to thread function
|
||||||
|
** edi point to Thread::Finish()
|
||||||
|
*/
|
||||||
|
_ThreadRoot:
|
||||||
|
ThreadRoot:
|
||||||
|
pushl %ebp
|
||||||
|
movl %esp,%ebp
|
||||||
|
pushl InitialArg
|
||||||
|
call *StartupPC
|
||||||
|
call *InitialPC
|
||||||
|
call *WhenDonePC
|
||||||
|
|
||||||
|
# NOT REACHED
|
||||||
|
movl %ebp,%esp
|
||||||
|
popl %ebp
|
||||||
|
ret
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* void SWITCH( thread *t1, thread *t2 )
|
||||||
|
**
|
||||||
|
** on entry, stack looks like this:
|
||||||
|
** 8(esp) -> thread *t2
|
||||||
|
** 4(esp) -> thread *t1
|
||||||
|
** (esp) -> return address
|
||||||
|
**
|
||||||
|
** we push the current eax on the stack so that we can use it as
|
||||||
|
** a pointer to t1, this decrements esp by 4, so when we use it
|
||||||
|
** to reference stuff on the stack, we add 4 to the offset.
|
||||||
|
*/
|
||||||
|
.comm _eax_save,4
|
||||||
|
|
||||||
|
.globl SWITCH
|
||||||
|
.globl _SWITCH
|
||||||
|
_SWITCH:
|
||||||
|
SWITCH:
|
||||||
|
movl %eax,_eax_save # save the value of eax
|
||||||
|
movl 4(%esp),%eax # move pointer to t1 into eax
|
||||||
|
movl %ebx,_EBX(%eax) # save registers
|
||||||
|
movl %ecx,_ECX(%eax)
|
||||||
|
movl %edx,_EDX(%eax)
|
||||||
|
movl %esi,_ESI(%eax)
|
||||||
|
movl %edi,_EDI(%eax)
|
||||||
|
movl %ebp,_EBP(%eax)
|
||||||
|
movl %esp,_ESP(%eax) # save stack pointer
|
||||||
|
movl _eax_save,%ebx # get the saved value of eax
|
||||||
|
movl %ebx,_EAX(%eax) # store it
|
||||||
|
movl 0(%esp),%ebx # get return address from stack into ebx
|
||||||
|
movl %ebx,_PC(%eax) # save it into the pc storage
|
||||||
|
|
||||||
|
movl 8(%esp),%eax # move pointer to t2 into eax
|
||||||
|
|
||||||
|
movl _EAX(%eax),%ebx # get new value for eax into ebx
|
||||||
|
movl %ebx,_eax_save # save it
|
||||||
|
movl _EBX(%eax),%ebx # retore old registers
|
||||||
|
movl _ECX(%eax),%ecx
|
||||||
|
movl _EDX(%eax),%edx
|
||||||
|
movl _ESI(%eax),%esi
|
||||||
|
movl _EDI(%eax),%edi
|
||||||
|
movl _EBP(%eax),%ebp
|
||||||
|
movl _ESP(%eax),%esp # restore stack pointer
|
||||||
|
movl _PC(%eax),%eax # restore return address into eax
|
||||||
|
movl %eax,4(%esp) # copy over the ret address on the stack
|
||||||
|
movl _eax_save,%eax
|
||||||
|
|
||||||
|
ret
|
||||||
|
|
||||||
|
#endif // x86
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(ApplePowerPC)
|
||||||
|
|
||||||
|
/* The AIX PowerPC code is incompatible with the assembler on MacOS X
|
||||||
|
* and Linux. So the SWITCH code was adapted for IBM 750 compatible
|
||||||
|
* processors, and ThreadRoot is modeled after the more reasonable
|
||||||
|
* looking ThreadRoot's in this file.
|
||||||
|
*
|
||||||
|
* Joshua LeVasseur <jtl@ira.uka.de>
|
||||||
|
*/
|
||||||
|
|
||||||
|
.align 2
|
||||||
|
.globl _SWITCH
|
||||||
|
_SWITCH:
|
||||||
|
stw r1, 0(r3) /* Store stack pointer. */
|
||||||
|
stmw r13, 20(r3) /* Store general purpose registers 13 - 31. */
|
||||||
|
stfd f14, 96(r3) /* Store floating point registers 14 -31. */
|
||||||
|
stfd f15, 104(r3)
|
||||||
|
stfd f16, 112(r3)
|
||||||
|
stfd f17, 120(r3)
|
||||||
|
stfd f18, 128(r3)
|
||||||
|
stfd f19, 136(r3)
|
||||||
|
stfd f20, 144(r3)
|
||||||
|
stfd f21, 152(r3)
|
||||||
|
stfd f22, 160(r3)
|
||||||
|
stfd f23, 168(r3)
|
||||||
|
stfd f24, 176(r3)
|
||||||
|
stfd f25, 184(r3)
|
||||||
|
stfd f26, 192(r3)
|
||||||
|
stfd f27, 200(r3)
|
||||||
|
stfd f28, 208(r3)
|
||||||
|
stfd f29, 216(r3)
|
||||||
|
stfd f30, 224(r3)
|
||||||
|
stfd f31, 232(r3)
|
||||||
|
|
||||||
|
mflr r0
|
||||||
|
stw r0, 244(r3) /* Spill the link register. */
|
||||||
|
|
||||||
|
mfcr r12
|
||||||
|
stw r12, 240(r3) /* Spill the condition register. */
|
||||||
|
|
||||||
|
lwz r1, 0(r4) /* Load the incoming stack pointer. */
|
||||||
|
|
||||||
|
lwz r0, 244(r4) /* Load the incoming link register. */
|
||||||
|
mtlr r0 /* Restore the link register. */
|
||||||
|
|
||||||
|
lwz r12, 240(r4) /* Load the condition register value. */
|
||||||
|
mtcrf 0xff, r12 /* Restore the condition register. */
|
||||||
|
|
||||||
|
lmw r13, 20(r4) /* Restore registers r13 - r31. */
|
||||||
|
|
||||||
|
lfd f14, 96(r4) /* Restore floating point register f14 - f31. */
|
||||||
|
lfd f15, 104(r4)
|
||||||
|
lfd f16, 112(r4)
|
||||||
|
lfd f17, 120(r4)
|
||||||
|
lfd f18, 128(r4)
|
||||||
|
lfd f19, 136(r4)
|
||||||
|
lfd f20, 144(r4)
|
||||||
|
lfd f21, 152(r4)
|
||||||
|
lfd f22, 160(r4)
|
||||||
|
lfd f23, 168(r4)
|
||||||
|
lfd f24, 176(r4)
|
||||||
|
lfd f25, 184(r4)
|
||||||
|
lfd f26, 192(r4)
|
||||||
|
lfd f27, 200(r4)
|
||||||
|
lfd f28, 208(r4)
|
||||||
|
lfd f29, 216(r4)
|
||||||
|
lfd f30, 224(r4)
|
||||||
|
lfd f31, 232(r4)
|
||||||
|
|
||||||
|
/* When a thread first starts, the following blr instruction jumps
|
||||||
|
* to ThreadRoot. ThreadRoot expects the incoming thread block
|
||||||
|
* in r4.
|
||||||
|
*/
|
||||||
|
blr /* Branch to the address held in link register. */
|
||||||
|
|
||||||
|
|
||||||
|
.align 2
|
||||||
|
.globl _ThreadRoot
|
||||||
|
_ThreadRoot:
|
||||||
|
lwz r20, 16(r4) /* StartupPCState - ThreadBegin */
|
||||||
|
lwz r21, 8(r4) /* InitialArgState - arg */
|
||||||
|
lwz r22, 4(r4) /* InitialPCState - func */
|
||||||
|
lwz r23, 12(r4) /* WhenDonePCState - ThreadFinish */
|
||||||
|
|
||||||
|
/* Call ThreadBegin function. */
|
||||||
|
mtctr r20 /* The function pointer. */
|
||||||
|
bctrl
|
||||||
|
|
||||||
|
/* Call the target function. */
|
||||||
|
mr r3, r21 /* Function arg. */
|
||||||
|
mtctr r22 /* Function pointer. */
|
||||||
|
bctrl
|
||||||
|
|
||||||
|
/* Call the ThreadFinish function. */
|
||||||
|
mtctr r23
|
||||||
|
bctrl
|
||||||
|
|
||||||
|
/* We shouldn't execute here. */
|
||||||
|
1: b 1b
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(PowerPC) && !defined(ApplePowerPC)
|
||||||
|
.globl branch[ds]
|
||||||
|
.csect branch[ds]
|
||||||
|
.long .branch[PR]
|
||||||
|
.long TOC[tc0]
|
||||||
|
.long 0
|
||||||
|
.toc
|
||||||
|
T.branch: .tc .branch[tc], branch[ds]
|
||||||
|
.globl .branch[PR]
|
||||||
|
.csect .branch[PR]
|
||||||
|
|
||||||
|
l 0, 0x0(11) # load function address into r0
|
||||||
|
mtctr 0 # move r0 into counter register
|
||||||
|
l 2, 0x4(11) # move new TOC address into r2
|
||||||
|
l 11, 0x8(11) # reset function address
|
||||||
|
bctr # branch to the counter register
|
||||||
|
|
||||||
|
|
||||||
|
.globl ThreadRoot[ds]
|
||||||
|
.csect ThreadRoot[ds]
|
||||||
|
.long .ThreadRoot[PR]
|
||||||
|
.long TOC[tc0]
|
||||||
|
.long 0
|
||||||
|
.toc
|
||||||
|
T.ThreadRoot: .tc .ThreadRoot[tc], ThreadRoot[ds]
|
||||||
|
.globl .ThreadRoot[PR]
|
||||||
|
.csect .ThreadRoot[PR]
|
||||||
|
|
||||||
|
.set argarea, 32
|
||||||
|
.set linkarea, 24
|
||||||
|
.set locstckarea, 0
|
||||||
|
.set nfprs, 18
|
||||||
|
.set ngprs, 19
|
||||||
|
.set szdsa, 8*nfprs+4*ngprs+linkarea+argarea+locstckarea
|
||||||
|
|
||||||
|
|
||||||
|
mflr 0
|
||||||
|
mfcr 12
|
||||||
|
bl ._savef14
|
||||||
|
cror 0xf, 0xf, 0xf
|
||||||
|
stm 13, -8*nfprs-4*ngprs(1)
|
||||||
|
st 0, 8(1)
|
||||||
|
st 12, 4(1)
|
||||||
|
st 4, 24(1)
|
||||||
|
st 5, 28(1)
|
||||||
|
st 6, 32(1)
|
||||||
|
stu 1, -szdsa(1)
|
||||||
|
|
||||||
|
muli 11,3,1 # copy contents of register r24 to r11
|
||||||
|
bl .branch[PR] # call function branch
|
||||||
|
cror 0xf, 0xf, 0xf # no operation
|
||||||
|
|
||||||
|
ai 1,1,szdsa
|
||||||
|
lm 13, -8*nfprs-4*ngprs(1)
|
||||||
|
bl ._restf14
|
||||||
|
cror 0xf, 0xf, 0xf
|
||||||
|
l 0, 8(1)
|
||||||
|
l 12, 4(1)
|
||||||
|
mtlr 0
|
||||||
|
mtcrf 0x38, 12
|
||||||
|
l 4, 24(1)
|
||||||
|
l 5, 28(1)
|
||||||
|
l 6, 32(1)
|
||||||
|
|
||||||
|
mflr 0
|
||||||
|
mfcr 12
|
||||||
|
bl ._savef14
|
||||||
|
cror 0xf, 0xf, 0xf
|
||||||
|
stm 13, -8*nfprs-4*ngprs(1)
|
||||||
|
st 0, 8(1)
|
||||||
|
st 12, 4(1)
|
||||||
|
st 6, 24(1)
|
||||||
|
stu 1, -szdsa(1)
|
||||||
|
|
||||||
|
muli 3, 4,1 # load user function parameter r22 to r3
|
||||||
|
muli 11,5,1 # copy contents of register r21 to r11
|
||||||
|
bl .branch[PR] # call function branch
|
||||||
|
cror 0xf, 0xf, 0xf # no operation
|
||||||
|
|
||||||
|
ai 1,1,szdsa
|
||||||
|
lm 13, -8*nfprs-4*ngprs(1)
|
||||||
|
bl ._restf14
|
||||||
|
cror 0xf, 0xf, 0xf
|
||||||
|
l 0, 8(1)
|
||||||
|
l 12, 4(1)
|
||||||
|
mtlr 0
|
||||||
|
mtcrf 0x38, 12
|
||||||
|
l 6, 24(1)
|
||||||
|
|
||||||
|
muli 11,6,1 # copy contents of register r23 to r11
|
||||||
|
bl .branch[PR] # call function branch
|
||||||
|
cror 0xf, 0xf, 0xf # no operation
|
||||||
|
brl # the programme should not return here.
|
||||||
|
|
||||||
|
.extern ._savef14
|
||||||
|
.extern ._restf14
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.globl SWITCH[ds]
|
||||||
|
.csect SWITCH[ds]
|
||||||
|
.long .SWITCH[PR]
|
||||||
|
.long TOC[tc0]
|
||||||
|
.long 0
|
||||||
|
.toc
|
||||||
|
T.SWITCH: .tc .SWITCH[tc], SWITCH[ds]
|
||||||
|
.globl .SWITCH[PR]
|
||||||
|
.csect .SWITCH[PR]
|
||||||
|
|
||||||
|
st 1, 0(3) # store stack pointer
|
||||||
|
stm 13, 20(3) # store general purpose registers (13 -31)
|
||||||
|
stfd 14, 96(3) # store floating point registers (14 -31)
|
||||||
|
stfd 15, 104(3) # there is no single instruction to do for
|
||||||
|
stfd 16, 112(3) # floating point registers. so do one by one
|
||||||
|
stfd 17, 120(3)
|
||||||
|
stfd 18, 128(3)
|
||||||
|
stfd 19, 136(3)
|
||||||
|
stfd 20, 144(3)
|
||||||
|
stfd 21, 152(3)
|
||||||
|
stfd 22, 160(3)
|
||||||
|
stfd 23, 168(3)
|
||||||
|
stfd 24, 176(3)
|
||||||
|
stfd 25, 184(3)
|
||||||
|
stfd 26, 192(3)
|
||||||
|
stfd 27, 200(3)
|
||||||
|
stfd 28, 208(3)
|
||||||
|
stfd 29, 216(3)
|
||||||
|
stfd 30, 224(3)
|
||||||
|
stfd 31, 232(3)
|
||||||
|
mflr 0 # move link register value to register 0
|
||||||
|
st 0, 244(3) # store link register value
|
||||||
|
|
||||||
|
mfcr 12 # move condition register to register 12
|
||||||
|
st 12, 240(3) # store condition register value
|
||||||
|
|
||||||
|
|
||||||
|
l 1, 0(4) # load stack pointer
|
||||||
|
l 0, 244(4) # load link register value
|
||||||
|
mtlr 0
|
||||||
|
l 12, 240(4) # load condition register value
|
||||||
|
mtcrf 0x38, 12
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
lm 13, 20(4) # load into general purpose registers (13 -31)
|
||||||
|
lfd 14, 96(4) # load into floating point registers (14 -31)
|
||||||
|
lfd 15, 104(4) # there is no single instruction for
|
||||||
|
lfd 16, 112(4) # loading into more than one floating point
|
||||||
|
lfd 17, 120(4) # registers. so do one by one.
|
||||||
|
lfd 18, 128(4)
|
||||||
|
lfd 19, 136(4)
|
||||||
|
lfd 20, 144(4)
|
||||||
|
lfd 21, 152(4)
|
||||||
|
lfd 22, 160(4)
|
||||||
|
lfd 23, 168(4)
|
||||||
|
lfd 24, 176(4)
|
||||||
|
lfd 25, 184(4)
|
||||||
|
lfd 26, 192(4)
|
||||||
|
lfd 27, 200(4)
|
||||||
|
lfd 28, 208(4)
|
||||||
|
lfd 29, 216(4)
|
||||||
|
lfd 30, 224(4)
|
||||||
|
lfd 31, 232(4)
|
||||||
|
l 3, 16(4)
|
||||||
|
l 5, 4(4)
|
||||||
|
l 6, 12(4)
|
||||||
|
l 4, 8(4)
|
||||||
|
|
||||||
|
brl # branch to the address held in link register.
|
||||||
|
#endif // PowerPC
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef ALPHA
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Porting to Alpha was done by Shuichi Oikawa (shui@sfc.keio.ac.jp).
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Symbolic register names and register saving rules
|
||||||
|
*
|
||||||
|
* Legend:
|
||||||
|
* T Saved by caller (Temporaries)
|
||||||
|
* S Saved by callee (call-Safe registers)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define v0 $0 /* (T) return value */
|
||||||
|
#define t0 $1 /* (T) temporary registers */
|
||||||
|
#define s0 $9 /* (S) call-safe registers */
|
||||||
|
#define s1 $10
|
||||||
|
#define s2 $11
|
||||||
|
#define s3 $12
|
||||||
|
#define s4 $13
|
||||||
|
#define s5 $14
|
||||||
|
#define s6 $15
|
||||||
|
#define a0 $16 /* (T) argument registers */
|
||||||
|
#define a1 $17
|
||||||
|
#define ai $25 /* (T) argument information */
|
||||||
|
#define ra $26 /* (T) return address */
|
||||||
|
#define pv $27 /* (T) procedure value */
|
||||||
|
#define gp $29 /* (T) (local) data pointer */
|
||||||
|
#define sp $30 /* (S) stack pointer */
|
||||||
|
#define zero $31 /* wired zero */
|
||||||
|
|
||||||
|
.set noreorder # unless overridden
|
||||||
|
.align 3
|
||||||
|
.text
|
||||||
|
|
||||||
|
.globl ThreadRoot
|
||||||
|
.ent ThreadRoot,0
|
||||||
|
ThreadRoot:
|
||||||
|
.frame sp,0,ra
|
||||||
|
ldgp gp,0(pv)
|
||||||
|
|
||||||
|
mov zero,s6 # Clearing the frame pointer here
|
||||||
|
# makes gdb backtraces of thread stacks
|
||||||
|
# end here (I hope!)
|
||||||
|
mov StartupPC,pv
|
||||||
|
jsr ra,(pv) # call startup procedure
|
||||||
|
ldgp gp,0(ra)
|
||||||
|
|
||||||
|
mov InitialArg,a0
|
||||||
|
mov InitialPC,pv
|
||||||
|
jsr ra,(pv) # call main procedure
|
||||||
|
ldgp gp,0(ra)
|
||||||
|
|
||||||
|
mov WhenDonePC,pv
|
||||||
|
jsr ra,(pv) # when done, call clean up procedure
|
||||||
|
ldgp gp,0(ra)
|
||||||
|
|
||||||
|
.end ThreadRoot # NEVER REACHED
|
||||||
|
|
||||||
|
/* a0 -- pointer to old Thread *
|
||||||
|
* a1 -- pointer to new Thread */
|
||||||
|
.globl SWITCH
|
||||||
|
.ent SWITCH,0
|
||||||
|
SWITCH:
|
||||||
|
.frame sp,0,ra
|
||||||
|
ldgp gp,0(pv)
|
||||||
|
|
||||||
|
stq ra, PC(a0) # save return address
|
||||||
|
stq gp, GP(a0)
|
||||||
|
stq sp, SP(a0) # save new stack pointer
|
||||||
|
stq s0, S0(a0) # save all the callee-save registers
|
||||||
|
stq s1, S1(a0)
|
||||||
|
stq s2, S2(a0)
|
||||||
|
stq s3, S3(a0)
|
||||||
|
stq s4, S4(a0)
|
||||||
|
stq s5, S5(a0)
|
||||||
|
stq s6, S6(a0) # save frame pointer
|
||||||
|
|
||||||
|
ldq ra, PC(a1) # load the return address
|
||||||
|
ldq gp, GP(a1)
|
||||||
|
ldq sp, SP(a1) # load the new stack pointer
|
||||||
|
ldq s0, S0(a1) # load the callee-save registers
|
||||||
|
ldq s1, S1(a1)
|
||||||
|
ldq s2, S2(a1)
|
||||||
|
ldq s3, S3(a1)
|
||||||
|
ldq s4, S4(a1)
|
||||||
|
ldq s5, S5(a1)
|
||||||
|
ldq s6, S6(a1)
|
||||||
|
|
||||||
|
mov ra,pv
|
||||||
|
ret zero,(ra)
|
||||||
|
|
||||||
|
.end SWITCH
|
||||||
|
|
||||||
|
#endif // ALPHA
|
||||||
268
code/threads/switch.h
Executable file
268
code/threads/switch.h
Executable file
@@ -0,0 +1,268 @@
|
|||||||
|
/* switch.h
|
||||||
|
* Definitions needed for implementing context switching.
|
||||||
|
*
|
||||||
|
* Context switching is inherently machine dependent, since
|
||||||
|
* the registers to be saved, how to set up an initial
|
||||||
|
* call frame, etc, are all specific to a processor architecture.
|
||||||
|
*
|
||||||
|
* This file currently supports the DEC MIPS, DEC Alpha, SUN SPARC,
|
||||||
|
* HP PARISC, IBM PowerPC, and Intel x86 architectures.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright (c) 1992-1996 The Regents of the University of California.
|
||||||
|
All rights reserved. See copyright.h for copyright notice and limitation
|
||||||
|
of liability and disclaimer of warranty provisions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SWITCH_H
|
||||||
|
#define SWITCH_H
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
|
||||||
|
#ifdef DECMIPS
|
||||||
|
|
||||||
|
/* Registers that must be saved during a context switch.
|
||||||
|
* These are the offsets from the beginning of the Thread object,
|
||||||
|
* in bytes, used in switch.s
|
||||||
|
*/
|
||||||
|
#define SP 0
|
||||||
|
#define S0 4
|
||||||
|
#define S1 8
|
||||||
|
#define S2 12
|
||||||
|
#define S3 16
|
||||||
|
#define S4 20
|
||||||
|
#define S5 24
|
||||||
|
#define S6 28
|
||||||
|
#define S7 32
|
||||||
|
#define FP 36
|
||||||
|
#define PC 40
|
||||||
|
|
||||||
|
/* To fork a thread, we set up its saved register state, so that
|
||||||
|
* when we switch to the thread, it will start running in ThreadRoot.
|
||||||
|
*
|
||||||
|
* The following are the initial registers we need to set up to
|
||||||
|
* pass values into ThreadRoot (for instance, containing the procedure
|
||||||
|
* for the thread to run). The first set is the registers as used
|
||||||
|
* by ThreadRoot; the second set is the locations for these initial
|
||||||
|
* values in the Thread object -- used in Thread::AllocateStack().
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define InitialPC s0
|
||||||
|
#define InitialArg s1
|
||||||
|
#define WhenDonePC s2
|
||||||
|
#define StartupPC s3
|
||||||
|
|
||||||
|
#define PCState (PC/4-1)
|
||||||
|
#define FPState (FP/4-1)
|
||||||
|
#define InitialPCState (S0/4-1)
|
||||||
|
#define InitialArgState (S1/4-1)
|
||||||
|
#define WhenDonePCState (S2/4-1)
|
||||||
|
#define StartupPCState (S3/4-1)
|
||||||
|
|
||||||
|
#endif // DECMIPS
|
||||||
|
|
||||||
|
#ifdef SPARC
|
||||||
|
|
||||||
|
/* Registers that must be saved during a context switch. See comment above. */
|
||||||
|
#define I0 4
|
||||||
|
#define I1 8
|
||||||
|
#define I2 12
|
||||||
|
#define I3 16
|
||||||
|
#define I4 20
|
||||||
|
#define I5 24
|
||||||
|
#define I6 28
|
||||||
|
#define I7 32
|
||||||
|
|
||||||
|
/* Aliases used for clearing code. */
|
||||||
|
#define FP I6
|
||||||
|
#define PC I7
|
||||||
|
|
||||||
|
/* Registers for ThreadRoot. See comment above. */
|
||||||
|
#define InitialPC %o0
|
||||||
|
#define InitialArg %o1
|
||||||
|
#define WhenDonePC %o2
|
||||||
|
#define StartupPC %o3
|
||||||
|
|
||||||
|
#define PCState (PC/4-1)
|
||||||
|
#define InitialPCState (I0/4-1)
|
||||||
|
#define InitialArgState (I1/4-1)
|
||||||
|
#define WhenDonePCState (I2/4-1)
|
||||||
|
#define StartupPCState (I3/4-1)
|
||||||
|
|
||||||
|
#endif // SPARC
|
||||||
|
|
||||||
|
#ifdef PARISC
|
||||||
|
|
||||||
|
/* Registers that must be saved during a context switch. See comment above. */
|
||||||
|
#define SP 0
|
||||||
|
#define S0 4
|
||||||
|
#define S1 8
|
||||||
|
#define S2 12
|
||||||
|
#define S3 16
|
||||||
|
#define S4 20
|
||||||
|
#define S5 24
|
||||||
|
#define S6 28
|
||||||
|
#define S7 32
|
||||||
|
#define S8 36
|
||||||
|
#define S9 40
|
||||||
|
#define S10 44
|
||||||
|
#define S11 48
|
||||||
|
#define S12 52
|
||||||
|
#define S13 56
|
||||||
|
#define S14 60
|
||||||
|
#define S15 64
|
||||||
|
#define PC 68
|
||||||
|
|
||||||
|
/* Registers for ThreadRoot. See comment above. */
|
||||||
|
#define InitialPC %r3 /* S0 */
|
||||||
|
#define InitialArg %r4
|
||||||
|
#define WhenDonePC %r5
|
||||||
|
#define StartupPC %r6
|
||||||
|
|
||||||
|
#define PCState (PC/4-1)
|
||||||
|
#define InitialPCState (S0/4-1)
|
||||||
|
#define InitialArgState (S1/4-1)
|
||||||
|
#define WhenDonePCState (S2/4-1)
|
||||||
|
#define StartupPCState (S3/4-1)
|
||||||
|
|
||||||
|
#endif // PARISC
|
||||||
|
|
||||||
|
#ifdef x86
|
||||||
|
|
||||||
|
/* the offsets of the registers from the beginning of the thread object */
|
||||||
|
#define _ESP 0
|
||||||
|
#define _EAX 4
|
||||||
|
#define _EBX 8
|
||||||
|
#define _ECX 12
|
||||||
|
#define _EDX 16
|
||||||
|
#define _EBP 20
|
||||||
|
#define _ESI 24
|
||||||
|
#define _EDI 28
|
||||||
|
#define _PC 32
|
||||||
|
|
||||||
|
/* These definitions are used in Thread::AllocateStack(). */
|
||||||
|
#define PCState (_PC/4-1)
|
||||||
|
#define FPState (_EBP/4-1)
|
||||||
|
#define InitialPCState (_ESI/4-1)
|
||||||
|
#define InitialArgState (_EDX/4-1)
|
||||||
|
#define WhenDonePCState (_EDI/4-1)
|
||||||
|
#define StartupPCState (_ECX/4-1)
|
||||||
|
|
||||||
|
#define InitialPC %esi
|
||||||
|
#define InitialArg %edx
|
||||||
|
#define WhenDonePC %edi
|
||||||
|
#define StartupPC %ecx
|
||||||
|
|
||||||
|
#endif // x86
|
||||||
|
|
||||||
|
#ifdef PowerPC
|
||||||
|
|
||||||
|
#define SP 0 // stack pointer
|
||||||
|
#define P1 4 // parameters
|
||||||
|
#define P2 8
|
||||||
|
#define P3 12
|
||||||
|
#define P4 16
|
||||||
|
#define GP13 20 // general purpose registers 13-31
|
||||||
|
#define GP14 24
|
||||||
|
#define GP15 28
|
||||||
|
#define GP16 32
|
||||||
|
#define GP17 36
|
||||||
|
#define GP18 40
|
||||||
|
#define GP19 44
|
||||||
|
#define GP20 48
|
||||||
|
#define GP21 52
|
||||||
|
#define GP22 56
|
||||||
|
#define GP23 60
|
||||||
|
#define GP24 64
|
||||||
|
#define GP25 68
|
||||||
|
#define GP26 72
|
||||||
|
#define GP27 76
|
||||||
|
#define GP28 80
|
||||||
|
#define GP29 84
|
||||||
|
#define GP30 88
|
||||||
|
#define GP31 92
|
||||||
|
#define FP13 96 // floating point registers 14-31
|
||||||
|
#define FP15 104
|
||||||
|
#define FP16 112
|
||||||
|
#define FP17 120
|
||||||
|
#define FP18 128
|
||||||
|
#define FP19 136
|
||||||
|
#define FP20 144
|
||||||
|
#define FP21 152
|
||||||
|
#define FP22 160
|
||||||
|
#define FP23 168
|
||||||
|
#define FP24 176
|
||||||
|
#define FP25 184
|
||||||
|
#define FP26 192
|
||||||
|
#define FP27 200
|
||||||
|
#define FP28 208
|
||||||
|
#define FP29 216
|
||||||
|
#define FP30 224
|
||||||
|
#define FP31 232
|
||||||
|
#define CR 240 // control register
|
||||||
|
#define LR 244 // link register
|
||||||
|
#define TOC 248 // Table Of Contents
|
||||||
|
|
||||||
|
|
||||||
|
// for ThreadRoot assembly function
|
||||||
|
|
||||||
|
#define InitialPCState 0 // (P1/4 - 1) // user function address
|
||||||
|
#define InitialArgState 1 // (P2/4 - 1) // user function argument
|
||||||
|
#define WhenDonePCState 2 // (P3/4 - 1) // clean up function addr
|
||||||
|
#define StartupPCState 3 // (P4/4 - 1) // start up function addr
|
||||||
|
#define PCState 60 // (LR/4 - 1) // ThreadRoot addr (first time).
|
||||||
|
// Later PC addr when SWITCH
|
||||||
|
// occured
|
||||||
|
|
||||||
|
#define InitialLR 21
|
||||||
|
#define InitialArg 22
|
||||||
|
#define WhenDoneLR 23
|
||||||
|
#define StartupLR 24
|
||||||
|
|
||||||
|
#endif // PowerPC
|
||||||
|
|
||||||
|
#ifdef ALPHA
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Porting to Alpha was done by Shuichi Oikawa (shui@sfc.keio.ac.jp).
|
||||||
|
*/
|
||||||
|
/* Registers that must be saved during a context switch.
|
||||||
|
* These are the offsets from the beginning of the Thread object,
|
||||||
|
* in bytes, used in switch.s
|
||||||
|
*/
|
||||||
|
#define SP (0*8)
|
||||||
|
#define S0 (1*8)
|
||||||
|
#define S1 (2*8)
|
||||||
|
#define S2 (3*8)
|
||||||
|
#define S3 (4*8)
|
||||||
|
#define S4 (5*8)
|
||||||
|
#define S5 (6*8)
|
||||||
|
#define S6 (7*8) /* used as FP (Frame Pointer) */
|
||||||
|
#define GP (8*8)
|
||||||
|
#define PC (9*8)
|
||||||
|
|
||||||
|
/* To fork a thread, we set up its saved register state, so that
|
||||||
|
* when we switch to the thread, it will start running in ThreadRoot.
|
||||||
|
*
|
||||||
|
* The following are the initial registers we need to set up to
|
||||||
|
* pass values into ThreadRoot (for instance, containing the procedure
|
||||||
|
* for the thread to run). The first set is the registers as used
|
||||||
|
* by ThreadRoot; the second set is the locations for these initial
|
||||||
|
* values in the Thread object -- used in Thread::StackAllocate().
|
||||||
|
*/
|
||||||
|
#define InitialPC s0
|
||||||
|
#define InitialArg s1
|
||||||
|
#define WhenDonePC s2
|
||||||
|
#define StartupPC s3
|
||||||
|
|
||||||
|
#define PCState (PC/8-1)
|
||||||
|
#define FPState (S6/8-1)
|
||||||
|
#define InitialPCState (S0/8-1)
|
||||||
|
#define InitialArgState (S1/8-1)
|
||||||
|
#define WhenDonePCState (S2/8-1)
|
||||||
|
#define StartupPCState (S3/8-1)
|
||||||
|
|
||||||
|
#endif // HOST_ALPHA
|
||||||
|
|
||||||
|
#endif // SWITCH_H
|
||||||
297
code/threads/synch.cc
Executable file
297
code/threads/synch.cc
Executable file
@@ -0,0 +1,297 @@
|
|||||||
|
// synch.cc
|
||||||
|
// Routines for synchronizing threads. Three kinds of
|
||||||
|
// synchronization routines are defined here: semaphores, locks
|
||||||
|
// and condition variables.
|
||||||
|
//
|
||||||
|
// Any implementation of a synchronization routine needs some
|
||||||
|
// primitive atomic operation. We assume Nachos is running on
|
||||||
|
// a uniprocessor, and thus atomicity can be provided by
|
||||||
|
// turning off interrupts. While interrupts are disabled, no
|
||||||
|
// context switch can occur, and thus the current thread is guaranteed
|
||||||
|
// to hold the CPU throughout, until interrupts are reenabled.
|
||||||
|
//
|
||||||
|
// Because some of these routines might be called with interrupts
|
||||||
|
// already disabled (Semaphore::V for one), instead of turning
|
||||||
|
// on interrupts at the end of the atomic operation, we always simply
|
||||||
|
// re-set the interrupt state back to its original value (whether
|
||||||
|
// that be disabled or enabled).
|
||||||
|
//
|
||||||
|
// Once we'e implemented one set of higher level atomic operations,
|
||||||
|
// we can implement others using that implementation. We illustrate
|
||||||
|
// this by implementing locks and condition variables on top of
|
||||||
|
// semaphores, instead of directly enabling and disabling interrupts.
|
||||||
|
//
|
||||||
|
// Locks are implemented using a semaphore to keep track of
|
||||||
|
// whether the lock is held or not -- a semaphore value of 0 means
|
||||||
|
// the lock is busy; a semaphore value of 1 means the lock is free.
|
||||||
|
//
|
||||||
|
// The implementation of condition variables using semaphores is
|
||||||
|
// a bit trickier, as explained below under Condition::Wait.
|
||||||
|
//
|
||||||
|
// Copyright (c) 1992-1996 The Regents of the University of California.
|
||||||
|
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||||
|
// of liability and disclaimer of warranty provisions.
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
#include "synch.h"
|
||||||
|
#include "main.h"
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Semaphore::Semaphore
|
||||||
|
// Initialize a semaphore, so that it can be used for synchronization.
|
||||||
|
//
|
||||||
|
// "debugName" is an arbitrary name, useful for debugging.
|
||||||
|
// "initialValue" is the initial value of the semaphore.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
Semaphore::Semaphore(char* debugName, int initialValue)
|
||||||
|
{
|
||||||
|
name = debugName;
|
||||||
|
value = initialValue;
|
||||||
|
queue = new List<Thread *>;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Semaphore::Semaphore
|
||||||
|
// De-allocate semaphore, when no longer needed. Assume no one
|
||||||
|
// is still waiting on the semaphore!
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
Semaphore::~Semaphore()
|
||||||
|
{
|
||||||
|
delete queue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Semaphore::P
|
||||||
|
// Wait until semaphore value > 0, then decrement. Checking the
|
||||||
|
// value and decrementing must be done atomically, so we
|
||||||
|
// need to disable interrupts before checking the value.
|
||||||
|
//
|
||||||
|
// Note that Thread::Sleep assumes that interrupts are disabled
|
||||||
|
// when it is called.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
Semaphore::P()
|
||||||
|
{
|
||||||
|
Interrupt *interrupt = kernel->interrupt;
|
||||||
|
Thread *currentThread = kernel->currentThread;
|
||||||
|
|
||||||
|
// disable interrupts
|
||||||
|
IntStatus oldLevel = interrupt->SetLevel(IntOff);
|
||||||
|
|
||||||
|
while (value == 0) { // semaphore not available
|
||||||
|
queue->Append(currentThread); // so go to sleep
|
||||||
|
currentThread->Sleep(FALSE);
|
||||||
|
}
|
||||||
|
value--; // semaphore available, consume its value
|
||||||
|
|
||||||
|
// re-enable interrupts
|
||||||
|
(void) interrupt->SetLevel(oldLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Semaphore::V
|
||||||
|
// Increment semaphore value, waking up a waiter if necessary.
|
||||||
|
// As with P(), this operation must be atomic, so we need to disable
|
||||||
|
// interrupts. Scheduler::ReadyToRun() assumes that interrupts
|
||||||
|
// are disabled when it is called.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
Semaphore::V()
|
||||||
|
{
|
||||||
|
Interrupt *interrupt = kernel->interrupt;
|
||||||
|
|
||||||
|
// disable interrupts
|
||||||
|
IntStatus oldLevel = interrupt->SetLevel(IntOff);
|
||||||
|
|
||||||
|
if (!queue->IsEmpty()) { // make thread ready.
|
||||||
|
kernel->scheduler->ReadyToRun(queue->RemoveFront());
|
||||||
|
}
|
||||||
|
value++;
|
||||||
|
|
||||||
|
// re-enable interrupts
|
||||||
|
(void) interrupt->SetLevel(oldLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Semaphore::SelfTest, SelfTestHelper
|
||||||
|
// Test the semaphore implementation, by using a semaphore
|
||||||
|
// to control two threads ping-ponging back and forth.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
static Semaphore *ping;
|
||||||
|
static void
|
||||||
|
SelfTestHelper (Semaphore *pong)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
ping->P();
|
||||||
|
pong->V();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Semaphore::SelfTest()
|
||||||
|
{
|
||||||
|
Thread *helper = new Thread("ping", 1);
|
||||||
|
|
||||||
|
ASSERT(value == 0); // otherwise test won't work!
|
||||||
|
ping = new Semaphore("ping", 0);
|
||||||
|
helper->Fork((VoidFunctionPtr) SelfTestHelper, this);
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
ping->V();
|
||||||
|
this->P();
|
||||||
|
}
|
||||||
|
delete ping;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Lock::Lock
|
||||||
|
// Initialize a lock, so that it can be used for synchronization.
|
||||||
|
// Initially, unlocked.
|
||||||
|
//
|
||||||
|
// "debugName" is an arbitrary name, useful for debugging.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
Lock::Lock(char* debugName)
|
||||||
|
{
|
||||||
|
name = debugName;
|
||||||
|
semaphore = new Semaphore("lock", 1); // initially, unlocked
|
||||||
|
lockHolder = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Lock::~Lock
|
||||||
|
// Deallocate a lock
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
Lock::~Lock()
|
||||||
|
{
|
||||||
|
delete semaphore;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Lock::Acquire
|
||||||
|
// Atomically wait until the lock is free, then set it to busy.
|
||||||
|
// Equivalent to Semaphore::P(), with the semaphore value of 0
|
||||||
|
// equal to busy, and semaphore value of 1 equal to free.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void Lock::Acquire()
|
||||||
|
{
|
||||||
|
semaphore->P();
|
||||||
|
lockHolder = kernel->currentThread;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Lock::Release
|
||||||
|
// Atomically set lock to be free, waking up a thread waiting
|
||||||
|
// for the lock, if any.
|
||||||
|
// Equivalent to Semaphore::V(), with the semaphore value of 0
|
||||||
|
// equal to busy, and semaphore value of 1 equal to free.
|
||||||
|
//
|
||||||
|
// By convention, only the thread that acquired the lock
|
||||||
|
// may release it.
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
|
||||||
|
void Lock::Release()
|
||||||
|
{
|
||||||
|
ASSERT(IsHeldByCurrentThread());
|
||||||
|
lockHolder = NULL;
|
||||||
|
semaphore->V();
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Condition::Condition
|
||||||
|
// Initialize a condition variable, so that it can be
|
||||||
|
// used for synchronization. Initially, no one is waiting
|
||||||
|
// on the condition.
|
||||||
|
//
|
||||||
|
// "debugName" is an arbitrary name, useful for debugging.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
Condition::Condition(char* debugName)
|
||||||
|
{
|
||||||
|
name = debugName;
|
||||||
|
waitQueue = new List<Semaphore *>;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Condition::Condition
|
||||||
|
// Deallocate the data structures implementing a condition variable.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
Condition::~Condition()
|
||||||
|
{
|
||||||
|
delete waitQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Condition::Wait
|
||||||
|
// Atomically release monitor lock and go to sleep.
|
||||||
|
// Our implementation uses semaphores to implement this, by
|
||||||
|
// allocating a semaphore for each waiting thread. The signaller
|
||||||
|
// will V() this semaphore, so there is no chance the waiter
|
||||||
|
// will miss the signal, even though the lock is released before
|
||||||
|
// calling P().
|
||||||
|
//
|
||||||
|
// Note: we assume Mesa-style semantics, which means that the
|
||||||
|
// waiter must re-acquire the monitor lock when waking up.
|
||||||
|
//
|
||||||
|
// "conditionLock" -- lock protecting the use of this condition
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void Condition::Wait(Lock* conditionLock)
|
||||||
|
{
|
||||||
|
Semaphore *waiter;
|
||||||
|
|
||||||
|
ASSERT(conditionLock->IsHeldByCurrentThread());
|
||||||
|
|
||||||
|
waiter = new Semaphore("condition", 0);
|
||||||
|
waitQueue->Append(waiter);
|
||||||
|
conditionLock->Release();
|
||||||
|
waiter->P();
|
||||||
|
conditionLock->Acquire();
|
||||||
|
delete waiter;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Condition::Signal
|
||||||
|
// Wake up a thread waiting on this condition, if any.
|
||||||
|
//
|
||||||
|
// Note: we assume Mesa-style semantics, which means that the
|
||||||
|
// signaller doesn't give up control immediately to the thread
|
||||||
|
// being woken up (unlike Hoare-style).
|
||||||
|
//
|
||||||
|
// Also note: we assume the caller holds the monitor lock
|
||||||
|
// (unlike what is described in Birrell's paper). This allows
|
||||||
|
// us to access waitQueue without disabling interrupts.
|
||||||
|
//
|
||||||
|
// "conditionLock" -- lock protecting the use of this condition
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void Condition::Signal(Lock* conditionLock)
|
||||||
|
{
|
||||||
|
Semaphore *waiter;
|
||||||
|
|
||||||
|
ASSERT(conditionLock->IsHeldByCurrentThread());
|
||||||
|
|
||||||
|
if (!waitQueue->IsEmpty()) {
|
||||||
|
waiter = waitQueue->RemoveFront();
|
||||||
|
waiter->V();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Condition::Broadcast
|
||||||
|
// Wake up all threads waiting on this condition, if any.
|
||||||
|
//
|
||||||
|
// "conditionLock" -- lock protecting the use of this condition
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void Condition::Broadcast(Lock* conditionLock)
|
||||||
|
{
|
||||||
|
while (!waitQueue->IsEmpty()) {
|
||||||
|
Signal(conditionLock);
|
||||||
|
}
|
||||||
|
}
|
||||||
144
code/threads/synch.h
Executable file
144
code/threads/synch.h
Executable file
@@ -0,0 +1,144 @@
|
|||||||
|
// synch.h
|
||||||
|
// Data structures for synchronizing threads.
|
||||||
|
//
|
||||||
|
// Three kinds of synchronization are defined here: semaphores,
|
||||||
|
// locks, and condition variables. The implementation for
|
||||||
|
// semaphores is given; for the latter two, only the procedure
|
||||||
|
// interface is given -- they are to be implemented as part of
|
||||||
|
// the first assignment.
|
||||||
|
//
|
||||||
|
// Note that all the synchronization objects take a "name" as
|
||||||
|
// part of the initialization. This is solely for debugging purposes.
|
||||||
|
//
|
||||||
|
// Copyright (c) 1992-1996 The Regents of the University of California.
|
||||||
|
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||||
|
// synch.h -- synchronization primitives.
|
||||||
|
|
||||||
|
#ifndef SYNCH_H
|
||||||
|
#define SYNCH_H
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
#include "thread.h"
|
||||||
|
#include "list.h"
|
||||||
|
#include "main.h"
|
||||||
|
|
||||||
|
// The following class defines a "semaphore" whose value is a non-negative
|
||||||
|
// integer. The semaphore has only two operations P() and V():
|
||||||
|
//
|
||||||
|
// P() -- waits until value > 0, then decrement
|
||||||
|
//
|
||||||
|
// V() -- increment, waking up a thread waiting in P() if necessary
|
||||||
|
//
|
||||||
|
// Note that the interface does *not* allow a thread to read the value of
|
||||||
|
// the semaphore directly -- even if you did read the value, the
|
||||||
|
// only thing you would know is what the value used to be. You don't
|
||||||
|
// know what the value is now, because by the time you get the value
|
||||||
|
// into a register, a context switch might have occurred,
|
||||||
|
// and some other thread might have called P or V, so the true value might
|
||||||
|
// now be different.
|
||||||
|
|
||||||
|
class Semaphore {
|
||||||
|
public:
|
||||||
|
Semaphore(char* debugName, int initialValue); // set initial value
|
||||||
|
~Semaphore(); // de-allocate semaphore
|
||||||
|
char* getName() { return name;} // debugging assist
|
||||||
|
|
||||||
|
void P(); // these are the only operations on a semaphore
|
||||||
|
void V(); // they are both *atomic*
|
||||||
|
void SelfTest(); // test routine for semaphore implementation
|
||||||
|
|
||||||
|
private:
|
||||||
|
char* name; // useful for debugging
|
||||||
|
int value; // semaphore value, always >= 0
|
||||||
|
List<Thread *> *queue;
|
||||||
|
// threads waiting in P() for the value to be > 0
|
||||||
|
};
|
||||||
|
|
||||||
|
// The following class defines a "lock". A lock can be BUSY or FREE.
|
||||||
|
// There are only two operations allowed on a lock:
|
||||||
|
//
|
||||||
|
// Acquire -- wait until the lock is FREE, then set it to BUSY
|
||||||
|
//
|
||||||
|
// Release -- set lock to be FREE, waking up a thread waiting
|
||||||
|
// in Acquire if necessary
|
||||||
|
//
|
||||||
|
// In addition, by convention, only the thread that acquired the lock
|
||||||
|
// may release it. As with semaphores, you can't read the lock value
|
||||||
|
// (because the value might change immediately after you read it).
|
||||||
|
|
||||||
|
class Lock {
|
||||||
|
public:
|
||||||
|
Lock(char* debugName); // initialize lock to be FREE
|
||||||
|
~Lock(); // deallocate lock
|
||||||
|
char* getName() { return name; } // debugging assist
|
||||||
|
|
||||||
|
void Acquire(); // these are the only operations on a lock
|
||||||
|
void Release(); // they are both *atomic*
|
||||||
|
|
||||||
|
bool IsHeldByCurrentThread() {
|
||||||
|
return lockHolder == kernel->currentThread; }
|
||||||
|
// return true if the current thread
|
||||||
|
// holds this lock.
|
||||||
|
|
||||||
|
// Note: SelfTest routine provided by SynchList
|
||||||
|
|
||||||
|
private:
|
||||||
|
char *name; // debugging assist
|
||||||
|
Thread *lockHolder; // thread currently holding lock
|
||||||
|
Semaphore *semaphore; // we use a semaphore to implement lock
|
||||||
|
};
|
||||||
|
|
||||||
|
// The following class defines a "condition variable". A condition
|
||||||
|
// variable does not have a value, but threads may be queued, waiting
|
||||||
|
// on the variable. These are only operations on a condition variable:
|
||||||
|
//
|
||||||
|
// Wait() -- release the lock, relinquish the CPU until signaled,
|
||||||
|
// then re-acquire the lock
|
||||||
|
//
|
||||||
|
// Signal() -- wake up a thread, if there are any waiting on
|
||||||
|
// the condition
|
||||||
|
//
|
||||||
|
// Broadcast() -- wake up all threads waiting on the condition
|
||||||
|
//
|
||||||
|
// All operations on a condition variable must be made while
|
||||||
|
// the current thread has acquired a lock. Indeed, all accesses
|
||||||
|
// to a given condition variable must be protected by the same lock.
|
||||||
|
// In other words, mutual exclusion must be enforced among threads calling
|
||||||
|
// the condition variable operations.
|
||||||
|
//
|
||||||
|
// In Nachos, condition variables are assumed to obey *Mesa*-style
|
||||||
|
// semantics. When a Signal or Broadcast wakes up another thread,
|
||||||
|
// it simply puts the thread on the ready list, and it is the responsibility
|
||||||
|
// of the woken thread to re-acquire the lock (this re-acquire is
|
||||||
|
// taken care of within Wait()). By contrast, some define condition
|
||||||
|
// variables according to *Hoare*-style semantics -- where the signalling
|
||||||
|
// thread gives up control over the lock and the CPU to the woken thread,
|
||||||
|
// which runs immediately and gives back control over the lock to the
|
||||||
|
// signaller when the woken thread leaves the critical section.
|
||||||
|
//
|
||||||
|
// The consequence of using Mesa-style semantics is that some other thread
|
||||||
|
// can acquire the lock, and change data structures, before the woken
|
||||||
|
// thread gets a chance to run. The advantage to Mesa-style semantics
|
||||||
|
// is that it is a lot easier to implement than Hoare-style.
|
||||||
|
|
||||||
|
class Condition {
|
||||||
|
public:
|
||||||
|
Condition(char* debugName); // initialize condition to
|
||||||
|
// "no one waiting"
|
||||||
|
~Condition(); // deallocate the condition
|
||||||
|
char* getName() { return (name); }
|
||||||
|
|
||||||
|
void Wait(Lock *conditionLock); // these are the 3 operations on
|
||||||
|
// condition variables; releasing the
|
||||||
|
// lock and going to sleep are
|
||||||
|
// *atomic* in Wait()
|
||||||
|
void Signal(Lock *conditionLock); // conditionLock must be held by
|
||||||
|
void Broadcast(Lock *conditionLock);// the currentThread for all of
|
||||||
|
// these operations
|
||||||
|
// SelfTest routine provided by SyncLists
|
||||||
|
|
||||||
|
private:
|
||||||
|
char* name;
|
||||||
|
List<Semaphore *> *waitQueue; // list of waiting threads
|
||||||
|
};
|
||||||
|
#endif // SYNCH_H
|
||||||
130
code/threads/synchlist.cc
Executable file
130
code/threads/synchlist.cc
Executable file
@@ -0,0 +1,130 @@
|
|||||||
|
// synchlist.cc
|
||||||
|
// Routines for synchronized access to a list.
|
||||||
|
//
|
||||||
|
// Implemented in "monitor"-style -- surround each procedure with a
|
||||||
|
// lock acquire and release pair, using condition signal and wait for
|
||||||
|
// synchronization.
|
||||||
|
//
|
||||||
|
// 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 "synchlist.h"
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// SynchList<T>::SynchList
|
||||||
|
// Allocate and initialize the data structures needed for a
|
||||||
|
// synchronized list, empty to start with.
|
||||||
|
// Elements can now be added to the list.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
SynchList<T>::SynchList()
|
||||||
|
{
|
||||||
|
list = new List<T>;
|
||||||
|
lock = new Lock("list lock");
|
||||||
|
listEmpty = new Condition("list empty cond");
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// SynchList<T>::~SynchList
|
||||||
|
// De-allocate the data structures created for synchronizing a list.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
SynchList<T>::~SynchList()
|
||||||
|
{
|
||||||
|
delete listEmpty;
|
||||||
|
delete lock;
|
||||||
|
delete list;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// SynchList<T>::Append
|
||||||
|
// Append an "item" to the end of the list. Wake up anyone
|
||||||
|
// waiting for an element to be appended.
|
||||||
|
//
|
||||||
|
// "item" is the thing to put on the list.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void
|
||||||
|
SynchList<T>::Append(T item)
|
||||||
|
{
|
||||||
|
lock->Acquire(); // enforce mutual exclusive access to the list
|
||||||
|
list->Append(item);
|
||||||
|
listEmpty->Signal(lock); // wake up a waiter, if any
|
||||||
|
lock->Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// SynchList<T>::RemoveFront
|
||||||
|
// Remove an "item" from the beginning of the list. Wait if
|
||||||
|
// the list is empty.
|
||||||
|
// Returns:
|
||||||
|
// The removed item.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
T
|
||||||
|
SynchList<T>::RemoveFront()
|
||||||
|
{
|
||||||
|
T item;
|
||||||
|
|
||||||
|
lock->Acquire(); // enforce mutual exclusion
|
||||||
|
while (list->IsEmpty())
|
||||||
|
listEmpty->Wait(lock); // wait until list isn't empty
|
||||||
|
item = list->RemoveFront();
|
||||||
|
lock->Release();
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// SynchList<T>::Apply
|
||||||
|
// Apply function to every item on a list.
|
||||||
|
//
|
||||||
|
// "func" -- the function to apply
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void
|
||||||
|
SynchList<T>::Apply(void (*func)(T))
|
||||||
|
{
|
||||||
|
lock->Acquire(); // enforce mutual exclusion
|
||||||
|
list->Apply(func);
|
||||||
|
lock->Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// SynchList<T>::SelfTest, SelfTestHelper
|
||||||
|
// Test whether the SynchList implementation is working,
|
||||||
|
// by having two threads ping-pong a value between them
|
||||||
|
// using two synchronized lists.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void
|
||||||
|
SynchList<T>::SelfTestHelper (void* data)
|
||||||
|
{
|
||||||
|
SynchList<T>* _this = (SynchList<T>*)data;
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
_this->Append(_this->selfTestPing->RemoveFront());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void
|
||||||
|
SynchList<T>::SelfTest(T val)
|
||||||
|
{
|
||||||
|
Thread *helper = new Thread("ping", 1);
|
||||||
|
|
||||||
|
ASSERT(list->IsEmpty());
|
||||||
|
selfTestPing = new SynchList<T>;
|
||||||
|
helper->Fork(SynchList<T>::SelfTestHelper, this);
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
selfTestPing->Append(val);
|
||||||
|
ASSERT(val == this->RemoveFront());
|
||||||
|
}
|
||||||
|
delete selfTestPing;
|
||||||
|
}
|
||||||
51
code/threads/synchlist.h
Executable file
51
code/threads/synchlist.h
Executable file
@@ -0,0 +1,51 @@
|
|||||||
|
// synchlist.h
|
||||||
|
// Data structures for synchronized access to a list.
|
||||||
|
//
|
||||||
|
// Identical interface to List, except accesses are synchronized.
|
||||||
|
//
|
||||||
|
// Copyright (c) 1992-1996 The Regents of the University of California.
|
||||||
|
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||||
|
// of liability and disclaimer of warranty provisions.
|
||||||
|
|
||||||
|
#ifndef SYNCHLIST_H
|
||||||
|
#define SYNCHLIST_H
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
#include "list.h"
|
||||||
|
#include "synch.h"
|
||||||
|
|
||||||
|
// The following class defines a "synchronized list" -- a list for which
|
||||||
|
// these constraints hold:
|
||||||
|
// 1. Threads trying to remove an item from a list will
|
||||||
|
// wait until the list has an element on it.
|
||||||
|
// 2. One thread at a time can access list data structures
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
class SynchList {
|
||||||
|
public:
|
||||||
|
SynchList(); // initialize a synchronized list
|
||||||
|
~SynchList(); // de-allocate a synchronized list
|
||||||
|
|
||||||
|
void Append(T item); // append item to the end of the list,
|
||||||
|
// and wake up any thread waiting in remove
|
||||||
|
|
||||||
|
T RemoveFront(); // remove the first item from the front of
|
||||||
|
// the list, waiting if the list is empty
|
||||||
|
|
||||||
|
void Apply(void (*f)(T)); // apply function to all elements in list
|
||||||
|
|
||||||
|
void SelfTest(T value); // test the SynchList implementation
|
||||||
|
|
||||||
|
private:
|
||||||
|
List<T> *list; // the list of things
|
||||||
|
Lock *lock; // enforce mutual exclusive access to the list
|
||||||
|
Condition *listEmpty; // wait in Remove if the list is empty
|
||||||
|
|
||||||
|
// these are only to assist SelfTest()
|
||||||
|
SynchList<T> *selfTestPing;
|
||||||
|
static void SelfTestHelper(void* data);
|
||||||
|
};
|
||||||
|
|
||||||
|
#include "synchlist.cc"
|
||||||
|
|
||||||
|
#endif // SYNCHLIST_H
|
||||||
435
code/threads/thread.cc
Executable file
435
code/threads/thread.cc
Executable file
@@ -0,0 +1,435 @@
|
|||||||
|
// thread.cc
|
||||||
|
// Routines to manage threads. These are the main operations:
|
||||||
|
//
|
||||||
|
// Fork -- create a thread to run a procedure concurrently
|
||||||
|
// with the caller (this is done in two steps -- first
|
||||||
|
// allocate the Thread object, then call Fork on it)
|
||||||
|
// Begin -- called when the forked procedure starts up, to turn
|
||||||
|
// interrupts on and clean up after last thread
|
||||||
|
// Finish -- called when the forked procedure finishes, to clean up
|
||||||
|
// Yield -- relinquish control over the CPU to another ready thread
|
||||||
|
// Sleep -- relinquish control over the CPU, but thread is now blocked.
|
||||||
|
// In other words, it will not run again, until explicitly
|
||||||
|
// put back on the ready queue.
|
||||||
|
//
|
||||||
|
// Copyright (c) 1992-1996 The Regents of the University of California.
|
||||||
|
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||||
|
// of liability and disclaimer of warranty provisions.
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
#include "thread.h"
|
||||||
|
#include "switch.h"
|
||||||
|
#include "synch.h"
|
||||||
|
#include "sysdep.h"
|
||||||
|
|
||||||
|
// this is put at the top of the execution stack, for detecting stack overflows
|
||||||
|
const int STACK_FENCEPOST = 0xdedbeef;
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Thread::Thread
|
||||||
|
// Initialize a thread control block, so that we can then call
|
||||||
|
// Thread::Fork.
|
||||||
|
//
|
||||||
|
// "threadName" is an arbitrary string, useful for debugging.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
Thread::Thread(char* threadName, int threadID)
|
||||||
|
{
|
||||||
|
ID = threadID;
|
||||||
|
name = threadName;
|
||||||
|
stackTop = NULL;
|
||||||
|
stack = NULL;
|
||||||
|
status = JUST_CREATED;
|
||||||
|
for (int i = 0; i < MachineStateSize; i++) {
|
||||||
|
machineState[i] = NULL; // not strictly necessary, since
|
||||||
|
// new thread ignores contents
|
||||||
|
// of machine registers
|
||||||
|
}
|
||||||
|
space = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Thread::~Thread
|
||||||
|
// De-allocate a thread.
|
||||||
|
//
|
||||||
|
// NOTE: the current thread *cannot* delete itself directly,
|
||||||
|
// since it is still running on the stack that we need to delete.
|
||||||
|
//
|
||||||
|
// NOTE: if this is the main thread, we can't delete the stack
|
||||||
|
// because we didn't allocate it -- we got it automatically
|
||||||
|
// as part of starting up Nachos.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
Thread::~Thread()
|
||||||
|
{
|
||||||
|
DEBUG(dbgThread, "Deleting thread: " << name);
|
||||||
|
ASSERT(this != kernel->currentThread);
|
||||||
|
if (stack != NULL)
|
||||||
|
DeallocBoundedArray((char *) stack, StackSize * sizeof(int));
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Thread::Fork
|
||||||
|
// Invoke (*func)(arg), allowing caller and callee to execute
|
||||||
|
// concurrently.
|
||||||
|
//
|
||||||
|
// NOTE: although our definition allows only a single argument
|
||||||
|
// to be passed to the procedure, it is possible to pass multiple
|
||||||
|
// arguments by making them fields of a structure, and passing a pointer
|
||||||
|
// to the structure as "arg".
|
||||||
|
//
|
||||||
|
// Implemented as the following steps:
|
||||||
|
// 1. Allocate a stack
|
||||||
|
// 2. Initialize the stack so that a call to SWITCH will
|
||||||
|
// cause it to run the procedure
|
||||||
|
// 3. Put the thread on the ready queue
|
||||||
|
//
|
||||||
|
// "func" is the procedure to run concurrently.
|
||||||
|
// "arg" is a single argument to be passed to the procedure.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
Thread::Fork(VoidFunctionPtr func, void *arg)
|
||||||
|
{
|
||||||
|
Interrupt *interrupt = kernel->interrupt;
|
||||||
|
Scheduler *scheduler = kernel->scheduler;
|
||||||
|
IntStatus oldLevel;
|
||||||
|
|
||||||
|
DEBUG(dbgThread, "Forking thread: " << name << " f(a): " << (int) func << " " << arg);
|
||||||
|
StackAllocate(func, arg);
|
||||||
|
|
||||||
|
oldLevel = interrupt->SetLevel(IntOff);
|
||||||
|
scheduler->ReadyToRun(this); // ReadyToRun assumes that interrupts
|
||||||
|
// are disabled!
|
||||||
|
(void) interrupt->SetLevel(oldLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Thread::CheckOverflow
|
||||||
|
// Check a thread's stack to see if it has overrun the space
|
||||||
|
// that has been allocated for it. If we had a smarter compiler,
|
||||||
|
// we wouldn't need to worry about this, but we don't.
|
||||||
|
//
|
||||||
|
// NOTE: Nachos will not catch all stack overflow conditions.
|
||||||
|
// In other words, your program may still crash because of an overflow.
|
||||||
|
//
|
||||||
|
// If you get bizarre results (such as seg faults where there is no code)
|
||||||
|
// then you *may* need to increase the stack size. You can avoid stack
|
||||||
|
// overflows by not putting large data structures on the stack.
|
||||||
|
// Don't do this: void foo() { int bigArray[10000]; ... }
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
Thread::CheckOverflow()
|
||||||
|
{
|
||||||
|
if (stack != NULL) {
|
||||||
|
#ifdef HPUX // Stacks grow upward on the Snakes
|
||||||
|
ASSERT(stack[StackSize - 1] == STACK_FENCEPOST);
|
||||||
|
#else
|
||||||
|
ASSERT(*stack == STACK_FENCEPOST);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Thread::Begin
|
||||||
|
// Called by ThreadRoot when a thread is about to begin
|
||||||
|
// executing the forked procedure.
|
||||||
|
//
|
||||||
|
// It's main responsibilities are:
|
||||||
|
// 1. deallocate the previously running thread if it finished
|
||||||
|
// (see Thread::Finish())
|
||||||
|
// 2. enable interrupts (so we can get time-sliced)
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
Thread::Begin ()
|
||||||
|
{
|
||||||
|
ASSERT(this == kernel->currentThread);
|
||||||
|
DEBUG(dbgThread, "Beginning thread: " << name);
|
||||||
|
|
||||||
|
kernel->scheduler->CheckToBeDestroyed();
|
||||||
|
kernel->interrupt->Enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Thread::Finish
|
||||||
|
// Called by ThreadRoot when a thread is done executing the
|
||||||
|
// forked procedure.
|
||||||
|
//
|
||||||
|
// NOTE: we can't immediately de-allocate the thread data structure
|
||||||
|
// or the execution stack, because we're still running in the thread
|
||||||
|
// and we're still on the stack! Instead, we tell the scheduler
|
||||||
|
// to call the destructor, once it is running in the context of a different thread.
|
||||||
|
//
|
||||||
|
// NOTE: we disable interrupts, because Sleep() assumes interrupts
|
||||||
|
// are disabled.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
//
|
||||||
|
void
|
||||||
|
Thread::Finish ()
|
||||||
|
{
|
||||||
|
(void) kernel->interrupt->SetLevel(IntOff);
|
||||||
|
ASSERT(this == kernel->currentThread);
|
||||||
|
|
||||||
|
DEBUG(dbgThread, "Finishing thread: " << name);
|
||||||
|
Sleep(TRUE); // invokes SWITCH
|
||||||
|
// not reached
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Thread::Yield
|
||||||
|
// Relinquish the CPU if any other thread is ready to run.
|
||||||
|
// If so, put the thread on the end of the ready list, so that
|
||||||
|
// it will eventually be re-scheduled.
|
||||||
|
//
|
||||||
|
// NOTE: returns immediately if no other thread on the ready queue.
|
||||||
|
// Otherwise returns when the thread eventually works its way
|
||||||
|
// to the front of the ready list and gets re-scheduled.
|
||||||
|
//
|
||||||
|
// NOTE: we disable interrupts, so that looking at the thread
|
||||||
|
// on the front of the ready list, and switching to it, can be done
|
||||||
|
// atomically. On return, we re-set the interrupt level to its
|
||||||
|
// original state, in case we are called with interrupts disabled.
|
||||||
|
//
|
||||||
|
// Similar to Thread::Sleep(), but a little different.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
Thread::Yield ()
|
||||||
|
{
|
||||||
|
Thread *nextThread;
|
||||||
|
IntStatus oldLevel = kernel->interrupt->SetLevel(IntOff);
|
||||||
|
|
||||||
|
ASSERT(this == kernel->currentThread);
|
||||||
|
|
||||||
|
DEBUG(dbgThread, "Yielding thread: " << name);
|
||||||
|
|
||||||
|
nextThread = kernel->scheduler->FindNextToRun();
|
||||||
|
if (nextThread != NULL) {
|
||||||
|
kernel->scheduler->ReadyToRun(this);
|
||||||
|
kernel->scheduler->Run(nextThread, FALSE);
|
||||||
|
}
|
||||||
|
(void) kernel->interrupt->SetLevel(oldLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Thread::Sleep
|
||||||
|
// Relinquish the CPU, because the current thread has either
|
||||||
|
// finished or is blocked waiting on a synchronization
|
||||||
|
// variable (Semaphore, Lock, or Condition). In the latter case,
|
||||||
|
// eventually some thread will wake this thread up, and put it
|
||||||
|
// back on the ready queue, so that it can be re-scheduled.
|
||||||
|
//
|
||||||
|
// NOTE: if there are no threads on the ready queue, that means
|
||||||
|
// we have no thread to run. "Interrupt::Idle" is called
|
||||||
|
// to signify that we should idle the CPU until the next I/O interrupt
|
||||||
|
// occurs (the only thing that could cause a thread to become
|
||||||
|
// ready to run).
|
||||||
|
//
|
||||||
|
// NOTE: we assume interrupts are already disabled, because it
|
||||||
|
// is called from the synchronization routines which must
|
||||||
|
// disable interrupts for atomicity. We need interrupts off
|
||||||
|
// so that there can't be a time slice between pulling the first thread
|
||||||
|
// off the ready list, and switching to it.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
void
|
||||||
|
Thread::Sleep (bool finishing)
|
||||||
|
{
|
||||||
|
Thread *nextThread;
|
||||||
|
|
||||||
|
ASSERT(this == kernel->currentThread);
|
||||||
|
ASSERT(kernel->interrupt->getLevel() == IntOff);
|
||||||
|
|
||||||
|
DEBUG(dbgThread, "Sleeping thread: " << name);
|
||||||
|
|
||||||
|
status = BLOCKED;
|
||||||
|
//cout << "debug Thread::Sleep " << name << "wait for Idle\n";
|
||||||
|
while ((nextThread = kernel->scheduler->FindNextToRun()) == NULL) {
|
||||||
|
kernel->interrupt->Idle(); // no one to run, wait for an interrupt
|
||||||
|
}
|
||||||
|
// returns when it's time for us to run
|
||||||
|
kernel->scheduler->Run(nextThread, finishing);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// ThreadBegin, ThreadFinish, ThreadPrint
|
||||||
|
// Dummy functions because C++ does not (easily) allow pointers to member
|
||||||
|
// functions. So we create a dummy C function
|
||||||
|
// (which we can pass a pointer to), that then simply calls the
|
||||||
|
// member function.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
static void ThreadFinish() { kernel->currentThread->Finish(); }
|
||||||
|
static void ThreadBegin() { kernel->currentThread->Begin(); }
|
||||||
|
void ThreadPrint(Thread *t) { t->Print(); }
|
||||||
|
|
||||||
|
#ifdef PARISC
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// PLabelToAddr
|
||||||
|
// On HPUX, function pointers don't always directly point to code,
|
||||||
|
// so we need to do the conversion.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
static void *
|
||||||
|
PLabelToAddr(void *plabel)
|
||||||
|
{
|
||||||
|
int funcPtr = (int) plabel;
|
||||||
|
|
||||||
|
if (funcPtr & 0x02) {
|
||||||
|
// L-Field is set. This is a PLT pointer
|
||||||
|
funcPtr -= 2; // Get rid of the L bit
|
||||||
|
return (*(void **)funcPtr);
|
||||||
|
} else {
|
||||||
|
// L-field not set.
|
||||||
|
return plabel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Thread::StackAllocate
|
||||||
|
// Allocate and initialize an execution stack. The stack is
|
||||||
|
// initialized with an initial stack frame for ThreadRoot, which:
|
||||||
|
// enables interrupts
|
||||||
|
// calls (*func)(arg)
|
||||||
|
// calls Thread::Finish
|
||||||
|
//
|
||||||
|
// "func" is the procedure to be forked
|
||||||
|
// "arg" is the parameter to be passed to the procedure
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
Thread::StackAllocate (VoidFunctionPtr func, void *arg)
|
||||||
|
{
|
||||||
|
stack = (int *) AllocBoundedArray(StackSize * sizeof(int));
|
||||||
|
|
||||||
|
#ifdef PARISC
|
||||||
|
// HP stack works from low addresses to high addresses
|
||||||
|
// everyone else works the other way: from high addresses to low addresses
|
||||||
|
stackTop = stack + 16; // HP requires 64-byte frame marker
|
||||||
|
stack[StackSize - 1] = STACK_FENCEPOST;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef SPARC
|
||||||
|
stackTop = stack + StackSize - 96; // SPARC stack must contains at
|
||||||
|
// least 1 activation record
|
||||||
|
// to start with.
|
||||||
|
*stack = STACK_FENCEPOST;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PowerPC // RS6000
|
||||||
|
stackTop = stack + StackSize - 16; // RS6000 requires 64-byte frame marker
|
||||||
|
*stack = STACK_FENCEPOST;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef DECMIPS
|
||||||
|
stackTop = stack + StackSize - 4; // -4 to be on the safe side!
|
||||||
|
*stack = STACK_FENCEPOST;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ALPHA
|
||||||
|
stackTop = stack + StackSize - 8; // -8 to be on the safe side!
|
||||||
|
*stack = STACK_FENCEPOST;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef x86
|
||||||
|
// the x86 passes the return address on the stack. In order for SWITCH()
|
||||||
|
// to go to ThreadRoot when we switch to this thread, the return addres
|
||||||
|
// used in SWITCH() must be the starting address of ThreadRoot.
|
||||||
|
stackTop = stack + StackSize - 4; // -4 to be on the safe side!
|
||||||
|
*(--stackTop) = (int) ThreadRoot;
|
||||||
|
*stack = STACK_FENCEPOST;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PARISC
|
||||||
|
machineState[PCState] = PLabelToAddr(ThreadRoot);
|
||||||
|
machineState[StartupPCState] = PLabelToAddr(ThreadBegin);
|
||||||
|
machineState[InitialPCState] = PLabelToAddr(func);
|
||||||
|
machineState[InitialArgState] = arg;
|
||||||
|
machineState[WhenDonePCState] = PLabelToAddr(ThreadFinish);
|
||||||
|
#else
|
||||||
|
machineState[PCState] = (void*)ThreadRoot;
|
||||||
|
machineState[StartupPCState] = (void*)ThreadBegin;
|
||||||
|
machineState[InitialPCState] = (void*)func;
|
||||||
|
machineState[InitialArgState] = (void*)arg;
|
||||||
|
machineState[WhenDonePCState] = (void*)ThreadFinish;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "machine.h"
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Thread::SaveUserState
|
||||||
|
// Save the CPU state of a user program on a context switch.
|
||||||
|
//
|
||||||
|
// Note that a user program thread has *two* sets of CPU registers --
|
||||||
|
// one for its state while executing user code, one for its state
|
||||||
|
// while executing kernel code. This routine saves the former.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
Thread::SaveUserState()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < NumTotalRegs; i++)
|
||||||
|
userRegisters[i] = kernel->machine->ReadRegister(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Thread::RestoreUserState
|
||||||
|
// Restore the CPU state of a user program on a context switch.
|
||||||
|
//
|
||||||
|
// Note that a user program thread has *two* sets of CPU registers --
|
||||||
|
// one for its state while executing user code, one for its state
|
||||||
|
// while executing kernel code. This routine restores the former.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
Thread::RestoreUserState()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < NumTotalRegs; i++)
|
||||||
|
kernel->machine->WriteRegister(i, userRegisters[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// SimpleThread
|
||||||
|
// Loop 5 times, yielding the CPU to another ready thread
|
||||||
|
// each iteration.
|
||||||
|
//
|
||||||
|
// "which" is simply a number identifying the thread, for debugging
|
||||||
|
// purposes.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
static void
|
||||||
|
SimpleThread(int which)
|
||||||
|
{
|
||||||
|
int num;
|
||||||
|
|
||||||
|
for (num = 0; num < 5; num++) {
|
||||||
|
cout << "*** thread " << which << " looped " << num << " times\n";
|
||||||
|
kernel->currentThread->Yield();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Thread::SelfTest
|
||||||
|
// Set up a ping-pong between two threads, by forking a thread
|
||||||
|
// to call SimpleThread, and then calling SimpleThread ourselves.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
Thread::SelfTest()
|
||||||
|
{
|
||||||
|
DEBUG(dbgThread, "Entering Thread::SelfTest");
|
||||||
|
|
||||||
|
Thread *t = new Thread("forked thread", 1);
|
||||||
|
|
||||||
|
t->Fork((VoidFunctionPtr) SimpleThread, (void *) 1);
|
||||||
|
kernel->currentThread->Yield();
|
||||||
|
SimpleThread(0);
|
||||||
|
}
|
||||||
152
code/threads/thread.h
Executable file
152
code/threads/thread.h
Executable file
@@ -0,0 +1,152 @@
|
|||||||
|
// thread.h
|
||||||
|
// Data structures for managing threads. A thread represents
|
||||||
|
// sequential execution of code within a program.
|
||||||
|
// So the state of a thread includes the program counter,
|
||||||
|
// the processor registers, and the execution stack.
|
||||||
|
//
|
||||||
|
// Note that because we allocate a fixed size stack for each
|
||||||
|
// thread, it is possible to overflow the stack -- for instance,
|
||||||
|
// by recursing to too deep a level. The most common reason
|
||||||
|
// for this occuring is allocating large data structures
|
||||||
|
// on the stack. For instance, this will cause problems:
|
||||||
|
//
|
||||||
|
// void foo() { int buf[1000]; ...}
|
||||||
|
//
|
||||||
|
// Instead, you should allocate all data structures dynamically:
|
||||||
|
//
|
||||||
|
// void foo() { int *buf = new int[1000]; ...}
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Bad things happen if you overflow the stack, and in the worst
|
||||||
|
// case, the problem may not be caught explicitly. Instead,
|
||||||
|
// the only symptom may be bizarre segmentation faults. (Of course,
|
||||||
|
// other problems can cause seg faults, so that isn't a sure sign
|
||||||
|
// that your thread stacks are too small.)
|
||||||
|
//
|
||||||
|
// One thing to try if you find yourself with seg faults is to
|
||||||
|
// increase the size of thread stack -- ThreadStackSize.
|
||||||
|
//
|
||||||
|
// In this interface, forking a thread takes two steps.
|
||||||
|
// We must first allocate a data structure for it: "t = new Thread".
|
||||||
|
// Only then can we do the fork: "t->fork(f, arg)".
|
||||||
|
//
|
||||||
|
// Copyright (c) 1992-1996 The Regents of the University of California.
|
||||||
|
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||||
|
// of liability and disclaimer of warranty provisions.
|
||||||
|
|
||||||
|
#ifndef THREAD_H
|
||||||
|
#define THREAD_H
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
#include "utility.h"
|
||||||
|
#include "sysdep.h"
|
||||||
|
#include "machine.h"
|
||||||
|
#include "addrspace.h"
|
||||||
|
|
||||||
|
// CPU register state to be saved on context switch.
|
||||||
|
// The x86 needs to save only a few registers,
|
||||||
|
// SPARC and MIPS needs to save 10 registers,
|
||||||
|
// the Snake needs 18,
|
||||||
|
// and the RS6000 needs to save 75 (!)
|
||||||
|
// For simplicity, I just take the maximum over all architectures.
|
||||||
|
|
||||||
|
#define MachineStateSize 75
|
||||||
|
|
||||||
|
|
||||||
|
// Size of the thread's private execution stack.
|
||||||
|
// WATCH OUT IF THIS ISN'T BIG ENOUGH!!!!!
|
||||||
|
const int StackSize = (8 * 1024); // in words
|
||||||
|
|
||||||
|
|
||||||
|
// Thread state
|
||||||
|
enum ThreadStatus { JUST_CREATED, RUNNING, READY, BLOCKED, ZOMBIE };
|
||||||
|
|
||||||
|
|
||||||
|
// The following class defines a "thread control block" -- which
|
||||||
|
// represents a single thread of execution.
|
||||||
|
//
|
||||||
|
// Every thread has:
|
||||||
|
// an execution stack for activation records ("stackTop" and "stack")
|
||||||
|
// space to save CPU registers while not running ("machineState")
|
||||||
|
// a "status" (running/ready/blocked)
|
||||||
|
//
|
||||||
|
// Some threads also belong to a user address space; threads
|
||||||
|
// that only run in the kernel have a NULL address space.
|
||||||
|
|
||||||
|
class Thread {
|
||||||
|
private:
|
||||||
|
// NOTE: DO NOT CHANGE the order of these first two members.
|
||||||
|
// THEY MUST be in this position for SWITCH to work.
|
||||||
|
int *stackTop; // the current stack pointer
|
||||||
|
void *machineState[MachineStateSize]; // all registers except for stackTop
|
||||||
|
|
||||||
|
public:
|
||||||
|
Thread(char* debugName, int threadID); // initialize a Thread
|
||||||
|
~Thread(); // deallocate a Thread
|
||||||
|
// NOTE -- thread being deleted
|
||||||
|
// must not be running when delete
|
||||||
|
// is called
|
||||||
|
|
||||||
|
// basic thread operations
|
||||||
|
|
||||||
|
void Fork(VoidFunctionPtr func, void *arg);
|
||||||
|
// Make thread run (*func)(arg)
|
||||||
|
void Yield(); // Relinquish the CPU if any
|
||||||
|
// other thread is runnable
|
||||||
|
void Sleep(bool finishing); // Put the thread to sleep and
|
||||||
|
// relinquish the processor
|
||||||
|
void Begin(); // Startup code for the thread
|
||||||
|
void Finish(); // The thread is done executing
|
||||||
|
|
||||||
|
void CheckOverflow(); // Check if thread stack has overflowed
|
||||||
|
void setStatus(ThreadStatus st) { status = st; }
|
||||||
|
ThreadStatus getStatus() { return (status); }
|
||||||
|
char* getName() { return (name); }
|
||||||
|
|
||||||
|
int getID() { return (ID); }
|
||||||
|
void Print() { cout << name; }
|
||||||
|
void SelfTest(); // test whether thread impl is working
|
||||||
|
|
||||||
|
private:
|
||||||
|
// some of the private data for this class is listed above
|
||||||
|
|
||||||
|
int *stack; // Bottom of the stack
|
||||||
|
// NULL if this is the main thread
|
||||||
|
// (If NULL, don't deallocate stack)
|
||||||
|
ThreadStatus status; // ready, running or blocked
|
||||||
|
char* name;
|
||||||
|
int ID;
|
||||||
|
void StackAllocate(VoidFunctionPtr func, void *arg);
|
||||||
|
// Allocate a stack for thread.
|
||||||
|
// Used internally by Fork()
|
||||||
|
|
||||||
|
// A thread running a user program actually has *two* sets of CPU registers --
|
||||||
|
// one for its state while executing user code, one for its state
|
||||||
|
// while executing kernel code.
|
||||||
|
|
||||||
|
int userRegisters[NumTotalRegs]; // user-level CPU register state
|
||||||
|
|
||||||
|
public:
|
||||||
|
void SaveUserState(); // save user-level register state
|
||||||
|
void RestoreUserState(); // restore user-level register state
|
||||||
|
|
||||||
|
AddrSpace *space; // User code this thread is running.
|
||||||
|
};
|
||||||
|
|
||||||
|
// external function, dummy routine whose sole job is to call Thread::Print
|
||||||
|
extern void ThreadPrint(Thread *thread);
|
||||||
|
|
||||||
|
// Magical machine-dependent routines, defined in switch.s
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
// First frame on thread execution stack;
|
||||||
|
// call ThreadBegin
|
||||||
|
// call "func"
|
||||||
|
// (when func returns, if ever) call ThreadFinish()
|
||||||
|
void ThreadRoot();
|
||||||
|
|
||||||
|
// Stop running oldThread and start running newThread
|
||||||
|
void SWITCH(Thread *oldThread, Thread *newThread);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // THREAD_H
|
||||||
316
code/userprog/addrspace.cc
Executable file
316
code/userprog/addrspace.cc
Executable file
@@ -0,0 +1,316 @@
|
|||||||
|
// addrspace.cc
|
||||||
|
// Routines to manage address spaces (executing user programs).
|
||||||
|
//
|
||||||
|
// In order to run a user program, you must:
|
||||||
|
//
|
||||||
|
// 1. link with the -n -T 0 option
|
||||||
|
// 2. run coff2noff to convert the object file to Nachos format
|
||||||
|
// (Nachos object code format is essentially just a simpler
|
||||||
|
// version of the UNIX executable object code format)
|
||||||
|
// 3. load the NOFF file into the Nachos file system
|
||||||
|
// (if you are using the "stub" file system, you
|
||||||
|
// don't need to do this last step)
|
||||||
|
//
|
||||||
|
// Copyright (c) 1992-1996 The Regents of the University of California.
|
||||||
|
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||||
|
// of liability and disclaimer of warranty provisions.
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
#include "main.h"
|
||||||
|
#include "addrspace.h"
|
||||||
|
#include "machine.h"
|
||||||
|
#include "noff.h"
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// SwapHeader
|
||||||
|
// Do little endian to big endian conversion on the bytes in the
|
||||||
|
// object file header, in case the file was generated on a little
|
||||||
|
// endian machine, and we're now running on a big endian machine.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
static void
|
||||||
|
SwapHeader (NoffHeader *noffH)
|
||||||
|
{
|
||||||
|
noffH->noffMagic = WordToHost(noffH->noffMagic);
|
||||||
|
noffH->code.size = WordToHost(noffH->code.size);
|
||||||
|
noffH->code.virtualAddr = WordToHost(noffH->code.virtualAddr);
|
||||||
|
noffH->code.inFileAddr = WordToHost(noffH->code.inFileAddr);
|
||||||
|
#ifdef RDATA
|
||||||
|
noffH->readonlyData.size = WordToHost(noffH->readonlyData.size);
|
||||||
|
noffH->readonlyData.virtualAddr =
|
||||||
|
WordToHost(noffH->readonlyData.virtualAddr);
|
||||||
|
noffH->readonlyData.inFileAddr =
|
||||||
|
WordToHost(noffH->readonlyData.inFileAddr);
|
||||||
|
#endif
|
||||||
|
noffH->initData.size = WordToHost(noffH->initData.size);
|
||||||
|
noffH->initData.virtualAddr = WordToHost(noffH->initData.virtualAddr);
|
||||||
|
noffH->initData.inFileAddr = WordToHost(noffH->initData.inFileAddr);
|
||||||
|
noffH->uninitData.size = WordToHost(noffH->uninitData.size);
|
||||||
|
noffH->uninitData.virtualAddr = WordToHost(noffH->uninitData.virtualAddr);
|
||||||
|
noffH->uninitData.inFileAddr = WordToHost(noffH->uninitData.inFileAddr);
|
||||||
|
|
||||||
|
#ifdef RDATA
|
||||||
|
DEBUG(dbgAddr, "code = " << noffH->code.size <<
|
||||||
|
" readonly = " << noffH->readonlyData.size <<
|
||||||
|
" init = " << noffH->initData.size <<
|
||||||
|
" uninit = " << noffH->uninitData.size << "\n");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// AddrSpace::AddrSpace
|
||||||
|
// Create an address space to run a user program.
|
||||||
|
// Set up the translation from program memory to physical
|
||||||
|
// memory. For now, this is really simple (1:1), since we are
|
||||||
|
// only uniprogramming, and we have a single unsegmented page table
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
AddrSpace::AddrSpace()
|
||||||
|
{
|
||||||
|
pageTable = new TranslationEntry[NumPhysPages];
|
||||||
|
for (int i = 0; i < NumPhysPages; i++) {
|
||||||
|
pageTable[i].virtualPage = i; // for now, virt page # = phys page #
|
||||||
|
pageTable[i].physicalPage = i;
|
||||||
|
pageTable[i].valid = TRUE;
|
||||||
|
pageTable[i].use = FALSE;
|
||||||
|
pageTable[i].dirty = FALSE;
|
||||||
|
pageTable[i].readOnly = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// zero out the entire address space
|
||||||
|
bzero(kernel->machine->mainMemory, MemorySize);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// AddrSpace::~AddrSpace
|
||||||
|
// Dealloate an address space.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
AddrSpace::~AddrSpace()
|
||||||
|
{
|
||||||
|
delete pageTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// AddrSpace::Load
|
||||||
|
// Load a user program into memory from a file.
|
||||||
|
//
|
||||||
|
// Assumes that the page table has been initialized, and that
|
||||||
|
// the object code file is in NOFF format.
|
||||||
|
//
|
||||||
|
// "fileName" is the file containing the object code to load into memory
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
bool
|
||||||
|
AddrSpace::Load(char *fileName)
|
||||||
|
{
|
||||||
|
OpenFile *executable = kernel->fileSystem->Open(fileName);
|
||||||
|
NoffHeader noffH;
|
||||||
|
unsigned int size;
|
||||||
|
|
||||||
|
if (executable == NULL) {
|
||||||
|
cerr << "Unable to open file " << fileName << "\n";
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
executable->ReadAt((char *)&noffH, sizeof(noffH), 0);
|
||||||
|
if ((noffH.noffMagic != NOFFMAGIC) &&
|
||||||
|
(WordToHost(noffH.noffMagic) == NOFFMAGIC))
|
||||||
|
SwapHeader(&noffH);
|
||||||
|
ASSERT(noffH.noffMagic == NOFFMAGIC);
|
||||||
|
|
||||||
|
#ifdef RDATA
|
||||||
|
// how big is address space?
|
||||||
|
size = noffH.code.size + noffH.readonlyData.size + noffH.initData.size +
|
||||||
|
noffH.uninitData.size + UserStackSize;
|
||||||
|
// we need to increase the size
|
||||||
|
// to leave room for the stack
|
||||||
|
#else
|
||||||
|
// how big is address space?
|
||||||
|
size = noffH.code.size + noffH.initData.size + noffH.uninitData.size
|
||||||
|
+ UserStackSize; // we need to increase the size
|
||||||
|
// to leave room for the stack
|
||||||
|
#endif
|
||||||
|
numPages = divRoundUp(size, PageSize);
|
||||||
|
size = numPages * PageSize;
|
||||||
|
|
||||||
|
ASSERT(numPages <= NumPhysPages); // check we're not trying
|
||||||
|
// to run anything too big --
|
||||||
|
// at least until we have
|
||||||
|
// virtual memory
|
||||||
|
|
||||||
|
DEBUG(dbgAddr, "Initializing address space: " << numPages << ", " << size);
|
||||||
|
|
||||||
|
// then, copy in the code and data segments into memory
|
||||||
|
// Note: this code assumes that virtual address = physical address
|
||||||
|
if (noffH.code.size > 0) {
|
||||||
|
DEBUG(dbgAddr, "Initializing code segment.");
|
||||||
|
DEBUG(dbgAddr, noffH.code.virtualAddr << ", " << noffH.code.size);
|
||||||
|
executable->ReadAt(
|
||||||
|
&(kernel->machine->mainMemory[noffH.code.virtualAddr]),
|
||||||
|
noffH.code.size, noffH.code.inFileAddr);
|
||||||
|
}
|
||||||
|
if (noffH.initData.size > 0) {
|
||||||
|
DEBUG(dbgAddr, "Initializing data segment.");
|
||||||
|
DEBUG(dbgAddr, noffH.initData.virtualAddr << ", " << noffH.initData.size);
|
||||||
|
executable->ReadAt(
|
||||||
|
&(kernel->machine->mainMemory[noffH.initData.virtualAddr]),
|
||||||
|
noffH.initData.size, noffH.initData.inFileAddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef RDATA
|
||||||
|
if (noffH.readonlyData.size > 0) {
|
||||||
|
DEBUG(dbgAddr, "Initializing read only data segment.");
|
||||||
|
DEBUG(dbgAddr, noffH.readonlyData.virtualAddr << ", " << noffH.readonlyData.size);
|
||||||
|
executable->ReadAt(
|
||||||
|
&(kernel->machine->mainMemory[noffH.readonlyData.virtualAddr]),
|
||||||
|
noffH.readonlyData.size, noffH.readonlyData.inFileAddr);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
delete executable; // close file
|
||||||
|
return TRUE; // success
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// AddrSpace::Execute
|
||||||
|
// Run a user program using the current thread
|
||||||
|
//
|
||||||
|
// The program is assumed to have already been loaded into
|
||||||
|
// the address space
|
||||||
|
//
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
AddrSpace::Execute(char* fileName)
|
||||||
|
{
|
||||||
|
|
||||||
|
kernel->currentThread->space = this;
|
||||||
|
|
||||||
|
this->InitRegisters(); // set the initial register values
|
||||||
|
this->RestoreState(); // load page table register
|
||||||
|
|
||||||
|
kernel->machine->Run(); // jump to the user progam
|
||||||
|
|
||||||
|
ASSERTNOTREACHED(); // machine->Run never returns;
|
||||||
|
// the address space exits
|
||||||
|
// by doing the syscall "exit"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// AddrSpace::InitRegisters
|
||||||
|
// Set the initial values for the user-level register set.
|
||||||
|
//
|
||||||
|
// We write these directly into the "machine" registers, so
|
||||||
|
// that we can immediately jump to user code. Note that these
|
||||||
|
// will be saved/restored into the currentThread->userRegisters
|
||||||
|
// when this thread is context switched out.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
AddrSpace::InitRegisters()
|
||||||
|
{
|
||||||
|
Machine *machine = kernel->machine;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < NumTotalRegs; i++)
|
||||||
|
machine->WriteRegister(i, 0);
|
||||||
|
|
||||||
|
// Initial program counter -- must be location of "Start", which
|
||||||
|
// is assumed to be virtual address zero
|
||||||
|
machine->WriteRegister(PCReg, 0);
|
||||||
|
|
||||||
|
// Need to also tell MIPS where next instruction is, because
|
||||||
|
// of branch delay possibility
|
||||||
|
// Since instructions occupy four bytes each, the next instruction
|
||||||
|
// after start will be at virtual address four.
|
||||||
|
machine->WriteRegister(NextPCReg, 4);
|
||||||
|
|
||||||
|
// Set the stack register to the end of the address space, where we
|
||||||
|
// allocated the stack; but subtract off a bit, to make sure we don't
|
||||||
|
// accidentally reference off the end!
|
||||||
|
machine->WriteRegister(StackReg, numPages * PageSize - 16);
|
||||||
|
DEBUG(dbgAddr, "Initializing stack pointer: " << numPages * PageSize - 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// AddrSpace::SaveState
|
||||||
|
// On a context switch, save any machine state, specific
|
||||||
|
// to this address space, that needs saving.
|
||||||
|
//
|
||||||
|
// For now, don't need to save anything!
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void AddrSpace::SaveState()
|
||||||
|
{}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// AddrSpace::RestoreState
|
||||||
|
// On a context switch, restore the machine state so that
|
||||||
|
// this address space can run.
|
||||||
|
//
|
||||||
|
// For now, tell the machine where to find the page table.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void AddrSpace::RestoreState()
|
||||||
|
{
|
||||||
|
kernel->machine->pageTable = pageTable;
|
||||||
|
kernel->machine->pageTableSize = numPages;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// AddrSpace::Translate
|
||||||
|
// Translate the virtual address in _vaddr_ to a physical address
|
||||||
|
// and store the physical address in _paddr_.
|
||||||
|
// The flag _isReadWrite_ is false (0) for read-only access; true (1)
|
||||||
|
// for read-write access.
|
||||||
|
// Return any exceptions caused by the address translation.
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
ExceptionType
|
||||||
|
AddrSpace::Translate(unsigned int vaddr, unsigned int *paddr, int isReadWrite)
|
||||||
|
{
|
||||||
|
TranslationEntry *pte;
|
||||||
|
int pfn;
|
||||||
|
unsigned int vpn = vaddr / PageSize;
|
||||||
|
unsigned int offset = vaddr % PageSize;
|
||||||
|
|
||||||
|
if(vpn >= numPages) {
|
||||||
|
return AddressErrorException;
|
||||||
|
}
|
||||||
|
|
||||||
|
pte = &pageTable[vpn];
|
||||||
|
|
||||||
|
if(isReadWrite && pte->readOnly) {
|
||||||
|
return ReadOnlyException;
|
||||||
|
}
|
||||||
|
|
||||||
|
pfn = pte->physicalPage;
|
||||||
|
|
||||||
|
// if the pageFrame is too big, there is something really wrong!
|
||||||
|
// An invalid translation was loaded into the page table or TLB.
|
||||||
|
if (pfn >= NumPhysPages) {
|
||||||
|
DEBUG(dbgAddr, "Illegal physical page " << pfn);
|
||||||
|
return BusErrorException;
|
||||||
|
}
|
||||||
|
|
||||||
|
pte->use = TRUE; // set the use, dirty bits
|
||||||
|
|
||||||
|
if(isReadWrite)
|
||||||
|
pte->dirty = TRUE;
|
||||||
|
|
||||||
|
*paddr = pfn*PageSize + offset;
|
||||||
|
|
||||||
|
ASSERT((*paddr < MemorySize));
|
||||||
|
|
||||||
|
//cerr << " -- AddrSpace::Translate(): vaddr: " << vaddr <<
|
||||||
|
// ", paddr: " << *paddr << "\n";
|
||||||
|
|
||||||
|
return NoException;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
53
code/userprog/addrspace.h
Executable file
53
code/userprog/addrspace.h
Executable file
@@ -0,0 +1,53 @@
|
|||||||
|
// addrspace.h
|
||||||
|
// Data structures to keep track of executing user programs
|
||||||
|
// (address spaces).
|
||||||
|
//
|
||||||
|
// For now, we don't keep any information about address spaces.
|
||||||
|
// The user level CPU state is saved and restored in the thread
|
||||||
|
// executing the user program (see thread.h).
|
||||||
|
//
|
||||||
|
// Copyright (c) 1992-1996 The Regents of the University of California.
|
||||||
|
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||||
|
// of liability and disclaimer of warranty provisions.
|
||||||
|
|
||||||
|
#ifndef ADDRSPACE_H
|
||||||
|
#define ADDRSPACE_H
|
||||||
|
|
||||||
|
#include "copyright.h"
|
||||||
|
#include "filesys.h"
|
||||||
|
|
||||||
|
#define UserStackSize 1024 // increase this as necessary!
|
||||||
|
|
||||||
|
class AddrSpace {
|
||||||
|
public:
|
||||||
|
AddrSpace(); // Create an address space.
|
||||||
|
~AddrSpace(); // De-allocate an address space
|
||||||
|
|
||||||
|
bool Load(char *fileName); // Load a program into addr space from
|
||||||
|
// a file
|
||||||
|
// return false if not found
|
||||||
|
|
||||||
|
void Execute(char *fileName); // Run a program
|
||||||
|
// assumes the program has already
|
||||||
|
// been loaded
|
||||||
|
|
||||||
|
void SaveState(); // Save/restore address space-specific
|
||||||
|
void RestoreState(); // info on a context switch
|
||||||
|
|
||||||
|
// Translate virtual address _vaddr_
|
||||||
|
// to physical address _paddr_. _mode_
|
||||||
|
// is 0 for Read, 1 for Write.
|
||||||
|
ExceptionType Translate(unsigned int vaddr, unsigned int *paddr, int mode);
|
||||||
|
|
||||||
|
private:
|
||||||
|
TranslationEntry *pageTable; // Assume linear page table translation
|
||||||
|
// for now!
|
||||||
|
unsigned int numPages; // Number of pages in the virtual
|
||||||
|
// address space
|
||||||
|
|
||||||
|
void InitRegisters(); // Initialize user-level CPU registers,
|
||||||
|
// before jumping to user code
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ADDRSPACE_H
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user