本博文系列参考自<<汇编语言>>第三版,作者:王爽
8086CPU的标志寄存器有16位,其中存储的信息通常被称为程序状态字(PSW)。除了前面介绍的寄存器。本博文将介绍最后一个寄存器,标志寄存器。不同于之前的寄存器,标志寄存器是按位起作用的。其每一位都有特殊的含义。
8086标志寄存器的结构如下图所示:
标志寄存器的1,3,5,12,13,14,15都没有特殊含义。其0,2,4,6,7,8,9,10,11用作标志位使用。
11.1 ZF标志(zeros flag)
标志寄存器第6位为ZF标志寄存器,代表零标志,该标志位记录相关指令执行结束后是否为0。如果结果为0,那么zf=1,如果结果不为0,那么zf=0.
比如:
mov ax,1 sub ax,1
sub指令执行结束后结果为0,所以ZF=1
再比如:
mov ax,2 sub ax,1
sub指令执行结束后结果不为0,所以ZF=0
11.2 PF标志
标志寄存器的第二位为PF,该标志位为奇偶标志位,其记录相关指令执行结束后结果值的二进制位中1的个数是否为偶数,如果1的个数为偶数,则PF=1。如果1的个数为奇数,则PF=0.
比如:
mov al,1 add al,10
执行后,结果为00001011B,其中又三个1,则PF=0
再比如:
mov al,1 or al,2
执行结果为:00000011B,其中又两个1,为偶数,则PF=1
11.3 SF标志
标志位的第七位为SF,为符号标志位,执行完相关指令后,如果为负,则SF=1.如果为正,则SF=0
计算机可以将我们进行相关运算的数据看成有符号数和无符号数。比如:
mov al,10000001B add al,1
运算结束之后(al)=10000010B
当我们将ADD指令的运算看做无符号数的运算的时候(al)=130,如果我们将ADD指令的运算看做有符号数的运算的时候(al)=-126
所以,当我们把相关指令的运算数据看成有符号数的运算的时候,标志位SF的符号改变才会有意义,当我们把相关指令的运算数据看成无符号数的运算的时候。这时候虽然SF位有可能会改变,但是此时SF位的改变将无意义。
11.4 CF标志
标志寄存器的第0位为CF位,CF为记录的是无符号数运算的时候运算结果的最高位向更高位的进位结果,或从更高位的借位值。
假设对于8位的无符号数的更高位的理解如下图:
例如当两个数相加的时候,可能会产生向最高位的进位,然而这个位是不能保存下来的,其实并非直接舍弃,而是改变进位标识符CF的值。比如两个8位的数据98H+97H会产生向更高位的进位。
比如:
mov al,98h add al,al 执行后 al=30h,cf=1 add al,al 执行后 al=60h,cf=0
再比如,当两个数相减的时候,可能会产生想最高位借位的信息:
mov al,97h sub al,98h 执行后(al)=FFH ,cf=1 sub al,al 执行后(al)=0 , cf=0
11.5 OF标志
OF标志位溢出位标志,在第11位,标号在对有符号数进行运算的时候,如果超过机器能够表示的有符号数的范围,我们称为溢出。
比如8位有符号范围为-128~127 16位有符号数范围为-32768-32767
举例:
mov al,98h add al,97h
执行结果为197,超过了8位有符号数的正上限127。
再比如:
<pre name="code" class="plain"><pre name="code" class="plain">mov al,0F0H F0H为有符号数-16的补码 add al,088H 88H为有符号数-120的补码
执行结果为-136,超过了8位有符号数的负下线-128
如果在进行有符号数运算的时候发生溢出,结果将不正确。
比如
mov al,98h add al,97h
add指令的结果为(al)=0C5H,因为是有符号数的运算,所以al中存储的是有符号数,而C5H的为右符号数为-59的补码,98+99=-59显然是不正确的。造成这钟错误的原因是8位寄存器放不下197
再比如
mov al,0F0H F0H为有符号数-16的补码 add al,088H 88H为有符号数-120的补码
add运算后(al)=78H,因为进行的是有符号数的运算,所以al存储的是有符号数,而78h代表的有符号数120,则-16-120=120这样显然不正确。还是那个原因,因为8位有符号数的范围存储不下-136
CF和OF的区别:CF是对于无符号数运算有意义的标志位,OF是对于有符号数运算有意义的标志位。
比如:
mov al,98h add al,97h
运算后CF=0,OF=1,因为add运算后的值为197,对于8位无符号数的运算,没有产生进位,所以CF=0.对于8位有符号数的运算,已经超过上限127,所以OF=1
再比如:
mov al,0F0H F0H为有符号数-16的补码 add al,088H 88H为有符号数-120的补码
运算后CF=1,OF=0,因为对于8位无符号数来言,add运算后的值为178H(376)超过了无符号8位数的上限产生进位,CF=1.对于8位有符号数来言,add运算后的值为-136未超过8位无符号数的下限。所以未产生溢出。OF=0;
我们可以看出CF与OF所表示的进位和溢出,是分别针对无符号数和右符号数的运算而言的。它们之间无任何关系。
11.6 adc指令
adc是带进位的加法,利用CF标志位
指令格式:adc 对象1,对象2
指令意义:对象1=对象1+对象2+CF
比如 adc ax,bx 的含义是(ax)=(ax)+(bx)+CF
指令 add ax,bx
相当于
add al,bl
adc ah,bh
11.7 sbb指令
sbb是带借位的减法,利用CF标志位
指令格式:sbb 对象1,对象2
指令含义:对象1=对象1-对象2-CF
比如sbb ax,bx的含义是(ax)=(ax)-(bx)-CF
11.8 cmp指令
cmp相当于减法指令,但是cmp不保存减法结果,只是对相关的寄存器影响
指令格式;cmp 对象1,对象2
比如 cmp ax,ax
做ax-ax运算结果为0,并不保存结果,仅影响flag标志位,执行指令后:zf=1,pf=1,sf=0,cf=0,of=0
比如cmp ax,bx通过查看标志位可以对ax和bx比较大小,比较过程如下:
11.9检测比较结果的条件转移指令
转移是指改变IP的值,条件则是根据满足某种条件再更改IP值,进而实现跳转。
常见的比较跳转指令如下:
理解一个跳转指令的意义可以遵循如下规律:
11.10 DF标志和串传送指令
标志寄存器的第十位为DF标志位,方向标志位。在串处理指令中,控制每次操作后SI,DI的增减。
df=0 每次操作后si,di递增
df=1 每次操作后si,di递减
串操作指令:
指令格式: movsb
解释如下:
((es)*16+(di))=((ds)*16+si)
如果df=0 (si)=(si)+1 (di)=(di)+1
如果df=1 (si)=(si)-1 (di)=(di)-1
可以看出Movsb是将es:si指向的内存单元的字节送入es:di中,根据df值,设置si,di递增或者递减。
类似的指令还有movsw,Movsw是将es:si指向的内存单元的字送入es:di中,根据df值,设置si,di递增或者递减。
通畅将rep与movsb或者movsw配合使用:rep movsb 其含义是将:
s:movsb
loop s
所以rep movsb可以实现循环(cx)次的字节复制或者传送。
11.11 pushf和popf
pushf是将标志寄存器的值压栈,popf是从栈中弹出数据,传入标志寄存器中。
11.12标志寄存器在Debug中的表示
在debug工具中用r命令可以看到由下角有一些奇怪的符号,其实这些符号就是标志寄存器在debug下的表示。其对应关系如下图所示: