x86-64如何设置条件码

前言

本文讲述x86-64是如何设置条件码状态的

常用条件码

除了整数寄存器,CPU还维护着一组单个位的条件码寄存器。他们描述了最近的算术运算和逻辑操作属性,可以通过检测这些寄存器来执行条件分支指令,最常用的条件码有:

  • CF:进位标志。最近的操作使最高位产生了进位。可用来检查无符号操作的溢出。
  • ZF:零标志。最近的操作得出的结果为0。
  • SF:符号标志。最近的操作得到的结果为负数,只需要设置SF等于最高位即可
  • OF:溢出标志。最近的操作导致一个补码溢出,可以是正溢出或者负溢出

ZF

任何算术运算和逻辑操作都可能影响ZF标志,这个标志的设置比较简单,判断结果是否是0

SF

这个是表示得到的结果是否为负数,计算机判断负数的条件就是最高位是否为1,至于当前数是不是有符号整数,这是编译器需要做的事情,所以只需要设置SF等于最高位即可

OF

这个标志位设置的规则如下:

如果最高位发生了进位,A=1,否则A=0
如果次高位发生了进位,B=1,否则B=0
则OF = A^B(A与B的异或)

因为OF只作为有符号数的比较判断,所以我们用有符号数的编码规则来证明一下A^B是否能代表发生了溢出

假设有两个有符号数A和B,为了好理解,我们假设A和B都是4位数,则A和B可以分别表示成(可参考文章 深入理解计算机中的整数):
A = a 3 a 2 a 1 a 0 , a i = 0 或者 a i = 1 A = a_3a_2a_1a_0, a_i=0或者a_i=1 A=a3a2a1a0ai=0或者ai=1
B = b 3 b 2 b 1 b 0 , b i = 0 或者 b i = 1 B = b_3b_2b_1b_0,b_i=0或者b_i=1 B=b3b2b1b0bi=0或者bi=1

⇒ A = − a 3 × 2 3 + a 2 × 2 2 + a 1 × 2 1 + a 0 × 2 0 \Rightarrow A = -a_3\times 2^3 + a_2\times 2^2 +a_1\times 2^1 +a_0\times 2^0 A=a3×23+a2×22+a1×21+a0×20
B = − b 3 × 2 3 + b 2 × 2 2 + b 1 × 2 1 + b 0 × 2 0 B = -b_3\times 2^3 + b_2\times 2^2 +b_1\times 2^1 +b_0\times 2^0 B=b3×23+b2×22+b1×21+b0×20
⇒ A + B = − ( b 3 + a 3 ) × 2 3 + ( b 2 + a 2 ) × 2 2 + ( b 1 + a 1 ) × 2 1 + ( b 0 + a 0 ) × 2 0 \Rightarrow A + B = -(b_3 + a_3)\times 2^3 + (b_2 + a_2)\times 2^2 +(b_1 + a_1)\times 2^1 +(b_0 + a_0)\times 2^0 A+B=(b3+a3)×23+(b2+a2)×22+(b1+a1)×21+(b0+a0)×20

那么根据发生溢出的条件:
如果 A < 0 A<0 A<0 并且 B < 0 B<0 B<0,但是 A + B ≥ 0 A+B\ge0 A+B0 → \rightarrow 负溢出
如果 A ≥ 0 A\ge0 A0 并且 B ≥ 0 B\ge0 B0,但是 A + B < 0 A+B<0 A+B<0 → \rightarrow 正溢出
至于为什么这两个是发生溢出的条件,文章计算机中的整数运算有详细介绍

如果 A < 0 A<0 A<0 并且 B < 0 B<0 B<0
⇒ b 3 = 1 \Rightarrow b_3=1 b3=1并且 a 3 = 1 a_3=1 a3=1
⇒ b 3 + a 3 = 2 \Rightarrow b_3+a_3=2 b3+a3=2,最高位发生了进位,最高位变成0
如果次高位没有发生进位,最高位最终是0,表示非负数,即 A + B ≥ 0 A+B\ge0 A+B0,发生了溢出,如果次高位发生了进位,最高位最终为1,表示负数,没有发生溢出

