引言

1.8086CPU的标志寄存器有16位,其中存储信息通常被称为程序状态字(PSW)

2.我们已经使用过8086CPU的ax,bx,cx,dx,si,di,bp,sp,ip,cs,ss,ds,es等13个寄存器

3.本章中的标志寄存器【flag】是我们的最后一个寄存器

4.flag和其他寄存器不一样,其他寄存器是用存放数据的,都是整个寄存器具有一个含义

5.而flag寄存器是安位起作用,也就是说:它的每一位都有专门的含义,记录特定的信息

image-20230414160205653

6.flag在8086CPU中,只有 0 2 4 6 7 8 9 10 11具有特殊的含义,其他位置都没有意义

1.ZF标志

1.flag的第6位是ZF,零标志位,它记录相关指令执行后

1
2
结果为0, ZF=1
结果不为0,ZF=0

2.比如

1
2
3
4
5
mov ax,1
sub ax,1 执行后结果为0,则ZF=1

mov ax,2
sub ax,1 执行后结果为1,则ZF=0

3.对于ZF的值,我们可以这样来看,ZF标志相关指令的计算结果是否为0,如果是0,则在ZF要记录下“是0”这样的肯定信息

1
2
3
mov ax,1
add ax,0
执行后,结果为0,则ZF=1 表示结果是0

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

2.PF标志

1.PE,奇偶标志位。它记录指令执行后,结果的所有二进制位中的1的个数

1
2
为偶数,PE=1
为奇数,PE=0

2.比如

1
2
3
4
5
6
mov al,1
add al,10
执行后,结果为00001011B,其中有3个1,则PE=0
mov al,1
or al,10
执行后,结果为00000011B,其中有2个1,则PE=1

3.SF

1.flag的第7位是SF,符号标志位,它记录指令执行后

1
2
结果为负SF = 1
结果为正SF = 0

有符号数和补码[课外知识,得学]

比如

1
2
00000001B,可以看作无符号1,或者有符号数+1
10000001B,可以看作无符号数129,或者看作有符号数-127

2.示例:

1
2
3
mov al,10000001B
add al,1
结果(al)=10000010B

我们可以将add指令的运算当作无符号数的运算,那么add指令相当于计算129+1,结果是130【10000010B】

也可以将add指令进行的运算当作有符号的运算,那么add指令相当于计算-127+1,结果为-126【10000010B】

不管我们如何看待,cpu在执行add等指令的时候,就已经包含了两种含义,也就得到用同一种信息来记录的2中结果

关键在于我们需要哪一种结果

3.SF标志,就是CPU对有符号数运算结果的一种记录,它记录数据的正负

4.CPU在执行add等指令的时候,必然要影响到SF标志位的值

5.至于需不需要,关键是看我们自己需不需要

某些指令将影响标志寄存器中的多个标志位,这些被影响的标志位比较全面的记录了指令执行结果,为相关的处理提供了所需的依据,至于需不需要这些结果全都看我们自己

4.CF标志

【相对于无符号数而言】

1.flag的第0位是CF,进位标志位

2.一般情况下,在进行无符号数运算的时候,它记录了运算结果的最高有效位向更高位的进位值,或从更高位的借位值

3.对于位数为N的无符号数来说,其对应的二进制信息的最高位,也就是第N-1位,的最高有限位,而假象存在的第N位,就是相对于最高有效位的更高位

我们知道,当2个数据相加的时候,可能产生从最高有效位向更高位的进位,这个更高位去哪里了呢?

image-20230414164826803

比如

1
2
3
4
mov al,98H
add al,al;
执行后,al=30H ,cf=1
cf记录了最高有效位向更高位的进位值

在比如

两个8位的数据97H-98H,将产生借位,借位后,相当于是197H-98H,,而flag的cf位也可以用来记录这个借位值

5.OF标志

【对有符号数而言】

1.溢出

在进行有符号运算的时候,如结果超过了机器所能表示的范围称为溢出

2.什么是机器所能表示的范围?

add al,3 那么对于8位的有符号数据,机器所能表示的范围就是-128~127

3.如果我们使用add指令进行的是有符号数运算,则98+99=-59,这样的结果让人无法接受

造成这种情况的原因,就是实际结果是197,作为一个有符号数,在8位寄存器al中存放不下

由于在进行有符号数运算的时候,可能发生溢出而造成结果的错误。所以CPU需要对指令执行后是否参数溢出进行记录,从而有了OF

区别:CF和OF

1
2
3
4
5
6
1.CF是对无符号数运算有意义的标志位
用CF来记录是否产生了进位

2.OF是对无符号数运算有意义的标志位
用OF记录是否产生了溢出
用SF来记录结果的符号

无符号CF有符号PF+SF

image-20230414175326095

6.adc指令

1.adc是带有进位加法指令,它利用了CF上记录的进位值

2.格式

1
adc 操作对象1,操作对象2

3.功能:

1
2
3
操作对象1=操作对象1+操作对象2+CF
add ax,bx
(ax)=(ax)+(bx)+cf

4.在执行adc指令的时候加上的cf的值的含义,由adc指令前面的指令决定的,也就是说,关键在于所加上的cf的值是被什么指令设置的

1
显然,如果CF的值是被sub指令设置的,那么它的含义就是借位值;如果是被add指令设置的,那么就是进位值

5.我们来看一下两个数据:0198H和0183H如何相加

