正在加载……
专注、离线、切勿分心
CPU 内部的寄存器中, 有一种特殊的寄存器(不同处理机,个数和结构可能都不同)具有三种作用:
1、用来存储相关指令的某些执行结果;
2、用来为 CPU 执行相关指令提供行为依据;
3、用来控制 CPU 的相关工作方式;
这种特殊寄存器在8086CPU中,被称为标志寄存器。8086CPU 的标志寄存器有 16 位,其中存储的信息通常被称为 程序状态字(PSW)。
标志寄存器简称 flag ;





8086CPU 的 flag 寄存器结构
flag 的1、3、5、12、13、14、15 位在8086CPU 中没有使用,不具有任何含义,其他都有特殊含义。






ZF 标志
  flag的第 位,零标志位。它记录相关指令执行后,其结果是否为 0。结果为0,ZF=1,结果不为 0,那么ZF=0.
mov ax , 1
sub ax , 1

ZF=1
mov ax , 2
sub ax , 1

ZF=0
我们执行一条指令的时候,要注意这条指令的全部功能,包括其中执行结果对哪些标志寄存器会造成影响






PF标志
  flag的第 2 位,奇偶标志位。它记录相关指令执行后,其结果的所有二进制位中的 1 的个数是否为偶数。如果1的个数为偶数,PF=1,如果为奇数,PF=0。
mov al , 1
add al , 10

(al)=00001011,其中三个3,PF=0
mov al , 1
or al , 2

(al)=00000011,其中两个1,PF=1
0个1也是偶数,PF=1






SF 标志
  flag的第 7 位,符号标志位。它记录相关指令执行后,其结果是否为负。如果结果为负,SF=1,如果非负,SF=0。
如果CPU对是对有符号数运算,那么这个结果就有意义,它记录数据的正负。如果CPU当无符号数来运算,这个符号标志就没有意义,虽然相关的指令影响了它的值。
mov al , 10000001B
add al , 1

结果:10000010B,SF=1
表示:如果指令进行的是有符号数的运算,那么结果为负
mov al , 10000001B
add al , 01111111B

结果:00000000B , SF=0
表示:如果指令进行的是有符号数的运算,那么结果为正






CF 标志
  flag的第 0 位,进位标志位。一般情况下,在进行无符号数运算的时候,它记录了运算结果的最高有效位向更高位的进位值,或从更高位的借位值。
最高有效位:N位无符号数,二进制信息的最高位就是N-1位。假想存在第N位,就是相对于最高有效位的更高位。
      
mov al , 98h
add al , al  

结果:al=130h,(al)=30h,CF=1;
CF记录了从最高有效位向更高位的进位值。
mov al , 30h
add al , al

结果:(al)=60h,CF=0
CF记录了从最高有效位向更高位的进位值
当两个数据做减法的时候,有可能向更高位结位。
mov al , 97h
sub al , 98h

结果:(al)=FFh,CF=1,CF记录了向更高位的借位值
sub al , al

结果:(al)=0,CF=0,CF记录了向更高位的借位值






OF 标志
  flag的第 11 位,溢出标志位。它记录相关指令执行后,运算结果超出了机器所能表达的范围,将产生溢出。如果发生了溢出,OP=1,如果没有,OP=0;
mov al , 0F0h   ;有符号数-16的补码 (1001 0000->1111 0000)
add al , 088h   ;有符号数-120的补码(1111 1000->1000 1000)

结果:(al)=78h(有符号数+120),-16-120=-136,结果不对,这就是产生溢出了。OF=1
mov al , 0F0h
add al , 088h    (088h中这个0只是告诉编译器这是一个数值,不是指令,088的0可以不写,但是0F0必须写,因为它第一个时F)

结果:(al)=178h,CF=1,OF=1;
对于无符号数,有进位,CF=1;对于有符号数,有溢出,OF=1
CF和OF所表示的进位和溢出,是分别对无符号数和有符号数运算而言,它们之间没有任务关系。
CF 是对无符号数运算有意义的标志位,而 OF 是对有符号数运算有意义的标志位。






adc 指令
  adc 是带进位的加法指令,它利用 CF 位上记录的进位值。
指令格式:adc 操作对象1 , 操作对象2
功能:操作对象1 = 操作对象1 + 操作对象2 + CF
mov ax , 2
mov bx , 1
sub bx , ax
adc ax , 1

结果:(ax)=4,(ax)+1+CF=2+1+1=4
mov ax , 1
add ax , ax
adc ax , 3

结果:(ax)=5,(ax)+3+CF=2+3+0=5
adc 指令比 add 指令多加了一个 CF 位的值。

;  add 和 adc 配合实现更大数据的加法运算
;  1EF000H+201000H,结果放在ax(高16位)和bx(低16位)中

