汇编语言 —— 标志寄存器

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

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

8086CPU 的标志寄存器结构如下图:

8086CPU 的标志寄存器结构
8086CPU 的标志寄存器结构

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

示例

指令:

text

mov ax, 1
sub ax, 1

执行后,结果为 0,则 ZF=1。

指令:

text

mov ax, 2
sub ax, 1

执行后,结果不为 0,则 ZF=0。

技巧
在 8086CPU 的指令集中,有的指令的执行是影响标志寄存器的,比如:add、sub、mul、div、inc、or、and 等,它们大都是运算指令(进行逻辑或算术运算);有的指令的执行对标志寄存器没有影响,比如,mov、push、pop 等,它们大都是传送指令。

Flag 的第 2 位是 PF,奇偶标志位。它记录相关指令执行后,其结果的所有 bit 位中 1 的个数是否为偶数。如果 1 的个数为偶数,PF=1,如果为奇数,那么 PF=0。

示例

指令:

text

mov al, 1
add al, 10

执行后,结果为 00001011B,其中有 3 个 1,则 PF=0。

指令:

text

mov al, 1
or  al, 2

执行后,结果为 00000011B,其中有 2 个 1,则 PF=1。


Flag 的第 7 位是 SF,符号标志位。它记录相关指令执行后,其结果是否为负。如果结果为负,SF=1;如果非负,SF=0。

SF 标志,是 CPU 对有符号数运算结果的一种记录,它记录数据的正负。如果我们将数据当作无符号数来运算,SF 的值则没有意义,虽然相关的指令影响了它的值。

示例

指令:

text

mov al, 10000001B
add al, 1

执行后,结果为 10000010B,SF=1,表示:如果指令进行的是有符号数运算,那么结果为负。

指令:

text

mov al, 10000001B
add al, 01111111B

执行后,结果为 0,SF=0,表示:如果指令进行的是有符号数运算,那么结果为非负。


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

对于位数为 N 的无符号数来说,其对应的二进制信息的最高位,即第 N-1 位,就是它的最高有效位,而假想存在的第 N 位,就是相对于最高有效位的更高位。

假想的更高位
假想的更高位

当两个数据相加的时候, 有可能产生从最高有效位向更高位的进位。比如,两个 8 位数据:98H+98H,将产生进位。由于这个进位值在 8 位数中无法保存,CPU 在运算的时候记录在一个特殊的寄存器的某一位上,8086CPU 就用 Flag 的 CF 位来记录这个进位值。

示例

指令:

text

mov al, 98H
add al, al          ; 执行后: (al)=30H,CF=1,CF 记录了从最高有效位向更高位的进位值
add al, al          ; 执行后: (al)=60H,CF=0,CF 记录了从最高有效位向更高位的进位值

当两个数据做减法的时候,有可能向更高位借位。比如,两个 8 位数据:97H-98H,将产生借位,借位后,相当于计算 197H-98H,Flag 的 CF 位也可以用来记录这个借位值。

示例

指令:

text

mov al, 97H
sub al, 98H          ; 执行后: (al)=FFH,CF=1,CF 记录了向更高位的借位值
sub al, al           ; 执行后: (al)=0,CF=0,CF 记录了向更高位的借位值

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

Flag 的第 11 位是 OF,溢出标志位。 一般情况下,OF 记录了有符号数运算的结果是否发生了溢出。如果发生溢出,OF=1;如果没有,OF=0。

一定要注意 CF 和 OF 的区别:CF 是对无符号数运算有意义的标志位,而 OF 是对有符号数运算有意义的标志位。

示例

指令:

text

mov al, 98
add al, 99

add 指令执行后:CF=0,OF=1。

CPU 在执行 add 等指令的时候,就包含了两种含义:无符号数运算和有符号数运算。对于无符号数运算,CPU 用 CF 位来记录是否产生了进位;对于有符号数运算,CPU 用 OF 位来记录是否产生了溢出。


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

比如指令adc ax, bx实现的功能是:(ax)=(ax)+(bx)+CF。

CPU 提供 adc 指令的目的,是来进行加法的第二步运算的。adc 指令和 add 指令相配合就可以对更大的数据进行加法运算。

