Amoeba is the instruction set for the Amoeba virtual processor.
The Amoeba processor has the following components:
The environment is a linked collection of hash maps that associate names with variables.
An integer container.
An opcode specifying an operation and zero to three operands. Operands may be numbers or variables.
(Note: For now we use Instruction = String and use a scanner to extract opcodes and operands.)
An array of instructions to be executed.
A hash map that associates labels with program array indices.
A variable containing the index of the next instruction in the program array to be executed.
A variable holding the current instruction being executed or pre-processed.
The pre-processor reads a program from a specified file.
It loads each line of the file into the next available cell of the program array.
If an instruction is a label declaration, then it associates the label with the index of the instruction (+ 1) in the label table.
Repeatedly
1. load next instruction from the program array into ir and increments ip:
ir = program[ip++]
2. executes the instruction in ir.
3. halts if ir contains the halt instruction.
Comment "instructions are ignored. Here's an example:
comment: Dave was right!
Label declarations are executed by the pre-processor. Here's an example:
label: next
Variable declarations create entries in the environment. Here's an example:
def x 2
The load command loads a variable in the environment with a number or the content of another number. Here's the format:
load var src
Where
var = any variable
src = any variable or integer
Note: this changes the content of the variable, not the variable or the environment.
Most of the AL instructions have the format:
opcode var src1 src2
When executed, src1 and src2 are combined according to the opcode. The result is loaded into var.
Opcodes are:
add, mul, div, sub, and, or, equal, not
For example, here's how we would increment a variable named result:
add result result 1
The logic instructions interpret 0 to mean false and all other numbers to represent true.
The not opcode only takes to operands:
not result answer
This last instruction loads result with 1 if answer contains zero, otherwise it loads result with 0.
The goto command changes ip. For example:
goto label
When executed, this instruction looks up the index associated with label in the label table, then loads that index in ip:
ip = labelTable(label)
The if command is like the goto command, but the ir is only modified if the condition var is not zero. For example, executing the command:
if var label
is essentially:
if (env(var).content != 0) ip = labelTable(label)
When the fetch-execute loop sees the halt instruction, it terminates:
halt
The printmsg command can be thought of as a macro (a built-in function). It prints any message to the console window. For example:
printmsg enter a number:
The answer can be read into a variable with the read command:
read response
The content of a variable can be written to the console window:
print response
Using procedures, we can view an Amoeba program as a sequence of procedure declarations. The program begins with the main procedure. This ends with the halt command. After the main procedure we may find declarations of "helper" procedures. A helper procedure begins with a label naming the procedure and ends with a return command.
The call command is how a procedure calls a helper. It specifies the name of the procedure being called and any arguments being passed to the procedure. (These may be variables or numbers. Variables are passed by value.):
call proc arg0 arg1 ...
When a procedure is called, a temporary extension is added to the environment:
environment = new Environment(environment)
This is where all arguments are stored and given the default names of arg0, arg1, etc. Also, the ip is stored here just before it is set to labelTabel(proc).
The return command takes a variable or a number as an operand. This is the value being returned to the caller:
return result
The return command restores the ip and restores the environment to the caller's environment:
var result = environment.get(result)
ip = environment("ip")
environment = environment.extension
Every environment has a special, automatically created variable called "return". (I know, it's a hack.) The return command loads the result into the caller's return variable.
Output:
Enter program name: triangle
enter staircase height: height = 6
number of blocks needed: => 15
go again? response = 1
enter staircase height: height = 10
number of blocks needed: => 45
go again? response = 0
bye ...
Using the formula tri(n) = n * (n + 1)/2 to compute triangle numbers:
Output:
Enter program name: triangle2
enter staircase height: height = 6
number of blocks needed: => 21
go again? response = 1
enter staircase height: height = 10
number of blocks needed: => 55
go again? response = 0
bye ...
Complete the following implementation