
\documentstyle[11pt,psfig]{article}

\setlength{\oddsidemargin}{0in}
\setlength{\evensidemargin}{0in}
\setlength{\topmargin}{-0.3in}
\setlength{\headheight}{0in}
\setlength{\headsep}{0in}
\setlength{\textwidth}{6.5in}
\setlength{\textheight}{9.0in}
\setlength{\parindent}{0in}
\setlength{\parskip}{0.05in}

\begin{document}

\title{Chapter 4 \\
Machine-Language Program Generation, Loading and Execution}

\author{Norman Matloff \\
University of California at Davis}                                

\date{December 8, 1996}

\maketitle            

Throughout this chapter, the reader should make sure to not lose sight
of the chapter title, ``Machine-Language Program Generation, Loading and
Execution.'' Each of these three activities is significant.

First, what do we mean by ``machine-language program generation''?  Keep
in mind that by program we mean the actual machine code that executes.
In Chapter 3, we really did things the hard way.  We generated machine
language code by hand, painfully hex digit by hex digit.  Fortunately,
there are other ways to generate machine language.  In fact, you have
been doing so all along---any time you invoke a C compiler, the compiler
is generating machine language from your C source code.  But what if you
need to stay closer to the machine than C allows?  You still have an
alternative to generating machine language code by hand:  assembly
language.  You will see how this works in the next few pages.

Next, what about ``machine-language program loading''?  In Chapter 3 and
Appendices A and B, we also did this by hand, loading the hex values of
our machine code into memory, word by word.  Of course, Mac-1 and DLX
are simulated machines, but this can be done for real machines too.  For
example, the DOS operating system on PCs includes a program called {\bf
debug}, which works in a very similar way to what we saw in Appendices A
and B; one can use {\bf debug} to enter values, including program
instructions, into memory.

Finally, what about ``machine-language program execution''?  Again in
Chapter 3 and Appendices A and B thus was done by hand, via the
simulator commands.  Again it can be done on real machines, {\bf debug}
has such commands on PCs.

Just as compilers and assemblers allow us to escape the drudgery of
writing in machine language, operating systems allow us to avoid
tediously entering and running programs by hand.  Later in this
chapter we will see how this is done.

\section{Generating Machine Code:  Introduction to Assemblers}

\subsection{Hire a Clerk!}

{\it Programming in machine language is extremely tedious}.  It is 
quite inconvenient to work with the complicated set of codes for 
operations and addressing modes.  A much better alternative 
is {\bf assembly language}.  Essentially the assembly language for a 
given CPU is {\it equivalent} to the machine language for that CPU.  
The only difference is that the assembly language looks more like 
English, and thus is much more convenient to use.

For example, consider the instruction 0x1258 in the Mac-1 example in 
Chapter 3.  This is a {\bf stod} instruction which stores c(AC) into
location 600 of memory.  Our assembly language description of the 
instruction would be simple, intuitive and English-like:

\begin{verbatim}
stod 600
\end{verbatim}

For us humans, writing the program---or, for that matter, even 
reading it---would be much easier in this form than in 0x1258.  Yet
the machine can only execute the latter.  What can we do to enable
us to write the former and still execute the latter?  The answer is,
hire a clerk!  

For instance, we would tell the clerk that whenever we write ``stod,''
we mean 0x1, and that whatever we write following ``stod,'' in this
example 600, must be converted to hex and then appended to the 0x1.  
So, we would write
 
\begin{verbatim}
stod 600
\end{verbatim}

and the clerk would produce 0x1258 from it.

We would set up English-like notation similar to this for all the 
instructions in the Mac-1 architecture, and tell the clerk about
this notation.  In this way, we would write our program in ``English,''
which is much more convenient for us, and let the clerk handle the
drudgery of producing the actual machine code.

For the Mac-1 example program in Chapter 3 as a whole, we would write

\begin{verbatim}
lodd 540
stod 600
subd 552
jpos 262
lodd 552
stod 600
loco 1
addd 600
stod 600
\end{verbatim}

We would tell the clerk to produce the machine code from this ``English.''
The clerk would produce from this

\begin{verbatim}
0x021c
0x1258
0x3228
0x4106
0x0228
0x1258
0x7001
0x2258
0x1258
\end{verbatim}     

The clerk would not need to know anything about computers to do this;
his/her work would be purely mechanical.  That implies that we do not
need a human to do it, but could instead have the {\it computer
itself} do the work.  In other words, we write a special program,
called an {\bf assembler}, whose function is to do the work of the
clerk.  Whenever we had a machine language program we wished to
write, we would write it in a file in ``English'' as above, and
the assembler would read the file and output the machine language.

This is indeed what is done, on virtually any computer system.
The ``English'' is referred to as {\bf assembly language}, but
you should note that it is not really a language; it is merely
a set of shorthand abbrevations, such as ``stod'' above being an 
abbreviation for 0x1.  {\it Assembly language is essentially
equivalent to machine language.}  Thus different computer architectures
will have different assembly languages.

On Unix systems, for instance, the name of the assembler program is {\bf
as}.  The programmer writes a program in the assembly language of the
machine in question, and then uses a text editor to type it into a file
whose name has a ``.s'' suffix, just as C source files have a ``.c''
suffix.  Then he/she runs {\bf as} on that file, producing an output
file, whose name typically has a ``.o'' suffix, called the {\bf object}
file; this latter file consists of the machine language generated by the
assembler.

On DOS/Windows systems, popular assemblers are MASM from Microsoft
and TASM from Borland; input files are in Intel assembly language and
have the suffix ``.asm'' in their names, and produce Intel machine
language output files with the suffix ``.obj.''

For Mac-1, the assembler is named {\bf assem}.  Suffixes for input and
output files are ``.asm'' and ``.mac'', respectively, for the Mac-1
package written by Professor Rosalee Nerheim-Wolfe of DePaul University.

The case of DLX is a bit different.  The DLX simulator combines the
functions of an assembler and a simulator.  So, while the Mac-1 package
includes separate programs {\bf assem} and {\bf sim}, the DLX package
has only {\bf dlxsim}.  The programmer prepares the .s assembly-language
source file and then inputs it to {\bf dlxsim}, which both assembles
the code and runs it.  But if a real DLX machine were built, things
would be handled as in the Unix description above.

On most other systems, the first few bytes of the machine-language file
produced by the assembler (or, for that matter, by a compiler) will not
be machine language, but rather {\bf header} information which describes
the format of the file, its length and so on.

In describing the translation that the assembler does from assembly 
language to machine language, we say that the assembler {\bf assembles} 
a file of source code into a file of object code.  The latter 
terminology is used for high-level language compilers:  We say that a 
compiler {\bf compiles} a source code file, of say Pascal or C code, 
into a file of object code.

However, it is important not to confuse the terms {\it assemble} and 
{\it compile}.  The difference is in not just in the type of source code
involved (say assembly language versus C), but more importantly, in
a conceptual manner.  As mentioned early, assembly language is really
just a system of shorthand abbreviations for the corresponding machine
language, so an assembler program consists of little more than mere
table lookups.  By contrast, compiling machine language from a high 
level language like C is a creative process.  Two different compilers,
say the Microsoft and Turbo compilers on a DOS/Windows system, will
generally produce different machine language from the same C source
code.\footnote{Most Unix systems include at least two C compilers,
one being {\bf cc}, a compiler written by the manufacturer itself,
and the other being {\bf gcc}, a free compiler written by the GNU
Software Foundation.}

