N64 的 RDP(Reality Drawing Processor)作为固定功能图形管线,负责光栅化、纹理映射和 Z 缓冲等高吞吐任务,在模拟器中实现精确模拟面临性能瓶颈。传统解释执行逐指令模拟 RDP 命令会导致帧率低下,尤其在复杂场景如《塞尔达传说》多层 Alpha 混合下。动态重编译(dynarec)通过运行时将 RDP 微码和命令列表 JIT 编译为 x86 原生代码,提供近原生速度,同时保持周期精确性。1964 模拟器 RDP 插件(Rice 开发)引入 dynarec,正是 2001 年早期实验的核心创新。
典型 RDP dynarec 管线分为四个阶段:指令获取(fetch)、解码(decode)、x86 JIT 代码发射(emission)和优化传递(optimization passes)。首先,fetch 阶段从 RSP 协处理器 DMA 传输的命令缓冲区(通常位于 RDRAM 0x80000000 起)读取 RDP 命令列表。RSP 通过 DMA 将显示列表(display list)推入 RDP FIFO,模拟器需监控 RDP status 寄存器(0x04080000)中的 cmd_start/end 指针。实际参数:缓冲区大小固定 4KB(1024 个 64 位命令),fetch 采用 32 字节对齐块读取,避免跨缓存线污染。阈值设置:若剩余命令 < 16 个,预取下一 DMA 块,减少 stall 概率至 < 5%。
解码阶段解析每个 RDP 命令的 opcode 和参数。RDP 命令格式为 w0/w1 双字,w0 高 16 位为 opcode(如 0xE4=FillRectangle,0xF1=TexRect),低 16 位参数;w1 类似。解码器使用 switch-table 映射 opcode 到 handler,但 dynarec 需构建 IR(中间表示):如 FillRect 解码为 {primitive=fill, x0,y0=bits [15:0],x1,y1=bits [31:16],color=w1}。工程实践:预解码微码(RDP 运行 RSP-like 微码),识别循环模式如纹理三角形批处理。参数清单:opcode 表大小 256(全覆盖),参数解包用 bitfield extract(x86 bmi 指令),解码延迟阈值 < 10 cycles/instr。
核心是 x86 JIT emission:将解码 IR 发射为 x86 块。每个基本块(basic block)为连续无分支命令序列,典型大小 32-128 命令(~2-8KB x86)。例如,TexRect 命令 JIT 为:加载纹理地址到 xmm0,设置 u/v scale 到 xmm1/xmm2,调用手写 asm rasterizer loop(SSE 内插像素)。发射用宏 assembler:#define EMIT_MOV (reg, val) emit_bytes (0xB8 | (reg<<3), (val)&0xFF, ...);针对 P4-era x86(2001 上下文),优先 MMX/SSE1,避免复杂 uop。回边(loop back)用近跳转,跨块用间接 call。清单:块入口 prolog(保存 RDP regs 到栈),epilog(更新 status,check interrupt)。
优化 passes 提升效率。第一 pass:寄存器分配(regalloc)。RDP 有有限状态(~64 regs 如 cmd_cur,color_image),但 x86 仅 8-16 GPR 可用。使用 linear scan 算法:liveness 分析(从后向前,bitset<64> live),优先分配热变量到 eax/ecx,溢出 spill 到 [esp-0x100]。参数:spill 阈值 reg_pressure>12 则 split block;live range split 每 64 instr。第二 pass:死代码消除(DCE)和常量折叠,如连续 SetFillColor 后 FillRect,直接内联 color。第三 pass:循环优化,如 TexRect 循环 unroll x4(利用 SSE),但限 unroll_factor=8 防 ICache miss。监控点:reg spills/frame <1%,hit rate>95%(否则 fallback 解释器)。
落地配置参数至关重要。缓存管理:dynarec 代码缓存 32MB,分 page-aligned 4KB 页,LRU evict(hit 率监控,每 1M instr flush if <90%)。回滚策略:若 JIT 块 crash(非法 mem access),标记 invalid,重译 fallback 到 interp(解释器阈值:hotness>1000 execs 才 JIT)。线程安全:1964 单线程,但加 lock-free queue for cmd fetch。性能调优:block_size=64(平衡编译开销 / 密度),align=32B(P4 cache line),SSE threshold(若 cmd 有 float,emit SSE else MMX)。测试清单:1. 基准《Mario 64》Hud 渲染,目标 60fps;2. 异常如 NaN color,trap 到 interp;3.Valgrind check mem safety。
实际部署中,监控指标包括:JIT 时间占比 <5%、块翻译率> 1K/s、spill 率 < 0.1%。若 hit 率降至 80%,增大 cache 或 aggressive evict cold blocks。风险缓解:opcode whitelist,仅 JIT known microcodes(fast3d/gbi 等),unknown fallback。相比现代 paraLLEl RDP(Vulkan LLE),1964 dynarec 更轻量,适合低端 x86。
此管线源于 2001 Zilmar 论坛文档和 Rice 插件源码分析,“emudev.org 致力于硬件文档保存。”1 类似设计影响后代如 Mupen64 RSP/RDP 插件。参考:emudev.org,Rice RDP 源(存档)。
(正文约 1250 字)