如果 A ≥ 0 A\ge0 A0 并且 B ≥ 0 B\ge0 B0
⇒ b 3 = 0 \Rightarrow b_3=0 b3=0并且 a 3 = 0 a_3=0 a3=0
⇒ b 3 + a 3 = 0 , \Rightarrow b_3+a_3=0, b3+a3=0最高位没有发生进位,最高位为0
如果次高位没有发生进位,最高位最终是0,表示非负数,即 A + B ≥ 0 A+B\ge0 A+B0,没有发生溢出,如果次高位发生了进位,最高位最终为1,表示负数,发生溢出

总结下来就是:

最高位发生了进位,次高位没有发生进位,溢出
最高位发生了进位,次高位发生进位,没有溢出
最高位没有发生了进位,次高位没有发生进位,没有溢出
最高位没有发生了进位,次高位发生进位,溢出

正好是异或的逻辑
所以标志位OF = 最高位发生了进位^次高位发生了进位能表示补码的溢出状态

CF

这个标志位的定义表述是最近的操作使最高位产生了进位,这个进位其实还包含借位,借位是两个数相减是可能发生的。因为CF标志位作为无符号数的进位或者借位判断依据,我们考虑发生的情况如下:
相加:两个数相加,如果最高位发生了进位,表示溢出
相减:A - B,如果A<B,就说明发生了借位

但是现在有一个问题,怎么知道发生了借位呢,我们进行加法运算的时候,是从右到左依此相加的,并且还有一个进位位,每次两个位相加都会更新进位位,作为下一步两个位相加的进位项,最高位结束后,如果进位位=1,就说明发生了溢出。这个操作用逻辑电路是很好实现的,但是减法可就没那么简单了,我们每次操作的时候得需要判断是不是需要借位,借位之后下一步的时候得先减去借的位,然后在判断需不需要借位。。。太难了。

好在,有一种方式可以使用加法来计算减法,比如两个无符号整数A,B相减(A,B都是n位的)
A − B = A − B + 2 n − 2 n + 1 − 1 = ( 2 n − 1 − B ) + A + 1 − 2 n \begin{aligned} &A - B \\ =&A - B +2^n-2^n+1-1\\ =&(2^n-1-B) + A +1 -2^n \end{aligned} ==ABAB+2n2n+11(2n1B)+A+12n
我们观察 ( 2 n − 1 − B ) (2^n-1-B) (2n1B)发现这玩意其实就是 111 … 11 ( n − 1 位 ) − B 111\dots11(n-1位)-B 11111(n1)B,这不正是B的反码吗
除此之外, 我们还发现几个特点:

  • 如果 A ≥ B A\ge B AB ( 2 n − 1 − B ) + A + 1 ≥ 2 n (2^n-1-B) + A +1\ge2^n (2n1B)+A+12n一定会发生溢出,并且丢弃的溢出的数据刚好相当于减去 2 n 2^n 2n
  • 如果 A < B A< B A<B,一定会发生借位, 借位相当于结果加了 2 n 2^n 2n,虽然 ( 2 n − 1 − B ) + A + 1 (2^n-1-B) + A +1 (2n1B)+A+1没有发生溢出,但是我们也不用在减 2 n 2^n 2n了,因为借了 2 n 2^n 2n再减 2 n 2^n 2n相当于没借没减。

所以,最终我们总结下来可以得到标志位CF的赋值规则:

  • 对于加法,如果最高位发生进位,表明溢出
  • 对于减法,我们使用加法的方式进行计算,如果发生进位,表明没有溢出,如果没有发生进位,说明发生了借位,也算溢出

所以CF = sub^最高位进位,sub表示是否是减法

  • 19
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值