Hotdry.
systems-engineering

Apple II Hi-Res图像压缩:LZ4FH算法与8位寄存器优化

针对Apple II Hi-Res图像的8KB内存布局,深入解析LZ4FH压缩算法的8位寄存器优化策略、屏幕空洞处理与性能参数对比。

在复古计算领域,Apple II 的 Hi-Res 图像格式(8192 字节)代表了 8 位时代的图形显示极限。然而,这个看似简单的 8KB 内存布局隐藏着复杂的工程挑战:特殊的屏幕空洞结构、有限的处理器资源,以及磁盘存储空间的宝贵性。本文将深入探讨如何为这一特定硬件平台设计高效的压缩算法,特别是 LZ4FH(LZ4 for Hi-Res)算法的 8 位寄存器优化策略。

Apple II Hi-Res 内存布局的独特性

Apple II 的 Hi-Res 显示模式采用了一种非线性的内存映射方式。整个 8192 字节的显存被划分为 128 字节的块,每个块对应屏幕上的 3 行像素。但这里有一个关键设计:每 128 字节块中,只有前 120 字节包含实际的像素数据,剩余的 8 字节是 "屏幕空洞"(screen holes)。

这种设计源于硬件限制 ——Apple II 的视频生成电路需要特定的时序间隔。从软件角度看,这些空洞在屏幕上不可见,但它们占据了内存地址空间。正如 Stack Exchange 上的讨论所指出的,这些空洞 "被用于固件或扩展卡,不应被随意修改"。

对于压缩算法而言,这意味着我们需要处理两种不同类型的数据:

  1. 可见像素数据(120 字节 / 块)
  2. 屏幕空洞数据(8 字节 / 块)

传统 RLE 压缩的局限性

在 Apple II 的黄金时代,大多数图像压缩工具采用运行长度编码(RLE)。RLE 算法简单直接:将连续的相同字节替换为 "计数 + 值" 对。对于 8 位系统,RLE 具有明显优势:

  • 实现简单,代码体积小
  • 编码和解码速度快
  • 内存占用少

然而,RLE 在处理复杂图像时效率低下。Apple II Hi-Res 图像虽然只有 560×192 分辨率,但游戏画面和艺术图像往往包含丰富的细节和渐变,这些都不适合 RLE 压缩。测试数据显示,RLE 对某些图像的压缩率可能低至 10-20%,远不能满足实际需求。

LZ4FH:为 8 位寄存器优化的 LZ4 变体

LZ4FH 算法是 LZ4 压缩格式的专门修改版,针对 Apple II 的 8 位架构进行了深度优化。核心思想是创建一个 "非对称编解码器":压缩过程可以在现代硬件上缓慢进行,但解压必须在 Apple II 上极快执行。

8 位寄存器优化策略

LZ4FH 的关键修改在于适应 8 位寄存器的限制。标准 LZ4 使用 16 位或 32 位的偏移量和长度字段,这在 8 位系统上处理效率低下。LZ4FH 的优化包括:

  1. 字节对齐的匹配操作:所有数据比较和复制操作都设计为 8 位友好,避免跨字节边界的复杂操作。

  2. 简化的控制流:减少条件分支,使用线性解码流程,这在 6502 处理器上特别重要,因为分支预测代价高昂。

  3. 内存访问模式优化:利用 Apple II 内存访问的特性,将频繁访问的数据保持在零页(zero page)内存中。

两种压缩模式的选择

fhpack 工具提供了两种压缩模式,类似于 LZ4 的 "快速" 和 "高压缩" 模式:

  • 快速模式:使用贪婪解析算法,虽然不是特别快,但对于 8KB 数据来说可以接受。平均压缩时间在几秒内。

  • 高压缩模式:使用最优解析算法,比快速模式慢 12 倍,但压缩率提高约 4%。对于图像库的批量处理,这种模式更有价值。

值得注意的是,压缩端的复杂算法不能在 6502 上实现。正如项目文档所述:"最优解析理论上可以在 128KB RAM 的机器上完成,但需要非常长的时间运行。" 这体现了非对称设计的合理性。

屏幕空洞处理的工程化参数

