前言
本文讲述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=a3a2a1a0,ai=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=b3b2b1b0,bi=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+B≥0
→
\rightarrow
→负溢出
如果
A
≥
0
A\ge0
A≥0 并且
B
≥
0
B\ge0
B≥0,但是
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+B≥0,发生了溢出,如果次高位发生了进位,最高位最终为1,表示负数,没有发生溢出
如果
A
≥
0
A\ge0
A≥0 并且
B
≥
0
B\ge0
B≥0
⇒
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+B≥0,没有发生溢出,如果次高位发生了进位,最高位最终为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}
==A−BA−B+2n−2n+1−1(2n−1−B)+A+1−2n
我们观察
(
2
n
−
1
−
B
)
(2^n-1-B)
(2n−1−B)发现这玩意其实就是
111
…
11
(
n
−
1
位
)
−
B
111\dots11(n-1位)-B
111…11(n−1位)−B,这不正是B的反码吗
除此之外, 我们还发现几个特点:
- 如果 A ≥ B A\ge B A≥B, ( 2 n − 1 − B ) + A + 1 ≥ 2 n (2^n-1-B) + A +1\ge2^n (2n−1−B)+A+1≥2n一定会发生溢出,并且丢弃的溢出的数据刚好相当于减去 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 (2n−1−B)+A+1没有发生溢出,但是我们也不用在减 2 n 2^n 2n了,因为借了 2 n 2^n 2n再减 2 n 2^n 2n相当于没借没减。
所以,最终我们总结下来可以得到标志位CF的赋值规则:
- 对于加法,如果最高位发生进位,表明溢出
- 对于减法,我们使用加法的方式进行计算,如果发生进位,表明没有溢出,如果没有发生进位,说明发生了借位,也算溢出
所以CF = sub^最高位进位,sub表示是否是减法