[
  {
    "path": ".gitignore",
    "content": "*.class\n\n# Mobile Tools for Java (J2ME)\n.mtj.tmp/\n\n# Package Files #\n*.jar\n*.war\n*.ear\n\n# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml\nhs_err_pid*\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2014, Terence Parr\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this\n  list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice,\n  this list of conditions and the following disclaimer in the documentation\n  and/or other materials provided with the distribution.\n\n* Neither the name of the {organization} nor the names of its\n  contributors may be used to endorse or promote products derived from\n  this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
  },
  {
    "path": "README.md",
    "content": "simple-virtual-machine\n======================\n\nA simple VM for a talk on building VMs in Java. See [video](https://www.youtube.com/watch?v=OjaAToVkoTw) and [slides](http://www.slideshare.net/parrt/how-to-build-a-virtual-machine).\n\nThere are multiple branches:\n\n* [master](https://github.com/parrt/simple-virtual-machine). Basic instructions only (no function calls).\n* [add-functions](https://github.com/parrt/simple-virtual-machine/tree/add-functions). Includes CALL/RET instructions, runs factorial test function.\n* [split-stack](https://github.com/parrt/simple-virtual-machine/tree/split-stack). Split into operand stack and function call stack.\n* [func-meta-info](https://github.com/parrt/simple-virtual-machine/tree/func-meta-info).  CALL bytecode instruction takes an index into a metadata table for functions rather than an address and the number of arguments. This makes it much easier for bytecode compiler to generate code because it doesn't need to worry about forward references. This branch also properly allocates space for local variables.\n* [shatter-stack](https://github.com/parrt/simple-virtual-machine/tree/shatter-stack). Broke apart the `Context[]` stack into a linked-list with `invokingContext` as parent pointer to caller. added call stack for trace.\n\nSee also a [C version derived from split-stack](https://github.com/parrt/simple-virtual-machine-C). Parts derived from [codyebberson's C implementation](https://github.com/codyebberson/vm).\n"
  },
  {
    "path": "simple-virtual-machine.iml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<module type=\"JAVA_MODULE\" version=\"4\">\n  <component name=\"NewModuleRootManager\" inherit-compiler-output=\"true\">\n    <exclude-output />\n    <content url=\"file://$MODULE_DIR$\">\n      <sourceFolder url=\"file://$MODULE_DIR$/src\" isTestSource=\"false\" />\n    </content>\n    <orderEntry type=\"inheritedJdk\" />\n    <orderEntry type=\"sourceFolder\" forTests=\"false\" />\n  </component>\n</module>\n\n"
  },
  {
    "path": "simple-virtual-machine.ipr",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"CompilerConfiguration\">\n    <wildcardResourcePatterns>\n      <entry name=\"!?*.java\" />\n      <entry name=\"!?*.form\" />\n      <entry name=\"!?*.class\" />\n      <entry name=\"!?*.groovy\" />\n      <entry name=\"!?*.scala\" />\n      <entry name=\"!?*.flex\" />\n      <entry name=\"!?*.kt\" />\n      <entry name=\"!?*.clj\" />\n    </wildcardResourcePatterns>\n  </component>\n  <component name=\"CopyrightManager\" default=\"\" />\n  <component name=\"DependencyValidationManager\">\n    <option name=\"SKIP_IMPORT_STATEMENTS\" value=\"false\" />\n  </component>\n  <component name=\"Encoding\" useUTFGuessing=\"true\" native2AsciiForPropertiesFiles=\"false\" />\n  <component name=\"IdProvider\" IDEtalkID=\"97D7DBFBCE4D11CA285C5356222AF1E5\" />\n  <component name=\"InspectionProjectProfileManager\">\n    <profile version=\"1.0\">\n      <option name=\"myName\" value=\"Project Default\" />\n    </profile>\n    <version value=\"1.0\" />\n  </component>\n  <component name=\"KotlinCommonCompilerArguments\">\n    <option name=\"languageVersion\" value=\"1.1\" />\n    <option name=\"apiVersion\" value=\"1.1\" />\n  </component>\n  <component name=\"Palette2\">\n    <group name=\"Swing\">\n      <item class=\"com.intellij.uiDesigner.HSpacer\" tooltip-text=\"Horizontal Spacer\" icon=\"/com/intellij/uiDesigner/icons/hspacer.png\" removable=\"false\" auto-create-binding=\"false\" can-attach-label=\"false\">\n        <default-constraints vsize-policy=\"1\" hsize-policy=\"6\" anchor=\"0\" fill=\"1\" />\n      </item>\n      <item class=\"com.intellij.uiDesigner.VSpacer\" tooltip-text=\"Vertical Spacer\" icon=\"/com/intellij/uiDesigner/icons/vspacer.png\" removable=\"false\" auto-create-binding=\"false\" can-attach-label=\"false\">\n        <default-constraints vsize-policy=\"6\" hsize-policy=\"1\" anchor=\"0\" fill=\"2\" />\n      </item>\n      <item class=\"javax.swing.JPanel\" icon=\"/com/intellij/uiDesigner/icons/panel.png\" removable=\"false\" auto-create-binding=\"false\" can-attach-label=\"false\">\n        <default-constraints vsize-policy=\"3\" hsize-policy=\"3\" anchor=\"0\" fill=\"3\" />\n      </item>\n      <item class=\"javax.swing.JScrollPane\" icon=\"/com/intellij/uiDesigner/icons/scrollPane.png\" removable=\"false\" auto-create-binding=\"false\" can-attach-label=\"true\">\n        <default-constraints vsize-policy=\"7\" hsize-policy=\"7\" anchor=\"0\" fill=\"3\" />\n      </item>\n      <item class=\"javax.swing.JButton\" icon=\"/com/intellij/uiDesigner/icons/button.png\" removable=\"false\" auto-create-binding=\"true\" can-attach-label=\"false\">\n        <default-constraints vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"0\" fill=\"1\" />\n        <initial-values>\n          <property name=\"text\" value=\"Button\" />\n        </initial-values>\n      </item>\n      <item class=\"javax.swing.JRadioButton\" icon=\"/com/intellij/uiDesigner/icons/radioButton.png\" removable=\"false\" auto-create-binding=\"true\" can-attach-label=\"false\">\n        <default-constraints vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"8\" fill=\"0\" />\n        <initial-values>\n          <property name=\"text\" value=\"RadioButton\" />\n        </initial-values>\n      </item>\n      <item class=\"javax.swing.JCheckBox\" icon=\"/com/intellij/uiDesigner/icons/checkBox.png\" removable=\"false\" auto-create-binding=\"true\" can-attach-label=\"false\">\n        <default-constraints vsize-policy=\"0\" hsize-policy=\"3\" anchor=\"8\" fill=\"0\" />\n        <initial-values>\n          <property name=\"text\" value=\"CheckBox\" />\n        </initial-values>\n      </item>\n      <item class=\"javax.swing.JLabel\" icon=\"/com/intellij/uiDesigner/icons/label.png\" removable=\"false\" auto-create-binding=\"false\" can-attach-label=\"false\">\n        <default-constraints vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"8\" fill=\"0\" />\n        <initial-values>\n          <property name=\"text\" value=\"Label\" />\n        </initial-values>\n      </item>\n      <item class=\"javax.swing.JTextField\" icon=\"/com/intellij/uiDesigner/icons/textField.png\" removable=\"false\" auto-create-binding=\"true\" can-attach-label=\"true\">\n        <default-constraints vsize-policy=\"0\" hsize-policy=\"6\" anchor=\"8\" fill=\"1\">\n          <preferred-size width=\"150\" height=\"-1\" />\n        </default-constraints>\n      </item>\n      <item class=\"javax.swing.JPasswordField\" icon=\"/com/intellij/uiDesigner/icons/passwordField.png\" removable=\"false\" auto-create-binding=\"true\" can-attach-label=\"true\">\n        <default-constraints vsize-policy=\"0\" hsize-policy=\"6\" anchor=\"8\" fill=\"1\">\n          <preferred-size width=\"150\" height=\"-1\" />\n        </default-constraints>\n      </item>\n      <item class=\"javax.swing.JFormattedTextField\" icon=\"/com/intellij/uiDesigner/icons/formattedTextField.png\" removable=\"false\" auto-create-binding=\"true\" can-attach-label=\"true\">\n        <default-constraints vsize-policy=\"0\" hsize-policy=\"6\" anchor=\"8\" fill=\"1\">\n          <preferred-size width=\"150\" height=\"-1\" />\n        </default-constraints>\n      </item>\n      <item class=\"javax.swing.JTextArea\" icon=\"/com/intellij/uiDesigner/icons/textArea.png\" removable=\"false\" auto-create-binding=\"true\" can-attach-label=\"true\">\n        <default-constraints vsize-policy=\"6\" hsize-policy=\"6\" anchor=\"0\" fill=\"3\">\n          <preferred-size width=\"150\" height=\"50\" />\n        </default-constraints>\n      </item>\n      <item class=\"javax.swing.JTextPane\" icon=\"/com/intellij/uiDesigner/icons/textPane.png\" removable=\"false\" auto-create-binding=\"true\" can-attach-label=\"true\">\n        <default-constraints vsize-policy=\"6\" hsize-policy=\"6\" anchor=\"0\" fill=\"3\">\n          <preferred-size width=\"150\" height=\"50\" />\n        </default-constraints>\n      </item>\n      <item class=\"javax.swing.JEditorPane\" icon=\"/com/intellij/uiDesigner/icons/editorPane.png\" removable=\"false\" auto-create-binding=\"true\" can-attach-label=\"true\">\n        <default-constraints vsize-policy=\"6\" hsize-policy=\"6\" anchor=\"0\" fill=\"3\">\n          <preferred-size width=\"150\" height=\"50\" />\n        </default-constraints>\n      </item>\n      <item class=\"javax.swing.JComboBox\" icon=\"/com/intellij/uiDesigner/icons/comboBox.png\" removable=\"false\" auto-create-binding=\"true\" can-attach-label=\"true\">\n        <default-constraints vsize-policy=\"0\" hsize-policy=\"2\" anchor=\"8\" fill=\"1\" />\n      </item>\n      <item class=\"javax.swing.JTable\" icon=\"/com/intellij/uiDesigner/icons/table.png\" removable=\"false\" auto-create-binding=\"true\" can-attach-label=\"false\">\n        <default-constraints vsize-policy=\"6\" hsize-policy=\"6\" anchor=\"0\" fill=\"3\">\n          <preferred-size width=\"150\" height=\"50\" />\n        </default-constraints>\n      </item>\n      <item class=\"javax.swing.JList\" icon=\"/com/intellij/uiDesigner/icons/list.png\" removable=\"false\" auto-create-binding=\"true\" can-attach-label=\"false\">\n        <default-constraints vsize-policy=\"6\" hsize-policy=\"2\" anchor=\"0\" fill=\"3\">\n          <preferred-size width=\"150\" height=\"50\" />\n        </default-constraints>\n      </item>\n      <item class=\"javax.swing.JTree\" icon=\"/com/intellij/uiDesigner/icons/tree.png\" removable=\"false\" auto-create-binding=\"true\" can-attach-label=\"false\">\n        <default-constraints vsize-policy=\"6\" hsize-policy=\"6\" anchor=\"0\" fill=\"3\">\n          <preferred-size width=\"150\" height=\"50\" />\n        </default-constraints>\n      </item>\n      <item class=\"javax.swing.JTabbedPane\" icon=\"/com/intellij/uiDesigner/icons/tabbedPane.png\" removable=\"false\" auto-create-binding=\"true\" can-attach-label=\"false\">\n        <default-constraints vsize-policy=\"3\" hsize-policy=\"3\" anchor=\"0\" fill=\"3\">\n          <preferred-size width=\"200\" height=\"200\" />\n        </default-constraints>\n      </item>\n      <item class=\"javax.swing.JSplitPane\" icon=\"/com/intellij/uiDesigner/icons/splitPane.png\" removable=\"false\" auto-create-binding=\"false\" can-attach-label=\"false\">\n        <default-constraints vsize-policy=\"3\" hsize-policy=\"3\" anchor=\"0\" fill=\"3\">\n          <preferred-size width=\"200\" height=\"200\" />\n        </default-constraints>\n      </item>\n      <item class=\"javax.swing.JSpinner\" icon=\"/com/intellij/uiDesigner/icons/spinner.png\" removable=\"false\" auto-create-binding=\"true\" can-attach-label=\"true\">\n        <default-constraints vsize-policy=\"0\" hsize-policy=\"6\" anchor=\"8\" fill=\"1\" />\n      </item>\n      <item class=\"javax.swing.JSlider\" icon=\"/com/intellij/uiDesigner/icons/slider.png\" removable=\"false\" auto-create-binding=\"true\" can-attach-label=\"false\">\n        <default-constraints vsize-policy=\"0\" hsize-policy=\"6\" anchor=\"8\" fill=\"1\" />\n      </item>\n      <item class=\"javax.swing.JSeparator\" icon=\"/com/intellij/uiDesigner/icons/separator.png\" removable=\"false\" auto-create-binding=\"false\" can-attach-label=\"false\">\n        <default-constraints vsize-policy=\"6\" hsize-policy=\"6\" anchor=\"0\" fill=\"3\" />\n      </item>\n      <item class=\"javax.swing.JProgressBar\" icon=\"/com/intellij/uiDesigner/icons/progressbar.png\" removable=\"false\" auto-create-binding=\"true\" can-attach-label=\"false\">\n        <default-constraints vsize-policy=\"0\" hsize-policy=\"6\" anchor=\"0\" fill=\"1\" />\n      </item>\n      <item class=\"javax.swing.JToolBar\" icon=\"/com/intellij/uiDesigner/icons/toolbar.png\" removable=\"false\" auto-create-binding=\"false\" can-attach-label=\"false\">\n        <default-constraints vsize-policy=\"0\" hsize-policy=\"6\" anchor=\"0\" fill=\"1\">\n          <preferred-size width=\"-1\" height=\"20\" />\n        </default-constraints>\n      </item>\n      <item class=\"javax.swing.JToolBar$Separator\" icon=\"/com/intellij/uiDesigner/icons/toolbarSeparator.png\" removable=\"false\" auto-create-binding=\"false\" can-attach-label=\"false\">\n        <default-constraints vsize-policy=\"0\" hsize-policy=\"0\" anchor=\"0\" fill=\"1\" />\n      </item>\n      <item class=\"javax.swing.JScrollBar\" icon=\"/com/intellij/uiDesigner/icons/scrollbar.png\" removable=\"false\" auto-create-binding=\"true\" can-attach-label=\"false\">\n        <default-constraints vsize-policy=\"6\" hsize-policy=\"0\" anchor=\"0\" fill=\"2\" />\n      </item>\n    </group>\n  </component>\n  <component name=\"ProjectModuleManager\">\n    <modules>\n      <module fileurl=\"file://$PROJECT_DIR$/simple-virtual-machine.iml\" filepath=\"$PROJECT_DIR$/simple-virtual-machine.iml\" />\n    </modules>\n  </component>\n  <component name=\"ProjectRootManager\" version=\"2\" languageLevel=\"JDK_1_6\" default=\"false\" project-jdk-name=\"1.8\" project-jdk-type=\"JavaSDK\">\n    <output url=\"file://$PROJECT_DIR$/out\" />\n  </component>\n  <component name=\"VcsDirectoryMappings\">\n    <mapping directory=\"\" vcs=\"Git\" />\n  </component>\n</project>"
  },
  {
    "path": "src/vm/Bytecode.java",
    "content": "package vm;\n\npublic class Bytecode {\n\tpublic static class Instruction {\n\t\tString name; // E.g., \"iadd\", \"call\"\n\t\tint n = 0;\n\t\tpublic Instruction(String name) { this(name,0); }\n\t\tpublic Instruction(String name, int nargs) {\n\t\t\tthis.name = name;\n\t\t\tthis.n = nargs;\n\t\t}\n\t}\n\n\t// INSTRUCTION BYTECODES (byte is signed; use a short to keep 0..255)\n\tpublic static final short IADD = 1;     // int add\n\tpublic static final short ISUB = 2;\n\tpublic static final short IMUL = 3;\n\tpublic static final short ILT  = 4;     // int less than\n\tpublic static final short IEQ  = 5;     // int equal\n\tpublic static final short BR   = 6;     // branch\n\tpublic static final short BRT  = 7;     // branch if true\n\tpublic static final short BRF  = 8;     // branch if true\n\tpublic static final short ICONST = 9;   // push constant integer\n\tpublic static final short LOAD   = 10;  // load from local context\n\tpublic static final short GLOAD  = 11;  // load from global memory\n\tpublic static final short STORE  = 12;  // store in local context\n\tpublic static final short GSTORE = 13;  // store in global memory\n\tpublic static final short PRINT  = 14;  // print stack top\n\tpublic static final short POP  = 15;    // throw away top of stack\n\tpublic static final short CALL = 16;\n\tpublic static final short RET  = 17;    // return with/without value\n\n\tpublic static final short HALT = 18;\n\n\tpublic static Instruction[] instructions = new Instruction[] {\n\t\tnull, // <INVALID>\n\t\tnew Instruction(\"iadd\"), // index is the opcode\n\t\tnew Instruction(\"isub\"),\n\t\tnew Instruction(\"imul\"),\n\t\tnew Instruction(\"ilt\"),\n\t\tnew Instruction(\"ieq\"),\n\t\tnew Instruction(\"br\", 1),\n\t\tnew Instruction(\"brt\", 1),\n\t\tnew Instruction(\"brf\", 1),\n\t\tnew Instruction(\"iconst\", 1),\n\t\tnew Instruction(\"load\", 1),\n\t\tnew Instruction(\"gload\", 1),\n\t\tnew Instruction(\"store\", 1),\n\t\tnew Instruction(\"gstore\", 1),\n\t\tnew Instruction(\"print\"),\n\t\tnew Instruction(\"pop\"),\n\t\tnew Instruction(\"call\", 1), // call index of function in meta-info table\n\t\tnew Instruction(\"ret\"),\n\t\tnew Instruction(\"halt\")\n\t};\n}\n"
  },
  {
    "path": "src/vm/Context.java",
    "content": "package vm;\n\n/** To call, push one of these and pop to return */\npublic class Context {\n\tContext invokingContext;\t// parent in the stack or \"caller\"\n\tFuncMetaData metadata;\t\t// info about function we're executing\n\tint returnip;\n\tint[] locals; // args + locals, indexed from 0\n\n\tpublic Context(Context invokingContext, int returnip, FuncMetaData metadata) {\n\t\tthis.invokingContext = invokingContext;\n\t\tthis.returnip = returnip;\n\t\tthis.metadata = metadata;\n\t\tlocals = new int[metadata.nargs+metadata.nlocals];\n\t}\n}\n"
  },
  {
    "path": "src/vm/FuncMetaData.java",
    "content": "package vm;\n\npublic class FuncMetaData {\n\tpublic String name;\n\tpublic int nargs;\n\tpublic int nlocals;\n\tpublic int address; // bytecode address\n\n\tpublic FuncMetaData(String name, int nargs, int nlocals, int address) {\n\t\tthis.name = name;\n\t\tthis.nargs = nargs;\n\t\tthis.nlocals = nlocals;\n\t\tthis.address = address;\n\t}\n}\n"
  },
  {
    "path": "src/vm/Test.java",
    "content": "package vm;\n\nimport static vm.Bytecode.BR;\nimport static vm.Bytecode.BRF;\nimport static vm.Bytecode.CALL;\nimport static vm.Bytecode.GLOAD;\nimport static vm.Bytecode.GSTORE;\nimport static vm.Bytecode.HALT;\nimport static vm.Bytecode.IADD;\nimport static vm.Bytecode.ICONST;\nimport static vm.Bytecode.ILT;\nimport static vm.Bytecode.IMUL;\nimport static vm.Bytecode.ISUB;\nimport static vm.Bytecode.LOAD;\nimport static vm.Bytecode.PRINT;\nimport static vm.Bytecode.RET;\nimport static vm.Bytecode.STORE;\n\npublic class Test {\n\tstatic int[] hello = {\n\t\tICONST, 1,\n\t\tICONST, 2,\n\t\tIADD,\n\t\tPRINT,\n\t\tHALT\n\t};\n\n\tstatic int[] loop = {\n\t// .GLOBALS 2; N, I\n\t// N = 10\t\t\t\t\t\tADDRESS\n\t\t\tICONST, 10,\t\t\t\t// 0\n\t\t\tGSTORE, 0,\t\t\t\t// 2\n\t// I = 0\n\t\t\tICONST, 0,\t\t\t\t// 4\n\t\t\tGSTORE, 1,\t\t\t\t// 6\n\t// WHILE I<N:\n\t// START (8):\n\t\t\tGLOAD, 1,\t\t\t\t// 8\n\t\t\tGLOAD, 0,\t\t\t\t// 10\n\t\t\tILT,\t\t\t\t\t// 12\n\t\t\tBRF, 24,\t\t\t\t// 13\n\t//     I = I + 1\n\t\t\tGLOAD, 1,\t\t\t\t// 15\n\t\t\tICONST, 1,\t\t\t\t// 17\n\t\t\tIADD,\t\t\t\t\t// 19\n\t\t\tGSTORE, 1,\t\t\t\t// 20\n\t\t\tBR, 8,\t\t\t\t\t// 22\n\t// DONE (24):\n\t// PRINT \"LOOPED \"+N+\" TIMES.\"\n\t\t\tHALT\t\t\t\t\t// 24\n\t};\n\tstatic FuncMetaData[] loop_metadata = {\n\t\tnew FuncMetaData(\"main\", 0, 0, 0)\n\t};\n\n\tstatic int FACTORIAL_INDEX = 1;\n\tstatic int FACTORIAL_ADDRESS = 0;\n\tstatic int MAIN_ADDRESS = 21;\n\tstatic int[] factorial = {\n//.def factorial: ARGS=1, LOCALS=0\tADDRESS\n//\tIF N < 2 RETURN 1\n\t\t\tLOAD, 0,\t\t\t\t// 0\n\t\t\tICONST, 2,\t\t\t\t// 2\n\t\t\tILT,\t\t\t\t\t// 4\n\t\t\tBRF, 10,\t\t\t\t// 5\n\t\t\tICONST, 1,\t\t\t\t// 7\n\t\t\tRET,\t\t\t\t\t// 9\n//CONT:\n//\tRETURN N * FACT(N-1)\n\t\t\tLOAD, 0,\t\t\t\t// 10\n\t\t\tLOAD, 0,\t\t\t\t// 12\n\t\t\tICONST, 1,\t\t\t\t// 14\n\t\t\tISUB,\t\t\t\t\t// 16\n\t\t\tCALL, FACTORIAL_INDEX,\t// 17\n\t\t\tIMUL,\t\t\t\t\t// 19\n\t\t\tRET,\t\t\t\t\t// 20\n//.DEF MAIN: ARGS=0, LOCALS=0\n// PRINT FACT(1)\n\t\t\tICONST, 5,\t\t\t\t// 21    <-- MAIN METHOD!\n\t\t\tCALL, FACTORIAL_INDEX,\t// 23\n\t\t\tPRINT,\t\t\t\t\t// 25\n\t\t\tHALT\t\t\t\t\t// 26\n\t};\n\tstatic FuncMetaData[] factorial_metadata = {\n\t\t//.def factorial: ARGS=1, LOCALS=0\tADDRESS\n\t\tnew FuncMetaData(\"main\", 0, 0, MAIN_ADDRESS),\n\t\tnew FuncMetaData(\"factorial\", 1, 0, FACTORIAL_ADDRESS)\n\t};\n\n\tstatic int[] f = {\n\t//\t\t\t\t\t\t\t\tADDRESS\n\t//.def main() { print f(10); }\n\t\tICONST, 10,\t\t\t\t\t// 0\n\t\tCALL, 1,\t\t\t\t\t// 2\n\t\tPRINT,\t\t\t\t\t\t// 4\n\t\tHALT,\t\t\t\t\t\t// 5\n\t//.def f(x): ARGS=1, LOCALS=1\n\t//  a = x;\n\t\tLOAD, 0,\t\t\t\t\t// 6\t<-- start of f\n\t\tSTORE, 1,\n\t// return 2*a\n\t\tLOAD, 1,\n\t\tICONST, 2,\n\t\tIMUL,\n\t\tRET\n\t};\n\tstatic FuncMetaData[] f_metadata = {\n\t\tnew FuncMetaData(\"main\", 0, 0, 0),\n\t\tnew FuncMetaData(\"f\", 1, 1, 6)\n\t};\n\n\n\tpublic static void main(String[] args) {\n\t\tVM vm = new VM(factorial, 0, factorial_metadata);\n\t\tvm.trace = true;\n\t\tvm.exec(factorial_metadata[0].address);\n\n\t\tvm = new VM(f, 2, f_metadata);\n\t\tvm.exec(f_metadata[0].address);\n\t\tvm.dumpDataMemory();\n\n\t\tvm = new VM(loop, 2, loop_metadata);\n\t\tvm.exec(loop_metadata[0].address);\n\t}\n}\n"
  },
  {
    "path": "src/vm/VM.java",
    "content": "package vm;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static vm.Bytecode.BR;\nimport static vm.Bytecode.BRF;\nimport static vm.Bytecode.BRT;\nimport static vm.Bytecode.CALL;\nimport static vm.Bytecode.GLOAD;\nimport static vm.Bytecode.GSTORE;\nimport static vm.Bytecode.HALT;\nimport static vm.Bytecode.IADD;\nimport static vm.Bytecode.ICONST;\nimport static vm.Bytecode.IEQ;\nimport static vm.Bytecode.ILT;\nimport static vm.Bytecode.IMUL;\nimport static vm.Bytecode.ISUB;\nimport static vm.Bytecode.LOAD;\nimport static vm.Bytecode.POP;\nimport static vm.Bytecode.PRINT;\nimport static vm.Bytecode.RET;\nimport static vm.Bytecode.STORE;\n\n/** A simple stack-based interpreter */\npublic class VM {\n\tpublic static final int DEFAULT_STACK_SIZE = 1000;\n\tpublic static final int DEFAULT_CALL_STACK_SIZE = 1000;\n\tpublic static final int FALSE = 0;\n\tpublic static final int TRUE = 1;\n\n\t// registers\n\tint ip;             // instruction pointer register\n\tint sp = -1;  \t\t// stack pointer register\n\n\t// memory\n\tint[] code;         // word-addressable code memory but still bytecodes.\n\tint[] globals;      // global variable space\n\tint[] stack;\t\t// Operand stack, grows upwards\n\tContext ctx;\t\t// the active context\n\n\t/** Metadata about the functions allows us to refer to functions by\n\t * \ttheir index in this table. It makes code generation easier for\n\t * \tthe bytecode compiler because it doesn't have to resolve\n\t *  addresses for forward references. It can generate simply\n\t *  \"CALL i\" where i is the index of the function. Later, the\n\t *  compiler can store the function address in the metadata table\n\t *  when the code is generated for that function.\n\t */\n\tFuncMetaData[] metadata;\n\n\tpublic boolean trace = false;\n\n\tpublic VM(int[] code, int nglobals, FuncMetaData[] metadata) {\n\t\tthis.code = code;\n\t\tglobals = new int[nglobals];\n\t\tstack = new int[DEFAULT_STACK_SIZE];\n\t\tthis.metadata = metadata;\n\t}\n\n\tpublic void exec(int startip) {\n\t\tip = startip;\n\t\tctx = new Context(null,0,metadata[0]); // simulate a call to main()\n\t\tcpu();\n\t}\n\n\t/** Simulate the fetch-decode execute cycle */\n\tprotected void cpu() {\n\t\tint opcode = code[ip];\n\t\tint a,b,addr,regnum;\n\t\twhile (opcode!= HALT && ip < code.length) {\n\t\t\tif ( trace ) System.err.printf(\"%-35s\", disInstr());\n\t\t\tip++; //jump to next instruction or to operand\n\t\t\tswitch (opcode) {\n\t\t\t\tcase IADD:\n\t\t\t\t\tb = stack[sp--];   \t\t\t// 2nd opnd at top of stack\n\t\t\t\t\ta = stack[sp--]; \t\t\t// 1st opnd 1 below top\n\t\t\t\t\tstack[++sp] = a + b;      \t// push result\n\t\t\t\t\tbreak;\n\t\t\t\tcase ISUB:\n\t\t\t\t\tb = stack[sp--];\n\t\t\t\t\ta = stack[sp--];\n\t\t\t\t\tstack[++sp] = a - b;\n\t\t\t\t\tbreak;\n\t\t\t\tcase IMUL:\n\t\t\t\t\tb = stack[sp--];\n\t\t\t\t\ta = stack[sp--];\n\t\t\t\t\tstack[++sp] = a * b;\n\t\t\t\t\tbreak;\n\t\t\t\tcase ILT :\n\t\t\t\t\tb = stack[sp--];\n\t\t\t\t\ta = stack[sp--];\n\t\t\t\t\tstack[++sp] = (a < b) ? TRUE : FALSE;\n\t\t\t\t\tbreak;\n\t\t\t\tcase IEQ :\n\t\t\t\t\tb = stack[sp--];\n\t\t\t\t\ta = stack[sp--];\n\t\t\t\t\tstack[++sp] = (a == b) ? TRUE : FALSE;\n\t\t\t\t\tbreak;\n\t\t\t\tcase BR :\n\t\t\t\t\tip = code[ip++];\n\t\t\t\t\tbreak;\n\t\t\t\tcase BRT :\n\t\t\t\t\taddr = code[ip++];\n\t\t\t\t\tif ( stack[sp--]==TRUE ) ip = addr;\n\t\t\t\t\tbreak;\n\t\t\t\tcase BRF :\n\t\t\t\t\taddr = code[ip++];\n\t\t\t\t\tif ( stack[sp--]==FALSE ) ip = addr;\n\t\t\t\t\tbreak;\n\t\t\t\tcase ICONST:\n\t\t\t\t\tstack[++sp] = code[ip++]; // push operand\n\t\t\t\t\tbreak;\n\t\t\t\tcase LOAD : // load local or arg\n\t\t\t\t\tregnum = code[ip++];\n\t\t\t\t\tstack[++sp] = ctx.locals[regnum];\n\t\t\t\t\tbreak;\n\t\t\t\tcase GLOAD :// load from global memory\n\t\t\t\t\taddr = code[ip++];\n\t\t\t\t\tstack[++sp] = globals[addr];\n\t\t\t\t\tbreak;\n\t\t\t\tcase STORE :\n\t\t\t\t\tregnum = code[ip++];\n\t\t\t\t\tctx.locals[regnum] = stack[sp--];\n\t\t\t\t\tbreak;\n\t\t\t\tcase GSTORE :\n\t\t\t\t\taddr = code[ip++];\n\t\t\t\t\tglobals[addr] = stack[sp--];\n\t\t\t\t\tbreak;\n\t\t\t\tcase PRINT :\n\t\t\t\t\tSystem.out.println(stack[sp--]);\n\t\t\t\t\tbreak;\n\t\t\t\tcase POP:\n\t\t\t\t\t--sp;\n\t\t\t\t\tbreak;\n\t\t\t\tcase CALL :\n\t\t\t\t\t// expects all args on stack\n\t\t\t\t\tint findex = code[ip++];\t\t\t// index of target function\n\t\t\t\t\tint nargs = metadata[findex].nargs;\t// how many args got pushed\n\t\t\t\t\tctx = new Context(ctx,ip,metadata[findex]);\n\t\t\t\t\t// copy args into new context\n\t\t\t\t\tint firstarg = sp-nargs+1;\n\t\t\t\t\tfor (int i=0; i<nargs; i++) {\n\t\t\t\t\t\tctx.locals[i] = stack[firstarg+i];\n\t\t\t\t\t}\n\t\t\t\t\tsp -= nargs;\n\t\t\t\t\tip = metadata[findex].address;\t\t// jump to function\n\t\t\t\t\tbreak;\n\t\t\t\tcase RET:\n\t\t\t\t\tip = ctx.returnip;\n\t\t\t\t\tctx = ctx.invokingContext;\t\t\t// pop\n\t\t\t\t\tbreak;\n\t\t\t\tdefault :\n\t\t\t\t\tthrow new Error(\"invalid opcode: \"+opcode+\" at ip=\"+(ip-1));\n\t\t\t}\n\t\t\tif ( trace ) System.err.printf(\"%-22s %s\\n\", stackString(), callStackString());\n\t\t\topcode = code[ip];\n\t\t}\n\t\tif ( trace ) System.err.printf(\"%-35s\", disInstr());\n\t\tif ( trace ) System.err.println(stackString());\n\t\tif ( trace ) dumpDataMemory();\n\t}\n\n\tprotected String stackString() {\n\t\tStringBuilder buf = new StringBuilder();\n\t\tbuf.append(\"stack=[\");\n\t\tfor (int i = 0; i <= sp; i++) {\n\t\t\tint o = stack[i];\n\t\t\tbuf.append(\" \");\n\t\t\tbuf.append(o);\n\t\t}\n\t\tbuf.append(\" ]\");\n\t\treturn buf.toString();\n\t}\n\n\tprotected String callStackString() {\n\t\tList<String> stack = new ArrayList<String>();\n\t\tContext c = ctx;\n\t\twhile ( c!=null ) {\n\t\t\tif ( c.metadata!=null ) {\n\t\t\t\tstack.add(0, c.metadata.name);\n\t\t\t}\n\t\t\tc = c.invokingContext;\n\t\t}\n\t\treturn \"calls=\"+stack.toString();\n\t}\n\n\tprotected String disInstr() {\n\t\tint opcode = code[ip];\n\t\tString opName = Bytecode.instructions[opcode].name;\n\t\tStringBuilder buf = new StringBuilder();\n\t\tbuf.append(String.format(\"%04d:\\t%-11s\", ip, opName));\n\t\tint nargs = Bytecode.instructions[opcode].n;\n\t\tif ( opcode==CALL ) {\n\t\t\tbuf.append(metadata[code[ip+1]].name);\n\t\t}\n\t\telse if ( nargs>0 ) {\n\t\t\tList<String> operands = new ArrayList<String>();\n\t\t\tfor (int i=ip+1; i<=ip+nargs; i++) {\n\t\t\t\toperands.add(String.valueOf(code[i]));\n\t\t\t}\n\t\t\tfor (int i = 0; i<operands.size(); i++) {\n\t\t\t\tString s = operands.get(i);\n\t\t\t\tif ( i>0 ) buf.append(\", \");\n\t\t\t\tbuf.append(s);\n\t\t\t}\n\t\t}\n\t\treturn buf.toString();\n\t}\n\n\tprotected void dumpDataMemory() {\n\t\tSystem.err.println(\"Data memory:\");\n\t\tint addr = 0;\n\t\tfor (int o : globals) {\n\t\t\tSystem.err.printf(\"%04d: %s\\n\", addr, o);\n\t\t\taddr++;\n\t\t}\n\t\tSystem.err.println();\n\t}\n}\n"
  }
]