CPU 内部的寄存器中, 有一种特殊的寄存器(不同处理机,个数和结构可能都不同)具有三种作用:
1、用来存储相关指令的某些执行结果;
2、用来为 CPU 执行相关指令提供行为依据;
3、用来控制 CPU 的相关工作方式;
这种特殊寄存器在8086CPU中,被称为标志寄存器。8086CPU 的标志寄存器有 16 位,其中存储的信息通常被称为 程序状态字(PSW)。
标志寄存器简称 flag ;
8086CPU 的 flag 寄存器结构
flag 的1、3、5、12、13、14、15 位在8086CPU 中没有使用,不具有任何含义,其他都有特殊含义。
ZF 标志
flag的第 6 位,零标志位。它记录相关指令执行后,其结果是否为 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状态的标志寄存器标记