在执行 adc 指令的时候加上的 CF 的值的含义,是由 adc 指令前面的指令决定的,也就是说,关键在于所加上的 CF 值是被什么指令设置的。显然,如果 CF 的值是被 sub 指令设置的,那么它的含义就是借位值;如果是被 add 指令设置的,那么它的含义就是进位值。

示例

编程,计算 1EF0001000FH+2010001EF0H,结果放在 ax(高16位),bx(次高16位),cx(低16位)中。

计算分 3 步进行:

  1. 先将低 16 位相加,完成后,CF 中记录本次相加的进位值;
  2. 再将次高 16 位和 CF(来自低 16 位的进位值)相加,完成后,CF 中记录本次相加的进位值;
  3. 最后高 16 位和 CF(来自次高 16 位的进位值)相加,完成后,CF 中记录本次相加的进位值。

程序如下:

text

mov ax, 001EH
mov bx, 0F000H
mov cx, 1000H
add cx, 1EF0H
adc bx, 1000H
adc ax, 0020H

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

比如指令sbb ax, bx实现的功能是:(ax)=(ax)-(bx)-CF。

sbb 指令执行后,将对 CF 进行设置。利用 sbb 指令可以对任意大的数据进行减法运算。

示例

计算 003E1000H-00202000H,结果放在 ax,bx 中。

程序如下:

text

mov bx, 1000H
mov ax, 003ЕН
sub bx, 2000H
sbb ax, 0020H

cmp 是比较指令,cmp 的功能相当于减法指令,只是不保存结果。cmp 指令执行后,将对标志寄存器产生影响。其他相关指令通过识别这些被影响的标志寄存器位来得知比较结果。

比如,指令cmp ax, ax,做 (ax)-(ax) 的运算,结果为 0,但并不在 ax 中保存,仅影响 flag 的相关各位。指令执行后:ZF=1, PF=1, SF=0, CF=0, OF=0。

示例

CMP 指令——无符号数运算

我们通过 cmp 指令执行后,相关标志位的值就可以看出比较的结果。以 cmp ax, bx 为例:

如果 (ax)=(bx) 则 (ax)-(bx)=0,所以:ZF=1;

如果 (ax)!=(bx) 则 (ax)-(bx)!=0,所以:ZF=0;

如果 (ax)<(bx) 则 (ax)-(bx) 将产生借位,所以:CF=1;

如果 (ax)≥(bx) 则 (ax)-(bx) 不必借位,所以:CF=0:如果 (ax)>(bx) 则 (ax)-(bx) 既不必借位,结果又不为 0,所以:CF=0 并且 ZF=0;

如果 (ax)≤(bx) 则 (ax)-(bx)既可能借位,结果可能为 0,所以:CF=1 或 ZF=1。

CPU 在执行 cmp 指令的时候,也包含两种含义:进行无符号数运算和进行有符号数运算。所以利用 cmp 指令可以对无符号数进行比较,也可以对有符号数进行比较。

示例

CMP 指令——有符号数运算

我们通过 cmp 指令执行后,考查 SF (得知实际结果的正负)的同时考查 OF (得知有没有溢出),就可以得知逻辑上真正结果的正负,同时就可以知道比较的结果。以 cmp ah, bh 为例:

  1. 如果 SF=1,OF=0

    OF=0,说明没有溢出,逻辑上真正结果的正负 == 实际结果的正负; 因 SF=1,实际结果为负,所以逻辑上真正的结果为负,所以(ah)<(bh)。

  2. 如果 SF=1,OF=1

    OF=1,说明有溢出,逻辑上真正结果的正负 != 实际结果的正负; 因 SF=1,实际结果为负。 实际结果为负,而又有溢出,这说明是由于溢出导致了实际结果为负,如果因为溢出导致了实际结果为负,那么逻辑上真正的结果必然为正。 因此,SF=1,OF=1,说明 (ah)=(bh)。

  3. 如果 SF=0,OF=1

    OF=1,说明有溢出,逻辑上真正结果的正负 != 实际结果的正负; 因 SF=0,实际结果非负。而 OF=1 说明有溢出,则结果非 0,所以,实际结果为正。 实际结果为正,而又有溢出,这说明是由于溢出导致了实际结果非负,如果因为溢出导致了实际结果为正,那么逻辑上真正的结果必然为负。 因此,SF=0,OF=1,说明 (ah)<(bh)。

  4. 如果 SF=0,而 OF=0

    OF=0,说明没有溢出,逻辑上真正结果的正负 = 实际结果的正负; 因 SF=0,实际结果非负,所以逻辑上真正的结果非负,所以 (ah)≥(bh)。


