├── LICENSE ├── README.md └── stack-vm-lessons ├── lesson1 ├── main.cpp ├── makefile ├── stack-vm.cpp └── stack-vm.h ├── lesson2 ├── sasm │ ├── lexer.cpp │ └── lexer.h └── stack-vm │ ├── main.cpp │ ├── makefile │ ├── stack-vm.cpp │ └── stack-vm.h ├── lesson3 ├── sasm │ ├── lexer.cpp │ └── lexer.h └── stack-vm │ ├── main.cpp │ ├── makefile │ ├── stack-vm.cpp │ └── stack-vm.h ├── lesson4 ├── .gitignore ├── sasm │ ├── lexer.cpp │ ├── lexer.h │ ├── sasm.cpp │ └── test.sasm └── stack-vm │ ├── main.cpp │ ├── makefile │ ├── stack-vm.cpp │ └── stack-vm.h ├── lesson5 ├── sasm │ ├── lexer.cpp │ ├── lexer.h │ ├── lexer.o │ ├── makefile │ ├── out.bin │ ├── sasm │ ├── sasm.cpp │ ├── sasm.o │ └── test.sasm └── stack-vm │ ├── main.cpp │ ├── makefile │ ├── out.bin │ ├── stack-vm.cpp │ └── stack-vm.h └── lesson6 ├── sasm ├── lexer.cpp ├── lexer.h ├── makefile ├── sasm.cpp └── test.sasm └── stack-vm ├── main.cpp ├── makefile ├── out.bin ├── stack-vm.cpp └── stack-vm.h /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, pbohun 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of fasm-tutorials nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Stack VM Tutorials 2 | This project is designed to provide an example of building a simple stack-based virtual machine from scratch. This project is implemented in c++. 3 | 4 | ## Tutorial Structure 5 | Each lesson has a corresponding video which can be found here: 6 | 7 | https://www.youtube.com/channel/UChEygfZo7SZ64E0mp038Jvg 8 | 9 | Each lesson is designed to provide additional information or functionality to the virtual machine. 10 | 11 | ## Usage 12 | Each lesson is self-contained and can be compiled an run on its own. Simply run the `make` command in the directory of the lesson you wish to compile. 13 | -------------------------------------------------------------------------------- /stack-vm-lessons/lesson1/main.cpp: -------------------------------------------------------------------------------- 1 | #include "stack-vm.h" 2 | 3 | int main() { 4 | StackVM vm; 5 | std::vector prog{3, 4, 0x40000001, 0x40000000}; 6 | vm.loadProgram(prog); 7 | vm.run(); 8 | return 0; 9 | } 10 | -------------------------------------------------------------------------------- /stack-vm-lessons/lesson1/makefile: -------------------------------------------------------------------------------- 1 | CFLAGS=-std=c++11 2 | 3 | all: stack-vm 4 | 5 | stack-vm: stack-vm.o main.o 6 | $(CXX) $(CFLAGS) stack-vm.o main.o -o stack-vm 7 | 8 | main.o: main.cpp 9 | $(CXX) $(CFLAGS) -c main.cpp 10 | 11 | stack-vm.o: stack-vm.h stack-vm.cpp 12 | $(CXX) $(CFLAGS) -c stack-vm.cpp 13 | 14 | clean: 15 | rm -f *.o stack-vm 16 | -------------------------------------------------------------------------------- /stack-vm-lessons/lesson1/stack-vm.cpp: -------------------------------------------------------------------------------- 1 | #include "stack-vm.h" 2 | 3 | /* 4 | * Instruction format 5 | * header: 2 bits 6 | * data: 30 bits 7 | * 8 | * header format: 9 | * 0 => positive integer 10 | * 1 => primitive instruction 11 | * 2 => negative integer 12 | * 3 => undefined 13 | * */ 14 | 15 | // functions 16 | StackVM::StackVM() { 17 | memory.reserve(1000000); 18 | } 19 | i32 StackVM::getType(i32 instruction) { 20 | i32 type = 0xc0000000; 21 | type = (type & instruction) >> 30; 22 | return type; 23 | } 24 | i32 StackVM::getData(i32 instruction) { 25 | i32 data = 0x3fffffff; 26 | data = data & instruction; 27 | return data; 28 | } 29 | void StackVM::fetch() { 30 | pc++; 31 | } 32 | void StackVM::decode() { 33 | typ = getType(memory[pc]); 34 | dat = getData(memory[pc]); 35 | } 36 | void StackVM::execute() { 37 | if (typ == 0 || typ == 2) { 38 | sp++; 39 | memory[sp] = dat; 40 | } else { 41 | doPrimitive(); 42 | } 43 | } 44 | void StackVM::doPrimitive() { 45 | switch (dat) { 46 | case 0: // halt 47 | std::cout << "halt" << std::endl; 48 | running = 0; 49 | break; 50 | case 1: // add 51 | std::cout << "add " << memory[sp - 1] << " " << memory[sp] << std::endl; 52 | memory[sp - 1] = memory[sp - 1] + memory[sp]; 53 | sp--; 54 | break; 55 | } 56 | } 57 | void StackVM::run() { 58 | pc -= 1; 59 | while (running == 1) { 60 | fetch(); 61 | decode(); 62 | execute(); 63 | std::cout << "tos: " << memory[sp] << std::endl; 64 | } 65 | } 66 | void StackVM::loadProgram(std::vector prog) { 67 | for (i32 i = 0; i < prog.size(); i++) { 68 | memory[pc + i] = prog[i]; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /stack-vm-lessons/lesson1/stack-vm.h: -------------------------------------------------------------------------------- 1 | #ifndef STACK_VM_H 2 | #define STACK_VM_H 3 | 4 | #include 5 | #include 6 | 7 | // type definitions 8 | typedef int32_t i32; 9 | 10 | class StackVM { 11 | i32 pc = 100; // program counter 12 | i32 sp = 0; // stack pointer 13 | std::vector memory; 14 | i32 typ = 0; 15 | i32 dat = 0; 16 | i32 running = 1; 17 | 18 | // private function 19 | i32 getType(i32 instruction); 20 | i32 getData(i32 instruction); 21 | void fetch(); 22 | void decode(); 23 | void execute(); 24 | void doPrimitive(); 25 | 26 | public: 27 | // public functions 28 | StackVM(); 29 | void run(); 30 | void loadProgram(std::vector prog); 31 | }; 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /stack-vm-lessons/lesson2/sasm/lexer.cpp: -------------------------------------------------------------------------------- 1 | #include "lexer.h" 2 | 3 | strings Lexer::lex(std::strings s) { 4 | strings strlst; 5 | char lexeme[256]; 6 | int i = 0; 7 | int j = 0; 8 | State state = START; 9 | int done = 0; 10 | int len = s.length(); 11 | int balance = 0; 12 | 13 | while(i < len) { 14 | switch(state) { 15 | case START: 16 | if (my_isspace(s[i])) { 17 | state = SKIP; 18 | } else if (isgroup(s[i])) { 19 | if(s[i] == '"') { 20 | lexeme[j] = s[i]; 21 | j++; 22 | i++; 23 | } 24 | state = READBLOCK; 25 | } else if (s[i] == '/' && s[i + 1] == '/') { 26 | i += 2; 27 | state = COMMENT; 28 | } else { 29 | state = READCHAR; 30 | } 31 | break; 32 | case READCHAR: 33 | if (my_isspace(s[i])) { 34 | state = DUMP; 35 | } else if (s[i] == '\\') { 36 | i += 2; 37 | } else if (isgroup(s[i])) { 38 | if (s[i] == '"') { 39 | lexeme[j] = s[i]; 40 | j++; 41 | i++; 42 | } 43 | state = READBLOCK; 44 | } else if (isspecial(s[i])) { 45 | if (j == 0) { 46 | lexeme[j] = s[i]; 47 | j++; 48 | i++; 49 | } 50 | state = DUMP; 51 | } else if (s[i] == '/' && s[i + 1] == '/') { 52 | i += 2; 53 | state = COMMENT; 54 | } else { 55 | lexeme[j] = s[i]; 56 | j++; 57 | i++; 58 | } 59 | break; 60 | case READBLOCK: 61 | if (s[i] == beg_char && s[i] != '"') { 62 | balance++; 63 | lexeme[j] = s[i]; 64 | j++; 65 | i++; 66 | } else if (s[i] == end_char) { 67 | balance--; 68 | lexeme[j] = s[i]; 69 | j++; 70 | i++; 71 | if (balance <= 0) { 72 | state = DUMP; 73 | } 74 | } else if (end_char == '"' && s[i] == '\\') { 75 | // TODO: fix this to actually record the chars 76 | i += 2; 77 | } else { 78 | lexeme[j] = s[i]; 79 | j++; 80 | i++; 81 | } 82 | break; 83 | case SKIP; 84 | if (my_isspace(s[i])) { 85 | i++; 86 | } else { 87 | state = READCHAR; 88 | } 89 | break; 90 | case DUMP: 91 | if (j < 0) { 92 | lexeme[j] = 0; 93 | strlst.push_back(lexeme); 94 | j = 0; 95 | } 96 | state = START; 97 | break; 98 | case COMMENT: 99 | if (s[i] != '\n') { 100 | i++; 101 | } else { 102 | state = READCHAR; 103 | } 104 | break; 105 | case END: 106 | i = len; 107 | break; 108 | } 109 | } 110 | if (j > 0) { 111 | lexeme[j] = 0; 112 | strlst.push_back(lexeme); 113 | } 114 | return strlst 115 | } 116 | -------------------------------------------------------------------------------- /stack-vm-lessons/lesson2/sasm/lexer.h: -------------------------------------------------------------------------------- 1 | #ifndef LEXER_H 2 | #define LEXER_H 3 | 4 | #include 5 | #include 6 | 7 | typedef uint8_t byte; 8 | typedef std::vector strings; 9 | 10 | enum State : byte { 11 | START, 12 | READCHAR, 13 | READBLOCK, 14 | SKIP, 15 | DUMP, 16 | COMMENT, 17 | END 18 | } 19 | 20 | class Lexer { 21 | bool my_issspace(char c); 22 | bool isspecial(char c); 23 | bool isgroup(char c); 24 | char end_char, beg_char; 25 | public: 26 | strings lex(std::string s); 27 | }; 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /stack-vm-lessons/lesson2/stack-vm/main.cpp: -------------------------------------------------------------------------------- 1 | #include "stack-vm.h" 2 | 3 | int main() { 4 | StackVM vm; 5 | std::vector prog{3, 4, 0x40000001, 5, 0x40000002, 3, 0x40000003, 2, 0x40000004, 0x40000000}; 6 | vm.loadProgram(prog); 7 | vm.run(); 8 | return 0; 9 | } 10 | -------------------------------------------------------------------------------- /stack-vm-lessons/lesson2/stack-vm/makefile: -------------------------------------------------------------------------------- 1 | CFLAGS=-std=c++11 2 | 3 | all: stack-vm 4 | 5 | stack-vm: stack-vm.o main.o 6 | $(CXX) $(CFLAGS) stack-vm.o main.o -o stack-vm 7 | 8 | main.o: main.cpp 9 | $(CXX) $(CFLAGS) -c main.cpp 10 | 11 | stack-vm.o: stack-vm.h stack-vm.cpp 12 | $(CXX) $(CFLAGS) -c stack-vm.cpp 13 | 14 | clean: 15 | rm -f *.o stack-vm 16 | -------------------------------------------------------------------------------- /stack-vm-lessons/lesson2/stack-vm/stack-vm.cpp: -------------------------------------------------------------------------------- 1 | #include "stack-vm.h" 2 | 3 | /* 4 | * Instruction format 5 | * header: 2 bits 6 | * data: 30 bits 7 | * 8 | * header format: 9 | * 0 => positive integer 10 | * 1 => primitive instruction 11 | * 2 => negative integer 12 | * 3 => undefined 13 | * */ 14 | 15 | // functions 16 | StackVM::StackVM() { 17 | memory.reserve(1000000); 18 | } 19 | i32 StackVM::getType(i32 instruction) { 20 | i32 type = 0xc0000000; 21 | type = (type & instruction) >> 30; 22 | return type; 23 | } 24 | i32 StackVM::getData(i32 instruction) { 25 | i32 data = 0x3fffffff; 26 | data = data & instruction; 27 | return data; 28 | } 29 | void StackVM::fetch() { 30 | pc++; 31 | } 32 | void StackVM::decode() { 33 | typ = getType(memory[pc]); 34 | dat = getData(memory[pc]); 35 | } 36 | void StackVM::execute() { 37 | if (typ == 0 || typ == 2) { 38 | sp++; 39 | memory[sp] = dat; 40 | } else { 41 | doPrimitive(); 42 | } 43 | } 44 | void StackVM::doPrimitive() { 45 | switch (dat) { 46 | case 0: // halt 47 | std::cout << "halt" << std::endl; 48 | running = 0; 49 | break; 50 | case 1: // add 51 | std::cout << "add " << memory[sp - 1] << " " << memory[sp] << std::endl; 52 | memory[sp - 1] = memory[sp - 1] + memory[sp]; 53 | sp--; 54 | break; 55 | case 2: // sub 56 | std::cout << "sub " << memory[sp - 1] << " " << memory[sp] << std::endl; 57 | memory[sp - 1] = memory[sp - 1] - memory[sp]; 58 | sp--; 59 | break; 60 | case 3: // mul 61 | std::cout << "mul " << memory[sp - 1] << " " << memory[sp] << std::endl; 62 | memory[sp - 1] = memory[sp - 1] * memory[sp]; 63 | sp--; 64 | 65 | break; 66 | case 4: // div 67 | std::cout << "div " << memory[sp - 1] << " " << memory[sp] << std::endl; 68 | memory[sp - 1] = memory[sp - 1] / memory[sp]; 69 | sp--; 70 | break; 71 | } 72 | } 73 | void StackVM::run() { 74 | pc -= 1; 75 | while (running == 1) { 76 | fetch(); 77 | decode(); 78 | execute(); 79 | std::cout << "tos: " << memory[sp] << std::endl; 80 | } 81 | } 82 | void StackVM::loadProgram(std::vector prog) { 83 | for (i32 i = 0; i < prog.size(); i++) { 84 | memory[pc + i] = prog[i]; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /stack-vm-lessons/lesson2/stack-vm/stack-vm.h: -------------------------------------------------------------------------------- 1 | #ifndef STACK_VM_H 2 | #define STACK_VM_H 3 | 4 | #include 5 | #include 6 | 7 | // type definitions 8 | typedef int32_t i32; 9 | 10 | class StackVM { 11 | i32 pc = 100; // program counter 12 | i32 sp = 0; // stack pointer 13 | std::vector memory; 14 | i32 typ = 0; 15 | i32 dat = 0; 16 | i32 running = 1; 17 | 18 | // private function 19 | i32 getType(i32 instruction); 20 | i32 getData(i32 instruction); 21 | void fetch(); 22 | void decode(); 23 | void execute(); 24 | void doPrimitive(); 25 | 26 | public: 27 | // public functions 28 | StackVM(); 29 | void run(); 30 | void loadProgram(std::vector prog); 31 | }; 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /stack-vm-lessons/lesson3/sasm/lexer.cpp: -------------------------------------------------------------------------------- 1 | #include "lexer.h" 2 | 3 | strings Lexer::lex(std::string s) { 4 | strings strlst; 5 | char lexeme[256]; 6 | int i = 0; 7 | int j = 0; 8 | State state = START; 9 | int done = 0; 10 | int len = s.length(); 11 | int balance = 0; 12 | 13 | while(i < len) { 14 | switch(state) { 15 | case START: 16 | if (my_isspace(s[i])) { 17 | state = SKIP; 18 | } else if (isgroup(s[i])) { 19 | if(s[i] == '"') { 20 | lexeme[j] = s[i]; 21 | j++; 22 | i++; 23 | } 24 | state = READBLOCK; 25 | } else if (s[i] == '/' && s[i + 1] == '/') { 26 | i += 2; 27 | state = COMMENT; 28 | } else { 29 | state = READCHAR; 30 | } 31 | break; 32 | case READCHAR: 33 | if (my_isspace(s[i])) { 34 | state = DUMP; 35 | } else if (s[i] == '\\') { 36 | i += 2; 37 | } else if (isgroup(s[i])) { 38 | if (s[i] == '"') { 39 | lexeme[j] = s[i]; 40 | j++; 41 | i++; 42 | } 43 | state = READBLOCK; 44 | } else if (isspecial(s[i])) { 45 | if (j == 0) { 46 | lexeme[j] = s[i]; 47 | j++; 48 | i++; 49 | } 50 | state = DUMP; 51 | } else if (s[i] == '/' && s[i + 1] == '/') { 52 | i += 2; 53 | state = COMMENT; 54 | } else { 55 | lexeme[j] = s[i]; 56 | j++; 57 | i++; 58 | } 59 | break; 60 | case READBLOCK: 61 | if (s[i] == beg_char && s[i] != '"') { 62 | balance++; 63 | lexeme[j] = s[i]; 64 | j++; 65 | i++; 66 | } else if (s[i] == end_char) { 67 | balance--; 68 | lexeme[j] = s[i]; 69 | j++; 70 | i++; 71 | if (balance <= 0) { 72 | state = DUMP; 73 | } 74 | } else if (end_char == '"' && s[i] == '\\') { 75 | // TODO: fix this to actually record the chars 76 | i += 2; 77 | } else { 78 | lexeme[j] = s[i]; 79 | j++; 80 | i++; 81 | } 82 | break; 83 | case SKIP; 84 | if (my_isspace(s[i])) { 85 | i++; 86 | } else { 87 | state = READCHAR; 88 | } 89 | break; 90 | case DUMP: 91 | if (j < 0) { 92 | lexeme[j] = 0; 93 | strlst.push_back(lexeme); 94 | j = 0; 95 | } 96 | state = START; 97 | break; 98 | case COMMENT: 99 | if (s[i] != '\n') { 100 | i++; 101 | } else { 102 | state = READCHAR; 103 | } 104 | break; 105 | case END: 106 | i = len; 107 | break; 108 | } 109 | } 110 | if (j > 0) { 111 | lexeme[j] = 0; 112 | strlst.push_back(lexeme); 113 | } 114 | return strlst; 115 | } 116 | 117 | // this function allows us to define what a space is 118 | bool Lexer::my_isspace(char c) { 119 | switch(c) { 120 | case '\n': 121 | case '\r': 122 | case '\t': 123 | case '\v': 124 | case ' ': 125 | case '\f': 126 | return true; 127 | default: 128 | return false; 129 | } 130 | } 131 | bool Lexer::isgroup(char c) { 132 | beg_char = c; 133 | switch(c) { 134 | case '"': 135 | end_char = '"'; 136 | return true; 137 | case '(': 138 | end_char = ')'; 139 | return true; 140 | case ')': 141 | return true; 142 | default: 143 | return false; 144 | } 145 | } 146 | bool Lexer::isspecial(char c) { 147 | switch(c) { 148 | case '[': 149 | case ']': 150 | return true; 151 | default: 152 | return false; 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /stack-vm-lessons/lesson3/sasm/lexer.h: -------------------------------------------------------------------------------- 1 | #ifndef LEXER_H 2 | #define LEXER_H 3 | 4 | #include 5 | #include 6 | 7 | typedef uint8_t byte; 8 | typedef std::vector strings; 9 | 10 | enum State : byte { 11 | START, 12 | READCHAR, 13 | READBLOCK, 14 | SKIP, 15 | DUMP, 16 | COMMENT, 17 | END 18 | } 19 | 20 | class Lexer { 21 | bool my_isspace(char c); 22 | bool isspecial(char c); 23 | bool isgroup(char c); 24 | char end_char, beg_char; 25 | public: 26 | strings lex(std::string s); 27 | }; 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /stack-vm-lessons/lesson3/stack-vm/main.cpp: -------------------------------------------------------------------------------- 1 | #include "stack-vm.h" 2 | 3 | int main() { 4 | StackVM vm; 5 | std::vector prog{3, 4, 0x40000001, 5, 0x40000002, 3, 0x40000003, 2, 0x40000004, 0x40000000}; 6 | vm.loadProgram(prog); 7 | vm.run(); 8 | return 0; 9 | } 10 | -------------------------------------------------------------------------------- /stack-vm-lessons/lesson3/stack-vm/makefile: -------------------------------------------------------------------------------- 1 | CFLAGS=-std=c++11 2 | 3 | all: stack-vm 4 | 5 | stack-vm: stack-vm.o main.o 6 | $(CXX) $(CFLAGS) stack-vm.o main.o -o stack-vm 7 | 8 | main.o: main.cpp 9 | $(CXX) $(CFLAGS) -c main.cpp 10 | 11 | stack-vm.o: stack-vm.h stack-vm.cpp 12 | $(CXX) $(CFLAGS) -c stack-vm.cpp 13 | 14 | clean: 15 | rm -f *.o stack-vm 16 | -------------------------------------------------------------------------------- /stack-vm-lessons/lesson3/stack-vm/stack-vm.cpp: -------------------------------------------------------------------------------- 1 | #include "stack-vm.h" 2 | 3 | /* 4 | * Instruction format 5 | * header: 2 bits 6 | * data: 30 bits 7 | * 8 | * header format: 9 | * 0 => positive integer 10 | * 1 => primitive instruction 11 | * 2 => negative integer 12 | * 3 => undefined 13 | * */ 14 | 15 | // functions 16 | StackVM::StackVM() { 17 | memory.reserve(1000000); 18 | } 19 | i32 StackVM::getType(i32 instruction) { 20 | i32 type = 0xc0000000; 21 | type = (type & instruction) >> 30; 22 | return type; 23 | } 24 | i32 StackVM::getData(i32 instruction) { 25 | i32 data = 0x3fffffff; 26 | data = data & instruction; 27 | return data; 28 | } 29 | void StackVM::fetch() { 30 | pc++; 31 | } 32 | void StackVM::decode() { 33 | typ = getType(memory[pc]); 34 | dat = getData(memory[pc]); 35 | } 36 | void StackVM::execute() { 37 | if (typ == 0 || typ == 2) { 38 | sp++; 39 | memory[sp] = dat; 40 | } else { 41 | doPrimitive(); 42 | } 43 | } 44 | void StackVM::doPrimitive() { 45 | switch (dat) { 46 | case 0: // halt 47 | std::cout << "halt" << std::endl; 48 | running = 0; 49 | break; 50 | case 1: // add 51 | std::cout << "add " << memory[sp - 1] << " " << memory[sp] << std::endl; 52 | memory[sp - 1] = memory[sp - 1] + memory[sp]; 53 | sp--; 54 | break; 55 | case 2: // sub 56 | std::cout << "sub " << memory[sp - 1] << " " << memory[sp] << std::endl; 57 | memory[sp - 1] = memory[sp - 1] - memory[sp]; 58 | sp--; 59 | break; 60 | case 3: // mul 61 | std::cout << "mul " << memory[sp - 1] << " " << memory[sp] << std::endl; 62 | memory[sp - 1] = memory[sp - 1] * memory[sp]; 63 | sp--; 64 | 65 | break; 66 | case 4: // div 67 | std::cout << "div " << memory[sp - 1] << " " << memory[sp] << std::endl; 68 | memory[sp - 1] = memory[sp - 1] / memory[sp]; 69 | sp--; 70 | break; 71 | } 72 | } 73 | void StackVM::run() { 74 | pc -= 1; 75 | while (running == 1) { 76 | fetch(); 77 | decode(); 78 | execute(); 79 | std::cout << "tos: " << memory[sp] << std::endl; 80 | } 81 | } 82 | void StackVM::loadProgram(std::vector prog) { 83 | for (i32 i = 0; i < prog.size(); i++) { 84 | memory[pc + i] = prog[i]; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /stack-vm-lessons/lesson3/stack-vm/stack-vm.h: -------------------------------------------------------------------------------- 1 | #ifndef STACK_VM_H 2 | #define STACK_VM_H 3 | 4 | #include 5 | #include 6 | 7 | // type definitions 8 | typedef int32_t i32; 9 | 10 | class StackVM { 11 | i32 pc = 100; // program counter 12 | i32 sp = 0; // stack pointer 13 | std::vector memory; 14 | i32 typ = 0; 15 | i32 dat = 0; 16 | i32 running = 1; 17 | 18 | // private function 19 | i32 getType(i32 instruction); 20 | i32 getData(i32 instruction); 21 | void fetch(); 22 | void decode(); 23 | void execute(); 24 | void doPrimitive(); 25 | 26 | public: 27 | // public functions 28 | StackVM(); 29 | void run(); 30 | void loadProgram(std::vector prog); 31 | }; 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /stack-vm-lessons/lesson4/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | -------------------------------------------------------------------------------- /stack-vm-lessons/lesson4/sasm/lexer.cpp: -------------------------------------------------------------------------------- 1 | #include "lexer.h" 2 | 3 | strings Lexer::lex(std::string s) { 4 | strings strlst; 5 | char lexeme[256]; 6 | int i = 0; 7 | int j = 0; 8 | State state = START; 9 | int done = 0; 10 | int len = s.length(); 11 | int balance = 0; 12 | 13 | while(i < len) { 14 | switch(state) { 15 | case START: 16 | if (my_isspace(s[i])) { 17 | state = SKIP; 18 | } else if (isgroup(s[i])) { 19 | if(s[i] == '"') { 20 | lexeme[j] = s[i]; 21 | j++; 22 | i++; 23 | } 24 | state = READBLOCK; 25 | } else if (s[i] == '/' && s[i + 1] == '/') { 26 | i += 2; 27 | state = COMMENT; 28 | } else { 29 | state = READCHAR; 30 | } 31 | break; 32 | case READCHAR: 33 | if (my_isspace(s[i])) { 34 | state = DUMP; 35 | } else if (s[i] == '\\') { 36 | i += 2; 37 | } else if (isgroup(s[i])) { 38 | if (s[i] == '"') { 39 | lexeme[j] = s[i]; 40 | j++; 41 | i++; 42 | } 43 | state = READBLOCK; 44 | } else if (isspecial(s[i])) { 45 | if (j == 0) { 46 | lexeme[j] = s[i]; 47 | j++; 48 | i++; 49 | } 50 | state = DUMP; 51 | } else if (s[i] == '/' && s[i + 1] == '/') { 52 | i += 2; 53 | state = COMMENT; 54 | } else { 55 | lexeme[j] = s[i]; 56 | j++; 57 | i++; 58 | } 59 | break; 60 | case READBLOCK: 61 | if (s[i] == beg_char && s[i] != '"') { 62 | balance++; 63 | lexeme[j] = s[i]; 64 | j++; 65 | i++; 66 | } else if (s[i] == end_char) { 67 | balance--; 68 | lexeme[j] = s[i]; 69 | j++; 70 | i++; 71 | if (balance <= 0) { 72 | state = DUMP; 73 | } 74 | } else if (end_char == '"' && s[i] == '\\') { 75 | // TODO: fix this to actually record the chars 76 | i += 2; 77 | } else { 78 | lexeme[j] = s[i]; 79 | j++; 80 | i++; 81 | } 82 | break; 83 | case SKIP; 84 | if (my_isspace(s[i])) { 85 | i++; 86 | } else { 87 | state = READCHAR; 88 | } 89 | break; 90 | case DUMP: 91 | if (j < 0) { 92 | lexeme[j] = 0; 93 | strlst.push_back(lexeme); 94 | j = 0; 95 | } 96 | state = START; 97 | break; 98 | case COMMENT: 99 | if (s[i] != '\n') { 100 | i++; 101 | } else { 102 | state = READCHAR; 103 | } 104 | break; 105 | case END: 106 | i = len; 107 | break; 108 | } 109 | } 110 | if (j > 0) { 111 | lexeme[j] = 0; 112 | strlst.push_back(lexeme); 113 | } 114 | return strlst; 115 | } 116 | 117 | // this function allows us to define what a space is 118 | bool Lexer::my_isspace(char c) { 119 | switch(c) { 120 | case '\n': 121 | case '\r': 122 | case '\t': 123 | case '\v': 124 | case ' ': 125 | case '\f': 126 | return true; 127 | default: 128 | return false; 129 | } 130 | } 131 | bool Lexer::isgroup(char c) { 132 | beg_char = c; 133 | switch(c) { 134 | case '"': 135 | end_char = '"'; 136 | return true; 137 | case '(': 138 | end_char = ')'; 139 | return true; 140 | case ')': 141 | return true; 142 | default: 143 | return false; 144 | } 145 | } 146 | bool Lexer::isspecial(char c) { 147 | switch(c) { 148 | case '[': 149 | case ']': 150 | return true; 151 | default: 152 | return false; 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /stack-vm-lessons/lesson4/sasm/lexer.h: -------------------------------------------------------------------------------- 1 | #ifndef LEXER_H 2 | #define LEXER_H 3 | 4 | #include 5 | #include 6 | 7 | typedef uint8_t byte; 8 | typedef std::vector strings; 9 | 10 | enum State : byte { 11 | START, 12 | READCHAR, 13 | READBLOCK, 14 | SKIP, 15 | DUMP, 16 | COMMENT, 17 | END 18 | } 19 | 20 | class Lexer { 21 | bool my_isspace(char c); 22 | bool isspecial(char c); 23 | bool isgroup(char c); 24 | char end_char, beg_char; 25 | public: 26 | strings lex(std::string s); 27 | }; 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /stack-vm-lessons/lesson4/sasm/sasm.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "lexer.h" 4 | 5 | typdef uint32_t i32; 6 | 7 | using namespace std; 8 | 9 | // functions 10 | vector compileToInstructions(strings s); 11 | bool isInteger(string s); 12 | bool isPrimimitive(string s); 13 | i32 mapToNumber(string s); 14 | 15 | int main(int argc, char *argv[]) { 16 | // check for input errors 17 | if (argc != 2) { 18 | cout << "Usage: " << argv[0] << " " << endl; 19 | exit(1); 20 | } 21 | 22 | // read input file 23 | ifstream infile; 24 | infile.open(argv[1]); 25 | if (!infile.is_open()) { 26 | cout << "Error: could not open [" << argv[1] << "]" << endl; 27 | exit(1); 28 | } 29 | string line; 30 | string contents; 31 | while (getline(infile, line)) { 32 | contents += line + "\n"; 33 | } 34 | infile.close(); 35 | 36 | // parse file 37 | Lexer lexer; 38 | strings lexemes = lexer.lex(contents); 39 | 40 | // compile to binary 41 | vector instructions = compileToInstructions(lexemes); 42 | 43 | // write to binary file 44 | ofstream ofile; 45 | ofile.open("out.bin", ios::binary); 46 | for (i32 i = 0; i < instructions.size(); i++) { 47 | ofile.write(reinterpret_cast(&instructions[i]), sizeof(i32)); 48 | } 49 | ofile.close(); 50 | return 0; 51 | } 52 | 53 | vector compileToInstructions(strings s) { 54 | vector instructions; 55 | for (i32 i = 0; i < s.size(); i++) { 56 | if (isInteger(s[i])) { 57 | instructions.push_back(stoi(s[i])); 58 | } else { 59 | i32 instruction = mapToNumber(s[i]); 60 | if (instruction != -1) { 61 | instructions.push_back(instruction); 62 | } else { 63 | cout << "Error: Invalid instruction [" << s[i] << "]" << endl; 64 | } 65 | } 66 | } 67 | return instructions; 68 | } 69 | bool isInteger(string s) { 70 | for (i32 i = 0; i < s.length(); i++) { 71 | if (!isdigit(s[i])) { 72 | return false; 73 | } 74 | } 75 | return true; 76 | } 77 | i32 mapToNumber(string s) { 78 | if (s == "+") { 79 | return 0x40000001; 80 | } else if (s == "-") { 81 | return 0x40000002; 82 | } else if (s == "*") { 83 | return 0x40000003; 84 | } else if (s == "/") { 85 | return 0x40000004; 86 | } 87 | return -1; // invalid instruction 88 | } 89 | -------------------------------------------------------------------------------- /stack-vm-lessons/lesson4/sasm/test.sasm: -------------------------------------------------------------------------------- 1 | 3 4 + 2 * 2 + 4 / 2 | -------------------------------------------------------------------------------- /stack-vm-lessons/lesson4/stack-vm/main.cpp: -------------------------------------------------------------------------------- 1 | #include "stack-vm.h" 2 | 3 | int main() { 4 | StackVM vm; 5 | std::vector prog{3, 4, 0x40000001, 5, 0x40000002, 3, 0x40000003, 2, 0x40000004, 0x40000000}; 6 | vm.loadProgram(prog); 7 | vm.run(); 8 | return 0; 9 | } 10 | -------------------------------------------------------------------------------- /stack-vm-lessons/lesson4/stack-vm/makefile: -------------------------------------------------------------------------------- 1 | CFLAGS=-std=c++11 2 | 3 | all: stack-vm 4 | 5 | stack-vm: stack-vm.o main.o 6 | $(CXX) $(CFLAGS) stack-vm.o main.o -o stack-vm 7 | 8 | main.o: main.cpp 9 | $(CXX) $(CFLAGS) -c main.cpp 10 | 11 | stack-vm.o: stack-vm.h stack-vm.cpp 12 | $(CXX) $(CFLAGS) -c stack-vm.cpp 13 | 14 | clean: 15 | rm -f *.o stack-vm 16 | -------------------------------------------------------------------------------- /stack-vm-lessons/lesson4/stack-vm/stack-vm.cpp: -------------------------------------------------------------------------------- 1 | #include "stack-vm.h" 2 | 3 | /* 4 | * Instruction format 5 | * header: 2 bits 6 | * data: 30 bits 7 | * 8 | * header format: 9 | * 0 => positive integer 10 | * 1 => primitive instruction 11 | * 2 => negative integer 12 | * 3 => undefined 13 | * */ 14 | 15 | // functions 16 | StackVM::StackVM() { 17 | memory.reserve(1000000); 18 | } 19 | i32 StackVM::getType(i32 instruction) { 20 | i32 type = 0xc0000000; 21 | type = (type & instruction) >> 30; 22 | return type; 23 | } 24 | i32 StackVM::getData(i32 instruction) { 25 | i32 data = 0x3fffffff; 26 | data = data & instruction; 27 | return data; 28 | } 29 | void StackVM::fetch() { 30 | pc++; 31 | } 32 | void StackVM::decode() { 33 | typ = getType(memory[pc]); 34 | dat = getData(memory[pc]); 35 | } 36 | void StackVM::execute() { 37 | if (typ == 0 || typ == 2) { 38 | sp++; 39 | memory[sp] = dat; 40 | } else { 41 | doPrimitive(); 42 | } 43 | } 44 | void StackVM::doPrimitive() { 45 | switch (dat) { 46 | case 0: // halt 47 | std::cout << "halt" << std::endl; 48 | running = 0; 49 | break; 50 | case 1: // add 51 | std::cout << "add " << memory[sp - 1] << " " << memory[sp] << std::endl; 52 | memory[sp - 1] = memory[sp - 1] + memory[sp]; 53 | sp--; 54 | break; 55 | case 2: // sub 56 | std::cout << "sub " << memory[sp - 1] << " " << memory[sp] << std::endl; 57 | memory[sp - 1] = memory[sp - 1] - memory[sp]; 58 | sp--; 59 | break; 60 | case 3: // mul 61 | std::cout << "mul " << memory[sp - 1] << " " << memory[sp] << std::endl; 62 | memory[sp - 1] = memory[sp - 1] * memory[sp]; 63 | sp--; 64 | 65 | break; 66 | case 4: // div 67 | std::cout << "div " << memory[sp - 1] << " " << memory[sp] << std::endl; 68 | memory[sp - 1] = memory[sp - 1] / memory[sp]; 69 | sp--; 70 | break; 71 | } 72 | } 73 | void StackVM::run() { 74 | pc -= 1; 75 | while (running == 1) { 76 | fetch(); 77 | decode(); 78 | execute(); 79 | std::cout << "tos: " << memory[sp] << std::endl; 80 | } 81 | } 82 | void StackVM::loadProgram(std::vector prog) { 83 | for (i32 i = 0; i < prog.size(); i++) { 84 | memory[pc + i] = prog[i]; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /stack-vm-lessons/lesson4/stack-vm/stack-vm.h: -------------------------------------------------------------------------------- 1 | #ifndef STACK_VM_H 2 | #define STACK_VM_H 3 | 4 | #include 5 | #include 6 | 7 | // type definitions 8 | typedef int32_t i32; 9 | 10 | class StackVM { 11 | i32 pc = 100; // program counter 12 | i32 sp = 0; // stack pointer 13 | std::vector memory; 14 | i32 typ = 0; 15 | i32 dat = 0; 16 | i32 running = 1; 17 | 18 | // private function 19 | i32 getType(i32 instruction); 20 | i32 getData(i32 instruction); 21 | void fetch(); 22 | void decode(); 23 | void execute(); 24 | void doPrimitive(); 25 | 26 | public: 27 | // public functions 28 | StackVM(); 29 | void run(); 30 | void loadProgram(std::vector prog); 31 | }; 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /stack-vm-lessons/lesson5/sasm/lexer.cpp: -------------------------------------------------------------------------------- 1 | #include "lexer.h" 2 | 3 | strings Lexer::lex(std::string s) { 4 | strings strlst; 5 | char lexeme[256]; 6 | int i = 0; 7 | int j = 0; 8 | State state = START; 9 | int done = 0; 10 | int len = s.length(); 11 | int balance = 0; 12 | 13 | while(i < len) { 14 | switch(state) { 15 | case START: 16 | if (my_isspace(s[i])) { 17 | state = SKIP; 18 | } else if (isgroup(s[i])) { 19 | if(s[i] == '"') { 20 | lexeme[j] = s[i]; 21 | j++; 22 | i++; 23 | } 24 | state = READBLOCK; 25 | } else if (s[i] == '/' && s[i + 1] == '/') { 26 | i += 2; 27 | state = COMMENT; 28 | } else { 29 | state = READCHAR; 30 | } 31 | break; 32 | case READCHAR: 33 | if (my_isspace(s[i])) { 34 | state = DUMP; 35 | } else if (s[i] == '\\') { 36 | i += 2; 37 | } else if (isgroup(s[i])) { 38 | if (s[i] == '"') { 39 | lexeme[j] = s[i]; 40 | j++; 41 | i++; 42 | } 43 | state = READBLOCK; 44 | } else if (isspecial(s[i])) { 45 | if (j == 0) { 46 | lexeme[j] = s[i]; 47 | j++; 48 | i++; 49 | } 50 | state = DUMP; 51 | } else if (s[i] == '/' && s[i + 1] == '/') { 52 | i += 2; 53 | state = COMMENT; 54 | } else { 55 | lexeme[j] = s[i]; 56 | j++; 57 | i++; 58 | } 59 | break; 60 | case READBLOCK: 61 | if (s[i] == beg_char && s[i] != '"') { 62 | balance++; 63 | lexeme[j] = s[i]; 64 | j++; 65 | i++; 66 | } else if (s[i] == end_char) { 67 | balance--; 68 | lexeme[j] = s[i]; 69 | j++; 70 | i++; 71 | if (balance <= 0) { 72 | state = DUMP; 73 | } 74 | } else if (end_char == '"' && s[i] == '\\') { 75 | // TODO: fix this to actually record the chars 76 | i += 2; 77 | } else { 78 | lexeme[j] = s[i]; 79 | j++; 80 | i++; 81 | } 82 | break; 83 | case SKIP: 84 | if (my_isspace(s[i])) { 85 | i++; 86 | } else { 87 | state = READCHAR; 88 | } 89 | break; 90 | case DUMP: 91 | if (j > 0) { 92 | lexeme[j] = 0; 93 | strlst.push_back(lexeme); 94 | j = 0; 95 | } 96 | state = START; 97 | break; 98 | case COMMENT: 99 | if (s[i] != '\n') { 100 | i++; 101 | } else { 102 | state = READCHAR; 103 | } 104 | break; 105 | case END: 106 | i = len; 107 | break; 108 | } 109 | } 110 | if (j > 0) { 111 | lexeme[j] = 0; 112 | strlst.push_back(lexeme); 113 | } 114 | return strlst; 115 | } 116 | 117 | // this function allows us to define what a space is 118 | bool Lexer::my_isspace(char c) { 119 | switch(c) { 120 | case '\n': 121 | case '\r': 122 | case '\t': 123 | case '\v': 124 | case ' ': 125 | case '\f': 126 | return true; 127 | default: 128 | return false; 129 | } 130 | } 131 | bool Lexer::isgroup(char c) { 132 | beg_char = c; 133 | switch(c) { 134 | case '"': 135 | end_char = '"'; 136 | return true; 137 | case '(': 138 | end_char = ')'; 139 | return true; 140 | case ')': 141 | return true; 142 | default: 143 | return false; 144 | } 145 | } 146 | bool Lexer::isspecial(char c) { 147 | switch(c) { 148 | case '[': 149 | case ']': 150 | return true; 151 | default: 152 | return false; 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /stack-vm-lessons/lesson5/sasm/lexer.h: -------------------------------------------------------------------------------- 1 | #ifndef LEXER_H 2 | #define LEXER_H 3 | 4 | #include 5 | #include 6 | 7 | typedef uint8_t byte; 8 | typedef std::vector strings; 9 | 10 | enum State : byte { 11 | START, 12 | READCHAR, 13 | READBLOCK, 14 | SKIP, 15 | DUMP, 16 | COMMENT, 17 | END 18 | }; 19 | 20 | class Lexer { 21 | bool my_isspace(char c); 22 | bool isspecial(char c); 23 | bool isgroup(char c); 24 | char end_char, beg_char; 25 | public: 26 | strings lex(std::string s); 27 | }; 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /stack-vm-lessons/lesson5/sasm/lexer.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pbohun/stack-vm-tutorials/0bc0c80707aa8ad012cdf24a247eeff7708900cd/stack-vm-lessons/lesson5/sasm/lexer.o -------------------------------------------------------------------------------- /stack-vm-lessons/lesson5/sasm/makefile: -------------------------------------------------------------------------------- 1 | CFLAGS=-std=c++11 2 | 3 | all: sasm 4 | 5 | sasm: sasm.o lexer.o 6 | $(CXX) $(CFLAGS) sasm.o lexer.o -o $@ 7 | 8 | lexer.o: lexer.h lexer.cpp 9 | $(CXX) $(CFLAGS) -c lexer.cpp 10 | 11 | sasm.o: lexer.h sasm.cpp 12 | $(CXX) $(CFLAGS) -c sasm.cpp 13 | 14 | clean: 15 | rm -f sasm *.o *.bin 16 | -------------------------------------------------------------------------------- /stack-vm-lessons/lesson5/sasm/out.bin: -------------------------------------------------------------------------------- 1 | @@@@ -------------------------------------------------------------------------------- /stack-vm-lessons/lesson5/sasm/sasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pbohun/stack-vm-tutorials/0bc0c80707aa8ad012cdf24a247eeff7708900cd/stack-vm-lessons/lesson5/sasm/sasm -------------------------------------------------------------------------------- /stack-vm-lessons/lesson5/sasm/sasm.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "lexer.h" 4 | 5 | typedef uint32_t i32; 6 | 7 | using namespace std; 8 | 9 | // functions 10 | vector compileToInstructions(strings s); 11 | bool isInteger(string s); 12 | bool isPrimimitive(string s); 13 | i32 mapToNumber(string s); 14 | 15 | int main(int argc, char *argv[]) { 16 | // check for input errors 17 | if (argc != 2) { 18 | cout << "Usage: " << argv[0] << " " << endl; 19 | exit(1); 20 | } 21 | 22 | // read input file 23 | ifstream infile; 24 | infile.open(argv[1]); 25 | if (!infile.is_open()) { 26 | cout << "Error: could not open [" << argv[1] << "]" << endl; 27 | exit(1); 28 | } 29 | string line; 30 | string contents; 31 | while (getline(infile, line)) { 32 | contents += line + "\n"; 33 | } 34 | infile.close(); 35 | 36 | // parse file 37 | Lexer lexer; 38 | strings lexemes = lexer.lex(contents); 39 | 40 | // compile to binary 41 | vector instructions = compileToInstructions(lexemes); 42 | 43 | // write to binary file 44 | ofstream ofile; 45 | ofile.open("out.bin", ios::binary); 46 | for (i32 i = 0; i < instructions.size(); i++) { 47 | ofile.write(reinterpret_cast(&instructions[i]), sizeof(i32)); 48 | } 49 | ofile.close(); 50 | return 0; 51 | } 52 | 53 | vector compileToInstructions(strings s) { 54 | vector instructions; 55 | for (i32 i = 0; i < s.size(); i++) { 56 | if (isInteger(s[i])) { 57 | instructions.push_back(stoi(s[i])); 58 | } else { 59 | i32 instruction = mapToNumber(s[i]); 60 | if (instruction != -1) { 61 | instructions.push_back(instruction); 62 | } else { 63 | cout << "Error: Invalid instruction [" << s[i] << "]" << endl; 64 | } 65 | } 66 | } 67 | return instructions; 68 | } 69 | bool isInteger(string s) { 70 | for (i32 i = 0; i < s.length(); i++) { 71 | if (!isdigit(s[i])) { 72 | return false; 73 | } 74 | } 75 | return true; 76 | } 77 | i32 mapToNumber(string s) { 78 | if (s == "+") { 79 | return 0x40000001; 80 | } else if (s == "-") { 81 | return 0x40000002; 82 | } else if (s == "*") { 83 | return 0x40000003; 84 | } else if (s == "/") { 85 | return 0x40000004; 86 | } 87 | return -1; // invalid instruction 88 | } 89 | -------------------------------------------------------------------------------- /stack-vm-lessons/lesson5/sasm/sasm.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pbohun/stack-vm-tutorials/0bc0c80707aa8ad012cdf24a247eeff7708900cd/stack-vm-lessons/lesson5/sasm/sasm.o -------------------------------------------------------------------------------- /stack-vm-lessons/lesson5/sasm/test.sasm: -------------------------------------------------------------------------------- 1 | 3 4 + 2 * 2 + 4 / 2 | -------------------------------------------------------------------------------- /stack-vm-lessons/lesson5/stack-vm/main.cpp: -------------------------------------------------------------------------------- 1 | #include "stack-vm.h" 2 | 3 | int main() { 4 | StackVM vm; 5 | std::vector prog{3, 4, 0x40000001, 5, 0x40000002, 3, 0x40000003, 2, 0x40000004, 0x40000000}; 6 | vm.loadProgram(prog); 7 | vm.run(); 8 | return 0; 9 | } 10 | -------------------------------------------------------------------------------- /stack-vm-lessons/lesson5/stack-vm/makefile: -------------------------------------------------------------------------------- 1 | CFLAGS=-std=c++11 2 | 3 | all: stack-vm 4 | 5 | stack-vm: stack-vm.o main.o 6 | $(CXX) $(CFLAGS) stack-vm.o main.o -o stack-vm 7 | 8 | main.o: main.cpp 9 | $(CXX) $(CFLAGS) -c main.cpp 10 | 11 | stack-vm.o: stack-vm.h stack-vm.cpp 12 | $(CXX) $(CFLAGS) -c stack-vm.cpp 13 | 14 | clean: 15 | rm -f *.o stack-vm 16 | -------------------------------------------------------------------------------- /stack-vm-lessons/lesson5/stack-vm/out.bin: -------------------------------------------------------------------------------- 1 | @@@@ -------------------------------------------------------------------------------- /stack-vm-lessons/lesson5/stack-vm/stack-vm.cpp: -------------------------------------------------------------------------------- 1 | #include "stack-vm.h" 2 | 3 | /* 4 | * Instruction format 5 | * header: 2 bits 6 | * data: 30 bits 7 | * 8 | * header format: 9 | * 0 => positive integer 10 | * 1 => primitive instruction 11 | * 2 => negative integer 12 | * 3 => undefined 13 | * */ 14 | 15 | // functions 16 | StackVM::StackVM() { 17 | memory.reserve(1000000); 18 | } 19 | i32 StackVM::getType(i32 instruction) { 20 | i32 type = 0xc0000000; 21 | type = (type & instruction) >> 30; 22 | return type; 23 | } 24 | i32 StackVM::getData(i32 instruction) { 25 | i32 data = 0x3fffffff; 26 | data = data & instruction; 27 | return data; 28 | } 29 | void StackVM::fetch() { 30 | pc++; 31 | } 32 | void StackVM::decode() { 33 | typ = getType(memory[pc]); 34 | dat = getData(memory[pc]); 35 | } 36 | void StackVM::execute() { 37 | if (typ == 0 || typ == 2) { 38 | sp++; 39 | memory[sp] = dat; 40 | } else { 41 | doPrimitive(); 42 | } 43 | } 44 | void StackVM::doPrimitive() { 45 | switch (dat) { 46 | case 0: // halt 47 | std::cout << "halt" << std::endl; 48 | running = 0; 49 | break; 50 | case 1: // add 51 | std::cout << "add " << memory[sp - 1] << " " << memory[sp] << std::endl; 52 | memory[sp - 1] = memory[sp - 1] + memory[sp]; 53 | sp--; 54 | break; 55 | case 2: // sub 56 | std::cout << "sub " << memory[sp - 1] << " " << memory[sp] << std::endl; 57 | memory[sp - 1] = memory[sp - 1] - memory[sp]; 58 | sp--; 59 | break; 60 | case 3: // mul 61 | std::cout << "mul " << memory[sp - 1] << " " << memory[sp] << std::endl; 62 | memory[sp - 1] = memory[sp - 1] * memory[sp]; 63 | sp--; 64 | 65 | break; 66 | case 4: // div 67 | std::cout << "div " << memory[sp - 1] << " " << memory[sp] << std::endl; 68 | memory[sp - 1] = memory[sp - 1] / memory[sp]; 69 | sp--; 70 | break; 71 | } 72 | } 73 | void StackVM::run() { 74 | pc -= 1; 75 | while (running == 1) { 76 | fetch(); 77 | decode(); 78 | execute(); 79 | std::cout << "tos: " << memory[sp] << std::endl; 80 | } 81 | } 82 | void StackVM::loadProgram(std::vector prog) { 83 | for (i32 i = 0; i < prog.size(); i++) { 84 | memory[pc + i] = prog[i]; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /stack-vm-lessons/lesson5/stack-vm/stack-vm.h: -------------------------------------------------------------------------------- 1 | #ifndef STACK_VM_H 2 | #define STACK_VM_H 3 | 4 | #include 5 | #include 6 | 7 | // type definitions 8 | typedef int32_t i32; 9 | 10 | class StackVM { 11 | i32 pc = 100; // program counter 12 | i32 sp = 0; // stack pointer 13 | std::vector memory; 14 | i32 typ = 0; 15 | i32 dat = 0; 16 | i32 running = 1; 17 | 18 | // private function 19 | i32 getType(i32 instruction); 20 | i32 getData(i32 instruction); 21 | void fetch(); 22 | void decode(); 23 | void execute(); 24 | void doPrimitive(); 25 | 26 | public: 27 | // public functions 28 | StackVM(); 29 | void run(); 30 | void loadProgram(std::vector prog); 31 | }; 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /stack-vm-lessons/lesson6/sasm/lexer.cpp: -------------------------------------------------------------------------------- 1 | #include "lexer.h" 2 | 3 | strings Lexer::lex(std::string s) { 4 | strings strlst; 5 | char lexeme[256]; 6 | int i = 0; 7 | int j = 0; 8 | State state = START; 9 | int done = 0; 10 | int len = s.length(); 11 | int balance = 0; 12 | 13 | while(i < len) { 14 | switch(state) { 15 | case START: 16 | if (my_isspace(s[i])) { 17 | state = SKIP; 18 | } else if (isgroup(s[i])) { 19 | if(s[i] == '"') { 20 | lexeme[j] = s[i]; 21 | j++; 22 | i++; 23 | } 24 | state = READBLOCK; 25 | } else if (s[i] == '/' && s[i + 1] == '/') { 26 | i += 2; 27 | state = COMMENT; 28 | } else { 29 | state = READCHAR; 30 | } 31 | break; 32 | case READCHAR: 33 | if (my_isspace(s[i])) { 34 | state = DUMP; 35 | } else if (s[i] == '\\') { 36 | i += 2; 37 | } else if (isgroup(s[i])) { 38 | if (s[i] == '"') { 39 | lexeme[j] = s[i]; 40 | j++; 41 | i++; 42 | } 43 | state = READBLOCK; 44 | } else if (isspecial(s[i])) { 45 | if (j == 0) { 46 | lexeme[j] = s[i]; 47 | j++; 48 | i++; 49 | } 50 | state = DUMP; 51 | } else if (s[i] == '/' && s[i + 1] == '/') { 52 | i += 2; 53 | state = COMMENT; 54 | } else { 55 | lexeme[j] = s[i]; 56 | j++; 57 | i++; 58 | } 59 | break; 60 | case READBLOCK: 61 | if (s[i] == beg_char && s[i] != '"') { 62 | balance++; 63 | lexeme[j] = s[i]; 64 | j++; 65 | i++; 66 | } else if (s[i] == end_char) { 67 | balance--; 68 | lexeme[j] = s[i]; 69 | j++; 70 | i++; 71 | if (balance <= 0) { 72 | state = DUMP; 73 | } 74 | } else if (end_char == '"' && s[i] == '\\') { 75 | // TODO: fix this to actually record the chars 76 | i += 2; 77 | } else { 78 | lexeme[j] = s[i]; 79 | j++; 80 | i++; 81 | } 82 | break; 83 | case SKIP: 84 | if (my_isspace(s[i])) { 85 | i++; 86 | } else { 87 | state = READCHAR; 88 | } 89 | break; 90 | case DUMP: 91 | if (j > 0) { 92 | lexeme[j] = 0; 93 | strlst.push_back(lexeme); 94 | j = 0; 95 | } 96 | state = START; 97 | break; 98 | case COMMENT: 99 | if (s[i] != '\n') { 100 | i++; 101 | } else { 102 | state = READCHAR; 103 | } 104 | break; 105 | case END: 106 | i = len; 107 | break; 108 | } 109 | } 110 | if (j > 0) { 111 | lexeme[j] = 0; 112 | strlst.push_back(lexeme); 113 | } 114 | return strlst; 115 | } 116 | 117 | // this function allows us to define what a space is 118 | bool Lexer::my_isspace(char c) { 119 | switch(c) { 120 | case '\n': 121 | case '\r': 122 | case '\t': 123 | case '\v': 124 | case ' ': 125 | case '\f': 126 | return true; 127 | default: 128 | return false; 129 | } 130 | } 131 | bool Lexer::isgroup(char c) { 132 | beg_char = c; 133 | switch(c) { 134 | case '"': 135 | end_char = '"'; 136 | return true; 137 | case '(': 138 | end_char = ')'; 139 | return true; 140 | case ')': 141 | return true; 142 | default: 143 | return false; 144 | } 145 | } 146 | bool Lexer::isspecial(char c) { 147 | switch(c) { 148 | case '[': 149 | case ']': 150 | return true; 151 | default: 152 | return false; 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /stack-vm-lessons/lesson6/sasm/lexer.h: -------------------------------------------------------------------------------- 1 | #ifndef LEXER_H 2 | #define LEXER_H 3 | 4 | #include 5 | #include 6 | 7 | typedef uint8_t byte; 8 | typedef std::vector strings; 9 | 10 | enum State : byte { 11 | START, 12 | READCHAR, 13 | READBLOCK, 14 | SKIP, 15 | DUMP, 16 | COMMENT, 17 | END 18 | }; 19 | 20 | class Lexer { 21 | bool my_isspace(char c); 22 | bool isspecial(char c); 23 | bool isgroup(char c); 24 | char end_char, beg_char; 25 | public: 26 | strings lex(std::string s); 27 | }; 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /stack-vm-lessons/lesson6/sasm/makefile: -------------------------------------------------------------------------------- 1 | CFLAGS=-std=c++11 2 | 3 | all: sasm 4 | 5 | sasm: sasm.o lexer.o 6 | $(CXX) $(CFLAGS) sasm.o lexer.o -o $@ 7 | 8 | lexer.o: lexer.h lexer.cpp 9 | $(CXX) $(CFLAGS) -c lexer.cpp 10 | 11 | sasm.o: lexer.h sasm.cpp 12 | $(CXX) $(CFLAGS) -c sasm.cpp 13 | 14 | clean: 15 | rm -f sasm *.o *.bin 16 | -------------------------------------------------------------------------------- /stack-vm-lessons/lesson6/sasm/sasm.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "lexer.h" 4 | 5 | typedef uint32_t i32; 6 | 7 | using namespace std; 8 | 9 | // functions 10 | vector compileToInstructions(strings s); 11 | bool isInteger(string s); 12 | bool isPrimimitive(string s); 13 | i32 mapToNumber(string s); 14 | 15 | int main(int argc, char *argv[]) { 16 | // check for input errors 17 | if (argc != 2) { 18 | cout << "Usage: " << argv[0] << " " << endl; 19 | exit(1); 20 | } 21 | 22 | // read input file 23 | ifstream infile; 24 | infile.open(argv[1]); 25 | if (!infile.is_open()) { 26 | cout << "Error: could not open [" << argv[1] << "]" << endl; 27 | exit(1); 28 | } 29 | string line; 30 | string contents; 31 | while (getline(infile, line)) { 32 | contents += line + "\n"; 33 | } 34 | infile.close(); 35 | 36 | // parse file 37 | Lexer lexer; 38 | strings lexemes = lexer.lex(contents); 39 | 40 | // compile to binary 41 | vector instructions = compileToInstructions(lexemes); 42 | 43 | // write to binary file 44 | ofstream ofile; 45 | ofile.open("out.bin", ios::binary); 46 | for (i32 i = 0; i < instructions.size(); i++) { 47 | ofile.write(reinterpret_cast(&instructions[i]), sizeof(i32)); 48 | } 49 | ofile.close(); 50 | return 0; 51 | } 52 | 53 | vector compileToInstructions(strings s) { 54 | vector instructions; 55 | for (i32 i = 0; i < s.size(); i++) { 56 | if (isInteger(s[i])) { 57 | instructions.push_back(stoi(s[i])); 58 | } else { 59 | i32 instruction = mapToNumber(s[i]); 60 | if (instruction != -1) { 61 | instructions.push_back(instruction); 62 | } else { 63 | cout << "Error: Invalid instruction [" << s[i] << "]" << endl; 64 | } 65 | } 66 | } 67 | instructions.push_back(0x40000000); // always halt at the end 68 | return instructions; 69 | } 70 | bool isInteger(string s) { 71 | for (i32 i = 0; i < s.length(); i++) { 72 | if (!isdigit(s[i])) { 73 | return false; 74 | } 75 | } 76 | return true; 77 | } 78 | i32 mapToNumber(string s) { 79 | if (s == "+") { 80 | return 0x40000001; 81 | } else if (s == "-") { 82 | return 0x40000002; 83 | } else if (s == "*") { 84 | return 0x40000003; 85 | } else if (s == "/") { 86 | return 0x40000004; 87 | } 88 | return -1; // invalid instruction 89 | } 90 | -------------------------------------------------------------------------------- /stack-vm-lessons/lesson6/sasm/test.sasm: -------------------------------------------------------------------------------- 1 | 3 4 + 2 * 2 + 4 / 2 | -------------------------------------------------------------------------------- /stack-vm-lessons/lesson6/stack-vm/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "stack-vm.h" 4 | 5 | using namespace std; 6 | 7 | int main(int argc, char *argv[]) { 8 | if (argc < 2) { 9 | cout << "Usage: " << argv[0] << " " << endl; 10 | return 0; 11 | } 12 | 13 | ifstream r(argv[1], ios::binary); 14 | i32 i; 15 | vector prog; 16 | 17 | while (r.read((char*)&i, sizeof(i))) { 18 | prog.push_back(i); 19 | } 20 | 21 | StackVM vm; 22 | vm.loadProgram(prog); 23 | vm.run(); 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /stack-vm-lessons/lesson6/stack-vm/makefile: -------------------------------------------------------------------------------- 1 | CFLAGS=-std=c++11 2 | 3 | all: stack-vm 4 | 5 | stack-vm: stack-vm.o main.o 6 | $(CXX) $(CFLAGS) stack-vm.o main.o -o stack-vm 7 | 8 | main.o: main.cpp 9 | $(CXX) $(CFLAGS) -c main.cpp 10 | 11 | stack-vm.o: stack-vm.h stack-vm.cpp 12 | $(CXX) $(CFLAGS) -c stack-vm.cpp 13 | 14 | clean: 15 | rm -f *.o stack-vm 16 | -------------------------------------------------------------------------------- /stack-vm-lessons/lesson6/stack-vm/out.bin: -------------------------------------------------------------------------------- 1 | @@@@@ -------------------------------------------------------------------------------- /stack-vm-lessons/lesson6/stack-vm/stack-vm.cpp: -------------------------------------------------------------------------------- 1 | #include "stack-vm.h" 2 | 3 | /* 4 | * Instruction format 5 | * header: 2 bits 6 | * data: 30 bits 7 | * 8 | * header format: 9 | * 0 => positive integer 10 | * 1 => primitive instruction 11 | * 2 => negative integer 12 | * 3 => undefined 13 | * */ 14 | 15 | // functions 16 | StackVM::StackVM() { 17 | memory.reserve(1000000); 18 | } 19 | i32 StackVM::getType(i32 instruction) { 20 | i32 type = 0xc0000000; 21 | type = (type & instruction) >> 30; 22 | return type; 23 | } 24 | i32 StackVM::getData(i32 instruction) { 25 | i32 data = 0x3fffffff; 26 | data = data & instruction; 27 | return data; 28 | } 29 | void StackVM::fetch() { 30 | pc++; 31 | } 32 | void StackVM::decode() { 33 | typ = getType(memory[pc]); 34 | dat = getData(memory[pc]); 35 | } 36 | void StackVM::execute() { 37 | if (typ == 0 || typ == 2) { 38 | sp++; 39 | memory[sp] = dat; 40 | } else { 41 | doPrimitive(); 42 | } 43 | } 44 | void StackVM::doPrimitive() { 45 | switch (dat) { 46 | case 0: // halt 47 | std::cout << "halt" << std::endl; 48 | running = 0; 49 | break; 50 | case 1: // add 51 | std::cout << "add " << memory[sp - 1] << " " << memory[sp] << std::endl; 52 | memory[sp - 1] = memory[sp - 1] + memory[sp]; 53 | sp--; 54 | break; 55 | case 2: // sub 56 | std::cout << "sub " << memory[sp - 1] << " " << memory[sp] << std::endl; 57 | memory[sp - 1] = memory[sp - 1] - memory[sp]; 58 | sp--; 59 | break; 60 | case 3: // mul 61 | std::cout << "mul " << memory[sp - 1] << " " << memory[sp] << std::endl; 62 | memory[sp - 1] = memory[sp - 1] * memory[sp]; 63 | sp--; 64 | 65 | break; 66 | case 4: // div 67 | std::cout << "div " << memory[sp - 1] << " " << memory[sp] << std::endl; 68 | memory[sp - 1] = memory[sp - 1] / memory[sp]; 69 | sp--; 70 | break; 71 | } 72 | } 73 | void StackVM::run() { 74 | pc -= 1; 75 | while (running == 1) { 76 | fetch(); 77 | decode(); 78 | execute(); 79 | std::cout << "tos: " << memory[sp] << std::endl; 80 | } 81 | } 82 | void StackVM::loadProgram(std::vector prog) { 83 | for (i32 i = 0; i < prog.size(); i++) { 84 | memory[pc + i] = prog[i]; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /stack-vm-lessons/lesson6/stack-vm/stack-vm.h: -------------------------------------------------------------------------------- 1 | #ifndef STACK_VM_H 2 | #define STACK_VM_H 3 | 4 | #include 5 | #include 6 | 7 | // type definitions 8 | typedef int32_t i32; 9 | 10 | class StackVM { 11 | i32 pc = 100; // program counter 12 | i32 sp = 0; // stack pointer 13 | std::vector memory; 14 | i32 typ = 0; 15 | i32 dat = 0; 16 | i32 running = 1; 17 | 18 | // private function 19 | i32 getType(i32 instruction); 20 | i32 getData(i32 instruction); 21 | void fetch(); 22 | void decode(); 23 | void execute(); 24 | void doPrimitive(); 25 | 26 | public: 27 | // public functions 28 | StackVM(); 29 | void run(); 30 | void loadProgram(std::vector prog); 31 | }; 32 | 33 | #endif 34 | --------------------------------------------------------------------------------