1
2
3
可以看出:分2步
1.低位相加;
2.高位相加加上低位相加产生的进位值

6.和 add ax,bx有相同结果

1
2
add al,bl
adc ah,bh

可以看出adc指令的目的就是,进行加法的第二步运算

adc指令和add指令相配合可以对更大的数据进行运算

编程小任务

编程计算1EF000H+201000H,结果放在ax(高16位)和bx(低16位)

1
2
3
4
mov ax,001EH
mov bx,0F000H
add bx,1000H
adc ax,0020H

adc指令执行后,也可能产生进位值,所以也会对CF位进行设置

7.sbb指令

1.sbb是带借位减法指令,它利用了CF位上记录的借位值

2.格式

1
sbb操作对象1,操作对象2

3.功能

1
操作对象1=操作对象1-操作对象2-CF

4..比如:sbb ax,bx

1
(ax)=(ax)-(bx)-CF

5.利用sbb指令,我们可以对任意大的数据进行剑法运算

比如:计算003E100OH-00202000H,结果放在ax 和 bx中

1
2
3
4
mov bx,1000H
mov ax,003EH
sub bx,2000H
sbb ax,0020

8.cmp指令

1.cmp是比较指令,功能相当于减法指令,只是不保存结果

2.cmp指令执行后,将对标志寄存器产生影响

3.其他相关指令通过识别这些被影响的标志寄存器位来得知比较结果

4.格式

1
cmp 操作对象1 - 操作对象2

5.功能:计算操作对象1-操作对象2,但并不能保存结果,仅仅根据计算结果对标志寄存器进行设置

比如:cmp ax,ax

1
ZF=1;PF=1;SF=0;CF=0;OF=0

image-20230415125533748

6.在使用cmp指令的时候,也包含了2中含义

1
2
1.进行无符号运算
2.进行有符号运算

对于有符号数的操作,不能说SF=1则操作对象<操作对象2

1
2
(ah)=22H,(bh)=0A0H
则(ah)-(bh)=34-(-96)=130=82H,82Hshi -126的补码,所以SF=1,但是不能说ah<bh

当产生溢出的时候,就会影响半段

所以我们就需要一起考虑到溢出的情况,也就是 OF寄存器

1
2
3
4
5
6
7
8
9
10
1.SF=1,OF=0
没有溢出,所以就是小于
2.SF=1,OF=1
有溢出的,所以就是大于
3.SF=0,OF=1
有溢出,所以就是小于
4.SF=0,OF=0
没有溢出,所以就是大于

总的来说,当有溢出的时候就取反。

9.检测比较结果的条件转移指令

1.被cmp指令影响的标志位,这些田间转移指令通常和cmp相配合使用

cmp指令的比较结果进行转移的指令分为2种

1
2
3
4
对于无符号数的检测
ZF CF
对于有符号数的检测
SF OF ZF

2.条件转移指令小结【对于无符号】

1
2
3
4
5
6
je		等于则转移		ZF=1
jne 不等于则转移 ZF=0
jb 低于则转移 CF=1
jnb 不低于则转移 CF=0
ja 高于则转移 CF=0,ZF=0
jna 不高于则转移 CF=1或者ZF=1

编程训练

实现如果(ah)=(bh)则(ah)=(ah)+(ah)否则(ah)=(ah)+(bh)

1
2
3
4
5
6
cmp ah,bh
je s
add,ah,bh
jmp short ok
s:add ah,ah
ok:ret

虽然je的含义是相等则转移,但是它进行的操作的是ZF=1则转移

je检测的是ZF位置,不管je前面是什么指令,只要CPU实现的时候je指令的时候,ZF=1那么就会发生转移

课堂练习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
计算出data段种的8的个数
assume cs:code
data segment
db 8,11,8,1,8,5,63,38
data ends

code segment
start: mov dax,data
mov dsLax
mov bx,0
mov ax,0
mov cx,0

s: cmp byte ptr [bx],8
jne next
inc ax

next: inc bx
loop s

mov ax,4c00H
int 21h

code ends
end start

10.DF标志和串传送指令

1.flag的第10位是DF,方向标志位

2.在串 处理指令中,控制每次操作后si和di的增减

1
2
DF=0 , 每次操作后 si di 递增
DF=1 , 每次操作后 si di 递减

3.格式 movsb

1
2
3
功能(以字节位单位传送)
1.((es)*16+(di))=((ds)*16+(si))
如果DF=0 DF=1

4.格式 movsw

1
以字来传递

5.movsb和movsw进行的是串传送操作中第一个不走,一般来说,movsb和movsw都和rep配合使用,格式rep movsb

rep的作用是根据cx的值,重复执行后面串传送指令

由于没执行依次movsb si di都会变化,则rep movsb就可以循环实现(cx)个字符的传送

6.如何修改DF?

1
2
cld 将DF设置成0
std 将DF设置成1

7.练习一手

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
assume cs:code
data segment
db 'welcome to masm!'
db 16 dup(0)
data ends

code segment
start: mov dx,data
mov ds,ax
mov si,0
mov es,ax
mov di,16

mov cx,16

cld ;设置DF=0
rep movsb ;1.((es)*16+(di))=((ds)*16+(si))

mov ax,4c00H
mov 21h

code ends
end start

11.pushf 和 popf

1.pushf:将标志寄存器的值压入栈中

popf:从栈中弹出数据,送入标志寄存器中

2.pushf和popf,为直接访问标志寄存器提供了一种方法