Assignment 6
GVM 2.0: A Stack Machine
GALA 2.0: View Data Memory & Data Stack
The purpose of this programming assignment primarily is to improve the programming model supported by the GVM. Secondarily, you provide some debugging tools.
The programming model now supports a functional style, including recursively defined functions. For debugging, parts of the GVM's state are made viewable while stepping through the execution of a Gala program.
You get 40% extra credit for writing a Gala program that:
- reads an integer n;
- recursively computes the nth Fibonacci number;
- writes this value to the console.
Each enhancement to the GVM & its programming environment is specified below.
GVM read & write instructions
Read
A read
instruction prompts the user to enter an integer via a JOptionPane.showInputDialog method.
It's 1 operand is the index of the data memory location that is set to the entered integer.
Its GVM opcode is 13; its Gala mnemonic is read
.
For example,
read N
N
.
Write
A write
instruction prompts the GVM to write the contents of a data memory cell to the console.
It's 1 operand is the index of the data memory location to be written.
Its GVM opcode is 14; its Gala mnemonic is write
.
For example,
write N
N
.
View Relevant Part of the Data Memory
We define a relevant data cell as one for which the Gala assembler has a defined identifier.
A string is in this set if it either is a predefined identifier
{ ACC, X, Y, WIDTH, HEIGHT, RED, GREEN, & BLUE }
or is defined by a Gala define
statement.
After the completion of a run or a step, each relevant data memory cell is displayed on 1 line: the value of the memory cell followed by its string identifier. Fig. 1, illustrates, among other things, the relevant data memory cell display.
Functional programming
The meaning of the term functional programming, in this context, means declaring & invoking functions. This programming style is one way to promote the DRY principle & create modular, reusable software. The functional programming that you implement includes recursive programming, which imposes extra programming constraints.
Architectural support
Functional programming requires changes to the GVM architecture. For some background on call stacks, please see a Wikipedia article and a Citizendium article.
When function A calls function B, program control transfers to the instructions that define function B. When function B completes, it returns program control the the instruction in function A that immediately follows the call to function B. Since function B can call function C, & so on, the GVM maintains a stack of return addresses; when a function is called, the caller pushes its next instruction address onto the stack; when the called function completes, it pops a return address from the stack & transfers program control to the instruction at that address.
Two new instructions support calling a function & returning control to the caller:
call
& return
.
Call
A call
instruction prompts the GVM to:
- push the address of the instruction immediately following the call, the return address onto the return address stack;
- transfer program control to the 1st instruction in of the called function, a program address specified by the call instruction's 1 operand.
call
.
For example, the call
instruction in the 2 instruction sequence below
call sum
pop total
sum
.
Return
A return
instruction prompts the GVM to:
- pop the return address stack;
- transfer program control to that address, the return address put on the stack by the corresponding call instruction. The return instruction has no operand.
return
.
For example, the sum
function called by the call statement above
would have a
return
call sum
instruction).
Returning a function's value & passing parameters to a function
To support:
- passing parameters to a called function
- passing a function's return value back to the caller
Push
A push
instruction prompts the GVM to
push the contents of a data memory cell onto the stack.
Which cell is specified by its 1 operand.
Its GVM opcode is 18; its Gala mnemonic is push
.
For example,
push ACC
Pop
A pop
instruction prompts the GVM to
pop the data stack, storing that value into the data memory cell specified by its 1 operand.
Its GVM opcode is 17; its Gala mnemonic is pop
.
For example,
pop ACC
A function call protocol
To call a function, the caller must:
- If the caller is calling itself, push local variables onto the data stack (because, these memory cells may be overwritten by the recursive call)
- push parameters onto the data stack
- call the function
- pop the parameters off the data stack;
- compute the function
- push the return value onto the data stack
- return
- pop the returned value off the data stack
- If this is the return of a recursive call, it pops the local variable values off the data stack
- resumes its computation
Viewing the data stack
Like viewing relevant memory cells, provide a view of the data stack after each run request & each step request. This feature is of value when debugging: observing the affect of an instruction, or a sequence of instructions, on data memory & the data stack.
GUI controls
You may lay out the fololowing GUI components any way you like:
- The Load, Run, & Step buttons
- The Image view
- The console
- The program source listing
- The relevant data cells
- The data stack
The image of Fig. 1 has all these components. Again, lay them out as you wish.
Sample GALA test files
A test file of a Gala program to multiply 2 integers is given below. We will test your submission with this Gala program. Fig. 1 shows the output from this program, after multiplying -3 X 15. Again, producing a Gala program for recursively computing the nth Fibonacci number is a great test, & earns 40% extra credit.
#___________________________________________________ # Test multiply function #___________________________________________________ define x 20 # int x; define y 21 # int y; define xXy 22 # int xXy; read x read y push y push x call multiply pop xXy write xXy stop #___________________________________________________ # Computes mX * mY. # @param mX - the 1st operand. # @param mY - the 2nd operand, which must be >= 0.. # @returns mX * mY. #___________________________________________________ # local variables define mX 50 # int mX; define mY 51 # int mY; define product 52 # int product; multiply pop mX # X = mX; pop mY # y = mY; set 0 store product # product = 0; load mY # if ( mY != 0 ) goto AddX mIdone zero addX # exit loop: return product; push product return # loop body: product += mX; addX load product add mX store product # mY--; set -1 add mY store mY goto mIdone
Implementation notes
- If your assembler is designed well, these new instructions, will require minimal modification of your assembler.
- I found it useful to use JSplitPane to put 2 things in a pane with a boundary that the user can adjust. To see how this is used, look at the Java tutorial.
Discussion of possible enhancements
Please post any enhancements to the assembler that would you like to see on Discussion forum.
Rubric
Value | Aspect |
---|---|
4 | Correctly assembles & runs a test program that calls a function that requires parameters & has a return value. |
4 | Correctly assembles & runs my Fibonacci program. (This demonstrates that your work supports recursively defined functions.) |
2 | Javadoc & Style |
10 | Total |
4 | Extra credit: Correctly assembles & runs your recursive Fibonacci program. |
For style, we want:
- Javadoc is tastefully authored according to standard guidelines; the web pages are sitting in your GitHub repository, in their own directory. Ensure that your @author, @param, @returns, & @throws annotations are completed properly.
- Consistent application of an indentation policy;
- Carefully chosen, meaningful identifier names;
- Use of symbolic program constants in place of literals;
- Thoughtful comments, where needed, expressed in concise, precise English.
- Well chosen OOD & assignments of responsibility.
- Methods are short, readable, & clear, using private helper methods to achieve this.