当英特尔在 1985 年推出 80386 处理器时,这颗芯片不仅带来了 32 位架构的革命,更在算术运算性能上实现了质的飞跃。在早期的 8086 处理器中,16 位除法指令需要超过 150 个时钟周期才能完成,而 386 将这一时间大幅缩短至 38 个周期(32 位操作数)。这种性能提升的核心并非单纯依靠时钟频率的提升,而是得益于一套精心设计的微架构 —— 它复用主 ALU 完成每周期的加减运算,同时通过专用数据通路处理移位和循环控制。本文将深入剖析 80386 整数除法单元的硬件实现细节,从非恢复除法算法的原理出发,逐步解析微代码层面的控制逻辑与关键微操作的作用机制。
非恢复除法算法的原理与硬件映射
80386 采用标准的非恢复除法算法(Non-Restoring Division Algorithm)来实现整数除法,这一算法相较于传统的恢复除法具有更高的硬件效率。在非恢复除法中,每一轮迭代根据当前部分余数的符号决定下一步是加除数还是减除数:如果部分余数为负,则下一轮加上除数;如果部分余数为正或零,则下一轮减去除数。这种 "加、减交替" 的策略避免了恢复除法中余数需要 "恢复" 的步骤,从而减少了每轮迭代所需的硬件操作。算法的核心在于,每一轮迭代都会将部分余数与除数进行比较,根据比较结果生成商的一位,同时更新部分余数为下一次迭代做准备。整个过程需要 N 轮迭代才能得到 N 位商,其中 N 为操作数的位宽。
在 80386 的硬件实现中,非恢复除法算法被映射到三个关键的内部寄存器:SIGMA 用于存放部分余数,DIVTMP 用于存放被除数的低 32 位,而 TMPB 则用于存放除数。被除数的高 32 位直接来自 EAX 的符号扩展部分,在除法开始前会被加载到 SIGMA 寄存器中。RESULT 寄存器则用于累加每一位商,随着迭代的进行不断左移并将新的商位填充到最低位。值得注意的是,这套数据通路的宽度为 64 位,能够完整容纳 32 位被除数与 32 位除数参与运算时产生的所有中间结果和最终余数。英特尔的设计哲学在这套数据通路中得到了充分体现 —— 通过复用主 ALU 完成每轮的加法或减法运算,避免了为除法单独设计专用乘法器,从而在硅片面积和性能之间取得了良好的平衡。
DIV7 与 DIV5:微操作级别的迭代控制
80386 的除法微代码将非恢复除法算法分解为两个关键的微操作:DIV7 和 DIV5。DIV7 是除法循环体的核心实现,它在一个时钟周期内完成部分余数的移位、加减判断以及商位的生成。具体而言,DIV7 首先将 SIGMA 和 DIVTMP 组成的 64 位部分余数左移一位,释放出新的商位位置;随后根据 SIGMA 的符号位决定是加除数还是减除数:如果符号位为 1(表示负余数),则执行 SIGMA 加上 TMPB;如果符号位为 0(表示正余数或零),则执行 SIGMA 减去 TMPB;最后,根据更新后的 SIGMA 是否非负来决定当前商位是 1 还是 0,并将这一位写入 RESULT 寄存器的最低位。整套操作在一个时钟周期内由硬件数据通路完成,微代码只需要通过 RPT(Repeat)指令控制循环次数即可。
DIV5 则是整个除法迭代结束后的最终校正步骤。非恢复除法算法在最后一轮迭代后可能产生一个负的 "假余数",需要通过一次额外的加除数操作将其修正为正确的余数。DIV5 正是执行这一校正操作的微操作:当 SIGMA 为负时,它将除数加回到 SIGMA 上,使其变回正确的余数值。这一校正步骤是非恢复除法的标志性特征,也是它与恢复除法的关键区别所在。80386 的微代码通过两行简洁的指令就完成了整个除法过程:首先执行 SIGMA TMPB DIV7 RPT DLY 启动循环,让 DIV7 在 RPT 控制下自动迭代 N 次(其中 N 等于操作数位宽减一);随后执行 SIGMA TMPB DIV5 完成最后的校正。整个过程中,微代码扮演着 "orchestrator" 的角色,负责设置初始条件、启动循环和处理结果,而真正繁重的计算任务则由硬件数据通路在每个时钟周期高效完成。
有符号除法的额外复杂度:IDIV 的微代码处理
80386 的有符号整数除法指令 IDIV 比无符号的 DIV 复杂得多,这不仅体现在执行周期数的差异上(43 周期对比 38 周期),更体现在微代码必须处理符号转换和结果修正这一系列额外步骤。IDIV 的微代码实现采用了 "三步走" 的策略:首先将被除数和除数都转换为绝对值,执行无符号除法,最后再根据原始操作数的符号对商和余数进行符号校正。这种方法的优势在于主除法循环可以使用与 DIV 完全相同的硬件逻辑,不需要为有符号情况设计额外的 ALU 操作,从而简化了硬件设计。
IDIV 微代码中的三个特殊微操作揭示了这一复杂性。PREDIV 负责计算被除数和除数的绝对值,并将它们的原始符号保存到内部的触发器中,同时执行除法的第一轮迭代。IDIV1 在主循环结束后校正余数的符号 —— 按照数学定义,余数必须与被除数同号,因此如果原始被除数为负,即使除法过程使用的是绝对值,最终也需要将余数取反。IDIV2 则校正商的符号:如果被除数和除数的符号不同,则商应为负数。这三个微操作的协同工作解释了为什么 IDIV 比 DIV 多消耗 5 个时钟周期 —— 这些周期全部用于符号相关的计算和校正。有趣的是,80386 在这里选择了让微代码承担符号处理的重担,而非在硬件层面增加额外的符号判断逻辑,这体现了当时处理器设计中 "软件加速" 与 "硬件优化" 之间的权衡艺术。
从迭代到并行:除法算法的演进启示
80386 所采用的 "一位每周期" 迭代除法方案,在 1985 年的技术条件下是一种优雅而实用的工程折中。它复用了现有的 ALU 硬件,避免了为除法单独设计复杂的乘法器阵列,从而在有限的硅片面积内实现了可接受的除法性能。然而,这种方案的局限性也很明显:对于 32 位操作数,最少需要 32 个时钟周期才能完成除法,这在现代处理器的高频环境下是不可接受的。现代 x86 处理器采用了截然不同的策略:radix-4 或 radix-16 除法通过每周期计算 2 到 4 位商来大幅缩短迭代次数,而猜测执行和值预测技术则允许处理器在除法完成之前就继续执行后续指令,从而隐藏除法的延迟。理解 80386 的除法微架构,不仅是一次对经典处理器设计的致敬,更是理解计算机体系结构如何在性能、功耗和复杂度之间持续演进的重要窗口。微代码驱动、复用 ALU、迭代计算 —— 这些设计决策在四十年前是工程智慧的选择,在今天则成为了理解现代处理器如何解决同样问题的历史参照系。
资料来源:本文核心内容来自 nand2mario 对 80386 微代码的反向工程分析,该工作建立在 reenigne、gloriouscow、smartest blob 以及 Ken Shirriff 等研究者的硅级逆向工程基础之上。