处理屏幕空洞是 Apple II 图像压缩特有的挑战。fhpack 提供了三种处理策略,每种都有其适用场景:

1. 保留模式(-h 标志)

参数:-h
效果:完全保留原始空洞数据
适用场景:需要精确数据还原的应用
压缩率:最低

2. 填零模式

效果:将所有空洞填充为零
适用场景:大多数图像,特别是空洞原本就为零的情况
压缩率:中等

3. 模式匹配填充

效果:用相邻数据的模式填充空洞
适用场景:空洞包含有意义但非必要的模式数据
压缩率:最高(在某些情况下)

实际测试显示,模式 2 和 3 的差异通常在 70-90 字节范围内。由于现代硬件上 fhpack 运行速度很快,工具默认会尝试两种方法并选择压缩率更高的结果。

性能参数与对比数据

基于约 70 个测试图像(主要来自游戏和早期贡献程序)的基准测试,我们可以得到以下关键性能数据:

压缩率对比

算法 总字节数 压缩率
LZ4-HC 248,473 37.4%
fhpack (LZ4FH) 242,771 36.5%
LZW/II (ShrinkIt) 232,201 34.9%

解压速度参数

  • 6502 处理器:约 5.6 FPS(消除所有开销的基准测试)
  • 65816 处理器:约 12 FPS(使用批量数据复制指令)

磁盘加载时间对比

在 AppleWin 模拟器中使用 "真实" 磁盘访问速度:

  • 未压缩图像:约 1.7 秒 / 图像(0.6 FPS)
  • 压缩图像:约 1.4 秒 / 图像(0.7 FPS)

这意味着对于 5.25 英寸磁盘,加载压缩图像并解压通常比直接加载未压缩图像更快。

实际应用中的工程考量

内存布局约束

解压器需要两个参数:压缩数据的地址和输出缓冲区的地址。在当前实现中,输出缓冲区必须是 $2000 或 $4000(两个 Hi-Res 页面)。这些地址通过内存位置 $02FC 和 $02FE 传递。

文件格式兼容性

压缩图像使用 FOT($08)文件类型,auxtype 为 $8066(0x66 是 ASCII 'f')。这些文件可以在 CiderPress v4.0.1 及更高版本中查看。

代码体积优化

6502 版本的解压器代码非常紧凑,适合嵌入到各种应用程序中。65816 版本虽然更大,但利用了处理器的增强指令集,特别是数据移动指令,显著提高了性能。

技术局限性与替代方案

LZ4FH 的局限性

  1. 不可压缩数据的处理:对于完全随机的数据(如 test/nomatch),LZ4 无法压缩,但 fhpack 通过处理屏幕空洞仍能获得一定压缩率。

  2. 压缩端资源需求:高压缩模式需要现代硬件,无法在 Apple II 上运行。

替代算法:LZSS

LZSS(Lempel-Ziv-Storer-Szymanski)是另一个可行的选择,被 HardPressed 等工具使用。它将测试语料库压缩到 243,991 字节(36.7%),虽然通常不如 LZ4,但在某些情况下是可行的替代方案。

LZSS 的主要限制是最大匹配长度和偏移量较短,但对于 Hi-Res 图像来说这并不太重要。更大的问题是文字字节使用单独的标志位标识,而不是作为字节流处理,这对长文字串的性能有影响。

结论:复古计算的现代启示

Apple II Hi-Res 图像压缩的案例展示了如何将现代压缩算法适配到复古硬件上。LZ4FH 的成功关键在于:

  1. 非对称设计哲学:接受压缩和解压的不对称性,在现代硬件上完成繁重工作。

  2. 硬件特性利用:深度理解目标平台的架构限制(8 位寄存器、内存布局、指令集)。

  3. 领域特定优化:针对特定数据格式(屏幕空洞)设计专门的优化策略。

对于今天的开发者来说,这个案例的启示是:即使在资源受限的环境中,通过精心设计的算法和深入理解硬件特性,仍然可以实现令人印象深刻的性能提升。这种 "为特定平台深度优化" 的思维方式,在现代嵌入式系统和边缘计算中仍然具有重要价值。

资料来源

  1. fhpack - Compression for Apple II hi-res images
  2. What are the "Screen Holes" in Apple II graphics?
查看归档