Skip to content

内存模型概念 (Memory Model Concepts)

256MB 内存布局总览

pokeemerald-wasm 在 WebAssembly 中模拟了 GBA 的完整 256MB 地址空间。这块内存通过 WebAssembly.Memory 创建,初始大小恰好为 256MB(268,435,456 字节)。

线性内存模型

WebAssembly 采用平坦线性内存模型——内存是一块连续的字节数组,通过偏移量(offset)访问。没有虚拟内存、没有分页、没有内存保护。这与 GBA 的硬件设计高度契合:

在 C 源码中,GBA 硬件通过内存映射 I/O (MMIO) 访问——读写特定地址即控制硬件。编译为 WebAssembly 后,这些地址访问变成了对线性内存对应偏移量的读写。app.js 中的 JavaScript 代码则监控这些内存区域的变化,模拟硬件行为。

为什么是 256MB?

GBA 的 32 位地址总线理论上可寻址 4GB,但实际硬件只使用了低 256MB。initial-memory=268435456(即 0x10000000 = 256 * 1024 * 1024)这个值确保 WASM 内存覆盖所有 GBA 地址空间:

地址范围用途实际大小
0x00000000BIOS ROM16KB
0x02000000EWRAM(外部工作 RAM)256KB
0x03000000IWRAM(内部工作 RAM)32KB
0x04000000I/O 寄存器~1KB
0x05000000调色板 RAM1KB
0x06000000VRAM(视频 RAM)96KB
0x07000000OAM(精灵属性内存)1KB
0x08000000游戏 ROM最大 32MB
0x0E000000Flash 存档128KB

SharedArrayBuffer 与内存共享

使用 SharedArrayBuffer 而非普通 ArrayBuffer 的原因:

  1. 线程共享SharedArrayBuffer 可以在主线程和 Web Worker 之间共享,为将来将 WASM 执行移至 Worker 留下空间。
  2. 零拷贝访问:WASM 模块和 JavaScript 通过同一个底层缓冲区操作,无需数据拷贝。
  3. 实时同步:JavaScript 对内存视图的修改立即可被 WASM 代码感知,反之亦然。

注意:使用 SharedArrayBuffer 要求服务器设置 Cross-Origin-Opener-Policy: same-originCross-Origin-Embedder-Policy: require-corp HTTP 头。

地址映射策略

pokeemerald-wasm 采用直接映射策略——GBA 地址就是 WASM 线性内存的偏移量:

javascript
// GBA 地址 0x06000000 (VRAM) 对应:
memoryBuffer[0x06000000]     // Uint8Array 中的字节偏移
// 等价于 WASM 内存中的 i32.load offset=0x06000000

这种直接映射意味着:

  • C 源码中的指针地址无需转换即可在 WASM 中使用
  • JavaScript 通过类型化数组视图直接操作对应地址
  • 硬件模拟代码只需关注特定地址范围的变化

关键地址的 JavaScript 监控

app.js 通过定期检查(polling)或写入拦截来监控关键地址:

监控区域JavaScript 行为
0x04000000 (DISPCNT 等)解析显示控制寄存器,更新渲染模式
0x04000130 (REG_KEYINPUT)写入按键状态供游戏读取
0x05000000 (Palette)读取调色板颜色用于渲染
0x06000000 (VRAM)读取图块数据渲染背景和精灵
0x07000000 (OAM)读取精灵位置、大小等属性
0x0E000000 (Flash)监控存档写入,序列化到 localStorage

内存初始化流程

初始化时,JavaScript 先创建 256MB 的 SharedArrayBuffer,以此构造 WebAssembly.Memory 对象。随后将 GBA ROM 数据复制到 0x08000000 起始位置。WASM 模块实例化时接收这块共享内存,C 代码中的指针操作直接作用于此。

相关概念

  • WebAssembly -- WebAssembly 基础与线性内存模型
  • VRAM -- 视频内存的详细结构与渲染作用
  • 输入系统 -- I/O 寄存器中的按键输入部分
  • 存档系统 -- Flash 存档区域的读写机制
  • 渲染管线 -- VRAM 数据如何变为屏幕像素