标志寄存器

标志寄存器是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中可以看到标志寄存器各个标志位的信息:
对于这些信息的解释:
 
 
 
 
posted @ 2019-07-13 15:29  L丶银甲闪闪  阅读(501)  评论(0编辑  收藏  举报