当我们谈论 CSS 时,很少会将其与「计算」联系在一起。这个诞生于 1994 年的样式语言,本质上只是描述文档呈现方式的声明式规则。然而,Lyra Rebane 开发的 x86CSS 项目彻底打破了这一认知边界 —— 它是一个完全运行在 CSS 中的 x86 CPU 模拟器,能够执行真实的 8086 机器码,且无需任何 JavaScript。
核心架构:从机器码到 CSS 表达式的转译
x86CSS 的设计哲学并非在运行时解析 x86 指令,而是采用离线转译的方式,将编译好的机器码预先转换为 CSS 可理解的表达形式。项目作者首先编写 C 程序,使用 gcc-ia16 编译为 8086 机器码,随后通过 Python 脚本将二进制指令反汇编为结构化的数据表,最终生成包含所有指令信息的 DOM 节点与 CSS 规则集合。
每个 x86 指令被分解为多个字段:操作码(opcode)、操作数类型、寄存器读写信号、标志位更新需求等。这些信息以 HTML 元素和 CSS 属性的形式嵌入页面。指令指针(EIP)本身就是一个 CSS 变量,通过属性选择器 body[step="42"] .inst-42 来激活当前执行的指令。这种设计将控制流从「执行」转化为「状态选择」—— 哪个指令被激活,取决于 EIP 的当前值。
寄存器与内存的 CSS 建模
传统 CPU 的寄存器是硬件电路中的存储单元,而在 x86CSS 中,通用寄存器被建模为一系列 CSS 自定义属性。例如,EAX 寄存器被拆解为 --eax0、--eax1 等位级属性,每个属性存储对应位的值。这种拆分策略使得后续的算术逻辑可以在位操作层面展开。
状态标志位(ZF、CF、OF 等)同样作为布尔类型的 CSS 变量存在。当执行如 ADD、SUB 等影响标志位的指令时,CSS 规则会根据操作结果动态更新这些变量的值。后续的条件跳转指令(如 JZ、JNZ)则通过检查这些标志位变量来决定下一条指令的地址。
内存模型的实现更具创造性。项目将有限的内存区域(默认 0x600 字节,即 1.5KB)映射为 --mem_0000、--mem_0001 等属性序列。地址计算在编译阶段完成 —— 对于 [ebp+4] 这样的内存寻址模式,转译器预先计算出对应的 CSS 属性名,运行时只需直接访问即可。这种策略规避了在 CSS 中实现通用地址运算的难题。
算术逻辑的 CSS 实现
CSS 缺乏显式的循环和条件分支,但它提供了 calc()、min()、max() 等计算函数。x86CSS 利用这些函数构建基本的算术电路。位级逻辑运算通过数学恒等式实现:AND 可以用乘法近似,OR 则通过 min(1, a + b) 这样的表达式模拟。进位标志(CF)的计算则依赖预生成的查找表,由转译器在编译阶段生成对应的控制信号。
选择器在此扮演了多路复用器的角色。对于一条 ADD 指令,当且仅当其被标记为「当前活跃指令」时,相应的 CSS 规则才会覆盖目标寄存器的属性值。规则之间通过特异性(specificity)机制确保互斥 —— 每个时钟周期只有一套规则生效,从而实现类似顺序执行的逻辑。
时钟驱动与用户交互
CPU 需要时钟信号来推进执行周期。x86CSS 使用两种时钟实现方式:首选方案是利用 CSS 动画配合样式容器查询(style container queries),产生自动推进的时钟信号,用户无需任何交互即可观察程序运行;备用方案则依赖鼠标悬停状态,类似 Jane Ori 的经典 CPU Hack 项目。前者更符合「纯 CSS」的理念,但速度较慢且稳定性略差;后者响应迅速,但部分研究者认为需要持续用户输入不满足图灵完备的严格定义。
对于程序输出,x86CSS 提供了内存映射 I/O 接口。特定地址(0x2000 系列)被预留为系统调用入口:C 程序可以将这些地址强制转换为函数指针,调用由 CSS 实现的输出例程。例如,向 0x2000 地址写入字符数据会触发 CSS 规则,将数据推送至 DOM 缓冲区并渲染为可视文本。键盘输入则通过 0x2100 地址的标志位控制 CSS UI 组件的显示状态,用户点击的键值会写回内存供程序读取。
工程约束与实用考量
尽管 x86CSS 实现了令人惊叹的复杂功能,但它并非完整的 8086 模拟器。部分标志位行为未被精确模拟,指令集也经过了精简 —— 作者采用「按需实现」的策略,仅编译运行所需的指令。这种务实的方法大幅降低了开发工作量,同时保证了演示程序的正常运行。
性能是另一个显著瓶颈。每条指令的执行涉及大量的 CSS 属性重计算,复杂的程序可能需要数秒甚至更长时间完成一个循环。然而,项目的目标从来不是替代真正的 CPU 或高性能模拟器,而是探索 CSS 的能力边界,验证浏览器渲染引擎的计算潜力。
从工程实践角度看,x86CSS 展示了几个关键启示:首先,离线转译是处理复杂计算任务的有效策略,将 heavy lifting 转移到构建阶段;其次,现代 CSS 特性(自定义属性、容器查询、calc ())赋予了样式语言远超预期的表达能力;最后,浏览器渲染引擎可以被重新定义为通用计算平台,只要合理利用其依赖追踪与级联计算机制。
资料来源
- x86CSS 官方项目页面:https://lyra.horse/x86css/
- GitHub 仓库:https://github.com/rebane2001/x86css