【汇编基础教程】标志寄存器
前言
在8086CPU中,有一个特殊的寄存器,用来存放各种指令产生的临时信息,它被称为flag,即标志寄存器。本文将介绍该寄存器的使用,在编程时应灵活使用其特性。
标志寄存器简介
标志寄存器可以视为几个单二进制位的寄存器所拼凑出来的一个寄存器(几个各具自己含义的单二进制位的寄存器挤在同一个寄存器里面),存放一些指令执行时产生的附加信息(如加减法时的进位、借位,溢出等)。在标志寄存器中,每个二进制位代表一个信息。下面将详细介绍各标志寄存器的用途~
标志寄存器组成
标志寄存器由以下各位组成:
位数 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
表示 OF DF IF TF SF ZF AF PF CF
其中空的位没有实际意义,有意义的位将进行介绍——
标志寄存器使用
DF标志(第10位,方向标志位)与串传送指令
DF标志控制串传送指令每次操作后si、di的增减。若DF值为0,则每次操作后si、di递增;若DF值为1,则每次操作后si、di递减。
串传送指令用于在内存中移动一批数据。其中movsb针对一个字节,movsw针对一个字。movsb的功能如下:
(1)将ds:si指向的内存单元字节送入es:di处。
(2)若DF值为0,则si、di各自增1;
若DF值为1,则si、di各自减1。同理,movsw的功能如下:
(1)将ds:si指向的内存单元字送入es:di处。
(2)若DF值为0,则si、di各自增2;
若DF值为1,则si、di各自减2。串传送指令一般和rep配合使用,如:
rep movsb其等价于:
s:movsb
loop s易知rep的作用:可以实现(cx)个字节/字的传送。
由此,不难想到,CPU应提供直接更改DF值的方式。事实确实如此。在8086CPU中,我们可以通过cld/std指令将DF的值设置为0/1,格式:
cld/stdZF标志(第6位,零标志位)
ZF标志用于记录相关指令执行后,其结果是否为0。若结果为0,则ZF值为1;反之则ZF值为0。其中,add、sub、mul、div、inc、or、and等运算指令会影响ZF的值。例如,执行如下指令后,ZF值为1:
mov ax,1
sub ax,1PF标志(第2位,奇偶标志位)
PF标志用于记录一些指令执行后,其结果的所有单字节位中1的个数是否为偶数。若为偶数,则PF值为1;反之则PF值为0。例如,执行如下指令后,PF值为0:
mov al,1
or al,1SF标志(第7位,符号标志位)
SF标志记录一些指令执行后,其结果是否为负。若结果为负,则SF值为1;否则SF值为0。需要注意的是,计算机中存放的数字有无符号数和有符号数两种,而一个字节单元存放的数有无符号由编程者意识决定。当你认为运算对象是有符号数时,8086CPU会将运算结果视为补码,并将其转为原码后判断其符号位的正负来更新SF位的值;当你认为运算对象是无符号数时,CPU也会以相同方式更新SF位,但此时符号对你没有意义,所以请无视SF位。例如,执行如下指令后,SF值为0(运算结果换成原码后为0,即非负数):
mov al,10000001b
add al,01111111bCF标志(第0位,进退位标志位)
CF标志记录add、sub执行后最高位是否产生进、退位。若产生了进、退位,则CF值为1;反之则CF值为0。需要注意的是,inc和loop指令不影响CF位。
abc、sbb指令
abc、sbb指令是带借位加、减法指令。相当于在操作目标上再加、减一个CF的值,例如:
add al,bl
abc ah,bh以上指令等效于:
add ax,bx由此可见,我们可以通过abc和sbb指令进行大数据相加。例如,以下代码能计算1EF0001000H+2010001EFH的值,其中高、中、低16位分别存放在ax、bx、cx寄存器中:
mov ax,001EH
mov bx,0F000H
mov cx,1000H
add cx,1EF0H
abc bx,1000H
abc ax,0020HOF标志(第11位,溢出标志位)
我们在进行有符号数运算时,可能会发生溢出,这会导致运算结果不正确。例如,我们要将两个8位二进制数(范围-128~127)98和99相加,正确结果是197。但是,由于机器无法存下,发生了溢出,我们得到了-59,显然,这与我们的希望不符。
OF标志位就是用于记录溢出的位。如果发生溢出,OF值为1;反之OF值为0。上面的98+99运算后,OF值为1。但需要注意OF和CF的区别。CF针对无符号数,而OF针对有符号数。
cmp指令和检测比较结果的条件跳转指令
cmp指令为比较指令,格式:
cmp 操作对象1,操作对象2
当执行cmp指令时,CPU会对两个操作对象作减法,但不保存在操作对象中,只影响标志寄存器的值。例如,执行如下指令后,ZF值为0,PF值为1,SF值为0,CF值为0,OF值为0:
mov ax,8
mov bx,3
cmp ax,bx则执行cmp指令后,我们通过标志寄存器的值即可判断两个操作对象的大小。由逻辑推理,我们可以得到:
当ZF值为1时,(ax)=(bx)
当CF值为1时,(ax)<(bx)
当CF值为0且ZF值为0时,(ax)>(bx)对于有符号数,由于其靠补码表示,故情况较为复杂:
当SF值与OF值不同时,(ax)<(bx)
当SF值与OF值相同时,(ax)≥(bx),然后根据ZF的值判断是否相等通过上面的讨论,我们知道了不同比较结果下cmp指令的实际意义,下面介绍常用的使用cmp比较结果的方法——检测比较结果的条件转移指令。对于无符号数,其条件转移指令如下:
je/jz:等于(ZF值为1)则转移
jne/jnz:不等于(ZF值为0)则转移
ja/jnbe:高于(CF、ZF值均为0)则转移
jna/jbe:不高于(CF、ZF中至少一个值为1)则转移
jb/jnba:低于(CF值为1)则转移
jnb/jae:不低于(CF值为0)则转移对于有符号数,其条件转移指令如下:
je/jz:等于(ZF值为1)则转移
jne/jnz:不等于(ZF值为0)则转移
jg/jnle:大于(SF、OF值相等且ZF值为1)则转移
jng/jle:不大于(SF、OF值不等或ZF值为0)则转移
jl/jnge:小于(SF、OF值不等)则转移
jnl/jge:不小于(SF、OF值相等)则转移如果只想要用其中一个标志位,则可以使用下面针对某一个位的指令:
jc/jo/jp/js:CF/OF/PF/SF值为1则转移
jnc/jno/jnp/jns:CF/OF/PF/SF值为0则转移以ja为例,以上全部转移指令的格式:
ja 标号将ja替换为其他指令也可。注意,以上条件转移指令均为短转移。
AF标志(第四位,辅助进退位标志位)
AF标志位用于BCD码运算相关操作。BCD码是一种用一组四个二进制位表示十进制数的编码。若两数相加,其中低四位的最高位有进位,则AF值为1,反之AF值为0。
直接获得标志寄存器中的数据
8086CPU支持直接访问标志寄存器的数据。pushf能够将标志寄存器压栈,popf刚能够从栈中弹出数据到标志寄存器中,格式:
pushf/popf同时,还有一个lahf指令,能够将标志寄存器的低8位送入ah寄存器。格式:
lahf在得到标志寄存器中的数据后,我们即可通过位运算指令,建立值-指令映射关系,同样可以实现类似条件转移指令的功能(高级语言中if语句的底层逻辑就是映射)。