VML upstream: 22 language front ends × 17 backends
The VML bundled with your copy of Dolaima is a trimmed-down distribution of the upstream project VML Toolchain. This page is about the upstream project in full — it is a “many front ends → one IR → many backends” compiler toolchain that stands on its own and runs on the desktop without Dolaima.
What this page covers
How it relates to the copy inside Dolaima
In one line: one project, two shipping scopes. Upstream is the complete project (desktop IDE and hardware emulators included); the copy inside Dolaima is the subset trimmed to the goal “compile and run in-process on phone and desktop”.
| The copy bundled with Dolaima | Upstream VML Toolchain | |
|---|---|---|
| How it gets in | Source copy (vendoring) into this repository, called in-process | A repository of its own, built and released on its own |
| Assembler / VM / front ends | Yes (27) | Yes (22) |
| 17 backend translators | Yes (a build dependency, not decoration) | Yes |
| Standard library and samples | Library complete; samples are a selected subset | Yes (the full sample tree) |
| Desktop IDE | No | Yes (VMLIde) |
| Hardware emulators | No | Yes (ConsoleEmulator / FullDevicesEmulator) |
| Documentation site and web demos | No | Yes (this page is distilled from it) |
Nothing that was dropped touches the main line of “write code, compile it, run it on your phone” — what was dropped is desktop debugging and demonstration machinery. The flip side: upstream can do more than the bundled copy. To drive an old machine emulator, burn to a microcontroller, or use the desktop IDE, go upstream.
On the numbers: the upstream home page says “22 languages × 18 architectures”, but the list it enumerates has 17 backends (the copy in this repository holds 17 as well: 5 for 8-bit, 2 for 16-bit, 7 for 32-bit and 3 virtual machines). This page counts what is enumerated, so it says 17.
What it is
A bus for compilers: the front ends compile each language into one intermediate representation, and the backends translate that representation onto each architecture. Write it in C, Python, Rust, Go or Java — compile to VML assembly, then translate to 6502, ARM, RISC-V, x86 or any other target.
source .c .bas .pas .py .lua .fth .rs .go .ld .java .js .cs
.swift .cpp .kt .scm .rb .dart .m .r .d .f90
↓ 22 front-end compilers
one IR VML assembly (.vml) → VMB binary (.vmb)
↓ 17 backend translators
targets 6502 / Z80 / 8051 / AVR / PIC / MSP430 / PIC24 / ARM-CM
x86 / MIPS / RISC-V / 68000 / PowerPC / SPARC / JVM / .NET / Wasm
One convention runs through the whole project: every language is compiled with the same command, and the compiler is picked by file extension. Learn the command line once, use it for all 22.
vmltool hello.c -o hello.vml
vmltool -r hello.vml
22 language front ends
Each language has its own front-end compiler, and all of them run the same four-stage pipeline: lexing → parsing → code generation → VML assembly. Sixteen are production-grade (90% or more complete); six are newer and aimed at teaching.
| Language | Extension | Complete | Calling convention |
|---|---|---|---|
| C | .c | ~96% | cdecl |
| Forth | .fth | ~94% | stack |
| JavaScript | .js | ~94% | stdcall |
| C++ | .cpp | ~93% | cdecl |
| Rust | .rs | ~93% | stdcall |
| Kotlin | .kt | ~93% | stdcall |
| Scheme | .scm | ~93% | stdcall |
| Ladder | .ld | ~93% | — |
| BASIC | .bas | ~92% | CCv2 |
| Pascal | .pas | ~92% | pascal |
| Lua | .lua | ~91% | stdcall |
| Go | .go | ~90% | stdcall |
| Python | .py | ~90% | stdcall |
| C# | .cs | ~90% | stdcall |
| Java | .java | ~90% | stdcall |
| Swift | .swift | ~90% | stdcall |
| Ruby | .rb | ~85% | stdcall |
| Dart | .dart | ~80% | stdcall |
| Objective-C | .m | ~80% | cdecl |
| R | .r | ~80% | stdcall |
| D | .d | ~80% | stdcall |
| Fortran | .f90 | ~80% | stdcall |
The front ends share code: every language uses the common lexer base and code-generator base; the C-like languages (C, Go, Rust, JavaScript) share the expression and type-system layer; the object-oriented ones (C++, Java, C#, Swift, Kotlin) share another layer for classes, inheritance and virtual functions.
Four calling conventions (cdecl, CCv2, pascal, stdcall) decide how arguments travel and who cleans the stack. That layer is what lets the 22 languages call each other.
17 target backends
Translation has four stages: parse VML instructions → map instructions and registers → emit target assembly → format the artifact (HEX / BIN / ELF / S19). The 8-bit targets emulate registers in zero-page memory; ARM Cortex-M and RISC-V map 1:1.
| Width | Architecture | Typically found in |
|---|---|---|
| 8-bit | 6502 | Apple II / C64 / NES |
| 8-bit | Z80 | CP/M / ZX Spectrum / MSX |
| 8-bit | 8051 | Classic embedded MCUs |
| 8-bit | AVR | Arduino / ATmega328P |
| 8-bit | PIC | Microchip PIC16F84A |
| 16-bit | MSP430 | TI ultra-low power |
| 16-bit | PIC24 | Microchip 16-bit |
| 32-bit | ARM Cortex-M | STM32 / Cortex-M |
| 32-bit | x86 | IBM PC / general computing |
| 32-bit | MIPS | PIC32 / networking gear |
| 32-bit | RISC-V | SiFive / open ISA |
| 32-bit | 68000 | Amiga / Atari ST |
| 32-bit | PowerPC | Xilinx PPC405 / consoles |
| 32-bit | SPARC | LEON3 / space-grade |
| VM | JVM | Java bytecode |
| VM | .NET | CLR / CIL |
| VM | Wasm | Browser / WebAssembly |
Burning firmware is one command, from source all the way to an image:
vml2hex blink.c -arch arm-cm -f hex -o blink.hex
Three optimisation levels: -O0 none (debugging), -O1 dead-code elimination plus
constant folding (the default), -O2 adding copy propagation and loop optimisation (release).
Six output formats: Intel HEX (default), Binary, Motorola S19, ELF, DOS EXE and Text Dump.
Runtimes and emulators
The same VMB bytecode runs on several runtimes — covering everything from “as fast as possible” to “on an old machine”:
| Runtime | Language | Notes |
|---|---|---|
| VMLRuntime | C# (.NET) | The core execution engine: 92 instructions, 16 general registers R0–R15, 8 float registers F0–F7, 4 doubles D0–D3, 30-plus syscalls, interrupts and exceptions (CLI / STI / IRET, TRY / CATCH), MMIO device emulation, VMB load and save |
| VMLRuntimeC | C99 | A native cross-platform runtime, dependency-free at roughly 100 KB: all 92 of 92 opcodes aligned one-for-one with the C# runtime, VGA 80×25 text rendering, PC speaker emulation and WAV recording, statically linkable into a single file |
| ConsoleEmulator | C# (.NET) | Terminal emulator: VGA text mode, scripted keystrokes, and emulation of six classic machines (Apple II / Commodore 64 / ZX Spectrum / MSX / Amstrad CPC / Commodore PET) |
| FullDevicesEmulator | C# (Avalonia) | A graphical full-device emulator: device visualisation and live status panels |
| VMLIde | C# (Avalonia) | A cross-platform desktop IDE (about 70% complete): editing, breakpoints, stepping and register inspection, device visualisation, an AI panel |
Memory is a fixed 32-bit address space: code from 0x000000, data from 0x000400,
the heap growing up from 0x008000 and the stack growing down from the top. 0xE000
holds the FFI function pointer table and 0xF000 the MMIO device registers.
The CLI toolchain
| Tool | What it does |
|---|---|
| VMLTool | The main tool: a GCC-style command line that picks the compiler by extension and can compile-and-run in one go, or stop at assembly, or preprocess only |
| VMLAssembler | Assembler and disassembler: VML text ↔ VMB binary, both directions; label resolution, directives, expression evaluation and library linking all live here |
| VMLToHex | Compile, link, translate and format: 17 target architectures, 6 output formats, three optimisation levels |
| VMLPacker | Packs a program into a standalone executable, and can cross-pack for other platforms; a C version produces smaller static binaries |
| GenLib | Compiles C sources into a VML static shared library and generates the module wrappers and bindings for all 22 languages |
| GenDyn | Scans the exported symbols of a .dylib / .so / .dll and generates the FFI wrapper plus call bindings for 22 languages |
| GenDev | Generates device driver code for 22 languages from an XML device description (register addresses, peripheral init functions) |
The generators share one idea: generate once, get all 22 languages. Write a C library and GenLib produces the calling convention for the other 21 languages too.
FFI, the foreign function interface
A VML program can call native shared libraries — and all 22 languages write it in their own native syntax, with no hand-written assembly. Four syscalls carry the call chain:
| Syscall | Number | What it does |
|---|---|---|
dl_open | #370 | Load a shared library, return a handle |
dl_sym | #371 | Look a function up by name, return a function pointer id |
dl_close | #372 | Unload the library |
native_call | #373 | Call a native function (integer / pointer arguments, up to 8) |
native_call_0 | #374 | Call a native function with no arguments |
native_call_f | #375 | Call a native function with float arguments — built for OpenGL-style interfaces |
The upstream showpiece is the same rotating OpenGL cube written 22 times, every version calling the same VML wrapper and the same native library. Each language's main loop looks like this:
fn main() {
gl_setup();
while gl_should_close() == 0 {
gl_frame();
}
gl_teardown();
}
One detail is worth knowing: naming habits differ between languages (Lua and Forth add a prefix, Pascal and C# need a forward declaration), so the wrapper exports every alias at once and each language can use its most natural spelling.
OS mode and MCU mode
The same compiler wears two faces, chosen by the mode switch:
| OS mode | MCU mode | |
|---|---|---|
| Output | A desktop application | Dependency-free bare-metal firmware, all the way to a burnable HEX |
| Dynamic library loading | Yes (FFI) | No |
| Graphics | The OpenGL stack | No |
| C standard library | Complete | The BIOS keeps malloc and file I/O |
| Left out | — | Async, threads, garbage collection |
This is not two projects sharing a file here and there: it is the same front ends and the same standard library, with a different backend and runtime. Code that is written correctly compiles in both modes.
Where to start
📘 The VML inside Dolaima
The bundled copy is the trimmed distribution: 27 languages compile and run as soon as you install, with samples and games included, all offline.
🧭 Write your first program
From “hello” to drawing windows and writing a small game, step by step, with code you can paste and run.
🔗 The upstream repository
The full sources, changelog, roadmap and documentation live upstream — including the complete 22 languages × many architectures write-up.
📥 Run it on your phone
On mobile, open the Files tab and the sample folder is already there, ready to compile and run.
Building upstream and running the first program (inside the upstream repository):
git clone https://gitee.com/aleckstygit/vml.git
dotnet build VMLToolchain.sln
vmltool hello.c -o hello.vml
vmltool -r hello.vml
This page is distilled from the upstream project's own introduction pages (its web/
directory); the figures and tables follow its wording. The “17 backends” count is our own enumeration rather
than a figure confirmed upstream. Upstream licence: MIT License. The copy bundled with Dolaima is
vendored into third_party/vml/ in this repository; the local adaptations are documented in the
README there.
Dolaima