在嵌入式系统与复古计算复兴的背景下,从零(clean-room)实现 Z80 与 ZX Spectrum 模拟器已成为验证 AI 辅助编程能力的标杆实验。这种方法避免参考现有开源代码,仅依赖官方规范与测试向量,确保原创性与准确性。焦点在于低层工程细节:指令解码的 opcode 分发、内存访问回调、ULA 内存争用模型、视频时序同步与中断机制。这些要素直接决定了模拟器的周期精确性(T-states 准确率)和兼容性(如通过 ZEXALL 测试套件)。
Z80 指令解码与执行循环
Z80 模拟器的核心是高效的指令解码与执行循环。采用 “指令步进”(instruction-stepping)而非 “时钟步进”(cycle-stepping),每个 z80_step () 调用执行完整一条指令,返回消耗的 T-states 总数。这种设计简化了实现,同时满足 ZX Spectrum 等平台的时序需求。
解码采用 switch-case 或跳转表实现 256 个主 opcode、CB/ED/DD/FD 等前缀组,以及所有非官方指令(如 SLL、IXH 操作)。例如,主循环从 PC 读取 opcode,计算 T-states(包含 M 周期与内存访问),更新寄存器与标志位(SZ53P 表预计算 X/Y 位)。
关键参数清单:
- Opcode 分发表:uint8_t opcode_handlers [256],映射到执行函数。
- T-states 计算:LD r,r' = 4T;JR NZ = 12/7T(条件分支);BLOCK(如 LDIR)= 21/16T/5M。
- 标志位表:预计算 sz53p_table [256],覆盖 S/Z/5/3/P;特定指令如 CP 从操作数取 X/Y。
- R 寄存器:每指令递增低 7 位(opcode fetch + prefix),位 7 来自 LD R,A。
落地实现:每步前检查 PC 陷阱(如 CP/M BDOS CALL 0005h),后更新 cpu.clocks += tstates。测试覆盖 154 单元测试 + ZEXDOC/ZEXALL(100% 通过)。
“Claude Code 产生的 Z80 核心在 1200 行 C 代码中通过了 ZEXDOC 和 ZEXALL 测试。” 此验证确保标志与时序精确,避免 “硅行为” 偏差。
内存模型与访问回调
内存模型通过函数指针解耦:z80_t.mem_read (addr)、mem_write (addr, val)。ZX Spectrum 布局:0x0000-0x3FFF ROM(只读,写忽略);0x4000-0xFFFF RAM(48K)。
回调注入平台特定逻辑:
- ROM:直接返回 rom [addr](不复制节省内存)。
- RAM:spectrum->ram [addr-0x4000],注入 ULA 争用。
- I/O:端口解码(偶地址),ULA 返回键盘矩阵(行选择 & 按键位)/ 边框色。
参数:
- 地址掩码:I/O 仅低位有效,A0=0 触发 ULA。
- 键盘矩阵:8 行 x 5 位(active low),多行 OR。
- Kempston 摇杆:A14-A12=0,返回高电平方向 / 火键。
此模型支持多平台:Spectrum/CP/M 通过不同回调复用 Z80 核心。
ZX Spectrum 视频时序与 ULA 争用
ZX Spectrum 的挑战在于 ULA 与 Z80 共享 0x4000-0x7FFF RAM,导致 “争用”(contention)。显示期(scanline 64-255,前 128 T-states),ULA 抢占总线,CPU 等待。
精确模型不用 69KB 查找表,而是算术计算:
- 帧 T-state → scanline = ts / 224,pos = ts % 224。
- 若 contended(scanline 64-255,pos <128),延迟 = contention_table [(ts % 8)]。
- 争用表(8 T-state 周期):
周期位 0 1 2 3 4 5 6 7 等待态 6 5 4 3 2 1 0 0
在 mem_read/write 回调中添加 cpu.clocks += delay,确保总 T-states 匹配真实硬件。
视频时序:
- 帧参数:69888 T-states,312 scanline x 224 T-states,50.08 Hz。
- V-sync:前 8 线。
- 上 / 下边框:各 56 线。
- 显示:192 线(256x192 像素)。
- zx_tick (min_tstates):执行 ≥min,帧边界优先返回(frame_tstates=0)。
- 渲染:自动(逐 scanline zx_set_framebuffer)或按需 zx_render_screen。
- 位图:0x4000-0x57FF,非线性(像素 x,y → 010 Y7Y6 Y2Y1Y0 | Y5Y4Y3 X7..X3)。
- 属性:0x5800-0x5AFF,墨 / 纸 / 亮度 / 闪光。
音频:1-bit 蜂鸣,帧末生成 882 PCM 样本(44.1kHz),队列深度控速。
中断与帧同步
中断使用 IM1:每帧 T-state 0 生成 INT(向量从 0x0038 取)。FLASH 每 16 帧翻转(attr bit 7)。
参数:
- 中断向量:0x0038(ROM 处理键盘 / FLASH)。
- 帧事件:边界时 flush scanline、渲染音频、递增 frame_count、FIRE NMI(可选)。
- 粒度控制:
调用 min_tstates 用例 zx_tick(zx, 0) 1 指令 磁带加载(EAR 更新) zx_tick(zx, 224) 1 scanline 嵌入式渲染 zx_frame(zx) 全帧 桌面模拟
监控点:
- cpu.clocks:总 T-states(uint64_t)。
- frame_tstates:当前帧内(0-69887)。
- border_color:实时边框(0-7)。
- speaker:当前电平(0/1)。
工程化落地清单
- 规范收集:Z80 数据手册、Spectrum ULA 文档、ZEX 测试二进制。
- 核心实现:Z80 (z80.c) → Spectrum (spectrum.c) → TZX (tzx.c)。
- 测试:make test (232 单元);make fulltest (ZEX)。
- 回滚阈值:T-states 偏差 >1% → 调试标志 / 转储。
- 嵌入式优化:无 SDL,ROM 指针(非复制),8B 争用表。
此实践证明,AI 辅助下 clean-room 实现可达生产级:1.2k 行 Z80 通过全测试,Spectrum 支持 .z80/.tzx 加载,CP/M 跑 WordStar。适用于 RP2040 等 MCU,推动复古游戏 / 教育模拟。
资料来源:
- antirez 实验:https://antirez.com/news/160
- ZOT 仓库:https://github.com/antirez/ZOT