# uVM - Micro Virtual Machine uVM is simply an experiment in developing a working virtual machine with it's own bytecode. ## Contributing I am taking any and all contributions! This is a fun project for me and I'd like to see other people throw their ideas into this. ## Requirements * CMake version 3.8 or higher * Clang and Clang++ * Boost (because im lazy) ## Building 1. `git clone` the repository 2. `cmake .` 3. `make` ## Usage _NOTE_: This is unimplemented - Looking for contributors `uvm [-v] [-h] [-d ] -f .uc` * `-v`: Enable verbose logging mode * `-h`: Show usage * `-f .uc`: Load the uvm bytecode file for processing * `-d `: The maximum stack depth in words ## Testing CMake tests are used to confirm a handful of sample programs will run successfully in the VM 1. `cmake .` 2. `make test` ## Architecture uVM is a _stack machine_. That is, it gets all of it's arguments from the stack, and returns the result of the computation back to the stack when it's done. There are some exceptions to this. In particular the current version of uvm supports registers. ## uVM Instruction Set uVM possesses the standard issue instruction set you might expect in a simple VM: | Instruction | Opcode | Action | | ------------ | ------ | -------------------------------------------------------------------------------------- | | ipush _X_ | 0 | Pushes _integer_ X onto the stack | | isave0 | 1 | Takes the top of the stack and stores it in `t0` | | isave1 | 2 | Takes the top of the stack and stores it in `t1` | | isave2 | 3 | Takes the top of the stack and stores it in `t2` | | iload0 | 4 | Puts the value in `t0` onto the top of the stack | | iload1 | 5 | Puts the value in `t1` onto the top of the stack | | iload2 | 6 | Puts the value in `t2` onto the top of the stack | | icmp | 7 | Compares the top two items on the stack together and returns a boolean result | | iadd | 8 | Adds the top two _integer_ arguments of the stack together | | isub | 9 | Subtracts the top two _integer_ arguments of the stack from each other | | jmp _BYTE_ | A | Unconditional jump to _BYTE_ | | jc _BYTE_ | B | Jump to _BYTE_ if the top of the stack is a 1 | | halt | C | Halts the VM | | print | D | Prints the current top of the stack as an integer | | imul | E | Multiples the top two integers on the stack | | call | F | TBD | | idiv | 10 | Takes the top two values on the stack and performs integer division on them | | irem | 11 | Takes the integer remainder of the division of the top two integer values on the stack | Additionally some registers exist to ease computation: | Register | Use | | -------- | ---------------------------------- | | t0 | Temporary _integer_ register 0 | | t1 | Temporary _integer_ register 1 | | t2 | Temporary _integer_ register 2 | | cf | Comparison flag register | As I learn more about VM development this instruction set will likely become much more robust. ## uVM Calling Convention uVM borrows heavily from past work - so a CDECL convention modified to work with stack machines (result is store on the top of the stack instead of EAX) is used. ## TODO - [ ] Tests for good paths for all instructions - [ ] Come up with a clever way to support floating point operations - [ ] Tests for uncompilable code - [ ] Tests for maximum stack size reached - [ ] Tests to make sure the maximum stack depth is always greater than 0 - [ ] Test to make sure that run doesn't run when the byte code array is empty - [ ] Logging out current stack position, etc when verbose mode is enabled - [ ] If verbose mode isn't enabled it shows the ascii loading while processing - [ ] It would be cool to eventually write a high level language compiler that compiles down to the uvm