除了 jcxz 之外,CPU 还提供了其他条件转移指令,大多数条件转移指令都检测标志寄存器的相关标志位,根据检测的结果来决定是否修改 IP。检测的标志位就是被 cmp 指令影响的那些,表示比较结果的标志位。这些条件转移指令通常都和 cmp 相配合使用。

因为 cmp 指令可以同时进行两种比较,无符号数比较和有符号数比较,所以根据 cmp 指令的比较结果进行转移的指令也分为两种,即根据无符号数的比较结果进行转移的条件转移指令(它们检测 ZF、CF 的值)和根据有符号数的比较结果进行转移的条件转移指令(它们检测 SF、OF 和 ZF 的值)。

下面是常用的根据无符号数的比较结果进行转移的条件转移指令。

指令 含义 检测的相关标志位
je 等于则转移 ZF=1
jne 不等于则转移 ZF=0
jb 低于则转移 CF=1
jnb 不低于则转移 CF=0
ja 高于则转移 CF=0 且 ZF=0
jna 不高于则转移 CF=1 或 ZF=1

Flag 的第 10 位是 DF,方向标志位。在串处理指令中,控制每次操作后 si、di 的增减:

  • DF=0 每次操作后 si、di 递增;
  • DF=0 每次操作后 si、di 递减。

功能:执行 movsb 指令相当于进行下面几步操作:

  1. ((es)x16+(di))=((ds)x16+(si))
  2. 如果 DF=0 则:
    • (si)=(si)+1
    • (di)=(di)+1
  3. 如果 DF=1 则:
    • (si)=(si)-1
    • (di)=(di)-1

可以看出,movsb 的功能是将 ds:si 指向的内存单元中的字节送入 es:di 中,然后根据标志寄存器 DF 位的值,将 si 和 di 递增或递减。

当然,也可以传送一个字,使用 movsw 指令。

movsw 的功能是将 ds:si 指向的内存字单元中的字送入 es:di 中,然后根据标志寄存器 df 位的值,将 si 和 di 递增 2 或递减 2。

movsb 和 movsw 进行的是串传送操作中的一个步骤,一般来说,movsb 和 movsw 都和 rep 配合使用,格式为:rep movsbrep movsw

rep 的作用是根据 cx 的值,重复执行后面的串传送指令。由于每执行一次 movsb 指令 si 和 di 都会递增或递减指向后一个单元或前一个单元,则 rep movsb 就可以循环实现(cx)个字符的传送。

由于 flag 的 df 位决定着串传送指令执行后,si 和 di 改变的方向,所以 CPU 应该提供 相应的指令来对 df 位进行设置,从而使程序员能够决定传送的方向。

8086CPU 提供下面两条指令对 df 位进行设置:

  • cld 指令:将标志寄存器的 df 位置 0;
  • std 指令:将标志寄存器的 df 位置 1。

例题:编程,用串传送指令,将 data 段中的第一个字符串复制到它后面的空间中。

text

data segment
    db 'Welcome to masm!'
    db 16 dup (0)
data ends
技巧

我们分析一下,使用串传送指令进行数据的传送,需要给它提供一些必要的信息,它们是:

  1. 传送的原始位置:ds:si;
  2. 传送的目的位置:es:di;
  3. 传送的长度:cx;
  4. 传送的方向:df。

在这个问题中,这些信息如下:

  1. 传送的原始位置:data:0;
  2. 传送的目的位置:data:0010;
  3. 传送的长度: 16;
  4. 传送的方向:因为正向传送(每次串传送指令执行后,si 和 di 递增)比较方便,所以设置 DF=0。

明确上述信息后,编写程序:

text

mov ax, data
mov ds, ax
mov si, 0           ; ds:si 指向 data:0

mov es, ax
mov di, 16          ; es:di 指向 data:0010

mov cx, 16          ; (cx)=16,rep 循环 16 次

cld                 ; 设置 DF=0,正向传送

rep movsb

pushf 的功能是将标志寄存器的值压栈,而 popf 是从栈中弹出数据,送入标志寄存器中。pushf 和 popf,为直接访问标志寄存器提供了一种方法。