\section{A Closer Look:  Mac-1 Assembly Language Example}

In order to learn more about assembly language and assemblers, let us
look at another Mac-1 program.  This one will compute the first ten
eollows.  The first two numbers, $f_1$ and $f_2$, are equal to 1, and
for $j \geq 2$ we have

$$
f_j = f_{j-1} + f_{j-2}
$$

So $f_3 = 2$, $f_4 = 3$, $f_5 = 5$, and so on.

Here is the Mac-1 source code:

\begin{verbatim}
;  Fibonacci number generator
;
top      lodd i
         swap
         lodl fib   ; get fib_i
         stod tmp
         insp 1
         lodl fib   ; get fib_{i+1}
         addd tmp
         insp 1 
         stol fib   ; store fib_{i+2}
         desp 1
         swap
         stod i     ; next i
         subd n     ; done with loop?
         jnzr top
;
i        const 0
n        const 8
tmp      const 0
fib      const 1
         const 1
         end

    Program 4.1
\end{verbatim}

The assembler will produce machine language from this, which will later
(at run time) be loaded into memory as follows:\footnote{Keep in mind 
that the assembler itself does not load a program into memory.  That 
operation is done by another program, called a {\bf loader}, which will be
discussed later.}

\begin{verbatim}
0x000: 000e
0x001: fa00
0x002: 8011
0x003: 1010
0x004: fc01
0x005: 8011
0x006: 2010
0x007: fc01
0x008: 9011
0x009: fe01
0x00a: fa00
0x00b: 100e
0x00c: 300f
0x00d: d000
0x00e: 0000
0x00f: 0008
0x010: 0000
0x011: 0001
0x012: 0001

    Program 4.2
\end{verbatim}

As you can see, the assembler prepares the program for (later, runtime) 
loading to begin a location 0x000 of memory.  Thus when we run the 
program, the {\bf lodd} instruction you see in the first line will be 
in location 0x000, the {\bf swap} will be in location 0x001, and so on.

Most of Program 4.1 should look familiar to you, as it consists of Mac-1
instruction names, such as {\bf lodd}, {\bf swap} and so on.  

Some of the new items are quite straightforward.  Any material to
the right of a semicolon within a line is considered a comment and 
thus ignored by the assembler.  And the {\bf end} line tells the 
assembler to stop assembling; no code will follow this line.

Other items will be explained shortly.  But first, we will discuss
the fact that the assembler must make two {\bf passes} at Program 4.1 
during its assembly process, meaning that it will read through the
assembly language source file twice.  In the first pass, the assembler
will look for {\bf labels}, and record their locations.  In Program
4.1, the labels are top, i, n, tmp and fib, which are similar to the
concept of variables in high-level language source code.  The assembler
will form a symbol table for them, which would look like this:

\begin{verbatim}
top        0x000
i          0x00e
n          0x00f
tmp        0x010
fib        0x011
\end{verbatim}

The specific locations listed here---0x000, 0x00e, 0x00f, and so on---are
determined by the order of the lines in the assembly language source
file.  Since ``top'' is a label for the first (noncomment) line in 
the file, and since the assembler will assign that instruction for 
later loading at location 0x000, then ``top'' will be a name meaning 
location 0x000.  Similarly, the line labeled ``i'' is the fifteenth
noncomment line in the file, and thus will be assigned to memory
location 14 = 0x00e.

Note too that we are telling the assembler to initialize i to 0,
n to 8, and so on.\footnote{A more accurate statement would be that
we are telling the assembler to tell the loader to make these
initializations.}  However, these values can and will change as the 
program executes, so the Mac-1 assembler term {\bf const} (``constant'') 
here is misleading.\footnote{The reader should think about the fact that
the assembler could not enforce keeping these locations' contents
constant even if it wanted to.  A machine instruction can, after
all, write to any location in (RAM) memory.}

We mentioned earlier that these symbols play the same role as variables 
in high-level languages.\footnote{An exception is the symbol ``top'',
which plays the role of the label corresponding to a {\bf goto} 
statement in C.}  In that context, it should be noted that ``fib''
will play the role of an array variable.  In other words, location
0x011 will not only have the name ``fib'' (during the assembly process),
but also it will be the start of an array, the entirety of which we
will also refer to as ``fib''.\footnote{Recall that such a dual
interpretation exists in the C language too.  If we have an array
named, say x, then for example x[3] means the fourth element of the
array, but x by itself means the start of the array.  The resemblance
is no accident; the designers of C intended it to be similar to
assembly language in some senses, because they intended C as 
more convenient alternative to assembly language in applications
requiring low-level control of the machine.}  So location 0x011
will be the first element of the array, 0x012 the second element,
and so on.  Since the program will calculate the first ten Fibonacci
numbers, we will need room for ten array elements.  

This is no problem here, since there are no ``variables'' declared after
fib; the entire remainder of the memory after 0x011 is available to us.
But if another variable, say y, had followed fib, we would need to use
the Mac-1 assembler's {\bf org} construct, as follows:

\begin{verbatim}
i        const 0
n        const 8
tmp      const 0
fib      const 1
         const 1
y        org 27
         end
\end{verbatim}

This forces the assembler to define y as 27, i.e 0x01b, in its symbol
table.  Since fib starts at 0x011 = 17, this will leave 10 words for
fib, exactly what we want.

Another use for the ``org'' directive is to ensure access to specific
memory locations.  For instance, many systems use {\bf memory-mapped
input/output}.  Here an input/output device, say the keyboard, is
accessible via what appears to be a specific memory location; in order
for a program to read a key typed at the keyboard, it reads that memory
location.\footnote{The device actually is not part of memory, and in
fact there is no memory at that particular location.  We are merely
making things {\it appear} to the CPU that it is accessing memory.}; 

By the way, the Mac-1 assembler's constructs {\bf const} and {\bf org}
above are called {\bf directives} (or sometimes {\bf pseudo-ops}).
This name stems from the fact that they are indeed directing---that
is, commanding---the assembler to take some action, such as to define
y at 27 in its symbol table.  These are in sharp contrast to lines
like 

\begin{verbatim}
         insp 1
\end{verbatim}

which correspond to actual machine instructions. 