mov ax , 001Eh
mov bx , 0F000h
add bx , 1000h
adc ax , 0020h
把两个数都分成两个16位数,然后再相加,第一次相加记录了进位值

名称:add123
功能:两个128位数据进行相加
参数:ds:si 指向存储第一个数的内存空间,因为数据为128位,所以需要8个字单元,由低地址单元到高地址单元依次存放128位数据由低到高的各个字。运算结果存储在第一个数的存储空间中。

assume cs:codesg , ds:datasg
datasg segment
        dw 8 dup (3220h)          ;第一个加数
        dw 8 dup (0CDEFh)       ;第二个加数
datasg ends

codesg segment       
start:  mov ax , datasg
        mov ds , ax
        mov si , 0
        mov di , 16
      
        sub ax , ax               ; 把 CF 设置为0
        mov cx , 8
s:        mov ax , [si]
        adc ax , [di]              ;带进位的加
        mov [si] , ax 
        inc si                       ; 用add直接加2会产生进位
        inc si
        inc di
        inc di
        loop s

        mov ax , 4c00h
        int 21h
codesg ends
end star






inc 不会对标志寄存器产生影响







sbb 指令
  sbb 是带借位减法指令,它利用了 CF 位上的借位值。
指令格式:sbb 操作对象1 , 操作对象2
功能:操作对象1 = 操作对象1 - 操作对象2 - CF
eg:sbb ax , bx    实现的功能是:(ax)=(ax)-(bx)-CF
利用 sbb 指令可以实现对任意大的数据进行减法运算
; 计算 003E1000H - 00202000H,结果放在 ax,bx中
mov bx , 1000h
mov ax , 003Eh
sub bx , 2000h
sbb ax , 0020h
结果:(ax)=001DH,(bx)=F000H

sub bx , 2000h 后 CF=1
sbb ax , 0020h == 003E - 0020 - 1 = 001D







cmp 指令
  比较指令,cmp 的功能相当于减法指令,只是不保存结果。cmp 指令执行后,将对标志寄存器产生影响。其他相关指令通过识别这些被影响的标志寄存器位来得知比较结果。
指令格式:cmp 操作对象1 , 操作对象2
功能:操作对象1 = 操作对象1 - 操作对象2  但是不保存结果,仅仅根据计算结果对标志寄存器进行设置。
cmp 可以进行无符号数的比较,也可以进行有符号数的比较;进行无符号数的比较
cmp ax , ax

做 (ax)-(ax)的运算,结果为 0 ,但并不在ax中保存(只是放在CPU内部的暂存器中),仅影响flag的相关各位。
执行后:ZF=1,PF=1,SF=0,CF=0,OF=0
mov ax , 8
mov bx , 3
cmp ax , bx

执行后:(ax)=8,ZF=0,PF=1,SF=0,CF=0,OF=0
进行有符号数的比较
mov ah , 22h    (0010 0010)
mov bh , 0A0h (1010 0000->1110 0000)
cmp ah , bh

(ah)-(bh)=34-(-96)=82h(130(1000 0010)),82h是-126的补码。
SF=-1,ZF=0,CF=0,OF=0,PF=1(01111110)
(82h 的补码 0111 1110)

; cmp 操作数1 , 操作数2     ; 结果SF=1,不能说明 操作数1<操作数2
mov ah , 22h
mov bh , 0A0h
sub ah , bh
22h-0A0h=82h , OF=1,SF=1

34-(-96)=130,溢出,82h(10000010),寄存器当做有符号数解释为-125(82h的补码),这时候SF=1不能说明在逻辑运算上所得的正确结果的正负。SF只记录实际结果的正负
◀▶如果 SF=1,而 OF=0
结果没有溢出,逻辑上真正的结果正负=实际结果的正负

◀▶如果 SF=1,而 OF=1
结果溢出,逻辑上真正的结果正负≠实际结果的正负
但是是因为溢出导致了实际结果为负,所以逻辑上真正的结果为正

◀▶如果 SF=0,而 OF=1
结果溢出,逻辑上真正的结果正负≠实际结果的正负
符号位显示为正,实际结果为正逻辑上真正的结果为负

◀▶如果 SF=0,而 OF=0
结果没有溢出,逻辑上真正的结果正负=实际结果的正负







检测比较结果的条件转移指令
  jcxz 条件转移指令,它检测 cx 中的数值,如果(cx)=0,就修改 IP,否则什么也不做;所有条件转移指令的转移位移都是[-128,127]。
  大多数条件转移指令都检测标志寄存器的相关标志位,这些标志位是被 cmp 指令影响的那些,表示比较结果标志位。这些条件转移指令通常都和 cmp 相配合使用。
根据无符号数的比较结果进行转移的条件转移指令,他们检测 ZF、CF 的值。
根据有符号数的比较结果进行转移的条件转移指令,他们检测 SF、OF、ZF 的值。
无符号数比较

