2 - Stack up!
What to implement
Functions
get_program_counter
tos
step
finished
get_program_counter
tos
step
finished
Instructions
OP_BIPUSH
OP_DUP
OP_IADD
OP_IAND
OP_IOR
OP_ISUB
OP_NOP
OP_POP
OP_SWAP
OP_ERR
OP_HALT
OP_OUT
OP_IN
In order to pass test2, correctly implement all of the functions and instructions listed above.
OP_BIPUSH
OP_DUP
OP_IADD
OP_IAND
OP_IOR
OP_ISUB
OP_NOP
OP_POP
OP_SWAP
OP_ERR
OP_HALT
OP_OUT
OP_IN
In order to pass test2, correctly implement all of the functions and instructions listed above.
Introduction
In this chapter, you will implement the basic stack operations of the IJVM, such as popping two words from the stack, adding them and then pushing the result (OP_IADD
).
All IJVM stack operations operate on words, so the implemented stack should also operate on words.
The program counter
The program counter is an index in the text data that points to the current instruction. It should be initialised to 0
at the start of your program and incremented accordingly with each executed instruction.
The suggested approach
Start with implementing the stack, as all the functions and instructions depend on it. The simplest and fastest way to implement the stack is to simply allocate an array for the data on the stack and keep track of its maximum capacity and current size. As a reminder: a stack is a LIFO (last in, first out) data structure with three main principal operations:
push
: add an element to the top of the stackpop
: remove the element on the top of the stack and return ittop
: return the element on the top of the stack
Once you've implemented the stack, shift your focus to the step
function. The step
function executes the current instruction (i.e., the instruction pointed to by the program counter). Consequently, your step
function must be able to handle all the instructions required in order to pass test2
.
Each consecutive chapter will introduce a new set of instructions for you to implement, so your emulator will eventually be able to handle the entire IJVM instruction set. It is a good idea to use a switch statement to determine how to handle the current instruction.
The instructions for task 2 are:
OpCode | Instruction | Args | Description |
---|---|---|---|
0x10 | BIPUSH | byte | Push a (signed) byte onto the stack |
0x59 | DUP | N/A | Duplicate top of stack: Push a copy of the top word onto the stack |
0x60 | IADD | N/A | Pop two words from the stack and push their sum |
0x7E | IAND | N/A | Pop two words from the stack and push their bitwise AND |
0xB0 | IOR | N/A | Pop two words from the stack and push their bitwise OR |
0x64 | ISUB | N/A | Pop two words from the stack and push their difference Note: subtract the top word from the second to top word |
0x00 | NOP | N/A | Do nothing |
0x57 | POP | N/A | Pop a word from the stack |
0x5F | SWAP | N/A | Swap the two top words on the stack |
0xFE | ERR | N/A | Print an error message and halt the emulator |
0xFF | HALT | N/A | Halt the emulator |
0xFC | IN | N/A | Read a character (byte) from the machine input and push it onto the stack. If no character is available, push 0 instead. |
0xFD | OUT | N/A | Pop a word from the stack and print it to the machine output as a character |
Hints
Please refer to the ijvm.h header file for a description of each function in machine.h
Since you are going to use a stack, it makes sense to implement a stack data type (i.e., a struct).
Remember to add some basic error handling to your code (i.e., assert that the stack is not empty when pop
is called). Adding these kinds of checks will help you a lot down the line.
It is a good idea to enlarge your stack if it's full when push
is called. You can use realloc
for example.
To implement the IN
instruction you have to read a byte from the in
file description, which you can do with fgetc(in)
. If no character is avaible, fgetc
will return EOF
. Similarly, to write a byte value
to out
, use fprintf(out, "%c", value)
.
Please refer to the ijvm.h header file for a description of each function in machine.h
Since you are going to use a stack, it makes sense to implement a stack data type (i.e., a struct).
Remember to add some basic error handling to your code (i.e., assert that the stack is not empty when pop
is called). Adding these kinds of checks will help you a lot down the line.
It is a good idea to enlarge your stack if it's full when push
is called. You can use realloc
for example.
To implement the IN
instruction you have to read a byte from the in
file description, which you can do with fgetc(in)
. If no character is avaible, fgetc
will return EOF
. Similarly, to write a byte value
to out
, use fprintf(out, "%c", value)
.