VML: the virtual machine and 22 languages that ship in the box
VML is the lightweight virtual machine and compiler toolchain built into Dolaima. It compiles 22 languages into its own bytecode and runs them on phone and desktop offline. It downloads no executable code and does not depend on any compiler already installed on your system — the compilers are inside the package.
What this page covers
What it is
In one sentence: a virtual machine plus a complete set of compilers, shipped with the installer to your machine.
- A virtual machine: loads and executes VML bytecode (full CPU simulation, MMIO, system calls).
- A set of compilers: 22 languages → VML assembly → VML bytecode. All compilation happens locally.
- Offline: it works the moment it is installed. The sample programs and games come with the package. No network, no SDK, no compiler needed.
It is not the same thing as "putting a language's official toolchain on a phone": VML has its own instruction set, its own standard library and its own ABI. The 22 languages are just different ways of writing code — they all compile to the same back end and share the same runtime and the same UI interfaces.
The same stack on phone and desktop
The same source file, the same front-end compiler, the same virtual machine. A program you verified on your computer runs directly on the phone.
Called in-process, not bolted on as an exe
On mobile, VML's source is vendored into the app and called in-process — the compiler is not built into an executable and handed to a shell.
Why it has to be that way: the iOS sandbox physically refuses fork/exec (starting your
own child process is never going to work there), and Android 10+ W^X makes files in an app's private
directory non-executable. Link the managed code straight into the app and all three limits
disappear.
What it is made of
| Part | What it does |
|---|---|
| Virtual machine | Loads and executes bytecode: full CPU simulation + MMIO + system calls |
| Assembler | VML assembly text ↔ VML bytecode, both directions |
| 22 language front ends | Compile source in 22 languages into VML assembly |
| 18 back-end translators | Translate VML into real hardware architectures and VM targets |
| Shared library | One cross-language standard library, shared by all 22 languages |
| Drawing and UI interfaces | Text mode tty_*, graphics mode ui_*, plus compatibility layers for old programs |
| Samples and games | Under examples/, one directory per language — compile and run them right away |
The 22 language front ends
Each language has its own front-end compiler, and all of them are marked production-ready:
C (C99)
.c
C++
.cpp
Python
.py
Go
.go
Rust
.rs
Java
.java
C#
.cs
JavaScript
.js
Swift
.swift
Kotlin
.kt
Dart
.dart
Ruby
.rb
Lua
.lua
Scheme
.scm
Forth
.fth / .fs
Fortran
.f90
Pascal
.pas
BASIC
.bas
Objective-C
.m
D
.d
R
.r
Ladder
.ld (ladder logic)
Two of them also come with dialects: 10 BASIC dialects (QBasic / TurboBasic / FreeBasic / TrueBasic / PureBasic / ChipBasic / MiniBasic / GW-BASIC / PowerBASIC / VisualBasic) and 6 Pascal dialects (Turbo Pascal / Delphi / FreePascal / ISO / UCSD / Oberon).
The standard library is shared too: three layers of strings (string / wstring /
ustring), formatted output and input (printf / scanf plus their
wide-character variants), expression evaluation (parserexp / parserexpf),
maths and floating point (math / float / softdouble /
softfloat / softint64), and os / file /
network / memory / device / convert.
A library written in one language can be called from the others.
The 18 back-end translators
VML does not only run by itself — it can also be translated into the assembly of real hardware:
| Word size | Targets |
|---|---|
| 8-bit | 6502 (Apple II / C64 / NES), Z80 (CP/M / ZX Spectrum), 8051, AVR (Arduino / ATmega), PIC |
| 16-bit | MSP430, PIC24 |
| 32-bit | ARM Cortex-M (STM32), x86, MIPS, RISC-V, 68000, PowerPC, SPARC |
| Virtual machines | JVM, .NET (CLR), Wasm (browsers) |
How the drawing interfaces are layered: tty_* and ui_*
Libraries are split into four layers and do not cross into each other. New programs should only use the first two:
| Purpose | Library | Notes |
|---|---|---|
| New · text mode | tty_* | Characters only, no graphics at all; coordinates start at 1, 16 indexed colours |
| New · graphics mode | ui_* | Windows, primitives, touch and key messages, timers, sound, saved data |
| Old programs · text | conio / crt | Compatibility layer |
| Old programs · graphics | graphics (BGI) | Compatibility layer; it forwards to ui_* and does not simulate video memory |
The rule is simple: a newly written program should not use conio /
crt / graphics directly — those are compatibility layers that fill in the
environment for old programs, not new APIs. Every game and sample running on mobile goes through
ui_*.
Why it is useful
Real programming on a phone
The mobile app ships an editor and can compile and run. The samples and games under
examples/ work out of the box — vml run examples/c/tetris.c and it plays.
Verify on desktop, run on mobile
The desktop uses the same compile chain, with second-long iteration — get the syntax and the logic right on your computer, then do the last step on the phone.
Old programs still run
Most text programs from the DOS and Unix era compile and run without a single line changed: the
classic terminal clock tty-clock (689 lines) compiles and runs unmodified, the animated
train sl (295 lines) animates end to end, and cmatrix (894 lines) draws its
matrix.
Old-program compatibility is not a binary "yes / no" but four tiers:
| Tier | Kind | Feasibility |
|---|---|---|
| A | Pure character interfaces (curses / conio / ANSI escapes) | High — the bulk of them, and already working |
| B | Terminal control using termios / ioctl | Medium |
| C | x86-specific (DOS memory model, bios.h, int86, video memory 0xB8000) | Low, and should not be done — out of scope, not "not yet done" |
| D | Graphics (BGI graphics.h / Allegro) | BGI has a shim (it forwards to ui_*); Allegro / SDL are not done |
The boundaries it deliberately does not cross
The following are not "not yet done" but deliberately not done (these are settled decisions, written down in the project's own documentation):
| Shape | Examples |
|---|---|
| Manipulating assembly directly | asm / __asm__ blocks, __emit__, register pseudo-variables _AX / _BX / _CX / _DX |
| Manipulating memory directly | Writing video memory directly (text 0xB8000 / graphics 0xA0000), bios.h, int86, peek / poke |
| Bare hardware | interrupt / _interrupt handlers, ports inp / outp, BASIC's DEF SEG |
The reason is that the semantics do not translate: those programs mean "write to a block of memory at a fixed address and let the host blit it to the screen", whereas this platform's model is "the program calls host interfaces to draw". Those are two parallel drawing surfaces — and fixed addresses (video memory, ports, interrupt vectors) simply have no counterpart on a phone.
Such things cannot be treated as "transparent blanks" and quietly skipped — dropping them
compiles silently wrong, and a program that looks like it runs but gives the wrong result is far
harder to diagnose than one that fails to compile. What has to change is the program itself (write to
ui_* instead of video memory, rewrite the asm block in a high-level language),
not the grammar.
Also deliberately not done:
windows.h(Win32 GUI) — it maps to nothing on a phone;- the whole
gfx_*/ video-memory family — see above; system()— this platform has no shell. Rather than pretend (system("copy a b")would report success while nothing happened), it is better for the symbol to be missing at compile time;- Allegro / SDL — they carry their own audio, input and timer runtime; that is no longer "filling in an environment" but "porting a platform";
- mouse interrupts (INT 33h) and BGI's driver-loading model (
registerbgidriver/ font files /setviewport).
The rule is consistent throughout: if it is missing, the symbol is missing at compile time — there is no pretending. "It compiles but shows nothing" is far harder to diagnose than "it does not compile".
Where to start
🧭 Start from zero
Where to run it, your first program, why registers are written with @, how to open a window
and draw, and how to set up a game skeleton — every step can be followed, and the code can be pasted
and run as-is.
📥 Install it first
On mobile, open the Files page and examples/ is already there. On desktop, download, unzip
and run.
One more thing: the editor and the ability to compile and run C and VML assembly are free. The "Full Edition" is a one-time in-app purchase (not a subscription) that unlocks the compilers for all 22 languages and the optimiser.
Dolaima 都来码