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,即“半字节”):
二、 举个例子
假设我们执行两个十六进制数的加法:
1. 触发 AF 的情况
- 分析:
0x0F + 0x01 = 0x10。 - 在二进制下,低 4 位的
1111加上1变成了10000。 - 那个第 4 位的
1就是从低 4 位“溢出”过去的。 - 结果:AF = 1。(此时 CF 依然是 0,因为总结果没超过 8 位)。
2. 不触发 AF 的情况
- 分析:
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代表 01001代表 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 的值自动把结果修正为正确的十进制显示。
四、 现状:它在现代开发中还有用吗?
答案是:几乎完全没用了。
- 64 位模式禁用:在 x86-64 架构(即现代 64 位模式)中,处理 BCD 码的指令(如
AAA,DAA,AAS,DAS)已经全部被移除或禁用了。 - 现代做法:现在的程序员如果需要处理高精度十进制,会直接使用软件算法或浮点数,不再依赖这种硬件层面的半字节进位。
- 不可跳转:与 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 逻辑了?