开发者生态
morning
WriteUp:16 字节 x86 将矩阵雨变成声音
2026-05-18
1 阅读
HellMood
醒醒吧! 16b 于 2026 年 5 月在荷兰奥门举行的 Outline Demoparty 上发布,探索 16 字节 x86 程序集的算法密度。观看视频 Demozoo 入门 在演示场景中,探索在极端限制下可以实现的目标是一项有益的技术挑战。以下 16 字节的 x86 实模式 DOS 汇编代码代表了算法密度的仔细练习。执行时,它利用计算机的视频内存作为计算空间来绘制无限谢尔宾斯基分形,同时将该几何图形解释为音频数据。整数 10 小时; 2 字节 mov bh, 0xb8 ; 2 字节 mov ds, bx ; 2字节L:lodsb; 1 字节子 si,字节 57 ; 3 字节 xor [si], al ; 61h,al 输出 2 个字节; 2 字节 jmp 短 L ; 2 个字节 1. 画布:Primed Void 代码以标准 BIOS 中断开始: int 10h 。这将初始化视频模式 0,建立 40x25 文本模式网格。后续指令将数据段 ( DS ) 指向 0xB800,即 VGA/CGA 文本缓冲区的物理内存地址。当 BIOS 在此中断期间清除屏幕时,它不会用绝对零填充内存。在文本模式下,每个字符空间由两个字节组成:ASCII 字符和颜色属性。 BIOS 统一初始化所有 2,000 个字符槽:ASCII 字节设置为 0x20(空格字符),颜色字节设置为 0x07(黑色背景上的浅灰色文本)。虽然屏幕看起来完全是空的,但它实际上是一个涂有统一数据模式的画布。一致性的重要性:我们即将探索的数学级数依赖于可预测的环境。如果内存包含随机工件数据,算法计算将吸收这些差异。在元胞自动机中,意外的位可能会破坏模式。合理均匀的存储空间为分形的清晰显现提供了基础。 2. 引擎:加法前缀和 为了理解分形的纯数学,让我们暂时隔离我们的变量。我们将模拟一个完美的归零状态,而不是基本的 0x20 初始化。此外,我们将用 add 代替 xor ,并在该内存中一次前进 16 个字节,假设累加器 AL 加载了值 2 。实模式 DOS 段正好跨越 65,536 字节。通过每次迭代向前移动 16 个字节,遍历该段正好需要 4,096 步 (\( 65536 / 16 = 4096 \))。当 SI 寄存器超过 0xFFFF 时,它会干净地回绕到 0x0000。随着循环的进行,它将累加器的当前值添加到存储单元,并将更新后的值读回累加器。这有效地创建了运行前缀和。由于 4,096 是 256(我们的 8 位寄存器的容量)的倍数,因此当段换行时,数学结转会对齐,从而在每次完整扫描结束时将 AL 干净地重置为 2。在跨段传递 \( p \) 期间,第 \( k \) 个修改单元的值遵循二项式系数序列,按初始值 2 缩放: $$A^{(p)}[k] \equiv 2 \binom{k+p}{p-1} \pmod{256}$$ 下表说明了跨 16 个存储单元的前 16 个计算步骤,演示了值如何逐行累积:传递 \ Cell 3。结晶:异或和谢尔宾斯基移位 这些十进制值中存在更深层次的模式。当执行二进制加法时,位平面转移到相邻位置。但是,如果我们丢弃算术进位并严格执行模 2 的加法,则只剩下异或 (XOR) 运算。通过使用 xor 而不是 add ,该算法隔离了位平面。因为我们建模的起始值为 2(二进制 00000010 ),所以只有位 1 受到此特定计算的影响。级联十进制数成为 0x00 和 0x02 之间的纯粹切换。这个级数完美地映射到 Stephen Wolfram 的基本元胞自动机中的规则 60: $$Cell^{(p)}[k] = Cell^{(p-1)}[k] \oplus Cell^{(p)}[k-1]$$ 根据卢卡斯定理,这种 XOR 关系在数学上保证与加法表中的位 1 的状态匹配。我们可以通过可视化二进制传播来验证这一点(其中“2”表示设置了位 1):Pass \ Cell 4. 机器的声音:将数据转换为音频 指令中有一个非常优雅的细节:out 61h,al Port 61h 与内部 PC 扬声器的接口。该端口的位 1 直接控制扬声器锥体,设置为 1 时将其向外推动,设置为 0 时将其返回。我们的例程通过 XOR 计算分形,更新内存,并立即将该字节发送到扬声器端口。由于该算法专门隔离和切换位 1,因此谢尔宾斯基三角形的几何形状可用作扬声器纸盆的直接指令集。 CPU 的执行速度决定了功能采样率。分形生成的 1 和 0 模式产生不同的方波,自然变化