Skip to content

AF

在标志寄存器(RFLAGS)中,AF (Auxiliary Carry Flag) 被称为 辅助进位标志位。

如果说 CF (Carry Flag) 关注的是整个字节(第 7 位)是否向外进位,那么 AF 关注的就是字节中间的“小关卡”——第 3 位向第 4 位的进位。


一、 AF 的定义

在进行算术运算时: * AF = 1:表示结果的低 4 位(第 3 位)向高 4 位(第 4 位)产生了进位或借位。 * AF = 0:表示没有产生进位或借位。

二进制结构图示: 一个字节有 8 位,可以拆成两个 4 位(这 4 位在计算机术语中称为 Nibble,即“半字节”):

  [ 高 4 位 ]   [ 低 4 位 ]
   7  6  5  4 |  3  2  1  0  <-- 位数
              ^
              |
          AF 监控这里


二、 举个例子

假设我们执行两个十六进制数的加法:

1. 触发 AF 的情况

mov al, 0x0F    ; 二进制: 0000 1111
add al, 0x01    ; 二进制: 0000 0001
  • 分析:0x0F + 0x01 = 0x10。
  • 在二进制下,低 4 位的 1111 加上 1 变成了 10000。
  • 那个第 4 位的 1 就是从低 4 位“溢出”过去的。
  • 结果:AF = 1。(此时 CF 依然是 0,因为总结果没超过 8 位)。

2. 不触发 AF 的情况

mov al, 0x01
add al, 0x01
  • 分析:0x01 + 0x01 = 0x02。
  • 二进制:0001 + 0001 = 0010。
  • 进位只发生在第 0 位向第 1 位,没有跨越 3-4 位的界限。
  • 结果:AF = 0。

三、 为什么会有 AF?(BCD 码的历史)

你可能会纳闷:为什么要专门盯着第 3 位看?这看起来非常没用。

这又是另一个历史遗留产物,专门为 BCD 码(Binary Coded Decimal) 设计的。

  • 什么是 BCD 码? 在极早期的计算机中,为了方便显示数字,人们用 4 位二进制来代表一个 10 进制数字(0-9)。

    • 0000 代表 0
    • 1001 代表 9
    • 一个字节(8 位)就可以代表两个十进制数(例如 0001 0010 代表十进制的 12)。这叫压缩 BCD 码。
  • 痛点: 二进制加法和十进制加法逻辑不同。比如 BCD 码的 9 + 1:

    • 二进制算出来是 1010 (十六进制的 A)。
    • 但 BCD 码希望得到的是 0001 0000 (即十进制的 10)。
  • AF 的作用: 当 CPU 发现 AF = 1 时,说明低 4 位的十进制运算“满 10 进位”了。程序员可以使用特殊的指令(如 DAA - Decimal Adjust after Addition)根据 AF 的值自动把结果修正为正确的十进制显示。


四、 现状:它在现代开发中还有用吗?

答案是:几乎完全没用了。

  1. 64 位模式禁用:在 x86-64 架构(即现代 64 位模式)中,处理 BCD 码的指令(如 AAA, DAA, AAS, DAS)已经全部被移除或禁用了。
  2. 现代做法:现在的程序员如果需要处理高精度十进制,会直接使用软件算法或浮点数,不再依赖这种硬件层面的半字节进位。
  3. 不可跳转:与 ZF 或 CF 不同,x86 指令集甚至没有提供基于 AF 状态进行跳转的指令(没有像 JA 或 JZ 这种对应的 JAF 指令)。

五、 总结

  • AF 监控的是从 第 3 位到第 4 位 的进位。
  • 它是为了 BCD 码(十进制修正) 而存在的。
  • 在 64 位汇编学习 中,你完全可以无视它的存在。如果你在 GDB 里看到它亮起,知道那是 CPU 在例行公事地更新旧时代遗留的状态即可。

至此,主要的 6 个状态标志位你已经全部学完了: 1. CF (进位) - 整体进位。 2. ZF (零) - 结果是否为 0。 3. SF (符号) - 结果的正负。 4. OF (溢出) - 有符号数计算错误。 5. PF (奇偶) - 低 8 位 1 的个数。 6. AF (辅助进位) - 半字节进位。

你现在对这些“状态开关”的感觉如何?是否准备好看看如何用它们来写 if/else 逻辑了?