Restructured the vm

This commit is contained in:
Mitchell Plamann
2015-03-22 23:32:13 -04:00
parent 3eb1346eef
commit e4a297a28d
2 changed files with 326 additions and 253 deletions

View File

@@ -1,275 +1,243 @@
stack = [] op = require("./opcodes");
pc = 0;
program = [];
variables = [];
function doIf(shouldJump) { var ProgramState = function(parsed_file) {
var main_function = parsed_file.function_pool[0];
this.stack = []
this.pc = 0;
this.program = main_function.code;
this.variables = [];
for (var i = 0; i < main_function.num_vars; i++)
variables.push(0);
this.file = parsed_file;
}
ProgramState.prototype.doIf = function(shouldJump) {
if (shouldJump) { if (shouldJump) {
var address_offset = (program[pc+1] * 0x1000) + program[pc+2]; var address_offset = (this.program[this.pc+1] * 0x1000) +
pc += address_offset; this.program[this.pc+2];
this.pc += address_offset;
} else { } else {
pc += 3; this.pc += 3;
} }
} }
// Takes in a parsed .bc0 file and runs it ProgramState.prototype.step = function() {
function execute(file) { console.log("Running opcode " + op.lookup_table[this.program[this.pc]]);
console.log("Beginning execution of file " + file); switch (this.program[this.pc]) {
main_function = file.function_pool[0]; // Stack manipulation
program = main_function.code; case op.POP:
for (var i = 0; i < main_function.num_vars; i++) this.pc++;
variables.push(0); this.stack.pop();
while (true) { break;
console.log("Running opcode " + program[pc].toString(16)); case op.DUP:
switch (program[pc]) { this.pc++;
// Stack manipulation var v = this.stack.pop();
case POP: this.stack.push(v);
pc++; this.stack.push(v);
stack.pop(); break;
break; case op.SWAP:
case DUP: this.pc++;
pc++; var v1 = this.stack.pop();
var v = stack.pop(); var v2 = this.stack.pop();
stack.push(v); this.stack.push(v1);
stack.push(v); this.stack.push(v2);
break; break;
case SWAP: case op.BIPUSH:
pc++; this.pc += 2;
var v1 = stack.pop(); var val = this.program[this.pc-1];
var v2 = stack.pop();
stack.push(v1);
stack.push(v2);
break;
case BIPUSH:
pc += 2;
var val = program[pc-1];
// Do sign extension if necessary // Do sign extension if necessary
if (val & 0x80 != 0) if (val & 0x80 != 0)
val = -0x80 + (val & 0x7F); val = -0x80 + (val & 0x7F);
stack.push(val); this.stack.push(val);
break; break;
// Returning from a function // Returning from a function
case RETURN: case op.RETURN:
var retVal = stack.pop(); var retVal = this.stack.pop();
return retVal; throw retVal;
// Arithmetic // Arithmetic
case IADD: case op.IADD:
pc++; this.pc++;
var y = stack.pop(); var y = this.stack.pop();
var x = stack.pop(); var x = this.stack.pop();
console.log("Adding " + x + " and " + y); console.log("Adding " + x + " and " + y);
stack.push((x+y) % 0x100000000); this.stack.push((x+y) % 0x100000000);
break; break;
case ISUB: case op.ISUB:
pc++; this.pc++;
var y = stack.pop(); var y = this.stack.pop();
var x = stack.pop(); var x = this.stack.pop();
stack.push((x-y) % 0x100000000); this.stack.push((x-y) % 0x100000000);
break; break;
case IMUL: case op.IMUL:
pc++; this.pc++;
var y = stack.pop(); var y = this.stack.pop();
var x = stack.pop(); var x = this.stack.pop();
stack.push((x*y) % 0x100000000); this.stack.push((x*y) % 0x100000000);
break; break;
case IDIV: case op.IDIV:
pc++; this.pc++;
var y = stack.pop(); var y = this.stack.pop();
var x = stack.pop(); var x = this.stack.pop();
if (y == 0) c0_arith_error("Divide by zero"); if (y == 0) c0_arith_error("Divide by zero");
if (x == INT_MIN && y == -1) c0_arith_error("Arithmetic overflow"); if (x == INT_MIN && y == -1) c0_arith_error("Arithmetic overflow");
// This does int division. // This does int division.
// As I understand it, the ~~ is treated as the identity on integers // As I understand it, the ~~ is treated as the identity on integers
// which forces the type to int, not float // which forces the type to int, not float
stack.push(~~(x/y)); this.stack.push(~~(x/y));
break; break;
case IREM: case op.IREM:
pc++; this.pc++;
var y = stack.pop(); var y = this.stack.pop();
var x = stack.pop(); var x = this.stack.pop();
if (y == 0) c0_arith_error("Divide by zero"); if (y == 0) c0_arith_error("Divide by zero");
if (x == INT_MIN && y == -1) c0_arith_error("Arithmetic overflow"); if (x == INT_MIN && y == -1) c0_arith_error("Arithmetic overflow");
stack.push(x%y); this.stack.push(x%y);
break; break;
case IAND: case op.IAND:
pc++; this.pc++;
var y = stack.pop(); var y = this.stack.pop();
var x = stack.pop(); var x = this.stack.pop();
stack.push(x&y); this.stack.push(x&y);
break; break;
case IOR: case op.IOR:
pc++; this.pc++;
var y = stack.pop(); var y = this.stack.pop();
var x = stack.pop(); var x = this.stack.pop();
stack.push(x|y); this.stack.push(x|y);
break; break;
case IXOR: case op.IXOR:
pc++; this.pc++;
var y = stack.pop(); var y = this.stack.pop();
var x = stack.pop(); var x = this.stack.pop();
stack.push(x^y); this.stack.push(x^y);
break; break;
case ISHL: case op.ISHL:
pc++; this.pc++;
var y = stack.pop(); var y = this.stack.pop();
var x = stack.pop(); var x = this.stack.pop();
if (y < 0 || y > 31) c0_arith_error("Shifting by too many bits"); if (y < 0 || y > 31) c0_arith_error("Shifting by too many bits");
stack.push(x<<y); this.stack.push(x<<y);
break; break;
case ISHR: case op.ISHR:
pc++; this.pc++;
var y = stack.pop(); var y = this.stack.pop();
var x = stack.pop(); var x = this.stack.pop();
if (y < 0 || y > 31) c0_arith_error("Shifting by too many bits"); if (y < 0 || y > 31) c0_arith_error("Shifting by too many bits");
stack.push(x>>y); this.stack.push(x>>y);
break; break;
// Operations on local variables // Operations on local variables
case VLOAD: case op.VLOAD:
pc += 2; this.pc += 2;
var index = program[pc-1]; var index = this.program[this.pc-1];
stack.push(variables[index]); this.stack.push(this.variables[index]);
break; break;
case VSTORE: case op.VSTORE:
pc +=2 ; this.pc += 2;
var index = program[pc-1]; var index = this.program[this.pc-1];
variables[index] = stack.pop(); this.variables[index] = this.stack.pop();
break; break;
case ACONST_NULL: case op.ACONST_NULL:
pc++; this.pc++;
stack.push(0); this.stack.push(0);
break; break;
case ILDC: case op.ILDC:
pc += 3; this.pc += 3;
var c1 = program[pc-2]; var c1 = this.program[this.pc-2];
var c2 = program[pc-1]; var c2 = this.program[this.pc-1];
var index = (c1 * 0x1000) + c2; var index = (c1 * 0x1000) + c2;
stack.push(file.int_pool[index]); this.stack.push(this.file.int_pool[index]);
break; break;
case ALDC: case op.ALDC:
console.log("Error: I don't know how to handle ALDC yet"); console.log("Error: I don't know how to handle ALDC yet");
return; throw "Error - can't handle ALDC";
// Control flow // Control flow
case NOP: case op.NOP:
pc++; this.pc++;
break; break;
case IF_CMPEQ: case op.IF_CMPEQ:
var y = stack.pop(); var y = this.stack.pop();
var x = stack.pop(); var x = this.stack.pop();
doIf(y == x); this.doIf(y == x);
break; break;
case IF_CMPNE: case op.IF_CMPNE:
var y = stack.pop(); var y = this.stack.pop();
var x = stack.pop(); var x = this.stack.pop();
doIf(y != x); this.doIf(y != x);
break; break;
case IF_CMPLT: case op.IF_CMPLT:
var y = stack.pop(); var y = this.stack.pop();
var x = stack.pop(); var x = this.stack.pop();
doIf(y > x); this.doIf(y > x);
break; break;
case IF_CMPGE: case op.IF_CMPGE:
var y = stack.pop(); var y = this.stack.pop();
var x = stack.pop(); var x = this.stack.pop();
doIf(y <= x); this.doIf(y <= x);
break; break;
case IF_CMPGT: case op.IF_CMPGT:
var y = stack.pop(); var y = this.stack.pop();
var x = stack.pop(); var x = this.stack.pop();
doIf(y < x); this.doIf(y < x);
break; break;
case IF_CMPLE: case op.IF_CMPLE:
var y = stack.pop(); var y = this.stack.pop();
var x = stack.pop(); var x = this.stack.pop();
doIf(y >= x); this.doIf(y >= x);
break; break;
case GOTO: case op.GOTO:
doIf(true); this.doIf(true);
break; break;
case ATHROW: case op.ATHROW:
pc++; this.pc++;
c0_user_error(stack.pop()); c0_user_error(this.stack.pop());
break; break;
case ASSERT: case op.ASSERT:
pc++; this.pc++;
var a = stack.pop(); var a = this.stack.pop();
if (stack.pop() == 0) if (this.stack.pop() == 0)
c0_assertion_failure(a); c0_assertion_failure(a);
break; break;
default: default:
console.log("Error: Unknown opcode\n"); console.log("Error: Unknown opcode\n");
return; throw "Error - unknown opcode";
}
} }
return false;
}
// Takes in a parsed .bc0 file and runs it
function execute(f) {
console.log("Beginning execution of file " + f);
var state = new ProgramState(f);
while (true) {
// I'm not sure how to structure this control flow yet,
// so if anyone has a better idea, let me know
try {
state.step();
} catch (val) {
return val;
}
// if (at_breakpoint) {
// save state (maybe in a global in this file?)
// return;
// }
}
} }
exports.execute = execute; exports.execute = execute;
// opcode definitions // opcode definitions
/* arithmetic operations */
IADD = 0x60;
IAND = 0x7E;
IDIV = 0x6C;
IMUL = 0x68;
IOR = 0x80;
IREM = 0x70;
ISHL = 0x78;
ISHR = 0x7A;
ISUB = 0x64;
IXOR = 0x82;
/* stack operations */
DUP = 0x59;
POP = 0x57;
SWAP = 0x5F;
/* memory allocation */
NEWARRAY = 0xBC;
ARRAYLENGTH = 0xBE;
NEW = 0xBB;
/* memory access */
AADDF = 0x62;
AADDS = 0x63;
IMLOAD = 0x2E;
AMLOAD = 0x2F;
IMSTORE = 0x4E;
AMSTORE = 0x4F;
CMLOAD = 0x34;
CMSTORE = 0x55;
/* local variables */
VLOAD = 0x15;
VSTORE = 0x36;
/* constants */
ACONST_NULL = 0x01;
BIPUSH = 0x10;
ILDC = 0x13;
ALDC = 0x14;
/* control flow */
NOP = 0x00;
IF_CMPEQ = 0x9F;
IF_CMPNE = 0xA0;
IF_ICMPLT = 0xA1;
IF_ICMPGE = 0xA2;
IF_ICMPGT = 0xA3;
IF_ICMPLE = 0xA4;
GOTO = 0xA7;
ATHROW = 0xBF;
ASSERT = 0xCF;
/* function calls and returns */
INVOKESTATIC = 0xB8;
INVOKENATIVE = 0xB7;
RETURN = 0xB0