j 表示 jump
e:表示  equal
ne:表示 not equal
b:表示 below
nb:表示 not below
a:表示 above
na:表示 not above

如果 (ah)=(bh) 则 (ah)=(ah)+(ah),否则(ah)=(ah)+(bh)         cmp ah , bh
        je s
        add ah , bh
        jmp short ok
s:     add ah , ah
ok:
**je 之前使不使用cmp指令在于我们的安排。je 检测的是 ZF 位置,不管前面是什么指令;只要CPU执行je指令时,ZF=1,就转移。
        mov ax , 0
        add ax , 0
        je s
        inc ax
s:     inc ax
add ax , 0 使得 ZF=1,je 看到这个就进行转移,这个时候的转移就不带有“相等则转移”的含义

; 编程:统计 data 段中数值为8的字节的个数,用ax保存结果
assume cs:codesg , ds:datasg
datasg segment
        db 8 , 11 , 8 , 1 , 8 , 5 , 63 , 38
datasg ends

codesg segment
start:    mov ax , datasg
        mov ds , ax
        mov bx , 0
        mov dx , 0
        mov cx , 8

s:     cmp byte ptr [bx] , 8     ; 等于8更改标志寄存器,je 进行判断
        je ok
        jmp short next
ok:       inc dx
next:    inc bx
        loop s

        mov ax , 4c00h
        int 21h
codesg ends
end start








DF 标志和串传送指令
  flag的第 10 位,方向标志位。在串处理指令中,控制每次操作后 si , di 的增减。DF=0 每次操作后 si , di 递增;DF=1 每次操作后 si , di 递减
串传送指令:
格式:movsb
功能:(1) ((es)*16+(di))=((ds)*16+(si))
           (2) 如果 DF=0 则:inc si  inc di    (si)=(si)+1    (di)=(di)+1
                如果 DF=1 则:dec si dec di   (si)=(si)-1     (di)=(di)-1
字传送指令:
格式:movsw
功能:将 ds:si 指向的内存字单元中 word 送入 es:di 中,然后根据标志寄存器 DF 位的值,将 si 和 di 递增2或递减2.
cld  : 将标志寄存器的 DF 位置 0  
std :  将标志寄存器的 DF 位置 1

rep movsb
相当于:
s: movsb
    loop

rep 根据 cx 的值,重复执行后面的串传送指令。每执行一次 si 和 di 都会递增或递减指向后一个单元或前一个单元,则 rep movsb 就可以循环实现(cx)个字符的传送。
assume cs:code , ds:data
data segment
        db 'Welcome to masm!'
        db 16 dup(0)
data ends
code segment
start:        mov ax , data
        mov ds , ax
        mov si , 0                ; ds:si 指向 data:0
        mov es , ax
        mov di , 16              ; es:di 指向 data:16
        mov cx , 16             ; (cx)=16,rep循环16次
        cld                   ; 设置 DF=0,正向传送
        rep movsb       ; 拷贝字符串
        mov ax , 4c00h
        int 21h
code ends
end start










pushf 和 popf
    pushf 功能是将标志寄存器的值压栈;而 popf 是从栈中弹出数据,送入标志寄存器中。
    pushf 和 popf,为直接访问标志寄存器提供了一种方法。
assume cs:code
code segment
start:        mov ax , 0
        push ax
        popf        ; 把刚才放入栈中的0送入标志寄存器
        mov ax , 0fff0h
        add ax , 0010h
        pushf       ; 将标志寄存器的值压栈
        pop ax
        and al , 11000101b
        and ah , 00001000b
code ends
end start
pop ax==》ax=47 (0100 0111)
后面第一个and后 ax=45 (0100 0101)
查看标志寄存器变化确定的


debug 查看标志寄存器:cf(0)=1、pf(2)=1、zf(6)=1、of(11)=0
按我的想法:0000 1000 0100 0101   ax应该是0845
对于无符号数来说,CF=1

对于有符号数来说,OF=0

对于无符号数来说,fff0+0010=0,CF=1 ; 和 OF 没关系,OF=0
对于有符号数来说,fff0h 是有符号数 -16的补码;0010h =16,相加 0
FFF0=1111 1111 1111 0000 补码=1000 0000 0001 0000 ==-16
0010=0000 0000 0001 0000 补码=0000 0000 0001 0000 == 16
最后相加结果为0;







标志寄存器在Debug中的表示
    // 9号位的 IF 可屏蔽中断位没有,
标志 值为1的标记 值为0的标记
OF OV NV
SF NG PL
ZF ZR NZ
PF PE PO
CF CY NC
DF DN UP
TF DI EI
     // 全部置0状态的标志寄存器标记




posted on 2017-11-25 19:20  正在加载……  阅读(401)  评论(0编辑  收藏  举报