微软于 2025 年 11 月 21 日正式以 MIT 许可开源《Zork》三部曲源码,并向 GitHub historicalsource 仓库提交上游 PR,此举为研究 Z-Machine 虚拟机提供了权威基础。Z-Machine 是 Infocom 于 1979 年设计的字节码 VM,用于跨平台运行文本冒险游戏,其核心包括固定内存布局(header、对象表、字典)、栈式执行模型与自然语言解析。此文聚焦从开源 ZIL 源码逆向,工程化浏览器端(WASM/JS)可移植解释器,强调字节码分发效率、对象模型简洁性及字典匹配优化,实现低延迟 Web 游玩。
Z-Machine 核心机制剖析
Z-Machine 故事文件(.z1 至 .z8)以字节码形式打包,总内存 64KB–512KB,分静态(header 64 字节、对象表、属性表)、动态(调用栈、局部变量)区。Header 指定版本(Zork I 为 v3)、入口点、字典地址等。从 historicalsource/zork1 的 ZIL(如 gglobals.zil、gparser.zil)可见,游戏逻辑依赖对象树(短/长名、属性指针)、字典(词汇表支持词形变化)与 opcode(如 @jump、@store)。
字节码分发(Dispatch):解释器主循环从 PC(Program Counter)读取 opcode(1–2 字节),分发执行。v3 支持 205 个 opcode,分为 0OP(如 @rtrue)、1OP(如 @jz)、2OP(如 @add)、VAR(如 @call)。源码中 gmain.zil 隐含循环:fetch → decode → operand(支预测或直接)→ execute → PC++。浏览器实现需高效 switch,避免递归栈溢出(深度限 1024 帧)。
对象模型(Object Model):对象从 #1 起,属性动态(0=父、1=兄弟、2=子),v3 对象头 9 字节(父/兄弟/子指针 + 属性偏移)。ZIL 如 1actions.zil 定义 ROOM(如 West-of-House)、THING(剑),属性如 LOCATION、SIZE。解释器需对象表(array of structs:parent/sibling/child/props),属性分离表(packed bytes)。优化:预解析对象树至 JS Proxy,支持动态 get/set。
字典优化(Dictionary):解析器(gparser.zil)匹配输入词至词汇表(~2000 词,词长 4–9 字节,GRAMMAR table)。词典为 trie-like 结构(首字节哈希),支持词形(如 take/puton)。瓶颈:模糊匹配(前缀/后缀)。Web 优化:IndexedDB 缓存字典,Web Workers 分离解析,TrieNode(children Map)+ Levenshtein 距离阈值 1。
浏览器端可移植实现参数
利用 Emscripten 将 C/JS 解释器编译为 WASM,实现 60FPS 游玩。核心参数:
-
内存管理:
- 静态分配 Uint8Array(512*1024),分段:0x00-0x3F header、0x40-对象表、0x动态栈(growable)。
- Stack:SP 指针,双数组(call/ret addr + locals),限 1024 帧,回滚阈值 512。
- 清单:init() 验证 header.version==3、header.static_mem、header.dict_addr;loadStory(zfile) memcpy Uint8Array.slice。
-
字节码分发引擎:
- 巨型 switch(opcode),分组:case 0x00: rtrue(); break; ... case 0xBE: call_1n();。
- 优化:OpcodeTable[256] = fn_ptr,预加载避免分支预测 miss(Chrome V8 JIT 友好)。
- 参数:operand count 从 opcode 高 2 位,支预测 read_word/read_byte。超时:每帧 16ms,yield to RAF。
- 清单:dispatch() { while(!halt && perf.now()<deadline) { op=mem[pc++]; tableop; } }。
-
对象模型实现:
- Object:class { id, parent, sibling, child, props: Map<number,u16>() }。
- Props:属性表稀疏,get_prop(obj,attr) = mem[obj.prop_table + attr_off*2]。
- 动态:@get/set_prop,JS Proxy 代理访问。预热:startup() 遍历对象建树。
- 清单:validate_obj(id<obj_count),prop_default fallback。
-
字典与解析优化:
- Dict:class { words: Map<string,{part,action}>(), grammar: Uint16Array }。
- 加载:scan dict_addr,分离分离词/语法。匹配:input.split,trie match + dict[stem]。
- 优化:n-gram 缓存(LRU 1024),fuzzy: dp[10][10] <=2 edits。Web:OffscreenCanvas 渲染文本。
- 清单:parse(tokens) → actions/actor/noun,vocab_score >0.8 通过。
工程化落地与监控
构建清单:
- 工具:ZILF 编译 ZIL→.z3(验证源码),WASM-zmachine(如 bocfel-wasm)基准。
- 测试:Zork1.zip 覆盖率 95%,puzzles 解通(18 宝藏)。
- 性能:WASM 基准 1M IPC,JS fallback polyfill opcodes。
- 回滚:版本 pin(v3 only),A/B test dispatch table。
监控要点:
- Metrics:dispatch/s、parse ms、stack depth、GC pauses。
- Alerts:OOM (>80%)、parse fail (>5%)、帧掉 (>10%)。
- 追踪:Sentry opcode traces,Profiling V8 cpu_profiler。
开源 Zork 源码揭示 Z-Machine 的优雅:紧凑对象、栈 VM、词典解析。浏览器实现证明其永恒性,结合 WASM 零依赖部署。未来:AI 代理集成(如 GPT parse),重现 80 年代魔法。
资料来源: