相信对于每一个汇编初学者而言汇编中的标志位都是一个相当绕脑并且理解起来十分困难的东西。实际上标志位是一个非常简单的东西,之所以理解起来困难,是因为人为的添加了太多的解释进去。
例如1000 1111这么一个字节,在计算机里面他实际上就是晶体管的打开和关闭状态而已,并没有任何其他含义,但是我们在解释这个字节的时候,却要考虑好几种情况,比如它是有符号的还是无符号的呢?
对于CPU而言它并不知道计算结果是有符号还是无符号的,它只负责计算,并将计算结果以及对应的标志位反馈给程序员,程序员再依据这些信息进行判断。因此我们在理解标志位的时候不需要人为的加太多的因素进去,本章就从机器的角度来谈谈CPU到底是如何设置这些标志位的。
一、常见的几种标志位及其含义
标志名称 | 溢出 | 方向 | 中断 | 符号 | 零 | 辅助进位 | 奇偶 | 进位 |
---|---|---|---|---|---|---|---|---|
符号 | OV | UP | EI | PL | ZR | AC | PE | CY |
二、CPU中只有加法没有减法
任何减法都可以转换成加法!例如 9-2=9+(-2),其中我们将2转换成了-2,将2转换成了-2也被称为求2的补码,因此计算符号也就变成了加号。在计算机中数据是按照二进制保存的,关于求二进制补码的方法这里不再讲述,实际上本章也不推荐读者将补码的思想带入学习,因为这只会增加学习难度,并不会帮助你理解,原因也很简单:CPU并不懂什么是补码,这些都是后期人为加进去的,我们只要知道CPU中只有加法没有减法即可。
三、CPU加法操作是如何影响标志位的呢
位 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
目的操作数 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 1 | |
源操作数 | 0 | 1 | 1 | 0 | 1 | 1 | 0 | 0 | |
计算结果 | 1 | 0 | 1 | 0 | 1 | 1 | 1 | 0 | 1 |
在上表中,目的操作数和源操作数都是8位的字节,为了方便讲解我们增加的第8位,实际上这一位是不存在的,仅仅是为了方便讲解。此外我们称位7为最高位,这里不用最高有效位这个描述,主要是为了减少有些读者对概念的迷惑
好了下面我们解释CPU中标志位的含义:
1、使用计算结果的最高位的值来设置符号位(PL),上表中符号位PL=0
2、如果计算结果所有位都是0的话,ZR位就被设置为1,反之ZR被设置为0,上表中符号位ZR=0。(特别说明下,计算结果的位8,仅仅是用来方便我们讲解的,它并不是计算结果的一部分,我们讲的计算结果的所有位,指的是位0到位7)
3、在计算过程中最高位(位7)向前产生进位的话,CY被设置为1,反之CY被设置为0,上表中在计算过程中最高位向位8产生了进位,因此CY=1
4、为了方便描述我们先自己定义一个标志符,记作PTEST,并且规定在计算过程中向最高位产生进位的话PTEST=1,反之PTEST=0,上表在计算过程中位6向位7产生了进位,因此PTEST=1,接着将PTEST与CY进行异或操作(XOR),
并将计算结果作为OV的值,1 XOR 1 = 0,因此OV=0。
5、计算结果的0位至7位中1的个数为偶数的话,PE=1反之PE=0,上表计算结果中0位至7位中有5个1,因此PE=0。
6、计算过程中位2向位3产生进位的话,AC=1,否则AC=0,上表计算中位2未向位3进位因此AC=0
再看个例子:
位 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
目的操作数 | 1 | 1 | 0 | 1 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | |
源操作数 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | |
计算结果 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
上表目的操作数与源操作数都是2个字节16位,下面我们分析目的操作数与源程操作相加对标志位的影响。
1、结果最高位为0,因此PL=0
2、结果所有位并不全是0,因此ZR=0
3、计算过程中最高位(位15)向位16产生了进位,因此CY=1
4、计算过程中位14未向位15产生进位,因此PTEST=0,PTEST XOR CY = 0 XOR 1 = 1,因此OV = 1
5、计算结果的0位至7位中1的个数为偶数,因此PE=1
6、计算过程中位2向位3产生了进位,因此AC=1
32位或64位计算过程中标志位的设置与上面的原理都是一样的,这里就不再一一讲解了。
四、对标志位的解读
CPU将计算结果以及对应标志位信息反馈给程序员,下面程序员就要对这些信息进行解读,并给出合理的最终结果了。总而言之CPU的计算结果并不是最终结果,最终结果需要程序员通过标志位的值以及CPU计算结算判断给出。
下面我们就简单的对溢出以及进位2种情况进行判断。
1、如果目的操作数和源操作数都是无符号的,但是CY被设置为1,这就说明计算结果无效,此时程序员就要考虑将变量的存储空间设置成大一点的类型了
2、如果目的操作数和源操作数都是有符号的,但是CY被设置为1,这并不能说明任何问题,因为一个负数加上一个正数,如果结果为正的话,最高位必定要向前进一位
3、如果目的操作数和源操作数都是有符号的,但是OV被设置为1,这就说明计算结果无效,此时程序员就要考虑将变量的存储空间设置成大一点的类型了,这是为什么呢?前面讲过OV被设置为1只有2种情况:
a、最高位向前产生进位 且 最高位前一位未产生进位
b、最高位未向前产生进位 且 最高位前一位向前产生进位
如果读者理解补码机制的话,就可以把a,b两种情况翻译成下面两句话:
a意味着:两个负数相加,结果是正数
b意味着:两个正数相加,结果为负数
很多书籍上也是这么讲解有符号数据溢出的,如果你能理解上面的原理,就完全不要死记硬背这些话。
五、最后再讲个特例
对-128进行求反操作,OV标识为1,一些考试题以及面试题都会出现这玩意,为什么呢。对于有符号的单字节数据 1000 0000,它代表的不是-0.而是(1000 0001) -1 也就是-127-1=-128,因此计算机中不管何种大小的有符号数据,负数都会比正数的范围多1