Taylor Bockman
5785cbf556
|
7 years ago | |
---|---|---|
include | 7 years ago | |
src | 7 years ago | |
.gitignore | 7 years ago | |
CMakeLists.txt | 7 years ago | |
README.md | 7 years ago |
README.md
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
git clone
the repositorycmake .
make
Usage
NOTE: This is unimplemented - Looking for contributors
uvm [-v] [-h] [-d <stack_depth>] -f <your_file_of_bytecode>.uc
-v
: Enable verbose logging mode-h
: Show usage-f <file_name>.uc
: Load the uvm bytecode file for processing-d <stack_depth>
: The maximum stack depth in words
Testing
CMake tests are used to confirm a handful of sample programs will run successfully in the VM
cmake .
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 a single variable,
called t0
, that can be written to using xpop0
where x is the datatype of the thing getting taken off
the stack.
Additionally the contents of t0
can be placed on the top of the stack using xload0
.
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 |
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