Restructured the vm
This commit is contained in:
474
src/c0vm.js
474
src/c0vm.js
@@ -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
105
src/opcodes.js
Normal 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"
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user