.text
la $1, table0 // load the address of a label table0 (pseudo instruction)
lw $2, $1 // $2 <= 0x000C0FFEE
lw $3, table0 // load the value at label table0 (pseudo instruction), $3 <= 0x000C0FFEE
lw $4, 3(table0) // $4 <= table1
lw $5, 1($4) // $5 <= 0x1DEADBEEF
sw $5, 1($1) // 0x001COFFEE is overwritten to 0x1DEADBEEF
li $6, 0xC0FFEE // load immediate (pseudo instruction), $6 <= 0xC0FFEE
.data
table0:
.word 0x000C0FFEE
.word 0x001C0FFEE
.word 0x002C0FFEE
.word table1
table1:
.word 0x0DEADBEEF
.word 0x1DEADBEEF
.word 0x2DEADBEEF
.word 0x3DEADBEEF
.word 0x0DEADBEEF, 0x1DEADBEEF, 0x2DEADBEEF
.fill 10 0x0
Indicates the start of the text section, consisting of instructions. .text can appear many times in an assembly file, but they must be merged into a single section in the output of the assembler.
Indicates the start of the data section. Similar to .text, there can be multiple .data in an assembly file.
Specifies the word data at a memory location. It can be followed by multiple one or more words to describe data for multiple words.
Duplicates a data many times. For example, .fill 10 0x0 duplicates 0x0 ten times.
Represents an instruction or data memory address, similar to labels in other languages, can be used in instructions, especially pseudo or macro instructions to specify a specific address (e.g. la $0, table0). When you use labels, you can minimize the amount of manual modifications in your assembly code upon insertions or deletions of instructions and/or data. For example, suppose that .word 0x003COFFEE is insterted after .word 0x002C0FFEE in the previous sample code; the only needed change is the fourth instruction from lw $4, 3(table0) to lw $4, 4(table). In other words, labels make the code maintenance cost more manageable by keeping the effect of a code modification be local to a label.
The assembly code is very hard to be understood. To prevent the programmers from forgetting why they write these code, the assembler framework also allows the programmers to append some comments. All comment must be started with "//". In other words, all the contents after "//" will not be processed by the assembler framework.
The assembler framework can recognize instructions with the form of operator + operands. The assembler framework also supports the following operand formats as appeared in the previous example code.
The operand is composed of a register and offset.
The operand is composed of label and offset.
The operand is a label.
The operand is a register.
The operand is an immediate value.
In addition, the assembler framework does not limit the number of operands that an instruction can have.
The simplest way to build your own assembler on top of the assembler framewrok is to create a class that extends the Assembler class and realize the abstract methods in the class. These methods include processLabel, generateCode, updateProgramCounter, initialization, replaceInstructionLabel, and replaceMemoryLabel.
The AssembleCode method in the Assembler class can help you generate the machine code. Therefore, you may write a main method in your assembler class as:
public static void main(String[] arg) throws IOException
{
asm assembler = new asm(arg);
assembler.AssembleCode(arg);
}
The rest of this document describes all the classes in the assembler
framwork in detail.
In the following paragraphs, we will provide a detail description of classes in our assembler framework. We will first start by the Assembler class, which is the core of assembler framework. The assembler framework also has an Instruction class, Operand class, a Memory class, and a MemoryEntry class to help the Assembler class complete the assemler functions. In the following paragraphs, we will describe these classes in detail.
| Field Summary | |
|
|
sourceFile The file reader of the input source file. You need to intialize it before processing the source code. |
|
|
out_code
The file writer for machine code output (*_i.coe). You need to initialize it before generating any code output. |
|
|
out_data
The file writer for data code output (*_d.coe). You need to initialize it before generating any data output. |
|
|
keywords
The array stores the keywords used by the Assembler. You may override it in your class to extend the assembly language with more keywords. |
|
|
instructions An array of Instruction objects that stores the parsed instructions. |
|
|
memory The object stores the parsed data words. |
|
|
instructionCount The number of scanned instructions. |
|
|
currentCodeSection The current code section. By default, 0 stands for text section, and 1 for data section. |
|
|
programCounter The current program counter. You may need it while processing labels. |
|
|
dataMemoryAddress The current data memory address. You may need it while processing labels. |
|
|
currentSourceCodeline How many source code lines has been scanned. You may not need it since the framework only uses for generating error messages. |
The Assembler class has two different constructors:
| Constructor Summary |
| Assembler() Construct an Assembler object without any initialization. |
| Assembler(String[] args)
Construct an Assembler object with the file I/O handlers initialized. |
The Assembler class has following methods:
| Method Summary | |
|
|
getNextInputLine() Get the next non-comment or non-empty line from the source code file, and return the scanned line with a String. All contents after "//" will be ignored and will not be contained in the returned String. Once the source code reaches an EOF, the method will return a "null". |
|
|
isKeyword(String sourceCodeLine) Examine if the input line from source code contains a keyword. |
|
|
extractKeyword(String sourceCodeLine) Extract the keyword from the input string. |
|
|
isLabel(String sourceCode) Examine if the input line from source code contains a label. |
|
|
extractLabel(String sourceCodeLine) Extract the label from the input string. |
|
|
processInstruction(String sourceCode) Parse the input source code into an Instruction object. |
|
|
processData(String sourceCode) Parse the source code from the data section into the memory object of Assembler. |
|
|
getOperandType(String operand) Examine if the input operand string is a meaningful term. The returned String will be "register" if the operand starts with "$". The returned String will be "immediate" if the operand starts with "0x". The returned String will be "label" if the operand cannot be categorize into the last two categories. |
|
|
outputErrorMessage(String errorMessage) Output the error message and show the incorrect source code line. |
|
|
AssembleCode(String arg[]) The most important method in Assembler class. This function controls the flow of Assembler. You may not need to change it. However, you may also override it if you have a better algorithm than our current two-parse algorithm. |
|
|
processAdditionalKeywords(String sourceCode) You may override to process additional keywords. |
|
|
processLabel(String sourceCode) You have to implement it to process the labels. You may also need to implement a symbol table to maintain the labels and their real addresses. |
|
|
replaceInstructionLabel(Instruction instruction) Replace the operands in the form of labels with immediate values. The method will be called before generating the machine code for an instruction in the AssembleCode method. |
|
|
generateCode(Instruction instruction) You have to implement it for generating the machine code from an Instruction object. |
|
|
updateProgramCounter(Instruction instruction) You have to implement it for updating the programCounter. If your algorithm does not require the variable, you may implement it as an empty method. |
|
|
initialization() The method is called before the source code is scanned or generated. You have to implement it to initialize some variables, such as file descriptors or local variables, in your class. |
|
|
replaceMemoryLabel() The method is called before the data memory is dumped to a $prefix_d.coe file. It will replace the data written in labels with immediate values. |
For each instruction in the text section, the assembler framework will parse them into an array of Instruction objects. The Instruction class has the following fields:
| Field Summary | |
|
|
operator A string stores the operator of the instruction. Take the code sw $5, 1($1) as an example, the operator will be the string "sw". |
|
|
operands
An array of Operand objects stores the arguments of the instruction. |
The Instruction class has two different constructors:
| Constructor Summary |
| Instruction(String sourceCodeLine) Construct an Instruction object using the scanned source code. |
| Instruction(String operator, Operand operands[])
Construct an Instruction object using the assigned operator and operands. |
The Instruction class has following methods:
| Method Summary | |
|
|
print() Print the operartor and operands of the current Instruction object. |
The Operand class has the following fields:
| Field Summary | |
|
|
name A string stores the name of a operand. |
|
|
offset
A offset of this operand. |
The Operand class has two different constructors:
| Constructor Summary |
| Operand() Construct an Operand object with their name initialized as "", and offset initialized as 0 |
| Operand(String i_name, int i_offset)
Construct an Operand object with their name initialized as i_name, and offset initialized as i_offset |
The Operand class has the following method:
| Method Summary | |
|
|
getOperandType(String operand) Examine if the input operand string is a meaningful term. The returned String will be "register" if the operand starts with "$". The returned String will be "immediate" if the operand starts with "0x". The returned String will be "label" if the operand cannot be categorize into the last two categories. |
|
|
extractRegisterNumber() If the operand is a register, this method will return an integer value of the register number. Otherwise, the method will return -1. |
|
|
extractImmediate() If the operand is an immediate value, this method will return an integer value of the immediate value. Otherwise, the method will return Integer.MIN_VALUE. |
The Memory class has the following fields:
| Field Summary | |
|
|
size Number of elements in the Memory class object. |
|
|
entries
An array stores the data in the memory. |
The Memory class has two different constructors:
| Constructor Summary |
| Memory() Construct an Memory object with the number of entries initialized to 1024. |
| Memory(int n)
Construct an Memory object with the number of entries initialized to n. |
The Memory class has the following method:
| Method Summary | |
|
|
add(String data,int address) Add the data into the Memory object and bookkeeping its data memory address. |
|
|
find(int address) Find the data stored in input address. |
|
|
print() Print the content of current Memory object. |
|
|
dump() Dump the content of current Memory object as the form of 34-bit machine code. |
|
|
leng() Return the size of current entries. |
The MemoryEntry class has the following fields:
| Field Summary | |
|
|
data A string stores the data word in an memory entry. |
|
|
address
The data memory address of the object. |