Hotdry.
systems

使用 Claude Code 实现纯净室 Z80 与 ZX Spectrum 模拟器:指令解码与时序参数

从零构建 Z80 指令解码器、内存模型、视频时序与中断处理:给出精确 T-states 计数、争用延迟表与帧同步参数。

在嵌入式系统与复古计算复兴的背景下,从零(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)。

工程化落地清单

  1. 规范收集:Z80 数据手册、Spectrum ULA 文档、ZEX 测试二进制。
  2. 核心实现:Z80 (z80.c) → Spectrum (spectrum.c) → TZX (tzx.c)。
  3. 测试:make test (232 单元);make fulltest (ZEX)。
  4. 回滚阈值:T-states 偏差 >1% → 调试标志 / 转储。
  5. 嵌入式优化:无 SDL,ROM 指针(非复制),8B 争用表。

此实践证明,AI 辅助下 clean-room 实现可达生产级:1.2k 行 Z80 通过全测试,Spectrum 支持 .z80/.tzx 加载,CP/M 跑 WordStar。适用于 RP2040 等 MCU,推动复古游戏 / 教育模拟。

资料来源

查看归档