Appearance
项目架构概览 — 练习
练习 1:绘制帧渲染流程图
根据你对架构的理解,绘制一个游戏帧从浏览器 requestAnimationFrame 触发到画面显示在 Canvas 上的完整流程。
参考答案
关键步骤:
requestAnimationFrame回调触发WasmRunFrame()- WASM 模块执行一帧游戏逻辑,写入 VRAM、OAM、调色板等硬件内存
WasmRunFrame()返回后,app.js 读取 VRAM 数据- 根据当前显示模式(DISPCNT 寄存器),渲染背景层和精灵层
- 将 GBA BGR555 颜色转换为 RGBA 格式
- 使用
putImageData将像素数据写入 240×160 Canvas - 浏览器显示渲染结果
练习 2:对比 GBA 和 WASM 构建
阅读 Makefile,回答以下问题:
make(GBA 构建)和make wasm的编译器有什么不同?-DWASM=1标志在源码中起什么作用?- 为什么 WASM 构建使用
--no-entry和--allow-undefined链接选项?
参考答案
- GBA 构建使用
agbcc(或 modern 的arm-none-eabi-gcc),目标是 ARM7TDMI;WASM 构建使用clang --target=wasm32-unknown-unknown,目标是 WebAssembly。 -DWASM=1定义预处理器宏,让源码中#if WASM/#ifdef WASM守卫的条件编译代码生效,用于处理 GBA 和 WASM 之间的行为差异。--no-entry因为 WASM 模块不需要标准的main()入口(使用导出的AgbMain);--allow-undefined因为游戏代码引用了 GBA BIOS 系统调用和硬件寄存器等符号,这些在 WASM 中由运行时通过内存映射提供,不需要真实实现。
练习 3:目录结构分析
分析 pokeemerald-wasm 的目录结构:
src/目录中有多少 C 源文件?哪些是 WASM 专用的?include/wasm/中的头文件解决了什么问题?web/目录中哪个文件是整个浏览器运行时的核心?
参考答案
src/包含 280+ 个 C 源文件,绝大多数来自 pret/pokeemerald。只有wasm_field_effect_scripts.c是 WASM 专用文件。include/wasm/提供了stdio.h、stdlib.h、string.h等标准库头文件的垫片声明,避免 Clang 的内建函数声明与 GBA 工具链的声明冲突。例如重新声明memcpy、memset、sprintf等函数。web/app.js是浏览器端宿主运行时,负责 WASM 加载、输入处理、存档管理、帧调度,以及把 WASM 合成的 RGBA 搬到 Canvas;GBA 硬件模拟与图形渲染本身在 WASM 模块内(src/wasm_display.c),不在app.js。
拓展挑战
- 阅读
web/app.js的WasmRunFrame调用逻辑,理解帧同步机制 - 对比
make wasm和make modern的编译选项差异 - 研究
wrangler.toml配置,理解 Cloudflare Workers 部署架构