Merge branch 'mplamann' into run_vm
Conflicts: public/vm/index.js
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
var ByteStream = function (byte_array) {
|
||||
console.log("Instance created.");
|
||||
this.byte_array = byte_array;
|
||||
this.index = 0;
|
||||
};
|
||||
|
||||
110
public/vm/c0ffi.js
Normal file
110
public/vm/c0ffi.js
Normal file
@@ -0,0 +1,110 @@
|
||||
exports.NATIVE_FADD = 0
|
||||
exports.NATIVE_FDIV = 1
|
||||
exports.NATIVE_FLESS = 2
|
||||
exports.NATIVE_FMUL = 3
|
||||
exports.NATIVE_FSUB = 4
|
||||
exports.NATIVE_FTOI = 5
|
||||
exports.NATIVE_ITOF = 6
|
||||
exports.NATIVE_PRINT_FPT = 7
|
||||
exports.NATIVE_PRINT_HEX = 8
|
||||
exports.NATIVE_PRINT_INT = 9
|
||||
|
||||
/* args */
|
||||
exports.NATIVE_ARGS_FLAG = 10
|
||||
exports.NATIVE_ARGS_INT = 11
|
||||
exports.NATIVE_ARGS_PARSE = 12
|
||||
exports.NATIVE_ARGS_STRING = 13
|
||||
|
||||
/* conio */
|
||||
exports.NATIVE_EOF = 14
|
||||
exports.NATIVE_FLUSH = 15
|
||||
exports.NATIVE_PRINT = 16
|
||||
exports.NATIVE_PRINTBOOL = 17
|
||||
exports.NATIVE_PRINTCHAR = 18
|
||||
exports.NATIVE_PRINTINT = 19
|
||||
exports.NATIVE_PRINTLN = 20
|
||||
exports.NATIVE_READLINE = 21
|
||||
|
||||
/* curses */
|
||||
exports.NATIVE_C_ADDCH = 22
|
||||
exports.NATIVE_C_CBREAK = 23
|
||||
exports.NATIVE_C_CURS_SET = 24
|
||||
exports.NATIVE_C_DELCH = 25
|
||||
exports.NATIVE_C_ENDWIN = 26
|
||||
exports.NATIVE_C_ERASE = 27
|
||||
exports.NATIVE_C_GETCH = 28
|
||||
exports.NATIVE_C_INITSCR = 29
|
||||
exports.NATIVE_C_KEYPAD = 30
|
||||
exports.NATIVE_C_MOVE = 31
|
||||
exports.NATIVE_C_NOECHO = 32
|
||||
exports.NATIVE_C_REFRESH = 33
|
||||
exports.NATIVE_C_SUBWIN = 34
|
||||
exports.NATIVE_C_WADDCH = 35
|
||||
exports.NATIVE_C_WADDSTR = 36
|
||||
exports.NATIVE_C_WCLEAR = 37
|
||||
exports.NATIVE_C_WERASE = 38
|
||||
exports.NATIVE_C_WMOVE = 39
|
||||
exports.NATIVE_C_WREFRESH = 40
|
||||
exports.NATIVE_C_WSTANDEND = 41
|
||||
exports.NATIVE_C_WSTANDOUT = 42
|
||||
exports.NATIVE_CC_GETBEGX = 43
|
||||
exports.NATIVE_CC_GETBEGY = 44
|
||||
exports.NATIVE_CC_GETMAXX = 45
|
||||
exports.NATIVE_CC_GETMAXY = 46
|
||||
exports.NATIVE_CC_GETX = 47
|
||||
exports.NATIVE_CC_GETY = 48
|
||||
exports.NATIVE_CC_HIGHLIGHT = 49
|
||||
exports.NATIVE_CC_KEY_IS_BACKSPACE = 50
|
||||
exports.NATIVE_CC_KEY_IS_DOWN = 51
|
||||
exports.NATIVE_CC_KEY_IS_ENTER = 52
|
||||
exports.NATIVE_CC_KEY_IS_LEFT = 53
|
||||
exports.NATIVE_CC_KEY_IS_RIGHT = 54
|
||||
exports.NATIVE_CC_KEY_IS_UP = 55
|
||||
exports.NATIVE_CC_WBOLDOFF = 56
|
||||
exports.NATIVE_CC_WBOLDON = 57
|
||||
exports.NATIVE_CC_WDIMOFF = 58
|
||||
exports.NATIVE_CC_WDIMON = 59
|
||||
exports.NATIVE_CC_WREVERSEOFF = 60
|
||||
exports.NATIVE_CC_WREVERSEON = 61
|
||||
exports.NATIVE_CC_WUNDEROFF = 62
|
||||
exports.NATIVE_CC_WUNDERON = 63
|
||||
|
||||
/* file */
|
||||
exports.NATIVE_FILE_CLOSE = 64
|
||||
exports.NATIVE_FILE_CLOSED = 65
|
||||
exports.NATIVE_FILE_EOF = 66
|
||||
exports.NATIVE_FILE_READ = 67
|
||||
exports.NATIVE_FILE_READLINE = 68
|
||||
|
||||
/* img */
|
||||
exports.NATIVE_IMAGE_CLONE = 69
|
||||
exports.NATIVE_IMAGE_CREATE = 70
|
||||
exports.NATIVE_IMAGE_DATA = 71
|
||||
exports.NATIVE_IMAGE_DESTROY = 72
|
||||
exports.NATIVE_IMAGE_HEIGHT = 73
|
||||
exports.NATIVE_IMAGE_LOAD = 74
|
||||
exports.NATIVE_IMAGE_SAVE = 75
|
||||
exports.NATIVE_IMAGE_SUBIMAGE = 76
|
||||
exports.NATIVE_IMAGE_WIDTH = 77
|
||||
|
||||
/* parse */
|
||||
exports.NATIVE_PARSE_BOOL = 78
|
||||
exports.NATIVE_PARSE_INT = 79
|
||||
|
||||
/* string */
|
||||
exports.NATIVE_CHAR_CHR = 80
|
||||
exports.NATIVE_CHAR_ORD = 81
|
||||
exports.NATIVE_STRING_CHARAT = 82
|
||||
exports.NATIVE_STRING_COMPARE = 83
|
||||
exports.NATIVE_STRING_EQUAL = 84
|
||||
exports.NATIVE_STRING_FROM_CHARARRAY = 85
|
||||
exports.NATIVE_STRING_FROMBOOL = 86
|
||||
exports.NATIVE_STRING_FROMCHAR = 87
|
||||
exports.NATIVE_STRING_FROMINT = 88
|
||||
exports.NATIVE_STRING_JOIN = 89
|
||||
exports.NATIVE_STRING_LENGTH = 90
|
||||
exports.NATIVE_STRING_SUB = 91
|
||||
exports.NATIVE_STRING_TERMINATED = 92
|
||||
exports.NATIVE_STRING_TO_CHARARRAY = 93
|
||||
exports.NATIVE_STRING_TOLOWER = 94
|
||||
|
||||
@@ -1,234 +1,464 @@
|
||||
op = require("./opcodes");
|
||||
|
||||
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;
|
||||
var INT_MIN = 0x80000000;
|
||||
var INT_MAX = 0x7FFFFFFF;
|
||||
|
||||
var verbose = false;
|
||||
function log(message) {
|
||||
if (verbose) console.log(message);
|
||||
}
|
||||
|
||||
function c0_assertion_failure(val) {
|
||||
throw ("c0 assertion failure: " + val);
|
||||
}
|
||||
|
||||
ProgramState.prototype.doIf = function(shouldJump) {
|
||||
if (shouldJump) {
|
||||
var address_offset = (this.program[this.pc+1] * 0x1000) +
|
||||
this.program[this.pc+2];
|
||||
this.pc += address_offset;
|
||||
function c0_memory_error(val) {
|
||||
throw ("c0 memory error: " + val);
|
||||
}
|
||||
|
||||
function i32_to_array(i32) {
|
||||
return [(i32 & 0xFF),
|
||||
((i32 >> 8) & 0xFF),
|
||||
((i32 >> 16) & 0xFF),
|
||||
((i32 >> 24) & 0xFF)];
|
||||
|
||||
}
|
||||
|
||||
function array_to_i32(array) {
|
||||
return array[0] +
|
||||
(array[1] << 8) +
|
||||
(array[2] << 16) +
|
||||
(array[3] << 24);
|
||||
}
|
||||
|
||||
var StackFrame = function(file, f) {
|
||||
log("Creating stack frame");
|
||||
this.stack = [];
|
||||
this.pc = 0;
|
||||
this.program = f.code;
|
||||
this.variables = [];
|
||||
for (var i = 0; i < f.num_vars; i++)
|
||||
this.variables.push(0);
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
var ProgramState = function(parsed_file, callback_dict) {
|
||||
log("Creating program state with file " + parsed_file);
|
||||
var main_function = parsed_file.function_pool[0];
|
||||
|
||||
this.frame = new StackFrame(parsed_file, parsed_file.function_pool[0]);
|
||||
this.call_stack = [];
|
||||
this.file = parsed_file;
|
||||
|
||||
this.natives = {};
|
||||
|
||||
for (var i = 0; i < 95; i++) {
|
||||
try {
|
||||
this.natives[i] = callback_dict[i];
|
||||
} catch (key_not_found) {
|
||||
this.natives[i] = function (arg) {
|
||||
console.log("Native function " + name + " called, ran method stub.");
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Memory is just a big array of bytes, right?
|
||||
// "Allocation" is appending onto this array
|
||||
// A pointer to memory is an index into this array.
|
||||
|
||||
// Structs are stored as themselves
|
||||
// Arrays are stored as an entry for the number of elements
|
||||
// and then the array, byte-by-byte
|
||||
this.heap = [];
|
||||
}
|
||||
|
||||
ProgramState.prototype.push = function(val) {
|
||||
this.frame.stack.push(val);
|
||||
}
|
||||
|
||||
ProgramState.prototype.pop = function() {
|
||||
return this.frame.stack.pop();
|
||||
}
|
||||
|
||||
ProgramState.prototype.goto_offset = function() {
|
||||
var c1 = this.frame.program[this.frame.pc+1];
|
||||
var c2 = this.frame.program[this.frame.pc+2]
|
||||
|
||||
var address_offset = (c1 << 8) + c2;
|
||||
// Handle negative values
|
||||
if ((address_offset & 0x8000) != 0)
|
||||
address_offset = -0x8000 + (address_offset & 0x7FFF);
|
||||
|
||||
this.frame.pc += address_offset;
|
||||
}
|
||||
|
||||
ProgramState.prototype.doIf = function(f) {
|
||||
var y = this.pop();
|
||||
var x = this.pop();
|
||||
if (f(x,y)) {
|
||||
this.goto_offset();
|
||||
} else {
|
||||
this.pc += 3;
|
||||
this.frame.pc += 3;
|
||||
}
|
||||
}
|
||||
|
||||
ProgramState.prototype.step = function() {
|
||||
console.log("Running opcode " + op.lookup_table[this.program[this.pc]]);
|
||||
switch (this.program[this.pc]) {
|
||||
var opcode = this.frame.program[this.frame.pc]
|
||||
log("0x" + this.frame.pc.toString(16) + " Running opcode " +
|
||||
op.lookup_table[opcode]);
|
||||
switch (opcode) {
|
||||
// Stack manipulation
|
||||
case op.POP:
|
||||
this.pc++;
|
||||
this.stack.pop();
|
||||
this.frame.pc++;
|
||||
this.pop();
|
||||
break;
|
||||
case op.DUP:
|
||||
this.pc++;
|
||||
var v = this.stack.pop();
|
||||
this.stack.push(v);
|
||||
this.stack.push(v);
|
||||
this.frame.pc++;
|
||||
var v = this.pop();
|
||||
this.push(v);
|
||||
this.push(v);
|
||||
break;
|
||||
case op.SWAP:
|
||||
this.pc++;
|
||||
var v1 = this.stack.pop();
|
||||
var v2 = this.stack.pop();
|
||||
this.stack.push(v1);
|
||||
this.stack.push(v2);
|
||||
this.frame.pc++;
|
||||
var v1 = this.pop();
|
||||
var v2 = this.pop();
|
||||
this.push(v1);
|
||||
this.push(v2);
|
||||
break;
|
||||
case op.BIPUSH:
|
||||
this.pc += 2;
|
||||
var val = this.program[this.pc-1];
|
||||
|
||||
this.frame.pc += 2;
|
||||
var val = this.frame.program[this.frame.pc-1];
|
||||
|
||||
// Do sign extension if necessary
|
||||
if (val & 0x80 != 0)
|
||||
if ((val & 0x80) != 0)
|
||||
val = -0x80 + (val & 0x7F);
|
||||
this.stack.push(val);
|
||||
this.push(val);
|
||||
break;
|
||||
|
||||
// Returning from a function
|
||||
case op.RETURN:
|
||||
var retVal = this.stack.pop();
|
||||
throw retVal;
|
||||
var retVal = this.pop();
|
||||
if (this.call_stack.length == 0)
|
||||
return retVal;
|
||||
|
||||
this.frame = this.call_stack.pop();
|
||||
this.push(retVal);
|
||||
|
||||
break;
|
||||
// Arithmetic
|
||||
case op.IADD:
|
||||
this.pc++;
|
||||
var y = this.stack.pop();
|
||||
var x = this.stack.pop();
|
||||
console.log("Adding " + x + " and " + y);
|
||||
this.stack.push((x+y) % 0x100000000);
|
||||
this.frame.pc++;
|
||||
var y = this.pop();
|
||||
var x = this.pop();
|
||||
log("Adding " + x + " and " + y);
|
||||
this.push((x+y) % 0x100000000);
|
||||
break;
|
||||
case op.ISUB:
|
||||
this.pc++;
|
||||
var y = this.stack.pop();
|
||||
var x = this.stack.pop();
|
||||
this.stack.push((x-y) % 0x100000000);
|
||||
this.frame.pc++;
|
||||
var y = this.pop();
|
||||
var x = this.pop();
|
||||
this.push((x-y) % 0x100000000);
|
||||
break;
|
||||
case op.IMUL:
|
||||
this.pc++;
|
||||
var y = this.stack.pop();
|
||||
var x = this.stack.pop();
|
||||
this.stack.push((x*y) % 0x100000000);
|
||||
this.frame.pc++;
|
||||
var y = this.pop();
|
||||
var x = this.pop();
|
||||
this.push((x*y) % 0x100000000);
|
||||
break;
|
||||
case op.IDIV:
|
||||
this.pc++;
|
||||
var y = this.stack.pop();
|
||||
var x = this.stack.pop();
|
||||
this.frame.pc++;
|
||||
var y = this.pop();
|
||||
var x = this.pop();
|
||||
if (y == 0) c0_arith_error("Divide by zero");
|
||||
if (x == INT_MIN && y == -1) c0_arith_error("Arithmetic overflow");
|
||||
|
||||
// This does int division.
|
||||
// As I understand it, the ~~ is treated as the identity on integers
|
||||
// which forces the type to int, not float
|
||||
this.stack.push(~~(x/y));
|
||||
this.push(~~(x/y));
|
||||
break;
|
||||
case op.IREM:
|
||||
this.pc++;
|
||||
var y = this.stack.pop();
|
||||
var x = this.stack.pop();
|
||||
this.frame.pc++;
|
||||
var y = this.pop();
|
||||
var x = this.pop();
|
||||
if (y == 0) c0_arith_error("Divide by zero");
|
||||
if (x == INT_MIN && y == -1) c0_arith_error("Arithmetic overflow");
|
||||
this.stack.push(x%y);
|
||||
this.push(x%y);
|
||||
break;
|
||||
case op.IAND:
|
||||
this.pc++;
|
||||
var y = this.stack.pop();
|
||||
var x = this.stack.pop();
|
||||
this.stack.push(x&y);
|
||||
this.frame.pc++;
|
||||
var y = this.pop();
|
||||
var x = this.pop();
|
||||
this.push(x&y);
|
||||
break;
|
||||
case op.IOR:
|
||||
this.pc++;
|
||||
var y = this.stack.pop();
|
||||
var x = this.stack.pop();
|
||||
this.stack.push(x|y);
|
||||
this.frame.pc++;
|
||||
var y = this.pop();
|
||||
var x = this.pop();
|
||||
this.push(x|y);
|
||||
break;
|
||||
case op.IXOR:
|
||||
this.pc++;
|
||||
var y = this.stack.pop();
|
||||
var x = this.stack.pop();
|
||||
this.stack.push(x^y);
|
||||
this.frame.pc++;
|
||||
var y = this.pop();
|
||||
var x = this.pop();
|
||||
this.push(x^y);
|
||||
break;
|
||||
case op.ISHL:
|
||||
this.pc++;
|
||||
var y = this.stack.pop();
|
||||
var x = this.stack.pop();
|
||||
this.frame.pc++;
|
||||
var y = this.pop();
|
||||
var x = this.pop();
|
||||
if (y < 0 || y > 31) c0_arith_error("Shifting by too many bits");
|
||||
this.stack.push(x<<y);
|
||||
this.push(x<<y);
|
||||
break;
|
||||
case op.ISHR:
|
||||
this.pc++;
|
||||
var y = this.stack.pop();
|
||||
var x = this.stack.pop();
|
||||
this.frame.pc++;
|
||||
var y = this.pop();
|
||||
var x = this.pop();
|
||||
if (y < 0 || y > 31) c0_arith_error("Shifting by too many bits");
|
||||
this.stack.push(x>>y);
|
||||
this.push(x>>y);
|
||||
break;
|
||||
|
||||
// Operations on local variables
|
||||
|
||||
case op.VLOAD:
|
||||
this.pc += 2;
|
||||
var index = this.program[this.pc-1];
|
||||
this.stack.push(this.variables[index]);
|
||||
this.frame.pc += 2;
|
||||
var index = this.frame.program[this.frame.pc-1];
|
||||
this.push(this.frame.variables[index]);
|
||||
break;
|
||||
case op.VSTORE:
|
||||
this.pc += 2;
|
||||
var index = this.program[this.pc-1];
|
||||
this.variables[index] = this.stack.pop();
|
||||
this.frame.pc += 2;
|
||||
var index = this.frame.program[this.frame.pc-1];
|
||||
this.frame.variables[index] = this.pop();
|
||||
break;
|
||||
case op.ACONST_NULL:
|
||||
this.pc++;
|
||||
this.stack.push(0);
|
||||
this.frame.pc++;
|
||||
this.push(0);
|
||||
break;
|
||||
case op.ILDC:
|
||||
this.pc += 3;
|
||||
var c1 = this.program[this.pc-2];
|
||||
var c2 = this.program[this.pc-1];
|
||||
this.frame.pc += 3;
|
||||
var c1 = this.frame.program[this.frame.pc-2];
|
||||
var c2 = this.frame.program[this.frame.pc-1];
|
||||
var index = (c1 * 0x1000) + c2;
|
||||
|
||||
this.stack.push(this.file.int_pool[index]);
|
||||
this.push(this.file.int_pool[index]);
|
||||
break;
|
||||
case op.ALDC:
|
||||
console.log("Error: I don't know how to handle ALDC yet");
|
||||
throw "Error - can't handle ALDC";
|
||||
this.frame.pc += 3;
|
||||
var c1 = this.frame.program[this.frame.pc-2];
|
||||
var c2 = this.frame.program[this.frame.pc-1];
|
||||
var index = (c1 * 0x1000) + c2;
|
||||
|
||||
this.push(this.file.string_pool[index]);
|
||||
break;
|
||||
|
||||
// Control flow
|
||||
case op.NOP:
|
||||
this.pc++;
|
||||
this.frame.pc++;
|
||||
break;
|
||||
case op.IF_CMPEQ:
|
||||
var y = this.stack.pop();
|
||||
var x = this.stack.pop();
|
||||
this.doIf(y == x);
|
||||
this.doIf(function (x,y) {return y == x;});
|
||||
break;
|
||||
case op.IF_CMPNE:
|
||||
var y = this.stack.pop();
|
||||
var x = this.stack.pop();
|
||||
this.doIf(y != x);
|
||||
this.doIf(function (x,y) {return y != x;});
|
||||
break;
|
||||
case op.IF_CMPLT:
|
||||
var y = this.stack.pop();
|
||||
var x = this.stack.pop();
|
||||
this.doIf(y > x);
|
||||
case op.IF_ICMPLT:
|
||||
this.doIf(function (x,y) {return y > x;});
|
||||
break;
|
||||
case op.IF_CMPGE:
|
||||
var y = this.stack.pop();
|
||||
var x = this.stack.pop();
|
||||
this.doIf(y <= x);
|
||||
case op.IF_ICMPGE:
|
||||
this.doIf(function (x,y) {return y <= x;});
|
||||
break;
|
||||
case op.IF_CMPGT:
|
||||
var y = this.stack.pop();
|
||||
var x = this.stack.pop();
|
||||
this.doIf(y < x);
|
||||
case op.IF_ICMPGT:
|
||||
this.doIf(function (x,y) {return y < x;});
|
||||
break;
|
||||
case op.IF_CMPLE:
|
||||
var y = this.stack.pop();
|
||||
var x = this.stack.pop();
|
||||
this.doIf(y >= x);
|
||||
case op.IF_ICMPLE:
|
||||
this.doIf(function (x,y) {return y >= x;});
|
||||
break;
|
||||
case op.GOTO:
|
||||
this.doIf(true);
|
||||
this.goto_offset();
|
||||
break;
|
||||
case op.ATHROW:
|
||||
this.pc++;
|
||||
c0_user_error(this.stack.pop());
|
||||
this.frame.pc++;
|
||||
c0_user_error(this.pop());
|
||||
break;
|
||||
case op.ASSERT:
|
||||
this.pc++;
|
||||
var a = this.stack.pop();
|
||||
if (this.stack.pop() == 0)
|
||||
this.frame.pc++;
|
||||
var a = this.pop();
|
||||
if (this.pop() == 0)
|
||||
c0_assertion_failure(a);
|
||||
break;
|
||||
|
||||
// Function call operations
|
||||
|
||||
case op.INVOKESTATIC:
|
||||
var c1 = this.frame.program[this.frame.pc+1];
|
||||
var c2 = this.frame.program[this.frame.pc+2];
|
||||
this.frame.pc += 3;
|
||||
|
||||
var index = (c1 << 8) + c2;
|
||||
|
||||
var f = this.file.function_pool[index];
|
||||
var newFrame = new StackFrame(this.file, f);
|
||||
for (var i = f.num_args - 1; i >= 0; i--) {
|
||||
newFrame.variables[i] = this.pop();
|
||||
}
|
||||
|
||||
this.call_stack.push(this.frame);
|
||||
this.frame = newFrame;
|
||||
break;
|
||||
|
||||
case op.INVOKENATIVE:
|
||||
var c1 = this.frame.program[this.frame.pc+1];
|
||||
var c2 = this.frame.program[this.frame.pc+2];
|
||||
this.frame.pc += 3;
|
||||
|
||||
var index = (c1 << 8) + c2;
|
||||
|
||||
var f = this.file.native_pool[index];
|
||||
var arg_array = [];
|
||||
for (var i = f.num_args - 1; i >= 0; i--)
|
||||
arg_array[i] = this.pop();
|
||||
|
||||
var native_function = this.natives[f.function_table_index];
|
||||
if (native_function === undefined) {
|
||||
native_function = function (ignored) {
|
||||
console.log("Could not find native function with index " +
|
||||
f.function_table_index);
|
||||
return 0;
|
||||
};
|
||||
console.log("Unknown native function index " + f.function_table_index);
|
||||
}
|
||||
this.push(native_function(arg_array));
|
||||
break;
|
||||
|
||||
// Memory allocation operations:
|
||||
|
||||
case op.NEW:
|
||||
var size = this.frame.program[this.frame.pc+1];
|
||||
var address = this.heap.length;
|
||||
|
||||
for (var i = 0; i < size; i++) this.heap.push(0);
|
||||
|
||||
this.push(address);
|
||||
this.frame.pc += 2;
|
||||
break;
|
||||
|
||||
case op.NEWARRAY:
|
||||
var size = this.frame.program[this.frame.pc+1];
|
||||
var address = this.heap.length;
|
||||
var num_elements = this.pop();
|
||||
if (num_elements < 0) c0_memory_error("Array size must be nonnegative");
|
||||
|
||||
this.heap.push(num_elements);
|
||||
this.heap.push(size);
|
||||
|
||||
for (var i = 0; i < num_elements; i++) {
|
||||
for (var j = 0; j < size; j++)
|
||||
this.heap.push(0);
|
||||
}
|
||||
|
||||
this.push(address);
|
||||
this.frame.pc += 2;
|
||||
break;
|
||||
|
||||
case op.ARRAYLENGTH:
|
||||
var pointer = this.pop();
|
||||
this.push(this.heap[pointer]);
|
||||
break;
|
||||
|
||||
// Memory access operations:
|
||||
|
||||
case op.AADDF:
|
||||
// Read offset into a struct
|
||||
var offset = this.frame.program[this.frame.pc + 1];
|
||||
var index = this.pop();
|
||||
this.push(this.heap[index + offset]);
|
||||
this.frame.pc += 2;
|
||||
break;
|
||||
|
||||
case op.AADDS:
|
||||
// Read offset into an array
|
||||
var elt_index = this.pop();
|
||||
var index = this.pop();
|
||||
var array_length = this.heap[index];
|
||||
var elt_size = this.heap[index+1];
|
||||
if (elt_index >= array_length) c0_memory_error("Array index out of bounds.");
|
||||
this.push(this.heap[index + 2 + elt_size*elt_index]);
|
||||
this.frame.pc++;
|
||||
break;
|
||||
|
||||
case op.IMLOAD:
|
||||
var addr = this.pop();
|
||||
// Get int32 from bytes
|
||||
var c1 = this.heap[addr];
|
||||
var c2 = this.heap[addr+1];
|
||||
var c3 = this.heap[addr+2];
|
||||
var c4 = this.heap[addr+3];
|
||||
var combined = array_to_i32([c1, c2, c3, c4]);
|
||||
this.push(combined);
|
||||
this.frame.pc++;
|
||||
break;
|
||||
|
||||
case op.IMSTORE:
|
||||
var value = this.pop();
|
||||
var addr = this.pop();
|
||||
var array = i32_to_array(value);
|
||||
|
||||
for (var i = 0; i < 4; i++)
|
||||
this.heap[addr + i] = array[i];
|
||||
this.frame.pc++;
|
||||
|
||||
case op.AMLOAD:
|
||||
var addr = this.pop();
|
||||
this.push(this.heap[addr]);
|
||||
this.frame.pc++;
|
||||
break;
|
||||
|
||||
case op.AMSTORE:
|
||||
var value = this.pop();
|
||||
var addr = this.pop();
|
||||
this.heap[addr] = value;
|
||||
this.frame.pc++;
|
||||
break;
|
||||
|
||||
case op.CMLOAD:
|
||||
var addr = this.pop();
|
||||
this.push(this.heap[addr]);
|
||||
this.frame.pc++;
|
||||
break;
|
||||
|
||||
case op.CMSTORE:
|
||||
var value = this.pop();
|
||||
var addr = this.pop();
|
||||
this.heap[addr] = value;
|
||||
this.frame.pc++;
|
||||
break;
|
||||
|
||||
default:
|
||||
console.log("Error: Unknown opcode\n");
|
||||
var opcode_name;
|
||||
try {
|
||||
opcode_name = op.lookup_table[opcode];
|
||||
} catch (ignored) {
|
||||
opcode_name = "UNKNOWN";
|
||||
}
|
||||
console.log("Error: Unknown opcode: 0x" + opcode.toString(16) +
|
||||
" (" + opcode_name + ")\n");
|
||||
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);
|
||||
function execute(file, callbacks, v) {
|
||||
verbose = typeof v !== 'undefined' ? v : true;
|
||||
log("Initializing with file " + file);
|
||||
|
||||
var state = new ProgramState(f);
|
||||
var state = new ProgramState(file, callbacks);
|
||||
|
||||
log("Beginning execution");
|
||||
|
||||
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;
|
||||
}
|
||||
var val = state.step();
|
||||
if (val !== undefined) return val;
|
||||
|
||||
// if (at_breakpoint) {
|
||||
// save state (maybe in a global in this file?)
|
||||
@@ -238,6 +468,3 @@ function execute(f) {
|
||||
}
|
||||
|
||||
exports.execute = execute;
|
||||
|
||||
// opcode definitions
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
parser = require("./bytecode-parser");
|
||||
c0vm = require("./c0vm.js");
|
||||
c0ffi = require("./c0ffi.js");
|
||||
|
||||
// console.log("Reading in sample bytecode file:");
|
||||
// console.log(parser.getBytes("../test/test.bc0"));
|
||||
@@ -9,8 +10,28 @@ c0vm = require("./c0vm.js");
|
||||
// console.log(file);
|
||||
// console.log(file.function_pool[0].code);
|
||||
|
||||
// UI interaction functions
|
||||
|
||||
function print(arg) {
|
||||
$("#output").append(arg);
|
||||
}
|
||||
|
||||
callbacks = {};
|
||||
callbacks[c0ffi.NATIVE_PRINT] = function(args) {
|
||||
print(args[0]);
|
||||
print("<br />");
|
||||
}
|
||||
|
||||
callbacks[c0ffi.NATIVE_PRINTINT] = function(args) {
|
||||
print(args[0]);
|
||||
print("<br />");
|
||||
}
|
||||
|
||||
console.log(callbacks);
|
||||
|
||||
$("#run").click(function() {
|
||||
var input = $("#bytecode").html().replace(/(\r\n|\n|\r)/gm,"");
|
||||
var file = parser.parse($("#bytecode").text());
|
||||
$("#output").text(c0vm.execute(file));
|
||||
print("<br />");
|
||||
print(c0vm.execute(file, callbacks));
|
||||
});
|
||||
|
||||
@@ -90,12 +90,12 @@ exports.lookup_table = {
|
||||
0x13: "ILDC",
|
||||
0x14: "ALDC",
|
||||
0x00: "NOP",
|
||||
0x9F: "IF",
|
||||
0xA0: "IF",
|
||||
0xA1: "IF",
|
||||
0xA2: "IF",
|
||||
0xA3: "IF",
|
||||
0xA4: "IF",
|
||||
0x9F: "IF_CMPEQ",
|
||||
0xA0: "IF_CMPNE",
|
||||
0xA1: "IF_ICMPLT",
|
||||
0xA2: "IF_ICMPGE",
|
||||
0xA3: "IF_ICMPGT",
|
||||
0xA4: "IF_ICMPLE",
|
||||
0xA7: "GOTO",
|
||||
0xBF: "ATHROW",
|
||||
0xCF: "ASSERT",
|
||||
|
||||
655
public/vm/vm.js
655
public/vm/vm.js
@@ -1,6 +1,5 @@
|
||||
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
|
||||
var ByteStream = function (byte_array) {
|
||||
console.log("Instance created.");
|
||||
this.byte_array = byte_array;
|
||||
this.index = 0;
|
||||
};
|
||||
@@ -147,238 +146,580 @@ function parse(filename) {
|
||||
exports.getBytes = getBytes;
|
||||
exports.parse = parse;
|
||||
|
||||
},{"./byte-stream":1,"fs":6}],3:[function(require,module,exports){
|
||||
},{"./byte-stream":1,"fs":7}],3:[function(require,module,exports){
|
||||
exports.NATIVE_FADD = 0
|
||||
exports.NATIVE_FDIV = 1
|
||||
exports.NATIVE_FLESS = 2
|
||||
exports.NATIVE_FMUL = 3
|
||||
exports.NATIVE_FSUB = 4
|
||||
exports.NATIVE_FTOI = 5
|
||||
exports.NATIVE_ITOF = 6
|
||||
exports.NATIVE_PRINT_FPT = 7
|
||||
exports.NATIVE_PRINT_HEX = 8
|
||||
exports.NATIVE_PRINT_INT = 9
|
||||
|
||||
/* args */
|
||||
exports.NATIVE_ARGS_FLAG = 10
|
||||
exports.NATIVE_ARGS_INT = 11
|
||||
exports.NATIVE_ARGS_PARSE = 12
|
||||
exports.NATIVE_ARGS_STRING = 13
|
||||
|
||||
/* conio */
|
||||
exports.NATIVE_EOF = 14
|
||||
exports.NATIVE_FLUSH = 15
|
||||
exports.NATIVE_PRINT = 16
|
||||
exports.NATIVE_PRINTBOOL = 17
|
||||
exports.NATIVE_PRINTCHAR = 18
|
||||
exports.NATIVE_PRINTINT = 19
|
||||
exports.NATIVE_PRINTLN = 20
|
||||
exports.NATIVE_READLINE = 21
|
||||
|
||||
/* curses */
|
||||
exports.NATIVE_C_ADDCH = 22
|
||||
exports.NATIVE_C_CBREAK = 23
|
||||
exports.NATIVE_C_CURS_SET = 24
|
||||
exports.NATIVE_C_DELCH = 25
|
||||
exports.NATIVE_C_ENDWIN = 26
|
||||
exports.NATIVE_C_ERASE = 27
|
||||
exports.NATIVE_C_GETCH = 28
|
||||
exports.NATIVE_C_INITSCR = 29
|
||||
exports.NATIVE_C_KEYPAD = 30
|
||||
exports.NATIVE_C_MOVE = 31
|
||||
exports.NATIVE_C_NOECHO = 32
|
||||
exports.NATIVE_C_REFRESH = 33
|
||||
exports.NATIVE_C_SUBWIN = 34
|
||||
exports.NATIVE_C_WADDCH = 35
|
||||
exports.NATIVE_C_WADDSTR = 36
|
||||
exports.NATIVE_C_WCLEAR = 37
|
||||
exports.NATIVE_C_WERASE = 38
|
||||
exports.NATIVE_C_WMOVE = 39
|
||||
exports.NATIVE_C_WREFRESH = 40
|
||||
exports.NATIVE_C_WSTANDEND = 41
|
||||
exports.NATIVE_C_WSTANDOUT = 42
|
||||
exports.NATIVE_CC_GETBEGX = 43
|
||||
exports.NATIVE_CC_GETBEGY = 44
|
||||
exports.NATIVE_CC_GETMAXX = 45
|
||||
exports.NATIVE_CC_GETMAXY = 46
|
||||
exports.NATIVE_CC_GETX = 47
|
||||
exports.NATIVE_CC_GETY = 48
|
||||
exports.NATIVE_CC_HIGHLIGHT = 49
|
||||
exports.NATIVE_CC_KEY_IS_BACKSPACE = 50
|
||||
exports.NATIVE_CC_KEY_IS_DOWN = 51
|
||||
exports.NATIVE_CC_KEY_IS_ENTER = 52
|
||||
exports.NATIVE_CC_KEY_IS_LEFT = 53
|
||||
exports.NATIVE_CC_KEY_IS_RIGHT = 54
|
||||
exports.NATIVE_CC_KEY_IS_UP = 55
|
||||
exports.NATIVE_CC_WBOLDOFF = 56
|
||||
exports.NATIVE_CC_WBOLDON = 57
|
||||
exports.NATIVE_CC_WDIMOFF = 58
|
||||
exports.NATIVE_CC_WDIMON = 59
|
||||
exports.NATIVE_CC_WREVERSEOFF = 60
|
||||
exports.NATIVE_CC_WREVERSEON = 61
|
||||
exports.NATIVE_CC_WUNDEROFF = 62
|
||||
exports.NATIVE_CC_WUNDERON = 63
|
||||
|
||||
/* file */
|
||||
exports.NATIVE_FILE_CLOSE = 64
|
||||
exports.NATIVE_FILE_CLOSED = 65
|
||||
exports.NATIVE_FILE_EOF = 66
|
||||
exports.NATIVE_FILE_READ = 67
|
||||
exports.NATIVE_FILE_READLINE = 68
|
||||
|
||||
/* img */
|
||||
exports.NATIVE_IMAGE_CLONE = 69
|
||||
exports.NATIVE_IMAGE_CREATE = 70
|
||||
exports.NATIVE_IMAGE_DATA = 71
|
||||
exports.NATIVE_IMAGE_DESTROY = 72
|
||||
exports.NATIVE_IMAGE_HEIGHT = 73
|
||||
exports.NATIVE_IMAGE_LOAD = 74
|
||||
exports.NATIVE_IMAGE_SAVE = 75
|
||||
exports.NATIVE_IMAGE_SUBIMAGE = 76
|
||||
exports.NATIVE_IMAGE_WIDTH = 77
|
||||
|
||||
/* parse */
|
||||
exports.NATIVE_PARSE_BOOL = 78
|
||||
exports.NATIVE_PARSE_INT = 79
|
||||
|
||||
/* string */
|
||||
exports.NATIVE_CHAR_CHR = 80
|
||||
exports.NATIVE_CHAR_ORD = 81
|
||||
exports.NATIVE_STRING_CHARAT = 82
|
||||
exports.NATIVE_STRING_COMPARE = 83
|
||||
exports.NATIVE_STRING_EQUAL = 84
|
||||
exports.NATIVE_STRING_FROM_CHARARRAY = 85
|
||||
exports.NATIVE_STRING_FROMBOOL = 86
|
||||
exports.NATIVE_STRING_FROMCHAR = 87
|
||||
exports.NATIVE_STRING_FROMINT = 88
|
||||
exports.NATIVE_STRING_JOIN = 89
|
||||
exports.NATIVE_STRING_LENGTH = 90
|
||||
exports.NATIVE_STRING_SUB = 91
|
||||
exports.NATIVE_STRING_TERMINATED = 92
|
||||
exports.NATIVE_STRING_TO_CHARARRAY = 93
|
||||
exports.NATIVE_STRING_TOLOWER = 94
|
||||
|
||||
|
||||
},{}],4:[function(require,module,exports){
|
||||
op = require("./opcodes");
|
||||
|
||||
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;
|
||||
var INT_MIN = 0x80000000;
|
||||
var INT_MAX = 0x7FFFFFFF;
|
||||
|
||||
var verbose = false;
|
||||
function log(message) {
|
||||
if (verbose) console.log(message);
|
||||
}
|
||||
|
||||
function c0_assertion_failure(val) {
|
||||
throw ("c0 assertion failure: " + val);
|
||||
}
|
||||
|
||||
ProgramState.prototype.doIf = function(shouldJump) {
|
||||
if (shouldJump) {
|
||||
var address_offset = (this.program[this.pc+1] * 0x1000) +
|
||||
this.program[this.pc+2];
|
||||
this.pc += address_offset;
|
||||
function c0_memory_error(val) {
|
||||
throw ("c0 memory error: " + val);
|
||||
}
|
||||
|
||||
function i32_to_array(i32) {
|
||||
return [(i32 & 0xFF),
|
||||
((i32 >> 8) & 0xFF),
|
||||
((i32 >> 16) & 0xFF),
|
||||
((i32 >> 24) & 0xFF)];
|
||||
|
||||
}
|
||||
|
||||
function array_to_i32(array) {
|
||||
return array[0] +
|
||||
(array[1] << 8) +
|
||||
(array[2] << 16) +
|
||||
(array[3] << 24);
|
||||
}
|
||||
|
||||
var StackFrame = function(file, f) {
|
||||
log("Creating stack frame");
|
||||
this.stack = [];
|
||||
this.pc = 0;
|
||||
this.program = f.code;
|
||||
this.variables = [];
|
||||
for (var i = 0; i < f.num_vars; i++)
|
||||
this.variables.push(0);
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
var ProgramState = function(parsed_file, callback_dict) {
|
||||
log("Creating program state with file " + parsed_file);
|
||||
var main_function = parsed_file.function_pool[0];
|
||||
|
||||
this.frame = new StackFrame(parsed_file, parsed_file.function_pool[0]);
|
||||
this.call_stack = [];
|
||||
this.file = parsed_file;
|
||||
|
||||
this.natives = {};
|
||||
|
||||
for (var i = 0; i < 95; i++) {
|
||||
try {
|
||||
this.natives[i] = callback_dict[i];
|
||||
} catch (key_not_found) {
|
||||
this.natives[i] = function (arg) {
|
||||
console.log("Native function " + name + " called, ran method stub.");
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Memory is just a big array of bytes, right?
|
||||
// "Allocation" is appending onto this array
|
||||
// A pointer to memory is an index into this array.
|
||||
|
||||
// Structs are stored as themselves
|
||||
// Arrays are stored as an entry for the number of elements
|
||||
// and then the array, byte-by-byte
|
||||
this.heap = [];
|
||||
}
|
||||
|
||||
ProgramState.prototype.push = function(val) {
|
||||
this.frame.stack.push(val);
|
||||
}
|
||||
|
||||
ProgramState.prototype.pop = function() {
|
||||
return this.frame.stack.pop();
|
||||
}
|
||||
|
||||
ProgramState.prototype.goto_offset = function() {
|
||||
var c1 = this.frame.program[this.frame.pc+1];
|
||||
var c2 = this.frame.program[this.frame.pc+2]
|
||||
|
||||
var address_offset = (c1 << 8) + c2;
|
||||
// Handle negative values
|
||||
if ((address_offset & 0x8000) != 0)
|
||||
address_offset = -0x8000 + (address_offset & 0x7FFF);
|
||||
|
||||
this.frame.pc += address_offset;
|
||||
}
|
||||
|
||||
ProgramState.prototype.doIf = function(f) {
|
||||
var y = this.pop();
|
||||
var x = this.pop();
|
||||
if (f(x,y)) {
|
||||
this.goto_offset();
|
||||
} else {
|
||||
this.pc += 3;
|
||||
this.frame.pc += 3;
|
||||
}
|
||||
}
|
||||
|
||||
ProgramState.prototype.step = function() {
|
||||
console.log("Running opcode " + op.lookup_table[this.program[this.pc]]);
|
||||
switch (this.program[this.pc]) {
|
||||
var opcode = this.frame.program[this.frame.pc]
|
||||
log("0x" + this.frame.pc.toString(16) + " Running opcode " +
|
||||
op.lookup_table[opcode]);
|
||||
switch (opcode) {
|
||||
// Stack manipulation
|
||||
case op.POP:
|
||||
this.pc++;
|
||||
this.stack.pop();
|
||||
this.frame.pc++;
|
||||
this.pop();
|
||||
break;
|
||||
case op.DUP:
|
||||
this.pc++;
|
||||
var v = this.stack.pop();
|
||||
this.stack.push(v);
|
||||
this.stack.push(v);
|
||||
this.frame.pc++;
|
||||
var v = this.pop();
|
||||
this.push(v);
|
||||
this.push(v);
|
||||
break;
|
||||
case op.SWAP:
|
||||
this.pc++;
|
||||
var v1 = this.stack.pop();
|
||||
var v2 = this.stack.pop();
|
||||
this.stack.push(v1);
|
||||
this.stack.push(v2);
|
||||
this.frame.pc++;
|
||||
var v1 = this.pop();
|
||||
var v2 = this.pop();
|
||||
this.push(v1);
|
||||
this.push(v2);
|
||||
break;
|
||||
case op.BIPUSH:
|
||||
this.pc += 2;
|
||||
var val = this.program[this.pc-1];
|
||||
|
||||
this.frame.pc += 2;
|
||||
var val = this.frame.program[this.frame.pc-1];
|
||||
|
||||
// Do sign extension if necessary
|
||||
if (val & 0x80 != 0)
|
||||
if ((val & 0x80) != 0)
|
||||
val = -0x80 + (val & 0x7F);
|
||||
this.stack.push(val);
|
||||
this.push(val);
|
||||
break;
|
||||
|
||||
// Returning from a function
|
||||
case op.RETURN:
|
||||
var retVal = this.stack.pop();
|
||||
throw retVal;
|
||||
var retVal = this.pop();
|
||||
if (this.call_stack.length == 0)
|
||||
return retVal;
|
||||
|
||||
this.frame = this.call_stack.pop();
|
||||
this.push(retVal);
|
||||
|
||||
break;
|
||||
// Arithmetic
|
||||
case op.IADD:
|
||||
this.pc++;
|
||||
var y = this.stack.pop();
|
||||
var x = this.stack.pop();
|
||||
console.log("Adding " + x + " and " + y);
|
||||
this.stack.push((x+y) % 0x100000000);
|
||||
this.frame.pc++;
|
||||
var y = this.pop();
|
||||
var x = this.pop();
|
||||
log("Adding " + x + " and " + y);
|
||||
this.push((x+y) % 0x100000000);
|
||||
break;
|
||||
case op.ISUB:
|
||||
this.pc++;
|
||||
var y = this.stack.pop();
|
||||
var x = this.stack.pop();
|
||||
this.stack.push((x-y) % 0x100000000);
|
||||
this.frame.pc++;
|
||||
var y = this.pop();
|
||||
var x = this.pop();
|
||||
this.push((x-y) % 0x100000000);
|
||||
break;
|
||||
case op.IMUL:
|
||||
this.pc++;
|
||||
var y = this.stack.pop();
|
||||
var x = this.stack.pop();
|
||||
this.stack.push((x*y) % 0x100000000);
|
||||
this.frame.pc++;
|
||||
var y = this.pop();
|
||||
var x = this.pop();
|
||||
this.push((x*y) % 0x100000000);
|
||||
break;
|
||||
case op.IDIV:
|
||||
this.pc++;
|
||||
var y = this.stack.pop();
|
||||
var x = this.stack.pop();
|
||||
this.frame.pc++;
|
||||
var y = this.pop();
|
||||
var x = this.pop();
|
||||
if (y == 0) c0_arith_error("Divide by zero");
|
||||
if (x == INT_MIN && y == -1) c0_arith_error("Arithmetic overflow");
|
||||
|
||||
// This does int division.
|
||||
// As I understand it, the ~~ is treated as the identity on integers
|
||||
// which forces the type to int, not float
|
||||
this.stack.push(~~(x/y));
|
||||
this.push(~~(x/y));
|
||||
break;
|
||||
case op.IREM:
|
||||
this.pc++;
|
||||
var y = this.stack.pop();
|
||||
var x = this.stack.pop();
|
||||
this.frame.pc++;
|
||||
var y = this.pop();
|
||||
var x = this.pop();
|
||||
if (y == 0) c0_arith_error("Divide by zero");
|
||||
if (x == INT_MIN && y == -1) c0_arith_error("Arithmetic overflow");
|
||||
this.stack.push(x%y);
|
||||
this.push(x%y);
|
||||
break;
|
||||
case op.IAND:
|
||||
this.pc++;
|
||||
var y = this.stack.pop();
|
||||
var x = this.stack.pop();
|
||||
this.stack.push(x&y);
|
||||
this.frame.pc++;
|
||||
var y = this.pop();
|
||||
var x = this.pop();
|
||||
this.push(x&y);
|
||||
break;
|
||||
case op.IOR:
|
||||
this.pc++;
|
||||
var y = this.stack.pop();
|
||||
var x = this.stack.pop();
|
||||
this.stack.push(x|y);
|
||||
this.frame.pc++;
|
||||
var y = this.pop();
|
||||
var x = this.pop();
|
||||
this.push(x|y);
|
||||
break;
|
||||
case op.IXOR:
|
||||
this.pc++;
|
||||
var y = this.stack.pop();
|
||||
var x = this.stack.pop();
|
||||
this.stack.push(x^y);
|
||||
this.frame.pc++;
|
||||
var y = this.pop();
|
||||
var x = this.pop();
|
||||
this.push(x^y);
|
||||
break;
|
||||
case op.ISHL:
|
||||
this.pc++;
|
||||
var y = this.stack.pop();
|
||||
var x = this.stack.pop();
|
||||
this.frame.pc++;
|
||||
var y = this.pop();
|
||||
var x = this.pop();
|
||||
if (y < 0 || y > 31) c0_arith_error("Shifting by too many bits");
|
||||
this.stack.push(x<<y);
|
||||
this.push(x<<y);
|
||||
break;
|
||||
case op.ISHR:
|
||||
this.pc++;
|
||||
var y = this.stack.pop();
|
||||
var x = this.stack.pop();
|
||||
this.frame.pc++;
|
||||
var y = this.pop();
|
||||
var x = this.pop();
|
||||
if (y < 0 || y > 31) c0_arith_error("Shifting by too many bits");
|
||||
this.stack.push(x>>y);
|
||||
this.push(x>>y);
|
||||
break;
|
||||
|
||||
// Operations on local variables
|
||||
|
||||
case op.VLOAD:
|
||||
this.pc += 2;
|
||||
var index = this.program[this.pc-1];
|
||||
this.stack.push(this.variables[index]);
|
||||
this.frame.pc += 2;
|
||||
var index = this.frame.program[this.frame.pc-1];
|
||||
this.push(this.frame.variables[index]);
|
||||
break;
|
||||
case op.VSTORE:
|
||||
this.pc += 2;
|
||||
var index = this.program[this.pc-1];
|
||||
this.variables[index] = this.stack.pop();
|
||||
this.frame.pc += 2;
|
||||
var index = this.frame.program[this.frame.pc-1];
|
||||
this.frame.variables[index] = this.pop();
|
||||
break;
|
||||
case op.ACONST_NULL:
|
||||
this.pc++;
|
||||
this.stack.push(0);
|
||||
this.frame.pc++;
|
||||
this.push(0);
|
||||
break;
|
||||
case op.ILDC:
|
||||
this.pc += 3;
|
||||
var c1 = this.program[this.pc-2];
|
||||
var c2 = this.program[this.pc-1];
|
||||
this.frame.pc += 3;
|
||||
var c1 = this.frame.program[this.frame.pc-2];
|
||||
var c2 = this.frame.program[this.frame.pc-1];
|
||||
var index = (c1 * 0x1000) + c2;
|
||||
|
||||
this.stack.push(this.file.int_pool[index]);
|
||||
this.push(this.file.int_pool[index]);
|
||||
break;
|
||||
case op.ALDC:
|
||||
console.log("Error: I don't know how to handle ALDC yet");
|
||||
throw "Error - can't handle ALDC";
|
||||
this.frame.pc += 3;
|
||||
var c1 = this.frame.program[this.frame.pc-2];
|
||||
var c2 = this.frame.program[this.frame.pc-1];
|
||||
var index = (c1 * 0x1000) + c2;
|
||||
|
||||
this.push(this.file.string_pool[index]);
|
||||
break;
|
||||
|
||||
// Control flow
|
||||
case op.NOP:
|
||||
this.pc++;
|
||||
this.frame.pc++;
|
||||
break;
|
||||
case op.IF_CMPEQ:
|
||||
var y = this.stack.pop();
|
||||
var x = this.stack.pop();
|
||||
this.doIf(y == x);
|
||||
this.doIf(function (x,y) {return y == x;});
|
||||
break;
|
||||
case op.IF_CMPNE:
|
||||
var y = this.stack.pop();
|
||||
var x = this.stack.pop();
|
||||
this.doIf(y != x);
|
||||
this.doIf(function (x,y) {return y != x;});
|
||||
break;
|
||||
case op.IF_CMPLT:
|
||||
var y = this.stack.pop();
|
||||
var x = this.stack.pop();
|
||||
this.doIf(y > x);
|
||||
case op.IF_ICMPLT:
|
||||
this.doIf(function (x,y) {return y > x;});
|
||||
break;
|
||||
case op.IF_CMPGE:
|
||||
var y = this.stack.pop();
|
||||
var x = this.stack.pop();
|
||||
this.doIf(y <= x);
|
||||
case op.IF_ICMPGE:
|
||||
this.doIf(function (x,y) {return y <= x;});
|
||||
break;
|
||||
case op.IF_CMPGT:
|
||||
var y = this.stack.pop();
|
||||
var x = this.stack.pop();
|
||||
this.doIf(y < x);
|
||||
case op.IF_ICMPGT:
|
||||
this.doIf(function (x,y) {return y < x;});
|
||||
break;
|
||||
case op.IF_CMPLE:
|
||||
var y = this.stack.pop();
|
||||
var x = this.stack.pop();
|
||||
this.doIf(y >= x);
|
||||
case op.IF_ICMPLE:
|
||||
this.doIf(function (x,y) {return y >= x;});
|
||||
break;
|
||||
case op.GOTO:
|
||||
this.doIf(true);
|
||||
this.goto_offset();
|
||||
break;
|
||||
case op.ATHROW:
|
||||
this.pc++;
|
||||
c0_user_error(this.stack.pop());
|
||||
this.frame.pc++;
|
||||
c0_user_error(this.pop());
|
||||
break;
|
||||
case op.ASSERT:
|
||||
this.pc++;
|
||||
var a = this.stack.pop();
|
||||
if (this.stack.pop() == 0)
|
||||
this.frame.pc++;
|
||||
var a = this.pop();
|
||||
if (this.pop() == 0)
|
||||
c0_assertion_failure(a);
|
||||
break;
|
||||
|
||||
// Function call operations
|
||||
|
||||
case op.INVOKESTATIC:
|
||||
var c1 = this.frame.program[this.frame.pc+1];
|
||||
var c2 = this.frame.program[this.frame.pc+2];
|
||||
this.frame.pc += 3;
|
||||
|
||||
var index = (c1 << 8) + c2;
|
||||
|
||||
var f = this.file.function_pool[index];
|
||||
var newFrame = new StackFrame(this.file, f);
|
||||
for (var i = f.num_args - 1; i >= 0; i--) {
|
||||
newFrame.variables[i] = this.pop();
|
||||
}
|
||||
|
||||
this.call_stack.push(this.frame);
|
||||
this.frame = newFrame;
|
||||
break;
|
||||
|
||||
case op.INVOKENATIVE:
|
||||
var c1 = this.frame.program[this.frame.pc+1];
|
||||
var c2 = this.frame.program[this.frame.pc+2];
|
||||
this.frame.pc += 3;
|
||||
|
||||
var index = (c1 << 8) + c2;
|
||||
|
||||
var f = this.file.native_pool[index];
|
||||
var arg_array = [];
|
||||
for (var i = f.num_args - 1; i >= 0; i--)
|
||||
arg_array[i] = this.pop();
|
||||
|
||||
var native_function = this.natives[f.function_table_index];
|
||||
if (native_function === undefined) {
|
||||
native_function = function (ignored) {
|
||||
console.log("Could not find native function with index " +
|
||||
f.function_table_index);
|
||||
return 0;
|
||||
};
|
||||
console.log("Unknown native function index " + f.function_table_index);
|
||||
}
|
||||
this.push(native_function(arg_array));
|
||||
break;
|
||||
|
||||
// Memory allocation operations:
|
||||
|
||||
case op.NEW:
|
||||
var size = this.frame.program[this.frame.pc+1];
|
||||
var address = this.heap.length;
|
||||
|
||||
for (var i = 0; i < size; i++) this.heap.push(0);
|
||||
|
||||
this.push(address);
|
||||
this.frame.pc += 2;
|
||||
break;
|
||||
|
||||
case op.NEWARRAY:
|
||||
var size = this.frame.program[this.frame.pc+1];
|
||||
var address = this.heap.length;
|
||||
var num_elements = this.pop();
|
||||
if (num_elements < 0) c0_memory_error("Array size must be nonnegative");
|
||||
|
||||
this.heap.push(num_elements);
|
||||
this.heap.push(size);
|
||||
|
||||
for (var i = 0; i < num_elements; i++) {
|
||||
for (var j = 0; j < size; j++)
|
||||
this.heap.push(0);
|
||||
}
|
||||
|
||||
this.push(address);
|
||||
this.frame.pc += 2;
|
||||
break;
|
||||
|
||||
case op.ARRAYLENGTH:
|
||||
var pointer = this.pop();
|
||||
this.push(this.heap[pointer]);
|
||||
break;
|
||||
|
||||
// Memory access operations:
|
||||
|
||||
case op.AADDF:
|
||||
// Read offset into a struct
|
||||
var offset = this.frame.program[this.frame.pc + 1];
|
||||
var index = this.pop();
|
||||
this.push(this.heap[index + offset]);
|
||||
this.frame.pc += 2;
|
||||
break;
|
||||
|
||||
case op.AADDS:
|
||||
// Read offset into an array
|
||||
var elt_index = this.pop();
|
||||
var index = this.pop();
|
||||
var array_length = this.heap[index];
|
||||
var elt_size = this.heap[index+1];
|
||||
if (elt_index >= array_length) c0_memory_error("Array index out of bounds.");
|
||||
this.push(this.heap[index + 2 + elt_size*elt_index]);
|
||||
this.frame.pc++;
|
||||
break;
|
||||
|
||||
case op.IMLOAD:
|
||||
var addr = this.pop();
|
||||
// Get int32 from bytes
|
||||
var c1 = this.heap[addr];
|
||||
var c2 = this.heap[addr+1];
|
||||
var c3 = this.heap[addr+2];
|
||||
var c4 = this.heap[addr+3];
|
||||
var combined = array_to_i32([c1, c2, c3, c4]);
|
||||
this.push(combined);
|
||||
this.frame.pc++;
|
||||
break;
|
||||
|
||||
case op.IMSTORE:
|
||||
var value = this.pop();
|
||||
var addr = this.pop();
|
||||
var array = i32_to_array(value);
|
||||
|
||||
for (var i = 0; i < 4; i++)
|
||||
this.heap[addr + i] = array[i];
|
||||
this.frame.pc++;
|
||||
|
||||
case op.AMLOAD:
|
||||
var addr = this.pop();
|
||||
this.push(this.heap[addr]);
|
||||
this.frame.pc++;
|
||||
break;
|
||||
|
||||
case op.AMSTORE:
|
||||
var value = this.pop();
|
||||
var addr = this.pop();
|
||||
this.heap[addr] = value;
|
||||
this.frame.pc++;
|
||||
break;
|
||||
|
||||
case op.CMLOAD:
|
||||
var addr = this.pop();
|
||||
this.push(this.heap[addr]);
|
||||
this.frame.pc++;
|
||||
break;
|
||||
|
||||
case op.CMSTORE:
|
||||
var value = this.pop();
|
||||
var addr = this.pop();
|
||||
this.heap[addr] = value;
|
||||
this.frame.pc++;
|
||||
break;
|
||||
|
||||
default:
|
||||
console.log("Error: Unknown opcode\n");
|
||||
var opcode_name;
|
||||
try {
|
||||
opcode_name = op.lookup_table[opcode];
|
||||
} catch (ignored) {
|
||||
opcode_name = "UNKNOWN";
|
||||
}
|
||||
console.log("Error: Unknown opcode: 0x" + opcode.toString(16) +
|
||||
" (" + opcode_name + ")\n");
|
||||
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);
|
||||
function execute(file, callbacks, v) {
|
||||
verbose = typeof v !== 'undefined' ? v : true;
|
||||
log("Initializing with file " + file);
|
||||
|
||||
var state = new ProgramState(f);
|
||||
var state = new ProgramState(file, callbacks);
|
||||
|
||||
log("Beginning execution");
|
||||
|
||||
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;
|
||||
}
|
||||
var val = state.step();
|
||||
if (val !== undefined) return val;
|
||||
|
||||
// if (at_breakpoint) {
|
||||
// save state (maybe in a global in this file?)
|
||||
@@ -388,13 +729,11 @@ function execute(f) {
|
||||
}
|
||||
|
||||
exports.execute = execute;
|
||||
|
||||
// opcode definitions
|
||||
|
||||
|
||||
},{"./opcodes":5}],4:[function(require,module,exports){
|
||||
},{"./opcodes":6}],5:[function(require,module,exports){
|
||||
parser = require("./bytecode-parser");
|
||||
c0vm = require("./c0vm.js");
|
||||
c0ffi = require("./c0ffi.js");
|
||||
|
||||
// console.log("Reading in sample bytecode file:");
|
||||
// console.log(parser.getBytes("../test/test.bc0"));
|
||||
@@ -404,13 +743,33 @@ c0vm = require("./c0vm.js");
|
||||
// console.log(file);
|
||||
// console.log(file.function_pool[0].code);
|
||||
|
||||
// UI interaction functions
|
||||
|
||||
function print(arg) {
|
||||
$("#output").append(arg);
|
||||
}
|
||||
|
||||
callbacks = {};
|
||||
callbacks[c0ffi.NATIVE_PRINT] = function(args) {
|
||||
print(args[0]);
|
||||
print("<br />");
|
||||
}
|
||||
|
||||
callbacks[c0ffi.NATIVE_PRINTINT] = function(args) {
|
||||
print(args[0]);
|
||||
print("<br />");
|
||||
}
|
||||
|
||||
console.log(callbacks);
|
||||
|
||||
$("#run").click(function() {
|
||||
var input = $("#bytecode").html().replace(/(\r\n|\n|\r)/gm,"");
|
||||
var file = parser.parse($("#bytecode").text());
|
||||
$("#output").text(c0vm.execute(file));
|
||||
print("<br />");
|
||||
print(c0vm.execute(file, callbacks));
|
||||
});
|
||||
|
||||
},{"./bytecode-parser":2,"./c0vm.js":3}],5:[function(require,module,exports){
|
||||
},{"./bytecode-parser":2,"./c0ffi.js":3,"./c0vm.js":4}],6:[function(require,module,exports){
|
||||
/* arithmetic operations */
|
||||
exports.IADD = 0x60;
|
||||
exports.IAND = 0x7E;
|
||||
@@ -503,12 +862,12 @@ exports.lookup_table = {
|
||||
0x13: "ILDC",
|
||||
0x14: "ALDC",
|
||||
0x00: "NOP",
|
||||
0x9F: "IF",
|
||||
0xA0: "IF",
|
||||
0xA1: "IF",
|
||||
0xA2: "IF",
|
||||
0xA3: "IF",
|
||||
0xA4: "IF",
|
||||
0x9F: "IF_CMPEQ",
|
||||
0xA0: "IF_CMPNE",
|
||||
0xA1: "IF_ICMPLT",
|
||||
0xA2: "IF_ICMPGE",
|
||||
0xA3: "IF_ICMPGT",
|
||||
0xA4: "IF_ICMPLE",
|
||||
0xA7: "GOTO",
|
||||
0xBF: "ATHROW",
|
||||
0xCF: "ASSERT",
|
||||
@@ -517,6 +876,6 @@ exports.lookup_table = {
|
||||
0xB0: "RETURN"
|
||||
};
|
||||
|
||||
},{}],6:[function(require,module,exports){
|
||||
},{}],7:[function(require,module,exports){
|
||||
|
||||
},{}]},{},[3,2,1,5,4]);
|
||||
},{}]},{},[4,2,1,6,5]);
|
||||
|
||||
110
src/c0ffi.js
Normal file
110
src/c0ffi.js
Normal file
@@ -0,0 +1,110 @@
|
||||
exports.NATIVE_FADD = 0
|
||||
exports.NATIVE_FDIV = 1
|
||||
exports.NATIVE_FLESS = 2
|
||||
exports.NATIVE_FMUL = 3
|
||||
exports.NATIVE_FSUB = 4
|
||||
exports.NATIVE_FTOI = 5
|
||||
exports.NATIVE_ITOF = 6
|
||||
exports.NATIVE_PRINT_FPT = 7
|
||||
exports.NATIVE_PRINT_HEX = 8
|
||||
exports.NATIVE_PRINT_INT = 9
|
||||
|
||||
/* args */
|
||||
exports.NATIVE_ARGS_FLAG = 10
|
||||
exports.NATIVE_ARGS_INT = 11
|
||||
exports.NATIVE_ARGS_PARSE = 12
|
||||
exports.NATIVE_ARGS_STRING = 13
|
||||
|
||||
/* conio */
|
||||
exports.NATIVE_EOF = 14
|
||||
exports.NATIVE_FLUSH = 15
|
||||
exports.NATIVE_PRINT = 16
|
||||
exports.NATIVE_PRINTBOOL = 17
|
||||
exports.NATIVE_PRINTCHAR = 18
|
||||
exports.NATIVE_PRINTINT = 19
|
||||
exports.NATIVE_PRINTLN = 20
|
||||
exports.NATIVE_READLINE = 21
|
||||
|
||||
/* curses */
|
||||
exports.NATIVE_C_ADDCH = 22
|
||||
exports.NATIVE_C_CBREAK = 23
|
||||
exports.NATIVE_C_CURS_SET = 24
|
||||
exports.NATIVE_C_DELCH = 25
|
||||
exports.NATIVE_C_ENDWIN = 26
|
||||
exports.NATIVE_C_ERASE = 27
|
||||
exports.NATIVE_C_GETCH = 28
|
||||
exports.NATIVE_C_INITSCR = 29
|
||||
exports.NATIVE_C_KEYPAD = 30
|
||||
exports.NATIVE_C_MOVE = 31
|
||||
exports.NATIVE_C_NOECHO = 32
|
||||
exports.NATIVE_C_REFRESH = 33
|
||||
exports.NATIVE_C_SUBWIN = 34
|
||||
exports.NATIVE_C_WADDCH = 35
|
||||
exports.NATIVE_C_WADDSTR = 36
|
||||
exports.NATIVE_C_WCLEAR = 37
|
||||
exports.NATIVE_C_WERASE = 38
|
||||
exports.NATIVE_C_WMOVE = 39
|
||||
exports.NATIVE_C_WREFRESH = 40
|
||||
exports.NATIVE_C_WSTANDEND = 41
|
||||
exports.NATIVE_C_WSTANDOUT = 42
|
||||
exports.NATIVE_CC_GETBEGX = 43
|
||||
exports.NATIVE_CC_GETBEGY = 44
|
||||
exports.NATIVE_CC_GETMAXX = 45
|
||||
exports.NATIVE_CC_GETMAXY = 46
|
||||
exports.NATIVE_CC_GETX = 47
|
||||
exports.NATIVE_CC_GETY = 48
|
||||
exports.NATIVE_CC_HIGHLIGHT = 49
|
||||
exports.NATIVE_CC_KEY_IS_BACKSPACE = 50
|
||||
exports.NATIVE_CC_KEY_IS_DOWN = 51
|
||||
exports.NATIVE_CC_KEY_IS_ENTER = 52
|
||||
exports.NATIVE_CC_KEY_IS_LEFT = 53
|
||||
exports.NATIVE_CC_KEY_IS_RIGHT = 54
|
||||
exports.NATIVE_CC_KEY_IS_UP = 55
|
||||
exports.NATIVE_CC_WBOLDOFF = 56
|
||||
exports.NATIVE_CC_WBOLDON = 57
|
||||
exports.NATIVE_CC_WDIMOFF = 58
|
||||
exports.NATIVE_CC_WDIMON = 59
|
||||
exports.NATIVE_CC_WREVERSEOFF = 60
|
||||
exports.NATIVE_CC_WREVERSEON = 61
|
||||
exports.NATIVE_CC_WUNDEROFF = 62
|
||||
exports.NATIVE_CC_WUNDERON = 63
|
||||
|
||||
/* file */
|
||||
exports.NATIVE_FILE_CLOSE = 64
|
||||
exports.NATIVE_FILE_CLOSED = 65
|
||||
exports.NATIVE_FILE_EOF = 66
|
||||
exports.NATIVE_FILE_READ = 67
|
||||
exports.NATIVE_FILE_READLINE = 68
|
||||
|
||||
/* img */
|
||||
exports.NATIVE_IMAGE_CLONE = 69
|
||||
exports.NATIVE_IMAGE_CREATE = 70
|
||||
exports.NATIVE_IMAGE_DATA = 71
|
||||
exports.NATIVE_IMAGE_DESTROY = 72
|
||||
exports.NATIVE_IMAGE_HEIGHT = 73
|
||||
exports.NATIVE_IMAGE_LOAD = 74
|
||||
exports.NATIVE_IMAGE_SAVE = 75
|
||||
exports.NATIVE_IMAGE_SUBIMAGE = 76
|
||||
exports.NATIVE_IMAGE_WIDTH = 77
|
||||
|
||||
/* parse */
|
||||
exports.NATIVE_PARSE_BOOL = 78
|
||||
exports.NATIVE_PARSE_INT = 79
|
||||
|
||||
/* string */
|
||||
exports.NATIVE_CHAR_CHR = 80
|
||||
exports.NATIVE_CHAR_ORD = 81
|
||||
exports.NATIVE_STRING_CHARAT = 82
|
||||
exports.NATIVE_STRING_COMPARE = 83
|
||||
exports.NATIVE_STRING_EQUAL = 84
|
||||
exports.NATIVE_STRING_FROM_CHARARRAY = 85
|
||||
exports.NATIVE_STRING_FROMBOOL = 86
|
||||
exports.NATIVE_STRING_FROMCHAR = 87
|
||||
exports.NATIVE_STRING_FROMINT = 88
|
||||
exports.NATIVE_STRING_JOIN = 89
|
||||
exports.NATIVE_STRING_LENGTH = 90
|
||||
exports.NATIVE_STRING_SUB = 91
|
||||
exports.NATIVE_STRING_TERMINATED = 92
|
||||
exports.NATIVE_STRING_TO_CHARARRAY = 93
|
||||
exports.NATIVE_STRING_TOLOWER = 94
|
||||
|
||||
12
test/arrays.c0
Normal file
12
test/arrays.c0
Normal file
@@ -0,0 +1,12 @@
|
||||
#use <conio>
|
||||
|
||||
int main() {
|
||||
int[] A = alloc_array(int, -2);
|
||||
A[3] = 0;
|
||||
A[3] = 1;
|
||||
A[3] = 2;
|
||||
A[3] = 3;
|
||||
|
||||
printint(A[3]);
|
||||
return 0;
|
||||
}
|
||||
52
test/arrays.c0.bc0
Normal file
52
test/arrays.c0.bc0
Normal file
@@ -0,0 +1,52 @@
|
||||
C0 C0 FF EE # magic number
|
||||
00 09 # version 4, arch = 1 (64 bits)
|
||||
|
||||
00 00 # int pool count
|
||||
# int pool
|
||||
|
||||
00 00 # string pool total size
|
||||
# string pool
|
||||
|
||||
00 01 # function count
|
||||
# function_pool
|
||||
|
||||
#<main>
|
||||
00 00 # number of arguments = 0
|
||||
00 01 # number of local variables = 1
|
||||
00 33 # code length = 51 bytes
|
||||
10 FE # bipush -2 # -2
|
||||
BC 04 # newarray 4 # alloc_array(int, -(2))
|
||||
36 00 # vstore 0 # A = alloc_array(int, -(2));
|
||||
15 00 # vload 0 # A
|
||||
10 03 # bipush 3 # 3
|
||||
63 # aadds # &A[3]
|
||||
10 00 # bipush 0 # 0
|
||||
4E # imstore # A[3] = 0;
|
||||
15 00 # vload 0 # A
|
||||
10 03 # bipush 3 # 3
|
||||
63 # aadds # &A[3]
|
||||
10 01 # bipush 1 # 1
|
||||
4E # imstore # A[3] = 1;
|
||||
15 00 # vload 0 # A
|
||||
10 03 # bipush 3 # 3
|
||||
63 # aadds # &A[3]
|
||||
10 02 # bipush 2 # 2
|
||||
4E # imstore # A[3] = 2;
|
||||
15 00 # vload 0 # A
|
||||
10 03 # bipush 3 # 3
|
||||
63 # aadds # &A[3]
|
||||
10 03 # bipush 3 # 3
|
||||
4E # imstore # A[3] = 3;
|
||||
15 00 # vload 0 # A
|
||||
10 03 # bipush 3 # 3
|
||||
63 # aadds # &A[3]
|
||||
2E # imload # A[3]
|
||||
B7 00 00 # invokenative 0 # printint(A[3])
|
||||
57 # pop # (ignore result)
|
||||
10 00 # bipush 0 # 0
|
||||
B0 # return #
|
||||
|
||||
00 01 # native count
|
||||
# native pool
|
||||
00 01 00 09 # printint
|
||||
|
||||
1
test/arrays.c0.bc0out
Normal file
1
test/arrays.c0.bc0out
Normal file
@@ -0,0 +1 @@
|
||||
30
|
||||
0
test/arrays.c0.c0out
Normal file
0
test/arrays.c0.c0out
Normal file
BIN
test/arrays.c0.ex
Executable file
BIN
test/arrays.c0.ex
Executable file
Binary file not shown.
23
test/dsquared.c0
Normal file
23
test/dsquared.c0
Normal file
@@ -0,0 +1,23 @@
|
||||
int dsquared(int x, int y)
|
||||
{
|
||||
return x*x + y*y;
|
||||
}
|
||||
|
||||
int main ()
|
||||
{
|
||||
int a1 = 15;
|
||||
int b1 = 122;
|
||||
int a2 = 42;
|
||||
int b2 = 0;
|
||||
int a3 = -7;
|
||||
int b3 = -11;
|
||||
int a4 = -4;
|
||||
int b4 = 3;
|
||||
int sum = 0;
|
||||
sum = sum + dsquared(a1,b1);
|
||||
sum = sum + dsquared(a2,b2);
|
||||
sum = sum + dsquared(a3,b3);
|
||||
sum = sum + dsquared(a4,b4);
|
||||
return sum;
|
||||
}
|
||||
|
||||
78
test/dsquared.c0.bc0
Normal file
78
test/dsquared.c0.bc0
Normal file
@@ -0,0 +1,78 @@
|
||||
C0 C0 FF EE # magic number
|
||||
00 09 # version 4, arch = 1 (64 bits)
|
||||
|
||||
00 00 # int pool count
|
||||
# int pool
|
||||
|
||||
00 00 # string pool total size
|
||||
# string pool
|
||||
|
||||
00 02 # function count
|
||||
# function_pool
|
||||
|
||||
#<main>
|
||||
00 00 # number of arguments = 0
|
||||
00 09 # number of local variables = 9
|
||||
00 57 # code length = 87 bytes
|
||||
10 0F # bipush 15 # 15
|
||||
36 00 # vstore 0 # a1 = 15;
|
||||
10 7A # bipush 122 # 122
|
||||
36 01 # vstore 1 # b1 = 122;
|
||||
10 2A # bipush 42 # 42
|
||||
36 02 # vstore 2 # a2 = 42;
|
||||
10 00 # bipush 0 # 0
|
||||
36 03 # vstore 3 # b2 = 0;
|
||||
10 F9 # bipush -7 # -7
|
||||
36 04 # vstore 4 # a3 = -(7);
|
||||
10 F5 # bipush -11 # -11
|
||||
36 05 # vstore 5 # b3 = -(11);
|
||||
10 FC # bipush -4 # -4
|
||||
36 06 # vstore 6 # a4 = -(4);
|
||||
10 03 # bipush 3 # 3
|
||||
36 07 # vstore 7 # b4 = 3;
|
||||
10 00 # bipush 0 # 0
|
||||
36 08 # vstore 8 # sum = 0;
|
||||
15 08 # vload 8 # sum
|
||||
15 00 # vload 0 # a1
|
||||
15 01 # vload 1 # b1
|
||||
B8 00 01 # invokestatic 1 # dsquared(a1, b1)
|
||||
60 # iadd # (sum + dsquared(a1, b1))
|
||||
36 08 # vstore 8 # sum = (sum + dsquared(a1, b1));
|
||||
15 08 # vload 8 # sum
|
||||
15 02 # vload 2 # a2
|
||||
15 03 # vload 3 # b2
|
||||
B8 00 01 # invokestatic 1 # dsquared(a2, b2)
|
||||
60 # iadd # (sum + dsquared(a2, b2))
|
||||
36 08 # vstore 8 # sum = (sum + dsquared(a2, b2));
|
||||
15 08 # vload 8 # sum
|
||||
15 04 # vload 4 # a3
|
||||
15 05 # vload 5 # b3
|
||||
B8 00 01 # invokestatic 1 # dsquared(a3, b3)
|
||||
60 # iadd # (sum + dsquared(a3, b3))
|
||||
36 08 # vstore 8 # sum = (sum + dsquared(a3, b3));
|
||||
15 08 # vload 8 # sum
|
||||
15 06 # vload 6 # a4
|
||||
15 07 # vload 7 # b4
|
||||
B8 00 01 # invokestatic 1 # dsquared(a4, b4)
|
||||
60 # iadd # (sum + dsquared(a4, b4))
|
||||
36 08 # vstore 8 # sum = (sum + dsquared(a4, b4));
|
||||
15 08 # vload 8 # sum
|
||||
B0 # return #
|
||||
|
||||
|
||||
#<dsquared>
|
||||
00 02 # number of arguments = 2
|
||||
00 02 # number of local variables = 2
|
||||
00 0C # code length = 12 bytes
|
||||
15 00 # vload 0 # x
|
||||
15 00 # vload 0 # x
|
||||
68 # imul # (x * x)
|
||||
15 01 # vload 1 # y
|
||||
15 01 # vload 1 # y
|
||||
68 # imul # (y * y)
|
||||
60 # iadd # ((x * x) + (y * y))
|
||||
B0 # return #
|
||||
|
||||
00 00 # native count
|
||||
# native pool
|
||||
|
||||
1
test/dsquared.c0.bc0out
Normal file
1
test/dsquared.c0.bc0out
Normal file
@@ -0,0 +1 @@
|
||||
17068
|
||||
1
test/dsquared.c0.c0out
Normal file
1
test/dsquared.c0.c0out
Normal file
@@ -0,0 +1 @@
|
||||
17068
|
||||
BIN
test/dsquared.c0.ex
Executable file
BIN
test/dsquared.c0.ex
Executable file
Binary file not shown.
3
test/ishr.c0
Normal file
3
test/ishr.c0
Normal file
@@ -0,0 +1,3 @@
|
||||
int main () {
|
||||
return -1 >> 1; /* should return -1 */
|
||||
}
|
||||
24
test/ishr.c0.bc0
Normal file
24
test/ishr.c0.bc0
Normal file
@@ -0,0 +1,24 @@
|
||||
C0 C0 FF EE # magic number
|
||||
00 09 # version 4, arch = 1 (64 bits)
|
||||
|
||||
00 00 # int pool count
|
||||
# int pool
|
||||
|
||||
00 00 # string pool total size
|
||||
# string pool
|
||||
|
||||
00 01 # function count
|
||||
# function_pool
|
||||
|
||||
#<main>
|
||||
00 00 # number of arguments = 0
|
||||
00 00 # number of local variables = 0
|
||||
00 06 # code length = 6 bytes
|
||||
10 FF # bipush -1 # -1
|
||||
10 01 # bipush 1 # 1
|
||||
7A # ishr # (-(1) >> 1)
|
||||
B0 # return #
|
||||
|
||||
00 00 # native count
|
||||
# native pool
|
||||
|
||||
1
test/ishr.c0.bc0out
Normal file
1
test/ishr.c0.bc0out
Normal file
@@ -0,0 +1 @@
|
||||
-1
|
||||
1
test/ishr.c0.c0out
Normal file
1
test/ishr.c0.c0out
Normal file
@@ -0,0 +1 @@
|
||||
-1
|
||||
BIN
test/ishr.c0.ex
Executable file
BIN
test/ishr.c0.ex
Executable file
Binary file not shown.
10
test/isqrt.c0
Normal file
10
test/isqrt.c0
Normal file
@@ -0,0 +1,10 @@
|
||||
int main () {
|
||||
int n = 15122;
|
||||
int i = 0; int k = 0;
|
||||
while (k <= n) {
|
||||
k += 2*i + 1;
|
||||
i++;
|
||||
}
|
||||
return i-1;
|
||||
}
|
||||
|
||||
51
test/isqrt.c0.bc0
Normal file
51
test/isqrt.c0.bc0
Normal file
@@ -0,0 +1,51 @@
|
||||
C0 C0 FF EE # magic number
|
||||
00 09 # version 4, arch = 1 (64 bits)
|
||||
|
||||
00 01 # int pool count
|
||||
# int pool
|
||||
00 00 3B 12
|
||||
|
||||
00 00 # string pool total size
|
||||
# string pool
|
||||
|
||||
00 01 # function count
|
||||
# function_pool
|
||||
|
||||
#<main>
|
||||
00 00 # number of arguments = 0
|
||||
00 03 # number of local variables = 3
|
||||
00 34 # code length = 52 bytes
|
||||
13 00 00 # ildc 0 # c[0] = 15122
|
||||
36 00 # vstore 0 # n = 15122;
|
||||
10 00 # bipush 0 # 0
|
||||
36 01 # vstore 1 # i = 0;
|
||||
10 00 # bipush 0 # 0
|
||||
36 02 # vstore 2 # k = 0;
|
||||
# <00:loop>
|
||||
15 02 # vload 2 # k
|
||||
15 00 # vload 0 # n
|
||||
A4 00 06 # if_icmple +6 # if (k <= n) goto <01:body>
|
||||
A7 00 1A # goto +26 # goto <02:exit>
|
||||
# <01:body>
|
||||
15 02 # vload 2 # k
|
||||
10 02 # bipush 2 # 2
|
||||
15 01 # vload 1 # i
|
||||
68 # imul # (2 * i)
|
||||
10 01 # bipush 1 # 1
|
||||
60 # iadd # ((2 * i) + 1)
|
||||
60 # iadd #
|
||||
36 02 # vstore 2 # k += ((2 * i) + 1);
|
||||
15 01 # vload 1 # i
|
||||
10 01 # bipush 1 # 1
|
||||
60 # iadd #
|
||||
36 01 # vstore 1 # i += 1;
|
||||
A7 FF E2 # goto -30 # goto <00:loop>
|
||||
# <02:exit>
|
||||
15 01 # vload 1 # i
|
||||
10 01 # bipush 1 # 1
|
||||
64 # isub # (i - 1)
|
||||
B0 # return #
|
||||
|
||||
00 00 # native count
|
||||
# native pool
|
||||
|
||||
0
test/isqrt.c0.bc0out
Normal file
0
test/isqrt.c0.bc0out
Normal file
1
test/isqrt.c0.c0out
Normal file
1
test/isqrt.c0.c0out
Normal file
@@ -0,0 +1 @@
|
||||
122
|
||||
BIN
test/isqrt.c0.ex
Executable file
BIN
test/isqrt.c0.ex
Executable file
Binary file not shown.
10
test/mid.c0
Normal file
10
test/mid.c0
Normal file
@@ -0,0 +1,10 @@
|
||||
int main()
|
||||
{
|
||||
int low = 0x15;
|
||||
int high = 0x122;
|
||||
int mid;
|
||||
|
||||
mid = low + (high - low)/2;
|
||||
return mid;
|
||||
}
|
||||
|
||||
35
test/mid.c0.bc0
Normal file
35
test/mid.c0.bc0
Normal file
@@ -0,0 +1,35 @@
|
||||
C0 C0 FF EE # magic number
|
||||
00 09 # version 4, arch = 1 (64 bits)
|
||||
|
||||
00 01 # int pool count
|
||||
# int pool
|
||||
00 00 01 22
|
||||
|
||||
00 00 # string pool total size
|
||||
# string pool
|
||||
|
||||
00 01 # function count
|
||||
# function_pool
|
||||
|
||||
#<main>
|
||||
00 00 # number of arguments = 0
|
||||
00 03 # number of local variables = 3
|
||||
00 19 # code length = 25 bytes
|
||||
10 15 # bipush 21 # 21
|
||||
36 00 # vstore 0 # low = 21;
|
||||
13 00 00 # ildc 0 # c[0] = 290
|
||||
36 01 # vstore 1 # high = 290;
|
||||
15 00 # vload 0 # low
|
||||
15 01 # vload 1 # high
|
||||
15 00 # vload 0 # low
|
||||
64 # isub # (high - low)
|
||||
10 02 # bipush 2 # 2
|
||||
6C # idiv # ((high - low) / 2)
|
||||
60 # iadd # (low + ((high - low) / 2))
|
||||
36 02 # vstore 2 # mid = (low + ((high - low) / 2));
|
||||
15 02 # vload 2 # mid
|
||||
B0 # return #
|
||||
|
||||
00 00 # native count
|
||||
# native pool
|
||||
|
||||
1
test/mid.c0.bc0out
Normal file
1
test/mid.c0.bc0out
Normal file
@@ -0,0 +1 @@
|
||||
155
|
||||
1
test/mid.c0.c0out
Normal file
1
test/mid.c0.c0out
Normal file
@@ -0,0 +1 @@
|
||||
155
|
||||
BIN
test/mid.c0.ex
Executable file
BIN
test/mid.c0.ex
Executable file
Binary file not shown.
7
test/sample2.5.c0
Normal file
7
test/sample2.5.c0
Normal file
@@ -0,0 +1,7 @@
|
||||
int main () {
|
||||
int i; int sum = 0;
|
||||
for (i = 15; i <= 122; i++)
|
||||
sum += i;
|
||||
assert(sum == 0);
|
||||
return sum;
|
||||
}
|
||||
55
test/sample2.5.c0.bc0
Normal file
55
test/sample2.5.c0.bc0
Normal file
@@ -0,0 +1,55 @@
|
||||
C0 C0 FF EE # magic number
|
||||
00 09 # version 4, arch = 1 (64 bits)
|
||||
|
||||
00 00 # int pool count
|
||||
# int pool
|
||||
|
||||
00 25 # string pool total size
|
||||
# string pool
|
||||
73 61 6D 70 6C 65 32 2E 35 2E 63 30 3A 35 2E 32 2D 35 2E 31 39 3A 20 61 73 73 65 72 74 20 66 61 69 6C 65 64 00 # "sample2.5.c0:5.2-5.19: assert failed"
|
||||
|
||||
00 01 # function count
|
||||
# function_pool
|
||||
|
||||
#<main>
|
||||
00 00 # number of arguments = 0
|
||||
00 02 # number of local variables = 2
|
||||
00 3B # code length = 59 bytes
|
||||
10 00 # bipush 0 # 0
|
||||
36 01 # vstore 1 # sum = 0;
|
||||
10 0F # bipush 15 # 15
|
||||
36 00 # vstore 0 # i = 15;
|
||||
# <00:loop>
|
||||
15 00 # vload 0 # i
|
||||
10 7A # bipush 122 # 122
|
||||
A4 00 06 # if_icmple +6 # if (i <= 122) goto <01:body>
|
||||
A7 00 14 # goto +20 # goto <02:exit>
|
||||
# <01:body>
|
||||
15 01 # vload 1 # sum
|
||||
15 00 # vload 0 # i
|
||||
60 # iadd #
|
||||
36 01 # vstore 1 # sum += i;
|
||||
15 00 # vload 0 # i
|
||||
10 01 # bipush 1 # 1
|
||||
60 # iadd #
|
||||
36 00 # vstore 0 # i += 1;
|
||||
A7 FF E8 # goto -24 # goto <00:loop>
|
||||
# <02:exit>
|
||||
15 01 # vload 1 # sum
|
||||
10 00 # bipush 0 # 0
|
||||
9F 00 06 # if_cmpeq +6 # if (sum == 0) goto <03:cond_true>
|
||||
A7 00 08 # goto +8 # goto <04:cond_false>
|
||||
# <03:cond_true>
|
||||
10 01 # bipush 1 # true
|
||||
A7 00 05 # goto +5 # goto <05:cond_end>
|
||||
# <04:cond_false>
|
||||
10 00 # bipush 0 # false
|
||||
# <05:cond_end>
|
||||
14 00 00 # aldc 0 # s[0] = "sample2.5.c0:5.2-5.19: assert failed"
|
||||
CF # assert # assert(sum == 0) [failure message on stack]
|
||||
15 01 # vload 1 # sum
|
||||
B0 # return #
|
||||
|
||||
00 00 # native count
|
||||
# native pool
|
||||
|
||||
0
test/sample2.5.c0.bc0out
Normal file
0
test/sample2.5.c0.bc0out
Normal file
0
test/sample2.5.c0.c0out
Normal file
0
test/sample2.5.c0.c0out
Normal file
BIN
test/sample2.5.c0.ex
Executable file
BIN
test/sample2.5.c0.ex
Executable file
Binary file not shown.
18
test/structs.c0
Normal file
18
test/structs.c0
Normal file
@@ -0,0 +1,18 @@
|
||||
#use <conio>
|
||||
|
||||
struct test {
|
||||
int a;
|
||||
string b;
|
||||
int c;
|
||||
};
|
||||
|
||||
int main() {
|
||||
struct test* t = alloc(struct test);
|
||||
t->a = 1;
|
||||
t->b = "potato chip";
|
||||
t->c = 23;
|
||||
print(t->b);
|
||||
printint(t->a);
|
||||
printint(t->c);
|
||||
return 0;
|
||||
}
|
||||
54
test/structs.c0.bc0
Normal file
54
test/structs.c0.bc0
Normal file
@@ -0,0 +1,54 @@
|
||||
C0 C0 FF EE # magic number
|
||||
00 09 # version 4, arch = 1 (64 bits)
|
||||
|
||||
00 00 # int pool count
|
||||
# int pool
|
||||
|
||||
00 0C # string pool total size
|
||||
# string pool
|
||||
70 6F 74 61 74 6F 20 63 68 69 70 00 # "potato chip"
|
||||
|
||||
00 01 # function count
|
||||
# function_pool
|
||||
|
||||
#<main>
|
||||
00 00 # number of arguments = 0
|
||||
00 01 # number of local variables = 1
|
||||
00 38 # code length = 56 bytes
|
||||
BB 18 # new 24 # alloc(struct test)
|
||||
36 00 # vstore 0 # t = alloc(struct test);
|
||||
15 00 # vload 0 # t
|
||||
62 00 # aaddf 0 # &t->a
|
||||
10 01 # bipush 1 # 1
|
||||
4E # imstore # t->a = 1;
|
||||
15 00 # vload 0 # t
|
||||
62 08 # aaddf 8 # &t->b
|
||||
14 00 00 # aldc 0 # s[0] = "potato chip"
|
||||
4F # amstore # t->b = "potato chip";
|
||||
15 00 # vload 0 # t
|
||||
62 10 # aaddf 16 # &t->c
|
||||
10 17 # bipush 23 # 23
|
||||
4E # imstore # t->c = 23;
|
||||
15 00 # vload 0 # t
|
||||
62 08 # aaddf 8 # &t->b
|
||||
2F # amload # t->b
|
||||
B7 00 00 # invokenative 0 # print(t->b)
|
||||
57 # pop # (ignore result)
|
||||
15 00 # vload 0 # t
|
||||
62 00 # aaddf 0 # &t->a
|
||||
2E # imload # t->a
|
||||
B7 00 01 # invokenative 1 # printint(t->a)
|
||||
57 # pop # (ignore result)
|
||||
15 00 # vload 0 # t
|
||||
62 10 # aaddf 16 # &t->c
|
||||
2E # imload # t->c
|
||||
B7 00 01 # invokenative 1 # printint(t->c)
|
||||
57 # pop # (ignore result)
|
||||
10 00 # bipush 0 # 0
|
||||
B0 # return #
|
||||
|
||||
00 02 # native count
|
||||
# native pool
|
||||
00 01 00 10 # print
|
||||
00 01 00 13 # printint
|
||||
|
||||
1
test/structs.c0.bc0out
Normal file
1
test/structs.c0.bc0out
Normal file
@@ -0,0 +1 @@
|
||||
potato chip1230
|
||||
1
test/structs.c0.c0out
Normal file
1
test/structs.c0.c0out
Normal file
@@ -0,0 +1 @@
|
||||
potato chip1230
|
||||
BIN
test/structs.c0.ex
Executable file
BIN
test/structs.c0.ex
Executable file
Binary file not shown.
8
test/testif.c0
Normal file
8
test/testif.c0
Normal file
@@ -0,0 +1,8 @@
|
||||
int main() {
|
||||
int x = 4;
|
||||
if (x < 3)
|
||||
x = 23;
|
||||
else
|
||||
x = 32;
|
||||
return x;
|
||||
}
|
||||
36
test/testif.c0.bc0
Normal file
36
test/testif.c0.bc0
Normal file
@@ -0,0 +1,36 @@
|
||||
C0 C0 FF EE # magic number
|
||||
00 09 # version 4, arch = 1 (64 bits)
|
||||
|
||||
00 00 # int pool count
|
||||
# int pool
|
||||
|
||||
00 00 # string pool total size
|
||||
# string pool
|
||||
|
||||
00 01 # function count
|
||||
# function_pool
|
||||
|
||||
#<main>
|
||||
00 00 # number of arguments = 0
|
||||
00 01 # number of local variables = 1
|
||||
00 1C # code length = 28 bytes
|
||||
10 04 # bipush 4 # 4
|
||||
36 00 # vstore 0 # x = 4;
|
||||
15 00 # vload 0 # x
|
||||
10 03 # bipush 3 # 3
|
||||
A1 00 06 # if_icmplt +6 # if (x < 3) goto <00:then>
|
||||
A7 00 0A # goto +10 # goto <01:else>
|
||||
# <00:then>
|
||||
10 17 # bipush 23 # 23
|
||||
36 00 # vstore 0 # x = 23;
|
||||
A7 00 07 # goto +7 # goto <02:endif>
|
||||
# <01:else>
|
||||
10 20 # bipush 32 # 32
|
||||
36 00 # vstore 0 # x = 32;
|
||||
# <02:endif>
|
||||
15 00 # vload 0 # x
|
||||
B0 # return #
|
||||
|
||||
00 00 # native count
|
||||
# native pool
|
||||
|
||||
1
test/testif.c0.bc0out
Normal file
1
test/testif.c0.bc0out
Normal file
@@ -0,0 +1 @@
|
||||
32
|
||||
1
test/testif.c0.c0out
Normal file
1
test/testif.c0.c0out
Normal file
@@ -0,0 +1 @@
|
||||
32
|
||||
BIN
test/testif.c0.ex
Executable file
BIN
test/testif.c0.ex
Executable file
Binary file not shown.
73
test/tests.js
Normal file
73
test/tests.js
Normal file
@@ -0,0 +1,73 @@
|
||||
parser = require("../src/bytecode-parser.js");
|
||||
c0vm = require("../src/c0vm.js");
|
||||
c0ffi = require("../src/c0ffi.js");
|
||||
|
||||
var callbacks = {}
|
||||
var printout = "";
|
||||
|
||||
callbacks[c0ffi.NATIVE_PRINT] = function(args) {
|
||||
printout += args[0];
|
||||
}
|
||||
|
||||
callbacks[c0ffi.NATIVE_PRINTINT] = function(args) {
|
||||
printout += args[0];
|
||||
}
|
||||
|
||||
callbacks[c0ffi.NATIVE_PRINTLN] = function(args) {
|
||||
printout += (args[0] + "\n");
|
||||
}
|
||||
|
||||
function doTest(filename, expected_result) {
|
||||
return function(test) {
|
||||
var result = c0vm.execute(parser.parse(filename), callbacks, false);
|
||||
test.ok(result == expected_result,
|
||||
filename + " - did not get expected result " + expected_result +
|
||||
", instead got " + result);
|
||||
test.done();
|
||||
}
|
||||
}
|
||||
|
||||
exports.testIADD = doTest("iadd.c0.bc0", -2);
|
||||
|
||||
exports.testPrint = function(test) {
|
||||
printout = "";
|
||||
var result = c0vm.execute(parser.parse("test.bc0"), callbacks, false);
|
||||
test.ok(printout == "Hello, world.\nYou don't look so good.\n",
|
||||
"test.bc0 - Did not print to screen correctly: output was " + printout);
|
||||
test.done();
|
||||
}
|
||||
|
||||
exports.testISHR = doTest("ishr.c0.bc0", -1);
|
||||
|
||||
exports.testISQRT = doTest("isqrt.c0.bc0", 122);
|
||||
|
||||
exports.testMID = doTest("mid.c0.bc0", 155);
|
||||
|
||||
// This one should throw an exception because an assertion fails
|
||||
exports.testSample25 = function(test) {
|
||||
test.throws(function () {
|
||||
c0vm.execute(parser.parse("sample2.5.c0.bc0"), callbacks, false)
|
||||
});
|
||||
test.done();
|
||||
}
|
||||
|
||||
exports.testIF = doTest("testif.c0.bc0", 32);
|
||||
|
||||
exports.testDSQUARED = doTest("dsquared.c0.bc0", 17068);
|
||||
|
||||
exports.testArrays = function(test) {
|
||||
test.throws(function () {
|
||||
c0vm.execute(parser.parse("arrays.c0.bc0"), callbacks, true)
|
||||
});
|
||||
test.done();
|
||||
}
|
||||
|
||||
exports.testStructs = function(test) {
|
||||
printout = "";
|
||||
var result = c0vm.execute(parser.parse("structs.c0.bc0"), callbacks, false);
|
||||
test.ok(printout == "potato chip123",
|
||||
"structs.c0.bc0 - Did not print to screen correctly, result was " +
|
||||
printout);
|
||||
test.done();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user