You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
641 lines
14 KiB
641 lines
14 KiB
/*
|
|
* @(#)Interpreter.java 2.1 2003/10/07
|
|
*
|
|
* Copyright (C) 1999, 2003 D.A. Watt and D.F. Brown
|
|
* Dept. of Computing Science, University of Glasgow, Glasgow G12 8QQ Scotland
|
|
* and School of Computer and Math Sciences, The Robert Gordon University,
|
|
* St. Andrew Street, Aberdeen AB25 1HG, Scotland.
|
|
* All rights reserved.
|
|
*
|
|
* This software is provided free for educational use only. It may
|
|
* not be used for commercial purposes without the prior written permission
|
|
* of the authors.
|
|
*/
|
|
|
|
package triangle.abstractMachine;
|
|
|
|
import java.io.DataInputStream;
|
|
import java.io.FileInputStream;
|
|
import java.io.FileNotFoundException;
|
|
import java.io.IOException;
|
|
|
|
public class Interpreter {
|
|
|
|
static String objectName;
|
|
|
|
// DATA STORE
|
|
|
|
static int[] data = new int[1024];
|
|
|
|
// DATA STORE REGISTERS AND OTHER REGISTERS
|
|
|
|
final static int CB = 0, SB = 0, HB = 1024; // = upper bound of data array + 1
|
|
|
|
static int CT, CP, ST, HT, LB, status;
|
|
|
|
// status values
|
|
final static int running = 0, halted = 1, failedDataStoreFull = 2, failedInvalidCodeAddress = 3,
|
|
failedInvalidInstruction = 4, failedOverflow = 5, failedZeroDivide = 6, failedIOError = 7;
|
|
|
|
static long accumulator;
|
|
|
|
static int content(int r) {
|
|
var register = Register.values()[r];
|
|
return content(register);
|
|
}
|
|
|
|
static int content(Register r) {
|
|
// Returns the current content of register r,
|
|
// even if r is one of the pseudo-registers L1..L6.
|
|
|
|
switch (r) {
|
|
case CB:
|
|
return CB;
|
|
case CT:
|
|
return CT;
|
|
case PB:
|
|
return Machine.PB;
|
|
case PT:
|
|
return Machine.PT;
|
|
case SB:
|
|
return SB;
|
|
case ST:
|
|
return ST;
|
|
case HB:
|
|
return HB;
|
|
case HT:
|
|
return HT;
|
|
case LB:
|
|
return LB;
|
|
case L1:
|
|
return data[LB];
|
|
case L2:
|
|
return data[data[LB]];
|
|
case L3:
|
|
return data[data[data[LB]]];
|
|
case L4:
|
|
return data[data[data[data[LB]]]];
|
|
case L5:
|
|
return data[data[data[data[data[LB]]]]];
|
|
case L6:
|
|
return data[data[data[data[data[data[LB]]]]]];
|
|
case CP:
|
|
return CP;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// PROGRAM STATUS
|
|
|
|
static void dump() {
|
|
// Writes a summary of the machine state.
|
|
|
|
System.out.println("");
|
|
System.out.println("State of data store and registers:");
|
|
System.out.println("");
|
|
if (HT == HB) {
|
|
System.out.println(" |--------| (heap is empty)");
|
|
} else {
|
|
System.out.println(" HB-->");
|
|
System.out.println(" |--------|");
|
|
for (var addr = HB - 1; addr >= HT; addr--) {
|
|
System.out.print(addr + ":");
|
|
if (addr == HT) {
|
|
System.out.print(" HT-->");
|
|
} else {
|
|
System.out.print(" ");
|
|
}
|
|
System.out.println("|" + data[addr] + "|");
|
|
}
|
|
System.out.println(" |--------|");
|
|
}
|
|
System.out.println(" |////////|");
|
|
System.out.println(" |////////|");
|
|
if (ST == SB) {
|
|
System.out.println(" |--------| (stack is empty)");
|
|
} else {
|
|
var dynamicLink = LB;
|
|
var staticLink = LB;
|
|
var localRegNum = Register.LB;
|
|
System.out.println(" ST--> |////////|");
|
|
System.out.println(" |--------|");
|
|
for (var addr = ST - 1; addr >= SB; addr--) {
|
|
System.out.print(addr + ":");
|
|
if (addr == SB) {
|
|
System.out.print(" SB-->");
|
|
} else if (addr == staticLink) {
|
|
switch (localRegNum) {
|
|
case LB:
|
|
System.out.print(" LB-->");
|
|
break;
|
|
case L1:
|
|
System.out.print(" L1-->");
|
|
break;
|
|
case L2:
|
|
System.out.print(" L2-->");
|
|
break;
|
|
case L3:
|
|
System.out.print(" L3-->");
|
|
break;
|
|
case L4:
|
|
System.out.print(" L4-->");
|
|
break;
|
|
case L5:
|
|
System.out.print(" L5-->");
|
|
break;
|
|
case L6:
|
|
System.out.print(" L6-->");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
staticLink = data[addr];
|
|
localRegNum = Register.values()[localRegNum.ordinal() + 1];
|
|
} else {
|
|
System.out.print(" ");
|
|
}
|
|
if (addr == dynamicLink && dynamicLink != SB) {
|
|
System.out.print("|SL=" + data[addr] + "|");
|
|
} else if (addr == dynamicLink + 1 && dynamicLink != SB) {
|
|
System.out.print("|DL=" + data[addr] + "|");
|
|
} else if (addr == dynamicLink + 2 && dynamicLink != SB) {
|
|
System.out.print("|RA=" + data[addr] + "|");
|
|
} else {
|
|
System.out.print("|" + data[addr] + "|");
|
|
}
|
|
System.out.println("");
|
|
if (addr == dynamicLink) {
|
|
System.out.println(" |--------|");
|
|
dynamicLink = data[addr + 1];
|
|
}
|
|
}
|
|
}
|
|
System.out.println("");
|
|
}
|
|
|
|
static void showStatus() {
|
|
// Writes an indication of whether and why the program has terminated.
|
|
System.out.println("");
|
|
switch (status) {
|
|
case running:
|
|
System.out.println("Program is running.");
|
|
break;
|
|
case halted:
|
|
System.out.println("Program has halted normally.");
|
|
break;
|
|
case failedDataStoreFull:
|
|
System.out.println("Program has failed due to exhaustion of Data Store.");
|
|
break;
|
|
case failedInvalidCodeAddress:
|
|
System.out.println("Program has failed due to an invalid code address.");
|
|
break;
|
|
case failedInvalidInstruction:
|
|
System.out.println("Program has failed due to an invalid instruction.");
|
|
break;
|
|
case failedOverflow:
|
|
System.out.println("Program has failed due to overflow.");
|
|
break;
|
|
case failedZeroDivide:
|
|
System.out.println("Program has failed due to division by zero.");
|
|
break;
|
|
case failedIOError:
|
|
System.out.println("Program has failed due to an IO error.");
|
|
break;
|
|
}
|
|
if (status != halted) {
|
|
dump();
|
|
}
|
|
}
|
|
|
|
// INTERPRETATION
|
|
|
|
static void checkSpace(int spaceNeeded) {
|
|
// Signals failure if there is not enough space to expand the stack or
|
|
// heap by spaceNeeded.
|
|
|
|
if (HT - ST < spaceNeeded) {
|
|
status = failedDataStoreFull;
|
|
}
|
|
}
|
|
|
|
static boolean isTrue(int datum) {
|
|
// Tests whether the given datum represents true.
|
|
return (datum == Machine.trueRep);
|
|
}
|
|
|
|
static boolean equal(int size, int addr1, int addr2) {
|
|
// Tests whether two multi-word objects are equal, given their common
|
|
// size and their base addresses.
|
|
|
|
boolean eq;
|
|
int index;
|
|
|
|
eq = true;
|
|
index = 0;
|
|
while (eq && (index < size)) {
|
|
if (data[addr1 + index] == data[addr2 + index]) {
|
|
index = index + 1;
|
|
} else {
|
|
eq = false;
|
|
}
|
|
}
|
|
|
|
return eq;
|
|
}
|
|
|
|
static int overflowChecked(long datum) {
|
|
// Signals failure if the datum is too large to fit into a single word,
|
|
// otherwise returns the datum as a single word.
|
|
|
|
if ((-Machine.maxintRep <= datum) && (datum <= Machine.maxintRep)) {
|
|
return (int) datum;
|
|
} else {
|
|
status = failedOverflow;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int toInt(boolean b) {
|
|
return b ? Machine.trueRep : Machine.falseRep;
|
|
}
|
|
|
|
static int currentChar;
|
|
|
|
static int readInt() throws java.io.IOException {
|
|
int temp = 0;
|
|
int sign = 1;
|
|
|
|
do {
|
|
currentChar = System.in.read();
|
|
} while (Character.isWhitespace((char) currentChar));
|
|
|
|
if ((currentChar == '-') || (currentChar == '+')) {
|
|
do {
|
|
sign = (currentChar == '-') ? -1 : 1;
|
|
currentChar = System.in.read();
|
|
} while ((currentChar == '-') || currentChar == '+');
|
|
}
|
|
|
|
if (Character.isDigit((char) currentChar)) {
|
|
do {
|
|
temp = temp * 10 + (currentChar - '0');
|
|
currentChar = System.in.read();
|
|
} while (Character.isDigit((char) currentChar));
|
|
}
|
|
|
|
return sign * temp;
|
|
}
|
|
|
|
static void callPrimitive(int primitiveDisplacement) {
|
|
// Invokes the given primitive routine.
|
|
|
|
int addr, size;
|
|
char ch;
|
|
|
|
var primitive = Primitive.values()[primitiveDisplacement];
|
|
switch (primitive) {
|
|
case ID:
|
|
break; // nothing to be done
|
|
case NOT:
|
|
data[ST - 1] = toInt(!isTrue(data[ST - 1]));
|
|
break;
|
|
case AND:
|
|
ST = ST - 1;
|
|
data[ST - 1] = toInt(isTrue(data[ST - 1]) & isTrue(data[ST]));
|
|
break;
|
|
case OR:
|
|
ST = ST - 1;
|
|
data[ST - 1] = toInt(isTrue(data[ST - 1]) | isTrue(data[ST]));
|
|
break;
|
|
case SUCC:
|
|
data[ST - 1] = overflowChecked(data[ST - 1] + 1);
|
|
break;
|
|
case PRED:
|
|
data[ST - 1] = overflowChecked(data[ST - 1] - 1);
|
|
break;
|
|
case NEG:
|
|
data[ST - 1] = -data[ST - 1];
|
|
break;
|
|
case ADD:
|
|
ST = ST - 1;
|
|
accumulator = data[ST - 1];
|
|
data[ST - 1] = overflowChecked(accumulator + data[ST]);
|
|
break;
|
|
case SUB:
|
|
ST = ST - 1;
|
|
accumulator = data[ST - 1];
|
|
data[ST - 1] = overflowChecked(accumulator - data[ST]);
|
|
break;
|
|
case MULT:
|
|
ST = ST - 1;
|
|
accumulator = data[ST - 1];
|
|
data[ST - 1] = overflowChecked(accumulator * data[ST]);
|
|
break;
|
|
case DIV:
|
|
ST = ST - 1;
|
|
accumulator = data[ST - 1];
|
|
if (data[ST] != 0) {
|
|
data[ST - 1] = (int) (accumulator / data[ST]);
|
|
} else {
|
|
status = failedZeroDivide;
|
|
}
|
|
break;
|
|
case MOD:
|
|
ST = ST - 1;
|
|
accumulator = data[ST - 1];
|
|
if (data[ST] != 0) {
|
|
data[ST - 1] = (int) (accumulator % data[ST]);
|
|
} else {
|
|
status = failedZeroDivide;
|
|
}
|
|
break;
|
|
case LT:
|
|
ST = ST - 1;
|
|
data[ST - 1] = toInt(data[ST - 1] < data[ST]);
|
|
break;
|
|
case LE:
|
|
ST = ST - 1;
|
|
data[ST - 1] = toInt(data[ST - 1] <= data[ST]);
|
|
break;
|
|
case GE:
|
|
ST = ST - 1;
|
|
data[ST - 1] = toInt(data[ST - 1] >= data[ST]);
|
|
break;
|
|
case GT:
|
|
ST = ST - 1;
|
|
data[ST - 1] = toInt(data[ST - 1] > data[ST]);
|
|
break;
|
|
case EQ:
|
|
size = data[ST - 1]; // size of each comparand
|
|
ST = ST - 2 * size;
|
|
data[ST - 1] = toInt(equal(size, ST - 1, ST - 1 + size));
|
|
break;
|
|
case NE:
|
|
size = data[ST - 1]; // size of each comparand
|
|
ST = ST - 2 * size;
|
|
data[ST - 1] = toInt(!equal(size, ST - 1, ST - 1 + size));
|
|
break;
|
|
case EOL:
|
|
data[ST] = toInt(currentChar == '\n');
|
|
ST = ST + 1;
|
|
break;
|
|
case EOF:
|
|
data[ST] = toInt(currentChar == -1);
|
|
ST = ST + 1;
|
|
break;
|
|
case GET:
|
|
ST = ST - 1;
|
|
addr = data[ST];
|
|
try {
|
|
currentChar = System.in.read();
|
|
} catch (java.io.IOException s) {
|
|
status = failedIOError;
|
|
}
|
|
data[addr] = currentChar;
|
|
break;
|
|
case PUT:
|
|
ST = ST - 1;
|
|
ch = (char) data[ST];
|
|
System.out.print(ch);
|
|
break;
|
|
case GETEOL:
|
|
try {
|
|
while ((currentChar = System.in.read()) != '\n')
|
|
;
|
|
} catch (java.io.IOException s) {
|
|
status = failedIOError;
|
|
}
|
|
break;
|
|
case PUTEOL:
|
|
System.out.println("");
|
|
break;
|
|
case GETINT:
|
|
ST = ST - 1;
|
|
addr = data[ST];
|
|
try {
|
|
accumulator = readInt();
|
|
} catch (java.io.IOException s) {
|
|
status = failedIOError;
|
|
}
|
|
data[addr] = (int) accumulator;
|
|
break;
|
|
case PUTINT:
|
|
ST = ST - 1;
|
|
accumulator = data[ST];
|
|
System.out.print(accumulator);
|
|
break;
|
|
case NEW:
|
|
size = data[ST - 1];
|
|
checkSpace(size);
|
|
HT = HT - size;
|
|
data[ST - 1] = HT;
|
|
break;
|
|
case DISPOSE:
|
|
ST = ST - 1; // no action taken at present
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void interpretProgram() {
|
|
// Runs the program in code store.
|
|
|
|
Instruction currentInstr;
|
|
|
|
// Initialize registers ...
|
|
ST = SB;
|
|
HT = HB;
|
|
LB = SB;
|
|
CP = CB;
|
|
status = running;
|
|
do {
|
|
// Fetch instruction ...
|
|
currentInstr = Machine.code[CP];
|
|
// Decode instruction ...
|
|
var op = currentInstr.opCode;
|
|
var r = currentInstr.register;
|
|
var n = currentInstr.length;
|
|
var d = currentInstr.operand;
|
|
int addr;
|
|
|
|
// Execute instruction ...
|
|
switch (op) {
|
|
case LOAD:
|
|
addr = d + content(r);
|
|
checkSpace(n);
|
|
for (var index = 0; index < n; index++) {
|
|
data[ST + index] = data[addr + index];
|
|
}
|
|
ST = ST + n;
|
|
CP = CP + 1;
|
|
break;
|
|
case LOADA:
|
|
addr = d + content(r);
|
|
checkSpace(1);
|
|
data[ST] = addr;
|
|
ST = ST + 1;
|
|
CP = CP + 1;
|
|
break;
|
|
case LOADI:
|
|
ST = ST - 1;
|
|
addr = data[ST];
|
|
checkSpace(n);
|
|
for (var index = 0; index < n; index++) {
|
|
data[ST + index] = data[addr + index];
|
|
}
|
|
ST = ST + n;
|
|
CP = CP + 1;
|
|
break;
|
|
case LOADL:
|
|
checkSpace(1);
|
|
data[ST] = d;
|
|
ST = ST + 1;
|
|
CP = CP + 1;
|
|
break;
|
|
case STORE:
|
|
addr = d + content(r);
|
|
ST = ST - n;
|
|
for (var index = 0; index < n; index++) {
|
|
data[addr + index] = data[ST + index];
|
|
}
|
|
CP = CP + 1;
|
|
break;
|
|
case STOREI:
|
|
ST = ST - 1;
|
|
addr = data[ST];
|
|
ST = ST - n;
|
|
for (var index = 0; index < n; index++) {
|
|
data[addr + index] = data[ST + index];
|
|
}
|
|
CP = CP + 1;
|
|
break;
|
|
case CALL:
|
|
addr = d + content(r);
|
|
if (addr >= Machine.PB) {
|
|
callPrimitive(addr - Machine.PB);
|
|
CP = CP + 1;
|
|
} else {
|
|
checkSpace(3);
|
|
if (0 <= n && n <= 15) {
|
|
data[ST] = content(n); // static link
|
|
} else {
|
|
status = failedInvalidInstruction;
|
|
}
|
|
data[ST + 1] = LB; // dynamic link
|
|
data[ST + 2] = CP + 1; // return address
|
|
LB = ST;
|
|
ST = ST + 3;
|
|
CP = addr;
|
|
}
|
|
break;
|
|
case CALLI:
|
|
ST = ST - 2;
|
|
addr = data[ST + 1];
|
|
if (addr >= Machine.PB) {
|
|
callPrimitive(addr - Machine.PB);
|
|
CP = CP + 1;
|
|
} else {
|
|
// data[ST] = static link already
|
|
data[ST + 1] = LB; // dynamic link
|
|
data[ST + 2] = CP + 1; // return address
|
|
LB = ST;
|
|
ST = ST + 3;
|
|
CP = addr;
|
|
}
|
|
break;
|
|
case RETURN:
|
|
addr = LB - d;
|
|
CP = data[LB + 2];
|
|
LB = data[LB + 1];
|
|
ST = ST - n;
|
|
for (var index = 0; index < n; index++) {
|
|
data[addr + index] = data[ST + index];
|
|
}
|
|
ST = addr + n;
|
|
break;
|
|
case PUSH:
|
|
checkSpace(d);
|
|
ST = ST + d;
|
|
CP = CP + 1;
|
|
break;
|
|
case POP:
|
|
addr = ST - n - d;
|
|
ST = ST - n;
|
|
for (var index = 0; index < n; index++) {
|
|
data[addr + index] = data[ST + index];
|
|
}
|
|
ST = addr + n;
|
|
CP = CP + 1;
|
|
break;
|
|
case JUMP:
|
|
CP = d + content(r);
|
|
break;
|
|
case JUMPI:
|
|
ST = ST - 1;
|
|
CP = data[ST];
|
|
break;
|
|
case JUMPIF:
|
|
ST = ST - 1;
|
|
if (data[ST] == n) {
|
|
CP = d + content(r);
|
|
} else {
|
|
CP = CP + 1;
|
|
}
|
|
break;
|
|
case HALT:
|
|
status = halted;
|
|
break;
|
|
}
|
|
if (CP < CB || CP >= CT) {
|
|
status = failedInvalidCodeAddress;
|
|
}
|
|
} while (status == running);
|
|
}
|
|
|
|
// LOADING
|
|
|
|
static void loadObjectProgram(String objectName) {
|
|
// Loads the TAM object program into code store from the named file.
|
|
|
|
boolean finished = false;
|
|
|
|
try (var objectFile = new FileInputStream(objectName)) {
|
|
var objectStream = new DataInputStream(objectFile);
|
|
|
|
var addr = Machine.CB;
|
|
while (!finished) {
|
|
Machine.code[addr] = Instruction.read(objectStream);
|
|
if (Machine.code[addr] == null) {
|
|
finished = true;
|
|
} else {
|
|
addr = addr + 1;
|
|
}
|
|
}
|
|
CT = addr;
|
|
} catch (FileNotFoundException s) {
|
|
CT = CB;
|
|
System.err.println("Error opening object file: " + s);
|
|
} catch (IOException s) {
|
|
CT = CB;
|
|
System.err.println("Error reading object file: " + s);
|
|
}
|
|
}
|
|
|
|
// RUNNING
|
|
|
|
public static void main(String[] args) {
|
|
System.out.println("********** TAM Interpreter (Java Version 2.1) **********");
|
|
|
|
if (args.length == 1) {
|
|
objectName = args[0];
|
|
} else {
|
|
objectName = "obj.tam";
|
|
}
|
|
|
|
loadObjectProgram(objectName);
|
|
if (CT != CB) {
|
|
interpretProgram();
|
|
showStatus();
|
|
}
|
|
}
|
|
}
|
|
|