VML:随包附带的虚拟机与 22 门语言

VML 是都来码内置的一套轻量虚拟机与编译器工具链:把 22 门语言编译成它自己的字节码, 在手机和桌面上离线运行。它不下载任何可执行代码,也不依赖系统里已有的编译器 —— 编译器本身就在包里。

它是什么

一句话:它是一台虚拟机 + 一整套编译器,随安装包一起发到你的机器上。

  • 一台虚拟机:装载并执行 VML 字节码(完整 CPU 模拟、MMIO、系统调用)。
  • 一套编译器:22 门语言 → VML 汇编 → VML 字节码。编译全部在本机完成。
  • 离线:装完就能编译运行。示例程序与游戏随包附带,不需要联网、不需要 SDK、不需要编译器。

它与「把一门语言的官方工具链装进手机」是两回事:VML 有自己的指令集、自己的标准库、自己的 ABI, 22 门语言只是不同的写法 —— 它们最后都编到同一个后端,共用同一份运行时与同一套 UI 接口。

手机与桌面同一套

同一份源码、同一个前端编译器、同一台虚拟机。你在电脑上验证过的程序,手机上直接跑。

进程内调用,不是外挂一个 exe

手机端是把 VML 的源码(以源码复制的形式)并入安装包的,在进程内调用它 —— 不是把编译器编成可执行文件丢给 shell 去跑。

为什么必须这样:iOS 的沙箱物理拒绝 fork/exec(自己启动子进程这条路永远走不通), Android 10+ 的 W^X 让应用私有目录里的文件不可执行。把托管代码直接链进 App, 这三条限制就都不存在了。

由什么组成

部件做什么
虚拟机装载并执行字节码:完整 CPU 模拟 + MMIO + 系统调用
汇编器VML 汇编文本 ↔ VML 字节码,两个方向都通
22 个语言前端把 22 门语言的源码编译成 VML 汇编
18 个后端翻译器把 VML 转译成真实硬件架构与虚拟机目标的汇编
共享库跨语言通用的一套标准库,22 门语言共用同一份实现
绘图与 UI 接口文字模式 tty_*、图像模式 ui_*,以及给老程序用的兼容层
示例与游戏examples/ 下按语言分目录,装完就能编译运行

22 个语言前端

每种语言一个独立的前端编译器,全部标记为生产可用:

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(梯形图)

其中两个还带方言:BASIC 10 个方言(QBasic / TurboBasic / FreeBasic / TrueBasic / PureBasic / ChipBasic / MiniBasic / GW-BASIC / PowerBASIC / VisualBasic)、Pascal 6 个方言 (Turbo Pascal / Delphi / FreePascal / ISO / UCSD / Oberon)。

共享库也是共用的:三层字符串(string / wstring / ustring)、 格式化输出与输入(printf / scanf 及宽字符变体)、表达式求值 (parserexp / parserexpf)、数学与浮点(math / float / softdouble / softfloat / softint64), 以及 os / file / network / memory / device / convert。一门语言写好的库,别的语言直接调。

18 个后端翻译器

VML 不止能自己跑,还能被转译成真实硬件的汇编:

字长目标
8 位6502(Apple II / C64 / NES)、Z80(CP/M / ZX Spectrum)、8051、AVR(Arduino / ATmega)、PIC
16 位MSP430、PIC24
32 位ARM Cortex-M(STM32)、x86、MIPS、RISC-V、68000、PowerPC、SPARC
虚拟机JVM、.NET(CLR)、Wasm(浏览器)

绘图接口的分层:tty_* 与 ui_*

库按用途分了四层,互不越界 —— 新写的程序只该用前两层:

用途库说明
新版 · 文字模式tty_*只有字符、没有任何图形功能;坐标 1 起算、16 色索引色
新版 · 图像模式ui_*开窗、图元绘制、触摸与按键消息、定时器、音效、存档
旧程序 · 文字conio / crt兼容层
旧程序 · 图形graphics(BGI)兼容层;它转发到 ui_*,不模拟显存

判据很直白:新写的程序不要直接用 conio / crt / graphics —— 那是给老程序补环境的兼容层,不是新 API。手机上跑的游戏与示例,全部走 ui_*。

为什么有用

📱

手机也能真编程

手机端自带编辑器与编译运行。examples/ 下的示例与游戏开箱即用, vml run examples/c/tetris.c 就能跑起来。

🖥️

桌面验证、手机运行

桌面上同一条编译链,秒级迭代 —— 先在电脑上把语法和逻辑验完,手机上只做最后一步。

🕰️

老程序能跑

DOS / Unix 时代的文本程序大多一行不改就能编过并跑起来:经典终端时钟 tty-clock(689 行)源码零修改编译 + 运行通过,动画火车 sl(295 行) 全程动画,cmatrix(894 行)已能画出矩阵。

老程序的兼容性不按「能 / 不能」二分,而是分四档:

档类型可行性
A纯字符界面(curses / conio / ANSI 转义)高 —— 大头,且已经跑通
B用了 termios / ioctl 的终端控制中
Cx86 专属(DOS 内存模型、bios.h、int86、显存 0xB8000)低,且不该做 —— 范围外,不是"还没做"
D图形(BGI graphics.h / Allegro)BGI 已做垫层(转发到 ui_*);Allegro / SDL 不做

有意不支持的边界

下面这些不是"还没做",而是有意不做(写进文档的定案):

形态例子
直接操作汇编asm / __asm__ 块、__emit__、寄存器伪变量 _AX / _BX / _CX / _DX
直接操作内存显存直写(文本 0xB8000 / 图形 0xA0000)、bios.h、int86、peek / poke
裸硬件interrupt / _interrupt 中断处理程序、端口 inp / outp、BASIC 的 DEF SEG

理由是语义对不上:那些程序的语义是"往一块固定地址的显存写、宿主把它贴到屏幕上", 而本平台的目标形态是"程序调宿主接口画"。这是两套并行的绘图面, 而固定地址(显存、端口、中断向量)在手机上根本没有对应物。

这类东西不能被当成"透明的空白"混过去 —— 丢掉它们会静默编错, 程序看起来能跑、结果不对,比直接编不过难查得多。该改的是程序自己(把显存直写改成 ui_*、 把 asm 段改写成高级语言),不是把它们从语法里抹掉。

同样有意不做的还有:

  • windows.h(Win32 图形界面)—— 映射不到手机上;
  • gfx_* / 显存那一整套 —— 见上;
  • system() —— 本平台没有 shell。与其假装支持(system("copy a b") 会拿到"成功"却什么也没发生),不如让它编译期就找不到符号;
  • Allegro / SDL —— 它们自带音频、输入、定时器一整套运行时,那已经不是"补环境"而是"移植一个平台";
  • 鼠标中断(INT 33h)、BGI 的驱动加载模型(registerbgidriver / 字体文件 / setviewport)。

口径是统一的:缺了就是编译期找不到符号,不假装支持。 "编得过、跑起来什么也没有"比"编不过"难查得多。

从哪开始

🧭 从零上手

在哪跑、第一个程序、寄存器为什么写 @、怎么开窗画图、怎么起一个游戏骨架 —— 每一步都能照着做,代码可以直接粘贴运行。

读 VML 教学

📥 先装上

手机端点开「文件」页就有 examples/;桌面端下载解压即可用。

去下载

顺带一提:编辑器和 C 语言与 VML 汇编的编译运行是免费用的; 「全能版」是一次性内购(非订阅),解锁全部 22 门语言的编译器与优化器。