Read these instructions carefully. There is a considerable amount of
information for you to read before the part about what you're supposed
to do. For this assignment you will optimize code that simulates a simple
CPU with memory and output.
Our simulated CPU fetches instructions from the memory (which may be
thought of as part of the CPU) and executes them. A special instruction,
HALT, tells the CPU to stop executing altogether.
Our simulated CPU stores its instructions in an array of C structs.
It can hold up to 2000 instructions. It represents memory as another array
of 10000 ints called memory[]. Our CPU has special
integer variable in it called the Program Counter. This is the index into
the instruction array where the currently executing instruction is found.
Our CPU simulator uses the following algorithm to execute programs:
gcc -O3 -o sim sim.c simulate.cThis will leave an executable called sim in your directory.
struct instruction { int type, /* type of instruction */ addr1, /* first address */ addr2, /* second address */ data; /* data for the instruction */ }; struct instruction program[2000];So the program[] array holds the CPU's program. The first field, type, indicates the type of instruction to be executed. For instance, if type is 7, then the instruction is a "multiply" instruction. The second and third fields, addr1 and addr2, are "addresses." These are usually indices into the memory array, but in some special cases they will be indices into the instruction array. The last field, data, is used to hold miscellaneous information a particular instruction might need. Here is a table of all the instructions in our simple CPU. Each instruction is given by
type Value | Mnemonic | Description |
---|---|---|
0 | NOP | No Operation; just does nothing |
1 | STORE_CONST | Store constant: memory[addr1] = data |
2 | MOVE | Move memory: memory[addr1] = memory[addr2] |
3 | MOVEID | Move memory indirect to direct: memory[memory[addr1]] = memory[addr2] |
4 | MOVEDI | Move memory direct to indirect: memory[addr1] = memory[memory[addr2]] |
5 | ADD_CONST | Add constant: memory[addr1] += data |
6 | ADD | Add: memory[addr1] += memory[addr2] |
7 | MUL | Multiply: memory[addr1] *= memory[addr2] |
8 | DIV | Divide: memory[addr1] /= memory[addr2] |
9 | MOD | Modulus (i.e., remainder): memory[addr1] %= memory[addr2] |
10 | OUTPUT | Convert memory[addr1] to char and print to standard output. putchar(memory[addr1]) will do this. |
11 | COMPARE | Compare memory[addr1] to memory[addr2];
if memory[addr1] < memory[addr2] then memory[addr1] = -1; if memory[addr1] > memory[addr2] then memory[addr1] = 1; if memory[addr1] is equal to memory[addr2] then memory[addr1] = 0. Note that this destroys one of the items being compared, and that's okay. |
12 | JUMP | "Jump" to another instruction; Program Counter = addr1 |
13 | COND_JUMP | Conditional Jump: if memory[addr1] is equal to data, then Program Counter = addr2; otherwise, continue as usual. |
14 | STOREPC | Store Program Counter to memory: memory[addr1] = Program Counter |
15 | LOADPC | Load Program Counter from memory: Program Counter = memory[addr1] |
16 | HALT | Halt; stop execution of instructions. |
testing instructions... jump seems to work; nop seems to work; move and cond_jump seem to work; add_const seems to work; mul seems to work; assuming add, div, and mod work, too; storepc seems to work; loadpc seems to work; done.Otherwise it will fail at one of those instructions and halt with a message.
wget http://www.cs.utsa.edu/~dj/cs2733/hw5/hello.bin wget http://www.cs.utsa.edu/~dj/cs2733/hw5/test.bin wget http://www.cs.utsa.edu/~dj/cs2733/hw5/primes.bin wget http://www.cs.utsa.edu/~dj/cs2733/hw5/bubble.bin
switch (type) { case 0: a; case 1: b; case 2: c; ... }do the following:
if (type == 0) a; else if (type == 1) b; else if (type == 2) c; ...Compile the new function with the following command:
gcc -O3 -o sim-1 sim.c simulate-1.cTime the new program versus the old program on the primes.bin program, redirecting output to /dev/null so that the time to do the output doesn't obscure the timing process:
time ./sim primes.bin > /dev/null time ./sim-1 primes.bin > /dev/nullWhich execution took longer? Was the second one faster? By how much?
Make a new program file called simulate-2.c that will contain another new version of the simulate() function. Temporarily modify the simulator program to count, for each instruction type, how often that type of instruction is executed, and print the instruction counts at the end of simulation. Run the modified simulator on the primes.bin program to collect the frequencies of the instruction types. Then reorder the if statements in the simulate() function to check the type fields in decreasing order of frequency, so that the most common instruction is checked first, etc.
Compile the new program with the following command:
gcc -O3 -o sim-2 sim.c simulate-2.cAgain, time the new program versus the old program on the primes.bin program:
time ./sim primes.bin > /dev/null time ./sim-2 primes.bin > /dev/nullWhich execution took longer? Was the second one faster? By how much? Also run your optimized simulator on the bubble.bin program to measure the speedup.
Note that you are not required to change the assembly language, but that your choice of C statements has a large impact on the generated assembly language. Also, this assignment sort-of shows you how a microprocessor works, so you get a little extra computer architecture learning at no extra charge.