I can haz unsined overflow?
Inspiration for studying Sparc.

CS 2208

In case you have questions, here's some relevant contact information:

Email address mburrel@uwo.ca
Office hoursThursdays 17:30–19:30
Office locationWestern Science Centre 127

Help on assignment 5

I had a request to try and summarize some of the stuff I went over for the structure of assignment 5. I think the stuff that needed most explanation was the readCodes function, so here it goes.

%i0Address of the filename of the file you're going to read from
%i1Address of the bytecode array you're going to fill up
fopen%o0The first argument to fopen is the filename, i.e., %i0
fopen%o1The second argument to fopen is the literal string "r" (or "rb" if you prefer). You will have to set up a .text segment to define this string literal.
fopenreturn valueIf it returns 0, there was an error reading the file. If it's non-zero, then it's a FILE * which you need to hang on to for the next two function calls!
fread%o0The first argument to fread is the address of the array to read into, i.e., %i1
fread%o1The second argument to fread is the size of each element to read in. Since you're reading in bytes, this will be 1.
fread%o2The third argument to fread is the maximum number of elements to read in. This is 100.
fread%o3The fourth argument to fread is the FILE * to read from. This is what was returned from fopen.
freadreturn valuefread returns a negative number of there was an error. If it was a non-negative number, then that indicates how many bytes were in the file that you just read from. You need to hang on to this because it's going to be the return value of readCodes!
fclose%o0fclose takes only one argument, which is the FILE * that was returned to you from your call to fopen

CRC animation

Here's the CRC animation thing in case you find it useful.

Makefiles: making...files

Don't you hate having to compile your stuff every time you change something? Well okay, maybe it's not so bad if you only have one file, but even then...ehh. Makefiles are glorious things which do all the compiling for you. But wait, there's more! They actually make compiling more efficient than doing it by hand.

gmake (the Unix utility that uses Makefiles) is dependency-driven. When it wants to compile something, it only builds the dependencies for that file if necessary. Example: let's say we had a program (not unlike your assignment 4) that was built out of C and assembler files. These C and assembler files are the dependencies of the program you're trying to compile. gmake won't recompile them unless they've changed (it looks at the timestamps to figure out the last time you modified them to accomplish this).

So, a concrete example. In my hypothetical program I have an awesome.m, which looks like so:

