-
Notifications
You must be signed in to change notification settings - Fork 60
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
284 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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") | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} | ||
|
||
} |