Keep in mind that an assembler (whether Mac-1's or any other) is itself 
a program.  The symbol table is then an array in that program.  Another
important variable in an assembler program is the {\bf location
counter}.\footnote{You will see some resemblance to the program counter
in a CPU, but they of course are different.  A program counter is a
permanent physical object, hardware, while the location counter is
just a variable in a program (the assembler).}  It keeps track of
which memory address the assembler is to assign each instruction or
data item.  The location counter starts at zero, and is incremented
each time the assembler goes to a new line of assembly code.

In our example here, for instance, the first item encountered by the 
assembler will be the line labeled ``top''.  Since the location counter
is 0 at that time, this label will be assigned the value 0 in the
symbol table.  Also, the assembler will assign that instruction
(lodd i) to address 0.  The next instruction (swap) will be assigned to
address 1, and so on.  By the time the assembler reaches the line
labeled i, the location counter will be 0x00e, so i will be assigned
that value in the symbol table.

In this light, what the org directive really does is tell the assembler
to set its location counter to the specified value.  The line

\begin{verbatim}
         org 27
\end{verbatim}

above tells the assembler to set its location counter to 27.  The
effect of this will be to leave room for fib, as desired.
 
In its second pass, the assembler begins its actual generation of
machine code.  It sees the first noncomment line,

\begin{verbatim}
top      lodd i
\end{verbatim}

and after consulting its symbol table, finds that ``i'' is the name 
of memory location 0x00e.  The assembler will now append that value
to the op code for {\bf lodd}, which is 0x0, thus producing the
full machine instruction corresponding to this line in the source
file, that is 0x000e.

As seen in Appendix A, Mac-1's {\bf swap} instruction exchanges the
contents of AC and SP, so now the value of i is in SP.  This will
be used in the next instruction:

\begin{verbatim}
         lodl fib   ; get f_i
\end{verbatim}

Since we did not use the {\bf lodl} instruction in Chapter 3, we will
introduce it here.  Its name stands for ``load local,'' the meaning
of which relates to local variables in high-level languages like C,
but which we will not be able to fully motivate until Chapter 5.  In
any case, its use here is more like indexed addressing, and works
as follows:  Mac-1 will treat {\bf lodl}'s operand, in this case,
fib, as a base, and then add the contents of SP to that base.  The
sum will be treated as a memory address, and the contents of that
address will then be loaded into the AC register.

Let us take a step back from the ``trees'' and look at the ``forest.''
What is the real import of what is occurring in this instruction?
Recall that the previous two instructions,

\begin{verbatim}
top      lodd i
         swap
\end{verbatim}

placed the value of i in SP.  So, from the description in the preceding
paragraph on the operation of {\bf lodl}, you see that this instruction
is fetching fib[i], just as the comment indicates.  You should now also
see why we said that SP, nominally the stack pointer, is also doubling
as an index register, in this case containing the index i.

Next, the {\bf stod} instruction copies that value of f[i] to
tmp, for later use.  Keep in mind that tmp is a memory location
(specifically 0x010); if Mac-1 had more registers, we would have
used one of them instead of memory.

Next, {\bf insp 1} adds 1 to SP, setting up for fetching f[i+1]
in the following {\bf lodd} instruction.  Then the {\bf addd}
instruction retrieves f[i] from tmp and adds it to the copy of
f[i+1] in AC, so AC now contains f[i]+f[i+1], which is f[i+2].

In order to store to the latter, we need to add 1 to SP again,
so we have another {\bf insp 1}, followed by {\bf stol}, which
is the mirror image of the {\bf lodl} instruction we explained
earlier.

Next, {\bf desp 1} decrements SP back to i+1, in preparation for
the next iteration of the loop.  In other words, we are doing
what pseudocode might describe as

\begin{verbatim}
i <-- i + 1
\end{verbatim}

To accomplish this, that is to get this new value back into the
memory location which we symbolically are calling i (0x00e), we
must first get the value into AC from SP, which we do through the
{\bf swap} instruction.  Then we store to i using {\bf stod}.

Finally, we must test whether we have finished all the loop iterations.
To do this, we use {\bf subd} to subtract n from i (actually, from the
{\it copy} of i in AC).  A zero result would mean we are done with 
the loop; if nonzero, we jump back to the location labeled ``top''.

Note again that in assembling this last line,

\begin{verbatim}
         jnzr top
\end{verbatim}

the assembler must again consult its symbol table to find what address
``top'' corresponds to.  This turns out to be 0x000, and since the
format for the {\bf jnzr} instruction is 0xdyyy, where y is the branch
target, the assembler produces the machine language 0xd000 from this
line of assembly code.

\section{DLX Assembly Language Example}

Here is a version of the Fibonacci number program for DLX:

\begin{verbatim}
          addi r1,r0,4   ;;  r1 will hold "i+1", initially "1", i.e. 4
          addi r2,r0,1   ;;  r2 will hold fib[i], starting with i = 0 
top:      lw r3,fib(r1)  ;;  fetch fib[i+1]
          add r4,r2,r3   ;;  calculate fib[i+2]
          addi r5,r1,4   ;;  r5 will hold "i+2"
          sw fib(r5),r4  ;;  store fib[i+2]
          addi r1,r1,4   ;;  prepare for next iteration of loop
          addi r2,r3,0   ;;  prepare for next iteration of loop  
          slti r7,r5,40  ;;  done with loop?
          bnez r7,top
          nop
fib:      
.word 1,1

             Program 4.3
\end{verbatim}

Armed with your understanding of Mac-1's assembly language, you should
find that the DLX case is similar.  Even though these are two quite
different architectures, the general principles are the same.

Almost all of the instructions you see here appeared in the DLX example
in Chapter 3.  The only new instructions are {\bf bnez} (``branch if not
zero''), which is simply the mirror image of the {\bf bqez} instruction
we saw in that example, and {\bf slti}, which is like {\bf slt} except
that the second argument is given in immediate addressing mode.

The syntax is new, even if the instructions are not.  The DLX syntax
tends to list destination registers before source registers.  Thus
for instance

\begin{verbatim}
          add r4,r2,r3  
\end{verbatim}

means to place the sum of r2 and r3 in r4.  Check Appendix B for the
syntax for other instructions.

The logic of the code is very similar to that of Program 4.1, the Mac-1
version.  A major difference, though, is that the program here has many
fewer instructions which access memory.  This is due to the fact that
DLX has many registers, while Mac-1 has very few.  For instance, in the
Mac-1 version, the quantity ``i'' had to be kept in memory, whereas with
DLX we can use a register (r1) for ``i+1''.\footnote{Since consecutive
word addresses in DLX differ by 4, compared to Mac-1's 1, each iteration
of our loop will increment r1 by 4.  So, r1 actually contains 4\*(i+1),
rather than i+1 itself.}  This is very important; as pointed out in
Chapter 2, memory access is usually the ``weakest link'' in a CPU-memory
system, the slow part of the system, so the fewer memory accesses a
program makes, the better.  As you can see here, the flexibility
provided by a larger register set also makes programming easier, both
for the assembly-language programmer and also for the compiler
writer.\footnote{However, the reader should not leap to the false
conclusion that having a small register set is a CISC characteristic.
As pointed out in Chapter 3, while some CISC architectures, notably
Intel's, do have small register sets, others, such as DEC's VAX, have
large register sets.}

Suppose this program is assembled and loaded starting at 0x100, 
say\footnote{The default for the DLX simulator.}, then these 
instructions will occupy words 0x100 through 0x128.  Let us look
at the process in detail for the line

\begin{verbatim}
top:      lw r3,fib(r1)  ;;  fetch fib[i+1]
\end{verbatim}

We see that this is the {\bf lw} (``load word'') instruction we
encountered in Chapter 3, and from Appendix B, we can see further that
the syntax here is an instance of
 
\begin{verbatim}
lw dr,c(sr)
\end{verbatim}

where dr specifies a destination register, sr a source register and
c a constant.  We also see that the action of this instruction is

\begin{verbatim}
dr <-- m[c+sr]
\end{verbatim}

The reader should recognize this as being the indexed addressing mode
which we discussed in previous chapters.

Appendix B also tells us that the machine code produced by the assembler
from this line of assembly code will have the form

\begin{verbatim}
100011 sr dr c
\end{verbatim}

where dr and sr are 5-bit fields and c is a 16-bit field.  Clearly sr 
is r1, and thus will be 00001, and dr is r3, that is 00011.  So the
first 16 bits of the instruction will be 

\begin{verbatim}
100011 00001 00011
\end{verbatim}

which in hex notation is 0x8c23.

Now, what about c?  Well, c is associated with fib in the instruction,
and since the label fib in the source code immediately follows the last
instruction (a {\bf nop}), which we found to be located at 0x128, the
label fib will mean the address 0x128+4 = 0x12c.  Thus c will be 0x012c,
and the entire instruction produced by the assembler will be 0x8c23012c.

Recall that this instruction uses indexed addressing mode, and that
this mode is intended for use with arrays.  We do indeed have an array
here, fib, and this instruction tells us to load into r3 the memory
word located c(r1) bytes past fib.  The connection to arrays becomes
even clearer from the comment in the assembly code above, which says
that r1 holds ``i+1'', or more precisely (i+1)\*sizeof(int).  The
word (i+1)\*sizeof(int) bytes past fib is fib[i+1], exactly what we
want.

This relation of indexed addressing mode to arrays is also the
inspiration for the syntax DLX assembly language uses for that mode,
in this case fib(r1), which looks like ``fib[r1]'' in C notation.
Similar syntax is used in most assembly languages for machines which
feature indexed addressing mode.

Just as Mac-1's assembly language allows directives, DLX's does too
(as does virtually every assembly language).  We see one of them
here, immediately following the line labeled fib:

\begin{verbatim}
.word 1,1
\end{verbatim}

This tells the assembler to insert the value 1 into two consecutive
words at that point in the machine language file.  Since the label
fib was determined to be address 0x12c, this means that when the
program begins execution, words 0x12c and 0x130 will contain the
value 0x00000001.

Suppose there were
another label in the file, after fib, say y:

\begin{verbatim}
fib:
.word 1,1
y:
\end{verbatim}

This would limit fib to only two words of memory, well short of the
10 words we need.  If we were to attempt to access fib[2], for instance,
that would be identical to y, which would be a disaster, since
presumably we intended y to be separate from the array fib.  To avoid
this problem, we can use another DLX directive:

\begin{verbatim}
fib:
.word 1,1
.space 8
y:
\end{verbatim}

where ``.space 8'' simply directs the assembler to leave 8 more words
of space for us before assigning a memory location for y.  This, of
course, is very similar to the purpose (though not the exact operation)
of the Mac-1 assembler's ``org'' directive.\footnote{DLX also has a
directive that works identically to Mac-1's ``org'', called ``.data''.
For instance,

.data 200

would mean that the assembler should set its location counter to 200.}

