This assignment continues the construction of the Java Virtual Machine (JVM) you began writing in Assignment 3. Hopefully you designed the code in an extensible fashion, and it is debugged.
You have two alternatives: extend your JVM code to implement the features in this assignment, or extend Zack's sample program. If you are to use the sample solution, you'll need to answer the following questions:
You'll need to add the following new bytecodes:
Name | Bytecode | Instruction Format | Operations | Description |
IALOAD | 0x2e | IALOAD | POP v1, PUSH *(v1) | Load from index at top of stack |
IASTORE | 0x4f | IASTORE | POP v1, POP v2, *(v1) = v2 | Store at index at top of stack |
IDIV | 0x6b | IDIV | POP v1, POP v2, PUSH (v2/v1) | Integer division (no rounding) |
END | 0x00 | END | End program | End your program |
Plus the original set, whose operations are described in more detail here:
Name | Bytecode | Instruction Format | Operations | Description |
IADD | 0x60 | IADD | POP v1, POP v2, PUSH (v2+v1) | Integer add |
ISUB | 0x64 | ISUB | POP v1, POP v2, PUSH (v2-v1) | Integer sub |
IMUL | 0x68 | IMUL | POP v1, POP v2, PUSH (v2*v1) | Integer mul |
ILOAD | 0x15 | ILOAD index | PUSH *(index) | Load from mem at index |
ISTORE | 0x36 | ISTORE index | POP v1; *index = v1 | Store to mem at index |
BIPUSH | 0x10 | BIPUSH immed | PUSH immed | Byte Immediate push |
SIPUSH | 0x17 | SIPUSH immedhi immedlo | PUSH immedhi_immedlo | Short Immediate push |
POP | 0x57 | POP | POP v1 | POP value, discard |
IFEQ | 0x99 | IFEQ offsethi offsetlo | POP v1; cond. br. to PC+ offsethi_offsetlo | Branch if v1==0 |
IFNE | 0x9A | IFNE offsethi offsetlo | POP v1; cond. br. to PC+ offsethi_offsetlo | Branch if v1!=0 |
IFLT | 0x9B | IFLT offsethi offsetlo | POP v1; cond. br. to PC+ offsethi_offsetlo | Branch if v1<0 |
IFLE | 0x9E | IFLE offsethi offsetlo | POP v1; cond. br. to PC+ offsethi_offsetlo | Branch if v1<=0 |
IFGT | 0x9D | IFGT offsethi offsetlo | POP v1; cond. br. to PC+ offsethi_offsetlo | Branch if v1>0 |
IFGE | 0x9C | IFGE offsethi offsetlo | POP v1; cond. br. to PC+ offsethi_offsetlo | Branch if v1>=0 |
GOTO | 0xA7 | GOTO offsethi offsetlo | Jump to PC+ offsethi_offsetlo | Unconditional branch |
IRETURN | 0xAC | IRETURN | POP v1 | Integer return |
Remember that the JVM is a stack machine. This means it has a stack of operands in some portion of memory; and all instructions will deal with operands taken from that stack. In addition, there is an area of local memory which the JVM can use. Basically, the stack works similarly to MIPS registers, and the local memory is just like standard RAM.
For the purposes of this class, assume that all storage is in 32-bit words; your stack will need to hold up to 256 instructions, and memory will need to be able to store up to 256 words. You'll need to make space in your JVM data segment for these areas, as well as the area for code (assume we won't give you a program more than 1024 bytes, and you won't have to write one longer than that, either). Note that you'll need to keep track of the JVM's current instruction (i.e. its internal program counter), as well as the JVM's internal stack pointer.
Hopefully the description of what each instruction does is clear from the table above. All concatenations of values, e.g. offsethi_offsetlo mean that you have to combine two 8-bit results into a single 16-bit result ((offsethi << 8) | offsetlo). The arithmetic instructions deal with signed 32-bit numbers from the stack. Note that the only values that can be put on the stack are 8-bit or 16-bit values. Both BIPUSH and SIPUSH need to sign-extend the values they push to the full 32 bits.
The branch and goto instructions look at the top value of the stack, and if it meets the conditions, they will do a branch. Again, the offset needs to be a sign-extended value, because you need to add it to the PC to get the new result.
The IASTORE and IALOAD are both indirect instructions, which go to a location in memory in order to get the value for the stack. This is how you can create variables; simply reserve space somewhere in memory to hold them, and you can pass values to and from the stack.
The END instruction is not really part of the real JVM, but we will use it to make your job easier, in terms of handling program termination.
You need to write the Execute and Store stages of the JVM. Most of the instructions should only take a few real MIPS instructions to implement. You may start with your code from Assignment 3, or from Zack's sample code.
Make sure your entire JVM is inside a function called jvm. The function accepts the address of the bytecode program in $a0, the address of the top of the stack in $a1, and the address of location 0 for local storage in $a2. The JVM function should also copy the value at the top of the stack to register $v0 and return it when exiting.
You should write a small test program using Java bytecodes for each instruction, to verify that your JVM works properly.
The final step is to write a Java bytecode program to sum the contents of an array, returning the result on the stack, and to verify that it runs on your JVM program. The array should be in the JVM's local memory area, with the values:
1 2 3 4 5 6 7 8 9 99 100 101 500 999
Note that we will be testing your program with other values in the array, but these should be sufficient for testing.
Your program should include your name; it is always a good idea to put a comment in your code to tell the reader who wrote the code.
Turn in a hardcopy (printout) of the program, as well as an electronic submission using the turnin program.