针对能够影响OF和CF标志位的指令,一般来说是涉及到数据运算的指令,这里使用add
举例,即不区分有无符号的加法指令,参与运算的数据,从二进制层级去考虑。
CF标志位
对于CF,它是carry flag,进位标志,这个进位,表示的是二进制层次下的进位,例如:
mov al,98h
add al,al
它的运算是
其中,得到的最高一位,会被丢弃,这个也就是进位了!
模拟一下:
这个进位还是很简单的!
如何实现add指令进位时,CF的变化
这个简单,我们只需要记录一下add的结果的假想最高位即可,就是加法器的进位位。
功能层级理解CF & 进位借位
其实我们只需要从功能角度去理解就可以了,不用关注具体如何实现的,因为这可能非常复杂,不过你的确可以简单思考一下。
CF不仅仅记录add
时候的进位,也记录sub
时候的借位,同时如何想使用这个进位或者借位,还有sbb
,add
等指令。
考虑借位的时候,只需要CF的结果等于加法器进位异或sub即可。
OF标志位
overflow flag,溢出标志位,它记录的不是二进制下的进位,而是十进制下的不合理结果,例如
- 正数 + 正数 = 负数
- 负数 + 负数 = 正数
- ……
我们举个例子,分别从二进制和十进制下看待这个问题。
mov al,98
add al,99
- 从十进制角度,98 + 99 = 197,但是对于8位补码,数据范围是
-128 - 127
,197是不合理的,它溢出了,因此OF 置 1
。 - 从二进制角度,
98 + 99 = 62h + 63h = c5h
,我们可以看到,单从计算机世界来说,两数相加,结果并没有进位,因此CF = 0
我们可以看到,从二进制世界来说,可以很容易识别出进位,但是不容易识别出溢出,因为溢出的规则,是由人类世界的十进制法则决定的,要想实现识别,应该单独设定一些其他逻辑。
因此,我们要联合人类世界和计算机世界,来思考如何实现OF标志位的逻辑。
OF标志置位复位的实现
我们先考虑一下可能溢出的场景(8位二进制补码真值范围 -128 ~ 127)
- 负数 + 负数 = 正数
- 正数 + 正数 = 负数
- 其他…
我们就先假定至于这两种场景吧,那么如何实现OF的逻辑?很简单,列真值表!
我们规定输入
- 操作数1为正数记为0,负数记为1(其实就是最高位)
- 运算:加法为0,减法为1
- 操作数2为正数记为0,负数记为1
- 运算结果为正数记为0,负数记为1
输出OF
操作数1 | 操作 | 操作数2 | 运算结果 | OF |
---|---|---|---|---|
1 | 0 | 1 | 0 | 1 |
0 | 0 | 0 | 1 | 1 |
… | … | … | … | 0 |
… | … | … | … | 1 |
这样我们就能够根据真值表,得到一个组合逻辑,实现OF了。
当然这只是一个思路而已,举这个例子是为了说明,OF的实现是与十进制运算密切相关的,仅依靠二进制看不出来。
CF与OF分开看
我们前面可以知道,CF有CF的实现逻辑,OF有OF的实现逻辑,实际执行add
运算的时候,我们根据
- 操作数1
- add运算
- 操作数2
- 运算结果
这几个因素,来分别生成OF和CF的结果,二者在机器层级上是相互独立并行工作的两个组合逻辑。
也就是说,CF与OF的组合可能是
- 00
- 01
- 10
- 11
并且二者没有什么关联。
CF、OF与有无符号(整)数运算的关系
注意暂时不谈浮点数!
首先,有无符号是从高级语言层级才能看出来,add
指令进行加法运算,不区分有无符号,不管高级语言是有符号加还是无符号加,都是add
指令,结果完全一样,因此,仅凭借add指令是无法区分的。
那如何区分呢?依靠其他指令以及CF、OF标志位!
通常来说
- CF用于识别无符号数运算的溢出,因为无符号数运算溢出,等价于二进制运算进位了。
- OF用于识别带符号数运算的溢出,这个逻辑的设置,本身就针对带符号数,没什么好说的,补码就针对带符号数
这里再强调人类世界与计算机世界关于整数的转换规则
- 带符号数:补码
- 无符号数:二进制位串
运算与标志位的关系
-
无含义的纯结果:对于
sub,add
这种不区分有无符号运算的指令,标志位的结果只和运算结果有关,也不需要分开看。 -
根据其他指令识别标志位,给结果赋予含义:但是如果我们去利用标志位做识别,就需要进行分开看,不同的标志位识别,决定了这个运算结果的不同含义,决定了它是带符号数还是无符号数运算,亦或者是比较运算。