C
最完整的一条路:绘图、输入、音效、存档、手柄全都用得上。仓库里那几个完整的游戏(俄罗斯方块、五子棋、吃豆人)都是 C 写的。
如果你不确定用哪门语言 —— 用 C。
在手机上怎么跑
vml run examples/c/test/sysinfo.c
编译要等一会儿(C 那种要一两分钟,脚本类语言几秒)。程序跑起来后屏幕底部就是手柄,方向键 + 四个动作键都在;点右上角返回可以回到命令行。
写法要点
- 引
#include <vml_ui.h>就有全部接口声明 - 颜色一律
0xAARRGGBB(alpha 在最前) - 数组别越界(报「内存错误」十有八九是它)
示例
| 文件 | 演示什么 |
|---|---|
app/gomoku.c | 五子棋(触摸落子、人机对战、只竖屏 + 不要手柄区) |
app/mario.c | 横版跳跃 |
app/pacman.c | 吃豆人(网格地图 + 追击 AI) |
app/starfall.c | 竖版弹幕 |
app/tetris.c | 俄罗斯方块(计分、等级、重开、音效) |
test/sysinfo.c | 调 ui_call_json("sysinfo") 打印设备信息 |
ui/draw_prims.c | 图元体检:每种绘图指令各画一格 |
同目录还有:3d/Avocado.glb、3d/ToyCar.glb、3d/air3d.c、3d/bench3d.c、3d/blend_demo.c、3d/cube.obj、3d/cube_min.c、3d/foxcatch.c、3d/g3_all.c、3d/gamma_demo.c、3d/glbview.c、3d/gltf_demo.c、3d/landscape.c、3d/light_demo.c、3d/normalmap_demo.c、3d/objload.c、3d/ortho_demo.c、3d/pbr_demo.c、3d/pool3d.c、3d/race3d.c、3d/rover3d.c、3d/rover_cutout.glb、3d/shadow_demo.c、3d/shadowmap_demo.c、3d/skybox_radius.c、3d/snake3d.c、3d/solar3d.c、3d/sphere.obj、3d/tank3d.c、3d/tex_demo.c、3d/torus.obj、3d/vml3d_tri.c、app/battery.c、app/calc.c、app/calendar.c、app/chess.c、app/clipboard.c、app/filedlg.c、app/gallery.c、app/gyro.c、app/level.c、app/locate.c、app/matrix_rain.c、app/notepad.c、app/piano.c、app/plane.c、app/stock.c、app/stocks.txt、app/stopwatch.c、app/tilt.c、app/todo.c、app/weather.c、app/weather.txt、bgi/demo_bgi.c、bgi/test_bgi.c、c.bat、demo/audio_all.c、demo/audio_steps.c、demo/demo_std.c、old/old_gfx_anim.c、old/old_gfx_bgi.c、old/old_gfx_lines.c、old/old_gfx_palette.c、old/old_gfx_plot.c、old/old_gfx_shapes.c、old/old_std_calc.c、old/old_std_guess.c、old/old_std_hanoi.c、old/old_std_primes.c、old/old_std_wc.c、old/old_tty_ansi.c、old/old_tty_box.c、old/old_tty_conio.c、old/old_tty_curses.c、old/old_tty_progress.c、test/audio_test.c、test/block_bench.c、test/builtin_dup_api.c、test/callports.c、test/file_api.c、test/http_api.c、test/libs.c、test/mask_bool.c、test/minmax_api.c、test/module_init_api.c、test/regname.c、test/sleep_api.c、test/string_api.c、test/ui_lang.c、test/wintest.c、tty/ansi_colors.c、tty/conio_screen.c、tty/curses_demo.c、tty/demo_tty.c、tty/demo_tty_alt.c、tty/dos_demo.c、tty/pcscreen.c、tty/tty_legacy.c、ui/color_chart.c、ui/demo_ui.c、ui/draw_brush.c、ui/draw_colors.c、ui/gradient_check.c、ui/shot.c、ui/test_font.c
实测踩过的坑
- 编译最慢:一份带标准库的程序,手机上要一两分钟(Python/Lua 那类只要几秒)。同一份反复跑的话,先编成
.vmb再跑,快很多。 - 全局数组的初始化器里带负数时注意(历史缺陷,已修;写方向表这类东西记得测一下)。
下面的内容是从 VML 源码里直接带的(third_party/vml/VMLPrepares/CCompiler/): README 讲这个前端支持什么、怎么编;语言规范 讲语法本身。上游一改,这里重新生成就是最新的。
语言规范
C 语言编译器规范说明
版本:v1.1 | 日期:2026-07-06 | 修订者:深圳市探索智能科技有限公司
规范标准
| 字段 | 值 |
|---|---|
| 目标标准 | C99 (ISO/IEC 9899:1999) |
| 发布年份 | 1999 |
| 完成度 | ~97% |
| 测试 | 43+ 通过 (含 12 Int64) + Lua 5.4 31/31 + cJSON/nbsdgames/parson/tiny_regex_c |
| 里程碑 | Lua 5.4 31/31 🎉 + SQLite 255K行 (v1.65.36) |
概述
本编译器支持 C99 标准的子集,将 C 语言源代码编译为 VML (Virtual Machine Language) 汇编代码。编译器采用经典的编译流程:预处理 → 词法分析 → 语法分析 → 代码生成。
支持的语言特性
1. 数据类型
基本数据类型
- 整数类型:
int,short,long,signed,unsigned- 支持类型修饰符组合:
unsigned int,signed long,unsigned short等 - 默认整数类型为
signed int unsigned单独使用等价于unsigned intsigned单独使用等价于signed int
- 支持类型修饰符组合:
- 字符类型:
char,signed char,unsigned char - 浮点类型:
float,double - 布尔类型:
bool(C99 扩展) - 空类型:
void
常量后缀
- 整数常量后缀:
L或l: 长整型常量,如100LU或u: 无符号整型常量,如100UUL或ul: 无符号长整型常量,如100UL
- 浮点常量后缀:
F或f: 单精度浮点常量,如3.14F- 无后缀: 双精度浮点常量,如
3.14
- 十六进制常量后缀:
- 支持十六进制常量后缀:
0xFFU,0x7FFFFFFFL,0xFFFFFFFFUL
- 支持十六进制常量后缀:
派生类型
- 指针:
int*,char*,void*等 - 数组: 一维和多维数组,支持初始化,如
int arr[10]和int matrix[2][3] = {{1,2,3},{4,5,6}} - 结构体: 全局结构体定义和成员访问
- 枚举: 全局枚举定义和使用
2. 变量声明和定义
变量声明
int x; // 全局变量
int y = 10; // 带初始化的全局变量
static int z = 20; // 静态变量
void func() {
int local_var; // 局部变量
int init_var = 30; // 带初始化的局部变量
static int static_local = 40; // 静态局部变量
}
常量
const int MAX_SIZE = 100;
const float PI = 3.14159;
3. 运算符
算术运算符
+ // 加法
- // 减法
* // 乘法
/ // 除法
% // 取模
++ // 自增
-- // 自减
关系运算符
== // 等于
!= // 不等于
< // 小于
<= // 小于等于
> // 大于
>= // 大于等于
逻辑运算符
&& // 逻辑与
|| // 逻辑或
! // 逻辑非
位运算符
& // 按位与
| // 按位或
^ // 按位异或
~ // 按位取反
<< // 左移
>> // 右移
赋值运算符
= // 赋值
+= // 加后赋值
-= // 减后赋值
*= // 乘后赋值
/= // 除后赋值
%= // 取模后赋值
&= // 按位与后赋值
|= // 按位或后赋值
^= // 按位异或后赋值
<<= // 左移后赋值
>>= // 右移后赋值
其他运算符
& // 取地址
* // 解引用
. // 结构体成员访问
-> // 指针结构体成员访问
sizeof // 获取类型大小
4. 类型转换和提升
隐式类型转换
- 整数提升: 在表达式中,
char和short类型自动提升为int - 浮点提升: 在混合类型表达式中,整数自动转换为浮点数
- 赋值转换: 赋值时右侧表达式类型自动转换为左侧变量类型
显式类型转换(强制转换)
int x = 10;
float y = (float)x; // 整数转浮点
int z = (int)3.14; // 浮点转整数
unsigned int u = (unsigned int)-10; // 有符号转无符号
类型提升规则
- 如果任一操作数是
float,结果为float - 如果任一操作数是
double,结果为double - 否则进行整数提升,按类型等级选择结果类型
- 类型等级(从低到高):
char/unsigned charshort/unsigned shortint/unsigned intlong/unsigned long
5. 控制流语句
条件语句
// if-else
if (condition) {
// 语句块
} else if (condition2) {
// 语句块
} else {
// 语句块
}
// 三元运算符
int result = condition ? value1 : value2;
循环语句
// while 循环
while (condition) {
// 循环体
}
// do-while 循环
do {
// 循环体
} while (condition);
// for 循环
for (int i = 0; i < 10; i++) {
// 循环体
}
跳转语句
break; // 跳出循环或switch
continue; // 继续下一次循环
return; // 返回函数值
goto label; // 跳转到标签
switch 语句
switch (expression) {
case constant1:
// 语句块
break;
case constant2:
// 语句块
break;
default:
// 语句块
break;
}
6. 函数
函数定义
// 函数声明
int add(int a, int b);
// 函数定义
int add(int a, int b) {
return a + b;
}
// 无返回值函数
void print_message(const char* msg) {
// 函数体
}
// 可变参数函数(有限支持)
int printf(const char* format, ...);
函数调用
int result = add(10, 20);
print_message("Hello, World!");
递归函数
int factorial(int n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}
中断服务函数 (interrupt)
interrupt 关键字声明一个函数为中断服务程序(ISR)。编译器自动保存/恢复全部通用寄存器,并使用 IRET 而非 RET 返回。
// 中断服务函数:保存全部寄存器,IRET 返回
interrupt void timer_isr(void) {
// 中断处理代码
// 编译器自动生成:PUSH R0..R11、PUSH R12、PUSH R15
// 恢复:POP R15、POP R12、POP R0..R11、IRET
}
// 可通过 interrupt + static 组合使用
interrupt static void keyboard_isr(void) {
// 仅本文件可见的 ISR
}
注意事项:
- ISR 不能有参数(必须为
void) - ISR 应尽量简短(中断处理时间有限)
- 编译器自动管理全部寄存器的保存和恢复
- 函数向量地址需要通过
.vectors伪指令在 VML 汇编中注册
7. 预处理器指令
文件包含
#include "vmlib.h" // 用户头文件
#include <stdio.h> // 系统头文件(有限支持)
宏定义
#define MAX_SIZE 100
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#ifdef DEBUG
#define DEBUG_PRINT(msg) printf("DEBUG: %s\n", msg)
#else
#define DEBUG_PRINT(msg)
#endif
条件编译
#if defined(WIN32)
// Windows 特定代码
#elif defined(LINUX)
// Linux 特定代码
#else
// 其他平台代码
#endif
8. 结构体和枚举
结构体
// 结构体定义
struct Point {
int x;
int y;
};
// 结构体变量声明
struct Point p1;
struct Point p2 = {10, 20};
// 结构体成员访问
p1.x = 5;
p1.y = 15;
// 结构体指针
struct Point* ptr = &p1;
ptr->x = 100;
枚举
// 枚举定义
enum Color {
RED,
GREEN,
BLUE
};
// 枚举变量
enum Color c = RED;
9. 指针和数组
指针
int x = 10;
int* ptr = &x; // 取地址
int y = *ptr; // 解引用
// 指针运算
int arr[10];
int* p = arr;
p++; // 指针前进
p--; // 指针后退
数组
// 数组声明和初始化
int arr1[10];
int arr2[5] = {1, 2, 3, 4, 5};
int arr3[] = {1, 2, 3}; // 自动推断大小
// 部分初始化(未指定元素自动为0)
int arr4[10] = {1, 2, 3}; // arr4[0]=1, arr4[1]=2, arr4[2]=3, arr4[3..9]=0
// 数组访问
arr1[0] = 100;
int value = arr2[2];
// 多维数组
int matrix[3][3] = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
// 多维数组部分初始化
int matrix2[2][3] = {
{1, 2}, // 第三列自动为0
{4, 5, 6}
};
// 嵌套数组初始化
int cube[2][2][2] = {
{
{1, 2},
{3, 4}
},
{
{5, 6},
{7, 8}
}
};
10. 存储类别与扩展关键字
自动变量 (auto)
void func() {
auto int x = 10; // auto 可省略
int y = 20; // 默认就是 auto
}
寄存器变量 (register)
void func() {
register int counter = 0; // 建议编译器使用寄存器
}
静态变量 (static)
static int global_static = 100; // 文件作用域
void func() {
static int local_static = 0; // 函数作用域,保持值
local_static++;
}
外部变量 (extern)
extern int external_var; // 声明外部变量
void func() {
external_var = 100; // 使用外部变量
}
中断服务函数 (interrupt)
interrupt void isr_name(void) {
// 中断处理代码
// 编译器自动保存 R0-R11,使用 IRET 返回
}
详见第 6 节「函数」中的「中断服务函数」。
编译器限制
不支持的特性
- 位域 (bit fields): 不支持结构体位域
- 复杂指针运算: 指针算术仅限于简单加减
- 函数指针: 声明语法已支持 (v1.65.35),运行时调用有限
- 变长数组 (VLA): 不支持 C99 的变长数组
- 复杂类型限定符: 不支持
restrict,_Atomic等 unsigned修饰符:unsigned int/unsigned char当作int/char处理(VML 无符号概念)
v1.65.35 里程碑: SQLite 编译
- 解析能力: 完成 SQLite amalgamation 全文件解析 (255,636 行, ~475K tokens)
- 编译通过: SQLite stub + 完整版本均可编译运行
- 运行时验证: sqlite3_open / CREATE TABLE / INSERT / SELECT / sqlite3_close 全部通过
- 类型系统增强: 匿名 struct 成员、双星号指针、函数指针声明
VML 扩展关键字
本编译器在标准 C99 基础上增加了以下扩展关键字:
| 关键字 | 用途 | 说明 |
|---|---|---|
interrupt / __interrupt | 函数声明 | 声明中断服务函数,自动保存寄存器、IRET 返回 |
__chipasm__ | 内联汇编块 | 插入多行 VML 汇编指令 |
asm | 内联汇编 | 插入单行 VML 汇编指令 |
__stdcall | 调用约定 | stdcall 调用约定(参数从右到左压栈,被调用者清理) |
__fastcall | 调用约定 | fastcall 调用约定(前2个参数通过寄存器传递) |
__cdecl | 调用约定 | cdecl 调用约定(参数从右到左压栈,调用者清理) |
__builtin_va_start | 变参 | 变参函数起始 |
__builtin_va_arg | 变参 | 访问变参 |
__builtin_va_end | 变参 | 结束变参 |
__builtin_va_copy | 变参 | 拷贝变参 |
部分支持的特性
- 预处理器: 支持
#include、#define、#undef、条件编译(#if/#ifdef/#ifndef/#else/#elif/#endif) - 标准库: 支持
stdio.h、stdlib.h、string.h、math.h、conio.h、graphics.h - 浮点运算: 支持基本浮点运算(
+ - * /、比较、类型转换) - 结构体: 支持基本结构体,支持结构体成员指针(
char *buf)、嵌套结构体 - 联合体: 支持 union 类型
- 数组初始化: 完整支持一维和多维嵌套初始化语法
- 内联汇编: 支持
asm("instruction")单行和__chipasm__ { }多行语法 - 多维数组: 支持声明、访问和嵌套初始化的完整多维数组
- 调用约定: 支持
__stdcall/__fastcall/__cdecl三种调用约定 - 指示字: 常量后缀(
L、U、F)仅在部分上下文中完全支持
编译流程
1. 预处理阶段
- 处理
#include指令 - 展开宏定义
- 处理条件编译
- 移除注释
2. 词法分析阶段
- 将源代码转换为令牌流
- 识别关键字、标识符、常量、运算符等
3. 语法分析阶段
- 构建抽象语法树 (AST)
- 检查语法正确性
- 建立符号表
4. 代码生成阶段
- 遍历 AST 生成 VML 指令
- 分配变量存储空间
- 生成函数调用代码
- 优化生成的代码
使用示例
简单程序
#include "vmlib.h"
int main() {
vga_clear();
vga_puts("Hello, C Compiler!");
return 0;
}
计算阶乘
#include "vmlib.h"
int factorial(int n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}
int main() {
int result = factorial(5);
vga_clear();
vga_puts("Factorial of 5 is: ");
// 需要将数字转换为字符串显示
return 0;
}
数组操作
#include "vmlib.h"
int main() {
int arr[5] = {1, 2, 3, 4, 5};
int sum = 0;
for (int i = 0; i < 5; i++) {
sum += arr[i];
}
vga_clear();
vga_puts("Sum of array: ");
// 显示结果
return 0;
}
多维数组初始化
#include "vmlib.h"
int main() {
// 二维数组初始化
int matrix[3][3] = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
// 计算矩阵对角线之和
int diag_sum = 0;
for (int i = 0; i < 3; i++) {
diag_sum += matrix[i][i];
}
vga_clear();
vga_puts("Diagonal sum: ");
// 显示结果
return 0;
}
复杂数组测试
#include "vmlib.h"
// 测试各种数组初始化方式
int main() {
// 自动大小推断
int auto_arr[] = {10, 20, 30, 40, 50};
// 部分初始化
int partial[10] = {1, 2, 3}; // 后7个元素为0
// 多维数组
int cube[2][2][2] = {
{{1, 2}, {3, 4}},
{{5, 6}, {7, 8}}
};
vga_clear();
vga_puts("Array tests completed");
return 0;
}
编译器选项
命令行参数
# 基本用法
dotnet run --project VMLPrepares\\CCompiler input.c -o output.vml
# 指定库路径
dotnet run --project VMLPrepares\\CCompiler -I Lib\c input.c -o output.vml
# 只进行预处理
dotnet run --project VMLPrepares\\CCompiler -E input.c
# 使用环境变量指定库路径
$env:VML_C_LIB="Lib\c"
dotnet run --project VMLPrepares\\CCompiler input.c -o output.vml
环境变量
VML_C_LIB: 指定 C 语言库文件搜索路径
错误处理
编译器会报告以下类型的错误:
语法错误
- 缺少分号、括号不匹配等
- 错误位置和错误信息
语义错误
- 未声明的标识符
- 类型不匹配
- 重复定义
预处理错误
- 找不到头文件
- 宏定义错误
C99 标准兼容性
本编译器目标是实现完整的 C99 标准兼容性。当前已实现约 97% 的 C99 核心功能:
已实现的核心功能
- 基本语法: 变量声明、表达式、控制结构
- 数据类型:
int,char,float,double,void, 指针、数组、结构体、枚举 - 控制流:
if-else,while,do-while,for,switch,break,continue,return,goto - 函数: 函数定义、声明、调用、递归、参数传递、隐式 int 返回类型
- 预处理器:
#include,#define,#ifdef,#ifndef,#if,#else,#elif,#endif - 数组: 一维和多维数组声明、嵌套初始化、元素访问
- 结构体: 全局结构体定义、变量声明、成员访问、结构体成员指针
- 联合体: 支持 union 类型
- 逗号表达式: 支持逗号运算符
- 注释: 支持
/* */和//风格注释
VML 扩展特性
- 中断服务函数:
interrupt关键字声明 ISR,自动保存全部寄存器、IRET 返回 - 内联汇编:
asm("instruction")单行和__chipasm__ { }多行 VML 指令 - 调用约定:
__stdcall/__fastcall/__cdecl跨语言互操作 - 跨语言互操作:
POKE/PEEK内存映射 I/O (MMIO)
缺失的关键功能(计划实现)
- 类型系统:
unsigned类型完整支持 - 标准库: 完成
stdio.h文件操作、math.h全部函数 - 高级特性: 函数指针、位域
- 复杂指针运算: 完善的指针算术和类型转换
- 宽字符:
wchar_t和相关函数
与标准 C 的差异
- 内存模型: 使用 VML 虚拟机的内存模型
- 标准库: 使用 VML 运行时库而非标准 C 库
- 系统调用: 通过 VML 运行时进行 I/O 操作
- 整数大小: 使用 VML 的 32 位整数
- 浮点精度: 使用 VML 的浮点表示
浮点与64位编译模式
VML 工具链通过三个编译参数控制浮点和 64 位整数的处理策略:
| 参数 | 可选值 | 默认值 | 说明 |
|---|---|---|---|
--float32 | hard / soft / none | hard | 32位浮点 (float) 处理模式 |
--float64 | hard / soft / none | soft | 64位浮点 (double) 处理模式 |
--int64 | hard / soft / none | soft | 64位整数 (long long) 处理模式 |
32位浮点 (float32)
本语言中的 32 位单精度浮点类型 float 按以下模式编译:
hard模式(默认): 使用 VML 原生浮点指令MOVEF/ADDF/SUBF/MULF/DIVF/CMPF/NEGF,通过 F0-F15 十六个浮点寄存器直接运算。性能最佳,适合支持浮点硬件的目标平台。soft模式: 使用 Q15.16 定点数软件模拟库softfloat.c,通过__vml_float_add/sub/mul/div/neg/abs/cmp等函数模拟浮点运算。适合无浮点硬件的 MCU 平台。none模式: 禁用所有 32 位浮点类型,遇到float声明时报告编译错误。
64位浮点 (double)
本语言中的 64 位双精度浮点类型 double 按以下模式编译:
soft模式(默认): 使用 IEEE 754 双精度软件模拟库softdouble.c,通过__vml_double_add/sub/mul/div/neg/abs/cmp、__vml_int2double/double2int、__vml_float2double/double2float等函数模拟。兼容所有平台(含 MCU)。hard模式: 使用 VML 双精度指令MOVED/ADDD/SUBD/MULD/DIVD/CMPD/NEGD,通过 D0-D7 八个双精度寄存器运算。需要目标平台支持 64 位运算。none模式: 禁用所有 64 位浮点类型,遇到double声明时报告编译错误。
64位整数 (int64)
本语言中的 64 位整数类型 long long 按以下模式编译:
soft模式(默认): 使用双寄存器软件模拟库softint64.c,通过__vml_i64_add/sub/neg/and/or/xor/not/shl/shr等函数模拟 64 位整数运算。hard模式: 使用 VML 原生 64 位整数指令MOVEL/ADDL/SUBL/MULL/DIVL/MODL/NEGL/CMPL/ANDL/ORL/XORL/NOTL/SHLL/SHRL,通过 L0-L7 八个长整数寄存器运算。v1.65.197+ 起可用。none模式: 禁用 64 位整数类型,遇到long long声明时报告编译错误。
软件模拟库
以上软件模拟库均位于 Lib/shared/ 目录,使用 C 语言编写并由 C 编译器编译为 VML,所有语言共享:
| 库文件 | 用途 | 核心函数 |
|---|---|---|
softfloat.c | Q15.16 定点数 32 位浮点模拟 | __vml_float_add/sub/mul/div/neg/abs/cmp、__vml_int2float/float2int |
softdouble.c | IEEE 754 双精度 64 位浮点模拟 | __vml_double_add/sub/mul/div/neg/abs/cmp、__vml_int2double/double2int、__vml_float2double/double2float |
softint64.c | 64 位整数双寄存器模拟 | __vml_i64_add/sub/neg/and/or/xor/not/shl/shr |
性能考虑
- 代码大小: 生成的 VML 代码比原生代码大
- 执行速度: 在 VML 虚拟机中运行,比原生代码慢
- 内存使用: 使用 VML 虚拟机的内存空间
- 优化级别: 当前为基本优化,可生成较直接的代码
扩展性
编译器设计为可扩展的架构:
- 添加新语法: 可扩展词法和语法分析器
- 优化通道: 可添加代码优化阶段
- 目标平台: 可扩展支持其他目标架构
- 语言特性: 可逐步添加更多 C 语言特性
相关文件
CCompiler.cs: 编译器主类Lexer.cs: 词法分析器Parser.cs: 语法分析器CodeGenerator.cs: 代码生成器Preprocessor.cs: 预处理器Program.cs: 命令行接口README.md: 项目说明文档
🆕 字符串类型 (v1.65.19)
C 编译器支持三种宽度的字符串类型:
| 字面量 | 类型 | 宽度 | VML 伪指令 | 终止符 |
|---|---|---|---|---|
"str" | char* | 8-bit | .string | 0x00 |
L"str" | wchar_t* | 16-bit | .wstring | 0x0000 |
U"str" | char32_t* | 32-bit | .ustring | 0x00000000 |
char *s1 = "Hello"; // .string (UTF-8)
wchar_t *s2 = L"你好"; // .wstring (UTF-16LE, BMP only)
char32_t *s3 = U"🎉"; // .ustring (UTF-32LE, full Unicode)
wchar_t c1 = L'字'; // 16-bit 宽字符
char32_t c2 = U'🎉'; // 32-bit Unicode 字符
预定义宏: VML_WSTRING — OS 模式下定义为 1,MCU 模式下未定义 标准头: <wchar.h> / <uchar.h> 自动链接 Lib/shared/wchar.vml / uchar.vml 输出: shared_print_wstr(wstr) — 自动转换 UTF-16LE→UTF-8→SYSCALL #1
🆕 预处理器扩展指令 (v1.65.19)
#param — 源文件内配置指令
| 指令 | 说明 | 示例 |
|---|---|---|
#param lib("xxx") | 自动链接指定库文件 | #param lib("wchar") |
#param path("xxx") | 添加 include 搜索路径 | #param path("./inc") |
#param prefix("xxx") | 设置函数名前缀 | #param prefix("python_") |
#param prefix — 同一源码编译多语言库
使用 #param prefix 或命令行 -D VML_PREFIX=xxx,同一份 C 源码可为不同语言生成不同前缀的库文件:
// wstring.c — 一份源码,多语言共用
#param prefix("python_") // 为 Python 编译时取消注释这行
// #param prefix("java_") // 为 Java 编译时取消注释这行
// #param prefix("js_") // 为 JavaScript 编译时取消注释这行
size_t wcslen(const wchar_t *s) { ... } // → python_wcslen
wchar_t *wcscpy(wchar_t *d, const wchar_t *s) { ... } // → python_wcscpy
编译命令:
# 为 Python 生成 python_wcslen, python_wcscpy ...
vmltool wstring.c -D VML_PREFIX=python_ -o python/wstring.vml
# 为 Java 生成 java_wcslen, java_wcscpy ...
vmltool wstring.c -D VML_PREFIX=java_ -o java/wstring.vml
# 为 JavaScript 生成 js_wcslen, js_wcscpy ...
vmltool wstring.c -D VML_PREFIX=js_ -o js/wstring.vml
前缀替换规则:
shared_xxx→{prefix}xxx(替换 shared_ 前缀)vml_xxx→{prefix}xxx(替换 vml_ 前缀)- 其他函数名 →
{prefix}+ 原名 (直接添加前缀)
多语言库生成脚本
#!/bin/bash
# build_all_langs.sh — 为所有语言编译 wstring 库
for lang in python java js kotlin swift go csharp; do
vmltool wstring.c -D "VML_PREFIX=${lang}_" -o "${lang}/wstring.vml" --no-link
done
#param lib — 自动库链接
// wstring.h 头文件中声明依赖
#param lib("wchar") // 自动链接 wchar.vml
// 用户只需 #include "wstring.h"
// 编译器自动链接 wstring.vml + wchar.vml
配合 #ifdef VML_WSTRING 可按模式选择编码。
#param path — 额外 include 路径
#param path("./include") // 添加相对搜索路径
调用约定 (v1.65.19)
C 编译器支持三种调用约定,通过函数修饰符指定:
| 修饰符 | 参数传递 | 栈清理 | 变参支持 |
|---|---|---|---|
__cdecl (默认) | 右→左压栈,R0-R3传递前4参数 | 调用者清理 | ✅ |
__stdcall | 右→左压栈 | 被调用者清理 | — |
__fastcall | R0-R3传递前4参数,其余压栈 | 被调用者清理 | — |
__cdecl 变参函数
// 变参 printf
__cdecl void shared_printf(const char *fmt, ...) {
char buf[512];
int *stack_args = (int*)(&fmt + 1); // fmt 之后是变参
int len = shared_vsnprintf(buf, fmt, stack_args, 8);
buf[len] = 0;
asm("SYSCALL #1"); // 输出到控制台
}
// 调用: 参数数量不限 (最多 8 个, 受 VML 栈限制)
shared_printf("Int: %d Hex: %x Str: %s\n", 42, 255, (int)"Hello");
// 变参 sprintf
__cdecl int shared_sprintf(char *buf, const char *fmt, ...) {
int *stack_args = (int*)(&fmt + 1);
return shared_vsnprintf(buf, fmt, stack_args, 8);
}
变参实现原理:
__cdecl参数从右向左压栈fmt是最接近栈顶的固定参数&fmt + 1指向栈上的第一个可变参数- 通过栈指针可以直接读取所有变参
__stdcall 固定参数
// 被调用者清理栈, Windows API 风格
__stdcall void shared_print_str(const char *str) {
asm("SYSCALL #1");
}
__fastcall 快速调用
// 前4参数用寄存器, 适合性能敏感场景
__fastcall int add_four(int a, int b, int c, int d) {
return a + b + c + d;
}
注意: __cdecl/__stdcall/__fastcall 修饰符用于函数定义。在调用侧,编译器自动根据函数定义选择正确的调用约定,无需显式声明。
编译器 README
C 编译器
路径: VMLPrepares/CCompiler/ 完成度: ~97% | 🟢 生产可用 标准库: Lib/c/ (29 个文件)
功能
- ✅ C99 完整标准 (ISO/IEC 9899:1999)
- ✅ 指针算术、多维数组、struct/union
- ✅ 预处理器 (#include, #define, #ifdef)
- ✅ 内联汇编 (
__chipasm__) - ✅ POKE/PEEK 内存操作
- ✅ 完整标准库 (stdio, stdlib, string, math, conio, graphics)
- ✅ 中断/外设支持 (interrupt)
编译模式
MCU 模式(默认 --mode mcu)
MCU 模式针对单片机/裸机环境(Arduino/STM32/8051 等)优化,自动跳过不兼容操作系统的特性。
跳过(遇到这些语法不生成代码):
- 无(C99无异步/线程/反射)
保留(由 BIOS 实现底层):
- malloc/free、fopen/fread、printf%f、va_list
- POKE/PEEK 内存映射 I/O (MMIO)
- interrupt 关键字(中断服务函数)
- 基本类型运算、控制流、函数调用
- printf/puts 映射到 UART
- 裸指针(有限制)
OS 模式(--mode os,预留)
OS 模式针对带操作系统环境(如 Linux 嵌入式、RTOS 等),届时支持全部语言特性(文件系统、多线程、异步、异常、反射等)。
RAM 级别
--ram k:KB级别(2KB~64KB,如 8051/PIC/AVR)--ram m:MB级别(64KB~1MB,如 ARM Cortex-M,默认)--ram g:GB级别(如 x86/DDR 系统)--stack-size <bytes>:手动指定栈大小(默认自动根据 --ram 分配)
MCU 安全编码提示
- 有限栈空间(256-4096 字节典型),避免深度递归
- 禁止深度递归(>10层需评估栈)
- 禁止动态加载(import/dofile/eval)
- 浮点运算可能需软浮点库
使用
dotnet run --project VMLPrepares/CCompiler input.c -o output.vml
vmltool input.c -o output.vml
测试
Test/c/ — 190+ 测试文件