标志寄存器是cpu内部的一种特殊寄存器;
作用:
1】用来储存相关指令的某些执行结果
2】用来为cpu执行相关指令提供行为依据
3】用来控制cpu的相关工作方式
8086cpu中的标志寄存器如图:
8086cpu的标志寄存器有16位;
其中存储的信息被称为状态字(psw);
标志寄存器不是用来存放数据的;
标志寄存器按位来起作用,每一位有特定的含义;
1.主要标志位的含义
1)ZF标志
零标志位;
是flag的第6位;
作用:
记录相关指令执行后结果是否为0;如果为0,zf=1;如果不为0,zf=0;
例如:下面的指令执行完后结果为0,zf=1
mov ax,1
sub ax,1
在8086cpu中,有些指令影响标志寄存器,大都是运算指令:add、sub、mul、div、inc、or、and等;
有些指令不影响标志寄存器,大都是传送指令:mov、push、pop等;
2)PF标志
奇偶标志位;
是flag的第2位;
作用:
记录相关指令执行后,结果的所有bit位中1的个数是否为偶数,如果是则pf=1,否则pf=0;
例如:下面指令执行后结果为1011B,1的个数为3,不为偶数,pf=0
mov al,1
add al,10
3)SF标志
符号标志位;
flag的第7位;
作用:
记录执行相关指令后结果是否为负;如果为负,sf=1,否则sf=0;
如果将数据当做是有符号的数据来计算,可以通过sf来判断结果的正负;
如果将数据当做无符号的数来计算,则sf的值没有意义;
例如:
mov al,10000001b
add al,1
如果当做有符号数来计算,则10000001b被当做补码,十进制值为-127;
计算后的值为补码10000010b,也就是十进制的-126;
值为负数,sf=1;
1】关于有符号的值
当一个二进制值作为有符号值时,其最高为被当做符号位,如果是1表示负数,如果是0则表示正数;
例如:10000001b作为有符号数时表示-127,作为无符号数时表示129
2】关于补码
二进制数计算时一般用补码表示;
例如:-1的表示法
原码 ->10000001b;也就是用一个字节的最高位即符号位来表示正负
反码 ->11111110b;相当于原码的符号位不变,其它位取反;
补码 ->11111111b;相当于反码+1;
正数的补码是其本身,比如00000001b的补码还是00000001b;
补码的意义:
因为机器计算时没有减法,只有加法,为了将减法转换成加法;即1-1=1+(-1);
例如:补码计算1-1
11111111b+00000001b;计算后所有位都为0,正好符合1-1=0;
4)CF标志
进位标志位;
flag的第0位;
作用:
进行无符号运算时,记录了运算结果的最高位向更高位的进位值,或从更高位的借位值;
也就是说,如果运算时如果结果超过了位数放不下,或者因为计算减法因为比被减的值小而借位时,cf=1,否则cf=0;
例如:
1】加法运算时,结果超过了最高位,则cf=1
mov al 98h
add al,al
计算后结果为:(al)=30h,cf=1;因为结果八位放不下,需要进位,用cf=1来标志发生了进位;
2】减法运算时,如果不够,则借一位,cf=1
mov al 97h
sub al,98h
计算后的结果为:(al)=ffh,cf=1;
因为97h比98h小,相当于借了一位变成197h-98h,cf=1标志发生了借位;
5)OF标志
溢出标志位;
flag的第11位;
作用:
记录有符号运算时,运算结果是否发生溢出,如果溢出of=1,否则of=0;
关于溢出:
有符号运算时,最高位为符号位,用补码计算;
例如:8位有符号值的范围为-128~127,即二进制的11111111b~01111111b;
如果两个有符号数相加的结果超出了这个范围,将发生溢出;
例如:98+99,结果超出了127,其结果为11000101b,当做补码来看对应的是-59,造成结果不正确,此时of=1来标识发生了溢出;
2.adc指令
adc是进位加法指令;
格式:
adc 操作对象1,操作对象2
例如:
adc ax,2
功能:
操作对象1=操作对象1+操作对象2+CF;
例如:
mov ax,2
mov bx,1
sub bx,ax ;bx的值小于ax,因此需要借位,使cf=1
adc ax,1 ;结果为 2+1+1=4
应用:
可以利用adc指令和add指令配合使用,来实现对较大的数据相加;
思路:
将加法分成两步:1】低位相加;2】高位相加,再加上低位的进位值(如果有的话);
低位用add相加,因为低位相加的值可能结果太大以至于低位放不下,而产生进位;
两个二进制数相加,如果有进位,进位值只可能是1;比如:1111b+1111b=11110b,其中1111b已经是4位二进制数的最大值了,但相加的进位值还是1;
通过cf的值判断是否有进位,如果有将进位值1加到高位中,用adc指令正好能够实现;
所得的结果不会因为进位造成值的丢失;
例如:两个128位数相加的子程序
add128: push ax ;防止寄存器冲突
push cx
push si
push di
sub ax,ax ;将cf的值设置为0
mov cx,8
s: mov ax,[si] ;si和di为参数,存放了相加的数在数据段中的偏移地址;
adc ax,[di]
mov [si],ax
inc si ;移动到16位后,如果用add si,2的话可能该变cf的值,造成进位值丢失
inc si ;inc和loop指令不会影响cf位的值
inc di
inc di
loop s
pop di ;子程序调用完后寄存器还原
pop si
pop cx
pop ax
ret
3.sbb指令
sbb是借位减法指令;
格式:
sbb 操作对象1,操作对象2
作用:
操作对象1=操作对象1-操作对象2-cf的值
sbb和adc类似,可以用来做借位相减;
4.cmp指令
cmp是比较指令;
格式:
cmp 操作对象1,对象2
作用:
计算操作对象1减去对象2的,以减的结果改变标志寄存器,但不保存计算结果;
1)无符号比较
cmp ax,bx
执行完后:
zf=1 ->ax=bx
zf=0 ->ax!=bx
cf=1 ->ax<bx
cf=0 ->ax>=bx
cf=0 && zf=0 ->ax>bx
cf=1 || zf=1 ->ax<=bx
2)有符号比较
cmp ax,bx
执行完后:
zf=1 ->ax=bx
zf=0 ->ax!=bx
sf=1 && of=0 ->ax<bx
sf=1 && of=1 ->ax>bx
sf=0 && of=0 ->ax>=bx
5.检测比较结果的条件转移指令
条件转移指令是指根据某种条件决定是否修改ip的指令,所有条件转移指令的位移都是-128~127;
例如jcxz,如果cx的值为0,则修改ip;
大多数条件转移指令都检测标志寄存器的相关标志位,根据其值来决定是否修改ip;
这些条件转移指令通常和cmp指令配合使用;
如图:无符号比较的条件转移指令
例如:统计数据大于8的数的个数
assume cs:code,ds:data
data segment
db 8,11,8,1,8,5,63,38
data ends
code segment
start:mov ax,data
mov ds,ax
mov bx,0
mov cx,8
mov ax,0
s: cmp byte ptr [bx],8 ;和8比较
jna k ;如果不大于8则ax不自增
inc ax
k: inc bx
loop s
code ends
end start
6.DF标志和串传送指令
1)df标志位
df标志位:
是flag的第十位,为方向标志位;
df的作用:
在串处理指令中,控制每次操作后si、di的增减;
df=0,每次操作后si、di递增;
df=1,每次操作后si、di递减;
2)串传送指令
格式:
movsb
作用:
执行movsb后,相当于执行了以下操作;
es*16+di=ds*16+si
如果df=0,si=si+1,di=di+1
如果df=1,si=si-1,di=di-1
也就是相当于把ds:[si]指向的字节赋值给es:[di],然后根据方向标志位,si和di递增或递减;
movsb一次传送一个字节;
如果想一次传送一个字,需要用到movsw
movsw的作用:
将ds:[si]处的字赋值给es:[di],然后根据方向标志位,si和di加2或减2;
8086cpu提供了两条指令用来对df进行设置:
cld ->将标志寄存器df位设置为0
std ->df置1
3)rep
movsb和movsw经常和ret一起使用;
格式:rep movsb
相当于:
s:movsb
loop s
作用:根据cx的值重复执行movsb
例如:将f000h的最后16个字节传送到data段中,用到了逆向传递,因此方向标志位df=1
data segment
db 16 dpu (0)
data ends
...
mov ax,0f000h
mov ds,ax
mov si,0ffffh ;ds:si指向f000:ffff
mov ax,data
mov es,ax
mov di,d5 ;es:di指向data段的最后一位
mov cx,16 ;循环16次
std ;df=1,设置逆向传送
rep movsb
...
7.pushf和popf
pushf ->将标志寄存器的值压栈;
popf ->将标志寄存器的值从栈中弹出;
8.标志寄存器在debug中表示
在debug中可以看到标志寄存器各个标志位的信息:
对于这些信息的解释: