Skip to content

Commit

Permalink
got intial interp going.
Browse files Browse the repository at this point in the history
  • Loading branch information
parrt committed May 10, 2014
1 parent 3c6bebd commit 9e2406e
Show file tree
Hide file tree
Showing 3 changed files with 284 additions and 0 deletions.
55 changes: 55 additions & 0 deletions src/vm/Bytecode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package vm;

public class Bytecode {
public static class Instruction {
String name; // E.g., "iadd", "call"
int n = 0;
public Instruction(String name) { this(name,0); }
public Instruction(String name, int nargs) {
this.name = name;
this.n = nargs;
}
}

// INSTRUCTION BYTECODES (byte is signed; use a short to keep 0..255)
public static final short IADD = 1; // int add
public static final short ISUB = 2;
public static final short IMUL = 3;
public static final short ILT = 4; // int less than
public static final short IEQ = 5; // int equal
public static final short CALL = 6;
public static final short RET = 7; // return with/without value
public static final short BR = 8; // branch
public static final short BRT = 9; // branch if true
public static final short BRF = 10; // branch if true
public static final short ICONST = 11; // push constant integer
public static final short LOAD = 12; // load from local context
public static final short GLOAD = 13; // load from global memory
public static final short STORE = 14; // store in local context
public static final short GSTORE = 15; // store in global memory
public static final short PRINT = 16; // print stack top
public static final short POP = 17; // throw away top of stack
public static final short HALT = 18;

public static Instruction[] instructions = new Instruction[] {
null, // <INVALID>
new Instruction("iadd"), // index is the opcode
new Instruction("isub"),
new Instruction("imul"),
new Instruction("ilt"),
new Instruction("ieq"),
new Instruction("call", 2), // call addr, nargs
new Instruction("ret"),
new Instruction("br", 1),
new Instruction("brt", 1),
new Instruction("brf", 1),
new Instruction("iconst", 1),
new Instruction("load", 1),
new Instruction("gload", 1),
new Instruction("store", 1),
new Instruction("gstore", 1),
new Instruction("print"),
new Instruction("pop"),
new Instruction("halt")
};
}
57 changes: 57 additions & 0 deletions src/vm/Test.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package vm;

import static vm.Bytecode.BR;
import static vm.Bytecode.BRF;
import static vm.Bytecode.GLOAD;
import static vm.Bytecode.GSTORE;
import static vm.Bytecode.HALT;
import static vm.Bytecode.IADD;
import static vm.Bytecode.ICONST;
import static vm.Bytecode.ILT;
import static vm.Bytecode.PRINT;

public class Test {
static int[] hello = {
ICONST, 1234,
PRINT,
HALT
};

static int[] loop = {
// .GLOBALS 2; N, I
// N = 100000000 ADDRESS
ICONST, 100000000, // 0
GSTORE, 0, // 2
// I = 0
ICONST, 0, // 4
GSTORE, 1, // 6
// WHILE I<N:
// START (8):
GLOAD, 1, // 8
GLOAD, 0, // 10
ILT, // 12
BRF, 24, // 13
// I = I + 1
GLOAD, 1, // 15
ICONST, 1, // 17
IADD, // 19
GSTORE, 1, // 20
BR, 8, // 22
// DONE (24):
// PRINT "LOOPED "+N+" TIMES."
HALT // 24
};

static int[] factorial = {

};

public static void main(String[] args) {
VM vm = new VM(hello, 0, 0);
vm.exec();

vm = new VM(loop, 0, 2);
vm.trace = true;
vm.exec();
}
}
172 changes: 172 additions & 0 deletions src/vm/VM.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
package vm;

import java.util.ArrayList;
import java.util.List;

import static vm.Bytecode.BR;
import static vm.Bytecode.BRF;
import static vm.Bytecode.BRT;
import static vm.Bytecode.CALL;
import static vm.Bytecode.GLOAD;
import static vm.Bytecode.GSTORE;
import static vm.Bytecode.HALT;
import static vm.Bytecode.IADD;
import static vm.Bytecode.ICONST;
import static vm.Bytecode.IEQ;
import static vm.Bytecode.ILT;
import static vm.Bytecode.IMUL;
import static vm.Bytecode.ISUB;
import static vm.Bytecode.LOAD;
import static vm.Bytecode.POP;
import static vm.Bytecode.PRINT;
import static vm.Bytecode.RET;
import static vm.Bytecode.STORE;