Note the {\bf nop} in Program 4.3.  It fills the delay slot described
in Chapter 3.  Hopefully, we can replace it by one of the other
instructions, thus increasing the speed of the program.  It turns
out that we can: 

\begin{verbatim}
          addi r1,r0,4   ;;  r1 will hold "i+1", initially "1", i.e. 4
          addi r2,r0,1   ;;  r2 will hold fib[i], starting with i = 0 
top:      lw r3,fib(r1)  ;;  fetch fib[i+1]
          add r4,r2,r3   ;;  calculate fib[i+2]
          addi r5,r1,4   ;;  r5 will hold "i+2"
          sw fib(r5),r4  ;;  store fib[i+2]
          addi r1,r1,4   ;;  prepare for next iteration of loop
          slti r7,r5,40  ;;  done with loop?
          bnez r7,top
          addi r2,r3,0   ;;  prepare for next iteration of loop  

fib:      
.word 1,1

               Program 4.4
\end{verbatim}

The instruction

\begin{verbatim}
          addi r2,r3,0   ;;  prepare for next iteration of loop
\end{verbatim}

which had previously been just before the {\bf slti} instruction,
has now replaced the {\bf nop}.  Remember, this change can be done only
because the intervening instructions, 

\begin{verbatim}
          slti r7,r5,40  ;;  done with loop?
          bnez r7,top
\end{verbatim}

do not rely on the contents of r2, which are affected by the {\bf addi}
instruction that we are moving.  

The DLX architecture is very similar to that of MIPS, a commercial
CPU made by Silicon Graphics.  An experimental version of MIPS,
called MIPS-X, actually has two delay slots after each branch
instruction.  Generally it is much harder to find two instructions
to move, so with MIPS-X one often must settle for having at least
one {\bf nop} after a branch.  But in the example here, we see
that both {\bf addi} instructions preceding the {\bf slti} could
be moved, and thus on MIPS-X both delay slots could be filled
in this case.

\section{Generating Machine Code:  Compilers}

The most common way to generate machine language is the one you
have been using all along:  You write source code in a high-level
language, say C, and then compile it.

On Unix systems, say we have a C source file gy.c.  If it is a full
program, we can type

\begin{verbatim}
cc gy.c
\end{verbatim}

producing a machine-language executable file a.out.  If on the other
hand the file is just one of many modules making up the source code,
we can type

\begin{verbatim}
cc -c gy.c
\end{verbatim}

which produces a machine-language (but nonexecutable) file gy.o,
which will later be combined with the other modules.

If we are curious what machine language the compiler produces, we
can request that the compiler produce an assembly-language file
instead.  The command

\begin{verbatim}
cc -S gy.c
\end{verbatim}

creates a file gy.s with the assembly code corresponding to a.out
or gy.o above.  

Note by the way that we could then assemble the gy.s file to
produce gy.o:

\begin{verbatim}
as gy.s -o gy.o
\end{verbatim}

The point here is that the machine instructions in a .o file
can come either from assembly-language source or a high-level
language source such as C.  One cannot tell by looking at the
instructions what kind of source they were generated from.
After all, for instance, an {\bf lw} instruction (say in DLX)
is the same, no matter how it was generated.

\subsection{DLX Example}

The compiler in the DLX simulator package is configured to produce
assembly code, as though we had used the ``-S'' command line option.
Let us look at an example, a C-language source file for calculating
Fibonacci numbers:

\begin{verbatim}
  1	
  2	
  3	#define NFIB 10
  4	
  5	
  6	int Fib[NFIB],I;
  7	
  8	
  9	main()
 10	
 11	{  Fib[0] = Fib[1] = 1;
 12	   for (I = 0; I < NFIB-2; I++)
 13	      Fib[I+2] = Fib[I] + Fib[I+1];
 14	}
 15	

             Program 4.5
\end{verbatim}

The command

\begin{verbatim}
dlxcc fib.c
\end{verbatim}

produces a file fib.s:\footnote{This file can then be input directly
into {\bf dlxsim} for (simulated) execution.}

