This tutorial is intended to follow the tutorial on assembly language input/output.
The INVOKEVIRTUAL instruction invokes a method. This instruction creates the method's stack frame (pointed to by the LV register) and sets the PC register to the address of the first instruction in the method. The stack frame consists of
The IRETURN instruction terminates a method and returns control to the calling method. This instruction restores the previous LV value, loads the return address into the PC register, removes the stack frame, and copies the method's return value to the top of the stack.
Methods (other than the main method) are optional. If present in a program, they must follow the main method. The general format for a method is given here:
General Format |
.method methodId (
paramList ) [variable declarations] executable code .end-method |
Every method returns a value. The value on the top of the stack prior to the execution of the IRETURN instruction is copied to the new top of stack when the method terminates. 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.
Every method must have at least one parameter: an object reference. This parameter is not included in the parameter list; it is implied. In a real Java Virtual Machine, it would identify the particular object that is invoking a class method. Since IJVM does not support objects, the value of this parameter is never actually used and it can be given any value. Nevertheless, the parameter is still required.
The object reference should be the first (and possibly the only) parameter pushed onto the stack prior to executing the INVOKEVIRTUAL instruction. Following the execution of INVOKEVIRTUAL, the LV register points at this parameter (which is at the base of the corresponding stack frame). The parameter value itself is replaced by a pointer to the return address.
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. (Technically, any instruction that places a value on top of the stack would suffice.)
A method's explicit parameters appear as a comma-delimited list of identifiers enclosed by parentheses. If the function has no parameters, the list is empty but the parentheses are still required. Since parameters are local to a method, you may use the same identifier in different methods.
Explicit parameters should be pushed onto the stack after the object reference parameter and, of course, before invoking the method. The values should be pushed onto the stack in the same order as the corresponding identifiers appear in the parameter list. That is, the first value pushed onto the stack becomes the value of the first parameter in the list. All parameters, including the object reference, become part of the stack frame and are automatically removed when the method terminates.
All explicit parameters are passed by value; just as integer parameters are in Java. There is no mechanism for passing a parameter by address (as a reference parameter).
The example program asks the user for an integer in the range 0 to 5, finds the sum of the integers up to and including this value, and displays the sum in hexadecimal. It includes four methods:
1. Clear
the memory of the computer (Memory menu; Clear Memory).
2. If
necessary, clear the source code editor (Source menu, Clear
Source Code).
3. Enter
the following assembly language program using the source code editor.
.constant OBJREF 100 NEWLINE 10 .end-constant .main .var n sum .end-var ldc_w OBJREF invokevirtual DisplayPrompt pop ldc_w OBJREF invokevirtual GetInput istore n ldc_w OBJREF iload n invokevirtual FindSum istore sum ldc_w OBJREF iload sum invokevirtual DisplaySum pop halt .end-main //------------------------------------------- // DisplayPrompt displays a prompt asking for // a number between 0 and 5. //------------------------------------------- .method DisplayPrompt() bipush 'N' // Display "N(0-5)? " out bipush '(' out bipush '0' out bipush '-' out bipush '5' out bipush ')' out bipush '?' out bipush ' ' out ireturn .end-method //------------------------------------------- // GetInput returns the single digit number // entered by the user. No error checking is // performed. //------------------------------------------- .method GetInput() .var ch .end-var // Loop until a character is entered top: in dup ifeq clear goto end clear: pop goto top end: istore ch // Echo print character followed by new line iload ch out ldc_w NEWLINE out // Convert ASCII character to decimal digit // digit = ch - '0' iload ch bipush '0' isub ireturn .end-method //------------------------------------------- // FindSum returns the sum 0 + 1 + ... + n. // // Parameter: n //------------------------------------------- .method FindSum(n) iload n // if (n == 0) dup ifeq end // return 0 // else iinc n -1 // return n + FindSum(n-1) ldc_w OBJREF iload n invokevirtual FindSum iadd goto end end: ireturn .end-method //------------------------------------------- // DisplaySum displays the sum as a // hexadecimal digit. // // Parameter: sum // // Precondition: 0 <= sum <= 15 //------------------------------------------- .method DisplaySum(Sum) // Display "Sum: 0x" ldc_w NEWLINE out bipush 'S' out bipush 'u' out bipush 'm' out bipush ':' out bipush ' ' out bipush '0' out bipush 'x' out // If (sum <= 9) // output digit (sum + '0') // else // output letter ((sum - 10) + 'A') bipush 9 iload sum isub iflt letter // digit: iload sum bipush '0' iadd out goto end letter: iload sum bipush 10 isub bipush 'A' iadd out end: ireturn .end-method
Be careful as you work through these instructions. It is easy to make mistakes and get out of sync.
4.
Assemble the program (tap the F2 function key). If you do not want to
save this program, just click the Cancel button in the file save dialog;
the machine language code will still be loaded into memory.
5. Check
the Single-Step through IJVM Code checkbox below the input/output window.
6. Click
the Reset button.
7.
Click the Display Words button below the memory display.
8.
Click the IJVM Step button twice to allow the machine language interpreter to perform its start-up routine.
9.
Click the IJVM Step button. This places the object reference
parameter (OBJREF) on the top of the stack:
10.
Click the IJVM Step button to invoke the DisplayPrompt method. The
object reference parameter has been replaced by a pointer to the return address
(4098). This is the first value in the stack frame and is pointed to by the LV
register. The stack frame also includes the return address (6) and the previous
LV value (8192):
11.
Click the IJVM Step button sixteen (16) times executing the
statements in the DisplayPrompt method up to but not including the IRETURN
instruction. The prompt is displayed in the input/output window. The stack is in the same state that it was immediately after invoking this
method. The value at the top of the stack is the previous LV value (8192).
12.
Click the IJVM Step button to execute the IRETURN instruction.
Notice that the stack frame has been removed and the value that had been at the
top of the stack has been copied to the new top of stack location:
13.
Logically, the DisplayPrompt method is a void function. Even so, as we have just
seen, the method returned the value that is currently on the top of the stack. Click the IJVM Step
button to pop this unused value from the stack:
14.
Click the IJVM Step button two (2) times to invoke the GetInput
method. The stack frame for this method begins at address 4097 and contains a
pointer to the return address, the local variable (ch), the return address, and
the previous value of the LV register (at the top of the stack). Notice that the
initial value of the local variable (at address 4098) is simply the value that
was left there from previous stack manipulations (it was the return address when
the first method was invoked):
15.
Before continuing program execution, you should input a single digit as follows.
Click anywhere in the input/output window to give it the focus. Enter the digit
'2'. Click the IJVM Step button. The IN instruction reads the
character '2' from the input buffer and puts its code value on the top of the
stack:
16.
Click the IJVM Step button four (4) times executing the
instructions up to and including "79: ISTORE 1".
The method has confirmed that you typed in a keystroke and has stored its code
value in the local variable "ch" at address 4098:
17.
Click the IJVM Step button four (4) times to echo print your
keystroke and send a new line character to the input/output window.
18.
The method should return the number 2 rather than the character '2'. To convert
a numeral to its corresponding decimal value, subtract the code value for the
numeral '0'. In this case '2' - '0' is really 50 - 48 or the number 2. Click the IJVM Step
button three (3) times to perform this arithmetic and leave the result on the
top of the stack as the return value of the method:
19.
Click the IJVM Step button to terminate the GetInput method. The
stack frame has been removed and the return value of the method is on the top of
the stack:
20.
Click the IJVM Step button once. The value returned by the
GetInput method is removed from the stack (SP points to 4096) and stored in the main method variable "n"
(pointed to by the LV register).
21.
Click the IJVM Step button three (3) times to load the object
reference onto the stack, load the function parameter (n) onto the stack, and invoke
the recursive function that will find the sum of the integers from 0 to n. The stack frame (pointed to by LV) contains a pointer to
the return address (where the object reference parameter used to be), the
function parameter (2, in this case), the return address (23), and the previous
value in the LV register (8192):
22.
The recursive function, FindSum, begins by testing for the exit condition (n=0).
Click the IJVM Step button to load the parameter onto the stack.
23.
Click the IJVM Step button to duplicate the parameter value on the
top of the stack.
24.
Click the IJVM Step button to test for the exit condition. The
branch fails and the next instruction, "104:
IINC 1 255", is the first instruction in the recursive invocation of
the function. Note, in particular, that the parameter value is still at the top
of the stack ready to be returned (if it had been a 0) or to be added to the
value returned by the recursively invoked method:
25.
Click the IJVM Step button to decrement the value of n (at address
4098):
26.
Click the IJVM Step button three (3) times to recursively invoke
the FindSum method. The stack frame for this second invocation is on the stack
above the previous value of n (from the first invocation) that is just waiting
to be added to the value returned by this second invocation. Notice that the
previous value of LV (at the top of the stack frame) is the address of the base
of the stack frame for the first invocation:
27.
Since the parameter passed to the second invocation was not zero, the FindSum
method will be invoked a third time. Click the IJVM Step button
seven (7) times. The stack frame from the third invocation is on the stack above
the previous value of n (from the second invocation) which will be added to the
value returned by this invocation:
28.
This time, the parameter passed to FindSum is 0 which is the exit condition. Click the IJVM Step button
three (3) times to test for the exit condition. Notice that the return value is
on top of the stack just prior to the termination of the method:
29.
Click the IJVM Step button to terminate the third invocation of
the FindSum method. The return value of 0 is on the top of the stack, ready to
be added to 1 (the parameter passed to the second invocation) by the IADD
instruction at 115:
30.
Click the IJVM Step button to execute the IADD instruction. The
value to be returned by the second invocation of FindSum (0+1) is on the top of
the stack:
31.
Click the IJVM Step button two (2) times to terminate the second
invocation of the FindSum method. The first click executes an unconditional
branch to the IRETURN instruction and the second click executes the IRETURN
instruction. The value (1) returned by the method is on the top of the stack
ready to be added to the parameter passed to the first invocation (2) by the
IADD instruction at 115.
32.
Click the IJVM Step button to execute the IADD instruction. The
value to be returned by the first invocation of FindSum (0+1+2) is on the top of
the stack:
33.
Click the IJVM Step button two (2) times to terminate the first
invocation of the FindSum method. The first click executes an unconditional
branch to the IRETURN instruction and the second click executes the IRETURN
instruction. The value (3) returned by the method is on the top of the stack:
34.
Click the IJVM Step button to pop this value from the stack and
store it in main method variable "sum":
35.
Click the IJVM Step button three (3) times. These instructions
invoke the DisplaySum method with the value of "sum" as the method parameter.
The stack frame for this method is shown here:
36.
Click the IJVM Step button sixteen (16) times to display "Sum:
0x" in the input/output window.
37.
All that remains to do is to display the sum as a hexadecimal digit. If the sum
is 9 or less, we can display a decimal digit by adding the code for '0' to the
sum. Otherwise, we subtract 10 from the sum and add the code for 'A'. Click the IJVM Step button
ten (10) times to display the numeral "3" and terminate the method. Even though
DisplaySum implements a void function, the method itself returns a value;
namely, the previous value of the LV register which had been at the top of the
stack frame:
38.
Click the IJVM Step button to execute the POP instruction at
address 33. This clears the stack; SP is back to 4096 where it started.
39.
Click the IJVM Step button to execute the HALT instruction at
address 34.
40. You
might try running the program with inputs of 0, 1, 2, 3, 4, and 5 to confirm
that you get sums of 0x0, 0x1, 0x3, 0x6, 0xA, and 0xF respectively.
This is the last assembly language tutorial.