parent
fbd8c5bab0
commit
3655a949f3
@ -0,0 +1,21 @@ |
||||
MIT License |
||||
|
||||
Copyright (c) 2022 deryckb |
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
of this software and associated documentation files (the "Software"), to deal |
||||
in the Software without restriction, including without limitation the rights |
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
copies of the Software, and to permit persons to whom the Software is |
||||
furnished to do so, subject to the following conditions: |
||||
|
||||
The above copyright notice and this permission notice shall be included in all |
||||
copies or substantial portions of the Software. |
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
SOFTWARE. |
@ -0,0 +1,3 @@ |
||||
/target/ |
||||
/.classpath |
||||
/build/ |
@ -0,0 +1,23 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<projectDescription> |
||||
<name>Triangle.AbstractMachine.Disassembler</name> |
||||
<comment></comment> |
||||
<projects> |
||||
</projects> |
||||
<buildSpec> |
||||
<buildCommand> |
||||
<name>org.eclipse.jdt.core.javabuilder</name> |
||||
<arguments> |
||||
</arguments> |
||||
</buildCommand> |
||||
<buildCommand> |
||||
<name>org.eclipse.buildship.core.gradleprojectbuilder</name> |
||||
<arguments> |
||||
</arguments> |
||||
</buildCommand> |
||||
</buildSpec> |
||||
<natures> |
||||
<nature>org.eclipse.jdt.core.javanature</nature> |
||||
<nature>org.eclipse.buildship.core.gradleprojectnature</nature> |
||||
</natures> |
||||
</projectDescription> |
@ -0,0 +1,12 @@ |
||||
apply plugin: 'java' |
||||
apply plugin: 'application' |
||||
|
||||
sourceCompatibility = 11 |
||||
|
||||
dependencies { |
||||
implementation project(':Triangle.AbstractMachine') |
||||
} |
||||
|
||||
application { |
||||
mainClass = 'Triangle.AbstractMachine.Disassembler' |
||||
} |
@ -0,0 +1,18 @@ |
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" |
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> |
||||
<modelVersion>4.0.0</modelVersion> |
||||
<artifactId>triangle-disassembler</artifactId> |
||||
<parent> |
||||
<groupId>triangle.tools</groupId> |
||||
<artifactId>triangle-tools</artifactId> |
||||
<version>2.1</version> |
||||
<relativePath>../</relativePath> |
||||
</parent> |
||||
<dependencies> |
||||
<dependency> |
||||
<groupId>triangle.tools</groupId> |
||||
<artifactId>triangle-abstractmachine</artifactId> |
||||
<version>2.1</version> |
||||
</dependency> |
||||
</dependencies> |
||||
</project> |
@ -0,0 +1,351 @@ |
||||
/* |
||||
* @(#)Disassembler.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; |
||||
|
||||
/** |
||||
* Disassembles the TAM code in the given file, and displays the instructions on |
||||
* standard output. |
||||
* |
||||
* For example: |
||||
* |
||||
* <pre> |
||||
* java TAM.Disassembler obj.tam |
||||
* </pre> |
||||
* |
||||
* <p> |
||||
* Copyright 1991 David A. Watt, University of Glasgow<br> |
||||
* Copyright 1998 Deryck F. Brown, The Robert Gordon University<br> |
||||
* </p> |
||||
* |
||||
*/ |
||||
|
||||
public class Disassembler { |
||||
|
||||
static String objectName; |
||||
|
||||
static int CT; |
||||
|
||||
/** |
||||
* Writes the r-field of an instruction in the form "l<I>reg</I>r", where l and |
||||
* r are the bracket characters to use. |
||||
* |
||||
* @param leftbracket the character to print before the register. |
||||
* @param r the number of the register. |
||||
* @param rightbracket the character to print after the register. |
||||
*/ |
||||
private static void writeR(char leftbracket, Register r, char rightbracket) { |
||||
|
||||
System.out.print(leftbracket); |
||||
System.out.print(r.toString()); |
||||
System.out.print(rightbracket); |
||||
} |
||||
|
||||
private static void writeR(char leftBracket, int r, char rightBracket) { |
||||
var register = Register.values()[r]; |
||||
writeR(leftBracket, register, rightBracket); |
||||
} |
||||
|
||||
/** |
||||
* Writes a void n-field of an instruction. |
||||
*/ |
||||
private static void blankN() { |
||||
System.out.print(" "); |
||||
} |
||||
|
||||
// Writes the n-field of an instruction.
|
||||
/** |
||||
* Writes the n-field of an instruction in the form "(n)". |
||||
* |
||||
* @param n the integer to write. |
||||
*/ |
||||
private static void writeN(int n) { |
||||
System.out.print("(" + n + ") "); |
||||
if (n < 10) { |
||||
System.out.print(" "); |
||||
} else if (n < 100) { |
||||
System.out.print(" "); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Writes the d-field of an instruction. |
||||
* |
||||
* @param d the integer to write. |
||||
*/ |
||||
private static void writeD(int d) { |
||||
System.out.print(d); |
||||
} |
||||
|
||||
/** |
||||
* Writes the name of primitive routine with relative address d. |
||||
* |
||||
* @param d the displacement of the primitive routine. |
||||
*/ |
||||
private static void writePrimitive(int d) { |
||||
var primitive = Primitive.values()[d]; |
||||
switch (primitive) { |
||||
case ID: |
||||
System.out.print("id "); |
||||
break; |
||||
case NOT: |
||||
System.out.print("not "); |
||||
break; |
||||
case AND: |
||||
System.out.print("and "); |
||||
break; |
||||
case OR: |
||||
System.out.print("or "); |
||||
break; |
||||
case SUCC: |
||||
System.out.print("succ "); |
||||
break; |
||||
case PRED: |
||||
System.out.print("pred "); |
||||
break; |
||||
case NEG: |
||||
System.out.print("neg "); |
||||
break; |
||||
case ADD: |
||||
System.out.print("add "); |
||||
break; |
||||
case SUB: |
||||
System.out.print("sub "); |
||||
break; |
||||
case MULT: |
||||
System.out.print("mult "); |
||||
break; |
||||
case DIV: |
||||
System.out.print("div "); |
||||
break; |
||||
case MOD: |
||||
System.out.print("mod "); |
||||
break; |
||||
case LT: |
||||
System.out.print("lt "); |
||||
break; |
||||
case LE: |
||||
System.out.print("le "); |
||||
break; |
||||
case GE: |
||||
System.out.print("ge "); |
||||
break; |
||||
case GT: |
||||
System.out.print("gt "); |
||||
break; |
||||
case EQ: |
||||
System.out.print("eq "); |
||||
break; |
||||
case NE: |
||||
System.out.print("ne "); |
||||
break; |
||||
case EOL: |
||||
System.out.print("eol "); |
||||
break; |
||||
case EOF: |
||||
System.out.print("eof "); |
||||
break; |
||||
case GET: |
||||
System.out.print("get "); |
||||
break; |
||||
case PUT: |
||||
System.out.print("put "); |
||||
break; |
||||
case GETEOL: |
||||
System.out.print("geteol "); |
||||
break; |
||||
case PUTEOL: |
||||
System.out.print("puteol "); |
||||
break; |
||||
case GETINT: |
||||
System.out.print("getint "); |
||||
break; |
||||
case PUTINT: |
||||
System.out.print("putint "); |
||||
break; |
||||
case NEW: |
||||
System.out.print("new "); |
||||
break; |
||||
case DISPOSE: |
||||
System.out.print("dispose "); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Writes the given instruction in assembly-code format. |
||||
* |
||||
* @param instr the instruction to display. |
||||
*/ |
||||
private static void writeInstruction(Instruction instr) { |
||||
|
||||
switch (instr.opCode) { |
||||
case LOAD: |
||||
System.out.print("LOAD "); |
||||
writeN(instr.length); |
||||
writeD(instr.operand); |
||||
writeR('[', instr.register, ']'); |
||||
break; |
||||
|
||||
case LOADA: |
||||
System.out.print("LOADA "); |
||||
blankN(); |
||||
writeD(instr.operand); |
||||
writeR('[', instr.register, ']'); |
||||
break; |
||||
|
||||
case LOADI: |
||||
System.out.print("LOADI "); |
||||
writeN(instr.length); |
||||
break; |
||||
|
||||
case LOADL: |
||||
System.out.print("LOADL "); |
||||
blankN(); |
||||
writeD(instr.operand); |
||||
break; |
||||
|
||||
case STORE: |
||||
System.out.print("STORE "); |
||||
writeN(instr.length); |
||||
writeD(instr.operand); |
||||
writeR('[', instr.register, ']'); |
||||
break; |
||||
|
||||
case STOREI: |
||||
System.out.print("STOREI"); |
||||
writeN(instr.length); |
||||
break; |
||||
|
||||
case CALL: |
||||
System.out.print("CALL "); |
||||
if (instr.register == Register.PB) { |
||||
blankN(); |
||||
writePrimitive(instr.operand); |
||||
} else { |
||||
writeR('(', instr.length, ')'); |
||||
System.out.print(" "); |
||||
writeD(instr.operand); |
||||
writeR('[', instr.register, ']'); |
||||
} |
||||
break; |
||||
|
||||
case CALLI: |
||||
System.out.print("CALLI "); |
||||
break; |
||||
|
||||
case RETURN: |
||||
System.out.print("RETURN"); |
||||
writeN(instr.length); |
||||
writeD(instr.operand); |
||||
break; |
||||
|
||||
case PUSH: |
||||
System.out.print("PUSH "); |
||||
blankN(); |
||||
writeD(instr.operand); |
||||
break; |
||||
|
||||
case POP: |
||||
System.out.print("POP "); |
||||
writeN(instr.length); |
||||
writeD(instr.operand); |
||||
break; |
||||
|
||||
case JUMP: |
||||
System.out.print("JUMP "); |
||||
blankN(); |
||||
writeD(instr.operand); |
||||
writeR('[', instr.register, ']'); |
||||
break; |
||||
|
||||
case JUMPI: |
||||
System.out.print("JUMPI "); |
||||
break; |
||||
|
||||
case JUMPIF: |
||||
System.out.print("JUMPIF"); |
||||
writeN(instr.length); |
||||
writeD(instr.operand); |
||||
writeR('[', instr.register, ']'); |
||||
break; |
||||
|
||||
case HALT: |
||||
System.out.print("HALT "); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Writes all instructions of the program in code store. |
||||
*/ |
||||
private static void disassembleProgram() { |
||||
for (int addr = Machine.CB; addr < CT; addr++) { |
||||
System.out.print(addr + ": "); |
||||
writeInstruction(Machine.code[addr]); |
||||
System.out.println(); |
||||
} |
||||
} |
||||
|
||||
// LOADING
|
||||
|
||||
/** |
||||
* Loads the TAM object program into code store from the named file. |
||||
* |
||||
* @param objectName the name of the file containing the program. |
||||
*/ |
||||
static void loadObjectProgram(String objectName) { |
||||
|
||||
var 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 = Machine.CB; |
||||
System.err.println("Error opening object file: " + s); |
||||
} catch (IOException s) { |
||||
CT = Machine.CB; |
||||
System.err.println("Error reading object file: " + s); |
||||
} |
||||
} |
||||
|
||||
// DISASSEMBLE
|
||||
|
||||
public static void main(String[] args) { |
||||
System.out.println("********** TAM Disassembler (Sun Version 2.1) **********"); |
||||
|
||||
if (args.length == 1) { |
||||
objectName = args[0]; |
||||
} else { |
||||
objectName = "obj.tam"; |
||||
} |
||||
|
||||
loadObjectProgram(objectName); |
||||
disassembleProgram(); |
||||
} |
||||
} |
@ -0,0 +1,3 @@ |
||||
/target/ |
||||
/.classpath |
||||
/build/ |
@ -0,0 +1,23 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<projectDescription> |
||||
<name>Triangle.AbstractMachine.Interpreter</name> |
||||
<comment></comment> |
||||
<projects> |
||||
</projects> |
||||
<buildSpec> |
||||
<buildCommand> |
||||
<name>org.eclipse.jdt.core.javabuilder</name> |
||||
<arguments> |
||||
</arguments> |
||||
</buildCommand> |
||||
<buildCommand> |
||||
<name>org.eclipse.buildship.core.gradleprojectbuilder</name> |
||||
<arguments> |
||||
</arguments> |
||||
</buildCommand> |
||||
</buildSpec> |
||||
<natures> |
||||
<nature>org.eclipse.jdt.core.javanature</nature> |
||||
<nature>org.eclipse.buildship.core.gradleprojectnature</nature> |
||||
</natures> |
||||
</projectDescription> |
@ -0,0 +1,12 @@ |
||||
apply plugin: 'java' |
||||
apply plugin: 'application' |
||||
|
||||
sourceCompatibility = 11 |
||||
|
||||
dependencies { |
||||
implementation project(':Triangle.AbstractMachine') |
||||
} |
||||
|
||||
application { |
||||
mainClass = 'Triangle.AbstractMachine.Interpreter' |
||||
} |
@ -0,0 +1,18 @@ |
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" |
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> |
||||
<modelVersion>4.0.0</modelVersion> |
||||
<artifactId>triangle-interpreter</artifactId> |
||||
<parent> |
||||
<groupId>triangle.tools</groupId> |
||||
<artifactId>triangle-tools</artifactId> |
||||
<version>2.1</version> |
||||
<relativePath>../</relativePath> |
||||
</parent> |
||||
<dependencies> |
||||
<dependency> |
||||
<groupId>triangle.tools</groupId> |
||||
<artifactId>triangle-abstractmachine</artifactId> |
||||
<version>2.1</version> |
||||
</dependency> |
||||
</dependencies> |
||||
</project> |
@ -0,0 +1,645 @@ |
||||
/* |
||||
* @(#)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 long startTimeNanos = 0; |
||||
|
||||
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."); |
||||
System.out.println("Total execution time (ns): " + (System.nanoTime() - startTimeNanos)); |
||||
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) { |
||||
startTimeNanos = System.nanoTime(); |
||||
interpretProgram(); |
||||
showStatus(); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,3 @@ |
||||
/target/ |
||||
/.classpath |
||||
/build/ |
@ -0,0 +1,23 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<projectDescription> |
||||
<name>Triangle.AbstractMachine</name> |
||||
<comment></comment> |
||||
<projects> |
||||
</projects> |
||||
<buildSpec> |
||||
<buildCommand> |
||||
<name>org.eclipse.jdt.core.javabuilder</name> |
||||
<arguments> |
||||
</arguments> |
||||
</buildCommand> |
||||
<buildCommand> |
||||
<name>org.eclipse.buildship.core.gradleprojectbuilder</name> |
||||
<arguments> |
||||
</arguments> |
||||
</buildCommand> |
||||
</buildSpec> |
||||
<natures> |
||||
<nature>org.eclipse.jdt.core.javanature</nature> |
||||
<nature>org.eclipse.buildship.core.gradleprojectnature</nature> |
||||
</natures> |
||||
</projectDescription> |
@ -0,0 +1,4 @@ |
||||
apply plugin: 'java-library' |
||||
apply plugin: 'eclipse' |
||||
|
||||
sourceCompatibility = 11 |
@ -0,0 +1,12 @@ |
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" |
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> |
||||
<modelVersion>4.0.0</modelVersion> |
||||
<artifactId>triangle-abstractmachine</artifactId> |
||||
<packaging>jar</packaging> |
||||
<parent> |
||||
<groupId>triangle.tools</groupId> |
||||
<artifactId>triangle-tools</artifactId> |
||||
<version>2.1</version> |
||||
<relativePath>../</relativePath> |
||||
</parent> |
||||
</project> |
@ -0,0 +1,67 @@ |
||||
/* |
||||
* @(#)Instruction.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.DataOutputStream; |
||||
import java.io.EOFException; |
||||
import java.io.IOException; |
||||
|
||||
public class Instruction { |
||||
|
||||
// Java has no type synonyms, so the following representations are
|
||||
// assumed:
|
||||
//
|
||||
// type
|
||||
// OpCode = 0..15; {4 bits unsigned}
|
||||
// Length = 0..255; {8 bits unsigned}
|
||||
// Operand = -32767..+32767; {16 bits signed}
|
||||
|
||||
// Represents TAM instructions.
|
||||
final OpCode opCode; |
||||
final Register register; |
||||
final int length; |
||||
int operand; // Not final to allow for patching jump address
|
||||
|
||||
public Instruction(OpCode opcode, Register register, int length, int operand) { |
||||
this.opCode = opcode; |
||||
this.register = register; |
||||
this.length = length; |
||||
this.operand = operand; |
||||
} |
||||
|
||||
public void setOperand(int operand) { |
||||
this.operand = operand; |
||||
} |
||||
|
||||
public void write(DataOutputStream output) throws IOException { |
||||
output.writeInt(opCode.ordinal()); |
||||
output.writeInt(register.ordinal()); |
||||
output.writeInt(length); |
||||
output.writeInt(operand); |
||||
} |
||||
|
||||
public static Instruction read(DataInputStream input) throws IOException { |
||||
try { |
||||
var opCode = OpCode.values()[input.readInt()]; |
||||
var register = Register.values()[input.readInt()]; |
||||
var length = input.readInt(); |
||||
var operand = input.readInt(); |
||||
return new Instruction(opCode, register, length, operand); |
||||
} catch (EOFException s) { |
||||
return null; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,54 @@ |
||||
/* |
||||
* @(#)Machine.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; |
||||
|
||||
public final class Machine { |
||||
|
||||
public final static int maxRoutineLevel = 7; |
||||
|
||||
// WORDS AND ADDRESSES
|
||||
|
||||
// Java has no type synonyms, so the following representations are
|
||||
// assumed:
|
||||
//
|
||||
// type
|
||||
// Word = -32767..+32767; {16 bits signed}
|
||||
// DoubleWord = -2147483648..+2147483647; {32 bits signed}
|
||||
// CodeAddress = 0..+32767; {15 bits unsigned}
|
||||
// DataAddress = 0..+32767; {15 bits unsigned}
|
||||
|
||||
// INSTRUCTIONS
|
||||
|
||||
// CODE STORE
|
||||
|
||||
public static Instruction[] code = new Instruction[1024]; |
||||
|
||||
// CODE STORE REGISTERS
|
||||
|
||||
public final static int CB = 0, PB = 1024, // = upper bound of code array + 1
|
||||
PT = 1052; // = PB + 28
|
||||
|
||||
// REGISTER NUMBERS
|
||||
|
||||
// DATA REPRESENTATION
|
||||
|
||||
public final static int booleanSize = 1, characterSize = 1, integerSize = 1, addressSize = 1, |
||||
closureSize = 2 * addressSize, |
||||
|
||||
linkDataSize = 3 * addressSize, |
||||
|
||||
falseRep = 0, trueRep = 1, maxintRep = 32767; |
||||
|
||||
} |
@ -0,0 +1,5 @@ |
||||
package triangle.abstractMachine; |
||||
|
||||
public enum OpCode { |
||||
LOAD, LOADA, LOADI, LOADL, STORE, STOREI, CALL, CALLI, RETURN, NOP, PUSH, POP, JUMP, JUMPI, JUMPIF, HALT |
||||
} |
@ -0,0 +1,6 @@ |
||||
package triangle.abstractMachine; |
||||
|
||||
public enum Primitive { |
||||
ID, NOT, AND, OR, SUCC, PRED, NEG, ADD, SUB, MULT, DIV, MOD, LT, LE, GE, GT, EQ, NE, EOL, EOF, GET, PUT, GETEOL, |
||||
PUTEOL, GETINT, PUTINT, NEW, DISPOSE |
||||
} |
@ -0,0 +1,5 @@ |
||||
package triangle.abstractMachine; |
||||
|
||||
public enum Register { |
||||
CB, CT, PB, PT, SB, ST, HB, HT, LB, L1, L2, L3, L4, L5, L6, CP |
||||
} |
@ -0,0 +1,4 @@ |
||||