\begin{verbatim}
   1	.global _exit
   2	.global _open
   3	.global _close
   4	.global _read
   5	.global _write
   6	.global _printf
   7	
   8		.align 4
   9	.global _main
  10	_main:
  11		;; Initialize Stack Pointer
  12		add r14,r0,r0
  13		lhi r14, ((memSize-4)>>16)&0xffff
  14		addui r14, r14, ((memSize-4)&0xffff)
  15		;; Save the old frame pointer 
  16		sw -4(r14),r30
  17		;; Save the return address 
  18		sw -8(r14),r31
  19		;; Establish new frame pointer 
  20		add r30,r0,r14
  21		;; Adjust Stack Pointer 
  22		add r14,r14,#-32
  23		;; Save Registers 
  24		sw 0(r14),r3
  25		sw 4(r14),r4
  26		sw 8(r14),r5
  27		sw 12(r14),r6
  28		sw 16(r14),r7
  29		lhi r3,(_Fib>>16)&0xffff
  30		addui r3,r3,(_Fib&0xffff)
  31		lhi r4,(_Fib>>16)&0xffff
  32		addui r4,r4,(_Fib&0xffff)
  33		addi r7,r0,#1
  34		sw 4(r4),r7
  35		addi r7,r0,#1
  36		sw 0(r3),r7
  37		lhi r3,(_I>>16)&0xffff
  38		addui r3,r3,(_I&0xffff)
  39		sw 0(r3),r0
  40	L2:
  41		lhi r3,(_I>>16)&0xffff
  42		addui r3,r3,(_I&0xffff)
  43		lw r3,0(r3)
  44		addi r4,r0,#7
  45			;cmpsi	r3,r4
  46		sgt	r1,r3,r4
  47		bnez	r1,L3
  48		nop
  49		lhi r4,(_I>>16)&0xffff
  50		addui r4,r4,(_I&0xffff)
  51		lhi r3,(_Fib+8>>16)&0xffff
  52		addui r3,r3,(_Fib+8&0xffff)
  53		lw r4,0(r4)
  54		slli r4,r4,#2
  55		add r3,r3,r4
  56		lhi r4,(_I>>16)&0xffff
  57		addui r4,r4,(_I&0xffff)
  58		lw r4,0(r4)
  59		lhi r5,(_Fib>>16)&0xffff
  60		addui r5,r5,(_Fib&0xffff)
  61		slli r4,r4,#2
  62		add r4,r5,r4
  63		lhi r6,(_I>>16)&0xffff
  64		addui r6,r6,(_I&0xffff)
  65		lhi r5,(_Fib+4>>16)&0xffff
  66		addui r5,r5,(_Fib+4&0xffff)
  67		lw r6,0(r6)
  68		slli r6,r6,#2
  69		add r5,r5,r6
  70		lw r4,0(r4)
  71		lw r5,0(r5)
  72		add r4,r4,r5
  73		sw 0(r3),r4
  74	L4:
  75		lhi r3,(_I>>16)&0xffff
  76		addui r3,r3,(_I&0xffff)
  77		add r3,r0,r3
  78		lw r4,0(r3)
  79		lw r4,0(r3)
  80		add r4,r4,#1
  81		sw 0(r3),r4
  82		j L2
  83		nop
  84	L3:
  85	L1:
  86		;; Restore the saved registers
  87		lw r3,-32(r30)
  88		nop
  89		lw r4,-28(r30)
  90		nop
  91		lw r5,-24(r30)
  92		nop
  93		lw r6,-20(r30)
  94		nop
  95		lw r7,-16(r30)
  96		nop
  97		;; Restore return address
  98		lw r31,-8(r30)
  99		nop
 100		;; Restore stack pointer
 101		add r14,r0,r30
 102		;; Restore frame pointer
 103		lw r30,-4(r30)
 104		nop
 105		;; HALT
 106		jal _exit
 107		nop
 108	
 109	_exit:
 110		trap #0
 111		jr r31
 112		nop
 113	_open:
 114		trap #1
 115		jr r31
 116		nop
 117	_close:
 118		trap #2
 119		jr r31
 120		nop
 121	_read:
 122		trap #3
 123		jr r31
 124		nop
 125	_write:
 126		trap #4
 127		jr r31
 128		nop
 129	_printf:
 130		trap #5
 131		jr r31
 132		nop
 133	.global _I
 134	_I:	.space 8
 135	.global _Fib
 136	_Fib:	.space 40

          Program 4.4
\end{verbatim}

Note first that variable and function names from the C source code
are prefixed with an underscore in the assembly code:  main, I and Fib
have become \_main, \_I and \_Fib.

The first few lines of fib.s consist of ``.global'' directives to the DLX
assembler.  These will be discussed in Chapter 5, as will lines
11-28, which set up {\bf frames} to be explained at that time.

Let us look at what then follows:

{\it Lines 29-30:}  

We see the use of a new (to us) DLX instruction here, {\bf lhi} (``load
high immediate'').  What this means is that a 16-bit constant, specified
within the instruction via immediate addressing mode, will be loaded
into the high 16 bits of the destination register, in this case r3, with
0s put in the low 16 bits.  This instruction is a bit clumsy, but since
instructions like it exist in many RISC machines (MIPS, SPARC, and so
on), and are used extremely frequently in those machines, we will spend
extra time on it here.

Suppose we needed to get, say, the constant 0x403c7916 into the register
r17.  Here is how we could do that in the DLX architecture:

\begin{verbatim}
    lhi r17,0x403c
    addui r17,r17,0x7916
\end{verbatim}

The {\bf lhi} instruction would result in c(r17) being 0x403c0000.
The {\bf addui} would then add 0x7916, so that c(r17) would be
0x403c7916, as desired.\footnote{The difference between the {\bf
addi} and {\bf addui} instructions is that the former does a
sign-extend (see Chapter 3) while the latter simply extends with
0s in the high bits.}  (Make sure you understand how this works.)

The {\bf lhi} instruction format consists of a 6-bit op-code, a 5-bit
unused field, a 5-bit field to specify the destination register, and the
16-bit constant to be loaded.  The fact that the constant is limited to
16 bits forced us to use two instructions, {\bf lhi} and {\bf addui},
just to load one 32-bit constant.  It is reasonable to ask why DLX was
not designed with a single load instruction which would load 32-bit
constants specified in immediate addressing mode.  The answer is that
that would require the entire instruction---op code, register number and
the constant---to be longer than 32 bits, say 48 bits as shown above.
In order to build a smooth pipeline, it is important to have all
instructions the same length, say 32 bits.  So, {\bf lhi} can load only
16-bit constants, and in order to load a 32-bit constant we must break
it into two pieces and used {\bf lhi} and {\bf addui} together as we saw
above.

Now, which constant are we loading (in two pieces) in lines 29-30?  The
answer is that it is \_Fib.  Here is how that works:  The notation 

\begin{verbatim}
(_Fib>>16)&0xffff
\end{verbatim}

is just like that of the C language:  we take a copy of \_Fib and shift
it 16 bits to the right, so that the upper 16 bits of \_Fib are now in
the lower 16 bits of this copy.  Call that copy y for our purposes.
Then the next action is

\begin{verbatim}
y&0x0000ffff
\end{verbatim}

using the C-language's \& operator.  The result is a 32-bit quantity
whose upper 16 bits are all 0s and whose lower 16 bits are a copy of
\_Fib's upper 16 bits.  Similarly, in line 30, we see that the
constant we are adding,

\begin{verbatim}
_Fib&0xffff
\end{verbatim}

consists of 16 0s and then the lower 16 bits of \_Fib. 

By the way, keep in mind that computations like