/** A simple stack-based interpreter */
public class VM {
public static final int DEFAULT_STACK_SIZE = 1000;
public static final int TRUE = 1;
public static final int FALSE = 0;

// registers
int ip; // instruction pointer register
int sp = -1; // stack pointer register
int fp = -1; // frame pointer register

int startip = 0; // where execution begins

// memory
int[] code; // word-addressable code memory but still bytecodes.
int[] globals; // global variable space
int[] stack; // Operand/call stack, grows upwards

public boolean trace = false;

public VM(int[] code, int startip, int nglobals) {
this.code = code;
this.startip = startip;
globals = new int[nglobals];
stack = new int[DEFAULT_STACK_SIZE];
}

public void exec() {
stack[++sp] = 0; // simulate call from operating system
cpu();
}

/** Simulate the fetch-decode execute cycle */
protected void cpu() {
int opcode = code[ip];
int a,b,addr,offset;
while (opcode!= HALT && ip < code.length) {
if ( trace ) trace();
ip++; //jump to next instruction or to operand
switch (opcode) {
case IADD:
a = stack[sp--]; // 1st opnd 1 below top
b = stack[sp--]; // 2nd opnd at top of stack
stack[++sp] = a + b; // push result
break;
case ISUB:
a = stack[sp--];
b = stack[sp--];
stack[++sp] = a - b;
break;
case IMUL:
a = stack[sp--];
b = stack[sp--];
stack[++sp] = a * b;
break;
case ILT :
a = stack[sp--];
b = stack[sp--];
stack[++sp] = a < b ? TRUE : FALSE;
break;
case IEQ :
a = stack[sp--];
b = stack[sp--];
stack[++sp] = a == b ? TRUE : FALSE;
break;
case CALL :
// expects all args on stack
addr = code[ip++]; // target addr of function
int nargs = code[ip++]; // how many args got pushed
stack[++sp] = nargs; // save num args
stack[++sp] = fp; // save fp
stack[++sp] = ip; // push return address
fp = sp; // fp points at ret addr on stack
ip = addr; // jump to function
// code preamble of func must push space for locals
break;
case RET:
int rvalue = stack[sp--]; // pop return value
sp = fp; // jump over locals to fp which points at ret addr
ip = stack[sp--]; // pop return address, jump to it
fp = stack[sp--]; // restore fp
nargs = stack[sp--]; // how many args to throw away?
sp -= nargs; // pop args
stack[++sp] = rvalue; // leave result on stack
break;
case BR :
ip = code[ip++];
break;
case BRT :
addr = code[ip++];
if ( stack[sp--]==TRUE ) ip = addr;
break;
case BRF :
addr = code[ip++];
if ( stack[sp--]==FALSE ) ip = addr;
break;
case ICONST:
stack[++sp] = code[ip++]; // push operand
break;
case LOAD : // load local or arg; 1st local is fp+1, args are fp-3, fp-4, fp-5, ...
offset = code[ip++];
stack[++sp] = stack[fp+offset];
break;
case GLOAD :// load from global memory
addr = code[ip++];
stack[++sp] = globals[addr];
break;
case STORE :
offset = code[ip++];
stack[fp+offset] = stack[sp--];
break;
case GSTORE :
addr = code[ip++];
globals[addr] = stack[sp--];
break;
case PRINT :
System.out.println(stack[sp--]);
break;
case POP:
--sp;
break;
default :
throw new Error("invalid opcode: "+opcode+" at ip="+(ip-1));
}
opcode = code[ip];
}
}

protected void trace() {
int opcode = code[ip];
String opName = Bytecode.instructions[opcode].name;
System.out.printf("%04d:\t%-11s", ip, opName);
int nargs = Bytecode.instructions[opcode].n;
if ( nargs>0 ) {
List<String> operands = new ArrayList<String>();
for (int i=ip+1; i<=ip+nargs; i++) {
operands.add(String.valueOf(code[i]));
}
for (int i = 0; i < operands.size(); i++) {
String s = operands.get(i);
if ( i>0 ) System.out.print(", ");
System.out.print(s);
}
}
System.out.println();
}

}

0 comments on commit 9e2406e

Please sign in to comment.