The INVOKEVIRTUAL instruction invokes a method. Its operand is a two-byte unsigned offset. The address of the method is stored in the constant pool at address CPP+offset.
The microinstructions are listed in the order in which they are executed; not the order in which they are stored in the control store. Since microinstructions can do several things at once, there is no simple progression of tasks; the tasks are somewhat intermingled. Some comments have been included in an effort to make it easier to follow what is happening in the code.
0x0b6 PC=PC+1; fetch; goto 0x42 ... 0x042 H=MBRU<<8; goto 0x43 0x043 H=H OR MBRU; goto 0x44 // H = offset 0x044 MAR=H+CPP; rd; goto 0x45 // MAR = CPP+offset (address of method address) 0x045 OPC=PC+1; goto 0x46 0x046 PC=MDR; fetch; goto 0x47 // PC = method address 0x047 PC=PC+1; fetch; goto 0x48 0x048 H=MBRU<<8; goto 0x49 0x049 H=H OR MBRU; goto 0x4a // H = # of parameters 0x04a PC=PC+1; fetch; goto 0x4b 0x04b TOS=SP-H; goto 0x4c 0x04c TOS=MAR=TOS+1; goto 0x4d 0x04d PC=PC+1; fetch; goto 0x4e 0x04e H=MBRU<<8; goto 0x4f 0x04f H=H OR MBRU; goto 0x50 // H = # of local variables 0x050 MDR=H+SP+1; wr; goto 0x51 // Write return address pointer to stack frame 0x051 SP=MAR=MDR; goto 0x52 0x052 MDR=OPC; wr; goto 0x53 // Write the return address to the stack frame 0x053 SP=MAR=SP+1; goto 0x54 0x054 MDR=LV; wr; goto 0x55 // Write the old LV to the stack frame 0x055 PC=PC+1; fetch; goto 0x56 // Fetch first opcode in method 0x056 LV=TOS; goto 0x2 // Set LV to point to the base of the stack frame
//--------------------------------------------- // Demonstrate the INVOKEVIRTUAL and IRETURN // instructions. // // 1. Clear Memory // 2. Assemble this program. // 3. Reset the computer. // 4. Click the "Display Words" radio button // below the memory display. // 5. Click the "Run" button. // // The local variable 'sum' in main (at offset // zero from LV) will contain the value 10. //--------------------------------------------- .constant OBJREF 0 .end-constant .main .var sum .end-var ldc_w OBJREF bipush 6 bipush 4 invokevirtual add istore sum halt .end-main //--------------------------------------------- // Note: normally, you would not use a local // variable in the method below; it is not // needed. You would just execute 'ireturn' // immediately after the 'iadd' instruction. // The variable was included to help illustrate // how stack frames are created. //--------------------------------------------- .method add(a, b) .var sum .end-var iload a iload b iadd istore sum // Store sum iload sum // Place sum on stack as return value ireturn .end-method
Before invoking a method two things must be done. First, an object reference must be placed onto the stack. In a real Java machine, this would identify the object that is invoking the method. Since objects are not supported in the IJVM, the value of this parameter is irrelevant. However, it still serves a purpose. The address of the object reference parameter is the base address of the stack frame for the method. When the stack frame is created, the value of the object reference parameter is replaced by a pointer to the return address. When the method terminates, the address of the object reference parameter becomes the address of the top of the stack and the value returned by the method is stored there.
When writing assembly code, declare a constant named OBJREF and give it an arbitrary value. Use the LDC_W instruction to load this constant onto the top of the stack as the object reference.
The second thing you must do before invoking a method is to push all of the explicit function parameters (if any) onto the stack. Note: the number of parameters in the method header includes the object reference (an implicit parameter) as well as the explicit method parameters. Consequently, every method has a least one parameter (the object reference).
As you can see by looking at the microcode above, a fair amount of work must be done to invoke a method. Following the execution of the INVOKEVIRTUAL instruction, a stack frame has been created with the following properties:
In the graphic below are two "snapshots" of memory: one taken just before the execution of the INVOKEVIRTUAL instruction in the example program above and one taken immediately afterward. In the first snapshot, the stack contains the object reference (OBJREF) and the two parameters that were pushed onto the stack.
The second snapshot illustrates the stack frame following the invocation of the INVOKEVIRTUAL instruction. The red numbers correspond to the stack frame properties listed above.
Every IJVM method returns a value. Following the termination of a method (see IRETURN), the return value will be on the top of the stack. When implementing a "value-returning" function, the calling routine should do something with this value. When implementing a void function (a procedure), the calling routine should just pop this value off the stack.