| .github/workflows | ||
| apps | ||
| common | ||
| doc | ||
| hosts | ||
| precompiled | ||
| test | ||
| uvm32 | ||
| Dockerfile | ||
| LICENSE | ||
| Makefile | ||
| README.md | ||
🌱 uvm32
uvm32 is a minimalist, dependency-free virtual machine sandbox designed for microcontrollers and other resource-constrained devices. Single C file, no dynamic memory allocations, asynchronous design, pure C99.
On an STM32L0 (ARM Cortex-M0+) the required footprint is under 4KB flash/1KB RAM.
What is it for?
- As a no-frills alternative to embedded script engines (Lua, Duktape, MicroPython, etc)
- As a sandbox to isolate untrusted or unreliable elements of a system
- As a way to allow development in modern systems programming languages where a compiler for the target may not be available (rust-hello)
- As a way to write once, run anywhere and avoid maintaining multiple software variants
Features
- Bytecode example apps written in C, Zig, Rust and assembly
- Non-blocking design, preventing misbehaving bytecode from stalling the host
- No assumptions about host IO capabilities (no stdio)
- Simple, opinionated execution model
- Safe minimally typed FFI
- Small enough for "if this then that" scripts/plugins, capable enough for much more
- Aims for safety over speed, bad code running in the VM should never be able to crash the host
Although based on a fully fledged CPU emulator, uvm32 is intended for executing custom script like logic, not for simulating hardware.
Understanding this repo
uvm32 is a tiny virtual machine, all of the code is in uvm32.
A minimal example of a host to run code in is at host-mini.
Everything else is a more advanced host, or a sample application which could be run.
Example
A simple VM host from host-mini
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "uvm32.h"
#include "uvm32_common_custom.h"
uint8_t rom[] = { // mandel.bin
0x23, 0x26, 0x11, 0x00, 0xef, 0x00, 0xc0, 0x00, 0xb7, 0x08, 0x00, 0x01,
...
...
};
int main(int argc, char *argv[]) {
uvm32_state_t vmst;
uvm32_evt_t evt;
bool isrunning = true;
uvm32_init(&vmst);
uvm32_load(&vmst, rom, sizeof(rom));
while(isrunning) {
uvm32_run(&vmst, &evt, 100); // num instructions before vm considered hung
switch(evt.typ) {
case UVM32_EVT_END:
isrunning = false;
break;
case UVM32_EVT_SYSCALL: // vm has paused to handle UVM32_SYSCALL
switch(evt.data.syscall.code) {
case UVM32_SYSCALL_PUTC:
printf("%c", uvm32_getval(&vmst, &evt, ARG0));
break;
case UVM32_SYSCALL_PRINTLN: {
const char *str = uvm32_getcstr(&vmst, &evt, ARG0);
printf("%s\n", str);
} break;
case UVM32_SYSCALL_YIELD:
break;
default:
printf("Unhandled syscall 0x%08x\n", evt.data.syscall.code);
break;
}
break;
case UVM32_EVT_ERR:
printf("UVM32_EVT_ERR '%s' (%d)\n", evt.data.err.errstr, (int)evt.data.err.errcode);
break;
default:
break;
}
}
return 0;
}
Samples
- VM hosts
- host vm host which loads a binary and runs to completion, handling multiple syscall types
- host-mini minimal vm host (shown above), with baked in bytecode
- host-parallel parallel vm host running multiple vm instances concurrently, with baked in bytecode
- host-arduino vm host as Arduino sketch (
make testto run AVR code in qemu) - C sample apps
- apps/helloworld C hello world program
- apps/heap Demonstration of
malloc()on extram in C - apps/conio C console IO demo
- apps/lissajous C console lissajous curve (showing softfp, floating point)
- apps/maze C ASCII art recursive maze generation
- apps/fib C fibonacci series program (iterative and recursive)
- apps/sketch C Arduino/Wiring/Processing type program in
setup()andloop()style - Rust sample apps
- apps/rust-hello Rust hello world program (note, the version of rust installed by brew on mac has issues, use the official rust installer from https://rust-lang.org/learn/get-started/)
- Zig sample apps
- apps/zig-mandel Zig ASCII mandelbrot generator program
- apps/zigtris Zig Tetris (https://github.com/ringtailsoftware/zigtris)
- apps/zigalloc Demonstration of using extram with zig allocator
- apps/zigdoom Port of PureDOOM (making use of Zig to provide an allocator and libc like functions)
- Assembly sample apps
- apps/hello-asm Minimal hello world assembly
- VM host as an app
- apps/self host-mini with embedded mandelbrot generation program, compiled as an app (VM running VM)
Quickstart (docker)
make dockerbuild
make dockershell
Then, from inside the docker shell
make
./hosts/host/host apps/helloworld/helloworld.bin
host is the command line test VM for running samples. Run host -h for a full list of options.
More information
Start at doc/README.md
License
This project is licensed under the MIT License. Feel free to use in research, products and embedded devices.