diff --git a/src/c0vm.js b/src/c0vm.js index 02d8535..13338a9 100755 --- a/src/c0vm.js +++ b/src/c0vm.js @@ -1,10 +1,17 @@ op = require("./opcodes"); +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; +} + var StackFrame = function(file, f) { log("Creating stack frame"); this.stack = []; @@ -12,7 +19,7 @@ var StackFrame = function(file, f) { this.program = f.code; this.variables = []; for (var i = 0; i < f.num_vars; i++) - variables.push(0); + this.variables.push(0); this.file = file; } @@ -21,7 +28,7 @@ var ProgramState = function(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.frame]; + this.call_stack = []; this.file = parsed_file; } @@ -33,13 +40,23 @@ 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)) { - var address_offset = (this.frame.program[this.frame.pc+1] * 0x1000) + - this.frame.program[this.frame.pc+2]; - this.frame.pc += address_offset; + this.goto_offset(); } else { this.frame.pc += 3; } @@ -47,7 +64,7 @@ ProgramState.prototype.doIf = function(f) { ProgramState.prototype.step = function() { var opcode = this.frame.program[this.frame.pc] - log("Running opcode " + + log("0x" + this.frame.pc.toString(16) + " Running opcode " + op.lookup_table[opcode]); switch (opcode) { // Stack manipulation @@ -81,8 +98,13 @@ ProgramState.prototype.step = function() { // Returning from a function case op.RETURN: var retVal = this.pop(); - throw retVal; + if (this.call_stack.length == 0) + throw retVal; + this.frame = this.call_stack.pop(); + this.push(retVal); + + break; // Arithmetic case op.IADD: this.frame.pc++; @@ -200,20 +222,20 @@ ProgramState.prototype.step = function() { case op.IF_CMPNE: this.doIf(function (x,y) {return y != x;}); break; - case op.IF_CMPLT: + case op.IF_ICMPLT: this.doIf(function (x,y) {return y > x;}); break; - case op.IF_CMPGE: + case op.IF_ICMPGE: this.doIf(function (x,y) {return y <= x;}); break; - case op.IF_CMPGT: + case op.IF_ICMPGT: this.doIf(function (x,y) {return y < x;}); break; - case op.IF_CMPLE: + case op.IF_ICMPLE: this.doIf(function (x,y) {return y >= x;}); break; case op.GOTO: - this.doIf(function (x,y) {return true;}); + this.goto_offset(); break; case op.ATHROW: this.frame.pc++; @@ -226,8 +248,34 @@ ProgramState.prototype.step = function() { 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; + default: - console.log("Error: Unknown opcode: 0x" + opcode.toString(16) + "\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; diff --git a/src/opcodes.js b/src/opcodes.js index b0ed6eb..98ec086 100644 --- a/src/opcodes.js +++ b/src/opcodes.js @@ -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", diff --git a/test/dsquared.c0 b/test/dsquared.c0 new file mode 100644 index 0000000..860baae --- /dev/null +++ b/test/dsquared.c0 @@ -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; +} + diff --git a/test/dsquared.c0.bc0 b/test/dsquared.c0.bc0 new file mode 100644 index 0000000..d95b37a --- /dev/null +++ b/test/dsquared.c0.bc0 @@ -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 + +#
+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 # + + +# +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 + diff --git a/test/dsquared.c0.bc0out b/test/dsquared.c0.bc0out new file mode 100644 index 0000000..52d2ba8 --- /dev/null +++ b/test/dsquared.c0.bc0out @@ -0,0 +1 @@ +17068 diff --git a/test/dsquared.c0.c0out b/test/dsquared.c0.c0out new file mode 100644 index 0000000..52d2ba8 --- /dev/null +++ b/test/dsquared.c0.c0out @@ -0,0 +1 @@ +17068 diff --git a/test/dsquared.c0.ex b/test/dsquared.c0.ex new file mode 100755 index 0000000..60c7863 Binary files /dev/null and b/test/dsquared.c0.ex differ diff --git a/test/isqrt.c0 b/test/isqrt.c0 new file mode 100644 index 0000000..b628688 --- /dev/null +++ b/test/isqrt.c0 @@ -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; +} + diff --git a/test/isqrt.c0.bc0 b/test/isqrt.c0.bc0 new file mode 100644 index 0000000..2a42549 --- /dev/null +++ b/test/isqrt.c0.bc0 @@ -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 + +#
+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 + diff --git a/test/isqrt.c0.bc0out b/test/isqrt.c0.bc0out new file mode 100644 index 0000000..e69de29 diff --git a/test/isqrt.c0.c0out b/test/isqrt.c0.c0out new file mode 100644 index 0000000..9f54fe3 --- /dev/null +++ b/test/isqrt.c0.c0out @@ -0,0 +1 @@ +122 diff --git a/test/isqrt.c0.ex b/test/isqrt.c0.ex new file mode 100755 index 0000000..40a9a81 Binary files /dev/null and b/test/isqrt.c0.ex differ diff --git a/test/mid.c0 b/test/mid.c0 new file mode 100644 index 0000000..c3efe9a --- /dev/null +++ b/test/mid.c0 @@ -0,0 +1,10 @@ +int main() +{ + int low = 0x15; + int high = 0x122; + int mid; + + mid = low + (high - low)/2; + return mid; +} + diff --git a/test/mid.c0.bc0 b/test/mid.c0.bc0 new file mode 100644 index 0000000..0d2323d --- /dev/null +++ b/test/mid.c0.bc0 @@ -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 + +#
+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 + diff --git a/test/mid.c0.bc0out b/test/mid.c0.bc0out new file mode 100644 index 0000000..bb79365 --- /dev/null +++ b/test/mid.c0.bc0out @@ -0,0 +1 @@ +155 diff --git a/test/mid.c0.c0out b/test/mid.c0.c0out new file mode 100644 index 0000000..bb79365 --- /dev/null +++ b/test/mid.c0.c0out @@ -0,0 +1 @@ +155 diff --git a/test/mid.c0.ex b/test/mid.c0.ex new file mode 100755 index 0000000..07c414a Binary files /dev/null and b/test/mid.c0.ex differ diff --git a/test/sample2.5.c0 b/test/sample2.5.c0 new file mode 100644 index 0000000..0a6bccb --- /dev/null +++ b/test/sample2.5.c0 @@ -0,0 +1,7 @@ +int main () { + int i; int sum = 0; + for (i = 15; i <= 122; i++) + sum += i; + assert(sum == 0); + return sum; +} diff --git a/test/sample2.5.c0.bc0 b/test/sample2.5.c0.bc0 new file mode 100644 index 0000000..6a97909 --- /dev/null +++ b/test/sample2.5.c0.bc0 @@ -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 + +#
+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 + diff --git a/test/sample2.5.c0.bc0out b/test/sample2.5.c0.bc0out new file mode 100644 index 0000000..e69de29 diff --git a/test/sample2.5.c0.c0out b/test/sample2.5.c0.c0out new file mode 100644 index 0000000..e69de29 diff --git a/test/sample2.5.c0.ex b/test/sample2.5.c0.ex new file mode 100755 index 0000000..f3055ed Binary files /dev/null and b/test/sample2.5.c0.ex differ diff --git a/test/testif.c0 b/test/testif.c0 new file mode 100644 index 0000000..173b796 --- /dev/null +++ b/test/testif.c0 @@ -0,0 +1,8 @@ +int main() { + int x = 4; + if (x < 3) + x = 23; + else + x = 32; + return x; +} diff --git a/test/testif.c0.bc0 b/test/testif.c0.bc0 new file mode 100644 index 0000000..578e4be --- /dev/null +++ b/test/testif.c0.bc0 @@ -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 + +#
+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 + diff --git a/test/testif.c0.bc0out b/test/testif.c0.bc0out new file mode 100644 index 0000000..f5c8955 --- /dev/null +++ b/test/testif.c0.bc0out @@ -0,0 +1 @@ +32 diff --git a/test/testif.c0.c0out b/test/testif.c0.c0out new file mode 100644 index 0000000..f5c8955 --- /dev/null +++ b/test/testif.c0.c0out @@ -0,0 +1 @@ +32 diff --git a/test/testif.c0.ex b/test/testif.c0.ex new file mode 100755 index 0000000..10fa75d Binary files /dev/null and b/test/testif.c0.ex differ diff --git a/test/tests.js b/test/tests.js index 334bf81..6229ddb 100644 --- a/test/tests.js +++ b/test/tests.js @@ -3,12 +3,17 @@ c0vm = require("../src/c0vm.js") var callbacks = {} -// Tests that IADD, BIPUSH, and RETURN (from main) work -exports.testIADD = function(test) { - var result = c0vm.execute(parser.parse("iadd.c0.bc0"), callbacks, false); - test.ok(result == -2, "iadd.c0.bc0 - Error in IADD, BIPUSH, or RETURN"); - test.done(); -}; +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) { var result = c0vm.execute(parser.parse("test.bc0"), callbacks, false); @@ -16,8 +21,19 @@ exports.testPrint = function(test) { test.done(); } -exports.testISHR = function(test) { - var result = c0vm.execute(parser.parse("ishr.c0.bc0"), callbacks, false); - test.ok(result == -1, "ishr.c0.bc0 - -1 >> 1 gave " + result + ", not -1"); +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(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); +