Up to now, you haven't needed the code for the operating system. We've just been building and testing a machine that could support an OS. This is the point at which we make the transition.
...\cebollita\apps
should already contain a file named
image
That's an OS disk image file, , including the OS and some test applications.
However, if you want to re-build it,
type "make image" in directory ...\cebollita\apps
.
That will also produce a file named image.map
that tells you the
disk block number each is loaded at (which you need to know to use the shell).
If you want to change the set of applications in the image you could try
editing the Makefile
: look for the line (near the top)
IMAGE_USERAPPS = tests/helloworld.exe ...
and add or
delete what you want.
Try running the image. In Cebollita, while in directory apps
:
./cebrun.sh imageIn SMOK, you need to attach the image file to the BlockController device (right-click/ Properties).
Because we're getting perilously close to what a real machine and system is at this point, you do not simply use the SMOK memory component to magically read the OS disk image into itself. Instead, you use SMOK's boot capability, which requires you to attach a virtual disk (i.e., the OS image file you generated) to the Block Controller device inside the memory map container. Having done that, you can use the power on toolbar button to boot your machine. (See the documentation on SMOK's Block Controller for more information.)
You also do not Run your machine, you Power On (boot) it. There is a (different) toobar button for that. When you boot your machine, the SMOK Block Controller will load the boot loader (whose source is bootloader.s) into memory at address 0. When that is done, your machine starts executing (so it should have its PC initialized to 0). The boot loader loads the rest of the OS into memory and then transfers control to it.
Once the OS has initialized itself it starts the first process, a shell. The shell is just a regular old user application. The OS transfers control to it, and it starts running.
This is a very primitive system -- it has no file system at all. (It has a disk, but it doesn't have any notion of files, just raw disk space.) All the shell can do for you is start other programs.
The shell sits in a loop. At the top of the loop it prints a prompt asking you for a disk block number. To identify the program you want to run, you have to give the disk block number at which it is located. The block number corresponding to each program on your disk is printed out when you create the disk image, so those are the numbers you need to supply.
bootloader.s
is the boot loader. When the machine (or simulator) is powered on,
the hardware loads the first block (512 bytes) of the disk into memory at location 0. The bootloader is
those first bytes on the disk. It (a) moves itself to a higher memory address, and then (b) loads the remainder of the
OS from the disk into memory, starting at location 0 (which is why it has to move itself). The boot loader is not linked
with the OS -- it's a stand-alone program that sets things up to the get the OS going.
The OS is in files trap-handler.s
and os.c
. The former is a small set of routines that need
to be written in assembler. For example, there is a routine to save all the registers on entry into the OS, and another
one to restore them on return from the OS.
os.c
is the bulk of the OS. Once it gets going, it's entered at exceptionHandler()
. Entries into
the OS are not procedure calls -- the OS doesn't do a "return" to go back to the application. Instead,
the trap handler has saved the application's registers on entry. dispatch()
restores them, and sets the PC to
the EPC. Voila, you're back in the application.
The OS has a $gp and $sp, just like anyone else. To preserve them across dispatches of applications, it saves them in memory locations 0 and 4. When control comes back to the OS, the trap handler retrieves them from there, and then the OS can run as normal C code again.
On boot, the OS initializes itself and then launches the first program, a shell. This shell is very primitive. You type a disk
block number and it tries to launch a new process, assuming that the block you gave was the first block of an executable
stored on disk.
(The block numbers are printed in your Cygwin window when you, or the makefile, does a java sim.DiskMake
.)
The makefile loads the disk with three applications: the shell, a helloworld application, and a quicksort application.
(Yes, you can launch a shell process from the shell.) You can terminate a shell's execution by giving a negative number
for the disk block. If it is the original shell, well, the OS crashes. That's intentional.
What you type into the shell is not echo'ed back to you. Ever.