\begin{verbatim}
(_Fib>>16)&0xffff
\end{verbatim}

are done by either at compile time (if we are producing a.out) or
at assembly time (if we produce fib.s first, and later do the
assembly); they are not done during run time, i.e. when the
program is executing.

The value of \_Fib is determined during the formation of the symbol
table, just as we saw before.  Recaling that {\bf dlxsim} loads programs
starting at location 0x100, then \_main will mean that location.  There
are 100 instructions in fib.s (comment lines, and lines consisting only
of labels such as line 40 are not instructions, of course), so \_I will
be at location

\begin{verbatim}
0x100 + 4*100 = 0x290
\end{verbatim}

(recall that each instruction occupies one word, and that consecutive
words in DLX differ in address by 4).  Since we see from line 134 that
the compiler has left one empty word between \_I and \_Fib, the address
of the latter will then be 

\begin{verbatim}
0x290 + 8 = 0x298
\end{verbatim}

So, the assembler discovers that the instruction

\begin{verbatim}
lhi r3,(_Fib>>16)&0xffff
\end{verbatim}

is actually

\begin{verbatim}
lhi r3,0x0000
\end{verbatim}

and the instruction 

\begin{verbatim}
addui r3,r3,(_Fib&0xffff)
\end{verbatim}

is actually

\begin{verbatim}
addui r3,r3,0x00000294
\end{verbatim}

So, when the instructions in lines 29-30 are executed later, the result
will be that r3 contains \_Fib, i.e. 0x00000294.\footnote{Once again,
keep in mind that the symbol table exists only at compile or
assembly time, not at execution time.  When we execute the program,
symbols like \_Fib will be gone.  The {\bf addui} instruction will be
0x24630294 (note the 0294).}

{\it Lines 31-32:}  In the same way, these will put \_Fib in r4.

{\it Lines 33-34:}  Here the compiler is implementing

\begin{verbatim}
Fib[1] = 1;
\end{verbatim}

from our C source code file fib.c.  Look closely at line 34.\footnote{You 
should review the material in Chapter 3 on the {\bf sw} instruction 
before continuing.}  The instruction copy r7 (which has 1 in it, from 
line 33) to the memory location 4 bytes past the one pointed to by r4.  
Well, r4 is pointing to \_Fib, thus to Fib[0], so the location 4 bytes 
past the one pointed to by r4 is Fib[1].  In other words, we are
putting 1 into Fib[1], just as desired.

{\it Lines 35-36:}  Here the compiler is implementing

\begin{verbatim}
Fib[0] = 1;
\end{verbatim}

{\it Lines 37-39:}  Lines 37-38 point r3 to \_I.  Thus line 39 is
copying r0 (which always contains 0) to the memory location 0 bytes
past I, i.e. to I itself.  In other words, lines 37-39 are implementing
the 

\begin{verbatim}
I = 0; 
\end{verbatim}

portion of

\begin{verbatim}
   for (I = 0; I < NFIB-2; I++)     
\end{verbatim}

{\it Lines 40-48:}  These are implementing the 

\begin{verbatim}
I < NFIB-2;
\end{verbatim}

portion of \begin{verbatim}
   for (I = 0; I < NFIB-2; I++)
\end{verbatim}

Here is how it works:

Lines 41-42 (line 40 is just a label) point r3 to I, as we saw in
similar code above.  Line 43 says to go to the memory location 0
bytes past I and copy that value to r3---in other words, copy the
value of I to r3.  We will make use of that soon.

Now, NFIB was defined to be 10, so 

\begin{verbatim}
I < NFIB-2;
\end{verbatim}

is the same as

\begin{verbatim}
I <= 7; 
\end{verbatim}

