汇编语言之标志寄存器

标志寄存器

CPU内部的寄存器中,有一种特殊的寄存器(对于不同的机器,个数和结构都有可能不同)具有以下三个功能:

  • 用来存储某些相关指令的执行结果
  • 用来为CPU执行相关的指令提供行为依据
  • 用来控制CPU的相关工作方式

这种特殊的寄存器在8086CPU中称为标志寄存器。8086的标志寄存器有16位,其中存储的信息通常被称为程序状态字(PSW)。

标志寄存器与其他寄存器不一样,其他寄存器是用来放数据的,都是整个寄存器具有一个含义,而标志寄存器是按位起作用的。

8086中的标志寄存器的结构如下图所示:

其中1、3、4、12、13、14、15位在8086中没有使用。另外对于有确切含义的每一位,我们称为标志位

ZF标志

标志寄存器的第六位是ZF,零标志位。他是记录相关指令执行后,其结果是否位零。如果为零,那么zf=1,如果不为零,那么zf=0

比如指令:

1
2
mov ax, 1
sub ax, 1

执行后,ax的结果为0,所以zf=1

再比如:

1
2
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

比如指令:

1
2
mov al, 1
add al, 10

执行后,al为00001011B,其中有3个1,所以pf=0

再比如:

1
2
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

比如指令:

1
2
mov al, 100000001B
add al, 1

执行后结果为10000010B,sf=1,表示如果进行的是有符号运算的话,则结果为负

再比如

1
2
mov al, 10000001B
add al, 01111111B

执行后,结果为0,sf=0,表示,如果进行的是有符号运算,这结果为非负

CF标志

标志寄存器的第0为是CF,仅为标志位。一般情况下,在进行 无符号 运算的时候,它记录了运算结果的最高有效位向更高有效位的进位,或从更高有效位的借位。

比如指令:

1
2
3
mov al, 98H
add al, al ; 执行后(al)=30H, CF=1, 产生进位
add al, al ; 执行后(al)=60H, CF=0, 没有进位,或者说进位为0

再比如:

1
2
3
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

这里一定要注意的是CF和OF的区别,CF是对无符号数运算有意义的标志位,而OF是对有符号数运算有意义的标志位。 它们之间没有任何关系

比如指令:

1
2
mov al, 98
add al, 99

执行后,of=1,发生溢出,没有进位

再比如:

1
2
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

一般,movsbmovsw一般配合指令rep指令使用,如rep movsb, 相当于

1
2
s: movsb  ; 当然啦,在此之前要先设置cx寄存器
loop s

adc、sbb、cmp、pushf和popf指令

再没有标志寄存器的时候,我们进行加减运算最多只能进行16位的加减运算,这在实际的应用中显然是不够的,那么有了标志寄存器,在结合adcsbb指令,我们就可以进行任意多位的数的加减法了

adc指令

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

  • 指令格式:adc 操作对象1,操作对象2
  • 功能: 操作对象1 = 操作对象1 + 操作对象2 + CF

比如:计算1EF000H + 201000H,结果放在ax(高16位)和bx(低16位)中:

1
2
3
4
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中

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

cmp指令

cmp是比较指令,功能相当于减法指令,只是不保存结果。执行后,对标志寄存器产生影响

  • cmp指令格式:cmp 操作对象1,操作对象2
  • 功能:计算操作对象1 - 操作对象2,但不保存结果

经过cmp计算后,我们就可以用jejnejbjnbjajna指令进行跳转了。

因为比较复杂,在这里就不具体说实现的细节了,具体在更。

pushf和popf指令

pushf的功能是将标志寄存器的值压栈,而popf是从栈中弹出数据送入标志寄存器

这两个指令为直接访问寄存器提供了一种方法


在这里,我们讨论了六种寄存器,另外还有IF,TF,和AF没有讨论,具体再更