Based on Carp by Maxwell Bernstein. The goal of the project is to try and build a small (and decently reliable) VM from the ground up. Right now there are instructions, registers, a stack, data memory, and calls. Recursion should work.
- C11 with -pedantic -Wall -Wextra and a few more
- Aims for zero compiler warnings in library, interpreter and tests
- Completely removed use of the C preprocessor (except for
#includes
and few#defined
constants) - OpenBSD-like KNF coding style
- Uses libok with asserts instead of libtap for tests (simpler)
- Cleaned-up project directory hierarchy
- Less focus on portability or compiler backwards compatibility
- Hooks to build, test and run the interpreter before every commit
- Cleanup the code and focus on correctness and simplicity
- Focus on memory safety and memory management correctness (talloc)
- Redesign the instruction set to offer a smaller core to build upon
- Improve documentation and rationale behind the instruction set
- Separate parsing into a compiler and build an intermediate bytecode
- Improve error handling and exit conditions/states
- Faster symbol lookup
- Small main loop and focus on cache coherence
- Exception mechanism
loadstr
to read a string into memory and push address onto stack- Bounds-checking
load
andstore
instructions
If you already have a local copy
git fetch
git rebase
If you don't already have a local copy
git clone
make
make test
(optional)make install
make clean
(optional)
See carp -h
for help with command-line arguments.
Run carp your_file.carp
- Include
carp/carp_machine.h
in your program - Run
gcc program.c /usr/local/lib/libcarp.a -o program
The syntax looks like most ASM with instructions, arguments and whatnot:
- Function/label definitions are defined with a colon at the end of
the name, like so:
add:
- Function calls are simple:
call add, 2
- Registers are not prefixed with %, like so:
ax
- Instruction arguments are separated by commas (though actually they don't have to be)
- All words must be shorter than 10 characters long
- All Carp files must end with a blank line
Example:
add:
load -4
load -3
add
pop ax
ret
main:
push 7
push 9
call add
preg ax
halt 0
This should print 16
.
Opcode | Arguments | Description |
---|---|---|
HALT | exit code | Sets ext to given code, halts, and attempts to clean up stack, data memory, and label memory. |
NOP | Does nothing. Seriously. | |
LOADR | reg, val | Loads given integer value into given register. |
LOAD | diff | Loads value at location fp + diff in the stack. |
STORE | diff, val | Stores value at location fp + diff. |
MOV | dst, src | Copies contents of src register into dst register. |
ADD | Pops the top two integers from the stack and pushes their sum. | |
SUB | Pops the top two integers from the stack and pushes the difference (lower minus upper). | |
MUL | Pops the top two integers from the stack and pushes their product. | |
MOD | Pops the top two integers from the stack and pushes lower % upper. | |
SHR | Pops the top two integers from the stack and pushes lower >> upper. | |
SHL | Pops the top two integers from the stack and pushes lower << upper. | |
NOT | Pops top integer from stack and pushes bitwise NOT of that integer. | |
XOR | Pops the top two integers from the stack and pushes bitwise XOR.. | |
OR | Pops the top two integers from the stack and pushes bitwise OR. | |
AND | Pops the top two integers from the stack and pushes bitwise AND. | |
INCR | reg | Increments value in given register. |
DECR | reg | Decrements value in given register. |
INC | Increments the value at the top of the stack. | |
DEC | Decrements the value at the top of the stack. | |
PUSHR | reg | Pushes value in given register. |
PUSH | val | Pushes given value. |
POP | reg | Pops an integer from the stack and dumps it into given register. |
CMP | Pops the top two integers from the stack and checks if equal. 0 means equal. Pushes result. | |
LT | Pops the top two integers from the stack and checks if lower < upper. Pushes result. | |
GT | Pops the top two integers from the stack and checks if lower > upper. Pushes result. | |
JZ | addr | Jumps to given absolute address if top of the stack is 0. |
RJZ | diff | Adds differential to ip (relative jump) if top of the stack is 0. |
JNZ | addr | Jumps to given absolute address if top of the stack is not 0. |
RJNZ | diff | Adds differential to ip (relative jump) if top of the stack is not 0. |
JMP | addr | Jumps to given absolute address unconditionally. |
RJMP | diff | Adds differential to ip (relative jump) unconditionally. |
CALL | key/addr | Save state and set IP to value in data memory at key. Function may return value in ax . |
RET | Put top of the stack into ax and load previous state. |
|
PREG | reg | Prints contents of given register. |
PTOP | Peeks top of stack and prints top value. |
Name | Purpose |
---|---|
r0 ... r9 |
General purpose. |
ax |
Return value for user-defined function. |
bx , cx , dx , rx |
... something in the future. Just taking up space for now. |
ip |
Instruction pointer. Used for keeping place in code, gotos, calling, etc. |
sp |
Stack pointer. |
fp |
Frame pointer. Used to keep state for function calls. |
gbg |
Garbage register mainly used for popping. |
run |
Boolean - is machine running? |
ext |
Exit code. |
GPLv3 - See LICENSE