CPU内部的寄存器中,有一种特殊的寄存器(对于不同的机器,个数和结构都有可能不同)具有以下三个功能:
- 用来存储某些相关指令的执行结果
- 用来为CPU执行相关的指令提供行为依据
- 用来控制CPU的相关工作方式
这种特殊的寄存器在8086CPU中称为标志寄存器。8086的标志寄存器有16位,其中存储的信息通常被称为程序状态字(PSW)。
标志寄存器与其他寄存器不一样,其他寄存器是用来放数据的,都是整个寄存器具有一个含义,而标志寄存器是按位起作用的。
8086中的标志寄存器的结构如下图所示:
其中1、3、4、12、13、14、15位在8086中没有使用。另外对于有确切含义的每一位,我们称为标志位
ZF标志
标志寄存器的第六位是ZF,零标志位。他是记录相关指令执行后,其结果是否位零。如果为零,那么zf=1,如果不为零,那么zf=0
比如指令:
mov ax, 1
sub ax, 1
执行后,ax的结果为0,所以zf=1
再比如:
mov ax, 2
sub ax, 1
执行后,ax的结果不为0,所以zf=0
在这里,有一点需要注意:在8086的指令集中,有的指令的执行是影响标志位的,比如add、sub、mul、div、inc、or、and等,它们大都是运算指令(进行逻辑或算数运算);有的指令的执行对标志寄存器没有影响,比如mov、push、pop等,它们大都是传送指令。
PF标志
标志寄存器的第二位是PF,奇偶标志位。它记录相关指令执行后,其结果的所有bit位中1的个数是否位偶数。如果是偶数,pf=1,如果位奇数,pf=0
比如指令:
mov al, 1
add al, 10
执行后,al为00001011B,其中有3个1,所以pf=0
再比如:
mov al, 1
or al, 2
执行后结果为00000011B,其中有2个1,所以pf=1
SF标志
标志寄存器的第七位是SF,符号标志位。它记录相关指令执行后,其结果是否为负。如果为负,sf=1,如果非负,sf=0
这里要明确一点,在计算机中,通常用补码来表示有符号的数据,计算机中的一个数据既可以看作有符号数,也可以看作无符号数。不管我们如何看待,当CPU在进行运算的时候,就已经包含了两种含义,也将的到同一种信息来记录的两种结果。关键在于我们的程序需要那一种。
SF标志,是CPU对有符号数运算结果一种记录。如果我们将数据当作无符号数来运算,SF标志位则没有意义,虽然计算过程中影响了他的值。
我个人在这里的更简单写的理解是计算后结果的最高位是否为1,为1,这sf=1,否则,sf=0
比如指令:
mov al, 100000001B
add al, 1
执行后结果为10000010B,sf=1,表示如果进行的是有符号运算的话,则结果为负
再比如
mov al, 10000001B
add al, 01111111B
执行后,结果为0,sf=0,表示,如果进行的是有符号运算,这结果为非负
CF标志
标志寄存器的第0为是CF,仅为标志位。一般情况下,在进行 无符号 运算的时候,它记录了运算结果的最高有效位向更高有效位的进位,或从更高有效位的借位。
比如指令:
mov al, 98H
add al, al ; 执行后(al)=30H, CF=1, 产生进位
add al, al ; 执行后(al)=60H, CF=0, 没有进位,或者说进位为0
再比如:
mov al, 97H
sub al, 98H ; 执行后(al)=FFH, CF=1, 产生借位
sub al, al ; 执行后(al)=0, CF=0, 借位为0
OF标志
标志寄存器的第11位是OF,溢出标志位。一般情况下,OF记录了 有符号数 的运算结果是否发生了溢出。如果发生了溢出,of=1,如果没有,of=0
{% note success %} 这里一定要注意的是CF和OF的区别,CF是对无符号数运算有意义的标志位,而OF是对有符号数运算有意义的标志位。 它们之间没有任何关系 {% endnote %}
比如指令:
mov al, 98
add al, 99
执行后,of=1,发生溢出,没有进位
再比如:
mov al, 0F0H
add al, 78H
执行后:of=0,没有溢出
DF标志
标志寄存器的第10位是DF,方向标志位。在串传送指令中,控制每次操作后si、di的增减。
df=0,每次操作后si、di递增
df=1,每次操作后si、di递减
例如串传送指令movsb
:
-
格式:
movsb
-
功能:执行下面几步操作:1、((es) x 16 + (di)) = ((ds) x 16 + (si)) 2、如果df=0,(si)=(si)+1, (di)=(di)+1。如果df=1,则(si)=(si)-1, (di)=(di)-1
另外还有movsw
,这时候就是一次传送一个字了,相应的si和di的+1-1也就变更成了+2-2
一般,movsb
和movsw
一般配合指令rep
指令使用,如rep movsb
, 相当于
s: movsb ; 当然啦,在此之前要先设置cx寄存器
loop s
adc、sbb、cmp、pushf和popf指令
再没有标志寄存器的时候,我们进行加减运算最多只能进行16位的加减运算,这在实际的应用中显然是不够的,那么有了标志寄存器,在结合adc
和sbb
指令,我们就可以进行任意多位的数的加减法了
adc指令
adc是带进位的加法指令,它利用了CF上记录的进位值
- 指令格式:adc 操作对象1,操作对象2
- 功能: 操作对象1 = 操作对象1 + 操作对象2 + CF
比如:计算1EF000H + 201000H,结果放在ax(高16位)和bx(低16位)中:
mov ax, 001EH
mov bx, 0F000H
add bx, 1000H
adc ax, 0020H
更多位数的数相加和以上同理
sbb指令
sbb是借位减法指令,它利用了CF位上记录的借位值
- 指令格式:sbb 操作对象1,操作对象2
- 功能:操作对象1 = 操作对象1 - 操作对象2 - CF
比如:计算003E1000H - 00202000H,结果保存在ax,bx中
mov bx, 1000H
mov ax, 003EH
sub bx, 2000H
sbb ax, 0020H
cmp指令
cmp是比较指令,功能相当于减法指令,只是不保存结果。执行后,对标志寄存器产生影响
- cmp指令格式:cmp 操作对象1,操作对象2
- 功能:计算操作对象1 - 操作对象2,但不保存结果
经过cmp
计算后,我们就可以用je
、jne
、jb
、jnb
、ja
、jna
指令进行跳转了。
因为比较复杂,在这里就不具体说实现的细节了,具体在更。
pushf和popf指令
pushf的功能是将标志寄存器的值压栈,而popf是从栈中弹出数据送入标志寄存器
这两个指令为直接访问寄存器提供了一种方法
在这里,我们讨论了六种寄存器,另外还有IF,TF,和AF没有讨论,具体再更
完