define(multiply,
        `mov $1, %o0
        call .mul
        mov $2, %o1')

    .global square
square:
    save %sp, -96, %sp
    multiply(%i0, %i0)
    mov %o0, %i0
    ret
    restore

And let's say you have a C file, try-square.c, which looks like so:

#include <stdio.h>

signed square(signed);

int
main(void)
{
    printf("square(%d) = %d\n", 50, square(50));
    return 0;
}

What we would like to do is turn both of these into a program we can run. To do this we need a Makefile! So, use your favourite text editor and create a file (in the same directory as your source files) called Makefile. Note: your Makefile must be called Makefile. With the capital M and everything.

My first Makefile is going to look like this:

try-square:    try-square.o awesome.o
        gcc -o $@ $^

The first line says "if you want to make a file called try-square, first you need its dependencies, which are try-square.o and awesome.o. Once you have those dependencies, then you can execute the gcc command listed there". The special variables $@ and $^ get substituted by the name of the target (try-square) and dependencies (try-square.o awesome.o) respectively.

Nota bene: when indenting in a Makefile, you must use tabs, never spaces. If you use spaces, your Makefile will likely not work. If you get mysterious errors like "missing separator", check to make sure you don't have spaces where they're not supposed to be. To clarify, after a colon (between try-square: and try-square.o) and at the beginning of an indented line (before gcc -o ...) you must have hard tabs. To help you out, I'm going to make the whitespace pink wherever you should put a tab.

So, we've successfully written our rule. We've described what file to make, what its dependencies are and what to do once we've got those dependencies. We save our Makefile and try it out using the gmake command:

obelix[68]% gmake
cc    -c -o try-square.o try-square.c
gmake: *** No rule to make target `awesome.o', needed by `try-square'.  Stop.

Fiddle-sticks! The good news is gmake knew what it was supposed to do: it was supposed to get the try-square.o and awesome.o dependencies. It just needs some help doing that. (Note that is already has some built-in knowledge of how to turn .c files into .o files, but doesn't know anything about .m files).

We move our attention to what are called automatic rules (sometimes called implicit rules). The rule we made for try-square was a very concrete rule. It was a rule for one target and a very specific, concrete set of dependencies. Automatic rules allow us to generate target files for certain classes of files.

We'll write 3 automatic rules. They, respectively, turn .m files into .S files, .S files into .o files and .c files into .o files.

.SUFFIXES:     .m .S .c .o

.m.S:
        m4 <$^ >$@
.S.o:
        gcc -c $^
.c.o:
        gcc -Wall -c $^

# and the rule we had before
try-square:    try-square.o awesome.o
         gcc -o $@ $^

The first line (the .SUFFIXES line) tells gmake the list of all the file extensions/suffixes we'll be working with. It needs that to figure out dependencies with automatic/implicit rules. Anyway! If we save our new Makefile and try it, we now get:

obelix[70]% gmake
m4 <awesome.m >awesome.S
gcc -c awesome.S
gcc -o try-square try-square.o awesome.o
rm awesome.S

Some things to note:

  1. It worked! Booya!
  2. We didn't have to tell it what order to do things in or anything like that. We didn't even have to tell out to first turn a .m into a .S and then a .S into a .o. All we did was give it a set of rules for turning dependencies into targets and it figured out the rest! Pretty cool
  3. It automatically deleted awesome.S for us once since that files is purely an intermediate file (not a dependency for any final rule). Kind of cool! If we want to get that file back, we just have to do gmake awesome.S and it'll figure out how to make it!

It gets better, though. Let's say we change one of our files. Let's say we change try-square.c (for instance, so that it computes the square of 100 instead of the square of 50). After we're done our change, we run gmake again and get:

obelix[72]% gmake
gcc -Wall -c try-square.c
gcc -o try-square try-square.o awesome.o

Notice anything different? gmake was clever enough to know that awesome.m didn't change and so there was no use reassembling it. It's not so important in a project this small, but in a project with thousands of files (and when you get into industry, you will be working with projects of thousands of files), recompiling only what's necessary is a huge win.

Here are the files I used for this part of the tutorial if you'd like to download them:

Multi-line macros

Here's the file I worked on in lab. Keep in mind that you don't need to know how to do any of this for the assignment; it would just make your life easier if you did know. Also keep in mind that if you get too crazy with this stuff we'll have to take off marks for bad readability.

If you're interested in learning more about m4, here's the GNU m4 manual. There are all sorts of crazy things in there which you'll never need.

Note that if you run into troubles with m4, the most common mistake (especially for simple macros) is not quoting enough...or quoting too much...or quoting in the wrong spot. It usually doesn't hurt to put something in `quotes' and keep in mind that quoting can be nested (that's why the start quote and end quote are different characters).

div_by_100.m

Here you go!. That's the code we wrote in tutorial today.

Alternatives to obelix

Obelix is a fantastic computer, but it's the only SPARC compute server on the GAUL network, which means sometimes it can get a bit overloaded. This is especially true towards the end of the term when all the undergrads are trying to get their projects done.

Thankfully 4 SPARC machines (Ultra 10s) in MC10 which you can use. They're not as nice as obelix, except when you're the only one using them!

If you still want to work remotely (e.g., from home), you can log in to these 4 machines via rlogin. Their names are:

Obelix's load average being almost 0
Obelix's load average is almost 0. May as well just stay on obelix and do your work there!

Here's how I'd recommend working on SPARC machines remotely on the gaul network:

  1. Ssh into obelix as usual.
  2. Run the command w (or alternatively uptime if you feel bad about stalking people). This will give you same basic information about the system: how long since the last reboot, how many people are logged in, what they're up to, etc.
  3. The important bit of information here is the load average at the end of the first line. The screenshot to the left is showing load averages of 0.00, 0.01 and 0.04. In Unix, the load of a system is a measure of how much time the CPU spends running programs. A program using up 100% CPU (e.g., an infinite loop) will induce a load of 1.00. In this case the load average is extremely low: the system is effectively idle. Thus, you should be able to use obelix without noticing any slowness.

    Note that w (or uptime) gives you three load averages. The first number is the system load averaged over the past minute; the second is averaged over the past 5 minutes; and the third is averaged over the past 15 minutes. The middle number is probably a good one to pay attention to for general "how heavily used is this computer?" kind of curiosity.

  4. If you're unlucky, the system will be heavily used and the load average will be fairly high. It's debatable what "fairly high" is. Note that obelix has 4 processors, so theoretically it should be able to sustain a system load of 4.00 without slowness. In practice, though, you'll probably notice some slowness before the load reaches 4.00. Once it gets to about 3, I'd start looking for another computer.
  5. Obelix's load average starting to climb
    Obelix's load average is starting to climb.
  6. To the right I have a screenshot of the load average starting to climb on obelix. In this particular case I've induced it myself by setting up 3 infinite loops ;), but in the real world this might be indicative that people are starting to overwhelm obelix! Time to think about switching to a different computer.
  7. As mentioned before, you have 4 other computers to choose from: aldor, yacc, boolean and class. In the screenshot I've chosen to rlogin into yacc because it has the shortest name and I'm lazy, but of course you can choose any of the 4.
  8. Probably the first thing I'd do after logging into the new system is check the load average. I'm also checking to see if someone else is logged in: if someone else is logged in, chances are they're actually sitting at the computer (not logged in remotely), so they might not appreciate me stealing their CPU time ;)
  9. Once I've found a computer, I type in renice $$. This step is optional, but it is a nice courtesy and I highly recommend it. This makes sure that everything you do while logged in will be "niced", i.e., given a lower priority than other programs. This way if someone does log onto that machine locally, they won't be too affected by what you're doing, since their programs will have a higher priority than yours.

Note none of this, of course, will do anything to help network slowness :(

Working with GAUL from Windows

Here's a quick reference for working with GAUL. I'm going to assume that you're working on your own computer, possibly from home, since most of you will be.

Working with GAUL, and specifically obelix, is a necessary evil of this course. Ideally you would be able to do all the work on your own computer. However, since practically no one has a SPARC processor in their home computer and this course teaches SPARC (because it's very beautiful), you need to be able to get your work done on a SPARC machine. Enter GAUL and obelix.

This tutorial is currently Windows-only. OS X and Linux to come.

Copying files from your computer to GAUL

If you're just getting started, probably the first thing you want to do is get some of the examples from the course website onto the obelix. For the purposes of this example, we'll use sum.s.

  1. Download sum.s from the course website onto your own computer.
  2. Download WinSCP onto your computer. Either the "installation package" or the "portable executable" will work (I'm using the "portable executable" in this tutorial).
  3. Run WinSCP. Fill in obelix.gaul.csd.uwo.ca as the hostname and fill in your GAUL username and password. Everything else can be left unchanged. Example screenshot:

    Screenshot of logging into obelix via WinSCP

    The first time you connect, it will warn about the key not being in your cache and will ask if you trust the key. Select "yes".

  4. You should be presented with a new window with two panes. In the left pane, find the file on your computer where you want to transfer. In the right pane, find the directory on obelix where you want to transfer to. Note: I highly recommend creating a new directory/folder (which can be done inside WinSCP) specific to 2208 and putting everything in there. Example screenshot:

    Screenshot of finding files to transfer

  5. Drag the file from the left pane to the right pane. A new window will pop up which doesn't have anything that you might care about. Just select the "Copy" button. You want to copy both sum.s and iofunc.o.

Assembling and running a file on obelix

Now that you've got a file transferred over, you can log onto obelix via SSH to do some real work! Once again, this is Windows-only for the time being.

  1. Download PuTTY. The first link there (for putty.exe) is the one you want.
  2. Run PuTTY. Fill in obelix.gaul.csd.uwo.ca for the hostname. Everything else can be left alone. Example screenshot:

    Screenshot of logging in via PuTTY

  3. A new window will open asking for your username and password. After successfully entering those in, you will be at what's called a "shell" which is prompting you for new commands. Example screenshot:

    Screenshot of a shell on obelix

    Note my fonts and colours might not match yours (I think I changed the defaults). Also, if you get something telling you about your terminal type being unknown and prompting you for a new terminal type, just type in vt100 and hit enter. If you really care, I can explain, but it's not very interesting.

  4. Change to your 2208 directory. If you called your directory 2208, type in cd 2208 and hit enter (cd stands for "change directory"). If you've forgotten what you called your 2208 directory, type in ls and hit enter (ls stands for "list contents")
  5. Once you're in the proper directory, do an ls to ensure that your sum.s is there.
  6. Type in gcc -o sum sum.s iofunc.o. If it worked correctly, it should appear as though nothing happened and you should just be returned back to the prompt (gcc stands for "GNU Compiler Collection". Even though we're not using it as a compiler, it makes things convenient for us when we're assembling or linking like we talked about in lab)
  7. Type in ./sum to run your program. It will prompt you for two numbers (hit enter after each one) and then print out the sum.
  8. When you are finished with obelix, type exit to log out.

Other courses

Here's a list of courses I've been an instructor for in the past:

Here's a list of courses I've been a TA for in the past: