Appearance
WASM 编译管线 — 概念
编译管线总览
Clang 编译阶段
目标三元组
编译使用 --target=wasm32-unknown-unknown,这意味着:
- wasm32 — 生成 32 位 WebAssembly 代码
- unknown-unknown — 不指定具体操作系统和供应商,产生独立的 WASM 模块
- 不依赖 WASI(WebAssembly System Interface),所有系统调用由 JavaScript 侧模拟
关键编译标志
| 标志 | 作用 |
|---|---|
-DMODERN=1 | 启用"现代"代码路径,绕过原始 GBA 硬件依赖 |
-DWASM=1 | 启用 WASM 特定代码分支,替代原生 ARM 汇编 |
-O2 | 启用二级优化,平衡编译速度和运行性能 |
-fno-builtin | 禁用内置函数替换,避免与 GBA 自定义实现冲突 |
-ffreestanding | 声明为独立环境(无标准库),因为 WASM 模块不链接 libc |
为什么需要 -DMODERN=1 和 -DWASM=1
原始 Pokemon Emerald 代码包含大量直接操作 GBA 硬件的代码,例如:
- ARM 汇编内联函数(如
Log、除法运算) - 直接读写 I/O 寄存器地址
- 硬件中断处理
定义 MODERN=1 和 WASM=1 后,条件编译会:
- 用纯 C 实现替代 ARM 汇编内联
- 将硬件寄存器访问重定向到 WASM 线性内存地址
- 禁用硬件中断,改用轮询方式
wasm-ld 链接阶段
wasm-ld 是 LLVM 工具链中的 WebAssembly 链接器,负责将多个目标文件合并为单一 .wasm 模块。
关键链接选项
| 选项 | 详细说明 |
|---|---|
--no-entry | WebAssembly 模块通常需要 _start 或 main 入口。本项目由 JS 侧调用导出函数,不需要入口点 |
--allow-undefined | 游戏代码引用了 GBA BIOS 函数(如 LZ77UnCompVram),这些函数在 WASM 中不存在,由 JS 模拟实现 |
--initial-memory=268435456 | 256MB,远超 GBA 原始 32KB WRAM + 256KB 外部 RAM,为资源数据提供充足空间 |
--export=AgbMain | 导出游戏初始化入口,JS 在加载 WASM 后首先调用此函数 |
--export=WasmRunFrame | 导出逐帧执行函数,JS 的 requestAnimationFrame 循环每帧调用 |
--export-all | 将所有全局符号导出,方便调试和直接访问内部函数 |
链接后的符号解析
目标文件编译流程
每个 .c 源文件独立编译为 .wasm.o 目标文件:
相关概念
- wasm-ld — WebAssembly 链接器详解
- WebAssembly — WebAssembly 基础知识
- GBA 硬件模拟 — 了解 WASM 内存中的硬件地址映射
- 资源转换管线 — 了解图形资源如何嵌入 WASM 模块