project4/.DS_Store
__MACOSX/project4/._.DS_Store
project4/nachos.tar
nachos/c++example/c++.ps
nachos/c++example/copyright.h
#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 */
nachos/c++example/inheritstack.cc
nachos/c++example/inheritstack.cc
// 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
;
}
nachos/c++example/list.cc
nachos/c++example/list.cc
// 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
);
}
nachos/c++example/templatestack.cc
nachos/c++example/templatestack.cc
// 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
;
}
nachos/c++example/stack.cc
nachos/c++example/stack.cc
// 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
;
}
nachos/c++example/Makefile
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
nachos/c++example/stack.h
// 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
nachos/c++example/templatestack.h
// 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
nachos/c++example/list.h
// 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
nachos/c++example/c++.tex
\documentstyle[12pt,fullpage]{article} \newcommand{\putfig}[3]% {\begin{figure}% \centerline{% \psfig{figure=#1.ps,width=#3}}% \caption{#2}% \label{fig:#1}% \end{figure}} \input{psfig} \begin{document} \begin{figure*}[t] \begin{center} {\LARGE\bf A Quick Introduction to C++} \vspace{3.0ex} {\Large Tom Anderson} \end{center} \end{figure*} \renewcommand{\thefootnote}{\fnsymbol{footnote}} \footnotetext{This article is based on an earlier version written by Wayne Christopher.} \renewcommand{\thefootnote}{} \renewcommand{\thefootnote}{\arabic{footnote}} \begin{quote} ``If programming in Pascal is like being put in a straightjacket, then programming in C is like playing with knives, and programming in C++ is like juggling chainsaws.'' \\ \hbox{} \hfill Anonymous. \end{quote} \section{Introduction} This note introduces some simple C++ concepts and outlines a subset of C++ that is easier to learn and use than the full language. Although we originally wrote this note for explaining the C++ used in the Nachos project, I believe it is useful to anyone learning C++. I assume that you are already somewhat familiar with C concepts like procedures, for loops, and pointers; these are pretty easy to pick up from reading Kernighan and Ritchie's ``The C Programming Language.'' I should admit up front that I am quite opinionated about C++, if that isn't obvious already. I know several C++ purists (an oxymoron perhaps?) who violently disagree with some of the prescriptions contained here; most of the objections are of the form, ``How could you have possibly left out feature X?'' However, I've found from teaching C++ to nearly 1000 undergrads over the past several years that the subset of C++ described here is pretty easy to learn, taking only a day or so for most students to get started. The basic premise of this note is that while object-oriented programming is a useful way to simplify programs, C++ is a wildly over-complicated language, with a host of features that only very, very rarely find a legitimate use. It's not too far off the mark to say that C++ includes every programming language feature ever imagined, and more. The natural tendency when faced with a new language feature is to try to use it, but in C++ this approach leads to disaster. Thus, we need to carefully distinguish between (i) those concepts that are fundamental (e.g., classes, member functions, constructors) -- ones that everyone should know and use, (ii) those that are sometimes but rarely useful (e.g., single inheritance, templates) -- ones that beginner programmers should be able to recognize (in case they run across them) but avoid using in their own programs, at least for a while, and (iii) those that are just a bad idea and should be avoided like the plague (e.g., multiple inheritance, exceptions, overloading, references, etc). Of course, all the items in this last category have their proponents, and I will admit that, like the hated goto, it is possible to construct cases when the program would be simpler using a goto or multiple inheritance. However, it is my belief that most programmers will never encounter such cases, and even if you do, you will be much more likely to misuse the feature than properly apply it. For example, I seriously doubt an undergraduate would need any of the features listed under (iii) for any course project (at least at Berkeley this is true). And if you find yourself wanting to use a feature like multiple inheritance, then, my advice is to fully implement your program both with and without the feature, and choose whichever is simpler. Sure, this takes more effort, but pretty soon you'll know from experience when a feature is useful and when it isn't, and you'll be able to skip the dual implementation. A really good way to learn a language is to read clear programs in that language. I have tried to make the Nachos code as readable as possible; it is written in the subset of C++ described in this note. It is a good idea to look over the first assignment as you read this introduction. Of course, your TA's will answer any questions you may have. You should not need a book on C++ to do the Nachos assignments, but if you are curious, there is a large selection of C++ books at Cody's and other technical bookstores. (My wife quips that C++ was invented to make researchers at Bell Labs rich from writing ``How to Program in C++'' books.) Most new software development these days is being done in C++, so it is a pretty good bet you'll run across it in the future. I use Stroustrup's "The C++ Programming Language" as a reference manual, although other books may be more readable. I would also recommend Scott Meyer's ``Effective C++'' for people just beginning to learn the language, and Coplien's ``Advanced C++'' once you've been programming in C++ for a couple years and are familiar with the language basics. Also, C++ is continually evolving, so be careful to buy books that describe the latest version (currently 3.0, I think!). \section{C in C++} To a large extent, C++ is a superset of C, and most carefully written ANSI C will compile as C++. There are a few major caveats though: \begin{enumerate} \item All functions must be declared before they are used, rather than defaulting to type {\tt int}. \item All function declarations and definition headers must use new-style declarations, e.g., \begin{verbatim} extern int foo(int a, char* b); \end{verbatim} The form {\tt extern int foo();} means that {\tt foo} takes {\it no} arguments, rather than arguments of an unspecified type and number. In fact, some advise using a C++ compiler even on normal C code, because it will catch errors like misused functions that a normal C compiler will let slide. \item If you need to link C object files together with C++, when you declare the C functions for the C++ files, they must be done like this: \begin{verbatim} extern "C" int foo(int a, char* b); \end{verbatim} Otherwise the C++ compiler will alter the name in a strange manner. \item There are a number of new keywords, which you may not use as identifiers --- some common ones are {\tt new}, {\tt delete}, {\tt const}, and {\tt class}. \end{enumerate} \section{Basic Concepts} Before giving examples of C++ features, I will first go over some of the basic concepts of object-oriented languages. If this discussion at first seems a bit obscure, it will become clearer when we get to some examples. \begin{enumerate} \item {\bf Classes and objects}. A class is similar to a C {\em structure}, except that the definition of the data structure, {\em and} all of the functions that operate on the data structure are grouped together in one place. An {\em object} is an instance of a class (an instance of the data structure); objects share the same functions with other objects of the same class, but each object (each instance) has its own copy of the data structure. A class thus defines two aspects of the objects: the {\em data} they contain, and the {\em behavior} they have. \item {\bf Member functions}. These are functions which are considered part of the object and are declared in the class definition. They are often referred to as {\em methods} of the class. In addition to member functions, a class's behavior is also defined by: \begin{enumerate} \item What to do when you create a new object (the {\bf constructor} for that object) -- in other words, initialize the object's data. \item What to do when you delete an object (the {\bf destructor} for that object). \end{enumerate} \item {\bf Private vs. public members}. A public member of a class is one that can be read or written by anybody, in the case of a data member, or called by anybody, in the case of a member function. A private member can only be read, written, or called by a member function of that class. \end{enumerate} Classes are used for two main reasons: (1) it makes it much easier to organize your programs if you can group together data with the functions that manipulate that data, and (2) the use of private members makes it possible to do {\em information hiding}, so that you can be more confident about the way information flows in your programs. \subsection{Classes} C++ classes are similar to C structures in many ways. In fact, a C++ struct is really a class that has only public data members. In the following explanation of how classes work, we will use a stack class as an example. \begin{enumerate} \item {\bf Member functions.} Here is a (partial) example of a class with a member function and some data members: \begin{verbatim} class Stack { public: void Push(int value); // Push an integer, checking for overflow. int top; // Index of the top of the stack. int stack[10]; // The elements of the stack. }; void Stack::Push(int value) { ASSERT(top < 10); // stack should never overflow stack[top++] = value; } \end{verbatim} This class has two data members, {\tt top} and {\tt stack}, and one member function, {\tt Push}. The notation {\em class}::{\em function} denotes the {\em function} member of the class {\em class}. (In the style we use, most function names are capitalized.) The function is defined beneath it. As an aside, note that we use a call to {\tt ASSERT} to check that the stack hasn't overflowed; ASSERT drops into the debugger if the condition is false. It is an extremely good idea for you to use ASSERT statements liberally throughout your code to document assumptions made by your implementation. Better to catch errors automatically via ASSERTs than to let them go by and have your program overwrite random locations. In actual usage, the definition of {\tt class Stack} would typically go in the file {\tt stack.h} and the definitions of the member functions, like {\tt Stack::Push}, would go in the file {\tt stack.cc}. If we have a pointer to a {\tt Stack} object called {\tt s}, we can access the {\tt top} element as {\tt s->top}, just as in C. However, in C++ we can also call the member function using the following syntax: \begin{verbatim} s->Push(17); \end{verbatim} Of course, as in C, {\tt s} must point to a valid {\tt Stack} object. Inside a member function, one may refer to the members of the class by their names alone. In other words, the class definition creates a scope that includes the member (function and data) definitions. Note that if you are inside a member function, you can get a pointer to the object you were called on by using the variable {\tt this}. If you want to call another member function on the same object, you do not need to use the {\tt this} pointer, however. Let's extend the Stack example to illustrate this by adding a {\tt Full()} function. \begin{verbatim} class Stack { public: void Push(int value); // Push an integer, checking for overflow. bool Full(); // Returns TRUE if the stack is full, FALSE otherwise. int top; // Index of the lowest unused position. int stack[10]; // A pointer to an array that holds the contents. }; \end{verbatim} \newpage \begin{verbatim} bool Stack::Full() { return (top == 10); } \end{verbatim} Now we can rewrite {\tt Push} this way: \begin{verbatim} void Stack::Push(int value) { ASSERT(!Full()); stack[top++] = value; } \end{verbatim} We could have also written the ASSERT: \begin{verbatim} ASSERT(!(this->Full()); \end{verbatim} but in a member function, the \verb+this->+ is implicit. The purpose of member functions is to encapsulate the functionality of a type of object along with the data that the object contains. A member function does not take up space in an object of the class. \item {\bf Private members.} One can declare some members of a class to be {\it private}, which are hidden to all but the member functions of that class, and some to be {\it public}, which are visible and accessible to everybody. Both data and function members can be either public or private. In our stack example, note that once we have the {\tt Full()} function, we really don't need to look at the {\tt top} or {\tt stack} members outside of the class -- in fact, we'd rather that users of the Stack abstraction {\em not} know about its internal implementation, in case we change it. Thus we can rewrite the class as follows: \begin{verbatim} class Stack { public: void Push(int value); // Push an integer, checking for overflow. bool Full(); // Returns TRUE if the stack is full, FALSE otherwise. private: int top; // Index of the top of the stack. int stack[10]; // The elements of the stack. }; \end{verbatim} Before, given a pointer to a {\tt Stack} object, say {\tt s}, any part of the program could access {\tt s->top}, in potentially bad ways. Now, since the {\tt top} member is private, only a member function, such as {\tt Full()}, can access it. If any other part of the program attempts to use {\tt s->top} the compiler will report an error. You can have alternating {\tt public:} and {\tt private:} sections in a class. Before you specify either of these, class members are private, thus the above example could have been written: \begin{verbatim} class Stack { int top; // Index of the top of the stack. int stack[10]; // The elements of the stack. public: void Push(int value); // Push an integer, checking for overflow. bool Full(); // Returns TRUE if the stack is full, FALSE otherwise. }; \end{verbatim} Which form you prefer is a matter of style, but it's usually best to be explicit, so that it is obvious what is intended. In Nachos, we make everything explicit. What is not a matter of style: {\bf all data members of a class should be private.} All operations on data should be via that class' member functions. Keeping data private adds to the modularity of the system, since you can redefine how the data members are stored without changing how you access them. \item {\bf Constructors and the operator new.} In C, in order to create a new object of type {\tt Stack}, one might write: \begin{verbatim} struct Stack *s = (struct Stack *) malloc(sizeof (struct Stack)); InitStack(s, 17); \end{verbatim} The {\tt InitStack()} function might take the second argument as the size of the stack to create, and use {\tt malloc()} again to get an array of 17 integers. The way this is done in C++ is as follows: \begin{verbatim} Stack *s = new Stack(17); \end{verbatim} The {\tt new} function takes the place of {\tt malloc()}. To specify how the object should be initialized, one declares a {\it constructor} function as a member of the class, with the name of the function being the same as the class name: \begin{verbatim} class Stack { public: Stack(int sz); // Constructor: initialize variables, allocate space. void Push(int value); // Push an integer, checking for overflow. bool Full(); // Returns TRUE if the stack is full, FALSE otherwise. private: int size; // The maximum capacity of the stack. int top; // Index of the lowest unused position. int* stack; // A pointer to an array that holds the contents. }; Stack::Stack(int sz) { size = sz; top = 0; stack = new int[size]; // Let's get an array of integers. } \end{verbatim} There are a few things going on here, so we will describe them one at a time. The {\tt new} operator automatically creates (i.e. allocates) the object and then calls the constructor function for the new object. This same sequence happens even if, for instance, you declare an object as an automatic variable inside a function or block -- the compiler allocates space for the object on the stack, and calls the constructor function on it. In this example, we create two stacks of different sizes, one by declaring it as an automatic variable, and one by using {\tt new}. \begin{verbatim} void test() { Stack s1(17); Stack* s2 = new Stack(23); } \end{verbatim} Note there are two ways of providing arguments to constructors: with {\tt new}, you put the argument list after the class name, and with automatic or global variables, you put them after the variable name. It is crucial that you {\bf always} define a constructor for every class you define, and that the constructor initialize {\bf every} data member of the class. If you don't define your own constructor, the compiler will automatically define one for you, and believe me, it won't do what you want (``the unhelpful compiler''). The data members will be initialized to random, unrepeatable values, and while your program may work anyway, it might not the next time you recompile (or vice versa!). As with normal C variables, variables declared inside a function are deallocated automatically when the function returns; for example, the {\tt s1} object is deallocated when {\tt test} returns. Data allocated with {\tt new} (such as {\tt s2}) is stored on the heap, however, and remains after the function returns; heap data must be explicitly disposed of using {\tt delete}, described below. The {\tt new} operator can also be used to allocate arrays, illustrated above in allocating an array of {\tt ints}, of dimension {\tt size}: \begin{verbatim} stack = new int[size]; \end{verbatim} Note that you can use {\tt new} and {\tt delete} (described below) with built-in types like {\tt int} and {\tt char} as well as with class objects like {\tt Stack}. \item {\bf Destructors and the operator delete.} Just as {\tt new} is the replacement for {\tt malloc()}, the replacement for {\tt free()} is {\tt delete}. To get rid of the {\tt Stack} object we allocated above with {\tt new}, one can do: \begin{verbatim} delete s2; \end{verbatim} This will deallocate the object, but first it will call the {\it destructor} for the {\tt Stack} class, if there is one. This destructor is a member function of {\tt Stack} called {\tt {\verb^~^}Stack()}: \begin{verbatim} class Stack { public: Stack(int sz); // Constructor: initialize variables, allocate space. ~Stack(); // Destructor: deallocate space allocated above. void Push(int value); // Push an integer, checking for overflow. bool Full(); // Returns TRUE if the stack is full, FALSE otherwise. private: int size; // The maximum capacity of the stack. int top; // Index of the lowest unused position. int* stack; // A pointer to an array that holds the contents. }; Stack::~Stack() { delete [] stack; // delete an array of integers } \end{verbatim} The destructor has the job of deallocating the data the constructor allocated. Many classes won't need destructors, and some will use them to close files and otherwise clean up after themselves. The destructor for an object is called when the object is deallocated. If the object was created with {\tt new}, then you must call {\tt delete} on the object, or else the object will continue to occupy space until the program is over -- this is called ``a memory leak.'' Memory leaks are bad things -- although virtual memory is supposed to be unlimited, you can in fact run out of it -- and so you should be careful to {\bf always} delete what you allocate. Of course, it is even worse to call {\tt delete} too early -- {\tt delete} calls the destructor and puts the space back on the heap for later re-use. If you are still using the object, you will get random and non-repeatable results that will be very difficult to debug. In my experience, using data that has already been deleted is major source of hard-to-locate bugs in student (and professional) programs, so hey, be careful out there! If the object is an automatic, allocated on the execution stack of a function, the destructor will be called and the space deallocated when the function returns; in the {\tt test()} example above, {\tt s1} will be deallocated when {\tt test()} returns, without you having to do anything. In Nachos, we always explicitly allocate and deallocate objects with {\tt new} and {\tt delete}, to make it clear when the constructor and destructor is being called. For example, if an object contains another object as a member variable, we use {\tt new} to explicitly allocated and initialize the member variable, instead of implicitly allocating it as part of the containing object. C++ has strange, non-intuitive rules for the order in which the constructors and destructors are called when you implicitly allocate and deallocate objects. In practice, although simpler, explicit allocation is slightly slower and it makes it more likely that you will forget to deallocate an object (a bad thing!), and so some would disagree with this approach. When you deallocate an array, you have to tell the compiler that you are deallocating an array, as opposed to a single element in the array. Hence to delete the array of integers in {\tt Stack::{\verb^~^}Stack}: \begin{verbatim} delete [] stack; \end{verbatim} \end{enumerate} \subsection{Other Basic C++ Features} Here are a few other C++ features that are useful to know. \begin{enumerate} \item When you define a {\tt class Stack}, the name {\tt Stack} becomes usable as a type name as if created with {\tt typedef}. The same is true for {\tt enum}s. \item You can define functions inside of a {\tt class} definition, whereupon they become {\it inline functions}, which are expanded in the body of the function where they are used. The rule of thumb to follow is to only consider inlining one-line functions, and even then do so rarely. As an example, we could make the {\tt Full} routine an inline. \begin{verbatim} class Stack { ... bool Full() { return (top == size); }; ... }; \end{verbatim} There are two motivations for inlines: convenience and performance. If overused, inlines can make your code more confusing, because the implementation for an object is no longer in one place, but spread between the {\tt .h} and {\tt .c} files. Inlines can sometimes speed up your code (by avoiding the overhead of a procedure call), but that shouldn't be your principal concern as a student (rather, at least to begin with, you should be most concerned with writing code that is simple and bug free). Not to mention that inlining sometimes slows down a program, since the object code for the function is duplicated wherever the function is called, potentially hurting cache performance. \item Inside a function body, you can declare some variables, execute some statements, and then declare more variables. This can make code a lot more readable. In fact, you can even write things like: \begin{verbatim} for (int i = 0; i < 10; i++) ; \end{verbatim} Depending on your compiler, however, the variable {\tt i} may still visible after the end of the {\tt for} loop, however, which is not what one might expect or desire. \item Comments can begin with the characters \verb+//+ and extend to the end of the line. These are usually more handy than the \verb+/* */+ style of comments. \item C++ provides some new opportunities to use the {\tt const} keyword from ANSI C. The basic idea of {\tt const} is to provide extra information to the compiler about how a variable or function is used, to allow it to flag an error if it is being used improperly. You should always look for ways to get the compiler to catch bugs for you. After all, which takes less time? Fixing a compiler-flagged error, or chasing down the same bug using gdb? For example, you can declare that a member function only reads the member data, and never modifies the object: \begin{verbatim} class Stack { ... bool Full() const; // Full() never modifies member data ... }; \end{verbatim} As in C, you can use {\tt const} to declare that a variable is never modified: \begin{verbatim} const int InitialHashTableSize = 8; \end{verbatim} This is {\em much} better than using {\tt \#define} for constants, since the above is type-checked. \item Input/output in C++ can be done with the {\tt >>} and {\tt <<} operators and the objects {\tt cin} and {\tt cout}. For example, to write to {\tt stdout}: \begin{verbatim} cout << "Hello world! This is section " << 3 << "!"; \end{verbatim} This is equivalent to the normal C code \begin{verbatim} fprintf(stdout, "Hello world! This is section %d!\n", 3); \end{verbatim} except that the C++ version is type-safe; with {\tt printf}, the compiler won't complain if you try to print a floating point number as an integer. In fact, you can use traditional {\tt printf} in a C++ program, but you will get bizarre behavior if you try to use both {\tt printf} and {\tt <<} on the same stream. Reading from {\tt stdin} works the same way as writing to {\tt stdout}, except using the shift right operator instead of shift left. In order to read two integers from {\tt stdin}: \begin{verbatim} int field1, field2; cin >> field1 >> field2; // equivalent to fscanf(stdin, "%d %d", &field1, &field2); // note that field1 and field2 are implicitly modified \end{verbatim} In fact, {\tt cin} and {\tt cout} are implemented as normal C++ objects, using operator overloading and reference parameters, but (fortunately!) you don't need to understand either of those to be able to do I/O in C++. \end{enumerate} \section{Advanced Concepts in C++: Dangerous but Occasionally Useful} There are a few C++ features, namely (single) inheritance and templates, which are easily abused, but can dramatically simplify an implementation if used properly. I describe the basic idea behind these ``dangerous but useful'' features here, in case you run across them. Feel free to skip this section -- it's long, complex, and you can understand 99\% of the code in Nachos without reading this section. Up to this point, there really hasn't been any fundamental difference between programming in C and in C++. In fact, most experienced C programmers organize their functions into modules that relate to a single data structure (a "class"), and often even use a naming convention which mimics C++, for example, naming routines {\tt StackFull()} and {\tt StackPush()}. However, the features I'm about to describe {\em do} require a paradigm shift -- there is no simple translation from them into a normal C program. The benefit will be that, in some circumstances, you will be able to write generic code that works with multiple kinds of objects. Nevertheless, I would advise a beginning C++ programmer against trying to use these features, because you will almost certainly misuse them. It's possible (even easy!) to write completely inscrutable code using inheritance and/or templates. Although you might find it amusing to write code that is impossible for your graders to understand, I assure you they won't find it amusing at all, and will return the favor when they assign grades. In industry, a high premium is placed on keeping code simple and readable. It's easy to write new code, but the real cost comes when you try to keep it working, even as you add new features to it. Nachos contains a few examples of the correct use of inheritance and templates, but realize that Nachos does {\em not} use them everywhere. In fact, if you get confused by this section, don't worry, you don't need to use any of these features in order to do the Nachos assignments. I omit a whole bunch of details; if you find yourself making widespread use of inheritance or templates, you should consult a C++ reference manual for the real scoop. This is meant to be just enough to get you started, and to help you identify when it would be appropriate to use these features and thus learn more about them! \subsection{Inheritance} Inheritance captures the idea that certain classes of objects are related to each other in useful ways. For example, lists and sorted lists have quite similar behavior -- they both allow the user to insert, delete, and find elements that are on the list. There are two benefits to using inheritance: \begin{enumerate} \item You can write generic code that doesn't care exactly which kind of object it is manipulating. For example, inheritance is widely used in windowing systems. Everything on the screen (windows, scroll bars, titles, icons) is its own object, but they all share a set of member functions in common, such as a routine {\tt Repaint} to redraw the object onto the screen. This way, the code to repaint the entire screen can simply call the {\tt Repaint} function on every object on the screen. The code that calls {\tt Repaint} doesn't need to know which kinds of objects are on the screen, as long as each implements {\tt Repaint}. \item You can share pieces of an implementation between two objects. For example, if you were to implement both lists and sorted lists in C, you'd probably find yourself repeating code in both places -- in fact, you might be really tempted to only implement sorted lists, so that you only had to debug one version. Inheritance provides a way to re-use code between nearly similar classes. For example, given an implementation of a list class, in C++ you can implement sorted lists by replacing the insert member function -- the other functions, delete, isFull, print, all remain the same. \end{enumerate} \subsubsection{Shared Behavior} Let me use our Stack example to illustrate the first of these. Our Stack implementation above could have been implemented with linked lists, instead of an array. Any code using a Stack shouldn't care which implementation is being used, except that the linked list implementation can't overflow. (In fact, we could also change the array implementation to handle overflow by automatically resizing the array as items are pushed on the stack.) To allow the two implementations to coexist, we first define an {\em abstract} Stack, containing just the public member functions, but no data. \begin{verbatim} class Stack { public: Stack(); virtual ~Stack(); // deallocate the stack virtual void Push(int value) = 0; // Push an integer, checking for overflow. virtual bool Full() = 0; // Is the stack is full? }; // For g++, need these even though no data to initialize. Stack::Stack {} Stack::~Stack() {} \end{verbatim} The {\tt Stack} definition is called a {\em base class} or sometimes a {\em superclass}. We can then define two different {\em derived classes}, sometimes called {\em subclasses} which inherit behavior from the base class. (Of course, inheritance is recursive -- a derived class can in turn be a base class for yet another derived class, and so on.) Note that I have prepended the functions in the base class is prepended with the keyword {\tt virtual}, to signify that they can be redefined by each of the two derived classes. The virtual functions are initialized to zero, to tell the compiler that those functions must be defined by the derived classes. Here's how we could declare the array-based and list-based implementations of {\tt Stack}. The syntax {\tt : public Stack} signifies that both {\tt ArrayStack} and {\tt ListStack} are kinds of {\tt Stacks}, and share the same behavior as the base class. \begin{verbatim} class ArrayStack : public Stack { // the same as in Section 2 public: ArrayStack(int sz); // Constructor: initialize variables, allocate space. ~ArrayStack(); // Destructor: deallocate space allocated above. void Push(int value); // Push an integer, checking for overflow. bool Full(); // Returns TRUE if the stack is full, FALSE otherwise. private: int size; // The maximum capacity of the stack. int top; // Index of the lowest unused position. int *stack; // A pointer to an array that holds the contents. }; class ListStack : public Stack { public: ListStack(); ~ListStack(); void Push(int value); bool Full(); private: List *list; // list of items pushed on the stack }; ListStack::ListStack() { list = new List; } ListStack::~ListStack() { delete list; } \end{verbatim} \newpage \begin{verbatim} void ListStack::Push(int value) { list->Prepend(value); } bool ListStack::Full() { return FALSE; // this stack never overflows! } \end{verbatim} The neat concept here is that I can assign pointers to instances of {\tt ListStack} or {\tt ArrayStack} to a variable of type {\tt Stack}, and then use them as if they were of the base type. \begin{verbatim} Stack *s1 = new ListStack; Stack *s2 = new ArrayStack(17); if (!stack->Full()) s1->Push(5); if (!s2->Full()) s2->Push(6); delete s1; delete s2; \end{verbatim} The compiler automatically invokes {\tt ListStack} operations for {\tt s1}, and {\tt ArrayStack} operations for {\tt s2}; this is done by creating a procedure table for each object, where derived objects override the default entries in the table defined by the base class. To the code above, it invokes the operations {\tt Full}, {\tt Push}, and {\tt delete} by indirection through the procedure table, so that the code doesn't need to know which kind of object it is. In this example, since I never create an instance of the abstract class {\tt Stack}, I do not need to {\em implement} its functions. This might seem a bit strange, but remember that the derived classes are the various implementations of Stack, and Stack serves only to reflect the shared behavior between the different implementations. Also note that the destructor for {\tt Stack} is a virtual function but the constructor is not. Clearly, when I create an object, I have to know which kind of object it is, whether {\tt ArrayStack} or {\tt ListStack}. The compiler makes sure that no one creates an instance of the abstract {\tt Stack} by mistake -- you cannot instantiate any class whose virtual functions are not completely defined (in other words, if any of its functions are set to zero in the class definition). But when I deallocate an object, I may no longer know its exact type. In the above code, I want to call the destructor for the derived object, even though the code only knows that I am deleting an object of class {\tt Stack}. If the destructor were not virtual, then the compiler would invoke {\tt Stack}'s destructor, which is not at all what I want. This is an easy mistake to make (I made it in the first draft of this article!) -- if you don't define a destructor for the abstract class, the compiler will define one for you implicitly (and by the way, it won't be virtual, since you have a {\em really} unhelpful compiler). The result for the above code would be a memory leak, and who knows how you would figure that out! \subsubsection{Shared Implementation} What about sharing code, the other reason for inheritance? In C++, it is possible to use member functions of a base class in its derived class. (You can also share data between a base class and derived classes, but this is a bad idea for reasons I'll discuss later.) Suppose that I wanted to add a new member function, {\tt NumberPushed()}, to both implementations of {\tt Stack}. The {\tt ArrayStack} class already keeps count of the number of items on the stack, so I could duplicate that code in {\tt ListStack}. Ideally, I'd like to be able to use the same code in both places. With inheritance, we can move the counter into the {\tt Stack} class, and then invoke the base class operations from the derived class to update the counter. \begin{verbatim} class Stack { public: virtual ~Stack(); // deallocate data virtual void Push(int value); // Push an integer, checking for overflow. virtual bool Full() = 0; // return TRUE if full int NumPushed(); // how many are currently on the stack? protected: Stack(); // initialize data private: int numPushed; }; Stack::Stack() { numPushed = 0; } void Stack::Push(int value) { numPushed++; } int Stack::NumPushed() { return numPushed; } \end{verbatim} We can then modify both {\tt ArrayStack} and {\tt ListStack} to make use the new behavior of {\tt Stack}. I'll only list one of them here: \begin{verbatim} class ArrayStack : public Stack { public: ArrayStack(int sz); ~ArrayStack(); void Push(int value); bool Full(); private: int size; // The maximum capacity of the stack. int *stack; // A pointer to an array that holds the contents. }; ArrayStack::ArrayStack(int sz) : Stack() { size = sz; stack = new int[size]; // Let's get an array of integers. } void ArrayStack::Push(int value) { ASSERT(!Full()); stack[NumPushed()] = value; Stack::Push(); // invoke base class to increment numPushed } \end{verbatim} There are a few things to note: \begin{enumerate} \item The constructor for {\tt ArrayStack} needs to invoke the constructor for {\tt Stack}, in order to initialize {\tt numPushed}. It does that by adding {\tt : Stack()} to the first line in the constructor: \begin{verbatim} ArrayStack::ArrayStack(int sz) : Stack() \end{verbatim} The same thing applies to destructors. There are special rules for which get called first -- the constructor/destructor for the base class or the constructor/destructor for the derived class. All I should say is, it's a bad idea to rely on whatever the rule is -- more generally, it is a bad idea to write code which requires the reader to consult a manual to tell whether or not the code works! \item I introduced a new keyword, {\tt protected}, in the new definition of {\tt Stack}. For a base class, {\tt protected} signifies that those member data and functions are accessible to classes derived (recursively) from this class, but inaccessible to other classes. In other words, protected data is {\tt public} to derived classes, and {\tt private} to everyone else. For example, we need {\tt Stack}'s constructor to be callable by {\tt ArrayStack} and {\tt ListStack}, but we don't want anyone else to create instances of {\tt Stack}. Hence, we make {\tt Stack}'s constructor a protected function. In this case, this is not strictly necessary since the compiler will complain if anyone tries to create an instance of {\tt Stack} because {\tt Stack} still has an undefined virtual functions, {\tt Push}. By defining {\tt Stack::Stack} as {\tt protected}, you are safe even if someone comes along later and defines {\tt Stack::Push}. Note however that I made {\tt Stack}'s data member {\tt private}, not {\tt protected}. Although there is some debate on this point, as a rule of thumb you should never allow one class to see directly access the data in another, even among classes related by inheritance. Otherwise, if you ever change the implementation of the base class, you will have to examine and change all the implementations of the derived classes, violating modularity. \item The interface for a derived class automatically includes all functions defined for its base class, without having to explicitly list them in the derived class. Although we didn't define {\tt NumPushed()} in {\tt ArrayStack}, we can still call it for those objects: \begin{verbatim} ArrayStack *s = new ArrayStack(17); ASSERT(s->NumPushed() == 0); // should be initialized to 0 \end{verbatim} \item Conversely, even though we have defined a routine {\tt Stack::Push()}, because it is declared as {\tt virtual}, if we invoke {\tt Push()} on an {\tt ArrayStack} object, we will get {\tt ArrayStack}'s version of {\tt Push}: \begin{verbatim} Stack *s = new ArrayStack(17); if (!s->Full()) // ArrayStack::Full s->Push(5); // ArrayStack::Push \end{verbatim} \item {\tt Stack::NumPushed()} is not {\tt virtual}. That means that it cannot be re-defined by {\tt Stack}'s derived classes. Some people believe that you should mark {\em all} functions in a base class as {\tt virtual}; that way, if you later want to implement a derived class that redefines a function, you don't have to modify the base class to do so. \item Member functions in a derived class can explicitly invoke public or protected functions in the base class, by the full name of the function, {\tt Base::Function()}, as in: \begin{verbatim} void ArrayStack::Push(int value) { ... Stack::Push(); // invoke base class to increment numPushed } \end{verbatim} Of course, if we just called {\tt Push()} here (without prepending {\tt Stack::}, the compiler would think we were referring to {\tt ArrayStack}'s {\tt Push()}, and so that would recurse, which is not exactly what we had in mind here. \end{enumerate} Whew! Inheritance in C++ involves lots and lots of details. But it's real downside is that it tends to spread implementation details across multiple files -- if you have a deep inheritance tree, it can take some serious digging to figure out what code actually executes when a member function is invoked. So the question to ask yourself before using inheritance is: what's your goal? Is it to write your programs with the fewest number of characters possible? If so, inheritance is really useful, but so is changing all of your function and variable names to be one letter long -- "a", "b", "c" -- and once you run out of lower case ones, start using upper case, then two character variable names: "XX XY XZ Ya ..." (I'm joking here.) Needless to say, it is really easy to write unreadable code using inheritance. So when is it a good idea to use inheritance and when should it be avoided? My rule of thumb is to only use it for representing {\em shared behavior} between objects, and to never use it for representing {\em shared implementation}. With C++, you can use inheritance for both concepts, but only the first will lead to truly simpler implementations. To illustrate the difference between shared behavior and shared implementation, suppose you had a whole bunch of different kinds of objects that you needed to put on lists. For example, almost everything in an operating system goes on a list of some sort: buffers, threads, users, terminals, etc. A very common approach to this problem (particularly among people new to object-oriented programming) is to make every object inherit from a single base class {\em Object}, which contains the forward and backward pointers for the list. But what if some object needs to go on multiple lists? The whole scheme breaks down, and it's because we tried to use inheritance to share implementation (the code for the forward and backward pointers) instead of to share behavior. A much cleaner (although slightly slower) approach would be to define a list implementation that allocated forward/backward pointers for each object that gets put on a list. In sum, if two classes share at least some of the same member function signatures -- that is, the same behavior, {\em and} if there's code that only relies on the shared behavior, then there {\em may} be a benefit to using inheritance. In Nachos, locks don't inherit from semaphores, even though locks are implemented using semaphores. The operations on semaphores and locks are different. Instead, inheritance is only used for various kinds of lists (sorted, keyed, etc.), and for different implementations of the physical disk abstraction, to reflect whether the disk has a track buffer, etc. A disk is used the same way whether or not it has a track buffer; the only difference is in its performance characteristics. \subsection{Templates} Templates are another useful but dangerous concept in C++. With templates, you can parameterize a class definition with a {\em type}, to allow you to write generic type-independent code. For example, our {\tt Stack} implementation above only worked for pushing and popping {\em integers}; what if we wanted a stack of characters, or floats, or pointers, or some arbitrary data structure? In C++, this is pretty easy to do using templates: \begin{verbatim} template <class T> class Stack { public: Stack(int sz); // Constructor: initialize variables, allocate space. ~Stack(); // Destructor: deallocate space allocated above. void Push(T value); // Push an integer, checking for overflow. bool Full(); // Returns TRUE if the stack is full, FALSE otherwise. private: int size; // The maximum capacity of the stack. int top; // Index of the lowest unused position. T *stack; // A pointer to an array that holds the contents. }; \end{verbatim} To define a template, we prepend the keyword {\tt template} to the class definition, and we put the parameterized type for the template in angle brackets. If we need to parameterize the implementation with two or more types, it works just like an argument list: {\tt template <class T, class S>}. We can use the type parameters elsewhere in the definition, just like they were normal types. When we provide the implementation for each of the member functions in the class, we also have to declare them as templates, and again, once we do that, we can use the type parameters just like normal types: \begin{verbatim} // template version of Stack::Stack template <class T> Stack<T>::Stack(int sz) { size = sz; top = 0; stack = new T[size]; // Let's get an array of type T } // template version of Stack::Push template <class T> void Stack<T>::Push(T value) { ASSERT(!Full()); stack[top++] = value; } \end{verbatim} Creating an object of a template class is similar to creating a normal object: \begin{verbatim} void test() { Stack<int> s1(17); Stack<char> *s2 = new Stack<char>(23); s1.Push(5); s2->Push('z'); delete s2; } \end{verbatim} Everything operates as if we defined two classes, one called {\tt Stack<int>} -- a stack of integers, and one called {\tt Stack<char>} -- a stack of characters. {\tt s1} behaves just like an instance of the first; {\tt s2} behaves just like an instance of the second. In fact, that is exactly how templates are typically implemented -- you get a complete {\em copy} of the code for the template for each different instantiated type. In the above example, we'd get one copy of the code for {\tt ints} and one copy for {\tt chars}. So what's wrong with templates? You've all been taught to make your code modular so that it can be re-usable, so {\em everything} should be a template, right? Wrong. The principal problem with templates is that they can be {\em very} difficult to debug -- templates are easy to use if they work, but finding a bug in them can be difficult. In part this is because current generation C++ debuggers don't really understand templates very well. Nevertheless, it is easier to debug a template than two nearly identical implementations that differ only in their types. So the best advice is -- don't make a class into a template unless there really is a near term use for the template. And if you do need to implement a template, implement and debug a non-template version first. Once that is working, it won't be hard to convert it to a template. Then all you have to worry about code explosion -- e.g., your program's object code is now megabytes because of the 15 copies of the hash table/list/... routines, one for each kind of thing you want to put in a hash table/list/... (Remember, you have an unhelpful compiler!) \section{Features To Avoid Like the Plague} Despite the length of this note, there are numerous features in C++ that I haven't explained. I'm sure each feature has its advocates, but despite programming in C and C++ for over 15 years, I haven't found a compelling reason to use them in any code that I've written (outside of a programming language class!) Indeed, there is a compelling reason to avoid using these features -- they are easy to misuse, resulting in programs that are harder to read and understand instead of easier to understand. In most cases, the features are also redundant -- there are other ways of accomplishing the same end. Why have two ways of doing the same thing? Why not stick with the simpler one? I do not use any of the following features in Nachos. If you use them, {\it caveat hacker}. \begin{enumerate} \item {\bf Multiple inheritance.} It is possible in C++ to define a class as inheriting behavior from multiple classes (for instance, a dog is both an animal and a furry thing). But if programs using single inheritance can be difficult to untangle, programs with multiple inheritance can get really confusing. \item {\bf References.} Reference variables are rather hard to understand in general; they play the same role as pointers, with slightly different syntax (unfortunately, I'm not joking!) Their most common use is to declare some parameters to a function as {\it reference parameters}, as in Pascal. A call-by-reference parameter can be modified by the calling function, without the callee having to pass a pointer. The effect is that parameters look (to the caller) like they are called by value (and therefore can't change), but in fact can be transparently modified by the called function. Obviously, this can be a source of obscure bugs, not to mention that the semantics of references in C++ are in general not obvious. \item {\bf Operator overloading.} C++ lets you redefine the meanings of the operators (such as {\tt +} and \verb+>>+) for class objects. This is dangerous at best ("exactly which implementation of '+' does this refer to?"), and when used in non-intuitive ways, a source of great confusion, made worse by the fact that C++ does implicit type conversion, which can affect which operator is invoked. Unfortunately, C++'s I/O facilities make heavy use of operator overloading and references, so you can't completely escape them, but think twice before you redefine '+' to mean ``concatenate these two strings''. \item {\bf Function overloading.} You can also define different functions in a class with the same name but different argument types. This is also dangerous (since it's easy to slip up and get the unintended version), and we never use it. We will also avoid using default arguments (for the same reason). Note that it can be a good idea to use the same name for functions in different classes, provided they use the same arguments and behave the same way -- a good example of this is that most Nachos objects have a {\tt Print()} method. \item {\bf Standard template library.} An ANSI standard has emerged for a library of routines implementing such things as lists, hash tables, etc., called the standard template library. Using such a library should make programming much simpler if the data structure you need is already provided in the library. Alas, the standard template library pushes the envelope of legal C++, and so virtually no compilers (including g++) can support it today. Not to mention that it uses (big surprise!) references, operator overloading, and function overloading. \item {\bf Exceptions.} There are two ways to return an error from a procedure. One is simple -- just define the procedure to return an error code if it isn't able to do it's job. For example, the standard library routine {\tt malloc} returns NULL if there is no available memory. However, lots of programmers are lazy and don't check error codes. So what's the solution? You might think it would be to get programmers who aren't lazy, but no, the C++ solution is to add a programming language construct! A procedure can return an error by ``raising an exception'' which effectively causes a {\tt goto} back up the execution stack to the last place the programmer put an exception handler. You would think this is too bizarre to be true, but unfortunately, I'm not making this up. \end{enumerate} While I'm at it, there are a number of features of C that you also should avoid, because they lead to bugs and make your code less easy to understand. See Maguire's "Writing Solid Code" for a more complete discussion of this issue. All of these features are legal C; what's legal isn't necessarily good. \begin{enumerate} \item Pointer arithmetic. Runaway pointers are a principal source of hard-to-find bugs in C programs, because the symptom of this happening can be mangled data structures in a completely different part of the program. Depending on exactly which objects are allocated on the heap in which order, pointer bugs can appear and disappear, seemingly at random. For example, {\tt printf} sometimes allocates memory on the heap, which can change the addresses returned by all future calls to {\tt new}. Thus, adding a {\tt printf} can change things so that a pointer which used to (by happenstance) mangle a critical data structure (such as the middle of a thread's execution stack), now overwrites memory that may not even be used. The best way to avoid runaway pointers is (no surprise) to be {\em very} careful when using pointers. Instead of iterating through an array with pointer arithmetic, use a separate index variable, and assert that the index is never larger than the size of the array. Optimizing compilers have gotten very good, so that the generated machine code is likely to be the same in either case. Even if you don't use pointer arithmetic, it's still easy (easy is bad in this context!) to have an off-by-one errror that causes your program to step beyond the end of an array. How do you fix this? Define a class to contain the array {\em and its length}; before allowing any access to the array, you can then check whether the access is legal or in error. \item Casts from integers to pointers and back. Another source of runaway pointers is that C and C++ allow you to convert integers to pointers, and back again. Needless to say, using a random integer value as a pointer is likely to result in unpredictable symptoms that will be very hard to track down. In addition, on some 64 bit machines, such as the Alpha, it is no longer the case that the size of an integer is the same as the the size of a pointer. If you cast between pointers and integers, you are also writing highly non-portable code. \item Using bit shift in place of a multiply or divide. This is a clarity issue. If you are doing arithmetic, use arithmetic operators; if you are doing bit manipulation, use bitwise operators. If I am trying to multiply by 8, which is easier to understand, {\tt x << 3} or {\tt x * 8}? In the 70's, when C was being developed, the former would yield more efficient machine code, but today's compilers generate the same code in both cases, so readability should be your primary concern. \item Assignment inside conditional. Many programmers have the attitude that simplicity equals saving as many keystrokes as possible. The result can be to hide bugs that would otherwise be obvious. For example: \begin{verbatim} if (x = y) { ... \end{verbatim} Was the intent really {\tt x == y}? After all, it's pretty easy to mistakenly leave off the extra equals sign. By never using assignment within a conditional, you can tell by code inspection whether you've made a mistake. \item Using {\tt \#define} when you could use {\tt enum}. When a variable can hold one of a small number of values, the original C practice was to use {\tt \#define} to set up symbolic names for each of the values. {\tt enum} does this in a type-safe way -- it allows the compiler to verify that the variable is only assigned one of the enumerated values, and none other. Again, the advantage is to eliminate a class of errors from your program, making it quicker to debug. \end{enumerate} \newpage \section{Style Guidelines} Even if you follow the approach I've outlined above, it is still as easy to write unreadable and undebuggable code in C++ as it is in C, and perhaps easier, given the more powerful features the language provides. For the Nachos project, and in general, we suggest you adhere to the following guidelines (and tell us if you catch us breaking them): \begin{enumerate} \item Words in a name are separated SmallTalk-style (i.e., capital letters at the start of each new word). All class names and member function names begin with a capital letter, except for member functions of the form {\tt getSomething()} and {\tt setSomething()}, where {\tt Something} is a data element of the class (i.e., accessor functions). Note that you would want to provide such functions only when the data should be visible to the outside world, but you want to force all accesses to go through one function. This is often a good idea, since you might at some later time decide to compute the data instead of storing it, for example. \item All global functions should be capitalized, except for {\tt main} and library functions, which are kept lower-case for historical reasons. \item Minimize the use of global variables. If you find yourself using a lot of them, try and group some together in a class in a natural way or pass them as arguments to the functions that need them if you can. \item Minimize the use of global functions (as opposed to member functions). If you write a function that operates on some object, consider making it a member function of that object. \item For every class or set of related classes, create a separate {\tt .h} file and {\tt .cc} file. The {\tt .h} file acts as the {\it interface} to the class, and the {\tt .cc} file acts as the {\it implementation} (a given {\tt .cc} file should {\tt include} it's respective {\tt .h} file). If using a particular {\tt .h} file requires another {\tt .h} file to be included (e.g., {\tt synch.h} needs class definitions from {\tt thread.h}) you should include the dependency in the {\tt .h} file, so that the user of your class doesn't have to track down all the dependencies himself. To protect against multiple inclusion, bracket each {\tt .h} file with something like: \begin{verbatim} #ifndef STACK_H #define STACK_H class Stack { ... }; #endif \end{verbatim} Sometimes this will not be enough, and you will have a circular dependency. For example, you might have a {\tt .h} file that uses a definition from one {\tt .h} file, but also defines something needed by that {\tt .h} file. In this case, you will have to do something ad-hoc. One thing to realize is that you don't always have to completely define a class before it is used. If you only use a pointer to class {\tt Stack} and do not access any member functions or data from the class, you can write, in lieu of including {\tt stack.h}: \begin{verbatim} class Stack; \end{verbatim} This will tell the compiler all it needs to know to deal with the pointer. In a few cases this won't work, and you will have to move stuff around or alter your definitions. \item Use {\tt ASSERT} statements liberally to check that your program is behaving properly. An assertion is a condition that if FALSE signifies that there is a bug in the program; {\tt ASSERT} tests an expression and aborts if the condition is false. We used {\tt ASSERT} above in {\tt Stack::Push()} to check that the stack wasn't full. The idea is to catch errors as early as possible, when they are easier to locate, instead of waiting until there is a user-visible symptom of the error (such as a segmentation fault, after memory has been trashed by a rogue pointer). Assertions are particularly useful at the beginnings and ends of procedures, to check that the procedure was called with the right arguments, and that the procedure did what it is supposed to. For example, at the beginning of List::Insert, you could assert that the item being inserted isn't already on the list, and at the end of the procedure, you could assert that the item is now on the list. If speed is a concern, ASSERTs can be defined to make the check in the debug version of your program, and to be a no-op in the production version. But many people run with ASSERTs enabled even in production. \item Write a module test for every module in your program. Many programmers have the notion that testing code means running the entire program on some sample input; if it doesn't crash, that means it's working, right? Wrong. You have no way of knowing how much code was exercised for the test. Let me urge you to be methodical about testing. Before you put a new module into a bigger system, make sure the module works as advertised by testing it standalone. If you do this for every module, then when you put the modules together, instead of {\em hoping} that everything will work, you will {\em know} it will work. Perhaps more importantly, module tests provide an opportunity to find as many bugs as possible in a localized context. Which is easier: finding a bug in a 100 line program, or in a 10000 line program? \end{enumerate} \section{Compiling and Debugging} The Makefiles we will give you works only with the GNU version of make, called ``gmake''. You may want to put ``alias make gmake'' in your .cshrc file. You should use {\bf gdb} to debug your program rather than {\bf dbx}. Dbx doesn't know how to decipher C++ names, so you will see function names like \verb+Run__9SchedulerP6Thread+. On the other hand, in GDB (but not DBX) when you do a stack backtrace when in a forked thread (in homework 1), after printing out the correct frames at the top of the stack, the debugger will sometimes go into a loop printing the lower-most frame ({\tt ThreadRoot}), and you have to type control-C when it says ``more?''. If you understand assembly language and can fix this, please let me know. \section{Example: A Stack of Integers} We've provided the complete, working code for the stack example. You should read through it and play around with it to make sure you understand the features of C++ described in this paper. To compile the simple stack test, type {\tt make all} -- this will compile the simple stack test ({\tt stack.cc}), the inherited stack test ({\tt inheritstack.cc}), and the template version of stacks ({\tt templatestack.cc}). \section{Epilogue} I've argued in this note that you should avoid using certain C++ and C features. But you're probably thinking I must be leaving something out -- if someone put the feature in the language, there must be a good reason, right? I believe that every programmer should strive to write code whose behavior would be immediately obvious to a reader; if you find yourself writing code that would require someone reading the code to thumb through a manual in order to understand it, you are almost certainly being way too subtle. There's probably a much simpler and more obvious way to accomplish the same end. Maybe the code will be a little longer that way, but in the real world, it's whether the code works and how simple it is for someone else to modify, that matters a whole lot more than how many characters you had to type. A final thought to remember: \begin{quote} ``There are two ways of constructing a software design: one way is to make it so simple that there are {\em obviously} no deficiencies and the other way is to make it so complicated that there are no {\em obvious} deficiencies.'' \\ \hbox{} \hfill C. A. R. Hoare, ``The Emperor's Old Clothes'', CACM Feb. 1981 \end{quote} \section{Further Reading} \begin{itemize} \item[] James Coplien, ``Advanced C++'', Addison-Wesley. This book is only for experts, but it has some good ideas in it, so keep it in mind once you've been programming in C++ for a few years. \item[] James Gosling. ``The Java Language.'' Online at ``http://java.sun.com/'' Java is a safe subset of C++. It's main application is the safe extension of Web browsers by allowing you to download Java code as part of clicking on a link to interpret and display the document. Safety is key here, since after all, you don't want to click on a Web link and have it download code that will crash your browser. Java was defined independently of this document, but interestingly, it enforces a very similar style (for example, no multiple inheritance and no operator overloading). \item[] C.A.R. Hoare, ``The Emperor's Old Clothes.'' {\em Communications of the ACM}, Vol. 24, No. 2, February 1981, pp. 75-83. Tony Hoare's Turing Award lecture. How do you build software that really works? Attitude is everything -- you need a healthy respect for how hard it is to build working software. It might seem that addding this whiz-bang feature is only ``a small matter of code'', but that's the path to late, buggy products that don't work. \item[] Brian Kernighan and Dennis Ritchie, ``The C Programming Language'', Prentice-Hall. The original C book -- a very easy read. But the language has evolved since it was first designed, and this book doesn't describe all of C's newest features. But still the best place for a beginner to start, even when learning C++. \item[] Steve Maguire, ``Writing Solid Code'', Microsoft Press. How to write bug-free software; I think this should be required reading for all software engineers. This really {\em will} change your life -- if you don't follow the recommendations in this book, you'll probably never write code that completely works, and you'll spend your entire life struggling with hard to find bugs. There is a better way! Contrary to the programming language types, this doesn't involve proving the correctness of your programs, whatever that means. Instead, Maguire has a set of practical engineering solutions to writing solid code. \item[] Steve Maguire, ``Debugging the Development Process'', Microsoft Press. Maguire's follow up book on how to lead an effective team, and by the way, how to be an effective engineer. Maguire's background is that he is a turnaround artist for Microsoft -- he gets assigned to floundering teams, and figures out how to make them effective. After you've pulled a few all-nighters to get that last bug out of your course project, you're probably wondering why in heck you're studying computer science anyway. This book will explain how to write programs that work, {\em and} still have a life! \item[] Scott Meyers, ``Effective C++''. This book describes how 50 easy ways to make mistakes C++; if you avoid these, you will be a lot more likely to write C++ code that works. \item[] Bjarne Stroustrup, ``The C++ Programming Language'', Addison-Wesley. This should be the definite reference manual, but it isn't. You probably thought I was joking when I said the C++ language was continually evolving. I bought the second edition of this book three years ago, and it is already out of date. Fortunately, it's still OK for the subset of C++ that I use. \end{itemize} \end{document}
nachos/c++example/inheritstack.h
// 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
nachos/coff2noff/coff2noff.x86Linux
nachos/coff2noff/copyright.h
/* 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
nachos/coff2noff/noff.h
/* noff.h * Data structures defining the Nachos Object Code Format * * Basically, we only know about three types of segments: * code (read-only), initialized data, and unitialized data */ #define NOFFMAGIC 0xbadfad /* magic number denoting Nachos * object code file */ typedef struct segment { int virtualAddr; /* location of segment in virt addr space */ int inFileAddr; /* location of segment in this file */ int size; /* size of segment */ } Segment; typedef struct noffHeader { int noffMagic; /* should be NOFFMAGIC */ Segment code; /* executable code segment */ Segment initData; /* initialized data segment */ #ifdef RDATA Segment readonlyData; /* read only data */ #endif Segment uninitData; /* uninitialized data segment -- * should be zero'ed before use */ } NoffHeader;
nachos/coff2noff/Makefile.dep
############################################################################# # Machine-specific definitions # # In the MFCF environment, this attempts to determine automatically # the machine type and OS type. If it cannot, it gives up and # prints a message. # # If you are not in the MFCF environment, you can either add a new # automatic test for your machine/OS type, or you can 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 Solaris (5.6) # At Waterloo: agnesi,bacon,fenchel,fitch,lassar,magnus,merrill # If Solaris, we assume we are on a SPARC, which is not necessarily # a good assumption outside of MFCF ifeq ($(osname),SunOS) ifeq ($(osrelease),5.6) HOSTCFLAGS = -DHOST_IS_BIG_ENDIAN hosttype = sparcSolaris endif endif # Test for Solaris (5.5) # At Waterloo: hermite.math,markov.math,picard.math,wronski.math,... # If Solaris, we assume we are on a SPARC, which is not necessarily # a good assumption outside of MFCF ifeq ($(osname),SunOS) ifeq ($(osrelease),5.5) HOSTCFLAGS = -DHOST_IS_BIG_ENDIAN hosttype = sparcSolaris endif endif # Test for Solaris (5.4) # At Waterloo: hume.math, hypatia.math,... # This is the same setup as Solaris 5.5 # If Solaris, we assume we are on a SPARC, which is not necessarily # a good assumption outside of MFCF ifeq ($(osname),SunOS) ifeq ($(osrelease),5.4) HOSTCFLAGS = -DHOST_IS_BIG_ENDIAN hosttype = sparcSolaris endif endif # Test for SunOS 4.xx # At Waterloo: descartes,cayley,napier,.... # If SunOS, we assume we are on a SPARC, which is not necessarily # a good assumption outside of MFCF ifeq ($(osname),SunOS) ifeq ($(osrelease),4.1.3_U1) HOSTCFLAGS = -DHOST_IS_BIG_ENDIAN hosttype = sparcSunOS endif endif # Test for ULTRIX # At Waterloo: cantor.math,noether.math # Assume ULTRIX on a MIPS architecture ifeq ($(osname),ULTRIX) HOSTCFLAGS = hosttype = mipsUltrix endif # Note: # If you are trying to build on Linux on an x86 # try something like this, substituting whatever # uname -s returns on your machine for the XXX # ifeq ($(osname),Linux) HOSTCFLAGS = hosttype = x86Linux endif ifeq ($(osname),CYGWIN_NT-5.1) HOSTCFLAGS = hosttype = x86Linux endif
nachos/coff2noff/coff2noff.o
nachos/coff2noff/Makefile
# Makefile for: # coff2noff -- converts a normal MIPS executable into a Nachos executable # # 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". # # Use "make" to build the executable(s) # Use "make clean" to remove .o files # Use "make distclean" to remove all files produced by make, including # the executable # # # 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 file has been modified for use at Waterloo # ############################################################################# # 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=gcc CFLAGS= $(HOSTCFLAGS) -DRDATA -m32 LD=gcc -m32 RM = /bin/rm MV = /bin/mv ifeq ($(hosttype),unknown) buildtargets = unknownhost else buildtargets = coff2noff.$(hosttype) endif all: $(buildtargets) # converts a COFF file to Nachos object format coff2noff.$(hosttype): coff2noff.o $(LD) coff2noff.o -o coff2noff.$(hosttype) strip coff2noff.$(hosttype) clean: $(RM) -f coff2noff.o distclean: clean $(MV) coff2noff.c temp.c $(RM) -f coff2noff.* $(MV) temp.c coff2noff.c unknownhost: @echo Host type could not be determined. @echo make is terminating @echo If you are on an MFCF machine, contact the instructor @echo to report this problem @echo Otherwise, edit Makefile.dep and try again.
nachos/coff2noff/coff2noff.c
/* coff2noff.c * * This program reads in a COFF format file, and outputs a NOFF format file. * The NOFF format is essentially just a simpler version of the COFF file, * recording where each segment is in the NOFF file, and where it is to * go in the virtual address space. * * Assumes coff file is linked with either * gld with -N -Ttext 0 * ld with -N -T 0 * to make sure the object file has no shared text. * * Also assumes that the COFF file has at most 3 segments: * .text -- read-only executable instructions * .data -- initialized data * .bss/.sbss -- uninitialized data (should be zero'd on program startup) #ifdef RDATA * .rdata -- read-only data (e.g., string literals). * mark this segment readonly to prevent it from being modified #endif * * * 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. */ /* * Modified at UW by KMS, August, 1997 * The modified program always writes the NOFF header in little-endian * format, rather than host format. This is to avoid the problem * that user programs run through coff2noff on a big-endian host * would not run properly on Nachos machines running on little-endian * hosts. * * Note that the Nachos address space loading code * (in AddrSpace::Load) on big-endian hosts converts the header * to big-endian format when it is read in. * Thus, the little-endian header NOFF * header should work OK whether Nachos is running on a little-endian * host or a big-endian host. */ #define MAIN #include "copyright.h" #undef MAIN #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <limits.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include "coff.h" #include "noff.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 little endian. */ 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) { #if 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); } // this routine was borrowed from userprog/addrspace.cc // on a big-endian machine, it converts all fields of // the NOFF header to little-endian format // on a little-endian machine, where the header is already // in little-endian format, it does nothing 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); } /****************************************************************/ #define ReadStruct(f,s) Read(f,(char *)&s,sizeof(s)) char *noffFileName = NULL; /* read and check for error */ void Read(int fd, char *buf, int nBytes) { if (read(fd, buf, nBytes) != nBytes) { fprintf(stderr, "File is too short\n"); unlink(noffFileName); exit(1); } } /* write and check for error */ void Write(int fd, char *buf, int nBytes) { if (write(fd, buf, nBytes) != nBytes) { fprintf(stderr, "Unable to write file\n"); unlink(noffFileName); exit(1); } } int main(int argc, char **argv) { int fdIn, fdOut, numsections, i, inNoffFile; struct filehdr fileh; struct aouthdr systemh; struct scnhdr *sections; char *buffer; NoffHeader noffH; if (argc < 2) { fprintf(stderr, "Usage: %s <coffFileName> <noffFileName>\n", argv[0]); exit(1); } /* open the COFF file (input) */ fdIn = open(argv[1], O_RDONLY, 0); if (fdIn == -1) { perror(argv[1]); exit(1); } /* open the NOFF file (output) */ fdOut = open(argv[2], O_WRONLY|O_CREAT|O_TRUNC , 0666); if (fdIn == -1) { perror(argv[2]); exit(1); } noffFileName = argv[2]; /* Read in the file header and check the magic number. */ ReadStruct(fdIn,fileh); fileh.f_magic = ShortToHost(fileh.f_magic); fileh.f_nscns = ShortToHost(fileh.f_nscns); if (fileh.f_magic != MIPSELMAGIC) { fprintf(stderr, "File is not a MIPSEL COFF file\n"); unlink(noffFileName); exit(1); } /* Read in the system header and check the magic number */ ReadStruct(fdIn,systemh); systemh.magic = ShortToHost(systemh.magic); if (systemh.magic != OMAGIC) { fprintf(stderr, "File is not a OMAGIC file\n"); unlink(noffFileName); exit(1); } /* Read in the section headers. */ numsections = fileh.f_nscns; printf("numsections %d \n",numsections); sections = (struct scnhdr *)malloc(numsections * sizeof(struct scnhdr)); Read(fdIn, (char *) sections, numsections * sizeof(struct scnhdr)); for (i = 0; i < numsections; i++) { sections[i].s_paddr = WordToHost(sections[i].s_paddr); sections[i].s_size = WordToHost(sections[i].s_size); sections[i].s_scnptr = WordToHost(sections[i].s_scnptr); } /* initialize the NOFF header, in case not all the segments are defined * in the COFF file */ noffH.noffMagic = NOFFMAGIC; noffH.code.size = 0; noffH.initData.size = 0; noffH.uninitData.size = 0; #ifdef RDATA noffH.readonlyData.size = 0; #endif /* Copy the segments in */ inNoffFile = sizeof(NoffHeader); lseek(fdOut, inNoffFile, 0); printf("Loading %d sections:\n", numsections); for (i = 0; i < numsections; i++) { printf("\t\"%s\", filepos 0x%x, mempos 0x%x, size 0x%x\n", sections[i].s_name, (unsigned int)sections[i].s_scnptr, (unsigned int)sections[i].s_paddr, (unsigned int)sections[i].s_size); if (sections[i].s_size == 0) { /* do nothing! */ } else if (!strcmp(sections[i].s_name, ".text")) { noffH.code.virtualAddr = sections[i].s_paddr; noffH.code.inFileAddr = inNoffFile; noffH.code.size = sections[i].s_size; lseek(fdIn, sections[i].s_scnptr, 0); buffer = malloc(sections[i].s_size); Read(fdIn, buffer, sections[i].s_size); Write(fdOut, buffer, sections[i].s_size); free(buffer); inNoffFile += sections[i].s_size; } else if (!strcmp(sections[i].s_name, ".data")){ noffH.initData.virtualAddr = sections[i].s_paddr; noffH.initData.inFileAddr = inNoffFile; noffH.initData.size = sections[i].s_size; lseek(fdIn, sections[i].s_scnptr, 0); buffer = malloc(sections[i].s_size); Read(fdIn, buffer, sections[i].s_size); Write(fdOut, buffer, sections[i].s_size); free(buffer); inNoffFile += sections[i].s_size; #ifdef RDATA } else if (!strcmp(sections[i].s_name, ".rdata")){ noffH.readonlyData.virtualAddr = sections[i].s_paddr; noffH.readonlyData.inFileAddr = inNoffFile; noffH.readonlyData.size = sections[i].s_size; lseek(fdIn, sections[i].s_scnptr, 0); buffer = malloc(sections[i].s_size); Read(fdIn, buffer, sections[i].s_size); Write(fdOut, buffer, sections[i].s_size); free(buffer); inNoffFile += sections[i].s_size; #endif } else if (!strcmp(sections[i].s_name, ".bss")){ /* need to check if we have both .bss and .sbss -- make sure they * are contiguous */ if (noffH.uninitData.size != 0) { if (sections[i].s_paddr == (noffH.uninitData.virtualAddr + noffH.uninitData.size)) { fprintf(stderr, "Can't handle both bss and sbss\n"); unlink(noffFileName); exit(1); } noffH.uninitData.size += sections[i].s_size; } else { noffH.uninitData.virtualAddr = sections[i].s_paddr; noffH.uninitData.size = sections[i].s_size; } /* we don't need to copy the uninitialized data! */ } else { fprintf(stderr, "Unknown segment type: %s\n", sections[i].s_name); unlink(noffFileName); exit(1); } } lseek(fdOut, 0, 0); // convert the NOFF header to little-endian before // writing it to the file SwapHeader(&noffH); Write(fdOut, (char *)&noffH, sizeof(NoffHeader)); close(fdIn); close(fdOut); exit(0); }
nachos/coff2noff/coff.h
/* coff.h * Data structures that describe the MIPS COFF format. */ struct filehdr { unsigned short f_magic; /* magic number */ unsigned short f_nscns; /* number of sections */ long f_timdat; /* time & date stamp */ long f_symptr; /* file pointer to symbolic header */ long f_nsyms; /* sizeof(symbolic hdr) */ unsigned short f_opthdr; /* sizeof(optional hdr) */ unsigned short f_flags; /* flags */ }; #define MIPSELMAGIC 0x0162 #define OMAGIC 0407 #define SOMAGIC 0x0701 typedef struct aouthdr { short magic; /* see above */ short vstamp; /* version stamp */ long tsize; /* text size in bytes, padded to DW bdry*/ long dsize; /* initialized data " " */ long bsize; /* uninitialized data " " */ long entry; /* entry pt. */ long text_start; /* base of text used for this file */ long data_start; /* base of data used for this file */ long bss_start; /* base of bss used for this file */ long gprmask; /* general purpose register mask */ long cprmask[4]; /* co-processor register masks */ long gp_value; /* the gp value used for this object */ } AOUTHDR; #define AOUTHSZ sizeof(AOUTHDR) struct scnhdr { char s_name[8]; /* section name */ long s_paddr; /* physical address, aliased s_nlib */ long s_vaddr; /* virtual address */ long s_size; /* section size */ long s_scnptr; /* file ptr to raw data for section */ long s_relptr; /* file ptr to relocation */ long s_lnnoptr; /* file ptr to gp histogram */ unsigned short s_nreloc; /* number of relocation entries */ unsigned short s_nlnno; /* number of gp histogram entries */ long s_flags; /* flags */ };
nachos/code/README
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>
nachos/code/build.cygwin/Makefile.dep
################################################################## # 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
nachos/code/build.cygwin/Makefile
# 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
nachos/code/machine/callback.h
// 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
nachos/code/machine/interrupt.cc
nachos/code/machine/interrupt.cc
// 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"
;
kernel
->
stats
->
Print
();
delete
kernel
;
// Never returns.
}
//----------------------------------------------------------------------
// Interrupt::Schedule
// Arrange for the CPU to be interrupted when simulated time
// reaches "now + when".
//
// Implementation: just put it on a sorted list.
//
// NOTE: the Nachos kernel should not call this routine directly.
// Instead, it is only called by the hardware device simulators.
//
// "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"
;
}
nachos/code/machine/network.h
// 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
nachos/code/machine/mipssim.h
// 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
nachos/code/machine/timer.h
// 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
nachos/code/machine/machine.cc
nachos/code/machine/machine.cc
// machine.cc
// Routines for simulating the execution of user programs.
//
// DO NOT CHANGE -- part of the machine emulation
//
// Copyright (c) 1992-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
;
}
nachos/code/machine/interrupt.h
// 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 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 bool inHandler; // TRUE if we are running an interrupt handler bool yieldOnReturn; // TRUE if we are to context switch // on return from the interrupt handler MachineStatus status; // idle, kernel mode, user mode // these functions are internal to the interrupt simulation code bool CheckIfDue(bool advanceClock); // Check if 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
nachos/code/machine/translate.h
// 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
nachos/code/machine/console.h
// 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
nachos/code/machine/translate.cc
nachos/code/machine/translate.cc
// 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
;
}
nachos/code/machine/timer.cc
nachos/code/machine/timer.cc
// 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
);
}
}
nachos/code/machine/stats.cc
nachos/code/machine/stats.cc
// 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"
;
}
nachos/code/machine/disk.h
// 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
nachos/code/machine/network.cc
nachos/code/machine/network.cc
// 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
;
}
nachos/code/machine/mipssim.cc
nachos/code/machine/mipssim.cc
// 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
: