VML 上游项目:22 语言前端 × 17 目标后端

你手上这份都来码里随包附带的 VML,是上游工程 VML Toolchain 的精简发行版。 这一页讲的是上游那一份的完整样子 —— 它本身是一套「多前端 → 统一 IR → 多后端」的编译器工具链, 可以脱离都来码,在桌面上独立使用。

它和都来码里那份是什么关系

一句话:同一个项目,两个发行范围。上游是完整工程(含桌面 IDE 与硬件模拟器), 都来码里那份是按「在手机与桌面进程内跑得起来」这个目标裁过的子集。

都来码随包内置的那份上游 VML Toolchain
引入方式源码复制(vendoring)进本仓库,进程内调用独立仓库,自己构建、自己发行
汇编器 / 虚拟机 / 22 语言前端有有
17 个后端翻译器有(编译依赖,不是可选装饰)有
标准库与示例有(标准库完整;示例留精选子集)有(示例是完整目录)
桌面 IDE没有有(VMLIde)
硬件模拟器没有有(ConsoleEmulator / FullDevicesEmulator)
文档站与网页 Demo没有有(本页内容即整理自它)

裁掉的都不影响「写一段代码、编出来、在手机上跑起来」这条主线 —— 它们是桌面上的调试与演示设施。 反过来说,上游能做的事比随包那份多:想跑老机器模拟器、想烧到单片机上、想用桌面 IDE, 就去上游。

口径说明:上游首页写的是「22 种语言 × 18 种架构」,但枚举出来是 17 个后端 (本仓库里 third_party/vml 下也是 17 个:8 位 5 个、16 位 2 个、32 位 7 个、虚拟机 3 个)。 本页按枚举列,写 17 个。

它是什么

一套编译器的总线:前端把各种语言编到同一个中间表示,后端再把中间表示翻到各种架构。 写 C / Python / Rust / Go / Java … 任意一门,编译成 VML 汇编,再翻译到 6502 / ARM / RISC-V / x86 … 任意一个目标。

源代码  .c .bas .pas .py .lua .fth .rs .go .ld .java .js .cs
        .swift .cpp .kt .scm .rb .dart .m .r .d .f90

   ↓  22 个前端编译器

统一 IR   VML 汇编 (.vml) → VMB 二进制 (.vmb)

   ↓  17 个后端翻译器

目标平台  6502 / Z80 / 8051 / AVR / PIC / MSP430 / PIC24 / ARM-CM
          x86 / MIPS / RISC-V / 68000 / PowerPC / SPARC / JVM / .NET / Wasm

一个贯穿全项目的约定:所有语言的编译命令是同一条,编译器按文件扩展名自动选择。 学一次命令行,22 门语言通用。

vmltool hello.c  -o hello.vml
vmltool -r hello.vml

22 门语言前端

每门语言一个独立的前端编译器,都走同一条四阶段流水线: 词法分析 → 语法分析 → 代码生成 → VML 汇编输出。 其中 16 个达到生产可用(完成度 90% 以上),6 个为新增的教学可用级别。

语言扩展名完成度调用约定
C.c~96%cdecl
Forth.fth~94%栈式
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

