程序控制过程机器级表示(访问条件码)-《深入理解计算机系统》笔记

来源:互联网 发布:mac 4k 字体太小 编辑:程序博客网 时间:2024/06/05 18:32
笔记(程序控制过程机器级表示):
粤语残片上面的字幕是要从右往左读的,跟现代的书写习惯不一致,因此看起来很不习惯。其实很多事物都有类似的情况,
之所以不习惯是因为你把它读反了。
就像用条件码去判断两个数(a和b)的大小,为什么是用(SF^OF)去判断?为什么不是用(SF&OF)不是(SF|OF)等?
从因为“(SF^OF)”所以是“a<b?”去推导,会比因为是“a<b? ” 所以有“(SF^OF)”难好多。因为前者是反向推导。


条件码:
除了整数寄存器外,CPU还维护着一组单个位的条件码(condtion code)寄存器,它们描述了最近的自述或逻辑操作的属性。
通过检测这些寄存器的状态来执行条件分支指令。最常用的条件码有:
    CF: 进位标志。最近的操作使用最高位产生了进位。可以用来检查无符号操作数的溢出。
    ZF: 零标志。最近的操作得出的结果为0.
    SF: 符号标志。最近的操作得到的结果为负数。
    OF: 溢出标志。最近的操作导致一个补码溢出——正溢出或负溢出。




试下顺着去推导:因为是“a<b? ” 所以有“(SF^OF)”

假设a和b是有符号数,怎样去判断“a<b”正确与否?通常的方法是将两个数相减,t=a+(-b);  根据结果t去判断。
假如t是负数,则"a<b"为真(SF=1)
假如t是零或正数,则"a<b"为假(SF=0)

但是因为a和b 是有符号数,要考虑到溢出的情况,因此不能单凭结果t去做判断;
结合溢出的情况重新整理如下:


假如t是正数(SF=0) ,没有发生溢出(OF=0),“则"a<b"为假 ”(0);
假如t是正数(SF=0) ,发生溢出(OF=1),     “则"a<b"为真”(1);
假如t是正数(SF=1) ,没有发生溢出(OF=0),“则"a<b"为真 ”(1);
假如t是正数(SF=1) ,发生溢出(OF=1),     “则"a<b"为假 ”(0);


变成表格:

SF
位运算
OF
结果:a<b
0
()
0
0
0
()
1
1
1
()
0
1
1
()
1
0

根据这个表格,很容易就得知表格括号里面填的是^(异或)而不是其他的& | 等。

现在将(SF^OF)的结果赋值给D,表示成一个指令:setl D

梳理一下:要判断a是否小于b,先是要将a和b相减得到结果t,再根据t和溢出状态去判断。
所以int comp(data_t  a , data_t  b)
这条函数转换成汇编指令就是: (a的值存在%rdi 里面,b 的值存在%rsi 里面 )

comp:
     cmpq       %rsi , %rdi       (比较a和b ,其实就是用a-b)
     setl           %al                  (将最近一次的算术或逻辑操作的属性放到%al里,结果是0或1)
     movzbl     %al ,  %eax        (将%eax的高位置零,防止之前有未置零的值影响到结果)
     ret


到此,解析了“a<b”是用(SF^OF) 去判断的原理,那“a<=b”又要怎么判断呢?
其实很简单,将(SF^OF)取反就是了,~(SF^OF)
慢慢推导可以得出下表:


有个疑惑的地方就是:
比较无符号数时有:t=a-b
当a-b<0时CMP 指令会设置进位标志(CF=1)?

但既然a和b是无符号数,就不会出现小于零的情况啊!
测试了下:
int main(void){

    unsigned int y = 2;
    unsigned int z = 1;
    printf("%u\n",z-y);

}

//结果是4294967295,就是二进制的32个1,有符号模式的-1

最后网上查了下,有人说虽然ab是无符号数,但进行CMP比较时还是将a-b的结果当成有符号数的。

这说法也比较符合上面的测试结果。
阅读全文
0 0
原创粉丝点击