import java.io.InputStream;
import org.antlr.runtime.*;
/** A simple interpreter for integer arithmetic */
public class Interp {
public static final int DEFAULT_STACK_SIZE = 100;
public static final int BOOLEAN_FALSE = -1;
public static final int BOOLEAN_TRUE = 1;
public static final int INSTR_ADD = 1;
public static final int INSTR_SUB = 2;
public static final int INSTR_MULT = 3;
public static final int INSTR_DIV = 4;
public static final int INSTR_LT = 5;
public static final int INSTR_GT = 6;
public static final int INSTR_EQ = 7;
public static final int INSTR_NOT = 8;
public static final int INSTR_CALL = 9;
public static final int INSTR_RET = 10; public static final int INSTR_RETV = 11; public static final int INSTR_BR = 12; public static final int INSTR_BRT = 13; public static final int INSTR_CONST = 14; public static final int INSTR_LOAD = 15; public static final int INSTR_FPLOAD = 16; public static final int INSTR_STORE = 17; public static final int INSTR_FPSTORE = 18; public static final int INSTR_LALLOC = 19; public static final int INSTR_PRINT =20; public static final int INSTR_HALT = 21;
/** List of instructions so assembler knows what to allow.
* Also used in disassembly.
*/
public static String[] instructions = new String[] {
"<INVALID>",
"ADD", "SUB",
"MULT",
"DIV",
"LT",
"GT",
"EQ",
"NOT",
"CALL",
"RET",
"RETV",
"BR",
"BRT",
"CONST",
"LOAD",
"FPLOAD",
"STORE",
"FPSTORE",
"LALLOC",
"PRINT",
"HALT",
};
/** Used for disassembly; says if opcode has an operand */
public static int[] operands = new int[] {
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, ?, ?, ?, ?, ?, 0, 0 };
/** byte-addressable memory stores code and data. Opcodes
* are 8 bits and operands are 32-bits; must recombine
* 4 bytes into 32-bit word. Operands are either integers or
* addresses (or saved registers).
*
* ----------------
* 0: | data memory |
* | |
* | |
* ----------------
* cp:| code memory | begins right after data
* | |
* ----------------
*
* The cp allows us to relocate code in the memory (w/o having to
* change the code--all code memory fetches are done relative to
* cp register).
*/
byte[] memory;
/** word-addressable stack (grows downward); stores regs, ints,
* addresses, booleans (boolean operator results).
*
* Here is a stack activation record for a function/method:
*
* | ... |
* ----------------
* | arg 1 |
* | arg 2 |
* | ... |
* | arg n |
* | ret addr |
* fp:| old fp |
* | local 1 |
* | local 2 |
* | ... |
* | local m |
* ----------------
* | work space |
* | ... |
*
* So arg i is at stack[fp+2+(n-i)] since we push args in
* specified order rather than reverse. Local i is at
* stack[fp-i]. The compiler is required to compute an
* integer constant such as 2 that accesses the first argument
* or -1 that accesses the first local.
*/
int[] stack;
/** Stack pointer */
int sp;
/** Frame pointer for function call activation records */
int fp;
/** Instruction pointer register */
int ip;
/** For relocatable code, set "code pointer" */
int cp;
int dataMemorySize = 0;
boolean trace = false;
public static void main(String[] args) throws Exception {
boolean trace = false;
if ( args.length>0 ) {
if ( args[0].equals("-trace") ) {
trace = true;
}
}
AssemblerLexer assemblerLexer = new AssemblerLexer(new ANTLRInputStream(System.in));
CommonTokenStream tokens = new CommonTokenStream(assemblerLexer);
Assembler assembler = new Assembler(tokens, instructions);
assembler.program();
byte[] program = assembler.getMachineCode();
int dataSize = assembler.getDataMemorySize();
int startip = assembler.getMainInstructionAddress();
disassemble(program);
System.out.println("\nOutput:");
Interp interpreter = new Interp();
interpreter.trace = trace;
interpreter.exec(program, startip, dataSize);
interpreter.coredump();
}
/** Execute the bytecodes in "code" starting at ip; alloc
* enough static memory for dataMemorySize bytes. You cannot
* define data memory before execution--must set with code.
*/
public void exec(byte[] code, int ip, int dataMemorySize)
throws Exception
{
}
/** Load program into memory, set up data memory, set ip etc... */
protected void loader(byte[] code, int ip, int dataMemorySize) {
this.dataMemorySize = dataMemorySize;
memory = new byte[code.length+dataMemorySize];
cp = dataMemorySize; for (int i=0; i<code.length; i++) {
memory[cp+i] = code[i];
}
this.ip = ip; }
/** Simulate the fetch-execute cycle */
protected void cpu() throws Exception {
byte opcode = memory[cp+ip];
int v = 0;
int addr = 0;
int left,right;
while (opcode!=INSTR_HALT) {
if ( trace ) {
System.out.print(hex(cp+ip)+": ");
disassembleInstruction(memory, cp+ip);
}
ip++; switch (opcode) {
...
default :
throw new Exception("invalid opcode: "+opcode+" at ip="+(ip-1));
}
opcode = memory[cp+ip];
}
}
public void coredump() {
System.out.println("\nData memory (offset 0):");
dump(memory, 0, dataMemorySize);
System.out.println("\nCode memory (offset "+cp+"):");
dump(memory, cp, memory.length-dataMemorySize);
}
public static void disassemble(byte[] memory) {
System.out.println("\nDisassembly:");
for (int i=0; i<memory.length; ) {
i = disassembleInstruction(memory, i);
}
}
public static int disassembleInstruction(byte[] memory, int ip) {
int opcode = memory[ip];
ip++;
String instrName = instructions[opcode];
System.out.print("\t"+instrName);
if ( operands[opcode]==1 ) {
int opnd = getInt(memory, ip);
ip += 4;
System.out.print(" "+opnd);
}
System.out.println();
return ip;
}
/** Pull off 4 bytes starting at ip and return 32-bit signed int value.
* Return with ip pointing *after* last byte of operand. The byte-order
* is high byte down to low byte, left to right.
*/
private int getIntOperand() {
}
public static int getInt(byte[] memory, int index) {
int b1 = memory[index++]&0xFF; int b2 = memory[index++]&0xFF;
int b3 = memory[index++]&0xFF;
int b4 = memory[index++]&0xFF;
int word = b1<<(8*3) | b2<<(8*2) | b3<<(8*1) | b4;
return word;
}
/** Write value at index into a byte array highest to lowest byte,
* left to right.
*/
public static void writeInt(byte[] bytes, int index, int value)
{
bytes[index+0] = (byte)((value>>(8*3))&0xFF); bytes[index+1] = (byte)((value>>(8*2))&0xFF);
bytes[index+2] = (byte)((value>>(8*1))&0xFF);
bytes[index+3] = (byte)(value&0xFF);
}
public static void dump(byte[] memory, int offset, int n) {
for (int i=0; memory!=null && i<n; i++) {
if ( i%8==0 && i!=0 ) {
System.out.println();
}
if ( i%8==0 ) {
System.out.print(hex(offset+i)+": ");
}
System.out.print(" "+hex(memory[i+offset]));
}
System.out.println();
}
public static String hex(byte b) {
String bs = Integer.toString(b&0xFF,16).toUpperCase();
if ( (b&0xFF)<=(byte)0x0F ) {
bs = '0'+bs;
}
return bs;
}
public static String hex(int b) {
String bs = Integer.toString(b,16).toUpperCase();
int n = bs.length();
for (int i=1; i<=(8-n); i++) {
bs = '0'+bs;
}
return bs;
}
}