From 3278bfe6c47f135c0a5b73d704c173b786f232e7 Mon Sep 17 00:00:00 2001 From: Mitchell Plamann Date: Mon, 6 Apr 2015 02:22:37 -0400 Subject: [PATCH] Added native function support --- src/c0ffi.js | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/c0vm.js | 60 +++++++++++++++++++++------ src/index.js | 14 ++++++- test/tests.js | 37 +++++++++++++---- 4 files changed, 199 insertions(+), 22 deletions(-) create mode 100644 src/c0ffi.js diff --git a/src/c0ffi.js b/src/c0ffi.js new file mode 100644 index 0000000..c3ce859 --- /dev/null +++ b/src/c0ffi.js @@ -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 + diff --git a/src/c0vm.js b/src/c0vm.js index c3f50f6..9cb7e30 100755 --- a/src/c0vm.js +++ b/src/c0vm.js @@ -9,7 +9,11 @@ function log(message) { } function c0_assertion_failure(val) { - throw "c0 assertion failure: " + val; + throw ("c0 assertion failure: " + val); +} + +function c0_memory_error(val) { + throw ("c0 memory error: " + val); } var StackFrame = function(file, f) { @@ -23,7 +27,7 @@ var StackFrame = function(file, f) { this.file = file; } -var ProgramState = function(parsed_file) { +var ProgramState = function(parsed_file, callback_dict) { log("Creating program state with file " + parsed_file); var main_function = parsed_file.function_pool[0]; @@ -31,6 +35,19 @@ var ProgramState = function(parsed_file) { 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) { + dict[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. @@ -108,7 +125,7 @@ ProgramState.prototype.step = function() { case op.RETURN: var retVal = this.pop(); if (this.call_stack.length == 0) - throw retVal; + return retVal; this.frame = this.call_stack.pop(); this.push(retVal); @@ -183,7 +200,6 @@ ProgramState.prototype.step = function() { this.frame.pc++; var y = this.pop(); var x = this.pop(); - console.log("y="+y+", x="+x); if (y < 0 || y > 31) c0_arith_error("Shifting by too many bits"); this.push(x>>y); break; @@ -276,6 +292,30 @@ ProgramState.prototype.step = function() { 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: @@ -354,7 +394,6 @@ ProgramState.prototype.step = function() { " (" + opcode_name + ")\n"); throw "Error - unknown opcode"; } - return false; } // Takes in a parsed .bc0 file and runs it @@ -362,18 +401,13 @@ function execute(file, callbacks, v) { verbose = typeof v !== 'undefined' ? v : true; log("Initializing with file " + file); - var state = new ProgramState(file); + 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?) diff --git a/src/index.js b/src/index.js index dd3b7a5..94d2a1d 100644 --- a/src/index.js +++ b/src/index.js @@ -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,5 +10,14 @@ c0vm = require("./c0vm.js"); // console.log(file); // console.log(file.function_pool[0].code); -var file = parser.parse("../test/iadd.c0.bc0"); -console.log("Result is " + c0vm.execute(file)); +callbacks = {}; +callbacks[c0ffi.NATIVE_PRINT] = function(args) { + console.log("Print function says: " + args[0]); +} +callbacks["printint"] = function(args) { + console.log("Printint function says: " + args[0]); +} + +var file = parser.parse("../test/sample2.5.c0.bc0"); +console.log("Result is " + c0vm.execute(file, callbacks)); + diff --git a/test/tests.js b/test/tests.js index 0e2116b..ce060b1 100644 --- a/test/tests.js +++ b/test/tests.js @@ -1,7 +1,21 @@ -parser = require("../src/bytecode-parser.js") -c0vm = require("../src/c0vm.js") +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) { @@ -16,8 +30,10 @@ function doTest(filename, expected_result) { exports.testIADD = doTest("iadd.c0.bc0", -2); exports.testPrint = function(test) { + printout = ""; var result = c0vm.execute(parser.parse("test.bc0"), callbacks, false); - test.ok(result == 0, "test.bc0 - Did not print to screen correctly"); + 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(); } @@ -29,7 +45,9 @@ exports.testMID = doTest("mid.c0.bc0", 155); // This one should throw an exception because an assertion fails exports.testSample25 = function(test) { - test.throws(c0vm.execute(parser.parse("sample2.5.c0.bc0"), callbacks, false)); + test.throws(function () { + c0vm.execute(parser.parse("sample2.5.c0.bc0"), callbacks, false) + }); test.done(); } @@ -38,13 +56,18 @@ exports.testIF = doTest("testif.c0.bc0", 32); exports.testDSQUARED = doTest("dsquared.c0.bc0", 17068); exports.testArrays = function(test) { - var result = c0vm.execute(parser.parse("arrays.c0.bc0"), callbacks, false); - test.ok(false, "test.bc0 - Did not print to screen correctly"); + 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(false, "test.bc0 - Did not print to screen correctly"); + test.ok(printout == "expected result here", + "structs.c0.bc0 - Did not print to screen correctly, result was " + + printout); test.done(); } +