Thus the decimal constant 7 (\# denotes decimal, i.e. not hex) in line
44 is not surprising.  Line 46 then uses it:\footnote{Review the DLX
instructions {\bf slt} and {\bf beqz} in Chapter 3 before continuing.
The instructions {\bf sgt} and {\bf bnez} are quite similar.} We set r1
to either 1 or 0, depending on whether r3 is greater than r4---that is,
depending on whether I is greater than 7.  In line 47, the {\bf bnez}
(``branch if not zero'') instruction then tests the result, branching to
L3 if c(r1) is not zero.; in other words, if I is greater than 7, we
leave the {\bf for} loop.

{\it Lines 49-52:}  Here we are preparing to compute the new Fibonacci
number Fib[I+2].  In order to understand the code, first think about
where Fib[I+2] stored:  Since consecutive elements of Fib are one word
apart, thus 4 addresses apart, Fib[I+2] will be 4\*(I+2) bytes past Fib,
that is at 

\begin{verbatim}
_Fib + 8 + 4*I
\end{verbatim}

Accordingly, the compiler has pointed r4 to I (lines 49-50) and r3 to
\_Fib+8 (lines 51-52).  Line 53 then actually loads I to r4.

{\it Lines 54-55:}  These lines are aimed at putting 

\begin{verbatim}
_Fib + 8 + 4*I
\end{verbatim}

into r3.  

The key is that line 54 will replace the old contents of r4, I, by 4\*I.  
This is DLX's {\bf slli} (``shift left logical immediate'') instruction.
We will discuss it further in Chapter 6, but the point here is that it
basically works the same as the C language's $<<$ operator.  Since the
immediate constant specified in the instruction is 2, that means that
r4 will be shifted two bits to the left, and the result put back in r4.
Since each shift to the left is equivalent to multiplication by 2, we
are actually multiplying by 4 here, just what we want.

So, r3 will now be pointing to Fib[I+2].

{\it Lines 56-69:}  In the same manner, these point r4 and r5 to
Fib[I] and Fib[I+1], respectively.

{\it Lines 70-71:}  These load Fib[I] and Fib[I+1] into r4 and r5.

{\it Lines 72-73:}  Finally, we compute Fib[I]+Fib[I+1] and store
it back to Fib[I+2].

{\it Lines 75-81:}  These implement the I++ part of

\begin{verbatim}
   for (I = 0; I < NFIB-2; I++) 
\end{verbatim}

Note the redundant code in lines 77 and 79.

{\it Lines 82-83:}  This is the jump back to the top of the loop at L2.
The {\bf j} (``jump'') instruction is an {\bf unconditional branch};
we jump for sure, no conditions.

{\it Lines 84ff:}  This is the ``cleanup'' of frames done at the end
of the program.  Again, we postpone the discussion of this until Chapter
5, though we will discuss line 109 later in this chapter.

\subsection{Compiler Optimization}

You probably noticed that the DLX example in the last subsection
contained quite a few {\bf nop} instructions.  Clearly, we would
like to remove these if possible, and in fact most compilers on
RISC machines which have delayed branch are equipped to do so.
The compilers have various optimization options which can be 
invoked on the command line.

For example, on DLX we can compile the above program fib.c
by typing

\begin{verbatim}
dlxcc -O fib.c
\end{verbatim}

and the compiler will not only produce correct machine code, but
will also attempt to fill delay slots with non-{\bf nop} instructions.

Even on machines which do not have delayed branch, compilers generally
cannot produce machine code which is as efficient as one can by writing
machine/assembly language directly.  For instance, look at lines 31-32
above.  Since lines 29-30 had already put \_Fib into r3, we could
replace lines 31-32 by the more efficient

\begin{verbatim}
add r4,r3,r0  ;; copy r3 to r4
\end{verbatim}

This would do the same work in one instruction that was being done in
two.  So you can see that compilers often produce inefficient code.
Thus almost any compiler on almost any machine offers optimizer options.

Here is the optimized fib.s file produced when we ask {\bf dlxcc}
to both compile and optimize:

\begin{verbatim}
   1	.global _exit
   2	.global _open
   3	.global _close
   4	.global _read
   5	.global _write
   6	.global _printf
   7	
   8		.align 4
   9	.global _main
  10	_main:
  11		;; Initialize Stack Pointer
  12		add r14,r0,r0
  13		lhi r14, ((memSize-4)>>16)&0xffff
  14		addui r14, r14, ((memSize-4)&0xffff)
  15		;; Save the old frame pointer 
  16		sw -4(r14),r30
  17		;; Save the return address 
  18		sw -8(r14),r31
  19		;; Establish new frame pointer 
  20		add r30,r0,r14
  21		;; Adjust Stack Pointer 
  22		add r14,r14,#-48
  23		;; Save Registers 
  24		sw 0(r14),r3
  25		sw 4(r14),r4
  26		sw 8(r14),r5
  27		sw 12(r14),r6
  28		sw 16(r14),r7
  29		sw 20(r14),r8
  30		sw 24(r14),r9
  31		sw 28(r14),r10
  32		sw 32(r14),r11
  33		lhi r3,(_Fib>>16)&0xffff
  34		addui r3,r3,(_Fib&0xffff)
  35		addi r11,r0,#1
  36		sw 4(r3),r11
  37		addi r11,r0,#1
  38		sw 0(r3),r11
  39		lhi r3,(_I>>16)&0xffff
  40		addui r3,r3,(_I&0xffff)
  41		sw 0(r3),r0
  42		lhi r3,(_I>>16)&0xffff
  43		addui r3,r3,(_I&0xffff)
  44		lw r4,0(r3)
  45		addi r3,r0,#7
  46		sgt	r1,r4,r3
  47		bnez r1,L6
  48		nop
  49		lhi r7,(_I>>16)&0xffff
  50		addui r7,r7,(_I&0xffff)
  51		lhi r9,(_Fib+8>>16)&0xffff
  52		addui r9,r9,(_Fib+8&0xffff)
  53		lhi r8,(_Fib>>16)&0xffff
  54		addui r8,r8,(_Fib&0xffff)
  55		add r10,r8,#4
  56	L5:
  57		lw r4,0(r7)
  58		slli r3,r4,#2
  59		add r6,r9,r3
  60		add r5,r8,r3
  61		add r3,r10,r3
  62		lw r5,0(r5)
  63		lw r3,0(r3)
  64		add r5,r5,r3
  65		sw 0(r6),r5
  66		add r4,r4,#1
  67		sw 0(r7),r4
  68		lw r4,0(r7)
  69		addi r3,r0,#7
  70		sle	r1,r4,r3
  71		bnez r1,L5
  72		nop
  73	L6:
  74		;; Restore the saved registers
  75		lw r3,-48(r30)
  76		nop
  77		lw r4,-44(r30)
  78		nop
  79		lw r5,-40(r30)
  80		nop
  81		lw r6,-36(r30)
  82		nop
  83		lw r7,-32(r30)
  84		nop
  85		lw r8,-28(r30)
  86		nop
  87		lw r9,-24(r30)
  88		nop
  89		lw r10,-20(r30)
  90		nop
  91		lw r11,-16(r30)
  92		nop
  93		;; Restore return address
  94		lw r31,-8(r30)
  95		nop
  96		;; Restore stack pointer
  97		add r14,r0,r30
  98		;; Restore frame pointer
  99		lw r30,-4(r30)
 100		nop
 101		;; HALT
 102		jal _exit
 103		nop
 104	
 105	_exit:
 106		trap #0
 107		jr r31
 108		nop
 109	_open:
 110		trap #1
 111		jr r31
 112		nop
 113	_close:
 114		trap #2
 115		jr r31
 116		nop
 117	_read:
 118		trap #3
 119		jr r31
 120		nop
 121	_write:
 122		trap #4
 123		jr r31
 124		nop
 125	_printf:
 126		trap #5
 127		jr r31
 128		nop
 129	.global _I
 130	_I:	.space 8
 131	.global _Fib
 132	_Fib:	.space 40
\end{verbatim}

Note that the compiler was not able to remove all the {\bf nop}
instructions; there is still one on line 40, for example.  However,
most of them have been successfully removed.

\section{Generating Code:  Linkers}

You have probably used the {\bf make} program which is available
on Unix and many other systems.  For example, suppose on a Unix
system we have machine-code files x.o and y.o, and a source-code
file z.c, all of which are supposed to be combined into one program.
The command

\begin{verbatim}
cc x.o y.o z.c
\end{verbatim}

will result first in compiling z.c to produce a machine-code file
z.o, and then combining the three .o files into an executable file
a.out.

The combining referred to in the last sentence is called {\bf linking}.
It is actually not done by {\bf cc} itself; instead, {\bf cc} invokes
another program, called a {\bf linker}, to do the combining job.  On
Unix systems, the name of the linker is {\bf ld},\footnote{Recall that
the DLX package, being only a simulator, does not actually produce .o
files.  The simulator includes its own internal assembler and linker.
Thus the discussion below, as well as that on loading and execution 
of programs, will be for general Unix systems.} in DOS/Windows it is 
called LINK, and so on.
  
Actually, {\bf cc} will invoke {\bf ld} even if the user sees only
one .o file.  If for instance we type

\begin{verbatim}
cc gy.c
\end{verbatim}

then {\bf cc} will create gy.o\footnote{Without the -c option, this
file will only be temporary, and {\bf cc} will remove it after a.out
has been successfully created.} and call {\bf ld} on that file.
However, {\bf ld} will also need other .o files; a command 
something like

\begin{verbatim}
ld /lib/crt0.o gy.o -lc
\end{verbatim}

will be issued from within {\bf cc}.\footnote{This can be done
for instance, using the system() function, say

system("ld /lib/crt0.o gy.o -lc");

}  Here {\bf ld} is linking gy.o with other .o files which contain
C-related code, the one being the crt0.o file, and the others
being in the file libc.a located in a directory such as 
/lib.\footnote{In a {\bf cc} command line, -l\underline{t} refers
to a file whose name is of the form lib\underline{t}.a in a
directory such as /lib, /usr/lib and so on.  For instance, -lX11
means the file libX11.a in one of those directories.  Files whose
names end with .a are {\bf archive files}, which are collections
of related .o files.}  The latter archive file contains functions
from the C library.

So, if we wished to first look at the assembly language produced from
gy.c by {\bf cc}, and then assemble and link ``by hand,'' we could
type

\begin{verbatim}
cc -S gy.c
(now use an editor to look at gy.s)
as gy.c -o gy.o
ld /lib/crt0.o gy.o -lc
\end{verbatim}

\section{Program Loading and Execution}

So far in this chapter, we have been considering the question of program
{\it generation}, meaning how actual the machine code which comprises
our executable program is produced.  We found that it can be produced
directly by hand, by an assembler from assembly code, or by a compiler
from high-level language source code.  We now consider the question of
how to load the machine code into memory when we wish to run a program,
and how the actual execution is initiated.

Keep in mind that assemblers and compilers do not do the loading.
\footnote{The DLX simulation package is something of an exception to
this, in that it combines the functions of an assembler and a loader.}
Instead, the loader is part of the operating system (OS).  On a Unix
system, for instance, suppose you have a file y.c and you type

\begin{verbatim}
cc y.c
\end{verbatim}

That will produce the executable program file a.out, which you 
then run by typing

\begin{verbatim}
% a.out
\end{verbatim}

where \% is the Unix prompt.  Here is what happens:\footnote{For
simplicity, we are assuming here that the system does not use
virtual memory.}

\begin{itemize}
\item [1.]  The OS' command-line reader printed the \% to
your screen, and reads in the characters `a', `.', `o', `u', `t'
and the end-of-line character.

\item [2.]  The OS searches its file table to determine where on
the disk the file a.out is.

\item [3.]  The OS consults its memory usage table to find a 
currently-unused area of memory large enough for a.out.

\item [4.]  The OS reads a.out from disk, and writes it to the
area of memory found in step 3.  Say for concreteness that the
region starts at location 0x5000.

\item [5.]  The OS executes a branch instruction, such as DLX's
{\bf j} instruction, to location 0x5000.

\item [6.]  At the next Step A, the CPU will see 0x5000 in its
PC register and fetch that instruction, and then decode and
execute it in Steps B and C---in other words, a.out is now
executing!\footnote{Keep in mind that the CPU does not ``know''
that a transfer of control has been made from one program (the
OS) to another (a.out).  At each Step A, it simply fetches the
instruction from whatever location the PC points to.} 

\item [7.]  Note carefully that a.out now has complete control
of the machine.  The OS is out of the picture except when a.out
makes calls to it, i.e. ``system calls,'' such as to read() and
write() for its input/output.\footnote{Another exception
occurs in the form of {\bf hardware interrupts}, which we will not
discuss in this chapter.}  

\item [8.]  Eventually a.out will complete execution.  It will
then make its last system call, to exit().  This returns control
to the OS, which then will print the familiar \% prompt to the
screen, waiting for your next command.
\end{itemize}

In our description of program loading and execution here, we should
mention that on systems which support {\bf dynamic linking}---which
means most Unix systems today, including Linux, as well as systems like
Windows 95---the description in step 4 above must be revised.  To
explain this suppose a C source file file gy.c were to contain a call to
a function lm() whose code is in lm.o.  At compile time, we could
statically link lm.o with gy.o to produce an executable file a.out which
contains the machine code from both .o files.  However, under dynamic
linking, a.out would not contain the code for lm(); instead, at each
point where lm() is called in a.out, there would be a {\bf stub} which
points to lm.o on disk.  During the initial loading of our program for
execution, the machine code in lm.o would not be loaded into memory.
But during the execution of our program, when we reach a point at
which lm() is called (which may or may not occur), {\it then} lm()
will be loaded and executed.

Again, an OS is itself a program.  Unix, for example, is written in the
C language, with a few small machine-dependent modules written in
assembly language.  It is of course very large, but still it is just a
program.  Thus in examining the role of the OS in the eight steps listed
about for the loading and execution of our user program a.out, a
question which arises is, ``How does the OS itself get loaded into
memory when we first turn on the machine, and how is the execution of
the OS initiated?''  Here is an outline of the answers to these
questions:

The circuitry in a CPU chip is designed so that upon powerup, the PC
will automatically be initialized to a specific value, say
0xfffffff0.\footnote{This is the address used by the Intel chips.  The
Motorola 68K family uses address 0.} The designers of a machine which
uses this CPU chip will then include a small program, known as a {\bf
boot loader}, in ROM at 0xfffffff0, so that when we turn on the machine,
the boot loader starts running.  The boot loader then reads a part of a
program from a fixed region on the disk, and loads into memory, let's
say at location 0x80000; that program is the OS.  Once the boot loader
has read in that part of the OS, it does a jump to 0x80000, so the
OS is running.  The OS then reads in the other OS parts from disk.


\section{Analytical Exercises}

{\bf 1.}  Fill in the blank:  The word in the title of this chapter 
which best describes the function of an assembler is
\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_.

{\bf 2.}  Consider the following DLX assembly code.  Assume that
when its corresponding machine code is run, it will have been loaded
at 0x100.

\begin{verbatim}
        addi r6,r0,0
        addi r10,r0,0
x:      add  r7,r6,r6
        sw y(r10),r7
        sw 600(r10),r7
        addi r6,r6,1
        addi r10,r10,4
        slti r2,r6,4
        bnez r2,x
        nop
y:
\end{verbatim}

\begin{itemize}

\item [(a)] Fill in the blanks:  Thinking of y as an array, one of
the effects of this code is to put
\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_ into ``y[i]'', for i =
0,1,...,\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_.

\item [(b)]  Show the complete symbol table for this code.

\item [(c)]  Could an instruction be moved into the delay slot?
If so, which one?  If not, why not?

\item [(d)]  Suppose we had forgotten about the delay slot, and not
even put in the {\bf nop} instruction.  Specify exactly what disaster,
if any, would occur when we ran the program.  Assume no virtual memory.
\end{itemize}

{\bf 3.}  In Programs 4.1, 4.3 and 4.4, which lines are directives?

{\bf 4.}  Suppose DLX were implemented as a real machine, so that the
assembler produced .o machine-code files.  In general, which would be the 
bigger file, x.s or x.o?  Explain why, using the instruction

\begin{verbatim}
   add r2,r5,r6
\end{verbatim}

and state the number of bytes corresponding to this instruction in 
both files.   

{\bf 5.}  Consider the DLX assembly-language code

\begin{verbatim}
   beqz r12,z
\end{verbatim}

Suppose this instruction is in location 0x804, and z is at location
0x768.  Give the resulting machine code for the instruction, in hex.

{\bf 6.}  On Unix systems, when we run {\bf cc} to produce an
executable file, we are also indirectly running another Unix command 
besides {\bf cc}.  Which one is it?

{\bf 7.}  Suppose there is a crash of the hard disk on a computer,
destroying all programs that were stored there.  (Assume that there
are no other disk drives.)  There would be one program left stored
in the machine.  Which program would that be?

\section{Programming Projects}

{\bf 1.}  Suppose in Program 4.3 each Fibonacci number $f_i$ stored
in one byte of the array fib instead of one word.  What changes would
need to be made to the program?

{\bf 2.}  Write and test a Mac-1 assembly-language program which will
look at three numbers in memory, at locations 500, 501 and 502, and
place the smallest of the three into location 503.

{\bf 3.}  Do Problem 2 on DLX.

{\bf 4.}  Write DLX assembly language code which will take 
a signed integer stored in r1 in 2's complement form, and convert 
it---if possible---to signed magnitude form (again in r1).  Your
procedure will also return an error code in r2 (1 for an error,
meaning that r1 contained an unconvertible negative number, or 0
for nonerror).

{\bf 5.}  Write DLX assembly language code which will determine
whether DLX is big- or little-endian.  Have the code place 1 or
0 in r1, accordingly.
 


\end{document} 

