├── rc ├── requirements.txt ├── .gitignore ├── rcc.py ├── rc_lex.py ├── rc_visitor.py ├── rc_ast.py ├── rc_parse.py ├── rc_semantics.py └── rc_compiler.py ├── .gitattributes ├── test ├── test.cpp ├── test.h ├── lib │ └── catch2_LICENSE ├── test_conversions.cpp ├── test_system.cpp ├── test_registers.cpp ├── test_stack.cpp ├── test_memory.cpp ├── test_branching.cpp └── test_arithmetic.cpp ├── .gitignore ├── .github ├── CONTRIBUTING.md └── CODE_OF_CONDUCT.md ├── examples ├── asm │ ├── helloworld.asm │ ├── test.asm │ ├── subroutines.asm │ ├── benchmark.asm │ └── primes.asm └── rc │ ├── test.c │ └── test.asm ├── src ├── main.cpp ├── vm.h └── vm.cpp ├── assembler ├── assembler.py ├── data.py └── internals.py ├── LICENSE ├── Makefile ├── .travis.yml └── README.md /rc/requirements.txt: -------------------------------------------------------------------------------- 1 | ply>=3.11 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | test/lib/* linguist-vendored -------------------------------------------------------------------------------- /rc/.gitignore: -------------------------------------------------------------------------------- 1 | parser.out 2 | parsetab.py -------------------------------------------------------------------------------- /test/test.cpp: -------------------------------------------------------------------------------- 1 | #define CATCH_CONFIG_MAIN 2 | 3 | #include "test.h" 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | __pycache__/ 3 | *.pyc 4 | *.bin 5 | *.o 6 | vm 7 | tests -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | All contributions are welcome! Please submit your issue, feature request or PR. 2 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | Treat others respectfully and engage in constructive discussion towards the project's goals. -------------------------------------------------------------------------------- /examples/asm/helloworld.asm: -------------------------------------------------------------------------------- 1 | ; jump over the data, to our entry label 2 | jmp .entry 3 | 4 | $hello byte[] "Hello world!", 0x0A 5 | 6 | ; label definition 7 | .entry: 8 | prints $hello 9 | halt -------------------------------------------------------------------------------- /examples/asm/test.asm: -------------------------------------------------------------------------------- 1 | ; testing 2 | jmp .entry 3 | .start: 4 | mul r2, r2, r0 5 | printi r2 6 | println 7 | 8 | inc r0 9 | jne r0, r1, .start 10 | 11 | halt 12 | .entry: 13 | lconsb r0, 1 14 | lconsb r1, 11 15 | lconsb r2, 2 16 | jmp .start 17 | -------------------------------------------------------------------------------- /examples/asm/subroutines.asm: -------------------------------------------------------------------------------- 1 | 2 | .main: 3 | call .sub1 4 | pop r1 5 | print r1, 1 6 | halt 7 | 8 | .sub1: 9 | push ra 10 | push r0 11 | 12 | call .sub2 13 | mov r2, r0 14 | 15 | pop r0 16 | pop ra 17 | 18 | push r2 19 | ret 20 | 21 | .sub2: 22 | lcons r0, 1 23 | ret 24 | -------------------------------------------------------------------------------- /test/test.h: -------------------------------------------------------------------------------- 1 | #ifndef __TEST_H__ 2 | #define __TEST_H__ 3 | 4 | #include 5 | #include "lib/catch.hpp" 6 | #include "../src/vm.h" 7 | 8 | #define _U32_GARBAGE 0xF1E2D3C4U 9 | #define _U16_GARBAGE 0xF1E2U 10 | #define _U8_GARBAGE 0xF1U 11 | 12 | #define _NTH_BYTE(num, n) (num >> (8 * n)) & 0xFF 13 | 14 | #define _ALMOST_EQUAL(x, y) fabs(x - y) < 0.0001 15 | 16 | #endif // __TEST_H__ -------------------------------------------------------------------------------- /examples/asm/benchmark.asm: -------------------------------------------------------------------------------- 1 | ; counter 2 | lconsb r0, 0 3 | ; loop end 4 | lcons r1, 2000000 5 | ; divider 6 | lconsb r2, 13 7 | lconsb r4, 0 8 | 9 | .loopStart: 10 | mov r3, r0 11 | mod r3, r2 12 | 13 | jnz r3, .loopEnd 14 | 15 | printi r0 16 | println 17 | 18 | .loopEnd: 19 | inc r0 20 | jb r0, r1, .loopStart 21 | 22 | 23 | halt -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "vm.h" 2 | 3 | int main(int argc, char *argv[]) 4 | { 5 | if (argc != 2) 6 | { 7 | printf("Usage: %s bin_file\n", argv[0]); 8 | return 1; 9 | } 10 | 11 | uint8_t *program; 12 | 13 | FILE *f = fopen(argv[1], "rb"); 14 | fseek(f, 0, SEEK_END); 15 | long fileLen = ftell(f); 16 | rewind(f); 17 | 18 | program = (uint8_t *)malloc(fileLen); 19 | size_t s = fread(program, fileLen, 1, f); 20 | fclose(f); 21 | 22 | VM vm(program, fileLen, 2192); 23 | return vm.run(); 24 | } 25 | -------------------------------------------------------------------------------- /examples/rc/test.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int num; 3 | int i; 4 | int isPrime; 5 | int primeCount; 6 | primeCount = 0; 7 | num = 2; 8 | 9 | while (num < 10000) { 10 | isPrime = 1; 11 | i = 2; 12 | 13 | while (i < num) { 14 | if (num % i == 0) { 15 | isPrime = 0; 16 | break; 17 | } 18 | ++i; 19 | } 20 | if (isPrime) { 21 | ++primeCount; 22 | } 23 | 24 | ++num; 25 | } 26 | 27 | print(primeCount); 28 | return 0; 29 | } -------------------------------------------------------------------------------- /assembler/assembler.py: -------------------------------------------------------------------------------- 1 | import os 2 | import argparse 3 | from internals import process_file, print_bytecode, write_bytecode 4 | 5 | parser = argparse.ArgumentParser( 6 | description="Assemble the specified file into bytecide" 7 | ) 8 | parser.add_argument("input_file", type=str, help="file to assemble") 9 | parser.add_argument("-o", "--out", type=str, help="output file") 10 | parser.add_argument("-a", "--data-align", type=int, default=1, help="data alignment (default 4)") 11 | parser.add_argument( 12 | "-p", "--print", action="store_true", help="print bytecode to stdout" 13 | ) 14 | 15 | args = parser.parse_args() 16 | 17 | if args.out: 18 | output_path = args.out 19 | else: 20 | output_path = os.path.splitext(args.input_file)[0] + ".bin" 21 | 22 | bytecode = process_file(args.input_file, args.data_align) 23 | write_bytecode(bytecode, output_path) 24 | 25 | if args.print: 26 | print_bytecode(bytecode) 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2018 Mario Falcao 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /rc/rcc.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | import argparse 4 | sys.path.append("..") 5 | 6 | import rc_parse 7 | from rc_visitor import PrintVisitor 8 | from rc_semantics import SemanticAnalyzer 9 | from rc_compiler import ASMCompileVisitor 10 | 11 | #from assembler.internals import process_file, write_bytecode 12 | 13 | parser = argparse.ArgumentParser( 14 | description="Compile our Reduced C to ASM" 15 | ) 16 | parser.add_argument("input_file", type=str, help="file to compile") 17 | parser.add_argument("-o", "--out", type=str, help="output file") 18 | parser.add_argument( 19 | "-p", "--print", action="store_true", help="print formatted code and assembly to stdout" 20 | ) 21 | 22 | args = parser.parse_args() 23 | 24 | if args.out: 25 | output_path = args.out 26 | else: 27 | output_path = os.path.splitext(args.input_file)[0] + ".asm" 28 | 29 | code = "" 30 | with open(args.input_file, "r", encoding="utf-8") as f: 31 | code = f.read(-1) 32 | 33 | program = rc_parse.parse(code) 34 | if program: 35 | printer = PrintVisitor() 36 | printer.visit_Program(program) 37 | if args.print: 38 | print(printer.result) 39 | 40 | analyzer = SemanticAnalyzer() 41 | analyzer.visit_Program(program) 42 | compiler = ASMCompileVisitor() 43 | compiler.visit_Program(program) 44 | if args.print: 45 | print(compiler.result) 46 | 47 | with open(output_path, "w", encoding="utf-8") as f: 48 | f.write(compiler.result) 49 | else: 50 | print("Error while compiling") 51 | -------------------------------------------------------------------------------- /test/lib/catch2_LICENSE: -------------------------------------------------------------------------------- 1 | The following license applies to the Catch2 testing library, included as 2 | "catch.hpp" in this project: 3 | 4 | 5 | Boost Software License - Version 1.0 - August 17th, 2003 6 | 7 | Permission is hereby granted, free of charge, to any person or organization 8 | obtaining a copy of the software and accompanying documentation covered by 9 | this license (the "Software") to use, reproduce, display, distribute, 10 | execute, and transmit the Software, and to prepare derivative works of the 11 | Software, and to permit third-parties to whom the Software is furnished to 12 | do so, all subject to the following: 13 | 14 | The copyright notices in the Software and this entire statement, including 15 | the above license grant, this restriction and the following disclaimer, 16 | must be included in all copies of the Software, in whole or in part, and 17 | all derivative works of the Software, unless such copies or derivative 18 | works are solely in the form of machine-executable object code generated by 19 | a source language processor. 20 | 21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 24 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 25 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 26 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 27 | DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /test/test_conversions.cpp: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | TEST_CASE("OP_F2I") 4 | { 5 | uint8_t program[] = { 6 | OP_F2I, R0, R1, 7 | OP_HALT}; 8 | VM vm(program, sizeof(program)); 9 | 10 | SECTION("1345.937") 11 | { 12 | float val = 1345.937f; 13 | int32_t expected = 1345; 14 | vm.setRegister(R1, *((uint32_t *)&val)); 15 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 16 | 17 | REQUIRE(vm.getRegister(R0) == *((uint32_t *)&expected)); 18 | } 19 | 20 | SECTION("-781345.719") 21 | { 22 | float val = -781345.719; 23 | int32_t expected = -781345; 24 | vm.setRegister(R1, *((uint32_t *)&val)); 25 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 26 | 27 | REQUIRE(vm.getRegister(R0) == *((uint32_t *)&expected)); 28 | } 29 | } 30 | 31 | TEST_CASE("OP_I2F") 32 | { 33 | uint8_t program[] = { 34 | OP_I2F, R0, R1, 35 | OP_HALT}; 36 | VM vm(program, sizeof(program)); 37 | 38 | SECTION("1345") 39 | { 40 | int32_t val = 1345; 41 | float expected = 1345.0f; 42 | vm.setRegister(R1, *((uint32_t *)&val)); 43 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 44 | 45 | REQUIRE(vm.getRegister(R0) == *((uint32_t *)&expected)); 46 | } 47 | 48 | SECTION("-781345") 49 | { 50 | int32_t val = -781345; 51 | float expected = -781345.0f; 52 | vm.setRegister(R1, *((uint32_t *)&val)); 53 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 54 | 55 | REQUIRE(vm.getRegister(R0) == *((uint32_t *)&expected)); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CXX ?= g++ 2 | CXXFLAGS := -std=c++11 -Wall -O2 -march=native -fno-strict-aliasing -g 3 | CXXFLAGS_TEST = -std=c++11 -fno-strict-aliasing 4 | 5 | all: vm tests 6 | $(info Done! Quick commands:) 7 | $(info - Interpret file: ./vm mybinary.bin) 8 | $(info - Run tests: ./tests) 9 | $(info - Assemble a file: python3 assembler/assembler.py mycode.asm) 10 | 11 | vm: main.o vm.o 12 | $(CXX) $(CXXFLAGS) -o vm src/main.o src/vm.o 13 | 14 | main.o: src/main.cpp 15 | $(CXX) $(CXXFLAGS) -o src/main.o -c src/main.cpp 16 | 17 | vm.o: src/vm.cpp src/vm.h 18 | $(CXX) $(CXXFLAGS) -o src/vm.o -c src/vm.cpp 19 | 20 | tests: vm.o test.o test_system.o test_registers.o test_stack.o test_memory.o test_arithmetic.o test_conversions.o test_branching.o 21 | $(CXX) $(CXXFLAGS_TEST) -o tests src/vm.o test/test.o test/test_system.o test/test_registers.o test/test_stack.o test/test_memory.o test/test_arithmetic.o test/test_conversions.o test/test_branching.o 22 | 23 | test.o: test/test.cpp 24 | $(CXX) $(CXXFLAGS_TEST) -o test/test.o -c test/test.cpp 25 | 26 | test_system.o: test/test_system.cpp 27 | $(CXX) $(CXXFLAGS_TEST) -o test/test_system.o -c test/test_system.cpp 28 | 29 | test_registers.o: test/test_registers.cpp 30 | $(CXX) $(CXXFLAGS_TEST) -o test/test_registers.o -c test/test_registers.cpp 31 | 32 | test_stack.o: test/test_stack.cpp 33 | $(CXX) $(CXXFLAGS_TEST) -o test/test_stack.o -c test/test_stack.cpp 34 | 35 | test_memory.o: test/test_memory.cpp 36 | $(CXX) $(CXXFLAGS_TEST) -o test/test_memory.o -c test/test_memory.cpp 37 | 38 | test_arithmetic.o: test/test_arithmetic.cpp 39 | $(CXX) $(CXXFLAGS_TEST) -o test/test_arithmetic.o -c test/test_arithmetic.cpp 40 | 41 | test_conversions.o: test/test_conversions.cpp 42 | $(CXX) $(CXXFLAGS_TEST) -o test/test_conversions.o -c test/test_conversions.cpp 43 | 44 | test_branching.o: test/test_branching.cpp 45 | $(CXX) $(CXXFLAGS_TEST) -o test/test_branching.o -c test/test_branching.cpp 46 | 47 | clean: 48 | rm -f src/*.o 49 | rm -f test/*.o 50 | rm -f vm 51 | rm -f tests 52 | -------------------------------------------------------------------------------- /examples/asm/primes.asm: -------------------------------------------------------------------------------- 1 | ; calculate primes below 100000 2 | ; runtime: 4,966s 3 | lconsb r0, 1 4 | lcons r1, 100000 5 | 6 | .loop: 7 | lconsb r2, 2 8 | 9 | .innerLoop: 10 | jae r2, r0, .isPrime 11 | 12 | mod r3, r0, r2 13 | jz r3, .loopEnd 14 | 15 | inc r2 16 | jmp .innerLoop 17 | 18 | .isPrime: 19 | print r0, 1 20 | 21 | .loopEnd: 22 | inc r0 23 | jb r0, r1, .loop 24 | 25 | .end: 26 | ; prints $doneStr 27 | halt 28 | 29 | ;$doneStr byte[] "done", '!', 0xA 30 | 31 | ; C++ equivalent 32 | ; runtime: 1,800s (G++ 7.3.0 -O3) 33 | 34 | ; #include 35 | ; #include 36 | ; int main() { 37 | ; for (uint32_t num=1; num<100000; num++) { 38 | ; bool isPrime = true; 39 | ; for (uint32_t i=2; i>" 78 | t_BAND = r"&" 79 | t_BOR = r"\|" 80 | t_BXOR = r"\^" 81 | t_BNOT = r"~" 82 | 83 | # comparison 84 | t_LT = r"<" 85 | t_GT = r">" 86 | t_LE = r"<=" 87 | t_GE = r">=" 88 | t_EQ = r"==" 89 | t_NE = r"!=" 90 | 91 | # logic 92 | t_OR = r"\|\|" 93 | t_AND = r"&&" 94 | t_NOT = r"!" 95 | 96 | t_LPAREN = r"\(" 97 | t_RPAREN = r"\)" 98 | t_LBRACE = r"\{" 99 | t_RBRACE = r"\}" 100 | t_SEMI = r";" 101 | t_COMMA = r"," 102 | 103 | t_INT = r"int" 104 | 105 | t_IF = r"if" 106 | t_ELSE = r"else" 107 | t_WHILE = r"while" 108 | #t_FOR = r"for" 109 | t_BREAK = r"break" 110 | t_RETURN = r"return" 111 | t_PRINT = r"print" 112 | #t_INPUT = r"input" 113 | #t_STR = r"str" 114 | #t_NUM = r"num" 115 | 116 | reserved_map = {} 117 | for r in KEYWORDS: 118 | reserved_map[r.lower()] = r 119 | 120 | 121 | def t_ID(t): 122 | r"[A-Za-z_][\w_]*" 123 | t.type = reserved_map.get(t.value, "ID") 124 | return t 125 | 126 | 127 | def t_NUMBER(t): 128 | r"[-+]?[0-9]+" 129 | t.value = int(t.value) 130 | if t.value > 2**31 - 1 or t.value < -2**31: 131 | raise ValueError("Invalid int32 {}".format(t.value)) 132 | return t 133 | 134 | 135 | # def t_STRING(t): 136 | # r"\"([^\\\n]|(\\.))*?\"" 137 | # t.value = bytes(t.value[1:-1], "utf-8").decode("unicode_escape") 138 | # return t 139 | 140 | 141 | def t_error(t): 142 | print("Illegal character %s" % repr(t.value[0])) 143 | t.lexer.skip(1) 144 | 145 | 146 | lexer = lex.lex() 147 | if __name__ == "__main__": 148 | lex.runmain(lexer) 149 | -------------------------------------------------------------------------------- /test/test_system.cpp: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | uint8_t intCode; 4 | bool intContinue; 5 | 6 | bool handleInterrupt(uint8_t code) 7 | { 8 | intCode = code; 9 | return intContinue; 10 | } 11 | 12 | TEST_CASE("OP_INT") 13 | { 14 | SECTION("Code is correct") 15 | { 16 | uint8_t program[] = { 17 | OP_INT, 3, 18 | OP_HALT}; 19 | VM vm(program, sizeof(program)); 20 | vm.onInterrupt(handleInterrupt); 21 | intContinue = true; 22 | intCode = 0; 23 | 24 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 25 | REQUIRE(intCode == 3); 26 | 27 | vm.reset(); 28 | uint8_t *memory = vm.memory(); 29 | intCode = 0; 30 | memory[1] = 253; 31 | 32 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 33 | REQUIRE(intCode == 253); 34 | } 35 | 36 | SECTION("Can stop exec") 37 | { 38 | uint8_t program[] = { 39 | OP_INT, 3, 40 | OP_LCONSB, R0, 123, 41 | OP_HALT}; 42 | VM vm(program, sizeof(program)); 43 | vm.onInterrupt(handleInterrupt); 44 | intContinue = true; 45 | intCode = 0; 46 | 47 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 48 | REQUIRE(intCode == 3); 49 | REQUIRE(vm.getRegister(R0) == 123); 50 | REQUIRE(vm.getRegister(IP) == 5); 51 | 52 | intContinue = false; 53 | intCode = 0; 54 | vm.reset(); 55 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 56 | REQUIRE(intCode == 3); 57 | REQUIRE(vm.getRegister(R0) == 0); 58 | REQUIRE(vm.getRegister(IP) == 1); 59 | } 60 | 61 | SECTION("Unhandled interrupt causes error") 62 | { 63 | uint8_t program[] = { 64 | OP_INT, 3, 65 | OP_LCONSB, R0, 123, 66 | OP_HALT}; 67 | VM vm(program, sizeof(program)); 68 | REQUIRE(vm.run() == ExecResult::VM_ERR_UNHANDLED_INTERRUPT); 69 | REQUIRE(vm.getRegister(IP) == 1); 70 | } 71 | } 72 | 73 | TEST_CASE("OP_HALT") 74 | { 75 | uint8_t program[] = { 76 | OP_HALT, 77 | OP_LCONSB, R0, 1, 78 | OP_HALT}; 79 | VM vm(program, sizeof(program)); 80 | vm.setRegister(R0, 123); 81 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 82 | REQUIRE(vm.getRegister(R0) == 123); 83 | REQUIRE(vm.getRegister(IP) == 0); 84 | } 85 | 86 | TEST_CASE("OP_NOP") 87 | { 88 | uint8_t program[] = { 89 | OP_NOP, 90 | OP_HALT}; 91 | VM vm(program, sizeof(program)); 92 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 93 | 94 | SECTION("Registers unchanged") 95 | { 96 | for (uint8_t i = R0; i <= T9; i++) 97 | REQUIRE(vm.getRegister((Register)i) == 0); 98 | } 99 | 100 | SECTION("Correct IP") 101 | { 102 | REQUIRE(vm.getRegister(IP) == 1); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /test/test_registers.cpp: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | TEST_CASE("OP_LCONS") 4 | { 5 | SECTION("Load zero") 6 | { 7 | uint8_t program[] = { 8 | OP_LCONS, R0, 0, 0, 0, 0, 9 | OP_HALT}; 10 | VM vm(program, sizeof(program)); 11 | vm.setRegister(R0, _U32_GARBAGE); // set garbage 12 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 13 | REQUIRE(vm.getRegister(R0) == 0); 14 | } 15 | 16 | SECTION("Load max") 17 | { 18 | uint8_t program[] = { 19 | OP_LCONS, R0, 0xFF, 0xFF, 0xFF, 0xFF, 20 | OP_HALT}; 21 | VM vm(program, sizeof(program)); 22 | vm.setRegister(R0, _U32_GARBAGE); // set garbage 23 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 24 | REQUIRE(vm.getRegister(R0) == UINT32_MAX); 25 | } 26 | } 27 | 28 | TEST_CASE("OP_LCONSW") 29 | { 30 | SECTION("Load zero") 31 | { 32 | uint8_t program[] = { 33 | OP_LCONSW, R0, 0, 0, 34 | OP_HALT}; 35 | VM vm(program, sizeof(program)); 36 | vm.setRegister(R0, _U32_GARBAGE); // set garbage 37 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 38 | REQUIRE(vm.getRegister(R0) == 0); 39 | } 40 | 41 | SECTION("Load max") 42 | { 43 | uint8_t program[] = { 44 | OP_LCONSW, R0, 0xFF, 0xFF, 45 | OP_HALT}; 46 | VM vm(program, sizeof(program)); 47 | vm.setRegister(R0, _U32_GARBAGE); // set garbage 48 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 49 | REQUIRE(vm.getRegister(R0) == UINT16_MAX); 50 | } 51 | } 52 | 53 | TEST_CASE("OP_LCONSB") 54 | { 55 | SECTION("Load zero") 56 | { 57 | uint8_t program[] = { 58 | OP_LCONSB, R0, 0, 59 | OP_HALT}; 60 | VM vm(program, sizeof(program)); 61 | vm.setRegister(R0, _U32_GARBAGE); // set garbage 62 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 63 | REQUIRE(vm.getRegister(R0) == 0); 64 | } 65 | 66 | SECTION("Load max") 67 | { 68 | uint8_t program[] = { 69 | OP_LCONSB, R0, 0xFF, 70 | OP_HALT}; 71 | VM vm(program, sizeof(program)); 72 | vm.setRegister(R0, _U32_GARBAGE); // set garbage 73 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 74 | REQUIRE(vm.getRegister(R0) == UINT8_MAX); 75 | } 76 | } 77 | 78 | TEST_CASE("OP_MOV") 79 | { 80 | uint8_t program[] = { 81 | OP_MOV, R0, R1, 82 | OP_HALT}; 83 | VM vm(program, sizeof(program)); 84 | 85 | SECTION("Load zero") 86 | { 87 | vm.reset(); 88 | vm.setRegister(R1, 0); 89 | vm.setRegister(R0, _U32_GARBAGE); // set garbage 90 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 91 | REQUIRE(vm.getRegister(R0) == 0); 92 | } 93 | 94 | SECTION("Load 1122334455U") 95 | { 96 | vm.reset(); 97 | vm.setRegister(R1, 1122334455U); 98 | vm.setRegister(R0, _U32_GARBAGE); // set garbage 99 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 100 | REQUIRE(vm.getRegister(R0) == 1122334455U); 101 | } 102 | 103 | SECTION("Load max") 104 | { 105 | vm.reset(); 106 | vm.setRegister(R1, UINT32_MAX); 107 | vm.setRegister(R0, _U32_GARBAGE); // set garbage 108 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 109 | REQUIRE(vm.getRegister(R0) == UINT32_MAX); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | sudo: false 3 | 4 | matrix: 5 | include: 6 | - os: linux 7 | compiler: gcc 8 | addons: 9 | apt: 10 | sources: 11 | - ubuntu-toolchain-r-test 12 | packages: 13 | - g++-4.9 14 | env: 15 | - MATRIX_EVAL="CC=gcc-4.9 && CXX=g++-4.9" 16 | 17 | - os: linux 18 | compiler: gcc 19 | addons: 20 | apt: 21 | sources: 22 | - ubuntu-toolchain-r-test 23 | packages: 24 | - g++-5 25 | env: 26 | - MATRIX_EVAL="CC=gcc-5 && CXX=g++-5" 27 | 28 | - os: linux 29 | compiler: gcc 30 | addons: 31 | apt: 32 | sources: 33 | - ubuntu-toolchain-r-test 34 | packages: 35 | - g++-6 36 | env: 37 | - MATRIX_EVAL="CC=gcc-6 && CXX=g++-6" 38 | 39 | - os: linux 40 | compiler: gcc 41 | addons: 42 | apt: 43 | sources: 44 | - ubuntu-toolchain-r-test 45 | packages: 46 | - g++-7 47 | env: 48 | - MATRIX_EVAL="CC=gcc-7 && CXX=g++-7" 49 | 50 | - os: linux 51 | compiler: gcc 52 | addons: 53 | apt: 54 | sources: 55 | - ubuntu-toolchain-r-test 56 | packages: 57 | - g++-8 58 | env: 59 | - MATRIX_EVAL="CC=gcc-8 && CXX=g++-8" 60 | 61 | - os: linux 62 | compiler: clang 63 | addons: 64 | apt: 65 | sources: 66 | - ubuntu-toolchain-r-test 67 | - llvm-toolchain-precise-3.6 68 | packages: 69 | - clang-3.6 70 | env: 71 | - MATRIX_EVAL="CC=clang-3.6 && CXX=clang++-3.6" 72 | 73 | - os: linux 74 | compiler: clang 75 | addons: 76 | apt: 77 | sources: 78 | - ubuntu-toolchain-r-test 79 | - llvm-toolchain-precise-3.7 80 | packages: 81 | - clang-3.7 82 | env: 83 | - MATRIX_EVAL="CC=clang-3.7 && CXX=clang++-3.7" 84 | 85 | - os: linux 86 | compiler: clang 87 | addons: 88 | apt: 89 | sources: 90 | - ubuntu-toolchain-r-test 91 | - llvm-toolchain-precise-3.8 92 | packages: 93 | - clang-3.8 94 | env: 95 | - MATRIX_EVAL="CC=clang-3.8 && CXX=clang++-3.8" 96 | 97 | - os: linux 98 | compiler: clang 99 | addons: 100 | apt: 101 | sources: 102 | - llvm-toolchain-trusty-3.9 103 | packages: 104 | - clang-3.9 105 | env: 106 | - MATRIX_EVAL="CC=clang-3.9 && CXX=clang++-3.9" 107 | 108 | - os: linux 109 | compiler: clang 110 | addons: 111 | apt: 112 | sources: 113 | - llvm-toolchain-trusty-4.0 114 | packages: 115 | - clang-4.0 116 | env: 117 | - MATRIX_EVAL="CC=clang-4.0 && CXX=clang++-4.0" 118 | 119 | - os: linux 120 | compiler: clang 121 | addons: 122 | apt: 123 | sources: 124 | - llvm-toolchain-trusty-5.0 125 | packages: 126 | - clang-5.0 127 | env: 128 | - MATRIX_EVAL="CC=clang-5.0 && CXX=clang++-5.0" 129 | 130 | - os: osx 131 | osx_image: xcode7.3 132 | compiler: clang 133 | env: 134 | - MATRIX_EVAL="CC=clang && CXX=clang++" 135 | 136 | - os: osx 137 | osx_image: xcode8 138 | compiler: clang 139 | env: 140 | - MATRIX_EVAL="CC=clang && CXX=clang++" 141 | 142 | - os: osx 143 | osx_image: xcode9 144 | compiler: clang 145 | env: 146 | - MATRIX_EVAL="CC=clang && CXX=clang++" 147 | 148 | - os: osx 149 | osx_image: xcode9.1 150 | compiler: clang 151 | env: 152 | - MATRIX_EVAL="CC=clang && CXX=clang++" 153 | 154 | before_install: 155 | - eval "${MATRIX_EVAL}" 156 | 157 | script: 158 | - make && ./tests 159 | -------------------------------------------------------------------------------- /test/test_stack.cpp: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | TEST_CASE("OP_PUSH") 4 | { 5 | uint8_t program[] = { 6 | OP_PUSH, R0, 7 | OP_HALT}; 8 | VM vm(program, sizeof(program)); 9 | 10 | SECTION("Zero") 11 | { 12 | vm.setRegister(R0, 0); 13 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 14 | REQUIRE(vm.stackCount() == 4); 15 | REQUIRE(vm.stackPop() == 0); 16 | } 17 | 18 | SECTION("1123497651") 19 | { 20 | vm.reset(); 21 | vm.setRegister(R0, 1123497651); 22 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 23 | REQUIRE(vm.stackCount() == 4); 24 | REQUIRE(vm.stackPop() == 1123497651); 25 | } 26 | 27 | SECTION("UINT32_MAX") 28 | { 29 | vm.reset(); 30 | vm.setRegister(R0, UINT32_MAX); 31 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 32 | REQUIRE(vm.stackCount() == 4); 33 | REQUIRE(vm.stackPop() == UINT32_MAX); 34 | } 35 | } 36 | 37 | TEST_CASE("OP_POP") 38 | { 39 | uint8_t program[] = { 40 | OP_POP, R0, 41 | OP_HALT}; 42 | VM vm(program, sizeof(program)); 43 | 44 | SECTION("Zero") 45 | { 46 | vm.stackPush(0); 47 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 48 | REQUIRE(vm.stackCount() == 0); 49 | REQUIRE(vm.getRegister(R0) == 0); 50 | } 51 | 52 | SECTION("1123497651") 53 | { 54 | vm.reset(); 55 | vm.stackPush(1123497651); 56 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 57 | REQUIRE(vm.stackCount() == 0); 58 | REQUIRE(vm.getRegister(R0) == 1123497651); 59 | } 60 | 61 | SECTION("UINT32_MAX") 62 | { 63 | vm.reset(); 64 | vm.stackPush(UINT32_MAX); 65 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 66 | REQUIRE(vm.stackCount() == 0); 67 | REQUIRE(vm.getRegister(R0) == UINT32_MAX); 68 | } 69 | } 70 | 71 | TEST_CASE("OP_POP2") 72 | { 73 | uint8_t program[] = { 74 | OP_POP2, R0, R1, 75 | OP_HALT}; 76 | VM vm(program, sizeof(program)); 77 | 78 | SECTION("Zero") 79 | { 80 | vm.stackPush(0); 81 | vm.stackPush(0); 82 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 83 | REQUIRE(vm.stackCount() == 0); 84 | REQUIRE(vm.getRegister(R0) == 0); 85 | REQUIRE(vm.getRegister(R1) == 0); 86 | } 87 | 88 | SECTION("1123497651 / 123") 89 | { 90 | vm.reset(); 91 | vm.stackPush(1123497651); 92 | vm.stackPush(123); 93 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 94 | REQUIRE(vm.stackCount() == 0); 95 | REQUIRE(vm.getRegister(R0) == 123); 96 | REQUIRE(vm.getRegister(R1) == 1123497651); 97 | } 98 | 99 | SECTION("UINT32_MAX") 100 | { 101 | vm.reset(); 102 | vm.stackPush(UINT32_MAX); 103 | vm.stackPush(UINT32_MAX); 104 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 105 | REQUIRE(vm.stackCount() == 0); 106 | REQUIRE(vm.getRegister(R0) == UINT32_MAX); 107 | REQUIRE(vm.getRegister(R1) == UINT32_MAX); 108 | } 109 | } 110 | 111 | TEST_CASE("OP_DUP") 112 | { 113 | uint8_t program[] = { 114 | OP_DUP, 115 | OP_HALT}; 116 | VM vm(program, sizeof(program)); 117 | 118 | SECTION("Zero") 119 | { 120 | vm.stackPush(0); 121 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 122 | REQUIRE(vm.stackCount() == 8); 123 | REQUIRE(vm.stackPop() == 0); 124 | REQUIRE(vm.stackPop() == 0); 125 | } 126 | 127 | SECTION("1123497651 ") 128 | { 129 | vm.reset(); 130 | vm.stackPush(1123497651); 131 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 132 | REQUIRE(vm.stackCount() == 8); 133 | REQUIRE(vm.stackPop() == 1123497651); 134 | REQUIRE(vm.stackPop() == 1123497651); 135 | } 136 | 137 | SECTION("UINT32_MAX") 138 | { 139 | vm.reset(); 140 | vm.stackPush(UINT32_MAX); 141 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 142 | REQUIRE(vm.stackCount() == 8); 143 | REQUIRE(vm.stackPop() == UINT32_MAX); 144 | REQUIRE(vm.stackPop() == UINT32_MAX); 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /rc/rc_visitor.py: -------------------------------------------------------------------------------- 1 | class PrintVisitor: 2 | def __init__(self): 3 | self.result = "" 4 | self.indent_count = 0 5 | 6 | def add(self, s): 7 | self.result += s 8 | 9 | def addline(self, s): 10 | self.result += "\n{}{}".format(" " * self.indent_count, s) 11 | 12 | def indent(self): 13 | self.indent_count += 4 14 | 15 | def dedent(self): 16 | self.indent_count -= 4 17 | 18 | def child_accept(self, parent, child): 19 | child.parent = parent 20 | child.accept(self) 21 | 22 | def visit_Program(self, node): 23 | for c in node.items: 24 | self.child_accept(node, c) 25 | 26 | def visit_VarDecl(self, node): 27 | self.addline("") 28 | self.child_accept(node, node.type) 29 | self.add(" ") 30 | self.child_accept(node, node.ident) 31 | self.add(";") 32 | 33 | # def visit_FuncDecl(self, node): 34 | # self.addline("") 35 | # self.child_accept(node, node.rtype) 36 | # self.add(" ") 37 | # self.child_accept(node, node.ident) 38 | # self.add(" ") 39 | # self.child_accept(node, node.args) 40 | # self.add(";") 41 | 42 | def visit_FuncDef(self, node): 43 | self.addline("") 44 | self.child_accept(node, node.rtype) 45 | self.add(" ") 46 | self.child_accept(node, node.ident) 47 | self.child_accept(node, node.args) 48 | self.child_accept(node, node.body) 49 | self.addline("") 50 | 51 | def visit_FuncParam(self, node): 52 | self.child_accept(node, node.type) 53 | self.add(" ") 54 | self.child_accept(node, node.ident) 55 | 56 | def visit_FuncParams(self, node): 57 | self.add("(") 58 | for i in range(len(node.args)): 59 | if i > 0: 60 | self.add(", ") 61 | self.child_accept(node, node.args[i]) 62 | self.add(")") 63 | 64 | def visit_FuncCall(self, node): 65 | self.child_accept(node, node.ident) 66 | self.add("(") 67 | self.child_accept(node, node.args) 68 | self.add(")") 69 | 70 | def visit_FuncArgs(self, node): 71 | for i in range(len(node.args)): 72 | if i > 0: 73 | self.add(", ") 74 | self.child_accept(node, node.args[i]) 75 | 76 | def visit_StatementBlock(self, node): 77 | self.add(" {") 78 | self.indent() 79 | self.child_accept(node, node.statements) 80 | self.dedent() 81 | self.addline("}") 82 | self.addline("") 83 | 84 | def visit_Statements(self, node): 85 | for s in node.statements: 86 | self.child_accept(node, s) 87 | 88 | def visit_AssignStatement(self, node): 89 | self.addline("") 90 | self.child_accept(node, node.ident) 91 | self.add(" = ") 92 | self.child_accept(node, node.value) 93 | self.add(";") 94 | 95 | def visit_BreakStatement(self, node): 96 | self.addline("break;") 97 | 98 | def visit_ReturnStatement(self, node): 99 | self.addline("return ") 100 | self.child_accept(node, node.expr) 101 | self.add(";") 102 | 103 | def visit_IfStatement(self, node): 104 | self.addline("if (") 105 | self.child_accept(node, node.condition) 106 | self.add(")") 107 | self.child_accept(node, node.true_block) 108 | if node.else_block: 109 | self.addline("else") 110 | self.child_accept(node, node.else_block) 111 | 112 | def visit_WhileStatement(self, node): 113 | self.addline("while (") 114 | self.child_accept(node, node.condition) 115 | self.add(")") 116 | self.child_accept(node, node.body) 117 | 118 | def visit_PrintStatement(self, node): 119 | self.addline("print (") 120 | self.child_accept(node, node.expr) 121 | self.add(");") 122 | 123 | def visit_UnaryOp(self, node): 124 | self.add(node.op) 125 | self.child_accept(node, node.right) 126 | 127 | def visit_BinaryOp(self, node): 128 | self.child_accept(node, node.left) 129 | self.add(" ") 130 | self.add(node.op) 131 | self.add(" ") 132 | self.child_accept(node, node.right) 133 | 134 | def visit_ComparisonOp(self, node): 135 | self.child_accept(node, node.left) 136 | self.add(" ") 137 | self.add(node.comp) 138 | self.add(" ") 139 | self.child_accept(node, node.right) 140 | 141 | def visit_LogicOp(self, node): 142 | self.child_accept(node, node.left) 143 | self.add(" ") 144 | self.add(node.op) 145 | self.add(" ") 146 | self.child_accept(node, node.right) 147 | 148 | def visit_BoolConst(self, node): 149 | self.add(str(node.value)) 150 | 151 | def visit_IntConst(self, node): 152 | self.add(str(node.value)) 153 | 154 | def visit_Identifier(self, node): 155 | self.add(str(node.name)) 156 | 157 | def visit_IdentifierExp(self, node): 158 | self.child_accept(node, node.identifier) 159 | 160 | def visit_ExpGroup(self, node): 161 | self.add("(") 162 | self.child_accept(node, node.expression) 163 | self.add(")") 164 | 165 | def visit_Type(self, node): 166 | self.add(node.name) 167 | -------------------------------------------------------------------------------- /assembler/data.py: -------------------------------------------------------------------------------- 1 | from enum import IntEnum 2 | 3 | REGISTERS = { 4 | "r0": 0, 5 | "r1": 1, 6 | "r2": 2, 7 | "r3": 3, 8 | "r4": 4, 9 | "r5": 5, 10 | "t0": 6, 11 | "t1": 7, 12 | "t2": 8, 13 | "t3": 9, 14 | "t4": 10, 15 | "t5": 11, 16 | "t6": 12, 17 | "t7": 13, 18 | "t8": 14, 19 | "t9": 15, 20 | "ip": 16, 21 | "bp": 17, 22 | "sp": 18, 23 | "ra": 19 24 | } 25 | 26 | class AutoNumber(IntEnum): 27 | def __new__(cls, value=None): 28 | if value is None: 29 | if len(cls.__members__) == 0: 30 | base = -1 31 | else: 32 | base = tuple(cls.__members__.values())[-1] 33 | value = base + 1 34 | 35 | obj = int.__new__(cls, value) 36 | obj._value_ = value 37 | return obj 38 | 39 | class Opcodes(AutoNumber): 40 | # system: 41 | NOP = () # do nothing 42 | HALT = () # halt execution 43 | INT = () 44 | # constants: 45 | LCONS = () # store a value in a register = () e.g.: lcons r0 = () 0xA2 0x00 0x00 0x00 46 | LCONSW = () # store a word value in a register = () e.g.: lconsw r0 = () 0xA2 0x00 47 | LCONSB = () # store a byte value in a register = () e.g.: lconsb r0 = () 0xA2 48 | # register operations: 49 | MOV = () # copy a value between registers = () e.g.: mov r0 = () r2 50 | # stack: 51 | PUSH = () # push a register onto the stack = () e.g.: push r0 52 | POP = () # pop the first element of the stack to a register = () e.g.: pop r0 53 | POP2 = () # pop the two first elements of the stack to registers = () e.g.: pop r0 = () r1 54 | DUP = () # duplicate the last value in the stack = () e.g.: dup 55 | # functions 56 | CALL = () # set register RA to the next instruction and jump to subroutine = () e.g.: call 0x10 0x00 57 | RET = () # return to the address of last callee (RA) = () e.g.: ret 58 | # memory: 59 | STOR = () # copy a value from a register to a heap address = () e.g.: stor 0x08 0x00 = () r0 60 | STOR_P = () 61 | STORW = () # copy a word value from a register to a heap address = () e.g.: storw 0x08 0x00 = () r0 62 | STORW_P = () 63 | STORB = () # copy a byte value from a register to a heap address = () e.g.: storb 0x08 0x00 = () r0 64 | STORB_P = () 65 | LOAD = () # copy a value from a heap address to a register = () e.g.: load r0 = () 0x08 0x00 66 | LOAD_P = () 67 | LOADW = () # copy a word value from a heap address to a register = () e.g.: loadw r0 = () 0x08 0x00 68 | LOADW_P = () 69 | LOADB = () # copy a byte value from a heap address to a register = () e.g.: loadb r0 = () 0x08 0x00 70 | LOADB_P = () 71 | MEMCPY = () # copy N bytes from one memory address S to another address D = () e.g.: memcpy 0xDD 0xDD = () 0xSS 0xSS = () 0xNN 0xNN 72 | MEMCPY_P = () 73 | # arithmetic: 74 | INC = () # increment the specified register = () e.g.: inc r0 75 | FINC = () # increment a float in the specified register = () e.g.: incf r0 76 | DEC = () # decrement the specified register = () e.g.: dec r0 77 | FDEC = () # decrement a float in the specified register = () e.g.: decf r0 78 | ADD = () # sum and store in first reg = () e.g.: add r0 = () r2 79 | FADD = () # sum two floats and store in first reg = () e.g.: addf r0 = () r2 80 | SUB = () # subtract and store in first reg = () e.g.: sub r0 = () r2 81 | FSUB = () # subtract two floats and store in first reg = () e.g.: subf r0 = () r2 82 | MUL = () # multiply and store in first reg = () e.g.: mul r0 = () r2 83 | IMUL = () # signed multiply and store in first reg = () e.g.: mul r0 = () r2 84 | FMUL = () # multiply two floats and store in first reg = () e.g.: mulf r0 = () r2 85 | DIV = () # divide and store in first reg = () e.g.: div r0 = () r2 86 | IDIV = () # signed divide and store in first reg = () e.g.: div r0 = () r2 87 | FDIV = () # divide two floats and store in first reg = () e.g.: divf r0 = () r2 88 | SHL = () # logical shift left = () e.g.: shl r0 = () r1 89 | SHR = () # logical shift right = () e.g.: shr r0 = () r1 90 | ISHR = () # arithmetic shift right (for signed values) = () e.g.: ishr r0 = () r1 91 | MOD = () # store division remainder in first reg = () e.g.: mod r0 = () r1 92 | IMOD = () # store signed division remainder in first reg = () e.g.: mod r0 = () r1 93 | AND = () # and two registers and store result in the first one = () e.g.: and r0 = () r1 94 | OR = () # or two registers and store result in the first one = () e.g.: or r0 = () r1 95 | XOR = () # xor two registers and store result in the first one = () e.g.: xor r0 = () r1 96 | NOT = () # not a register and store result = () e.g.: not r0 97 | # conversions: 98 | U2I = () # convert an unsigned integer to a signed integer = () e.g.: u2i r0 99 | I2U = () # convert an signed integer to a unsigned integer = () e.g.: i2u r0 100 | I2F = () # convert an integer stored in a register to a float = () e.g.: i2f r0 101 | F2I = () # convert a float stored in a register to an int = () e.g.: f2i r0 102 | # branching: 103 | JMP = () # jump to address = () e.g.: jmp 0x0A 0x00 104 | JR = () # jump to address in register = () e.g.: jr r1 105 | JZ = () # jump if zero = () e.g.: jz r0 = () 0x0A 0x00 106 | JNZ = () # jump if not zero = () e.g.: jnz r0 = () 0x0A 0x00 107 | JE = () # jump if equal = () e.g. je r0 = () r1 = () 0x0A 0x00 108 | JNE = () # jump if not equal = () e.g. jne r0 = () r1 = () 0x1C 0x00 109 | JA = () # (unsigned) jump if above = () e.g. ja r0 = () r1 = () 0x1C 0x00 110 | JG = () # (signed) jump if greater = () e.g. jg r0 = () r1 = () 0x1C 0x00 111 | JAE = () # (unsigned) jump if above or equal = () e.g. jae r0 = () r1 = () 0x1C 0x00 112 | JGE = () # (signed) jump if greater or equal = () e.g. jge r0 = () r1 = () 0x1C 0x00 113 | JB = () # (unsigned) jump if below = () e.g. jb r0 = () r1 = () 0x1C 0x00 114 | JL = () # (signed) jump if less = () e.g. jl r0 = () r1 = () 0x1C 0x00 115 | JBE = () # (unsigned) jump if below or equal = () e.g. jbe r0 = () r1 = () 0x1C 0x00 116 | JLE = () # (signed) jump if less or equal = () e.g. jle r0 = () r1 = () 0x1C 0x00 117 | # strings: 118 | PRINT = () # print an integer stored in a register = () e.g.: print r0 119 | PRINTI = () # print a signed integer stored in a register = () e.g.: printi r0 120 | PRINTF = () # print a float stored in a register = () e.g.: printf r0 121 | PRINTC = () # print a single character from a register 122 | PRINTS = () # print a string stored in a memory address 123 | PRINTLN = () # print a newline = () e.g.: println 124 | READ = () # read an integer from stdin 125 | READI = () # read a signed integer from stdin to the specified register 126 | READF = () # read a float from stdin to the specified register 127 | READC = () # read a single character's code from stdin to the specified register 128 | READS = () # read a line to the specified memory address, to a maximum length 129 | -------------------------------------------------------------------------------- /rc/rc_ast.py: -------------------------------------------------------------------------------- 1 | class Node: 2 | def __init__(self): 3 | self.parent = None 4 | 5 | 6 | class Program(Node): 7 | def __init__(self, items=None): 8 | super().__init__() 9 | self.items = items or [] 10 | 11 | def accept(self, visitor): 12 | visitor.visit_Program(self) 13 | 14 | 15 | class VarDecl(Node): 16 | def __init__(self, _type, ident): 17 | super().__init__() 18 | self.type = _type 19 | self.ident = ident 20 | 21 | def accept(self, visitor): 22 | visitor.visit_VarDecl(self) 23 | 24 | 25 | # class FuncDecl(Node): 26 | # def __init__(self, rtype, ident, args): 27 | # super().__init__() 28 | # self.rtype = rtype 29 | # self.ident = ident 30 | # self.args = args 31 | 32 | # def accept(self, visitor): 33 | # visitor.visit_FuncDecl(self) 34 | 35 | 36 | class FuncDef(Node): 37 | def __init__(self, rtype, ident, args, body): 38 | super().__init__() 39 | self.rtype = rtype 40 | self.ident = ident 41 | self.args = args 42 | self.body = body 43 | 44 | def accept(self, visitor): 45 | visitor.visit_FuncDef(self) 46 | 47 | 48 | class FuncParam(Node): 49 | def __init__(self, _type, ident): 50 | super().__init__() 51 | self.type = _type 52 | self.ident = ident 53 | 54 | def accept(self, visitor): 55 | visitor.visit_FuncParam(self) 56 | 57 | 58 | class FuncParams(Node): 59 | def __init__(self, args=None): 60 | super().__init__() 61 | self.args = args or [] 62 | 63 | def accept(self, visitor): 64 | visitor.visit_FuncParams(self) 65 | 66 | 67 | class FuncArgs(Node): 68 | def __init__(self, args=None): 69 | super().__init__() 70 | self.args = args or [] 71 | 72 | def accept(self, visitor): 73 | visitor.visit_FuncArgs(self) 74 | 75 | 76 | class FuncCall(Node): 77 | def __init__(self, ident, args): 78 | super().__init__() 79 | self.ident = ident 80 | self.args = args 81 | 82 | def accept(self, visitor): 83 | visitor.visit_FuncCall(self) 84 | 85 | 86 | class StatementBlock(Node): 87 | def __init__(self, statements=None): 88 | super().__init__() 89 | self.statements = statements or Statements() 90 | 91 | def accept(self, visitor): 92 | visitor.visit_StatementBlock(self) 93 | 94 | 95 | class Statements(Node): 96 | def __init__(self, statements=None): 97 | super().__init__() 98 | self.statements = statements or [] 99 | 100 | def accept(self, visitor): 101 | visitor.visit_Statements(self) 102 | 103 | 104 | class AssignStatement(Node): 105 | def __init__(self, ident, value): 106 | super().__init__() 107 | self.ident = ident 108 | self.value = value 109 | 110 | def accept(self, visitor): 111 | visitor.visit_AssignStatement(self) 112 | 113 | 114 | class BreakStatement(Node): 115 | def __init__(self, expr): 116 | super().__init__() 117 | 118 | def accept(self, visitor): 119 | visitor.visit_BreakStatement(self) 120 | 121 | 122 | class ReturnStatement(Node): 123 | def __init__(self, expr): 124 | super().__init__() 125 | self.expr = expr 126 | 127 | def accept(self, visitor): 128 | visitor.visit_ReturnStatement(self) 129 | 130 | 131 | class IfStatement(Node): 132 | def __init__(self, condition, true_block, else_block=None): 133 | super().__init__() 134 | self.condition = condition 135 | self.true_block = true_block 136 | self.else_block = else_block 137 | 138 | def accept(self, visitor): 139 | visitor.visit_IfStatement(self) 140 | 141 | 142 | class WhileStatement(Node): 143 | def __init__(self, condition, body): 144 | super().__init__() 145 | self.condition = condition 146 | self.body = body 147 | 148 | def accept(self, visitor): 149 | visitor.visit_WhileStatement(self) 150 | 151 | 152 | class PrintStatement(Node): 153 | def __init__(self, expr): 154 | super().__init__() 155 | self.expr = expr 156 | 157 | def accept(self, visitor): 158 | visitor.visit_PrintStatement(self) 159 | 160 | 161 | class UnaryOp(Node): 162 | def __init__(self, op, right): 163 | super().__init__() 164 | self.op = op 165 | self.right = right 166 | 167 | def accept(self, visitor): 168 | visitor.visit_UnaryOp(self) 169 | 170 | 171 | class BinaryOp(Node): 172 | def __init__(self, left, op, right): 173 | super().__init__() 174 | self.left = left 175 | self.op = op 176 | self.right = right 177 | 178 | def accept(self, visitor): 179 | visitor.visit_BinaryOp(self) 180 | 181 | 182 | class ComparisonOp(Node): 183 | def __init__(self, left, comp, right): 184 | super().__init__() 185 | self.left = left 186 | self.comp = comp 187 | self.right = right 188 | 189 | def accept(self, visitor): 190 | visitor.visit_ComparisonOp(self) 191 | 192 | 193 | class LogicOp(Node): 194 | def __init__(self, left, op, right): 195 | super().__init__() 196 | self.left = left 197 | self.op = op 198 | self.right = right 199 | 200 | def accept(self, visitor): 201 | visitor.visit_LogicOp(self) 202 | 203 | 204 | class IntConst(Node): 205 | def __init__(self, value): 206 | super().__init__() 207 | self.value = int(value) 208 | 209 | def accept(self, visitor): 210 | visitor.visit_IntConst(self) 211 | 212 | 213 | class Identifier(Node): 214 | def __init__(self, name): 215 | super().__init__() 216 | self.name = name 217 | 218 | def accept(self, visitor): 219 | visitor.visit_Identifier(self) 220 | 221 | 222 | class IdentifierExp(Node): 223 | def __init__(self, identifier): 224 | super().__init__() 225 | self.identifier = identifier 226 | 227 | def accept(self, visitor): 228 | visitor.visit_IdentifierExp(self) 229 | 230 | 231 | class ExpGroup(Node): 232 | def __init__(self, expression): 233 | super().__init__() 234 | self.expression = expression 235 | 236 | def accept(self, visitor): 237 | visitor.visit_ExpGroup(self) 238 | 239 | 240 | class Type(Node): 241 | def __init__(self, name): 242 | super().__init__() 243 | self.name = name 244 | 245 | def accept(self, visitor): 246 | visitor.visit_Type(self) 247 | 248 | 249 | # class For(Node): 250 | # def __init__(self, assignement, max_, body): 251 | # self.type = "for" 252 | # self.assignement = assignement 253 | # self.max = max_ 254 | # self.body = body 255 | 256 | # def children(self): 257 | # return (self.assignement, self.max, self.body) 258 | 259 | # class FuncCall(Node): 260 | # def __init__(self, identifier, args): 261 | # self.type = "funccall" 262 | # self.identifier = identifier 263 | # self.args = args 264 | 265 | # def children(self): 266 | # return tuple([self.type] + self.args) 267 | -------------------------------------------------------------------------------- /rc/rc_parse.py: -------------------------------------------------------------------------------- 1 | import rc_lex 2 | 3 | import rc_ast as ast 4 | from ply import yacc 5 | 6 | tokens = rc_lex.tokens 7 | 8 | precedence = ( 9 | ("left", "AND", "OR"), 10 | ("left", "BOR"), 11 | ("left", "BXOR"), 12 | ("left", "BAND"), 13 | ("left", "EQ", "NE"), 14 | ("left", "LT", "LE", "GT", "GE"), 15 | ("left", "SHL", "SHR"), 16 | ("left", "PLUS", "MINUS"), 17 | ("left", "MULTIPLY", "DIVIDE", "MODULO"), 18 | ("right", "UINCREMENT", "UDECREMENT", "UNOT", "UMINUS", "UBNOT"), 19 | ) 20 | 21 | 22 | def p_program(p): 23 | """program : program_item 24 | | program program_item""" 25 | if len(p) == 2: 26 | p[0] = ast.Program() 27 | p[0].items.append(p[1]) 28 | else: 29 | p[1].items.append(p[2]) 30 | p[0] = p[1] 31 | 32 | 33 | def p_program_item(p): 34 | """program_item : func_def""" 35 | p[0] = p[1] 36 | 37 | 38 | def p_var_decl(p): 39 | """var_decl : type ident SEMI""" 40 | p[0] = ast.VarDecl(p[1], p[2]) 41 | 42 | 43 | # def p_func_decl(p): 44 | # """func_decl : type ident LPAREN func_params RPAREN SEMI""" 45 | # p[0] = ast.FuncDecl(p[1], p[2], p[4]) 46 | 47 | 48 | def p_func_def(p): 49 | """func_def : type ident LPAREN func_params RPAREN func_body""" 50 | p[0] = ast.FuncDef(p[1], p[2], p[4], p[6]) 51 | 52 | 53 | def p_func_param(p): 54 | """func_param : type ident""" 55 | p[0] = ast.FuncParam(p[1], p[2]) 56 | 57 | 58 | def p_func_params(p): 59 | """func_params : 60 | | func_param 61 | | func_params COMMA func_param""" 62 | if len(p) == 1: 63 | p[0] = ast.FuncParams() 64 | elif len(p) == 2: 65 | p[0] = ast.FuncParams([p[1]]) 66 | else: 67 | p[1].args.append(p[3]) 68 | p[0] = p[1] 69 | 70 | 71 | def p_func_body(p): 72 | """func_body : statement_block""" 73 | p[0] = p[1] 74 | 75 | 76 | def p_func_args(p): 77 | """func_args : 78 | | expression 79 | | func_args COMMA expression""" 80 | if len(p) == 1: 81 | p[0] = ast.FuncArgs() 82 | elif len(p) == 2: 83 | p[0] = ast.FuncArgs([p[1]]) 84 | else: 85 | p[1].args.append(p[3]) 86 | p[0] = p[1] 87 | 88 | 89 | def p_func_call(p): 90 | """func_call : ident LPAREN func_args RPAREN""" 91 | p[0] = ast.FuncCall(p[1], p[3]) 92 | 93 | 94 | def p_type(p): 95 | """type : INT""" 96 | p[0] = ast.Type(p[1]) 97 | 98 | 99 | def p_statement_block(p): 100 | """statement_block : LBRACE RBRACE 101 | | LBRACE statement_list RBRACE""" 102 | if len(p) == 3: 103 | p[0] = ast.StatementBlock() 104 | else: 105 | p[0] = ast.StatementBlock(p[2]) 106 | 107 | 108 | def p_statement_list(p): 109 | """statement_list : statement 110 | | statement_list statement""" 111 | if len(p) == 2: 112 | p[0] = ast.Statements([p[1]]) 113 | else: 114 | p[1].statements.append(p[2]) 115 | p[0] = p[1] 116 | 117 | 118 | def p_statement(p): 119 | """statement : assign_statement 120 | | break_statement 121 | | return_statement 122 | | if_statement 123 | | while_statement 124 | | print_statement 125 | | expr_statement 126 | | var_decl""" 127 | p[0] = p[1] 128 | 129 | 130 | def p_statement_assign(p): 131 | """assign_statement : ident EQUALS expression SEMI""" 132 | p[0] = ast.AssignStatement(p[1], p[3]) 133 | 134 | 135 | def p_statement_return(p): 136 | """return_statement : RETURN expression SEMI""" 137 | p[0] = ast.ReturnStatement(p[2]) 138 | 139 | 140 | def p_statement_break(p): 141 | """break_statement : BREAK SEMI""" 142 | p[0] = ast.BreakStatement(p[2]) 143 | 144 | 145 | def p_statement_if(p): 146 | """if_statement : IF LPAREN expression RPAREN statement_block 147 | | IF LPAREN expression RPAREN statement_block ELSE statement_block""" 148 | if len(p) == 6: # simple if without else 149 | p[0] = ast.IfStatement(p[3], p[5]) 150 | else: # if with else 151 | p[0] = ast.IfStatement(p[3], p[5], p[7]) 152 | 153 | 154 | def p_statement_while(p): 155 | """while_statement : WHILE LPAREN expression RPAREN statement_block""" 156 | p[0] = ast.WhileStatement(p[3], p[5]) 157 | 158 | 159 | def p_statement_print(p): 160 | """print_statement : PRINT LPAREN expression RPAREN SEMI""" 161 | p[0] = ast.PrintStatement(p[3]) 162 | 163 | 164 | def p_statement_expr(p): 165 | """expr_statement : expression SEMI""" 166 | p[0] = p[1] 167 | 168 | 169 | def p_expression(p): 170 | """expression : binop_expression 171 | | unop_expression 172 | | comparison_expression 173 | | logic_expression 174 | | num_expression 175 | | group_expression 176 | | ident_expression 177 | | func_call""" 178 | p[0] = p[1] 179 | 180 | 181 | def p_expression_binop(p): 182 | """binop_expression : expression PLUS expression 183 | | expression MINUS expression 184 | | expression MULTIPLY expression 185 | | expression DIVIDE expression 186 | | expression MODULO expression 187 | | expression SHL expression 188 | | expression SHR expression 189 | | expression BOR expression 190 | | expression BXOR expression 191 | | expression BAND expression""" 192 | p[0] = ast.BinaryOp(p[1], p[2], p[3]) 193 | 194 | 195 | def p_comparison_expression(p): 196 | """comparison_expression : expression EQ expression 197 | | expression NE expression 198 | | expression LT expression 199 | | expression GT expression 200 | | expression LE expression 201 | | expression GE expression""" 202 | p[0] = ast.ComparisonOp(p[1], p[2], p[3]) 203 | 204 | 205 | def p_expression_logic(p): 206 | """logic_expression : expression OR expression 207 | | expression AND expression""" 208 | p[0] = ast.LogicOp(p[1], p[2], p[3]) 209 | 210 | 211 | def p_expression_unop(p): 212 | """unop_expression : NOT expression %prec UNOT 213 | | BNOT expression %prec UBNOT 214 | | MINUS expression %prec UMINUS 215 | | INCREMENT ident_expression %prec UINCREMENT 216 | | DECREMENT ident_expression %prec UDECREMENT""" 217 | p[0] = ast.UnaryOp(p[1], p[2]) 218 | 219 | 220 | def p_expression_number(p): 221 | """num_expression : NUMBER""" 222 | p[0] = ast.IntConst(p[1]) 223 | 224 | 225 | def p_expression_ident(p): 226 | """ident_expression : ident""" 227 | p[0] = ast.IdentifierExp(p[1]) 228 | 229 | 230 | def p_expression_group(p): 231 | """group_expression : LPAREN expression RPAREN""" 232 | p[0] = ast.ExpGroup(p[2]) 233 | 234 | 235 | def p_ident(p): 236 | """ident : ID""" 237 | p[0] = ast.Identifier(p[1]) 238 | 239 | 240 | def p_error(p): 241 | if p: 242 | print("Syntax error at '%s'" % p.value) 243 | else: 244 | print("Syntax error at EOF") 245 | 246 | 247 | yacc.yacc() 248 | 249 | 250 | def parse(code, debug=False): 251 | return yacc.parse(code, debug=debug) 252 | -------------------------------------------------------------------------------- /rc/rc_semantics.py: -------------------------------------------------------------------------------- 1 | from collections import OrderedDict 2 | 3 | 4 | class Symbol: 5 | def __init__(self, name, type=None): 6 | self.name = name 7 | self.type = type 8 | 9 | 10 | class FuncSymbol(Symbol): 11 | def __init__(self, name, rtype="int", arg_types=None): 12 | self.name = name 13 | self.rtype = rtype 14 | self.arg_types = arg_types or [] 15 | 16 | def args_size(self): 17 | sizes = {"int": 4} 18 | return sum(sizes[t] for t in self.arg_types) 19 | 20 | 21 | class VarSymbol(Symbol): 22 | SIZE_MAP = {"int": 4} 23 | 24 | def __init__(self, name, type="int", offset=None): 25 | super().__init__(name, type) 26 | self.offset = None 27 | 28 | def type_size(self): 29 | return self.SIZE_MAP[self.type] 30 | 31 | 32 | class LocalVarSymbol(VarSymbol): 33 | pass 34 | 35 | 36 | class ArgVarSymbol(VarSymbol): 37 | def __init__(self, name, type="int", offset=None, func_symbol=None): 38 | if not func_symbol: 39 | raise ValueError 40 | super().__init__(name, type) 41 | self.func_symbol = func_symbol 42 | 43 | 44 | class SymbolTable(object): 45 | def __init__(self, scope_name, scope_level, enclosing_scope=None): 46 | self._symbols = OrderedDict() 47 | self.scope_name = scope_name 48 | self.scope_level = scope_level 49 | self.stack_offset = 0 50 | self.arg_offset = 0 51 | self.enclosing_scope = enclosing_scope 52 | 53 | def insert(self, symbol): 54 | if isinstance(symbol, LocalVarSymbol): 55 | self.stack_offset += symbol.type_size() 56 | symbol.offset = self.stack_offset 57 | elif isinstance(symbol, ArgVarSymbol): 58 | self.arg_offset += symbol.type_size() 59 | symbol.offset = self.arg_offset 60 | self._symbols[symbol.name] = symbol 61 | 62 | def lookup(self, name, current_scope_only=False): 63 | symbol = self._symbols.get(name) 64 | 65 | if symbol: 66 | return symbol 67 | if current_scope_only: 68 | return None 69 | 70 | if self.enclosing_scope is not None: 71 | return self.enclosing_scope.lookup(name) 72 | 73 | 74 | class SemanticAnalyzer: 75 | def __init__(self): 76 | self.current_scope = None 77 | 78 | def child_accept(self, parent, child): 79 | child.parent = parent 80 | child.accept(self) 81 | 82 | def visit_Program(self, node): 83 | global_scope = SymbolTable( 84 | scope_name="global", 85 | scope_level=1, 86 | enclosing_scope=self.current_scope, # None 87 | ) 88 | self.current_scope = global_scope 89 | 90 | for c in node.items: 91 | self.child_accept(node, c) 92 | 93 | self.current_scope = self.current_scope.enclosing_scope 94 | 95 | def visit_VarDecl(self, node): 96 | var_name = node.ident.name 97 | if self.current_scope.lookup(var_name, current_scope_only=True): 98 | raise Exception("{} already declared".format(var_name)) 99 | 100 | node.symbol = LocalVarSymbol(var_name, node.type.name) 101 | self.current_scope.insert(node.symbol) 102 | 103 | def visit_FuncDef(self, node): 104 | func_name = node.ident.name 105 | if self.current_scope.lookup(func_name, current_scope_only=True): 106 | raise Exception("{} already declared".format(func_name)) 107 | 108 | node.symbol = FuncSymbol( 109 | func_name, node.rtype.name, [p.type.name for p in node.args.args] 110 | ) 111 | self.current_scope.insert(node.symbol) 112 | 113 | self.child_accept(node, node.rtype) 114 | self.child_accept(node, node.ident) 115 | 116 | node.scope = SymbolTable( 117 | scope_name=node.ident.name, 118 | scope_level=self.current_scope.scope_level + 1, 119 | enclosing_scope=self.current_scope, 120 | ) 121 | self.current_scope = node.scope 122 | 123 | self.child_accept(node, node.args) 124 | self.child_accept(node, node.body) 125 | 126 | self.current_scope = self.current_scope.enclosing_scope 127 | 128 | def visit_FuncParam(self, node): 129 | var_name = node.ident.name 130 | if self.current_scope.lookup(var_name, current_scope_only=True): 131 | raise Exception("{} already declared".format(var_name)) 132 | 133 | node.symbol = ArgVarSymbol(var_name, node.type.name, func_symbol=node.parent.parent.symbol) 134 | self.current_scope.insert(node.symbol) 135 | 136 | def visit_FuncParams(self, node): 137 | for i in range(len(node.args)): 138 | self.child_accept(node, node.args[i]) 139 | 140 | def visit_FuncCall(self, node): 141 | node.symbol = self.current_scope.lookup(node.ident.name) 142 | if not node.symbol or not isinstance(node.symbol, FuncSymbol): 143 | raise Exception("Invalid symbol {}".format(node.ident.name)) 144 | if len(node.args.args) != len(node.symbol.arg_types): 145 | raise Exception("Args number mismatch for {}".format(node.ident.name)) 146 | 147 | self.child_accept(node, node.ident) 148 | self.child_accept(node, node.args) 149 | 150 | def visit_FuncArgs(self, node): 151 | for i in range(len(node.args)): 152 | self.child_accept(node, node.args[i]) 153 | 154 | def visit_StatementBlock(self, node): 155 | self.child_accept(node, node.statements) 156 | 157 | def visit_Statements(self, node): 158 | for s in node.statements: 159 | self.child_accept(node, s) 160 | 161 | def visit_AssignStatement(self, node): 162 | node.symbol = self.current_scope.lookup(node.ident.name) 163 | if not node.symbol: 164 | raise Exception("Invalid symbol {}".format(node.ident.name)) 165 | 166 | self.child_accept(node, node.ident) 167 | self.child_accept(node, node.value) 168 | 169 | def visit_ReturnStatement(self, node): 170 | self.child_accept(node, node.expr) 171 | 172 | def visit_BreakStatement(self, node): 173 | pass 174 | 175 | def visit_IfStatement(self, node): 176 | self.child_accept(node, node.condition) 177 | self.child_accept(node, node.true_block) 178 | if node.else_block: 179 | self.child_accept(node, node.else_block) 180 | 181 | def visit_WhileStatement(self, node): 182 | self.child_accept(node, node.condition) 183 | self.child_accept(node, node.body) 184 | 185 | def visit_PrintStatement(self, node): 186 | self.child_accept(node, node.expr) 187 | 188 | def visit_UnaryOp(self, node): 189 | self.child_accept(node, node.right) 190 | 191 | def visit_BinaryOp(self, node): 192 | self.child_accept(node, node.left) 193 | self.child_accept(node, node.right) 194 | 195 | def visit_ComparisonOp(self, node): 196 | self.child_accept(node, node.left) 197 | self.child_accept(node, node.right) 198 | 199 | def visit_LogicOp(self, node): 200 | self.child_accept(node, node.left) 201 | self.child_accept(node, node.right) 202 | 203 | def visit_BoolConst(self, node): 204 | pass 205 | 206 | def visit_IntConst(self, node): 207 | pass 208 | 209 | def visit_Identifier(self, node): 210 | pass 211 | 212 | def visit_IdentifierExp(self, node): 213 | node.symbol = self.current_scope.lookup(node.identifier.name) 214 | if not node.symbol: 215 | raise Exception("Invalid symbol {}".format(node.identifier.name)) 216 | 217 | self.child_accept(node, node.identifier) 218 | 219 | def visit_ExpGroup(self, node): 220 | self.child_accept(node, node.expression) 221 | 222 | def visit_Type(self, node): 223 | pass 224 | -------------------------------------------------------------------------------- /src/vm.h: -------------------------------------------------------------------------------- 1 | #ifndef __VM_H__ 2 | #define __VM_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | enum ExecResult : uint8_t 10 | { 11 | VM_FINISHED, // execution completed (i.e. got halt instruction) 12 | VM_PAUSED, // execution paused since we hit the maximum instructions 13 | VM_ERR_UNKNOWN_OPCODE, // unknown opcode 14 | VM_ERR_UNSUPPORTED_OPCODE, // instruction not supported on this platform 15 | VM_ERR_INVALID_REGISTER, // invalid register access 16 | VM_ERR_UNHANDLED_INTERRUPT, // interrupt triggered without registered handler 17 | VM_ERR_STACK_OVERFLOW, // stack overflow 18 | VM_ERR_STACK_UNDERFLOW, // stack underflow 19 | VM_ERR_INVALID_ADDRESS, // tried to access an invalid memory address 20 | }; 21 | 22 | enum Instruction : uint8_t 23 | { 24 | // system: 25 | OP_NOP, // do nothing 26 | OP_HALT, // halt execution 27 | OP_INT, // interrupt which should be handled by user-registered function 28 | // constants: 29 | OP_LCONS, // store a value in a register, e.g.: lcons r0, 0xA2 0x00 0x00 0x00 30 | OP_LCONSW, // store a word value in a register, e.g.: lconsw r0, 0xA2 0x00 31 | OP_LCONSB, // store a byte value in a register, e.g.: lconsb r0, 0xA2 32 | // register operations: 33 | OP_MOV, // copy a value between registers, e.g.: mov r0, r2 34 | // stack: 35 | OP_PUSH, // push a register onto the stack, e.g.: push r0 36 | OP_POP, // pop the first element of the stack to a register, e.g.: pop r0 37 | OP_POP2, // pop the two first elements of the stack to registers, e.g.: pop r0, r1 38 | OP_DUP, // duplicate the last value in the stack, e.g.: dup 39 | // functions 40 | OP_CALL, // set register RA to the next instruction and jump to subroutine, e.g.: call 0x10 0x00 41 | OP_RET, // return to the address of last callee (RA), e.g.: ret 42 | // memory: 43 | OP_STOR, // copy a value from a register to a heap address, e.g.: stor 0x08 0x00, r0 44 | OP_STOR_P, 45 | OP_STORW, // copy a word value from a register to a heap address, e.g.: storw 0x08 0x00, r0 46 | OP_STORW_P, 47 | OP_STORB, // copy a byte value from a register to a heap address, e.g.: storb 0x08 0x00, r0 48 | OP_STORB_P, 49 | OP_LOAD, // copy a value from a heap address to a register, e.g.: load r0, 0x08 0x00 50 | OP_LOAD_P, 51 | OP_LOADW, // copy a word value from a heap address to a register, e.g.: loadw r0, 0x08 0x00 52 | OP_LOADW_P, 53 | OP_LOADB, // copy a byte value from a heap address to a register, e.g.: loadb r0, 0x08 0x00 54 | OP_LOADB_P, 55 | OP_MEMCPY, // copy N bytes from one memory address S to another address D, e.g.: memcpy 0xDD 0xDD, 0xSS 0xSS, 0xNN 0xNN 56 | OP_MEMCPY_P, 57 | // arithmetic: 58 | OP_INC, // increment the specified register, e.g.: inc r0 59 | OP_FINC, // increment a float in the specified register, e.g.: incf r0 60 | OP_DEC, // decrement the specified register, e.g.: dec r0 61 | OP_FDEC, // decrement a float in the specified register, e.g.: decf r0 62 | OP_ADD, // sum and store in first reg, e.g.: add r0, r2 63 | OP_FADD, // sum two floats and store in first reg, e.g.: addf r0, r2 64 | OP_SUB, // subtract and store in first reg, e.g.: sub r0, r2 65 | OP_FSUB, // subtract two floats and store in first reg, e.g.: subf r0, r2 66 | OP_MUL, // multiply and store in first reg, e.g.: mul r0, r2 67 | OP_IMUL, // signed multiply and store in first reg, e.g.: mul r0, r2 68 | OP_FMUL, // multiply two floats and store in first reg, e.g.: mulf r0, r2 69 | OP_DIV, // divide and store in first reg, e.g.: div r0, r2 70 | OP_IDIV, // signed divide and store in first reg, e.g.: div r0, r2 71 | OP_FDIV, // divide two floats and store in first reg, e.g.: divf r0, r2 72 | OP_SHL, // logical shift left, e.g.: shl r0, r1 73 | OP_SHR, // logical shift right, e.g.: shr r0, r1 74 | OP_ISHR, // arithmetic shift right (for signed values), e.g.: ishr r0, r1 75 | OP_MOD, // store in first reg the modulo of rreg 2 by reg 3, e.g.: mod r0, r1, r2 76 | OP_IMOD, // store signed division remainder in first reg, e.g.: mod r0, r1 77 | OP_AND, // and two registers and store result in the first one, e.g.: and r0, r1 78 | OP_OR, // or two registers and store result in the first one, e.g.: or r0, r1 79 | OP_XOR, // xor two registers and store result in the first one, e.g.: xor r0, r1 80 | OP_NOT, // not a register and store result, e.g.: not r0 81 | // conversions: 82 | OP_U2I, // convert an unsigned integer to a signed integer, e.g.: u2i r0 83 | OP_I2U, // convert an signed integer to a unsigned integer, e.g.: i2u r0 84 | OP_I2F, // convert an integer stored in a register to a float, e.g.: i2f r0 85 | OP_F2I, // convert a float stored in a register to an int, e.g.: f2i r0 86 | // branching: 87 | OP_JMP, // jump to address, e.g.: jmp 0x0A 0x00 88 | OP_JR, // jump to address in register, e.g.: jr r1 89 | OP_JZ, // jump if zero, e.g.: jz r0, 0x0A 0x00 90 | OP_JNZ, // jump if not zero, e.g.: jnz r0, 0x0A 0x00 91 | OP_JE, // jump if equal, e.g. je r0, r1, 0x0A 0x00 92 | OP_JNE, // jump if not equal, e.g. jne r0, r1, 0x1C 0x00 93 | OP_JA, // (unsigned) jump if above, e.g. ja r0, r1, 0x1C 0x00 94 | OP_JG, // (signed) jump if greater, e.g. jg r0, r1, 0x1C 0x00 95 | OP_JAE, // (unsigned) jump if above or equal, e.g. jae r0, r1, 0x1C 0x00 96 | OP_JGE, // (signed) jump if greater or equal, e.g. jge r0, r1, 0x1C 0x00 97 | OP_JB, // (unsigned) jump if below, e.g. jb r0, r1, 0x1C 0x00 98 | OP_JL, // (signed) jump if less, e.g. jl r0, r1, 0x1C 0x00 99 | OP_JBE, // (unsigned) jump if below or equal, e.g. jbe r0, r1, 0x1C 0x00 100 | OP_JLE, // (signed) jump if less or equal, e.g. jle r0, r1, 0x1C 0x00 101 | // strings: 102 | OP_PRINT, // print an integer stored in a register, e.g.: print r0 103 | OP_PRINTI, // print a signed integer stored in a register, e.g.: printi r0 104 | OP_PRINTF, // print a float stored in a register, e.g.: printf r0 105 | OP_PRINTC, // print a single char stored in a register 106 | OP_PRINTS, // print a string stored in a memory address 107 | OP_PRINTLN, // print a newline, e.g.: println 108 | OP_READ, // read an integer from stdin 109 | OP_READI, // read a signed integer from stdin to the specified register 110 | OP_READF, // read a float from stdin to the specified register 111 | OP_READC, // read a single character's code from stdin to the specified register 112 | OP_READS, // read a line to the specified memory address, to a maximum length 113 | INSTRUCTION_COUNT 114 | }; 115 | 116 | enum Register : uint8_t 117 | { 118 | // preserved across a call 119 | R0, 120 | R1, 121 | R2, 122 | R3, 123 | R4, 124 | R5, 125 | // temporaries - not preserved 126 | T0, 127 | T1, 128 | T2, 129 | T3, 130 | T4, 131 | T5, 132 | T6, 133 | T7, 134 | T8, 135 | T9, 136 | // special 137 | IP, 138 | BP, 139 | SP, 140 | RA, 141 | // count 142 | REGISTER_COUNT 143 | }; 144 | 145 | class VM 146 | { 147 | public: 148 | VM(uint8_t *program, uint16_t progLen, uint16_t stackSize = 256); 149 | ~VM(); 150 | 151 | ExecResult run(uint32_t maxInstr = 0); 152 | void reset(); 153 | void onInterrupt(bool (*callback)(uint8_t)); 154 | 155 | uint32_t stackCount(); 156 | void stackPush(uint32_t value); 157 | uint32_t stackPop(); 158 | 159 | uint8_t *memory(uint16_t addr = 0); 160 | 161 | uint32_t getRegister(Register reg); 162 | void setRegister(Register reg, uint32_t val); 163 | 164 | protected: 165 | uint8_t *_memory; 166 | uint32_t _registers[REGISTER_COUNT] = {0}; 167 | const uint16_t _memSize; 168 | const uint16_t _stackSize; 169 | const uint16_t _progLen; 170 | bool (*_interruptCallback)(uint8_t) = nullptr; 171 | }; 172 | 173 | #endif // __VM_H__ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RISVM - Reduced Instruction Set VM 2 | 3 | [![Build Status](https://travis-ci.com/bitmario/RISVM.svg?branch=master)](https://travis-ci.com/bitmario/RISVM) [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) 4 | 5 | RISVM is a low overhead, embeddable bytecode virtual machine with a RISC architecture which is designed to work on any little-endian platform with GCC or clang. 6 | 7 | It is implemented in around 1000 lines of simple C++ and includes [tests](test/) and a basic Python 3 [assembler](assembler/) with [documentation](#assembly) and [examples](examples/asm). 8 | 9 | This project is still a work in progress, so be aware that backwards compatibility may be broken at any time. 10 | 11 | ## Getting started 12 | 13 | Build the standard VM interpreter and assemble an example: 14 | 15 | ```bash 16 | make 17 | python3 assembler/assembler.py examples/asm/helloworld.asm 18 | ``` 19 | 20 | Run it! 21 | 22 | ``` 23 | ./vm examples/asm/helloworld.bin 24 | ``` 25 | 26 | ### Embedding 27 | 28 | Include `vm.h` in your project and do something like this: 29 | 30 | ```cpp 31 | uint8_t program[] = { ... }; 32 | VM vm(program, sizeof(program)); 33 | vm.run(); 34 | ``` 35 | 36 | ## Architecture 37 | 38 | ### Registers 39 | 40 | There are 16 general-purpose and 4 special registers, all of which are stored as 32-bit unsigned integers. 41 | 42 | | name | number | type | preserved | 43 | |-------|--------|----------------------------------|-----------| 44 | | r0-r5 | 0-5 | general-purpose | Y | 45 | | t0-t9 | 6-15 | general-purpose (temporary/args) | N | 46 | | ip | 16 | instruction pointer | Y | 47 | | bp | 17 | base pointer | Y | 48 | | sp | 18 | stack pointer | Y | 49 | | ra | 19 | return address | N | 50 | 51 | ### Memory 52 | 53 | Currently, data resides together with the program so care must be taken to ensure execution flow never reaches data sections. All labels and data references are made using positive 16-bit offsets from the first program byte. 54 | 55 | A stack is also available, currently hardcoded to 128 `uint32` values. 56 | 57 | Please note that there are currently no checks for stack overflows or out of bounds memory access so there will be undefined behavior if those happen. This is one of the reasons why you shouldn't run untrusted programs or use this for critical applications at this stage. 58 | 59 | ### Instructions 60 | 61 | Instruction codes are always one-byte long and, depending on type, may be followed by one or more bytes a representing operands. Instructions are also not aligned, in order to save space. 62 | 63 | In RISC spirit, there are only 70 [instructions](#instruction-reference), most of which are quite simple and operate only on registers (except load/store instructions and string operations). 64 | 65 | ## Assembly 66 | 67 | Instruction names are inspired on x86 which makes them sound familiar and allows us to leverage standard assembly highlighting in editors. 68 | 69 | Destinations (if any) are always the first operand. 70 | 71 | ### Constants 72 | 73 | Numerical constants can be specified as `123`, `-123` or `0xA1`. They will always be encoded as unsigned unless they are negative. 74 | 75 | Single-character constants can be specified with single quotes, e.g. `'A'`. 76 | 77 | Strings can be specified with double quotes, e.g. `"A short string"`. 78 | 79 | ### Defining data 80 | 81 | Data and respective labels may be defined by specifying a name prefixed with `$`, a type (`byte` for 8 bits, `word` for 16 bits or `dword` for 32 bits) and the value. To specity an array, append brackets to the type and separate values with commas. 82 | 83 | If a string is included in a `byte[]` definition, then a null terminator byte will automatically be appended to the byte array. If you build your string from individual characters, you must add the null terminator yourself. 84 | 85 | Examples: 86 | 87 | ```assembly 88 | $val32 dword 1234567 89 | $val16 word 1234 90 | $doneStr byte[] "done", '!', 0xA 91 | ``` 92 | 93 | ### Defining labels 94 | 95 | Labels pointing to specific addresses in the program can be defined by starting a line with a period, followed by an identifier and a colon. Labels must be defined in their own line. 96 | 97 | These labels can then be used as jump targets, e.g.: 98 | 99 | ```assembly 100 | ; jump over the data, to our entry label 101 | jmp .entry 102 | 103 | $hello byte[] "Hello world!", 0x0A 104 | 105 | ; label definition 106 | .entry: 107 | prints $hello 108 | halt 109 | ``` 110 | 111 | ### Instruction reference 112 | 113 | #### System 114 | 115 | ```assembly 116 | nop ; do nothing 117 | halt ; terminate execution 118 | int 0xFF ; trigger interrupt 0xFF 119 | ``` 120 | 121 | #### Registers 122 | 123 | ```assembly 124 | lcons r0, 0xFFFFFFFF ; load 32-bit constant 125 | lconsw r0, 0xFFFF ; load 16-bit (word) constant 126 | lconsb r0, 0xFF ; load 8-bit (byte) constant 127 | mov r0, r1 ; copy the contents of register r1 to r0 128 | ``` 129 | 130 | #### Stack 131 | 132 | ```assembly 133 | push r0 ; push a register onto the stack 134 | pop r0 ; pop a value from the stack into a register 135 | pop2 r0, r1 ; pop two values from the stack into two registers 136 | dup ; duplicate the last value in the stack 137 | ``` 138 | 139 | #### Functions 140 | 141 | ```assembly 142 | call .mySub ; set the return address register and jump to label 143 | ret ; return to the address of last caller 144 | ``` 145 | 146 | #### Memory 147 | 148 | ```assembly 149 | stor $var1, r0 ; store the 32-bit value of r0 in memory location var1 150 | stor_p r1, r0 ; store the 32-bit value of r0 in the memory location pointed by r1 151 | storw $var1, r0 ; store the 16-bit value of r0 in memory location var1 152 | storw_p r1, r0 ; store the 16-bit value of r0 in the memory location pointed by r1 153 | storb $var1, r0 ; store the 8-bit value of r0 in memory location var1 154 | storb_p r1, r0 ; store the 8-bit value of r0 in the memory location pointed by r1 155 | load r0, $var1 ; load the 32-bit value of memory location var1 into r0 156 | load_p r0, r1 ; load the 32-bit value of the memory location pointed by r1 into r0 157 | loadw r0, $var1 ; load the 16-bit value of memory location var1 into r0 158 | loadw_p r0, r1 ; load the 16-bit value of the memory location pointed by r1 into r0 159 | loadb r0, $var1 ; load the 8-bit value of memory location var1 into r0 160 | loadb_p r0, r1 ; load the 8-bit value of the memory location pointed by r1 into r0 161 | memcpy $dest, $src, 0xFFFF ; copy the specified number of bytes from source to dest 162 | memcpy_p r0, r1, r2 ; copy the # bytes in r2 from the address in r1 to the address in r0 163 | ``` 164 | 165 | #### Arithmetic 166 | 167 | ```assembly 168 | inc r0 ; increment the value of register 169 | finc r0 ; (float) increment the value of register 170 | dec r0 ; decrement the value of register 171 | fdec r0 ; (float) decrement the value of register 172 | add r0, r1, r2 ; add r1 and r2 and store result in r0 173 | fadd r0, r1, r2 ; (float) add r1 and r2 and store result in r0 174 | sub r0, r1, r2 ; subtract r2 from r1 and store result in r0 175 | fsub r0, r1, r2 ; (float) subtract r2 from r1 and store result in r0 176 | mul r0, r1, r2 ; multiply r1 by r2 and store result in r0 177 | imul r0, r1, r2 ; (signed) multiply r1 by r2 and store result in r0 178 | fmul r0, r1, r2 ; (float) multiply r1 by r2 and store result in r0 179 | div r0, r1, r2 ; divide r1 by r2 and store result in r0 180 | idiv r0, r1, r2 ; (signed) divide r1 by r2 and store result in r0 181 | fdiv r0, r1, r2 ; (float) divide r1 by r2 and store result in r0 182 | shl r0, r1, r2 ; shift r1 left by r2 and store result in r0 183 | shr r0, r1, r2 ; shift r1 right by r2 and store result in r0 184 | ishr r0, r1, r2 ; (signed) shift r1 right by r2 and store result in r0 185 | mod r0, r1, r2 ; store remainder of r1 divided by r2 in r0 186 | imod r0, r1, r2 ; (signed) store remainder of r1 divided by r2 in r0 187 | and r0, r1, r2 ; store reult of r1 AND r2 in r0 188 | or r0, r1, r2 ; store reult of r1 OR r2 in r0 189 | xor r0, r1, r2 ; store reult of r1 XOR r2 in r0 190 | not r0, r1 ; store reult of NOT r1 in r0 191 | ``` 192 | 193 | #### Conversions 194 | 195 | ```assembly 196 | f2i r0, r1 ; convert float r1 to signed int and store in r0 197 | i2f r0, r1 ; convert signed int r1 to float and store in r0 198 | ``` 199 | 200 | #### Branching 201 | 202 | ```assembly 203 | jmp .labelName ; unconditional jump to address 204 | jr r0 ; unconditional jump to address in register 205 | jz r0, .jmpDest ; jump if r0 is zero 206 | jnz r0, .jmpDest ; jump if r0 is not zero 207 | je r0, r1, .jmpDest ; jump if r0 and r1 are equal 208 | jne r0, r1, .jmpDest ; jump if r0 and r1 are not equal 209 | ja r0, r1, .jmpDest ; jump if r0 is greater than r1 210 | jg r0, r1, .jmpDest ; (signed) jump if r0 is greater than r1 211 | jae r0, r1, .jmpDest ; jump if r0 is greater or equal to r1 212 | jge r0, r1, .jmpDest ; (signed) jump if r0 is greater or equal to r1 213 | jb r0, r1, .jmpDest ; jump if r0 is less than r1 214 | jl r0, r1, .jmpDest ; (signed) jump if r0 is less than r1 215 | jbe r0, r1, .jmpDest ; jump if r0 is less or equal to r1 216 | jle r0, r1, .jmpDest ; (signed) jump if r0 is less or equal to r1 217 | ``` 218 | 219 | #### I/O 220 | 221 | ```assembly 222 | print r0, 0x1 ; print register r0, with or without newline (0/1) 223 | printi r0, 0x1 ; (signed) print register r0, with or without newline (1/0) 224 | printf r0, 0x1 ; (float) print register r0, with or without newline (1/0) 225 | printc r0 ; (char) print single character from a code in r0 226 | prints $myStr ; (char array) print string at address 227 | println ; print a newline 228 | read r0 ; read an integer from stdin to r0 229 | readi r0 ; (signed) read a signed integer from stdin to r0 230 | readf r0 ; (float) read a float from stdin to r0 231 | readc r0 ; (char) read a char from stdin to r0 232 | reads $strBuf, 10 ; (char array) read a max of 10 chars from stdin into $strBuf 233 | ``` 234 | 235 | ## Performance 236 | 237 | While performance is not the main focus, we aim to make the VM as efficient as possible without compromising simplicity. 238 | 239 | The [primes.asm](examples/asm/primes.asm) example was used as a benchmark on x86-64 system. Here are the results vs. comparable implementations in some popular languages: 240 | 241 | | implementation | runtime | performance hit | 242 | |-------------------|-----------|-----------------| 243 | | C++ (G++ 7.3 -O3) | 1.80s | 0% (baseline) | 244 | | JS (node 8.10) | 2.18s | 21% | 245 | | **RISVM** | **4.97s** | **176%** | 246 | | Lua 5.3 | 7.23s | 305% | 247 | | Python 3.6.6 | 35.95s | 1897% | 248 | 249 | It seems like RISVM is about 3x slower than native code, but still beats most scripting languages. However, on less powerful architectures like Xtensa and AVR the VM can be up to 10 times slower than native code. 250 | 251 | ## License 252 | 253 | Licnesed under the MIT License, see the [LICENSE](LICENSE) file for details. 254 | 255 | Includes the [Catch2](https://github.com/catchorg/Catch2) testing library which is licensed under the Boost Software License, read the full license [here](test/lib/catch2_LICENSE). 256 | -------------------------------------------------------------------------------- /rc/rc_compiler.py: -------------------------------------------------------------------------------- 1 | import random 2 | import string 3 | from enum import IntEnum 4 | 5 | from rc_semantics import LocalVarSymbol, ArgVarSymbol 6 | 7 | 8 | class CompilerContext: 9 | class Register: 10 | def __init(self): 11 | pass 12 | 13 | def __init__(self): 14 | self.registers = ["r{}".format(i) for i in range(6)] + [ 15 | "t{}".format(i) for i in range(10) 16 | ] 17 | 18 | 19 | class Compiler: 20 | def __init__(self): 21 | self.result = "" 22 | self.indent_count = 0 23 | 24 | def add(self, s): 25 | self.result += s 26 | 27 | def addline(self, s): 28 | self.result += "\n{}{}".format(" " * self.indent_count, s) 29 | 30 | def indent(self): 31 | self.indent_count += 4 32 | 33 | def dedent(self): 34 | self.indent_count -= 4 35 | 36 | def unique_label(self): 37 | return "loc_{}".format( 38 | "".join(random.choices(string.ascii_uppercase + string.digits, k=6)) 39 | ) 40 | 41 | 42 | class ASMCompiler(Compiler): 43 | def __init__(self): 44 | super().__init__() 45 | self.protected_registers = ["r0", "r1", "r5", "ra", "bp"] 46 | self.arg_offset = len(self.protected_registers) * 4 47 | self.loop_end_label = None 48 | 49 | def emit_label(self, label): 50 | if self.indent_count > 0: 51 | self.dedent() 52 | self.addline(".{}:".format(label)) 53 | self.indent() 54 | 55 | def emit_label_ref(self, label): 56 | self.addline(".{}".format(label)) 57 | 58 | def emit_push(self, register): 59 | self.addline("push {}".format(register)) 60 | 61 | def emit_pop(self, register): 62 | self.addline("pop {}".format(register)) 63 | 64 | def emit_mov(self, dest, src): 65 | self.addline("mov {}, {}".format(dest, src)) 66 | 67 | def emit_loadp(self, dest, src, nbytes): 68 | instr = {1: "loadb_p", 2: "loadw_p", 4: "load_p"}[nbytes] 69 | self.addline("{} {}, {}".format(instr, dest, src)) 70 | 71 | def emit_storp(self, dest, src, nbytes): 72 | instr = {1: "storb_p", 2: "storw_p", 4: "stor_p"}[nbytes] 73 | self.addline("{} {}, {}".format(instr, dest, src)) 74 | 75 | def emit_lcons(self, dest, val, nbytes): 76 | if val < 0: 77 | # HACK: negative values < 4 bytes are not handled correctly, this is a workaround 78 | nbytes = 4 79 | instr = {1: "lconsb", 2: "lconsw", 4: "lcons"}[nbytes] 80 | self.addline("{} {}, {}".format(instr, dest, val)) 81 | 82 | def emit_jmp(self, dest): 83 | self.addline("jmp .{}".format(dest)) 84 | 85 | def emit_jz(self, dest, val): 86 | self.addline("jz {}, .{}".format(val, dest)) 87 | 88 | def emit_jnz(self, dest, val): 89 | self.addline("jnz {}, .{}".format(val, dest)) 90 | 91 | def emit_call(self, dest): 92 | self.addline("call .{}".format(dest)) 93 | 94 | def emit_ret(self): 95 | self.addline("ret") 96 | 97 | def emit_halt(self): 98 | self.addline("halt") 99 | 100 | def emit_printi(self, reg, newline=1): 101 | self.addline("printi {}, {}".format(reg, newline)) 102 | 103 | def emit_inc(self, reg): 104 | self.addline("inc {}".format(reg)) 105 | 106 | def emit_dec(self, reg): 107 | self.addline("dec {}".format(reg)) 108 | 109 | def emit_arithmetic(self, op, dest, x, y, unsigned=False): 110 | instr = { 111 | "+": "add", 112 | "-": "sub", 113 | "*": "mul" if unsigned else "imul", 114 | "/": "div" if unsigned else "idiv", 115 | "<<": "shl", 116 | ">>": "shr" if unsigned else "ishr", 117 | "%": "mod" if unsigned else "imod", 118 | "&": "and", 119 | "|": "or", 120 | "^": "xor", 121 | }[op] 122 | self.addline("{} {}, {}, {}".format(instr, dest, x, y)) 123 | 124 | def emit_comparison(self, op, dest, x, y, unsigned=False): 125 | instr = { 126 | "==": "je", 127 | "!=": "jne", 128 | ">": "ja" if unsigned else "jg", 129 | ">=": "jae" if unsigned else "jge", 130 | "<": "jb" if unsigned else "jl", 131 | "<=": "jbe" if unsigned else "jle", 132 | }[op] 133 | 134 | true_label = self.unique_label() 135 | end_label = self.unique_label() 136 | 137 | self.addline("{} {}, {}, .{}".format(instr, x, y, true_label)) 138 | self.emit_lcons(dest, 0, 1) 139 | self.emit_jmp(end_label) 140 | self.emit_label(true_label) 141 | self.emit_lcons(dest, 1, 1) 142 | self.emit_label(end_label) 143 | 144 | def emit_logic(self, op, dest, x, y): 145 | end_label = self.unique_label() 146 | 147 | if op == "||": 148 | true_label = self.unique_label() 149 | self.emit_jnz(true_label, x) 150 | self.emit_jnz(true_label, y) 151 | self.emit_lcons(dest, 0, 1) 152 | self.emit_jmp(end_label) 153 | self.emit_label(true_label) 154 | self.emit_lcons(dest, 1, 1) 155 | self.emit_label(end_label) 156 | elif op == "&&": 157 | false_label = self.unique_label() 158 | self.emit_jz(false_label, x) 159 | self.emit_jz(false_label, y) 160 | self.emit_lcons(dest, 1, 1) 161 | self.emit_jmp(end_label) 162 | self.emit_label(false_label) 163 | self.emit_lcons(dest, 0, 1) 164 | self.emit_label(end_label) 165 | else: 166 | raise ValueError 167 | 168 | def emit_func_init(self): 169 | for r in self.protected_registers: 170 | self.emit_push(r) 171 | self.emit_mov("bp", "sp") 172 | 173 | def emit_func_cleanup(self): 174 | self.emit_mov("sp", "bp") 175 | for r in self.protected_registers[::-1]: 176 | self.emit_pop(r) 177 | 178 | def emit_func_return(self, val): 179 | self.emit_mov("t0", val) 180 | self.emit_func_cleanup() 181 | self.emit_ret() 182 | 183 | 184 | class ASMCompileVisitor(ASMCompiler): 185 | def child_accept(self, parent, child): 186 | child.parent = parent 187 | child.accept(self) 188 | 189 | def visit_Program(self, node): 190 | self.emit_call("main") 191 | self.emit_halt() 192 | for c in node.items: 193 | self.child_accept(node, c) 194 | 195 | def visit_VarDecl(self, node): 196 | pass 197 | 198 | def visit_FuncDef(self, node): 199 | # prologue 200 | self.emit_label(node.ident.name) 201 | self.emit_func_init() 202 | 203 | # allocate space for locals 204 | locals_size = node.scope.stack_offset 205 | if locals_size > 0: 206 | self.emit_lcons("r1", locals_size, 2) 207 | self.emit_arithmetic("-", "sp", "sp", "r1") 208 | 209 | # TODO: args 210 | 211 | # body 212 | self.child_accept(node, node.body) 213 | 214 | # epilogue 215 | self.emit_lcons("r0", 0, 1) 216 | self.emit_func_return("r0") 217 | 218 | def visit_FuncParam(self, node): 219 | pass 220 | # self.child_accept(node, node.type) 221 | # self.add(" ") 222 | # self.child_accept(node, node.ident) 223 | 224 | def visit_FuncParams(self, node): 225 | pass 226 | # self.add("(") 227 | # for i in range(len(node.args)): 228 | # if i > 0: 229 | # self.add(", ") 230 | # self.child_accept(node, node.args[i]) 231 | # self.add(")") 232 | 233 | def visit_FuncCall(self, node): 234 | self.child_accept(node, node.args) 235 | self.emit_call(node.ident.name) 236 | self.emit_mov("r0", "t0") 237 | for _ in node.args.args: 238 | self.emit_pop("t0") 239 | 240 | def visit_FuncArgs(self, node): 241 | for a in node.args: 242 | self.child_accept(node, a) 243 | self.emit_push("r0") 244 | 245 | def visit_StatementBlock(self, node): 246 | self.child_accept(node, node.statements) 247 | 248 | def visit_Statements(self, node): 249 | for s in node.statements: 250 | self.child_accept(node, s) 251 | 252 | def visit_AssignStatement(self, node): 253 | self.child_accept(node, node.value) 254 | if isinstance(node.symbol, LocalVarSymbol): 255 | self.emit_lcons("r5", node.symbol.offset, 2) 256 | self.emit_arithmetic("-", "r5", "bp", "r5") 257 | elif isinstance(node.symbol, ArgVarSymbol): 258 | self.emit_lcons( 259 | "r5", 260 | self.arg_offset + node.symbol.func_symbol.args_size() - node.symbol.offset, 261 | 2, 262 | ) 263 | self.emit_arithmetic("+", "r5", "bp", "r5") 264 | self.emit_storp("r5", "r0", node.symbol.type_size()) 265 | 266 | def visit_BreakStatement(self, node): 267 | self.emit_jmp(self.loop_end_label) 268 | 269 | def visit_ReturnStatement(self, node): 270 | self.child_accept(node, node.expr) 271 | self.emit_func_return("r0") 272 | 273 | def visit_IfStatement(self, node): 274 | else_label = self.unique_label() 275 | end_label = self.unique_label() 276 | 277 | self.child_accept(node, node.condition) 278 | self.emit_jz(else_label, "r0") 279 | self.child_accept(node, node.true_block) 280 | if node.else_block: 281 | self.emit_jmp(end_label) 282 | self.emit_label(else_label) 283 | if node.else_block: 284 | self.child_accept(node, node.else_block) 285 | self.emit_label(end_label) 286 | 287 | def visit_WhileStatement(self, node): 288 | start_label = self.unique_label() 289 | end_label = self.unique_label() 290 | 291 | self.emit_label(start_label) 292 | self.child_accept(node, node.condition) 293 | self.emit_jz(end_label, "r0") 294 | 295 | prev_end_label = self.loop_end_label 296 | self.loop_end_label = end_label 297 | self.child_accept(node, node.body) 298 | self.loop_end_label = prev_end_label 299 | 300 | self.emit_jmp(start_label) 301 | self.emit_label(end_label) 302 | 303 | def visit_PrintStatement(self, node): 304 | self.child_accept(node, node.expr) 305 | self.emit_printi("r0") 306 | 307 | def visit_UnaryOp(self, node): 308 | self.child_accept(node, node.right) 309 | if node.op == "-": 310 | self.emit_lcons("r1", -1, 1) 311 | self.emit_arithmetic("*", "r0", "r0", "r1") 312 | elif node.op == "!": 313 | zero_label = self.unique_label() 314 | end_label = self.unique_label() 315 | 316 | self.emit_jz(zero_label, "r0") 317 | self.emit_lcons("r0", 0, 1) 318 | self.emit_jmp(end_label) 319 | self.emit_label(zero_label) 320 | self.emit_lcons("r0", 1, 1) 321 | self.emit_label(end_label) 322 | elif node.op in ["++", "--"]: 323 | if node.op == "++": 324 | self.emit_inc("r0") 325 | elif node.op == "--": 326 | self.emit_dec("r0") 327 | # address should be in r5 already, so we just save 328 | self.emit_storp("r5", "r0", node.right.symbol.type_size()) 329 | elif node.op == "~": 330 | self.addline("not r0, r0") 331 | else: 332 | raise ValueError 333 | 334 | def visit_BinaryOp(self, node): 335 | self.child_accept(node, node.left) 336 | self.emit_push("r0") 337 | self.child_accept(node, node.right) 338 | self.emit_pop("r1") 339 | self.emit_arithmetic(node.op, "r0", "r1", "r0") 340 | 341 | def visit_ComparisonOp(self, node): 342 | self.child_accept(node, node.left) 343 | self.emit_push("r0") 344 | self.child_accept(node, node.right) 345 | self.emit_pop("r1") 346 | self.emit_comparison(node.comp, "r0", "r1", "r0") 347 | 348 | def visit_LogicOp(self, node): 349 | self.child_accept(node, node.left) 350 | self.emit_push("r0") 351 | self.child_accept(node, node.right) 352 | self.emit_pop("r1") 353 | self.emit_logic(node.op, "r0", "r1", "r0") 354 | 355 | def visit_IntConst(self, node): 356 | self.emit_lcons("r0", node.value, 4) 357 | 358 | def visit_Identifier(self, node): 359 | pass 360 | 361 | def visit_IdentifierExp(self, node): 362 | if isinstance(node.symbol, LocalVarSymbol): 363 | self.emit_lcons("r5", node.symbol.offset, 2) 364 | self.emit_arithmetic("-", "r5", "bp", "r5") 365 | elif isinstance(node.symbol, ArgVarSymbol): 366 | self.emit_lcons( 367 | "r5", 368 | self.arg_offset + node.symbol.func_symbol.args_size() - node.symbol.offset, 369 | 2, 370 | ) 371 | self.emit_arithmetic("+", "r5", "bp", "r5") 372 | self.emit_loadp("r0", "r5", node.symbol.type_size()) 373 | 374 | def visit_ExpGroup(self, node): 375 | self.child_accept(node, node.expression) 376 | 377 | def visit_Type(self, node): 378 | pass 379 | -------------------------------------------------------------------------------- /test/test_memory.cpp: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | TEST_CASE("OP_STOR") 4 | { 5 | uint8_t program[] = { 6 | OP_STOR, 5, 0, R0, 7 | OP_HALT, 8 | 0, 0, 0, 0, 0xFF}; 9 | VM vm(program, sizeof(program)); 10 | uint32_t actual = 0; 11 | 12 | SECTION("Store value") 13 | { 14 | vm.setRegister(R0, _U32_GARBAGE); 15 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 16 | uint8_t *memory = vm.memory(); 17 | memcpy(&actual, &memory[5], 4); 18 | 19 | REQUIRE(memory[4] == OP_HALT); 20 | REQUIRE(actual == _U32_GARBAGE); 21 | REQUIRE(memory[9] == 0xFF); 22 | } 23 | 24 | SECTION("Store zero") 25 | { 26 | vm.reset(); 27 | vm.setRegister(R0, 0); 28 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 29 | uint8_t *memory = vm.memory(); 30 | memcpy(&actual, &memory[5], 4); 31 | 32 | REQUIRE(memory[4] == OP_HALT); 33 | REQUIRE(actual == 0); 34 | REQUIRE(memory[9] == 0xFF); 35 | } 36 | } 37 | 38 | TEST_CASE("OP_STOR_P") 39 | { 40 | uint8_t program[] = { 41 | OP_STOR_P, R1, R0, 42 | OP_HALT, 43 | 0, 0, 0, 0, 0xFF}; 44 | VM vm(program, sizeof(program)); 45 | uint32_t actual = 0; 46 | 47 | SECTION("Store value") 48 | { 49 | vm.setRegister(R1, 4); 50 | vm.setRegister(R0, _U32_GARBAGE); 51 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 52 | uint8_t *memory = vm.memory(); 53 | memcpy(&actual, &memory[4], 4); 54 | 55 | REQUIRE(memory[3] == OP_HALT); 56 | REQUIRE(actual == _U32_GARBAGE); 57 | REQUIRE(memory[8] == 0xFF); 58 | } 59 | 60 | SECTION("Store zero") 61 | { 62 | vm.reset(); 63 | vm.setRegister(R1, 4); 64 | vm.setRegister(R0, 0); 65 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 66 | uint8_t *memory = vm.memory(); 67 | memcpy(&actual, &memory[4], 4); 68 | 69 | REQUIRE(memory[3] == OP_HALT); 70 | REQUIRE(actual == 0); 71 | REQUIRE(memory[8] == 0xFF); 72 | } 73 | } 74 | 75 | TEST_CASE("OP_STORW") 76 | { 77 | uint8_t program[] = { 78 | OP_STORW, 5, 0, R0, 79 | OP_HALT, 80 | 0, 0, 0xFF}; 81 | VM vm(program, sizeof(program)); 82 | uint16_t actual = 0; 83 | 84 | SECTION("Store value") 85 | { 86 | vm.setRegister(R0, _U16_GARBAGE); 87 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 88 | uint8_t *memory = vm.memory(); 89 | memcpy(&actual, &memory[5], 2); 90 | 91 | REQUIRE(memory[4] == OP_HALT); 92 | REQUIRE(actual == _U16_GARBAGE); 93 | REQUIRE(memory[7] == 0xFF); 94 | } 95 | 96 | SECTION("Store zero") 97 | { 98 | vm.reset(); 99 | vm.setRegister(R0, 0); 100 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 101 | uint8_t *memory = vm.memory(); 102 | memcpy(&actual, &memory[5], 2); 103 | 104 | REQUIRE(memory[4] == OP_HALT); 105 | REQUIRE(actual == 0); 106 | REQUIRE(memory[7] == 0xFF); 107 | } 108 | } 109 | 110 | TEST_CASE("OP_STORW_P") 111 | { 112 | uint8_t program[] = { 113 | OP_STORW_P, R1, R0, 114 | OP_HALT, 115 | 0, 0, 0xFF}; 116 | VM vm(program, sizeof(program)); 117 | uint16_t actual = 0; 118 | 119 | SECTION("Store value") 120 | { 121 | vm.setRegister(R1, 4); 122 | vm.setRegister(R0, _U16_GARBAGE); 123 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 124 | uint8_t *memory = vm.memory(); 125 | memcpy(&actual, &memory[4], 2); 126 | 127 | REQUIRE(memory[3] == OP_HALT); 128 | REQUIRE(actual == _U16_GARBAGE); 129 | REQUIRE(memory[6] == 0xFF); 130 | } 131 | 132 | SECTION("Store zero") 133 | { 134 | vm.reset(); 135 | vm.setRegister(R1, 4); 136 | vm.setRegister(R0, 0); 137 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 138 | uint8_t *memory = vm.memory(); 139 | memcpy(&actual, &memory[4], 2); 140 | 141 | REQUIRE(memory[3] == OP_HALT); 142 | REQUIRE(actual == 0); 143 | REQUIRE(memory[6] == 0xFF); 144 | } 145 | } 146 | 147 | TEST_CASE("OP_STORB") 148 | { 149 | uint8_t program[] = { 150 | OP_STORB, 5, 0, R0, 151 | OP_HALT, 152 | 0, 0xFF}; 153 | VM vm(program, sizeof(program)); 154 | 155 | SECTION("Store value") 156 | { 157 | vm.setRegister(R0, _U8_GARBAGE); 158 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 159 | uint8_t *memory = vm.memory(); 160 | 161 | REQUIRE(memory[4] == OP_HALT); 162 | REQUIRE(memory[5] == _U8_GARBAGE); 163 | REQUIRE(memory[6] == 0xFF); 164 | } 165 | 166 | SECTION("Store zero") 167 | { 168 | vm.reset(); 169 | vm.setRegister(R0, 0); 170 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 171 | uint8_t *memory = vm.memory(); 172 | 173 | REQUIRE(memory[4] == OP_HALT); 174 | REQUIRE(memory[5] == 0); 175 | REQUIRE(memory[6] == 0xFF); 176 | } 177 | } 178 | 179 | TEST_CASE("OP_STORB_P") 180 | { 181 | uint8_t program[] = { 182 | OP_STORB_P, R1, R0, 183 | OP_HALT, 184 | 0, 0xFF}; 185 | VM vm(program, sizeof(program)); 186 | 187 | SECTION("Store value") 188 | { 189 | vm.setRegister(R1, 4); 190 | vm.setRegister(R0, _U8_GARBAGE); 191 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 192 | uint8_t *memory = vm.memory(); 193 | 194 | REQUIRE(memory[3] == OP_HALT); 195 | REQUIRE(memory[4] == _U8_GARBAGE); 196 | REQUIRE(memory[5] == 0xFF); 197 | } 198 | 199 | SECTION("Store zero") 200 | { 201 | vm.reset(); 202 | vm.setRegister(R1, 4); 203 | vm.setRegister(R0, 0); 204 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 205 | uint8_t *memory = vm.memory(); 206 | 207 | REQUIRE(memory[3] == OP_HALT); 208 | REQUIRE(memory[4] == 0); 209 | REQUIRE(memory[5] == 0xFF); 210 | } 211 | } 212 | 213 | TEST_CASE("OP_LOAD") 214 | { 215 | uint8_t program[] = { 216 | OP_LOAD, R0, 5, 0, 217 | OP_HALT, 218 | _NTH_BYTE(_U32_GARBAGE, 0), _NTH_BYTE(_U32_GARBAGE, 1), 219 | _NTH_BYTE(_U32_GARBAGE, 2), _NTH_BYTE(_U32_GARBAGE, 3), 220 | 0xFF}; 221 | VM vm(program, sizeof(program)); 222 | uint32_t actual = 0; 223 | 224 | SECTION("Load value") 225 | { 226 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 227 | uint8_t *memory = vm.memory(); 228 | memcpy(&actual, &memory[5], 4); 229 | 230 | REQUIRE(memory[4] == OP_HALT); 231 | REQUIRE(actual == _U32_GARBAGE); 232 | REQUIRE(vm.getRegister(R0) == _U32_GARBAGE); 233 | REQUIRE(memory[9] == 0xFF); 234 | } 235 | 236 | SECTION("Load zero") 237 | { 238 | vm.reset(); 239 | uint8_t *memory = vm.memory(); 240 | 241 | memset(&memory[5], 0, 4); 242 | vm.setRegister(R0, _U32_GARBAGE); 243 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 244 | memcpy(&actual, &memory[5], 4); 245 | 246 | REQUIRE(memory[4] == OP_HALT); 247 | REQUIRE(actual == 0); 248 | REQUIRE(vm.getRegister(R0) == 0); 249 | REQUIRE(memory[9] == 0xFF); 250 | } 251 | } 252 | 253 | TEST_CASE("OP_LOAD_P") 254 | { 255 | uint8_t program[] = { 256 | OP_LOAD_P, R0, R1, 257 | OP_HALT, 258 | _NTH_BYTE(_U32_GARBAGE, 0), _NTH_BYTE(_U32_GARBAGE, 1), 259 | _NTH_BYTE(_U32_GARBAGE, 2), _NTH_BYTE(_U32_GARBAGE, 3), 260 | 0xFF}; 261 | VM vm(program, sizeof(program)); 262 | uint32_t actual = 0; 263 | 264 | SECTION("Load value") 265 | { 266 | vm.setRegister(R1, 4); 267 | vm.setRegister(R0, 0); 268 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 269 | uint8_t *memory = vm.memory(); 270 | memcpy(&actual, &memory[4], 4); 271 | 272 | REQUIRE(memory[3] == OP_HALT); 273 | REQUIRE(actual == _U32_GARBAGE); 274 | REQUIRE(vm.getRegister(R0) == _U32_GARBAGE); 275 | REQUIRE(memory[8] == 0xFF); 276 | } 277 | 278 | SECTION("Load zero") 279 | { 280 | vm.reset(); 281 | vm.setRegister(R1, 4); 282 | vm.setRegister(R0, _U32_GARBAGE); 283 | 284 | uint8_t *memory = vm.memory(); 285 | memset(&memory[4], 0, 4); 286 | 287 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 288 | memcpy(&actual, &memory[4], 4); 289 | 290 | REQUIRE(memory[3] == OP_HALT); 291 | REQUIRE(actual == 0); 292 | REQUIRE(vm.getRegister(R0) == 0); 293 | REQUIRE(memory[8] == 0xFF); 294 | } 295 | } 296 | 297 | TEST_CASE("OP_LOADW") 298 | { 299 | uint8_t program[] = { 300 | OP_LOADW, R0, 5, 0, 301 | OP_HALT, 302 | _NTH_BYTE(_U16_GARBAGE, 0), _NTH_BYTE(_U16_GARBAGE, 1), 303 | 0xFF}; 304 | VM vm(program, sizeof(program)); 305 | uint16_t actual = 0; 306 | 307 | SECTION("Load value") 308 | { 309 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 310 | uint8_t *memory = vm.memory(); 311 | memcpy(&actual, &memory[5], 2); 312 | 313 | REQUIRE(memory[4] == OP_HALT); 314 | REQUIRE(actual == _U16_GARBAGE); 315 | REQUIRE(vm.getRegister(R0) == _U16_GARBAGE); 316 | REQUIRE(memory[7] == 0xFF); 317 | } 318 | 319 | SECTION("Load zero") 320 | { 321 | vm.reset(); 322 | uint8_t *memory = vm.memory(); 323 | 324 | memset(&memory[5], 0, 2); 325 | vm.setRegister(R0, _U32_GARBAGE); 326 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 327 | memcpy(&actual, &memory[5], 2); 328 | 329 | REQUIRE(memory[4] == OP_HALT); 330 | REQUIRE(actual == 0); 331 | REQUIRE(vm.getRegister(R0) == 0); 332 | REQUIRE(memory[7] == 0xFF); 333 | } 334 | } 335 | 336 | TEST_CASE("OP_LOADW_P") 337 | { 338 | uint8_t program[] = { 339 | OP_LOADW_P, R0, R1, 340 | OP_HALT, 341 | _NTH_BYTE(_U16_GARBAGE, 0), _NTH_BYTE(_U16_GARBAGE, 1), 342 | 0xFF}; 343 | VM vm(program, sizeof(program)); 344 | uint16_t actual = 0; 345 | 346 | SECTION("Load value") 347 | { 348 | vm.setRegister(R1, 4); 349 | vm.setRegister(R0, 0); 350 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 351 | uint8_t *memory = vm.memory(); 352 | memcpy(&actual, &memory[4], 2); 353 | 354 | REQUIRE(memory[3] == OP_HALT); 355 | REQUIRE(actual == _U16_GARBAGE); 356 | REQUIRE(vm.getRegister(R0) == _U16_GARBAGE); 357 | REQUIRE(memory[6] == 0xFF); 358 | } 359 | 360 | SECTION("Load zero") 361 | { 362 | vm.reset(); 363 | vm.setRegister(R1, 4); 364 | vm.setRegister(R0, _U32_GARBAGE); 365 | 366 | uint8_t *memory = vm.memory(); 367 | memset(&memory[4], 0, 2); 368 | 369 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 370 | memcpy(&actual, &memory[4], 2); 371 | 372 | REQUIRE(memory[3] == OP_HALT); 373 | REQUIRE(actual == 0); 374 | REQUIRE(vm.getRegister(R0) == 0); 375 | REQUIRE(memory[6] == 0xFF); 376 | } 377 | } 378 | 379 | TEST_CASE("OP_LOADB") 380 | { 381 | uint8_t program[] = { 382 | OP_LOADB, R0, 5, 0, 383 | OP_HALT, 384 | _U8_GARBAGE, 385 | 0xFF}; 386 | VM vm(program, sizeof(program)); 387 | 388 | SECTION("Load value") 389 | { 390 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 391 | uint8_t *memory = vm.memory(); 392 | 393 | REQUIRE(memory[4] == OP_HALT); 394 | REQUIRE(memory[5] == _U8_GARBAGE); 395 | REQUIRE(vm.getRegister(R0) == _U8_GARBAGE); 396 | REQUIRE(memory[6] == 0xFF); 397 | } 398 | 399 | SECTION("Load zero") 400 | { 401 | vm.reset(); 402 | uint8_t *memory = vm.memory(); 403 | 404 | vm.setRegister(R0, _U32_GARBAGE); 405 | memory[5] = 0; 406 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 407 | 408 | REQUIRE(memory[4] == OP_HALT); 409 | REQUIRE(memory[5] == 0); 410 | REQUIRE(vm.getRegister(R0) == 0); 411 | REQUIRE(memory[6] == 0xFF); 412 | } 413 | } 414 | 415 | TEST_CASE("OP_LOADB_P") 416 | { 417 | uint8_t program[] = { 418 | OP_LOADB_P, R0, R1, 419 | OP_HALT, 420 | _U8_GARBAGE, 421 | 0xFF}; 422 | VM vm(program, sizeof(program)); 423 | 424 | SECTION("Load value") 425 | { 426 | vm.setRegister(R1, 4); 427 | vm.setRegister(R0, 0); 428 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 429 | uint8_t *memory = vm.memory(); 430 | 431 | REQUIRE(memory[3] == OP_HALT); 432 | REQUIRE(memory[4] == _U8_GARBAGE); 433 | REQUIRE(vm.getRegister(R0) == _U8_GARBAGE); 434 | REQUIRE(memory[5] == 0xFF); 435 | } 436 | 437 | SECTION("Load zero") 438 | { 439 | vm.reset(); 440 | vm.setRegister(R1, 4); 441 | vm.setRegister(R0, _U32_GARBAGE); 442 | 443 | uint8_t *memory = vm.memory(); 444 | memory[4] = 0; 445 | 446 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 447 | 448 | REQUIRE(memory[3] == OP_HALT); 449 | REQUIRE(memory[4] == 0); 450 | REQUIRE(vm.getRegister(R0) == 0); 451 | REQUIRE(memory[5] == 0xFF); 452 | } 453 | } 454 | 455 | TEST_CASE("OP_MEMCPY") 456 | { 457 | uint8_t program[] = { 458 | OP_MEMCPY, 8, 0, 12, 0, 4, 0, // copy 4 bytes from 0xC to 0x8 459 | OP_HALT, 460 | 0, 0, 0, 0, 461 | _NTH_BYTE(_U32_GARBAGE, 0), _NTH_BYTE(_U32_GARBAGE, 1), 462 | _NTH_BYTE(_U32_GARBAGE, 2), _NTH_BYTE(_U32_GARBAGE, 3), 463 | 0xFF}; 464 | VM vm(program, sizeof(program)); 465 | uint32_t actual = 0; 466 | uint32_t source = 0; 467 | 468 | SECTION("Copy 4 bytes") 469 | { 470 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 471 | uint8_t *memory = vm.memory(); 472 | memcpy(&actual, &memory[8], 4); 473 | memcpy(&source, &memory[12], 4); 474 | 475 | REQUIRE(memory[7] == OP_HALT); 476 | REQUIRE(actual == _U32_GARBAGE); 477 | REQUIRE(source == _U32_GARBAGE); 478 | REQUIRE(memory[16] == 0xFF); 479 | } 480 | } 481 | 482 | TEST_CASE("OP_MEMCPY_P") 483 | { 484 | uint8_t program[] = { 485 | OP_MEMCPY_P, R0, R1, R2, 486 | OP_HALT, 487 | 0, 0, 0, 0, 488 | _NTH_BYTE(_U32_GARBAGE, 0), _NTH_BYTE(_U32_GARBAGE, 1), 489 | _NTH_BYTE(_U32_GARBAGE, 2), _NTH_BYTE(_U32_GARBAGE, 3), 490 | 0xFF}; 491 | VM vm(program, sizeof(program)); 492 | uint32_t actual = 0; 493 | uint32_t source = 0; 494 | 495 | SECTION("Copy 4 bytes") 496 | { 497 | vm.setRegister(R0, 5); 498 | vm.setRegister(R1, 9); 499 | vm.setRegister(R2, 4); 500 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 501 | 502 | uint8_t *memory = vm.memory(); 503 | memcpy(&actual, &memory[5], 4); 504 | memcpy(&source, &memory[9], 4); 505 | 506 | REQUIRE(memory[4] == OP_HALT); 507 | REQUIRE(actual == _U32_GARBAGE); 508 | REQUIRE(source == _U32_GARBAGE); 509 | REQUIRE(memory[13] == 0xFF); 510 | } 511 | } 512 | -------------------------------------------------------------------------------- /assembler/internals.py: -------------------------------------------------------------------------------- 1 | import re 2 | from data import REGISTERS, Opcodes 3 | 4 | re_data = re.compile(r"^\$(?P[\w]+)\s+(?Pbyte|word|dword)(?P\[\d*\])?\s+(?P.*)$") 5 | type_sizes = {"byte": 1, "word": 2, "dword": 4} 6 | 7 | # includes actual labels AND data 8 | labels = {} 9 | label_instances = {} 10 | 11 | def process_file(path, align_data=4): 12 | bytecode = bytearray() 13 | f = open(path, "r", encoding="utf-8") 14 | 15 | line = f.readline() 16 | line_count = 0 17 | 18 | while line != "": 19 | line_count += 1 20 | line = line.lstrip().rstrip() 21 | 22 | if len(line) == 0 or line.startswith(";"): # comment or empty 23 | pass 24 | elif line.startswith("$"): # data 25 | handle_data(bytecode, line, align_data) 26 | elif line.startswith(".") and line.endswith(":") and len(line) > 2: # labels 27 | label = line[1:-1] 28 | if label in labels: 29 | raise ValueError("Label {} is already defined".format(label)) 30 | labels[label] = len(bytecode) 31 | else: # regular opcode 32 | line = line.partition(";")[0].rstrip() # ignore comments 33 | process_instruction(bytecode, line) 34 | 35 | line = f.readline() 36 | 37 | f.close() 38 | replace_label_instances(bytecode) 39 | 40 | return bytecode 41 | 42 | 43 | def handle_data(bytecode, line, align): 44 | match = re_data.match(line) 45 | if not match: 46 | raise ValueError("Invalid data specification") 47 | 48 | # first, align our bytecode 49 | while len(bytecode) % align != 0: 50 | bytecode.append(Opcodes.HALT) 51 | 52 | name, dtype, array, val = match.groups() 53 | 54 | if name in labels: 55 | raise ValueError("{} is already defined as a label".format(name)) 56 | labels[name] = len(bytecode) 57 | 58 | if not array or array == "[1]": 59 | if val.startswith('"'): 60 | val = ord(val[1]) 61 | else: 62 | val = int(val) 63 | 64 | bytecode.extend(int_to_bytes(val, type_sizes[dtype])) 65 | elif array == "[]": 66 | is_str = False 67 | for v in val.split(","): 68 | v = v.lstrip().rstrip() 69 | 70 | if dtype == "byte": 71 | if len(v) == 3 and v.startswith('"') and v.endswith('"'): 72 | bytecode.append(ord(v[1])) 73 | elif v.startswith('"') and v.endswith('"'): 74 | is_str = True 75 | for c in v[1:-1]: 76 | bytecode.append(ord(c)) 77 | else: 78 | bytecode.append(str_to_int(v, bytecode, accept_labels=False)) 79 | else: 80 | v = str_to_int(v, bytecode, accept_labels=False) 81 | bytecode.extend(int_to_bytes(v, type_sizes[dtype])) 82 | 83 | if is_str: 84 | bytecode.append(0) 85 | 86 | 87 | def print_bytecode(bytecode): 88 | print(", ".join("0x{:02X}".format(b) for b in bytecode)) 89 | 90 | 91 | def write_bytecode(bytecode, path): 92 | f = open(path, "wb") 93 | f.write(bytecode) 94 | f.close() 95 | 96 | 97 | def str_to_int(s, bytecode, bytes_ahead=0, accept_labels=True): 98 | if len(s) == 3 and s.startswith("'") and s.endswith("'"): 99 | return ord(s[1]) 100 | elif accept_labels and (s.startswith(".") or s.startswith("$")): 101 | label = s[1:] 102 | location = len(bytecode) + bytes_ahead 103 | if label in label_instances: 104 | label_instances[label].append(location) 105 | else: 106 | label_instances[label] = [location] 107 | return 0 # to be replaced later 108 | elif s.startswith("0x"): 109 | return int(s, 16) 110 | else: 111 | return int(s, 10) 112 | 113 | 114 | def int_to_bytes(val, nbytes): 115 | if val < 0: 116 | return val.to_bytes(nbytes, "little", signed=True) 117 | else: 118 | return val.to_bytes(nbytes, "little", signed=False) 119 | 120 | 121 | def register_from_name(s): 122 | s = s.lower() 123 | if s not in REGISTERS: 124 | raise ValueError("Invalid register '{}'".format(s)) 125 | return REGISTERS[s] 126 | 127 | 128 | def replace_label_instances(bytecode): 129 | for l, instances in label_instances.items(): 130 | if l not in labels: 131 | raise ValueError("Invalid label {}".format(l)) 132 | 133 | label_val = int_to_bytes(labels[l], 2) 134 | for instance in instances: 135 | bytecode[instance] = label_val[0] 136 | bytecode[instance + 1] = label_val[1] 137 | 138 | 139 | def singleop(bytecode, params, opcode): 140 | if params: 141 | raise ValueError( 142 | "Operation '{}' expects 0 arguments, got {}".format(opcode, len(params)) 143 | ) 144 | bytecode.append(opcode) 145 | 146 | 147 | def unop(bytecode, params, opcode): 148 | if len(params) != 1: 149 | raise ValueError( 150 | "Operation '{}' expects 1 argument, got {}".format(opcode, len(params)) 151 | ) 152 | reg = register_from_name(params[0]) 153 | bytecode.append(opcode) 154 | bytecode.append(reg) 155 | 156 | 157 | def unop_c(bytecode, params, opcode, nbytes): 158 | if len(params) != 1: 159 | raise ValueError( 160 | "Operation '{}' expects 1 argument, got {}".format(opcode, len(params)) 161 | ) 162 | val = str_to_int(params[0], bytecode, 1) 163 | bytecode.append(opcode) 164 | bytecode.extend(int_to_bytes(val, nbytes)) 165 | 166 | 167 | def binop(bytecode, params, opcode): 168 | if len(params) != 2: 169 | raise ValueError( 170 | "Operation '{}' expects 2 arguments, got {}".format(opcode, len(params)) 171 | ) 172 | reg1 = register_from_name(params[0]) 173 | reg2 = register_from_name(params[1]) 174 | bytecode.append(opcode) 175 | bytecode.append(reg1) 176 | bytecode.append(reg2) 177 | 178 | 179 | def binop_rc(bytecode, params, opcode, nbytes): 180 | if len(params) != 2: 181 | raise ValueError( 182 | "Operation '{}' expects 2 arguments, got {}".format(opcode, len(params)) 183 | ) 184 | reg = register_from_name(params[0]) 185 | val = str_to_int(params[1], bytecode, 2) 186 | bytecode.append(opcode) 187 | bytecode.append(reg) 188 | bytecode.extend(int_to_bytes(val, nbytes)) 189 | 190 | 191 | def binop_cr(bytecode, params, opcode, nbytes): 192 | if len(params) != 2: 193 | raise ValueError( 194 | "Operation '{}' expects 2 arguments, got {}".format(opcode, len(params)) 195 | ) 196 | val = str_to_int(params[0], bytecode, 1) 197 | reg = register_from_name(params[1]) 198 | bytecode.append(opcode) 199 | bytecode.extend(int_to_bytes(val, nbytes)) 200 | bytecode.append(reg) 201 | 202 | 203 | def binop_cc(bytecode, params, opcode, nbytes1, nbytes2): 204 | if len(params) != 2: 205 | raise ValueError( 206 | "Operation '{}' expects 2 arguments, got {}".format(opcode, len(params)) 207 | ) 208 | val1 = str_to_int(params[0], bytecode, 1) 209 | val2 = str_to_int(params[1], bytecode, 1 + nbytes1) 210 | bytecode.append(opcode) 211 | bytecode.extend(int_to_bytes(val1, nbytes1)) 212 | bytecode.extend(int_to_bytes(val2, nbytes2)) 213 | 214 | 215 | def ternop(bytecode, params, opcode): 216 | if len(params) != 3: 217 | raise ValueError( 218 | "Operation '{}' expects 3 arguments, got {}".format(opcode, len(params)) 219 | ) 220 | reg1 = register_from_name(params[0]) 221 | reg2 = register_from_name(params[1]) 222 | reg3 = register_from_name(params[2]) 223 | bytecode.append(opcode) 224 | bytecode.append(reg1) 225 | bytecode.append(reg2) 226 | bytecode.append(reg3) 227 | 228 | 229 | def ternop_ccc(bytecode, params, opcode, nbytes1, nbytes2, nbytes3): 230 | if len(params) != 3: 231 | raise ValueError( 232 | "Operation '{}' expects 3 arguments, got {}".format(opcode, len(params)) 233 | ) 234 | val1 = str_to_int(params[0], bytecode, 1) 235 | val2 = str_to_int(params[1], bytecode, 1 + nbytes1) 236 | val3 = str_to_int(params[2], bytecode, 1 + nbytes1 + nbytes2) 237 | bytecode.append(opcode) 238 | bytecode.extend(int_to_bytes(val1, nbytes1)) 239 | bytecode.extend(int_to_bytes(val2, nbytes2)) 240 | bytecode.extend(int_to_bytes(val3, nbytes3)) 241 | 242 | 243 | def ternop_rrc(bytecode, params, opcode, nbytes): 244 | if len(params) != 3: 245 | raise ValueError( 246 | "Operation '{}' expects 3 arguments, got {}".format(opcode, len(params)) 247 | ) 248 | reg1 = register_from_name(params[0]) 249 | reg2 = register_from_name(params[1]) 250 | val = str_to_int(params[2], bytecode, 3) 251 | bytecode.append(opcode) 252 | bytecode.append(reg1) 253 | bytecode.append(reg2) 254 | bytecode.extend(int_to_bytes(val, nbytes)) 255 | 256 | 257 | def process_instruction(bytecode, line): 258 | opcode, sep, params = line.partition(" ") 259 | opcode = opcode.lower() 260 | 261 | if sep == " ": 262 | params = [p.lstrip().rstrip() for p in params.split(",")] 263 | else: 264 | params = [] 265 | 266 | if opcode == "nop": 267 | singleop(bytecode, params, Opcodes.NOP) 268 | elif opcode == "lcons": 269 | binop_rc(bytecode, params, Opcodes.LCONS, 4) 270 | elif opcode == "lconsw": 271 | binop_rc(bytecode, params, Opcodes.LCONSW, 2) 272 | elif opcode == "lconsb": 273 | binop_rc(bytecode, params, Opcodes.LCONSB, 1) 274 | elif opcode == "mov": 275 | binop(bytecode, params, Opcodes.MOV) 276 | elif opcode == "push": 277 | unop(bytecode, params, Opcodes.PUSH) 278 | elif opcode == "pop": 279 | unop(bytecode, params, Opcodes.POP) 280 | elif opcode == "pop2": 281 | binop(bytecode, params, Opcodes.POP2) 282 | elif opcode == "dup": 283 | singleop(bytecode, params, Opcodes.DUP) 284 | elif opcode == "call": 285 | unop_c(bytecode, params, Opcodes.CALL, 2) 286 | elif opcode == "ret": 287 | singleop(bytecode, params, Opcodes.RET) 288 | elif opcode == "stor": 289 | binop_cr(bytecode, params, Opcodes.STOR, 2) 290 | elif opcode == "stor_p": 291 | binop(bytecode, params, Opcodes.STOR_P) 292 | elif opcode == "storw": 293 | binop_cr(bytecode, params, Opcodes.STORW, 2) 294 | elif opcode == "storw_p": 295 | binop(bytecode, params, Opcodes.STORW_P) 296 | elif opcode == "storb": 297 | binop_cr(bytecode, params, Opcodes.STORB, 2) 298 | elif opcode == "storb_p": 299 | binop(bytecode, params, Opcodes.STORB_P) 300 | elif opcode == "load": 301 | binop_rc(bytecode, params, Opcodes.LOAD, 2) 302 | elif opcode == "load_p": 303 | binop(bytecode, params, Opcodes.LOAD_P) 304 | elif opcode == "loadw": 305 | binop_rc(bytecode, params, Opcodes.LOADW, 2) 306 | elif opcode == "loadw_p": 307 | binop(bytecode, params, Opcodes.LOADW_P) 308 | elif opcode == "loadb": 309 | binop_rc(bytecode, params, Opcodes.LOADB, 2) 310 | elif opcode == "loadb_p": 311 | binop(bytecode, params, Opcodes.LOADB_P) 312 | elif opcode == "memcpy": 313 | ternop_ccc(bytecode, params, Opcodes.MEMCPY, 2, 2, 2) 314 | elif opcode == "memcpy_p": 315 | ternop(bytecode, params, Opcodes.MEMCPY_P) 316 | elif opcode == "inc": 317 | unop(bytecode, params, Opcodes.INC) 318 | elif opcode == "finc": 319 | unop(bytecode, params, Opcodes.FINC) 320 | elif opcode == "dec": 321 | unop(bytecode, params, Opcodes.DEC) 322 | elif opcode == "fdec": 323 | unop(bytecode, params, Opcodes.FDEC) 324 | elif opcode == "add": 325 | ternop(bytecode, params, Opcodes.ADD) 326 | elif opcode == "fadd": 327 | ternop(bytecode, params, Opcodes.FADD) 328 | elif opcode == "sub": 329 | ternop(bytecode, params, Opcodes.SUB) 330 | elif opcode == "fsub": 331 | ternop(bytecode, params, Opcodes.FSUB) 332 | elif opcode == "mul": 333 | ternop(bytecode, params, Opcodes.MUL) 334 | elif opcode == "imul": 335 | ternop(bytecode, params, Opcodes.IMUL) 336 | elif opcode == "fmul": 337 | ternop(bytecode, params, Opcodes.FMUL) 338 | elif opcode == "div": 339 | ternop(bytecode, params, Opcodes.DIV) 340 | elif opcode == "idiv": 341 | ternop(bytecode, params, Opcodes.IDIV) 342 | elif opcode == "fdiv": 343 | ternop(bytecode, params, Opcodes.FDIV) 344 | elif opcode == "shl": 345 | ternop(bytecode, params, Opcodes.SHL) 346 | elif opcode == "shr": 347 | ternop(bytecode, params, Opcodes.SHR) 348 | elif opcode == "ishr": 349 | ternop(bytecode, params, Opcodes.ISHR) 350 | elif opcode == "mod": 351 | ternop(bytecode, params, Opcodes.MOD) 352 | elif opcode == "imod": 353 | ternop(bytecode, params, Opcodes.IMOD) 354 | elif opcode == "and": 355 | ternop(bytecode, params, Opcodes.AND) 356 | elif opcode == "or": 357 | ternop(bytecode, params, Opcodes.OR) 358 | elif opcode == "xor": 359 | ternop(bytecode, params, Opcodes.XOR) 360 | elif opcode == "not": 361 | binop(bytecode, params, Opcodes.NOT) 362 | elif opcode == "u2i": 363 | unop(bytecode, params, Opcodes.U2I) 364 | elif opcode == "i2u": 365 | unop(bytecode, params, Opcodes.I2U) 366 | elif opcode == "i2f": 367 | binop(bytecode, params, Opcodes.I2F) 368 | elif opcode == "f2i": 369 | binop(bytecode, params, Opcodes.F2I) 370 | elif opcode == "jmp": 371 | unop_c(bytecode, params, Opcodes.JMP, 2) 372 | elif opcode == "jr": 373 | unop(bytecode, params, Opcodes.JR) 374 | elif opcode == "jz": 375 | binop_rc(bytecode, params, Opcodes.JZ, 2) 376 | elif opcode == "jnz": 377 | binop_rc(bytecode, params, Opcodes.JNZ, 2) 378 | elif opcode == "je": 379 | ternop_rrc(bytecode, params, Opcodes.JE, 2) 380 | elif opcode == "jne": 381 | ternop_rrc(bytecode, params, Opcodes.JNE, 2) 382 | elif opcode == "ja": 383 | ternop_rrc(bytecode, params, Opcodes.JA, 2) 384 | elif opcode == "jg": 385 | ternop_rrc(bytecode, params, Opcodes.JG, 2) 386 | elif opcode == "jae": 387 | ternop_rrc(bytecode, params, Opcodes.JAE, 2) 388 | elif opcode == "jge": 389 | ternop_rrc(bytecode, params, Opcodes.JGE, 2) 390 | elif opcode == "jb": 391 | ternop_rrc(bytecode, params, Opcodes.JB, 2) 392 | elif opcode == "jl": 393 | ternop_rrc(bytecode, params, Opcodes.JL, 2) 394 | elif opcode == "jbe": 395 | ternop_rrc(bytecode, params, Opcodes.JBE, 2) 396 | elif opcode == "jle": 397 | ternop_rrc(bytecode, params, Opcodes.JLE, 2) 398 | elif opcode == "print": 399 | binop_rc(bytecode, params, Opcodes.PRINT, 1) 400 | elif opcode == "printi": 401 | binop_rc(bytecode, params, Opcodes.PRINTI, 1) 402 | elif opcode == "printf": 403 | binop_rc(bytecode, params, Opcodes.PRINTF, 1) 404 | elif opcode == "printc": 405 | unop(bytecode, params, Opcodes.PRINTC) 406 | elif opcode == "prints": 407 | unop_c(bytecode, params, Opcodes.PRINTS, 2) 408 | elif opcode == "println": 409 | singleop(bytecode, params, Opcodes.PRINTLN) 410 | elif opcode == "read": 411 | unop(bytecode, params, Opcodes.READ) 412 | elif opcode == "readi": 413 | unop(bytecode, params, Opcodes.READI) 414 | elif opcode == "readf": 415 | unop(bytecode, params, Opcodes.READF) 416 | elif opcode == "readc": 417 | unop(bytecode, params, Opcodes.READC) 418 | elif opcode == "reads": 419 | binop_cc(bytecode, params, Opcodes.READS, 2, 2) 420 | elif opcode == "halt": 421 | singleop(bytecode, params, Opcodes.HALT) 422 | elif opcode == "int": 423 | unop_c(bytecode, params, Opcodes.INT, 1) 424 | else: 425 | raise ValueError("Unknown opcode") 426 | 427 | return bytecode 428 | -------------------------------------------------------------------------------- /test/test_branching.cpp: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | TEST_CASE("OP_JMP") 4 | { 5 | SECTION("Jump and set 1") 6 | { 7 | uint8_t program[] = { 8 | OP_JMP, 4, 0, 9 | OP_HALT, 10 | OP_LCONSB, R0, 1, 11 | OP_HALT}; 12 | VM vm(program, sizeof(program)); 13 | REQUIRE(vm.getRegister(R0) == 0); 14 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 15 | REQUIRE(vm.getRegister(R0) == 1); 16 | } 17 | } 18 | 19 | TEST_CASE("OP_JR") 20 | { 21 | SECTION("Jump and set 1") 22 | { 23 | uint8_t program[] = { 24 | OP_JR, R1, 25 | OP_HALT, 26 | OP_LCONSB, R0, 1, 27 | OP_HALT}; 28 | VM vm(program, sizeof(program)); 29 | vm.setRegister(R1, 3); 30 | REQUIRE(vm.getRegister(R0) == 0); 31 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 32 | REQUIRE(vm.getRegister(R0) == 1); 33 | } 34 | } 35 | 36 | TEST_CASE("OP_JZ") 37 | { 38 | uint8_t program[] = { 39 | OP_JZ, R1, 5, 0, 40 | OP_HALT, 41 | OP_LCONSB, R0, 1, 42 | OP_HALT}; 43 | VM vm(program, sizeof(program)); 44 | 45 | SECTION("True") 46 | { 47 | REQUIRE(vm.getRegister(R1) == 0); 48 | REQUIRE(vm.getRegister(R0) == 0); 49 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 50 | REQUIRE(vm.getRegister(R0) == 1); 51 | } 52 | 53 | SECTION("False") 54 | { 55 | vm.reset(); 56 | vm.setRegister(R1, 123); 57 | REQUIRE(vm.getRegister(R0) == 0); 58 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 59 | REQUIRE(vm.getRegister(R0) == 0); 60 | } 61 | } 62 | 63 | TEST_CASE("OP_JNZ") 64 | { 65 | uint8_t program[] = { 66 | OP_JNZ, R1, 5, 0, 67 | OP_HALT, 68 | OP_LCONSB, R0, 1, 69 | OP_HALT}; 70 | VM vm(program, sizeof(program)); 71 | 72 | SECTION("True") 73 | { 74 | vm.setRegister(R1, 123); 75 | REQUIRE(vm.getRegister(R0) == 0); 76 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 77 | REQUIRE(vm.getRegister(R0) == 1); 78 | } 79 | 80 | SECTION("False") 81 | { 82 | vm.reset(); 83 | REQUIRE(vm.getRegister(R1) == 0); 84 | REQUIRE(vm.getRegister(R0) == 0); 85 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 86 | REQUIRE(vm.getRegister(R0) == 0); 87 | } 88 | } 89 | 90 | TEST_CASE("OP_JE") 91 | { 92 | uint8_t program[] = { 93 | OP_JE, R1, R2, 6, 0, 94 | OP_HALT, 95 | OP_LCONSB, R0, 1, 96 | OP_HALT}; 97 | VM vm(program, sizeof(program)); 98 | 99 | SECTION("True") 100 | { 101 | vm.setRegister(R1, 123); 102 | vm.setRegister(R2, 123); 103 | REQUIRE(vm.getRegister(R0) == 0); 104 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 105 | REQUIRE(vm.getRegister(R0) == 1); 106 | } 107 | 108 | SECTION("False") 109 | { 110 | vm.reset(); 111 | vm.setRegister(R2, 123); 112 | REQUIRE(vm.getRegister(R1) == 0); 113 | REQUIRE(vm.getRegister(R0) == 0); 114 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 115 | REQUIRE(vm.getRegister(R0) == 0); 116 | } 117 | } 118 | 119 | TEST_CASE("OP_JNE") 120 | { 121 | uint8_t program[] = { 122 | OP_JNE, R1, R2, 6, 0, 123 | OP_HALT, 124 | OP_LCONSB, R0, 1, 125 | OP_HALT}; 126 | VM vm(program, sizeof(program)); 127 | 128 | SECTION("False") 129 | { 130 | vm.setRegister(R1, 123); 131 | vm.setRegister(R2, 123); 132 | REQUIRE(vm.getRegister(R0) == 0); 133 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 134 | REQUIRE(vm.getRegister(R0) == 0); 135 | } 136 | 137 | SECTION("True") 138 | { 139 | vm.reset(); 140 | vm.setRegister(R2, 123); 141 | REQUIRE(vm.getRegister(R1) == 0); 142 | REQUIRE(vm.getRegister(R0) == 0); 143 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 144 | REQUIRE(vm.getRegister(R0) == 1); 145 | } 146 | } 147 | 148 | TEST_CASE("OP_JA") 149 | { 150 | uint8_t program[] = { 151 | OP_JA, R1, R2, 6, 0, 152 | OP_HALT, 153 | OP_LCONSB, R0, 1, 154 | OP_HALT}; 155 | VM vm(program, sizeof(program)); 156 | 157 | SECTION("True") 158 | { 159 | vm.setRegister(R1, 123); 160 | vm.setRegister(R2, 122); 161 | REQUIRE(vm.getRegister(R0) == 0); 162 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 163 | REQUIRE(vm.getRegister(R0) == 1); 164 | } 165 | 166 | SECTION("False - equal") 167 | { 168 | vm.reset(); 169 | vm.setRegister(R1, 123); 170 | vm.setRegister(R2, 123); 171 | REQUIRE(vm.getRegister(R0) == 0); 172 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 173 | REQUIRE(vm.getRegister(R0) == 0); 174 | } 175 | 176 | SECTION("False - below") 177 | { 178 | vm.reset(); 179 | vm.setRegister(R1, 122); 180 | vm.setRegister(R2, 123); 181 | REQUIRE(vm.getRegister(R0) == 0); 182 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 183 | REQUIRE(vm.getRegister(R0) == 0); 184 | } 185 | } 186 | 187 | TEST_CASE("OP_JG") 188 | { 189 | uint8_t program[] = { 190 | OP_JG, R1, R2, 6, 0, 191 | OP_HALT, 192 | OP_LCONSB, R0, 1, 193 | OP_HALT}; 194 | VM vm(program, sizeof(program)); 195 | 196 | SECTION("True - negative") 197 | { 198 | int32_t val1 = -122; 199 | int32_t val2 = -123; 200 | vm.setRegister(R1, *((uint32_t *)&val1)); 201 | vm.setRegister(R2, *((uint32_t *)&val2)); 202 | REQUIRE(vm.getRegister(R0) == 0); 203 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 204 | REQUIRE(vm.getRegister(R0) == 1); 205 | } 206 | 207 | SECTION("True - neg/pos") 208 | { 209 | vm.reset(); 210 | int32_t val1 = 1; 211 | int32_t val2 = -122; 212 | vm.setRegister(R1, *((uint32_t *)&val1)); 213 | vm.setRegister(R2, *((uint32_t *)&val2)); 214 | REQUIRE(vm.getRegister(R0) == 0); 215 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 216 | REQUIRE(vm.getRegister(R0) == 1); 217 | } 218 | 219 | SECTION("False - equal") 220 | { 221 | vm.reset(); 222 | int32_t val1 = -123; 223 | int32_t val2 = -123; 224 | vm.setRegister(R1, *((uint32_t *)&val1)); 225 | vm.setRegister(R2, *((uint32_t *)&val2)); 226 | REQUIRE(vm.getRegister(R0) == 0); 227 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 228 | REQUIRE(vm.getRegister(R0) == 0); 229 | } 230 | 231 | SECTION("False - below negative") 232 | { 233 | vm.reset(); 234 | int32_t val1 = -124; 235 | int32_t val2 = -123; 236 | vm.setRegister(R1, *((uint32_t *)&val1)); 237 | vm.setRegister(R2, *((uint32_t *)&val2)); 238 | REQUIRE(vm.getRegister(R0) == 0); 239 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 240 | REQUIRE(vm.getRegister(R0) == 0); 241 | } 242 | 243 | SECTION("False - below neg/pos") 244 | { 245 | vm.reset(); 246 | int32_t val1 = -124; 247 | int32_t val2 = 1; 248 | vm.setRegister(R1, *((uint32_t *)&val1)); 249 | vm.setRegister(R2, *((uint32_t *)&val2)); 250 | REQUIRE(vm.getRegister(R0) == 0); 251 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 252 | REQUIRE(vm.getRegister(R0) == 0); 253 | } 254 | } 255 | 256 | TEST_CASE("OP_JAE") 257 | { 258 | uint8_t program[] = { 259 | OP_JAE, R1, R2, 6, 0, 260 | OP_HALT, 261 | OP_LCONSB, R0, 1, 262 | OP_HALT}; 263 | VM vm(program, sizeof(program)); 264 | 265 | SECTION("True - above") 266 | { 267 | vm.setRegister(R1, 123); 268 | vm.setRegister(R2, 122); 269 | REQUIRE(vm.getRegister(R0) == 0); 270 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 271 | REQUIRE(vm.getRegister(R0) == 1); 272 | } 273 | 274 | SECTION("True - equal") 275 | { 276 | vm.reset(); 277 | vm.setRegister(R1, 123); 278 | vm.setRegister(R2, 123); 279 | REQUIRE(vm.getRegister(R0) == 0); 280 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 281 | REQUIRE(vm.getRegister(R0) == 1); 282 | } 283 | 284 | SECTION("False - below") 285 | { 286 | vm.reset(); 287 | vm.setRegister(R1, 122); 288 | vm.setRegister(R2, 123); 289 | REQUIRE(vm.getRegister(R0) == 0); 290 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 291 | REQUIRE(vm.getRegister(R0) == 0); 292 | } 293 | } 294 | 295 | TEST_CASE("OP_JGE") 296 | { 297 | uint8_t program[] = { 298 | OP_JGE, R1, R2, 6, 0, 299 | OP_HALT, 300 | OP_LCONSB, R0, 1, 301 | OP_HALT}; 302 | VM vm(program, sizeof(program)); 303 | 304 | SECTION("True - greater negative") 305 | { 306 | int32_t val1 = -122; 307 | int32_t val2 = -123; 308 | vm.setRegister(R1, *((uint32_t *)&val1)); 309 | vm.setRegister(R2, *((uint32_t *)&val2)); 310 | REQUIRE(vm.getRegister(R0) == 0); 311 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 312 | REQUIRE(vm.getRegister(R0) == 1); 313 | } 314 | 315 | SECTION("True - greater neg/pos") 316 | { 317 | vm.reset(); 318 | int32_t val1 = 1; 319 | int32_t val2 = -122; 320 | vm.setRegister(R1, *((uint32_t *)&val1)); 321 | vm.setRegister(R2, *((uint32_t *)&val2)); 322 | REQUIRE(vm.getRegister(R0) == 0); 323 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 324 | REQUIRE(vm.getRegister(R0) == 1); 325 | } 326 | 327 | SECTION("True - equal") 328 | { 329 | vm.reset(); 330 | int32_t val1 = -123; 331 | int32_t val2 = -123; 332 | vm.setRegister(R1, *((uint32_t *)&val1)); 333 | vm.setRegister(R2, *((uint32_t *)&val2)); 334 | REQUIRE(vm.getRegister(R0) == 0); 335 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 336 | REQUIRE(vm.getRegister(R0) == 1); 337 | } 338 | 339 | SECTION("False - below negative") 340 | { 341 | vm.reset(); 342 | int32_t val1 = -124; 343 | int32_t val2 = -123; 344 | vm.setRegister(R1, *((uint32_t *)&val1)); 345 | vm.setRegister(R2, *((uint32_t *)&val2)); 346 | REQUIRE(vm.getRegister(R0) == 0); 347 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 348 | REQUIRE(vm.getRegister(R0) == 0); 349 | } 350 | 351 | SECTION("False - below neg/pos") 352 | { 353 | vm.reset(); 354 | int32_t val1 = -124; 355 | int32_t val2 = 1; 356 | vm.setRegister(R1, *((uint32_t *)&val1)); 357 | vm.setRegister(R2, *((uint32_t *)&val2)); 358 | REQUIRE(vm.getRegister(R0) == 0); 359 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 360 | REQUIRE(vm.getRegister(R0) == 0); 361 | } 362 | } 363 | 364 | TEST_CASE("OP_JB") 365 | { 366 | uint8_t program[] = { 367 | OP_JB, R1, R2, 6, 0, 368 | OP_HALT, 369 | OP_LCONSB, R0, 1, 370 | OP_HALT}; 371 | VM vm(program, sizeof(program)); 372 | 373 | SECTION("False - above") 374 | { 375 | vm.setRegister(R1, 123); 376 | vm.setRegister(R2, 122); 377 | REQUIRE(vm.getRegister(R0) == 0); 378 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 379 | REQUIRE(vm.getRegister(R0) == 0); 380 | } 381 | 382 | SECTION("False - equal") 383 | { 384 | vm.reset(); 385 | vm.setRegister(R1, 123); 386 | vm.setRegister(R2, 123); 387 | REQUIRE(vm.getRegister(R0) == 0); 388 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 389 | REQUIRE(vm.getRegister(R0) == 0); 390 | } 391 | 392 | SECTION("True - below") 393 | { 394 | vm.reset(); 395 | vm.setRegister(R1, 122); 396 | vm.setRegister(R2, 123); 397 | REQUIRE(vm.getRegister(R0) == 0); 398 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 399 | REQUIRE(vm.getRegister(R0) == 1); 400 | } 401 | } 402 | 403 | TEST_CASE("OP_JL") 404 | { 405 | uint8_t program[] = { 406 | OP_JL, R1, R2, 6, 0, 407 | OP_HALT, 408 | OP_LCONSB, R0, 1, 409 | OP_HALT}; 410 | VM vm(program, sizeof(program)); 411 | 412 | SECTION("False - greater negative") 413 | { 414 | int32_t val1 = -122; 415 | int32_t val2 = -123; 416 | vm.setRegister(R1, *((uint32_t *)&val1)); 417 | vm.setRegister(R2, *((uint32_t *)&val2)); 418 | REQUIRE(vm.getRegister(R0) == 0); 419 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 420 | REQUIRE(vm.getRegister(R0) == 0); 421 | } 422 | 423 | SECTION("False - greater neg/pos") 424 | { 425 | vm.reset(); 426 | int32_t val1 = 1; 427 | int32_t val2 = -122; 428 | vm.setRegister(R1, *((uint32_t *)&val1)); 429 | vm.setRegister(R2, *((uint32_t *)&val2)); 430 | REQUIRE(vm.getRegister(R0) == 0); 431 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 432 | REQUIRE(vm.getRegister(R0) == 0); 433 | } 434 | 435 | SECTION("False - equal") 436 | { 437 | vm.reset(); 438 | int32_t val1 = -123; 439 | int32_t val2 = -123; 440 | vm.setRegister(R1, *((uint32_t *)&val1)); 441 | vm.setRegister(R2, *((uint32_t *)&val2)); 442 | REQUIRE(vm.getRegister(R0) == 0); 443 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 444 | REQUIRE(vm.getRegister(R0) == 0); 445 | } 446 | 447 | SECTION("True - below negative") 448 | { 449 | vm.reset(); 450 | int32_t val1 = -124; 451 | int32_t val2 = -123; 452 | vm.setRegister(R1, *((uint32_t *)&val1)); 453 | vm.setRegister(R2, *((uint32_t *)&val2)); 454 | REQUIRE(vm.getRegister(R0) == 0); 455 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 456 | REQUIRE(vm.getRegister(R0) == 1); 457 | } 458 | 459 | SECTION("True - below neg/pos") 460 | { 461 | vm.reset(); 462 | int32_t val1 = -124; 463 | int32_t val2 = 1; 464 | vm.setRegister(R1, *((uint32_t *)&val1)); 465 | vm.setRegister(R2, *((uint32_t *)&val2)); 466 | REQUIRE(vm.getRegister(R0) == 0); 467 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 468 | REQUIRE(vm.getRegister(R0) == 1); 469 | } 470 | } 471 | 472 | TEST_CASE("OP_JBE") 473 | { 474 | uint8_t program[] = { 475 | OP_JBE, R1, R2, 6, 0, 476 | OP_HALT, 477 | OP_LCONSB, R0, 1, 478 | OP_HALT}; 479 | VM vm(program, sizeof(program)); 480 | 481 | SECTION("False - above") 482 | { 483 | vm.setRegister(R1, 123); 484 | vm.setRegister(R2, 122); 485 | REQUIRE(vm.getRegister(R0) == 0); 486 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 487 | REQUIRE(vm.getRegister(R0) == 0); 488 | } 489 | 490 | SECTION("True - equal") 491 | { 492 | vm.reset(); 493 | vm.setRegister(R1, 123); 494 | vm.setRegister(R2, 123); 495 | REQUIRE(vm.getRegister(R0) == 0); 496 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 497 | REQUIRE(vm.getRegister(R0) == 1); 498 | } 499 | 500 | SECTION("True - below") 501 | { 502 | vm.reset(); 503 | vm.setRegister(R1, 122); 504 | vm.setRegister(R2, 123); 505 | REQUIRE(vm.getRegister(R0) == 0); 506 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 507 | REQUIRE(vm.getRegister(R0) == 1); 508 | } 509 | } 510 | 511 | TEST_CASE("OP_JLE") 512 | { 513 | uint8_t program[] = { 514 | OP_JLE, R1, R2, 6, 0, 515 | OP_HALT, 516 | OP_LCONSB, R0, 1, 517 | OP_HALT}; 518 | VM vm(program, sizeof(program)); 519 | 520 | SECTION("False - greater negative") 521 | { 522 | int32_t val1 = -122; 523 | int32_t val2 = -123; 524 | vm.setRegister(R1, *((uint32_t *)&val1)); 525 | vm.setRegister(R2, *((uint32_t *)&val2)); 526 | REQUIRE(vm.getRegister(R0) == 0); 527 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 528 | REQUIRE(vm.getRegister(R0) == 0); 529 | } 530 | 531 | SECTION("False - greater neg/pos") 532 | { 533 | vm.reset(); 534 | int32_t val1 = 1; 535 | int32_t val2 = -122; 536 | vm.setRegister(R1, *((uint32_t *)&val1)); 537 | vm.setRegister(R2, *((uint32_t *)&val2)); 538 | REQUIRE(vm.getRegister(R0) == 0); 539 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 540 | REQUIRE(vm.getRegister(R0) == 0); 541 | } 542 | 543 | SECTION("True - equal") 544 | { 545 | vm.reset(); 546 | int32_t val1 = -123; 547 | int32_t val2 = -123; 548 | vm.setRegister(R1, *((uint32_t *)&val1)); 549 | vm.setRegister(R2, *((uint32_t *)&val2)); 550 | REQUIRE(vm.getRegister(R0) == 0); 551 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 552 | REQUIRE(vm.getRegister(R0) == 1); 553 | } 554 | 555 | SECTION("True - below negative") 556 | { 557 | vm.reset(); 558 | int32_t val1 = -124; 559 | int32_t val2 = -123; 560 | vm.setRegister(R1, *((uint32_t *)&val1)); 561 | vm.setRegister(R2, *((uint32_t *)&val2)); 562 | REQUIRE(vm.getRegister(R0) == 0); 563 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 564 | REQUIRE(vm.getRegister(R0) == 1); 565 | } 566 | 567 | SECTION("True - below neg/pos") 568 | { 569 | vm.reset(); 570 | int32_t val1 = -124; 571 | int32_t val2 = 1; 572 | vm.setRegister(R1, *((uint32_t *)&val1)); 573 | vm.setRegister(R2, *((uint32_t *)&val2)); 574 | REQUIRE(vm.getRegister(R0) == 0); 575 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 576 | REQUIRE(vm.getRegister(R0) == 1); 577 | } 578 | } 579 | -------------------------------------------------------------------------------- /test/test_arithmetic.cpp: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | TEST_CASE("OP_INC") 4 | { 5 | uint8_t program[] = { 6 | OP_INC, R0, 7 | OP_HALT}; 8 | VM vm(program, sizeof(program)); 9 | 10 | SECTION("Zero") 11 | { 12 | vm.setRegister(R0, 0); 13 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 14 | REQUIRE(vm.getRegister(R0) == 1); 15 | } 16 | 17 | SECTION("1123497651") 18 | { 19 | vm.reset(); 20 | vm.setRegister(R0, 1123497651); 21 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 22 | REQUIRE(vm.getRegister(R0) == 1123497652); 23 | } 24 | 25 | SECTION("UINT32_MAX") 26 | { 27 | vm.reset(); 28 | vm.setRegister(R0, UINT32_MAX); 29 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 30 | REQUIRE(vm.getRegister(R0) == 0); 31 | } 32 | } 33 | 34 | TEST_CASE("OP_FINC") 35 | { 36 | uint8_t program[] = { 37 | OP_FINC, R0, 38 | OP_HALT}; 39 | VM vm(program, sizeof(program)); 40 | 41 | SECTION("Zero") 42 | { 43 | float val = 0.0f; 44 | float expected = 1.0f; 45 | vm.setRegister(R0, *((uint32_t *)&val)); 46 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 47 | uint32_t actual = vm.getRegister(R0); 48 | REQUIRE(_ALMOST_EQUAL(*((float *)&actual), expected)); 49 | } 50 | 51 | SECTION("1123497.12") 52 | { 53 | float val = 1123497.12f; 54 | float expected = 1123498.12f; 55 | vm.setRegister(R0, *((uint32_t *)&val)); 56 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 57 | uint32_t actual = vm.getRegister(R0); 58 | REQUIRE(_ALMOST_EQUAL(*((float *)&actual), expected)); 59 | } 60 | 61 | SECTION("-5") 62 | { 63 | float val = -5.0f; 64 | float expected = -4.0f; 65 | vm.setRegister(R0, *((uint32_t *)&val)); 66 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 67 | uint32_t actual = vm.getRegister(R0); 68 | REQUIRE(_ALMOST_EQUAL(*((float *)&actual), expected)); 69 | } 70 | } 71 | 72 | TEST_CASE("OP_DEC") 73 | { 74 | uint8_t program[] = { 75 | OP_DEC, R0, 76 | OP_HALT}; 77 | VM vm(program, sizeof(program)); 78 | 79 | SECTION("Zero") 80 | { 81 | vm.setRegister(R0, 0); 82 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 83 | REQUIRE(vm.getRegister(R0) == UINT32_MAX); 84 | } 85 | 86 | SECTION("1123497651") 87 | { 88 | vm.reset(); 89 | vm.setRegister(R0, 1123497651); 90 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 91 | REQUIRE(vm.getRegister(R0) == 1123497650); 92 | } 93 | 94 | SECTION("UINT32_MAX") 95 | { 96 | vm.reset(); 97 | vm.setRegister(R0, UINT32_MAX); 98 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 99 | REQUIRE(vm.getRegister(R0) == (UINT32_MAX - 1)); 100 | } 101 | } 102 | 103 | TEST_CASE("OP_FDEC") 104 | { 105 | uint8_t program[] = { 106 | OP_FDEC, R0, 107 | OP_HALT}; 108 | VM vm(program, sizeof(program)); 109 | 110 | SECTION("Zero") 111 | { 112 | float val = 0.0f; 113 | float expected = -1.0f; 114 | vm.setRegister(R0, *((uint32_t *)&val)); 115 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 116 | uint32_t actual = vm.getRegister(R0); 117 | REQUIRE(_ALMOST_EQUAL(*((float *)&actual), expected)); 118 | } 119 | 120 | SECTION("1123497.12") 121 | { 122 | float val = 1123497.12f; 123 | float expected = 1123496.12f; 124 | vm.setRegister(R0, *((uint32_t *)&val)); 125 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 126 | uint32_t actual = vm.getRegister(R0); 127 | REQUIRE(_ALMOST_EQUAL(*((float *)&actual), expected)); 128 | } 129 | 130 | SECTION("-5") 131 | { 132 | float val = -5.0f; 133 | float expected = -6.0f; 134 | vm.setRegister(R0, *((uint32_t *)&val)); 135 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 136 | uint32_t actual = vm.getRegister(R0); 137 | REQUIRE(_ALMOST_EQUAL(*((float *)&actual), expected)); 138 | } 139 | } 140 | 141 | TEST_CASE("OP_ADD") 142 | { 143 | uint8_t program[] = { 144 | OP_ADD, R0, R1, R2, 145 | OP_HALT}; 146 | VM vm(program, sizeof(program)); 147 | 148 | SECTION("0 + 0") 149 | { 150 | vm.setRegister(R1, 0); 151 | vm.setRegister(R2, 0); 152 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 153 | REQUIRE(vm.getRegister(R0) == 0); 154 | } 155 | 156 | SECTION("1123497651 + 987513") 157 | { 158 | vm.reset(); 159 | vm.setRegister(R1, 1123497651); 160 | vm.setRegister(R2, 987513); 161 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 162 | REQUIRE(vm.getRegister(R0) == 1124485164); 163 | } 164 | 165 | SECTION("UINT32_MAX + 1") 166 | { 167 | vm.reset(); 168 | vm.setRegister(R1, UINT32_MAX); 169 | vm.setRegister(R2, 1); 170 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 171 | REQUIRE(vm.getRegister(R0) == 0); 172 | } 173 | } 174 | 175 | TEST_CASE("OP_FADD") 176 | { 177 | uint8_t program[] = { 178 | OP_FADD, R0, R1, R2, 179 | OP_HALT}; 180 | VM vm(program, sizeof(program)); 181 | 182 | SECTION("0 + 0") 183 | { 184 | float val = 0.0f; 185 | vm.setRegister(R1, *((uint32_t *)&val)); 186 | vm.setRegister(R2, *((uint32_t *)&val)); 187 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 188 | uint32_t actual = vm.getRegister(R0); 189 | REQUIRE(_ALMOST_EQUAL(*((float *)&actual), val)); 190 | } 191 | 192 | SECTION("357.34 + 847.21") 193 | { 194 | float val1 = 357.34f; 195 | float val2 = 847.21f; 196 | float expected = 1204.55f; 197 | vm.reset(); 198 | vm.setRegister(R1, *((uint32_t *)&val1)); 199 | vm.setRegister(R2, *((uint32_t *)&val2)); 200 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 201 | uint32_t actual = vm.getRegister(R0); 202 | REQUIRE(_ALMOST_EQUAL(*((float *)&actual), expected)); 203 | } 204 | 205 | SECTION("-3 + -5") 206 | { 207 | float val1 = -3.0f; 208 | float val2 = -5.0f; 209 | float expected = -8.0f; 210 | vm.reset(); 211 | vm.setRegister(R1, *((uint32_t *)&val1)); 212 | vm.setRegister(R2, *((uint32_t *)&val2)); 213 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 214 | uint32_t actual = vm.getRegister(R0); 215 | REQUIRE(_ALMOST_EQUAL(*((float *)&actual), expected)); 216 | } 217 | } 218 | 219 | TEST_CASE("OP_SUB") 220 | { 221 | uint8_t program[] = { 222 | OP_SUB, R0, R1, R2, 223 | OP_HALT}; 224 | VM vm(program, sizeof(program)); 225 | 226 | SECTION("0 - 0") 227 | { 228 | vm.setRegister(R1, 0); 229 | vm.setRegister(R2, 0); 230 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 231 | REQUIRE(vm.getRegister(R0) == 0); 232 | } 233 | 234 | SECTION("1123497651 - 987513") 235 | { 236 | vm.reset(); 237 | vm.setRegister(R1, 1123497651); 238 | vm.setRegister(R2, 987513); 239 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 240 | REQUIRE(vm.getRegister(R0) == 1122510138); 241 | } 242 | 243 | SECTION("0 - 1") 244 | { 245 | vm.reset(); 246 | vm.setRegister(R1, 0); 247 | vm.setRegister(R2, 1); 248 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 249 | REQUIRE(vm.getRegister(R0) == UINT32_MAX); 250 | } 251 | } 252 | 253 | TEST_CASE("OP_FSUB") 254 | { 255 | uint8_t program[] = { 256 | OP_FSUB, R0, R1, R2, 257 | OP_HALT}; 258 | VM vm(program, sizeof(program)); 259 | 260 | SECTION("0 - 0") 261 | { 262 | float val = 0.0f; 263 | vm.setRegister(R1, *((uint32_t *)&val)); 264 | vm.setRegister(R2, *((uint32_t *)&val)); 265 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 266 | uint32_t actual = vm.getRegister(R0); 267 | REQUIRE(_ALMOST_EQUAL(*((float *)&actual), val)); 268 | } 269 | 270 | SECTION("357.34 - 847.21") 271 | { 272 | float val1 = 357.34f; 273 | float val2 = 847.21f; 274 | float expected = -489.87f; 275 | vm.reset(); 276 | vm.setRegister(R1, *((uint32_t *)&val1)); 277 | vm.setRegister(R2, *((uint32_t *)&val2)); 278 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 279 | uint32_t actual = vm.getRegister(R0); 280 | REQUIRE(_ALMOST_EQUAL(*((float *)&actual), expected)); 281 | } 282 | 283 | SECTION("-3 - -5") 284 | { 285 | float val1 = -3.0f; 286 | float val2 = -5.0f; 287 | float expected = 2.0f; 288 | vm.reset(); 289 | vm.setRegister(R1, *((uint32_t *)&val1)); 290 | vm.setRegister(R2, *((uint32_t *)&val2)); 291 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 292 | uint32_t actual = vm.getRegister(R0); 293 | REQUIRE(_ALMOST_EQUAL(*((float *)&actual), expected)); 294 | } 295 | } 296 | 297 | TEST_CASE("OP_MUL") 298 | { 299 | uint8_t program[] = { 300 | OP_MUL, R0, R1, R2, 301 | OP_HALT}; 302 | VM vm(program, sizeof(program)); 303 | 304 | SECTION("1 * 0") 305 | { 306 | vm.setRegister(R1, 1); 307 | vm.setRegister(R2, 0); 308 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 309 | REQUIRE(vm.getRegister(R0) == 0); 310 | } 311 | 312 | SECTION("2 * 2") 313 | { 314 | vm.reset(); 315 | vm.setRegister(R1, 2); 316 | vm.setRegister(R2, 2); 317 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 318 | REQUIRE(vm.getRegister(R0) == 4); 319 | } 320 | 321 | SECTION("347 * 987513") 322 | { 323 | vm.reset(); 324 | vm.setRegister(R1, 347); 325 | vm.setRegister(R2, 987513); 326 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 327 | REQUIRE(vm.getRegister(R0) == 342667011); 328 | } 329 | } 330 | 331 | TEST_CASE("OP_IMUL") 332 | { 333 | uint8_t program[] = { 334 | OP_IMUL, R0, R1, R2, 335 | OP_HALT}; 336 | VM vm(program, sizeof(program)); 337 | 338 | SECTION("1 * 0") 339 | { 340 | vm.setRegister(R1, 1); 341 | vm.setRegister(R2, 0); 342 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 343 | REQUIRE(vm.getRegister(R0) == 0); 344 | } 345 | 346 | SECTION("2 * 2") 347 | { 348 | vm.reset(); 349 | vm.setRegister(R1, 2); 350 | vm.setRegister(R2, 2); 351 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 352 | REQUIRE(vm.getRegister(R0) == 4); 353 | } 354 | 355 | SECTION("-3 * -5") 356 | { 357 | int32_t val1 = -3; 358 | int32_t val2 = -5; 359 | int32_t expected = 15; 360 | vm.reset(); 361 | vm.setRegister(R1, *((uint32_t *)&val1)); 362 | vm.setRegister(R2, *((uint32_t *)&val2)); 363 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 364 | REQUIRE(vm.getRegister(R0) == *((uint32_t *)&expected)); 365 | } 366 | } 367 | 368 | TEST_CASE("OP_FMUL") 369 | { 370 | uint8_t program[] = { 371 | OP_FMUL, R0, R1, R2, 372 | OP_HALT}; 373 | VM vm(program, sizeof(program)); 374 | 375 | SECTION("1 * 0") 376 | { 377 | float val1 = 1.0f; 378 | float val2 = 0.0f; 379 | vm.setRegister(R1, *((uint32_t *)&val1)); 380 | vm.setRegister(R2, *((uint32_t *)&val2)); 381 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 382 | REQUIRE(vm.getRegister(R0) == *((uint32_t *)&val2)); 383 | } 384 | 385 | SECTION("357.34 * 847.21") 386 | { 387 | float val1 = 357.34f; 388 | float val2 = 847.21f; 389 | float expected = 302742.0214f; 390 | vm.reset(); 391 | vm.setRegister(R1, *((uint32_t *)&val1)); 392 | vm.setRegister(R2, *((uint32_t *)&val2)); 393 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 394 | uint32_t actual = vm.getRegister(R0); 395 | REQUIRE(_ALMOST_EQUAL(*((float *)&actual), expected)); 396 | } 397 | 398 | SECTION("-3 * -5") 399 | { 400 | float val1 = -3.0f; 401 | float val2 = -5.0f; 402 | float expected = 15.0f; 403 | vm.reset(); 404 | vm.setRegister(R1, *((uint32_t *)&val1)); 405 | vm.setRegister(R2, *((uint32_t *)&val2)); 406 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 407 | uint32_t actual = vm.getRegister(R0); 408 | REQUIRE(_ALMOST_EQUAL(*((float *)&actual), expected)); 409 | } 410 | } 411 | 412 | TEST_CASE("OP_DIV") 413 | { 414 | uint8_t program[] = { 415 | OP_DIV, R0, R1, R2, 416 | OP_HALT}; 417 | VM vm(program, sizeof(program)); 418 | 419 | // SECTION("1 / 0") 420 | // { 421 | // TODO: add code to test handling of division by zero 422 | // } 423 | 424 | SECTION("2 / 2") 425 | { 426 | vm.reset(); 427 | vm.setRegister(R1, 2); 428 | vm.setRegister(R2, 2); 429 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 430 | REQUIRE(vm.getRegister(R0) == 1); 431 | } 432 | 433 | SECTION("987513 / 347") 434 | { 435 | vm.reset(); 436 | vm.setRegister(R1, 987513); 437 | vm.setRegister(R2, 347); 438 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 439 | REQUIRE(vm.getRegister(R0) == 2845); 440 | } 441 | } 442 | 443 | TEST_CASE("OP_IDIV") 444 | { 445 | uint8_t program[] = { 446 | OP_IDIV, R0, R1, R2, 447 | OP_HALT}; 448 | VM vm(program, sizeof(program)); 449 | 450 | // SECTION("1 / 0") 451 | // { 452 | // TODO: add code to test handling of division by zero 453 | // } 454 | 455 | SECTION("2 / 2") 456 | { 457 | vm.reset(); 458 | vm.setRegister(R1, 2); 459 | vm.setRegister(R2, 2); 460 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 461 | REQUIRE(vm.getRegister(R0) == 1); 462 | } 463 | 464 | SECTION("-50 / -3") 465 | { 466 | int32_t val1 = -50; 467 | int32_t val2 = -3; 468 | int32_t expected = 16; 469 | vm.reset(); 470 | vm.setRegister(R1, *((uint32_t *)&val1)); 471 | vm.setRegister(R2, *((uint32_t *)&val2)); 472 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 473 | REQUIRE(vm.getRegister(R0) == *((uint32_t *)&expected)); 474 | } 475 | } 476 | 477 | TEST_CASE("OP_FDIV") 478 | { 479 | uint8_t program[] = { 480 | OP_FDIV, R0, R1, R2, 481 | OP_HALT}; 482 | VM vm(program, sizeof(program)); 483 | 484 | // SECTION("1 / 0") 485 | // { 486 | // TODO: add code to test handling of division by zero 487 | // } 488 | 489 | SECTION("847.21 / 357.34") 490 | { 491 | float val1 = 847.21f; 492 | float val2 = 357.34f; 493 | float expected = 2.370879275f; 494 | vm.reset(); 495 | vm.setRegister(R1, *((uint32_t *)&val1)); 496 | vm.setRegister(R2, *((uint32_t *)&val2)); 497 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 498 | uint32_t actual = vm.getRegister(R0); 499 | REQUIRE(_ALMOST_EQUAL(*((float *)&actual), expected)); 500 | } 501 | 502 | SECTION("-50 / -3") 503 | { 504 | float val1 = -50.0f; 505 | float val2 = -3.0f; 506 | float expected = 16.666666667f; 507 | vm.reset(); 508 | vm.setRegister(R1, *((uint32_t *)&val1)); 509 | vm.setRegister(R2, *((uint32_t *)&val2)); 510 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 511 | uint32_t actual = vm.getRegister(R0); 512 | REQUIRE(_ALMOST_EQUAL(*((float *)&actual), expected)); 513 | } 514 | } 515 | 516 | TEST_CASE("OP_SHL") 517 | { 518 | uint8_t program[] = { 519 | OP_SHL, R0, R1, R2, 520 | OP_HALT}; 521 | VM vm(program, sizeof(program)); 522 | 523 | SECTION("1 << 0") 524 | { 525 | vm.setRegister(R1, 1); 526 | vm.setRegister(R2, 0); 527 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 528 | REQUIRE(vm.getRegister(R0) == 1); 529 | } 530 | 531 | SECTION("0xFF00 << 1") 532 | { 533 | vm.reset(); 534 | vm.setRegister(R1, 0xFF00); 535 | vm.setRegister(R2, 1); 536 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 537 | REQUIRE(vm.getRegister(R0) == 0x1FE00); 538 | } 539 | 540 | SECTION("0xA310 << 16") 541 | { 542 | vm.reset(); 543 | vm.setRegister(R1, 0xA310); 544 | vm.setRegister(R2, 16); 545 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 546 | REQUIRE(vm.getRegister(R0) == 0xA3100000); 547 | } 548 | } 549 | 550 | TEST_CASE("OP_SHR") 551 | { 552 | uint8_t program[] = { 553 | OP_SHR, R0, R1, R2, 554 | OP_HALT}; 555 | VM vm(program, sizeof(program)); 556 | 557 | SECTION("1 >> 0") 558 | { 559 | vm.setRegister(R1, 1); 560 | vm.setRegister(R2, 0); 561 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 562 | REQUIRE(vm.getRegister(R0) == 1); 563 | } 564 | 565 | SECTION("0xFF00 >> 1") 566 | { 567 | vm.reset(); 568 | vm.setRegister(R1, 0xFF00); 569 | vm.setRegister(R2, 1); 570 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 571 | REQUIRE(vm.getRegister(R0) == 0x7F80); 572 | } 573 | 574 | SECTION("0xA3100000 >> 16") 575 | { 576 | vm.reset(); 577 | vm.setRegister(R1, 0xA3100000); 578 | vm.setRegister(R2, 16); 579 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 580 | REQUIRE(vm.getRegister(R0) == 0xA310); 581 | } 582 | } 583 | 584 | TEST_CASE("OP_ISHR") 585 | { 586 | uint8_t program[] = { 587 | OP_ISHR, R0, R1, R2, 588 | OP_HALT}; 589 | VM vm(program, sizeof(program)); 590 | 591 | SECTION("1 >> 0") 592 | { 593 | vm.setRegister(R1, 1); 594 | vm.setRegister(R2, 0); 595 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 596 | REQUIRE(vm.getRegister(R0) == 1); 597 | } 598 | 599 | SECTION("-100 >> 1") 600 | { 601 | int32_t val1 = -100; 602 | int32_t expected = -50; 603 | vm.reset(); 604 | vm.setRegister(R1, *((uint32_t *)&val1)); 605 | vm.setRegister(R2, 1); 606 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 607 | REQUIRE(vm.getRegister(R0) == *((uint32_t *)&expected)); 608 | } 609 | 610 | SECTION("0xA3100000 >> 16") 611 | { 612 | int32_t val1 = 100; 613 | int32_t expected = 50; 614 | vm.reset(); 615 | vm.setRegister(R1, *((uint32_t *)&val1)); 616 | vm.setRegister(R2, 1); 617 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 618 | REQUIRE(vm.getRegister(R0) == *((uint32_t *)&expected)); 619 | } 620 | } 621 | 622 | TEST_CASE("OP_MOD") 623 | { 624 | uint8_t program[] = { 625 | OP_MOD, R0, R1, R2, 626 | OP_HALT}; 627 | VM vm(program, sizeof(program)); 628 | 629 | // SECTION("1 % 0") 630 | // { 631 | // TODO: add code to test handling of division by zero 632 | // } 633 | 634 | SECTION("2 mod 2") 635 | { 636 | vm.reset(); 637 | vm.setRegister(R1, 2); 638 | vm.setRegister(R2, 2); 639 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 640 | REQUIRE(vm.getRegister(R0) == 0); 641 | } 642 | 643 | SECTION("987513 mod 347") 644 | { 645 | vm.reset(); 646 | vm.setRegister(R1, 987513); 647 | vm.setRegister(R2, 347); 648 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 649 | REQUIRE(vm.getRegister(R0) == 298); 650 | } 651 | } 652 | 653 | TEST_CASE("OP_IMOD") 654 | { 655 | uint8_t program[] = { 656 | OP_IMOD, R0, R1, R2, 657 | OP_HALT}; 658 | VM vm(program, sizeof(program)); 659 | 660 | // SECTION("1 / 0") 661 | // { 662 | // TODO: add code to test handling of division by zero 663 | // } 664 | 665 | SECTION("2 mod 2") 666 | { 667 | vm.reset(); 668 | vm.setRegister(R1, 2); 669 | vm.setRegister(R2, 2); 670 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 671 | REQUIRE(vm.getRegister(R0) == 0); 672 | } 673 | 674 | SECTION("-50 mod -3") 675 | { 676 | int32_t val1 = -50; 677 | int32_t val2 = -3; 678 | int32_t expected = -2; 679 | vm.reset(); 680 | vm.setRegister(R1, *((uint32_t *)&val1)); 681 | vm.setRegister(R2, *((uint32_t *)&val2)); 682 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 683 | REQUIRE(vm.getRegister(R0) == *((uint32_t *)&expected)); 684 | } 685 | } 686 | 687 | TEST_CASE("OP_AND") 688 | { 689 | uint8_t program[] = { 690 | OP_AND, R0, R1, R2, 691 | OP_HALT}; 692 | VM vm(program, sizeof(program)); 693 | 694 | SECTION("0 & 0") 695 | { 696 | vm.setRegister(R1, 0); 697 | vm.setRegister(R2, 0); 698 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 699 | REQUIRE(vm.getRegister(R0) == 0); 700 | } 701 | 702 | SECTION("1 & 0") 703 | { 704 | vm.setRegister(R1, 1); 705 | vm.setRegister(R2, 0); 706 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 707 | REQUIRE(vm.getRegister(R0) == 0); 708 | } 709 | 710 | SECTION("1 & 1") 711 | { 712 | vm.setRegister(R1, 1); 713 | vm.setRegister(R2, 1); 714 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 715 | REQUIRE(vm.getRegister(R0) == 1); 716 | } 717 | 718 | SECTION("0xF1F1F1F1 & 0xEAD1") 719 | { 720 | vm.setRegister(R1, 0xF1F1F1F1); 721 | vm.setRegister(R2, 0xEAD1); 722 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 723 | REQUIRE(vm.getRegister(R0) == 0xE0D1); 724 | } 725 | } 726 | 727 | TEST_CASE("OP_OR") 728 | { 729 | uint8_t program[] = { 730 | OP_OR, R0, R1, R2, 731 | OP_HALT}; 732 | VM vm(program, sizeof(program)); 733 | 734 | SECTION("0 | 0") 735 | { 736 | vm.setRegister(R1, 0); 737 | vm.setRegister(R2, 0); 738 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 739 | REQUIRE(vm.getRegister(R0) == 0); 740 | } 741 | 742 | SECTION("1 | 0") 743 | { 744 | vm.setRegister(R1, 1); 745 | vm.setRegister(R2, 0); 746 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 747 | REQUIRE(vm.getRegister(R0) == 1); 748 | } 749 | 750 | SECTION("1 | 1") 751 | { 752 | vm.setRegister(R1, 1); 753 | vm.setRegister(R2, 1); 754 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 755 | REQUIRE(vm.getRegister(R0) == 1); 756 | } 757 | 758 | SECTION("0xF1F1F1F1 | 0xEAD1") 759 | { 760 | vm.setRegister(R1, 0xF1F1F1F1); 761 | vm.setRegister(R2, 0xEAD1); 762 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 763 | REQUIRE(vm.getRegister(R0) == 0xF1F1FBF1); 764 | } 765 | } 766 | 767 | TEST_CASE("OP_XOR") 768 | { 769 | uint8_t program[] = { 770 | OP_XOR, R0, R1, R2, 771 | OP_HALT}; 772 | VM vm(program, sizeof(program)); 773 | 774 | SECTION("0 ^ 0") 775 | { 776 | vm.setRegister(R1, 0); 777 | vm.setRegister(R2, 0); 778 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 779 | REQUIRE(vm.getRegister(R0) == 0); 780 | } 781 | 782 | SECTION("1 ^ 0") 783 | { 784 | vm.setRegister(R1, 1); 785 | vm.setRegister(R2, 0); 786 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 787 | REQUIRE(vm.getRegister(R0) == 1); 788 | } 789 | 790 | SECTION("1 ^ 1") 791 | { 792 | vm.setRegister(R1, 1); 793 | vm.setRegister(R2, 1); 794 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 795 | REQUIRE(vm.getRegister(R0) == 0); 796 | } 797 | 798 | SECTION("0xF1F1F1F1 ^ 0xEAD1") 799 | { 800 | vm.setRegister(R1, 0xF1F1F1F1); 801 | vm.setRegister(R2, 0xEAD1); 802 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 803 | REQUIRE(vm.getRegister(R0) == 0xF1F11B20); 804 | } 805 | } 806 | 807 | TEST_CASE("OP_NOT") 808 | { 809 | uint8_t program[] = { 810 | OP_NOT, R0, R1, 811 | OP_HALT}; 812 | VM vm(program, sizeof(program)); 813 | 814 | SECTION("!0") 815 | { 816 | vm.setRegister(R1, 0); 817 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 818 | REQUIRE(vm.getRegister(R0) == 0xFFFFFFFF); 819 | } 820 | 821 | SECTION("!1") 822 | { 823 | vm.setRegister(R1, 1); 824 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 825 | REQUIRE(vm.getRegister(R0) == 0xFFFFFFFE); 826 | } 827 | 828 | SECTION("!0xF1F1F1F1") 829 | { 830 | vm.setRegister(R1, 0xF1F1F1F1); 831 | REQUIRE(vm.run() == ExecResult::VM_FINISHED); 832 | REQUIRE(vm.getRegister(R0) == 0xE0E0E0E); 833 | } 834 | } 835 | -------------------------------------------------------------------------------- /src/vm.cpp: -------------------------------------------------------------------------------- 1 | #include "vm.h" 2 | 3 | #define _NEXT_BYTE this->_memory[++this->_registers[IP]] 4 | #define _NEXT_SHORT ({ this->_registers[IP] += 2; this->_memory[this->_registers[IP]-1]\ 5 | | this->_memory[this->_registers[IP]] << 8; }) 6 | #define _NEXT_INT ({ \ 7 | this->_registers[IP] += 4; \ 8 | this->_memory[this->_registers[IP] - 3] | this->_memory[this->_registers[IP] - 2] << 8 | \ 9 | this->_memory[this->_registers[IP] - 1] << 16 | this->_memory[this->_registers[IP]] << 24; \ 10 | }) 11 | 12 | #ifndef VM_DISABLE_CHECKS 13 | #define _CHECK_ADDR_VALID(a) \ 14 | if (a >= this->_memSize) \ 15 | return ExecResult::VM_ERR_INVALID_ADDRESS; 16 | #define _CHECK_BYTES_AVAIL(n) \ 17 | _CHECK_ADDR_VALID(this->_registers[IP] + n) 18 | #define _CHECK_REGISTER_VALID(r) \ 19 | if (r >= REGISTER_COUNT) \ 20 | return ExecResult::VM_ERR_INVALID_REGISTER; 21 | #define _CHECK_CAN_PUSH(n) \ 22 | if (this->_registers[SP] - (n * sizeof(uint32_t)) < this->_progLen) \ 23 | return ExecResult::VM_ERR_STACK_OVERFLOW; 24 | #define _CHECK_CAN_POP(n) \ 25 | if (this->_registers[SP] + (n * sizeof(uint32_t)) > this->_memSize) \ 26 | return ExecResult::VM_ERR_STACK_UNDERFLOW; \ 27 | if (this->_registers[SP] < this->_progLen) \ 28 | return ExecResult::VM_ERR_STACK_OVERFLOW; 29 | #else 30 | #define _CHECK_ADDR_VALID(a) 31 | #define _CHECK_BYTES_AVAIL(n) 32 | #define _CHECK_REGISTER_VALID(r) 33 | #define _CHECK_CAN_PUSH(n) 34 | #define _CHECK_CAN_POP(n) 35 | #endif 36 | 37 | VM::VM(uint8_t *program, uint16_t progLen, uint16_t stackSize) 38 | : _memory(new uint8_t[progLen + stackSize]), _memSize(progLen + stackSize), _progLen(progLen), _stackSize(stackSize) 39 | { 40 | memcpy(this->_memory, program, progLen); 41 | this->reset(); 42 | } 43 | 44 | VM::~VM() 45 | { 46 | delete[] this->_memory; 47 | } 48 | 49 | void VM::reset() 50 | { 51 | memset(&this->_memory[this->_progLen], 0, this->_stackSize); 52 | memset(this->_registers, 0, REGISTER_COUNT * sizeof(uint32_t)); 53 | this->_registers[SP] = this->_progLen + this->_stackSize; 54 | } 55 | 56 | void VM::onInterrupt(bool (*callback)(uint8_t)) 57 | { 58 | this->_interruptCallback = callback; 59 | } 60 | 61 | uint32_t VM::stackCount() 62 | { 63 | return this->_progLen + this->_stackSize - this->_registers[SP]; 64 | } 65 | 66 | void VM::stackPush(uint32_t value) 67 | { 68 | this->_registers[SP] -= 4; 69 | memcpy(&this->_memory[this->_registers[SP]], &value, sizeof(uint32_t)); 70 | } 71 | 72 | uint32_t VM::stackPop() 73 | { 74 | uint32_t val = 0; 75 | memcpy(&val, &this->_memory[this->_registers[SP]], sizeof(uint32_t)); 76 | this->_registers[SP] += 4; 77 | return val; 78 | } 79 | 80 | uint8_t *VM::memory(uint16_t addr) 81 | { 82 | return &this->_memory[addr]; 83 | } 84 | 85 | uint32_t VM::getRegister(Register reg) 86 | { 87 | return this->_registers[reg]; 88 | } 89 | 90 | void VM::setRegister(Register reg, uint32_t val) 91 | { 92 | this->_registers[reg] = val; 93 | } 94 | 95 | ExecResult VM::run(uint32_t maxInstr) 96 | { 97 | uint32_t instrCount = 0; 98 | 99 | while (maxInstr == 0 || instrCount < maxInstr) 100 | { 101 | _CHECK_ADDR_VALID(this->_registers[IP]) 102 | const uint8_t instr = this->_memory[this->_registers[IP]]; 103 | if (instr >= INSTRUCTION_COUNT) 104 | return ExecResult::VM_ERR_UNKNOWN_OPCODE; 105 | 106 | switch (instr) 107 | { 108 | case OP_NOP: 109 | { 110 | break; 111 | } 112 | case OP_HALT: 113 | { 114 | return ExecResult::VM_FINISHED; 115 | } 116 | case OP_INT: 117 | { 118 | _CHECK_BYTES_AVAIL(1) 119 | const uint8_t code = _NEXT_BYTE; 120 | 121 | if (this->_interruptCallback == nullptr) 122 | return ExecResult::VM_ERR_UNHANDLED_INTERRUPT; 123 | if (!this->_interruptCallback(code)) 124 | return ExecResult::VM_FINISHED; 125 | break; 126 | } 127 | case OP_MOV: 128 | { 129 | _CHECK_BYTES_AVAIL(2) 130 | const uint8_t reg1 = _NEXT_BYTE; 131 | const uint8_t reg2 = _NEXT_BYTE; 132 | _CHECK_REGISTER_VALID(reg1) 133 | _CHECK_REGISTER_VALID(reg2) 134 | this->_registers[reg1] = this->_registers[reg2]; 135 | break; 136 | } 137 | case OP_LCONS: 138 | { 139 | _CHECK_BYTES_AVAIL(5) 140 | const uint8_t reg = _NEXT_BYTE; 141 | _CHECK_REGISTER_VALID(reg) 142 | this->_registers[reg] = _NEXT_INT; 143 | break; 144 | } 145 | case OP_LCONSW: 146 | { 147 | _CHECK_BYTES_AVAIL(3) 148 | const uint8_t reg = _NEXT_BYTE; 149 | _CHECK_REGISTER_VALID(reg) 150 | this->_registers[reg] = _NEXT_SHORT; 151 | break; 152 | } 153 | case OP_LCONSB: 154 | { 155 | _CHECK_BYTES_AVAIL(2) 156 | const uint8_t reg = _NEXT_BYTE; 157 | _CHECK_REGISTER_VALID(reg) 158 | this->_registers[reg] = _NEXT_BYTE; 159 | break; 160 | } 161 | case OP_PUSH: 162 | { 163 | _CHECK_BYTES_AVAIL(1) 164 | const uint8_t reg = _NEXT_BYTE; 165 | _CHECK_REGISTER_VALID(reg) 166 | _CHECK_CAN_PUSH(1) 167 | this->_registers[SP] -= 4; 168 | memcpy(&this->_memory[this->_registers[SP]], &this->_registers[reg], sizeof(uint32_t)); 169 | break; 170 | } 171 | case OP_POP: 172 | { 173 | _CHECK_BYTES_AVAIL(1) 174 | const uint8_t reg = _NEXT_BYTE; 175 | _CHECK_REGISTER_VALID(reg) 176 | _CHECK_CAN_POP(1) 177 | memcpy(&this->_registers[reg], &this->_memory[this->_registers[SP]], sizeof(uint32_t)); 178 | this->_registers[SP] += 4; 179 | break; 180 | } 181 | case OP_POP2: 182 | { 183 | _CHECK_BYTES_AVAIL(2) 184 | const uint8_t reg1 = _NEXT_BYTE; 185 | const uint8_t reg2 = _NEXT_BYTE; 186 | _CHECK_REGISTER_VALID(reg1) 187 | _CHECK_REGISTER_VALID(reg2) 188 | _CHECK_CAN_POP(2) 189 | memcpy(&this->_registers[reg1], &this->_memory[this->_registers[SP]], sizeof(uint32_t)); 190 | this->_registers[SP] += 4; 191 | memcpy(&this->_registers[reg2], &this->_memory[this->_registers[SP]], sizeof(uint32_t)); 192 | this->_registers[SP] += 4; 193 | break; 194 | } 195 | case OP_DUP: 196 | { 197 | _CHECK_CAN_PUSH(1) 198 | this->_registers[SP] -= 4; 199 | memcpy(&this->_memory[this->_registers[SP]], &this->_memory[this->_registers[SP]] + 4, sizeof(uint32_t)); 200 | break; 201 | } 202 | case OP_CALL: 203 | { 204 | _CHECK_BYTES_AVAIL(2) 205 | this->_registers[RA] = this->_registers[IP] + 3; 206 | this->_registers[IP] = _NEXT_SHORT - 1; 207 | break; 208 | } 209 | case OP_RET: 210 | { 211 | this->_registers[IP] = this->_registers[RA] - 1; 212 | break; 213 | } 214 | case OP_STOR: 215 | { 216 | _CHECK_BYTES_AVAIL(3) 217 | const uint16_t addr = _NEXT_SHORT; 218 | const uint8_t reg = _NEXT_BYTE; 219 | _CHECK_REGISTER_VALID(reg) 220 | _CHECK_ADDR_VALID((uint32_t)addr + 3) 221 | memcpy(&this->_memory[addr], &this->_registers[reg], sizeof(uint32_t)); 222 | break; 223 | } 224 | case OP_STOR_P: 225 | { 226 | _CHECK_BYTES_AVAIL(2) 227 | const uint8_t reg1 = _NEXT_BYTE; 228 | const uint8_t reg2 = _NEXT_BYTE; 229 | _CHECK_REGISTER_VALID(reg1) 230 | _CHECK_REGISTER_VALID(reg2) 231 | const uint16_t dest = this->_registers[reg1]; 232 | _CHECK_ADDR_VALID((uint32_t)dest + 3) 233 | memcpy(&this->_memory[dest], &this->_registers[reg2], sizeof(uint32_t)); 234 | break; 235 | } 236 | case OP_STORW: 237 | { 238 | _CHECK_BYTES_AVAIL(3) 239 | const uint16_t addr = _NEXT_SHORT; 240 | const uint8_t reg = _NEXT_BYTE; 241 | _CHECK_REGISTER_VALID(reg) 242 | _CHECK_ADDR_VALID((uint32_t)addr + 1) 243 | memcpy(&this->_memory[addr], &this->_registers[reg], sizeof(uint16_t)); 244 | break; 245 | } 246 | case OP_STORW_P: 247 | { 248 | _CHECK_BYTES_AVAIL(2) 249 | const uint8_t reg1 = _NEXT_BYTE; 250 | const uint8_t reg2 = _NEXT_BYTE; 251 | _CHECK_REGISTER_VALID(reg1) 252 | _CHECK_REGISTER_VALID(reg2) 253 | const uint16_t dest = this->_registers[reg1]; 254 | _CHECK_ADDR_VALID((uint32_t)dest + 1) 255 | memcpy(&this->_memory[dest], &this->_registers[reg2], sizeof(uint16_t)); 256 | break; 257 | } 258 | case OP_STORB: 259 | { 260 | _CHECK_BYTES_AVAIL(3) 261 | const uint16_t addr = _NEXT_SHORT; 262 | const uint8_t reg = _NEXT_BYTE; 263 | _CHECK_REGISTER_VALID(reg) 264 | _CHECK_ADDR_VALID(addr) 265 | memcpy(&this->_memory[addr], &this->_registers[reg], sizeof(uint8_t)); 266 | break; 267 | } 268 | case OP_STORB_P: 269 | { 270 | _CHECK_BYTES_AVAIL(2) 271 | const uint8_t reg1 = _NEXT_BYTE; 272 | const uint8_t reg2 = _NEXT_BYTE; 273 | _CHECK_REGISTER_VALID(reg1) 274 | _CHECK_REGISTER_VALID(reg2) 275 | const uint16_t dest = this->_registers[reg1]; 276 | _CHECK_ADDR_VALID((uint32_t)dest) 277 | memcpy(&this->_memory[dest], &this->_registers[reg2], sizeof(uint8_t)); 278 | break; 279 | } 280 | case OP_LOAD: 281 | { 282 | _CHECK_BYTES_AVAIL(3) 283 | const uint8_t reg = _NEXT_BYTE; 284 | const uint16_t addr = _NEXT_SHORT; 285 | _CHECK_REGISTER_VALID(reg) 286 | _CHECK_ADDR_VALID((uint32_t)addr + 3) 287 | memcpy(&this->_registers[reg], &this->_memory[addr], sizeof(uint32_t)); 288 | break; 289 | } 290 | case OP_LOAD_P: 291 | { 292 | _CHECK_BYTES_AVAIL(2) 293 | const uint8_t reg1 = _NEXT_BYTE; 294 | const uint8_t reg2 = _NEXT_BYTE; 295 | _CHECK_REGISTER_VALID(reg1) 296 | _CHECK_REGISTER_VALID(reg2) 297 | const uint16_t src = this->_registers[reg2]; 298 | _CHECK_ADDR_VALID((uint32_t)src + 3) 299 | memcpy(&this->_registers[reg1], &this->_memory[src], sizeof(uint32_t)); 300 | break; 301 | } 302 | case OP_LOADW: 303 | { 304 | _CHECK_BYTES_AVAIL(3) 305 | const uint8_t reg = _NEXT_BYTE; 306 | const uint16_t addr = _NEXT_SHORT; 307 | _CHECK_REGISTER_VALID(reg) 308 | _CHECK_ADDR_VALID((uint32_t)addr + 1) 309 | this->_registers[reg] = 0; 310 | memcpy(&this->_registers[reg], &this->_memory[addr], sizeof(uint16_t)); 311 | break; 312 | } 313 | case OP_LOADW_P: 314 | { 315 | _CHECK_BYTES_AVAIL(2) 316 | const uint8_t reg1 = _NEXT_BYTE; 317 | const uint8_t reg2 = _NEXT_BYTE; 318 | _CHECK_REGISTER_VALID(reg1) 319 | _CHECK_REGISTER_VALID(reg2) 320 | const uint16_t src = this->_registers[reg2]; 321 | _CHECK_ADDR_VALID((uint32_t)src + 1) 322 | this->_registers[reg1] = 0; 323 | memcpy(&this->_registers[reg1], &this->_memory[src], sizeof(uint16_t)); 324 | break; 325 | } 326 | case OP_LOADB: 327 | { 328 | _CHECK_BYTES_AVAIL(3) 329 | const uint8_t reg = _NEXT_BYTE; 330 | const uint16_t addr = _NEXT_SHORT; 331 | _CHECK_REGISTER_VALID(reg) 332 | _CHECK_ADDR_VALID((uint32_t)addr) 333 | this->_registers[reg] = this->_memory[addr]; 334 | break; 335 | } 336 | case OP_LOADB_P: 337 | { 338 | _CHECK_BYTES_AVAIL(2) 339 | const uint8_t reg1 = _NEXT_BYTE; 340 | const uint8_t reg2 = _NEXT_BYTE; 341 | _CHECK_REGISTER_VALID(reg1) 342 | _CHECK_REGISTER_VALID(reg2) 343 | const uint16_t src = this->_registers[reg2]; 344 | _CHECK_ADDR_VALID((uint32_t)src) 345 | this->_registers[reg1] = this->_memory[src]; 346 | break; 347 | } 348 | case OP_MEMCPY: 349 | { 350 | _CHECK_BYTES_AVAIL(6) 351 | const uint16_t dest = _NEXT_SHORT; 352 | const uint16_t source = _NEXT_SHORT; 353 | const uint16_t bytes = _NEXT_SHORT; 354 | _CHECK_ADDR_VALID((uint32_t)source + bytes - 1) 355 | _CHECK_ADDR_VALID((uint32_t)dest + bytes - 1) 356 | memcpy(&this->_memory[dest], &this->_memory[source], bytes); 357 | break; 358 | } 359 | case OP_MEMCPY_P: 360 | { 361 | _CHECK_BYTES_AVAIL(3) 362 | const uint8_t reg1 = _NEXT_BYTE; 363 | const uint8_t reg2 = _NEXT_BYTE; 364 | const uint8_t reg3 = _NEXT_BYTE; 365 | _CHECK_REGISTER_VALID(reg1) 366 | _CHECK_REGISTER_VALID(reg2) 367 | _CHECK_REGISTER_VALID(reg3) 368 | const uint16_t dest = this->_registers[reg1]; 369 | const uint16_t source = this->_registers[reg2]; 370 | const uint16_t bytes = this->_registers[reg3]; 371 | _CHECK_ADDR_VALID((uint32_t)source + bytes - 1) 372 | _CHECK_ADDR_VALID((uint32_t)dest + bytes - 1) 373 | memcpy(&this->_memory[dest], &this->_memory[source], bytes); 374 | break; 375 | } 376 | case OP_INC: 377 | { 378 | _CHECK_BYTES_AVAIL(1) 379 | const uint8_t reg = _NEXT_BYTE; 380 | _CHECK_REGISTER_VALID(reg) 381 | this->_registers[reg]++; 382 | break; 383 | } 384 | case OP_FINC: 385 | { 386 | _CHECK_BYTES_AVAIL(1) 387 | const uint8_t reg = _NEXT_BYTE; 388 | _CHECK_REGISTER_VALID(reg) 389 | (*((float *)&this->_registers[reg]))++; 390 | break; 391 | } 392 | case OP_DEC: 393 | { 394 | _CHECK_BYTES_AVAIL(1) 395 | const uint8_t reg = _NEXT_BYTE; 396 | _CHECK_REGISTER_VALID(reg) 397 | this->_registers[reg]--; 398 | break; 399 | } 400 | case OP_FDEC: 401 | { 402 | _CHECK_BYTES_AVAIL(1) 403 | const uint8_t reg = _NEXT_BYTE; 404 | _CHECK_REGISTER_VALID(reg) 405 | (*((float *)&this->_registers[reg]))--; 406 | break; 407 | } 408 | case OP_ADD: 409 | { 410 | _CHECK_BYTES_AVAIL(3) 411 | const uint8_t rreg = _NEXT_BYTE; 412 | const uint8_t reg1 = _NEXT_BYTE; 413 | const uint8_t reg2 = _NEXT_BYTE; 414 | _CHECK_REGISTER_VALID(rreg) 415 | _CHECK_REGISTER_VALID(reg1) 416 | _CHECK_REGISTER_VALID(reg2) 417 | this->_registers[rreg] = this->_registers[reg1] + this->_registers[reg2]; 418 | break; 419 | } 420 | case OP_FADD: 421 | { 422 | _CHECK_BYTES_AVAIL(3) 423 | const uint8_t rreg = _NEXT_BYTE; 424 | const uint8_t reg1 = _NEXT_BYTE; 425 | const uint8_t reg2 = _NEXT_BYTE; 426 | _CHECK_REGISTER_VALID(rreg) 427 | _CHECK_REGISTER_VALID(reg1) 428 | _CHECK_REGISTER_VALID(reg2) 429 | *((float *)&this->_registers[rreg]) = *((float *)&this->_registers[reg1]) + *((float *)&this->_registers[reg2]); 430 | break; 431 | } 432 | case OP_SUB: 433 | { 434 | _CHECK_BYTES_AVAIL(3) 435 | const uint8_t rreg = _NEXT_BYTE; 436 | const uint8_t reg1 = _NEXT_BYTE; 437 | const uint8_t reg2 = _NEXT_BYTE; 438 | _CHECK_REGISTER_VALID(rreg) 439 | _CHECK_REGISTER_VALID(reg1) 440 | _CHECK_REGISTER_VALID(reg2) 441 | this->_registers[rreg] = this->_registers[reg1] - this->_registers[reg2]; 442 | break; 443 | } 444 | case OP_FSUB: 445 | { 446 | _CHECK_BYTES_AVAIL(3) 447 | const uint8_t rreg = _NEXT_BYTE; 448 | const uint8_t reg1 = _NEXT_BYTE; 449 | const uint8_t reg2 = _NEXT_BYTE; 450 | _CHECK_REGISTER_VALID(rreg) 451 | _CHECK_REGISTER_VALID(reg1) 452 | _CHECK_REGISTER_VALID(reg2) 453 | *((float *)&this->_registers[rreg]) = *((float *)&this->_registers[reg1]) - *((float *)&this->_registers[reg2]); 454 | break; 455 | } 456 | case OP_MUL: 457 | { 458 | _CHECK_BYTES_AVAIL(3) 459 | const uint8_t rreg = _NEXT_BYTE; 460 | const uint8_t reg1 = _NEXT_BYTE; 461 | const uint8_t reg2 = _NEXT_BYTE; 462 | _CHECK_REGISTER_VALID(rreg) 463 | _CHECK_REGISTER_VALID(reg1) 464 | _CHECK_REGISTER_VALID(reg2) 465 | this->_registers[rreg] = this->_registers[reg1] * this->_registers[reg2]; 466 | break; 467 | } 468 | case OP_IMUL: 469 | { 470 | _CHECK_BYTES_AVAIL(3) 471 | const uint8_t rreg = _NEXT_BYTE; 472 | const uint8_t reg1 = _NEXT_BYTE; 473 | const uint8_t reg2 = _NEXT_BYTE; 474 | _CHECK_REGISTER_VALID(rreg) 475 | _CHECK_REGISTER_VALID(reg1) 476 | _CHECK_REGISTER_VALID(reg2) 477 | *((int32_t *)&this->_registers[rreg]) = *((int32_t *)&this->_registers[reg1]) * *((int32_t *)&this->_registers[reg2]); 478 | break; 479 | } 480 | case OP_FMUL: 481 | { 482 | _CHECK_BYTES_AVAIL(3) 483 | const uint8_t rreg = _NEXT_BYTE; 484 | const uint8_t reg1 = _NEXT_BYTE; 485 | const uint8_t reg2 = _NEXT_BYTE; 486 | _CHECK_REGISTER_VALID(rreg) 487 | _CHECK_REGISTER_VALID(reg1) 488 | _CHECK_REGISTER_VALID(reg2) 489 | *((float *)&this->_registers[rreg]) = *((float *)&this->_registers[reg1]) * *((float *)&this->_registers[reg2]); 490 | break; 491 | } 492 | case OP_DIV: 493 | { 494 | _CHECK_BYTES_AVAIL(3) 495 | const uint8_t rreg = _NEXT_BYTE; 496 | const uint8_t reg1 = _NEXT_BYTE; 497 | const uint8_t reg2 = _NEXT_BYTE; 498 | _CHECK_REGISTER_VALID(rreg) 499 | _CHECK_REGISTER_VALID(reg1) 500 | _CHECK_REGISTER_VALID(reg2) 501 | this->_registers[rreg] = this->_registers[reg1] / this->_registers[reg2]; 502 | break; 503 | } 504 | case OP_IDIV: 505 | { 506 | _CHECK_BYTES_AVAIL(3) 507 | const uint8_t rreg = _NEXT_BYTE; 508 | const uint8_t reg1 = _NEXT_BYTE; 509 | const uint8_t reg2 = _NEXT_BYTE; 510 | _CHECK_REGISTER_VALID(rreg) 511 | _CHECK_REGISTER_VALID(reg1) 512 | _CHECK_REGISTER_VALID(reg2) 513 | *((int32_t *)&this->_registers[rreg]) = *((int32_t *)&this->_registers[reg1]) / *((int32_t *)&this->_registers[reg2]); 514 | break; 515 | } 516 | case OP_FDIV: 517 | { 518 | _CHECK_BYTES_AVAIL(3) 519 | const uint8_t rreg = _NEXT_BYTE; 520 | const uint8_t reg1 = _NEXT_BYTE; 521 | const uint8_t reg2 = _NEXT_BYTE; 522 | _CHECK_REGISTER_VALID(rreg) 523 | _CHECK_REGISTER_VALID(reg1) 524 | _CHECK_REGISTER_VALID(reg2) 525 | *((float *)&this->_registers[rreg]) = *((float *)&this->_registers[reg1]) / *((float *)&this->_registers[reg2]); 526 | break; 527 | } 528 | case OP_SHL: 529 | { 530 | _CHECK_BYTES_AVAIL(3) 531 | const uint8_t rreg = _NEXT_BYTE; 532 | const uint8_t reg1 = _NEXT_BYTE; 533 | const uint8_t reg2 = _NEXT_BYTE; 534 | _CHECK_REGISTER_VALID(rreg) 535 | _CHECK_REGISTER_VALID(reg1) 536 | _CHECK_REGISTER_VALID(reg2) 537 | this->_registers[rreg] = this->_registers[reg1] << this->_registers[reg2]; 538 | break; 539 | } 540 | case OP_SHR: 541 | { 542 | _CHECK_BYTES_AVAIL(3) 543 | const uint8_t rreg = _NEXT_BYTE; 544 | const uint8_t reg1 = _NEXT_BYTE; 545 | const uint8_t reg2 = _NEXT_BYTE; 546 | _CHECK_REGISTER_VALID(rreg) 547 | _CHECK_REGISTER_VALID(reg1) 548 | _CHECK_REGISTER_VALID(reg2) 549 | this->_registers[rreg] = this->_registers[reg1] >> this->_registers[reg2]; 550 | break; 551 | } 552 | case OP_ISHR: 553 | { 554 | _CHECK_BYTES_AVAIL(3) 555 | const uint8_t rreg = _NEXT_BYTE; 556 | const uint8_t reg1 = _NEXT_BYTE; 557 | const uint8_t reg2 = _NEXT_BYTE; 558 | _CHECK_REGISTER_VALID(rreg) 559 | _CHECK_REGISTER_VALID(reg1) 560 | _CHECK_REGISTER_VALID(reg2) 561 | *((int32_t *)&this->_registers[rreg]) = *((int32_t *)&this->_registers[reg1]) >> *((int32_t *)&this->_registers[reg2]); 562 | break; 563 | } 564 | case OP_MOD: 565 | { 566 | _CHECK_BYTES_AVAIL(3) 567 | const uint8_t rreg = _NEXT_BYTE; 568 | const uint8_t reg1 = _NEXT_BYTE; 569 | const uint8_t reg2 = _NEXT_BYTE; 570 | _CHECK_REGISTER_VALID(rreg) 571 | _CHECK_REGISTER_VALID(reg1) 572 | _CHECK_REGISTER_VALID(reg2) 573 | this->_registers[rreg] = this->_registers[reg1] % this->_registers[reg2]; 574 | break; 575 | } 576 | case OP_IMOD: 577 | { 578 | _CHECK_BYTES_AVAIL(3) 579 | const uint8_t rreg = _NEXT_BYTE; 580 | const uint8_t reg1 = _NEXT_BYTE; 581 | const uint8_t reg2 = _NEXT_BYTE; 582 | _CHECK_REGISTER_VALID(rreg) 583 | _CHECK_REGISTER_VALID(reg1) 584 | _CHECK_REGISTER_VALID(reg2) 585 | *((int32_t *)&this->_registers[rreg]) = *((int32_t *)&this->_registers[reg1]) % *((int32_t *)&this->_registers[reg2]); 586 | break; 587 | } 588 | case OP_AND: 589 | { 590 | _CHECK_BYTES_AVAIL(3) 591 | const uint8_t rreg = _NEXT_BYTE; 592 | const uint8_t reg1 = _NEXT_BYTE; 593 | const uint8_t reg2 = _NEXT_BYTE; 594 | _CHECK_REGISTER_VALID(rreg) 595 | _CHECK_REGISTER_VALID(reg1) 596 | _CHECK_REGISTER_VALID(reg2) 597 | this->_registers[rreg] = this->_registers[reg1] & this->_registers[reg2]; 598 | break; 599 | } 600 | case OP_OR: 601 | { 602 | _CHECK_BYTES_AVAIL(3) 603 | const uint8_t rreg = _NEXT_BYTE; 604 | const uint8_t reg1 = _NEXT_BYTE; 605 | const uint8_t reg2 = _NEXT_BYTE; 606 | _CHECK_REGISTER_VALID(rreg) 607 | _CHECK_REGISTER_VALID(reg1) 608 | _CHECK_REGISTER_VALID(reg2) 609 | this->_registers[rreg] = this->_registers[reg1] | this->_registers[reg2]; 610 | break; 611 | } 612 | case OP_XOR: 613 | { 614 | _CHECK_BYTES_AVAIL(3) 615 | const uint8_t rreg = _NEXT_BYTE; 616 | const uint8_t reg1 = _NEXT_BYTE; 617 | const uint8_t reg2 = _NEXT_BYTE; 618 | _CHECK_REGISTER_VALID(rreg) 619 | _CHECK_REGISTER_VALID(reg1) 620 | _CHECK_REGISTER_VALID(reg2) 621 | this->_registers[rreg] = this->_registers[reg1] ^ this->_registers[reg2]; 622 | break; 623 | } 624 | case OP_NOT: 625 | { 626 | _CHECK_BYTES_AVAIL(2) 627 | const uint8_t rreg = _NEXT_BYTE; 628 | const uint8_t reg1 = _NEXT_BYTE; 629 | _CHECK_REGISTER_VALID(rreg) 630 | _CHECK_REGISTER_VALID(reg1) 631 | this->_registers[rreg] = ~this->_registers[reg1]; 632 | break; 633 | } 634 | case OP_U2I: 635 | { 636 | _CHECK_BYTES_AVAIL(1) 637 | const uint8_t reg = _NEXT_BYTE; 638 | _CHECK_REGISTER_VALID(reg) 639 | *((int32_t *)&this->_registers[reg]) = this->_registers[reg]; 640 | break; 641 | } 642 | case OP_I2U: 643 | { 644 | _CHECK_BYTES_AVAIL(1) 645 | const uint8_t reg = _NEXT_BYTE; 646 | _CHECK_REGISTER_VALID(reg) 647 | this->_registers[reg] = *((int32_t *)&this->_registers[reg]); 648 | break; 649 | } 650 | case OP_I2F: 651 | { 652 | _CHECK_BYTES_AVAIL(2) 653 | const uint8_t reg = _NEXT_BYTE; 654 | const uint8_t reg1 = _NEXT_BYTE; 655 | _CHECK_REGISTER_VALID(reg) 656 | _CHECK_REGISTER_VALID(reg1) 657 | *((float *)&this->_registers[reg]) = (float)*((int32_t *)&this->_registers[reg1]); 658 | break; 659 | } 660 | case OP_F2I: 661 | { 662 | _CHECK_BYTES_AVAIL(2) 663 | const uint8_t reg = _NEXT_BYTE; 664 | const uint8_t reg1 = _NEXT_BYTE; 665 | _CHECK_REGISTER_VALID(reg) 666 | _CHECK_REGISTER_VALID(reg1) 667 | *((int32_t *)&this->_registers[reg]) = (int32_t) * ((float *)&this->_registers[reg1]); 668 | break; 669 | } 670 | case OP_JMP: 671 | { 672 | _CHECK_BYTES_AVAIL(2) 673 | this->_registers[IP] = _NEXT_SHORT - 1; 674 | break; 675 | } 676 | case OP_JR: 677 | { 678 | _CHECK_BYTES_AVAIL(1) 679 | const uint8_t reg = _NEXT_BYTE; 680 | _CHECK_REGISTER_VALID(reg) 681 | this->_registers[IP] = this->_registers[reg] - 1; 682 | break; 683 | } 684 | case OP_JZ: 685 | { 686 | _CHECK_BYTES_AVAIL(3) 687 | const uint8_t reg = _NEXT_BYTE; 688 | const uint16_t addr = _NEXT_SHORT; 689 | _CHECK_REGISTER_VALID(reg) 690 | 691 | if (this->_registers[reg] == 0) 692 | this->_registers[IP] = addr - 1; 693 | break; 694 | } 695 | case OP_JNZ: 696 | { 697 | _CHECK_BYTES_AVAIL(3) 698 | const uint8_t reg = _NEXT_BYTE; 699 | const uint16_t addr = _NEXT_SHORT; 700 | _CHECK_REGISTER_VALID(reg) 701 | 702 | if (this->_registers[reg] != 0) 703 | this->_registers[IP] = addr - 1; 704 | break; 705 | } 706 | case OP_JE: 707 | { 708 | _CHECK_BYTES_AVAIL(4) 709 | const uint8_t reg1 = _NEXT_BYTE; 710 | const uint8_t reg2 = _NEXT_BYTE; 711 | const uint16_t addr = _NEXT_SHORT; 712 | _CHECK_REGISTER_VALID(reg1) 713 | _CHECK_REGISTER_VALID(reg2) 714 | 715 | if (this->_registers[reg1] == this->_registers[reg2]) 716 | this->_registers[IP] = addr - 1; 717 | break; 718 | } 719 | case OP_JNE: 720 | { 721 | _CHECK_BYTES_AVAIL(4) 722 | const uint8_t reg1 = _NEXT_BYTE; 723 | const uint8_t reg2 = _NEXT_BYTE; 724 | const uint16_t addr = _NEXT_SHORT; 725 | _CHECK_REGISTER_VALID(reg1) 726 | _CHECK_REGISTER_VALID(reg2) 727 | 728 | if (this->_registers[reg1] != this->_registers[reg2]) 729 | this->_registers[IP] = addr - 1; 730 | break; 731 | } 732 | case OP_JA: 733 | { 734 | _CHECK_BYTES_AVAIL(4) 735 | const uint8_t reg1 = _NEXT_BYTE; 736 | const uint8_t reg2 = _NEXT_BYTE; 737 | const uint16_t addr = _NEXT_SHORT; 738 | _CHECK_REGISTER_VALID(reg1) 739 | _CHECK_REGISTER_VALID(reg2) 740 | 741 | if (this->_registers[reg1] > this->_registers[reg2]) 742 | this->_registers[IP] = addr - 1; 743 | break; 744 | } 745 | case OP_JG: 746 | { 747 | _CHECK_BYTES_AVAIL(4) 748 | const uint8_t reg1 = _NEXT_BYTE; 749 | const uint8_t reg2 = _NEXT_BYTE; 750 | const uint16_t addr = _NEXT_SHORT; 751 | _CHECK_REGISTER_VALID(reg1) 752 | _CHECK_REGISTER_VALID(reg2) 753 | 754 | if (*((int32_t *)&this->_registers[reg1]) > *((int32_t *)&this->_registers[reg2])) 755 | this->_registers[IP] = addr - 1; 756 | break; 757 | } 758 | case OP_JAE: 759 | { 760 | _CHECK_BYTES_AVAIL(4) 761 | const uint8_t reg1 = _NEXT_BYTE; 762 | const uint8_t reg2 = _NEXT_BYTE; 763 | const uint16_t addr = _NEXT_SHORT; 764 | _CHECK_REGISTER_VALID(reg1) 765 | _CHECK_REGISTER_VALID(reg2) 766 | 767 | if (this->_registers[reg1] >= this->_registers[reg2]) 768 | this->_registers[IP] = addr - 1; 769 | break; 770 | } 771 | case OP_JGE: 772 | { 773 | _CHECK_BYTES_AVAIL(4) 774 | const uint8_t reg1 = _NEXT_BYTE; 775 | const uint8_t reg2 = _NEXT_BYTE; 776 | const uint16_t addr = _NEXT_SHORT; 777 | _CHECK_REGISTER_VALID(reg1) 778 | _CHECK_REGISTER_VALID(reg2) 779 | 780 | if (*((int32_t *)&this->_registers[reg1]) >= *((int32_t *)&this->_registers[reg2])) 781 | this->_registers[IP] = addr - 1; 782 | break; 783 | } 784 | case OP_JB: 785 | { 786 | _CHECK_BYTES_AVAIL(4) 787 | const uint8_t reg1 = _NEXT_BYTE; 788 | const uint8_t reg2 = _NEXT_BYTE; 789 | const uint16_t addr = _NEXT_SHORT; 790 | _CHECK_REGISTER_VALID(reg1) 791 | _CHECK_REGISTER_VALID(reg2) 792 | 793 | if (this->_registers[reg1] < this->_registers[reg2]) 794 | this->_registers[IP] = addr - 1; 795 | break; 796 | } 797 | case OP_JL: 798 | { 799 | _CHECK_BYTES_AVAIL(4) 800 | const uint8_t reg1 = _NEXT_BYTE; 801 | const uint8_t reg2 = _NEXT_BYTE; 802 | const uint16_t addr = _NEXT_SHORT; 803 | _CHECK_REGISTER_VALID(reg1) 804 | _CHECK_REGISTER_VALID(reg2) 805 | 806 | if (*((int32_t *)&this->_registers[reg1]) < *((int32_t *)&this->_registers[reg2])) 807 | this->_registers[IP] = addr - 1; 808 | break; 809 | } 810 | case OP_JBE: 811 | { 812 | _CHECK_BYTES_AVAIL(4) 813 | const uint8_t reg1 = _NEXT_BYTE; 814 | const uint8_t reg2 = _NEXT_BYTE; 815 | const uint16_t addr = _NEXT_SHORT; 816 | _CHECK_REGISTER_VALID(reg1) 817 | _CHECK_REGISTER_VALID(reg2) 818 | 819 | if (this->_registers[reg1] <= this->_registers[reg2]) 820 | this->_registers[IP] = addr - 1; 821 | break; 822 | } 823 | case OP_JLE: 824 | { 825 | _CHECK_BYTES_AVAIL(4) 826 | const uint8_t reg1 = _NEXT_BYTE; 827 | const uint8_t reg2 = _NEXT_BYTE; 828 | const uint16_t addr = _NEXT_SHORT; 829 | _CHECK_REGISTER_VALID(reg1) 830 | _CHECK_REGISTER_VALID(reg2) 831 | 832 | if (*((int32_t *)&this->_registers[reg1]) <= *((int32_t *)&this->_registers[reg2])) 833 | this->_registers[IP] = addr - 1; 834 | break; 835 | } 836 | case OP_PRINT: 837 | { 838 | _CHECK_BYTES_AVAIL(2) 839 | const uint8_t reg = _NEXT_BYTE; 840 | const uint8_t ln = _NEXT_BYTE; 841 | _CHECK_REGISTER_VALID(reg) 842 | 843 | printf("%u", this->_registers[reg]); 844 | if (ln != 0) 845 | putchar('\n'); 846 | break; 847 | } 848 | case OP_PRINTI: 849 | { 850 | _CHECK_BYTES_AVAIL(2) 851 | const uint8_t reg = _NEXT_BYTE; 852 | const uint8_t ln = _NEXT_BYTE; 853 | _CHECK_REGISTER_VALID(reg) 854 | 855 | printf("%d", *((int32_t *)&this->_registers[reg])); 856 | if (ln != 0) 857 | putchar('\n'); 858 | break; 859 | } 860 | case OP_PRINTF: 861 | { 862 | _CHECK_BYTES_AVAIL(2) 863 | const uint8_t reg = _NEXT_BYTE; 864 | const uint8_t ln = _NEXT_BYTE; 865 | _CHECK_REGISTER_VALID(reg) 866 | 867 | printf("%f", *((float *)&this->_registers[reg])); 868 | if (ln != 0) 869 | putchar('\n'); 870 | break; 871 | } 872 | case OP_PRINTC: 873 | { 874 | _CHECK_BYTES_AVAIL(1) 875 | const uint8_t reg = _NEXT_BYTE; 876 | _CHECK_REGISTER_VALID(reg) 877 | char *c = (char *)&this->_registers[reg]; 878 | putchar(*c); 879 | break; 880 | } 881 | case OP_PRINTS: 882 | { 883 | _CHECK_BYTES_AVAIL(2) 884 | const uint16_t addr = _NEXT_SHORT; 885 | _CHECK_ADDR_VALID(addr) 886 | char *curChar = (char *)&this->_memory[addr]; 887 | 888 | while (*curChar != '\0') 889 | { 890 | putchar(*curChar); 891 | curChar++; 892 | _CHECK_ADDR_VALID((uint8_t *)curChar - this->_memory) 893 | } 894 | break; 895 | } 896 | case OP_PRINTLN: 897 | { 898 | putchar('\n'); 899 | break; 900 | } 901 | case OP_READ: 902 | { 903 | _CHECK_BYTES_AVAIL(1) 904 | const uint8_t reg = _NEXT_BYTE; 905 | _CHECK_REGISTER_VALID(reg) 906 | scanf("%u", &this->_registers[reg]); 907 | break; 908 | } 909 | case OP_READI: 910 | { 911 | _CHECK_BYTES_AVAIL(1) 912 | const uint8_t reg = _NEXT_BYTE; 913 | _CHECK_REGISTER_VALID(reg) 914 | scanf("%d", (int32_t *)&this->_registers[reg]); 915 | break; 916 | } 917 | case OP_READF: 918 | { 919 | _CHECK_BYTES_AVAIL(1) 920 | const uint8_t reg = _NEXT_BYTE; 921 | _CHECK_REGISTER_VALID(reg) 922 | scanf("%f", (float *)&this->_registers[reg]); 923 | break; 924 | } 925 | case OP_READC: 926 | { 927 | _CHECK_BYTES_AVAIL(1) 928 | const uint8_t reg = _NEXT_BYTE; 929 | _CHECK_REGISTER_VALID(reg) 930 | this->_registers[reg] = getchar(); 931 | break; 932 | } 933 | case OP_READS: 934 | { 935 | _CHECK_BYTES_AVAIL(4) 936 | const uint16_t addr = _NEXT_SHORT; 937 | size_t maxLen = _NEXT_SHORT; 938 | _CHECK_ADDR_VALID((uint32_t)addr + maxLen) 939 | char *dest = (char *)&this->_memory[addr]; 940 | getline(&dest, &maxLen, stdin); 941 | break; 942 | } 943 | } 944 | 945 | this->_registers[IP]++; 946 | instrCount++; 947 | } 948 | 949 | return ExecResult::VM_PAUSED; 950 | } --------------------------------------------------------------------------------