Skip to content

WebAssembly

是什么

WebAssembly(简称 Wasm)是一种基于栈式虚拟机的二进制指令格式。它被设计为一种可移植、体积小、加载快的底层格式,能够在 Web 浏览器中以接近原生的速度执行代码。WebAssembly 不是一门编程语言,而是一种编译目标——开发者可以用 C、C++、Rust 等语言编写代码,然后编译成 .wasm 二进制文件,由浏览器的 Wasm 引擎直接运行。

为什么需要

在 pokeemerald-wasm 项目中,原始的 GBA 游戏(Pokémon Emerald)是用 C 语言编写的,原本需要通过 ARM 编译器编译为 GBA 硬件上的机器码。为了让这个游戏能够在浏览器中运行,我们需要一种高效的方式将 C 代码"搬运"到 Web 环境。WebAssembly 提供了这条路径:

  • 接近原生的性能:Wasm 的执行效率远超 JavaScript,能够满足游戏帧率的需求。
  • 直接的 C/C++ 编译支持:通过 Clang 等编译器,C 代码可以直接编译为 Wasm,无需重写。
  • 标准化的 Web 集成:所有现代浏览器都原生支持 Wasm,无需插件。

原理与实现

WebAssembly 的核心特性包括:

  • 栈式虚拟机:指令通过操作数栈来执行,而非使用寄存器。这种设计使得二进制格式更紧凑。
  • 线性内存:Wasm 模块拥有一块连续的线性内存(以页为单位,每页 64KB),可以通过 load/store 指令随机访问。GBA 的内存映射(VRAM、OAM 等)被映射到这块线性内存中。
  • 模块化导入导出:Wasm 模块可以导出函数供 JavaScript 调用(如 AgbMainWasmRunFrame),也可以导入 JavaScript 提供的函数。
  • 沙箱化执行:Wasm 代码在一个安全的沙箱环境中运行,无法直接访问系统资源。

在 pokeemerald-wasm 中的实现

在 pokeemerald-wasm 项目中,WebAssembly 的使用方式如下:

  1. 编译:使用 Clang(而非 Emscripten)将 C 源码编译为 .o 目标文件。
  2. 链接:使用 wasm-ld 将所有 .o 文件链接为最终的 .wasm 模块,配置初始内存为 256MB(--initial-memory=268435456)。
  3. 导出接口:链接时导出 AgbMain(游戏入口)和 WasmRunFrame(逐帧执行)两个关键函数。
  4. 渲染桥接:JavaScript 侧的 app.js 读取 Wasm 线性内存中的 VRAM、OAM、Palette 数据,将其转换为 Canvas 2D 图像进行渲染。

相关概念

  • wasm-ld — LLVM 的 WebAssembly 链接器,用于将编译产物链接为最终 .wasm 文件
  • emscripten — 未使用的替代方案,项目选择直接使用 Clang + wasm-ld