CS 203 - Lab 13 - Fall 2000


Lafayette College > Department of Computer Science > CS 203 > Labs > Lab #13

Description
For this lab, you will implement a virtual machine that runs the IJVM code described in the text.

Instructions
Reread section 4.2 of the text! We will be writing a class (call it IJVM) that will emulate the machine described in that section.

Recall that the IJVM has three areas of memory: the method area, which holds IJVM instructions; the constant pool, which holds strings, pointers to functions, and other things that are not changed during the execution of a program; and a stack, which holds intermediate values during computations as well as local variables and parameters to functions. Your IJVM objects should contain arrays corresponding to each memory area. Keep in mind that while the constant pool and the stack hold 32-bit values, the method area holds 8-bit values, so you should use the int type for the first two, and the byte type for the last (char in C++).

When your IJVM objects are created, they should load their constant pools and method areas from a file. The name of the file will be given on the command line, so in Java it will be in args[0] and in C++ it will be in argv[1]. The input file will be split into two parts: first the method area and then the constant pool. Each part begins with an integer representing the number of entries in that part. The entries then follow. The constant pool part begins immediately after the method area part.

You can use the following Java code to read the method area and the constant pool:

    public IJVM(String fname) throws IOException {
        BufferedReader input = new BufferedReader(new FileReader(fname));
        readMethods(input);
        readConstantPool(input);

	// More code here...
    }
    
    private void readMethods(BufferedReader input) throws IOException {
        int size = Integer.parseInt(input.readLine());

        methodArea = new byte[size];

        for (int i = 0; i < size; i++)
            methodArea[i] = (byte)(Integer.parseInt(input.readLine()));
    }

    private void readConstantPool(BufferedReader input) throws IOException {
        int size = Integer.parseInt(input.readLine());

        constantPool = new int[size];

        for (int i = 0; i < size; i++)
            constantPool[i] = Integer.parseInt(input.readLine());
    }

The stack is initially empty, but you should allocate enough space for it to hold any local variables and intermediate results. 4096 entries should be more than enough.

In addition to the three memory objects, an IJVM object should contain PC, SP, and LV registers. These are the only registers from the microarchitecture that you should have in your IJVM objects. SP and LV should be initialized to whatever is appropriate for an empty stack (make sure you initialize them to the same value so that the first thing pushed on the stack would be local variable number 1). PC should be initialized to zero.

Once everything has been initialized, your program should go into a loop that executes IJVM instructions. The IJVM instructions we will use are listed on p. 222 of the text, execpt we omit WIDE and add the following.

Opcode Mnemonic Description
1 IPRINT pop the top value off the stack and print it to standard output
2 IREAD read an integer from standard input and push it on the stack
3 SPRINT index print the null-terminated string beginning at entry index in the constant pool to standard output
4 HALT stop execution of the virtual machine
Also, since Java does not support unsigned integer types, all the opcodes have been converted to signed values. For example, the opcode for GOTO is 0xA7, which is the 8-bit two's complement representation of -89. Java's lack of unsigned types also complicates decoding two-byte signed values stored in the method area. For example, -23 in 16-bit two's complement is 0xFFE9, which is stored as the 8-bit signed integers -1 and -23.

Notes on instructions:
For the four branch instructions, the offset if given relative to the instruction following the branch and is two bytes long. The high byte is given first, followed by the low byte. For example, if the instruction IF_ICMPEQ 10 is stored at location 22 in the method area, PC would be set to 35 if the top two values on the stack were equal.

The constant needed by the IINC instruction is a signed byte.

Each character printed by SPRINT occupies an entire 32-bit entry in the constant pool.

The index into the constant pool needed by INVOKEVIRTUAL and LDC_W is two bytes long and is unsigned. Again, the high byte is given before the low byte.

Read the section in the text on the INVOKEVIRTUAL and IRETURN instructions carefully. Key points include the following:

Example programs:
double.ijvm allows the user to enter an integer (there is no prompt) and outputs two times that integer. This program uses the IPRINT, IREAD, DUP, IADD, and HALT instructions.

age.ijvm allows the user to enter his age and outputs what milestones he has passed.

square.ijvm prompts the user to enter an integer and outputs the square of that integer. This program uses the same instructions as double.ijvm plus IFLT, GOTO, BIPUSH, SWAP, ISUB, ILOAD, SPRINT, and ISTORE.

sumints.ijvm prompts the user to enter an integer n and outputs the sum of the first n positive integers (or 0 if n < 0).The code corresponds to:
SPRINT 1 print string starting at location 1 in the constant pool ("Enter n")
IREAD
DUP
IFLT 8
read n; skip method invocation if less than zero
BIPUSH 0
SWAP
dummy for reference to current object
INVOKEVIRTUAL 0 invoke method whose address is stored in entry 0 of the constant pool
IPRINT print result
HALT stop simulation
BIPUSH 0
IPRINT
HALT
n < 0, so print 0 and stop simulation
BIPUSH 0
ISTORE 2
initialize running total to 0
BIPUSH 1
ISTORE 3
initialize loop counter to 1
ILOAD 1
BIPUSH 1
IADD
compute n + 1 (limit for loop)
ILOAD 3
IF_ICMPEQ 13
compare n + 1 to loop counter; break loop if equal
ILOAD 2
ILOAD 3
IADD
ISTORE 2
add loop counter to running total
IINC 3 1
GOTO -23
add one to loop counter and go back to top of loop
ILOAD 2
IRETURN
return value in running total

Notes on command line arguments:
The file to be executed will be specified on the command line. For Java, the command will be line java IJVM double.ijvm; for C++, the your program will be executed by IJVM double.ijvm. The filename will be passed as a parameter to main. In Java, the prototype of main is void main(String[] args) and the filename will be in args[0]. In C++ the prototype for main is int main(int argc, char **argv) and the filename will be in argv[1].

Assignment (due Monday, December 4th at 11:59pm)
Submit your code by attaching it to an e-mail. To send multiple files, attach them all individually to a single e-mail (no tar or pkzip archives, please).