105
src/opcodes.js Normal file
View File

@@ -0,0 +1,105 @@
/* arithmetic operations */
exports.IADD = 0x60;
exports.IAND = 0x7E;
exports.IDIV = 0x6C;
exports.IMUL = 0x68;
exports.IOR = 0x80;
exports.IREM = 0x70;
exports.ISHL = 0x78;
exports.ISHR = 0x7A;
exports.ISUB = 0x64;
exports.IXOR = 0x82;
/* stack operations */
exports.DUP = 0x59;
exports.POP = 0x57;
exports.SWAP = 0x5F;
/* memory allocation */
exports.NEWARRAY = 0xBC;
exports.ARRAYLENGTH = 0xBE;
exports.NEW = 0xBB;
/* memory access */
exports.AADDF = 0x62;
exports.AADDS = 0x63;
exports.IMLOAD = 0x2E;
exports.AMLOAD = 0x2F;
exports.IMSTORE = 0x4E;
exports.AMSTORE = 0x4F;
exports.CMLOAD = 0x34;
exports.CMSTORE = 0x55;
/* local variables */
exports.VLOAD = 0x15;
exports.VSTORE = 0x36;
/* constants */
exports.ACONST_NULL = 0x01;
exports.BIPUSH = 0x10;
exports.ILDC = 0x13;
exports.ALDC = 0x14;
/* control flow */
exports.NOP = 0x00;
exports.IF_CMPEQ = 0x9F;
exports.IF_CMPNE = 0xA0;
exports.IF_ICMPLT = 0xA1;
exports.IF_ICMPGE = 0xA2;
exports.IF_ICMPGT = 0xA3;
exports.IF_ICMPLE = 0xA4;
exports.GOTO = 0xA7;
exports.ATHROW = 0xBF;
exports.ASSERT = 0xCF;
/* function calls and returns */
exports.INVOKESTATIC = 0xB8;
exports.INVOKENATIVE = 0xB7;
exports.RETURN = 0xB0
exports.lookup_table = {
0x60: "IADD",
0x7E: "IAND",
0x6C: "IDIV",
0x68: "IMUL",
0x80: "IOR",
0x70: "IREM",
0x78: "ISHL",
0x7A: "ISHR",
0x64: "ISUB",
0x82: "IXOR",
0x59: "DUP",
0x57: "POP",
0x5F: "SWAP",
0xBC: "NEWARRAY",
0xBE: "ARRAYLENGTH",
0xBB: "NEW",
0x62: "AADDF",
0x63: "AADDS",
0x2E: "IMLOAD",
0x2F: "AMLOAD",
0x4E: "IMSTORE",
0x4F: "AMSTORE",
0x34: "CMLOAD",
0x55: "CMSTORE",
0x15: "VLOAD",
0x36: "VSTORE",
0x01: "ACONST",
0x10: "BIPUSH",
0x13: "ILDC",
0x14: "ALDC",
0x00: "NOP",
0x9F: "IF",
0xA0: "IF",
0xA1: "IF",
0xA2: "IF",
0xA3: "IF",
0xA4: "IF",
0xA7: "GOTO",
0xBF: "ATHROW",
0xCF: "ASSERT",
0xB8: "INVOKESTATIC",
0xB7: "INVOKENATIVE",
0xB0: "RETURN"
};