(AT&T格式汇编)
零、有符号数的补码(two's-complement)表示(复习)
- 反码(ones' complement,用 ~a 表示 -a,a 为无符号形式)和 原码(sign-magnitude 用最高有效位作为符号位)都存在一个问题:0 有两种编码形式,+0 和 -0.
反码的属性:a + (-a) => 0xFFFF... = -0,也可表示为 [1111...] - a => -a. (that's why it's called ones' complement) - 补码:
属性:a + (-a) => [10000....(w个0)] = 2^w = 0,其中 w 为计算机表示的位数。(用 2^w - a 来表示 a 的负数,that's why it's called two's complement)
注意 a + (-a) 最高位产生了进位,实际运算结果会成为 [0000....(w个)],刚好是 0 的表示。所以有符号计算中,进位标志 CF 不代表其真实意义。
这种表示方式可直接做加减法,只要最高位为 1,一定为负数,而且 0 只有一种形式,因而被广泛使用。
补码的特性:- 因为其最高位实际上也相当于符号位,可知其负数域、和非负数域是一样大的。对 4 位的表示,非负数域是 0 ~ 7,而负数域是 -1 ~ -8,负数域比正数域大1.
- 在任意的位表示中,都有一个数,使 2^w - a = a成立,这就使 a 的负数表示成了它本身,这个数就是 [1000...(w-1个0)]。因为仍然用最高位表示符号位,因此在符号表示中,该数只存在负数形式,而没有正数形式了。在 4 位的表示中,它就是 -8,[10000000].
一、四个算术逻辑操作的标志位
- CF(carry flag): 进位标志,针对无符号运算。当最高位产生进位(加法),或者从更高位借位(减法)时,被置 1.
- ZF(zero flag): 当上一次运算的结果为 0 时,置 1.
- SF(sign flag): 符号标志,针对有符号运算。当上一次运算结果的最高位为 1(即为负数时),被置 1.
- OF(overflow flag): 有符号运算的溢出标志(补码)。(对 t = a + b 溢出的情况是 (a>0 && b > 0 && t <0) || (a<0 && b<0 && t>=0),若 a 或 b 有一个为 0,就不可能溢出,所以不需要考虑。)
要理清 OF 标志位的设置情况(对 a + b,减法可转换成补码加法 a + (-b))- 首先考虑到,a 和 b 必须同号,才可能溢出,因为异号、或者其中有一个是 0 的话,结果必定在范围内。
- 若 a 和 b 均 大于 0,t 小于 0,判断为正溢出(两正数求和溢出,结果一定异号)
- 若 a 和 b 均 小于 0,t 大于等于 0,判断为负溢出(同理,负数和溢出,必定异号。但是负溢出也可能溢出到 0 )
更精确地描述溢出,考虑 有符号数 a 和 b,用 w 位来表示它们,则其取值范围为 [-2^(w-1), 2^(w-1)-1](这很容易从负数域、和非负数域是一样大的推得)
则补码加法 [a + b] 的结果有三种情况:(这里引入 [a+b] 表示补码加法,而 a + b 表示理想的加法)- 正溢出:a + b >= 2^(w-1) 时,运算结果越过了上界,被截断为 a + b - 2^w,这表示最高位的进位(2^w)被舍弃。可看出溢出的结果一定为负数,当a = b = 2^(w-1)-1 时,运算结果有最大值 -2。
- 正常:a + b 仍在 [-2^(w-1), 2^(w-1)-1] 内,结果正常,为 a + b.
- 负溢出:a + b < -2^(w-1) 时,运算结果越过了下界,从位表示上看,这也是最高位产生了进位。这个进位本该表示 -2^w. 但是被舍弃,因而结果是 a + b + 2^w. 可看出溢出的结果一定为非正数,当a = b = -2^(w-1) 时,运算结果有最小值 0。
二、设置标志位的操作
- 所有逻辑运算,移位运算 和 加、减、乘 运算。
- cmp s1, s2 基于从 s2 减去 s1 的结果来设置标志位。(与 sub 的行为是一样的,只是它只设置标志位,并不保存运算结果)
- test s1, s2 与 and 的行为是一样的,只是它只设置标志位,并不保存运算结果
三、访问条件码
1. SET 指令
分为三类:通用指令(为 0 的测试是通用的)、有符号指令(greater less)、无符号指令(above below)
难点:setl 指令
(其他有符号的 set 指令都基于 OF^SF)
; a in %rdi, b in %rsi
comp:
cmpq %rsi, %rdi ;会根据 a-b 的结果设置标志位
setl %al
movzbl %al, %eax
ret
setl 会在 a<b 时设置 %al. 下面考虑一下它的实现。
- 当没有发生溢出时(OF 为 0),运算正常,所以 SF 为 1 时,a-b<0,否则 SF 为 0.
- 若 OF 为 1,又分两种情况。
- 若为正溢出,结果会为负数,因此 SF 会置 1。 [a-b] 发生正溢出,说明 b 为负数 a 为 正数,a>b
- 若为负溢出,结果会为正数或者 0,这都会有 a 为负数,b 为正数,a<b。
综上,当 OF 和 SF 相异时,a<b成立,即OF^SF。
而无符号的比较,相对简单很多,略过了。
2. 跳转指令
跳转指令也和 SET 指令很类似,分为 无条件跳转(jmp)、通用跳转(为 0 的测试)、有符号的、无符号的。
只要理解了 SET,这里标志位与命令后缀的对应关系完全类似。不多讲。
3. 条件转送
传统的条件跳转指令,不适合现代处理器的流水线处理。在可能的情况下,使用数据的条件传送会使程序更高效。
原因在于数据的条件传送指令,避免了条件分支,使程序更适合使用流水线( pipelining )处理。(但是当分支代码量很多时,使用条件传送会造成巨大的开销。而且有些代码并不能使用这种方式,会造成非法操作如空指针、指针越界等。。)
条件传送指令的后缀意义与跳转指令的完全类似。
参考
汇编语言--标志寄存器
CSAPP-深入理解计算机系统