前端之间共享代码:所有语言共用词法分析基类与代码生成基类;C 系语言(C / Go / Rust / JavaScript) 共用表达式与类型系统那一层;面向对象语言(C++ / Java / C# / Swift / Kotlin)再共用一个类、 继承与虚函数那一层。

四种调用约定(cdecl / CCv2 / pascal / stdcall)决定了参数怎么传、栈由谁清 —— 22 门语言彼此可以互相调用,靠的就是这一层约定对齐。

17 个目标后端

翻译是四段:解析 VML 指令 → 指令与寄存器映射 → 生成目标汇编 → 格式化产物(HEX / BIN / ELF / S19)。 8 位架构用零页内存模拟寄存器,ARM Cortex-M 与 RISC-V 可以做到 1:1 直接映射。

字长架构典型去处
8 位6502Apple II / C64 / NES
8 位Z80CP/M / ZX Spectrum / MSX
8 位8051嵌入式 MCU 经典
8 位AVRArduino / ATmega328P
8 位PICMicrochip PIC16F84A
16 位MSP430TI 超低功耗
16 位PIC24Microchip 16 位
32 位ARM Cortex-MSTM32 / Cortex-M
32 位x86IBM PC / 通用计算
32 位MIPSPIC32 / 网络设备
32 位RISC-VSiFive / 开源 ISA
32 位68000Amiga / Atari ST
32 位PowerPCXilinx PPC405 / 游戏机
32 位SPARCLEON3 / 航天级
虚拟机JVMJava 字节码
虚拟机.NETCLR / CIL
虚拟机Wasm浏览器 / WebAssembly

烧录用的是一个一站式命令,从源码一路到固件:

vml2hex blink.c -arch arm-cm -f hex -o blink.hex

三个优化档:-O0 不优化(调试)、-O1 死代码消除 + 常量折叠(默认)、 -O2 再加复制传播与循环优化(发布)。 六种输出格式:Intel HEX(默认)、Binary、Motorola S19、ELF、DOS EXE、Text Dump。

运行时与模拟器

同一份 VMB 字节码,有多个运行时可以执行它 —— 从「跑得快」到「跑在老机器上」都覆盖:

运行时语言特点
VMLRuntimeC# (.NET)核心执行引擎:92 条指令、16 个通用寄存器 R0–R15、8 个浮点 F0–F7、4 个双精度 D0–D3、30 多个系统调用、中断与异常(CLI / STI / IRET、TRY / CATCH)、MMIO 设备模拟、VMB 加载与保存
VMLRuntimeCC99跨平台原生运行时,零依赖、约 100 KB:92 / 92 个操作码与 C# 运行时逐号对齐,VGA 80×25 文本渲染、PC 扬声器模拟与 WAV 录音,可静态链接成单文件
ConsoleEmulatorC# (.NET)终端模拟器:VGA 文本模式、按键脚本自动化,可模拟六种老机器(Apple II / Commodore 64 / ZX Spectrum / MSX / Amstrad CPC / Commodore PET)
FullDevicesEmulatorC# (Avalonia)图形界面的全设备模拟器:设备可视化、实时状态面板
VMLIdeC# (Avalonia)跨平台桌面 IDE(约 70% 完成):代码编辑、断点单步与寄存器查看、设备可视化、AI 辅助面板

内存布局是固定的一段 32 位地址空间:代码段从 0x000000 起、数据段从 0x000400 起、 堆从 0x008000 向上增长、栈从顶部向下增长;0xE000 是 FFI 函数指针表、 0xF000 是 MMIO 设备寄存器。

CLI 工具集

工具做什么
VMLTool主工具,GCC 风格命令行,按扩展名自动选编译器;一条命令完成「编译 → 运行」,也能只生成汇编、只做预处理
VMLAssembler汇编器与反汇编器:VML 汇编文本 ↔ VMB 二进制,两个方向都通;标签解析、伪指令、表达式求值、库链接都由它做
VMLToHex编译 + 链接 + 翻译 + 产物格式化:17 个目标架构、6 种输出格式,-O0/-O1/-O2 三档优化
VMLPacker打包成独立可执行文件,可跨平台交叉打包;也有 C 语言版本,静态编译后体积更小
GenLib从 C 源码自动编译 VML 静态共享库,并为 22 种语言生成模块包装与绑定代码
GenDyn扫描 .dylib / .so / .dll 的导出符号,生成 FFI 包装器与 22 种语言调用绑定
GenDev从 XML 设备描述文件自动生成 22 种语言的设备驱动代码(寄存器地址、外设初始化函数)

生成器这一组有个共同的设计:一次生成,22 种语言同时到手。 写一个 C 库,GenLib 会替你把别的 21 种语言的调用方式也生成出来。

FFI 外部函数接口

VML 程序能调原生动态库 —— 而且22 门语言都用自己那门语言的原生语法写,不用手写汇编。 调用链是四个系统调用接起来的:

系统调用编号做什么
dl_open#370加载动态库,返回句柄
dl_sym#371按名字查找函数,返回函数指针编号
dl_close#372卸载动态库
native_call#373调用原生函数(整数 / 指针参数,最多 8 个)
native_call_0#374调用无参原生函数
native_call_f#375调用原生函数(浮点参数,为 OpenGL 那一类接口准备)

上游的完整示例是同一个 OpenGL 旋转立方体,用 22 种语言各写一遍, 全部调用同一个 VML 封装库、同一个原生动态库。每种语言的主循环都长这样:

fn main() {
    gl_setup();
    while gl_should_close() == 0 {
        gl_frame();
    }
    gl_teardown();
}

跨语言调用有一个细节值得一提:不同语言的符号命名习惯不一样(Lua 加前缀、Forth 加前缀、 Pascal 与 C# 需要先声明),封装库把各种别名一并导出,所以每种语言都能用最自然的那种写法调用。

OS 模式与 MCU 模式

同一个编译器有两副面孔,由模式开关决定生成什么:

OS 模式MCU 模式
产物桌面应用零依赖裸机固件,一路到 HEX 烧录
动态库加载有(FFI)没有
图形OpenGL 那一套没有
C 标准库完整BIOS 保留 malloc 与文件 I/O
跳过的部分—异步、线程、垃圾回收

这不是"两个项目共用一套代码",而是同一套前端、同一份标准库,只换后端与运行时 —— 程序写得对,两个模式都能编过。

从哪开始

📘 本站内置的 VML

都来码里那份是精简发行版:装完就能编译运行 22 门语言,示例与游戏随包附带,全部离线。

读 VML 介绍

🧭 想写第一个程序

从「写第一个程序」到「画图 / 写游戏」,一步步来,代码可以直接粘贴运行。

读 VML 教学

🔗 上游仓库

完整源码、更新日志、路线图与文档站都在上游。22 种语言 × 多架构的完整说明也在那里。

打开上游仓库

📥 把程序跑到手机上

手机端点开「文件」页就有示例目录,装完即可编译运行。

去下载都来码


上游构建与第一个程序(在上游仓库里执行):

git clone https://gitee.com/aleckstygit/vml.git
dotnet build VMLToolchain.sln
vmltool hello.c -o hello.vml
vmltool -r hello.vml

本页内容整理自上游项目自己的介绍页(web/ 目录), 数字与表格均照其原文;「17 个后端」是按枚举口径的更正,未经上游确认。 上游许可:MIT License。都来码随包内置的那份以源码复制方式并入本仓库的 third_party/vml/,本地适配说明见该目录下的 README。