Extensions

We had two main extensions to this lab: an assembler and an implementation of the division algorithm.

To make the task of programming our CPU easier, we wrote an assembler in Java. We created an assembly language that had one keyword for each of the possible opcodes. Each opcode takes a different number of arguments. For instance, to add the contents of register three to register four, and store it in register four, the command would be
ADD R4 R3
The order is fixed; the first argument in binary operators is always the register that gets stored to. However, unary instructions take only a single argument; there is no need to specify the output register. For example, to decrement register six, the command is: DEC R6

Two other features were added to the assembler: the ability to specify immediate numbers in decimal, binary, or hexadecimal, and labels. Numbers can be used in the five operations that take immediate values: LOAD, BRZA, BRA, BRZ, and BRN. C base specifiers were used, that is 12 (decimal) = 0b1100 (binary) = 0xC (hex); so
LOAD R4 0xFF
would load 255 into register 4.

Additionally, the branch instructions BRA (which was renamed to GOTO in the assembly language) and BRZA can take labels as their arguments. A label is defined as any alphanumeric sequence other than the opcodes, followed by a colon. Thus

ADD R4 R5
GOTO A
DEC R6 //not executed
A:
INC R6

would add register 4 to register 5, and then increment register 6, skipping over the decrement operation. “//” can be inserted anywhere in a line to comment out everything after it; the interpreter is case insensitive and white space insensitive, and a command is terminated by a newline.

The logic of the assembler is fairly simple. It is a two pass assembler (although it started out as a one pass assembler. This does not make a difference, unless you try to reference a label that is farther down the file from the GOTO). The first pass scans through the asm file and searches for labels. When it finds one, it adds the label and the address it references to a hashtable. It keeps track of the address by means of a count variable, which is not incremented when the program encounters a label, comment line, or blank line. The assembler then scans through the file a second time, and decodes each instruction into the output MIF file, dereferencing the labels from the hashtable. If it encounters a label, it simply skips over it as if it were a blank line.

The assembler is in three .class files, assemble, MyAssembler, and AssembleExcption. Assemble handles the I/O, MyAssembler is the actual logic of the assembler, and Assemble Exception is simply an internal helper class. The usage is “java assemble inputfile [output file]”. We chose to use Java for the assembler because of one of our (Evan’s) familiarity with the language, and because of its portability (we were working on both *nix and Windows machines). However, since we were simply processing one text file into another, perl would also have been a good choice.

When we got to the step in the lab in which we were to find the average of four numbers, we decided that we were not satisfied with just shifting the sum right two times to divide by four. This method, we reasoned, would cause us to lose the remainder and would not be easy to generalize to the sum of, for example, 3 or 5 numbers. Consequently, we decided to implement the full division algorithm.

First, we implemented the unsigned binary division algorithm. This was a fairly painless task, easily completed with the data registers and instructions that we already had available. We wanted to be able to find the average of negative numbers, too, though, so we continued. The two's complement algorithmproved to be far more difficult to implement. We added a new instruction to the set that would compare the signs of two registers, set theflags accordingly, and leave thevalues of both registers unchanged. This eliminated our need for temporary registers to store copies of values for this purpose. The operation did not change the value in either register. It sets the flags as follows:

Code for our unsigned division implementation

Code for our twos complement division implementation