标志寄存器的 3 种作用:

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

flag 寄存器是按位起作用的,每一位有专门的含义,记录特定信息。

flag 寄存器

ZF 标志

Zero Flag,零标志位,记录相关指令执行后,其结果是否为 0。结果为 0 时 zf = 1,否则 zf = 0。

大多运算指令(逻辑、算术运算)的执行对标志寄存器有影响。传送指令例如:mov、push、pop 对标志寄存器没有影响。

PF

Plural Flag,奇偶标志位,记录相关指令执行后,其结果的所有 bit 位中 1 的个数如果为偶数,pf = 1,如果为奇数,pf = 0。

SF 标志

Sign Flag,记录相关指令执行后,其结果是否为负。如果结果为负,sf = 1,如果非负,sf = 0。

CF 标志

Carry Flag,进位标识位。进行无符号数运算时,记录了运算结果的最高有效位更高位进位值,或从更高位的借位值

OF 标志

Overflow Flag,进行有符号数运算时,如果超过了机器所能表示的范围成为溢出

CF 和 OF 所表示的进位和溢出,是分别对于无符号数和有符号数运算而言的,他们之间没有任何关系。

adc 指令

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

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

例:

1
2
3
4
mov ax,2
mov bx,1
sub bx,ax
adc ax,1

执行后 (ax) = 4,相当于 (ax) + 1 + CF = 2 + 1 + 1 = 4。

利用 adc 和 add 指令可以对任意大的数据进行加法运算。

计算 1ef0001000H + 2010001ef0H,结果放在 ax(最高 16 位)、bx(次高 16 位)、cx(低 16 位)。

1
2
3
4
5
6
mov ax,001eH
mov bx,0f000H
mov cx,1000H
add cx,1ef0H
adc bx,1000H
adc ax,0020H

计算两个 128 位数据的和。ds:si 指向第一个数的内存空间;ds:di 指向存储第二个数的内存空间。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
add128:
push ax
push cx
push si
push di

sub ax,ax ;将 CF 设置为 0

mov cx,8
s: mov ax,[si]
adc ax,[di]
mov[si],ax
inc si ;不影响 CF 位
inc si
inc di
inc di
loop s

pop di
pop si
pop cx
pop ax
ret

sbb 指令

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

格式:

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

sbb ax,bx 实现了 (ax) = (ax) - (bx) - 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
cmp 操作对象1,操作对象2

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

1
2
3
mov ax,8
mov bx,3
cmp ax,bx

比较指令的设计思路:通过做减法运算,影响标志寄存器,标志寄存器的相关位记录了比较的结果。

无符号数的情况

1
cmp ax,bx
  1. zf = 1,说明 (ax) = (bx)
  2. zf = 0,说明 (ax) ≠ (bx)
  3. cf = 1,说明 (ax) < (bx)
  4. cf = 0,说明 (ax) ≥ (bx)
  5. cf = 0 并且 zf = 0,说明 (ax) > (bx)
  6. cf = 1 或者 zf = 1,说明 (ax) ≤ (bx)

有符号数的情况

  1. sf = 1 且 of = 0,说明 (ax) < (bx)
  2. sf = 1 且 of = 1,说明 (ax) > (bx)
  3. sf = 0 且 of = 1,说明 (ax) < (bx)
  4. sf = 0 且 of = 0,说明 (ax) ≥ (bx)

关于 2 的推理过程:如果结果为负,但是溢出了,说明是溢出导致结果为负,那么逻辑上真正的结果为正,所以 ax > bx。

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

根据某种条件决定是否修改 IP。

大多数条件转移指令都检测标志寄存器的相关标志位,根据检测结果决定是否修改 IP。常与 cmp 指令配合使用。

cmp 指令可以同时进行有符号数比较无符号数比较,因此根据 cmp 指令的比较结果进行转移的指令也分为两种。

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

统计 data 段中数值为 8 的字节的个数,用 ax 保存统计结果:

1
2
3
4
5
6
7
8
9
10
      mov ax,data
mov ds,ax
mov bx,0
mov ax,0
mov cx,8
s: cmp byte ptr [bx],8
jne next ;不等于 8 就跳走,不计数
inc ax ;计数
next: inc bx
loop s

DF 标志和串传送指令

flag 的第 10 位是 DF,方向标志位,Direction Flag。

df = 0,每次操作后 si、di 递增;
df = 1,每次操作后 si、di 递减。

串传送指令 movsb

功能:

  1. ((es)*16+(di)) = ((ds)*16+(si))
  2. 如果 df = 0,则:(si) = (si) + 1(di) = (di) + 1
    如果 df = 1,则:(si) = (si) - 1(di) = (di) - 1

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

也可以传送一个字:

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

一般 movsbmovsw 都和 rep 配合使用。

1
rep movsb

相当于:

1
2
s: movsb
loop s

rep 根据 cx 的值,重复执行后面的串传输指令。实现将 (cx) 个字符从 ds:si 传送到 es:di

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

  1. cld 指令:将标志寄存器的 df 位设置为 0
  2. std 指令:将标志寄存器的 df 位设置为 1

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
assume cs:code,ds:data
data segment
db 'Welcom to masm!'
db 16 dup (0)
data ends

code segment
start:
mov ax,data
mov es,ax
mov ds,ax
mov si,0 ;设置数据源起始位置
mov di,16 ;设置目的地起始位置
mov cx,16 ;设置数据长度
cld
rep movsb
code ends
end start

pushf 和 popf

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

pushfpopf 为直接访问标志寄存器提供了一种方法。

检测点 11.4

1
2
3
4
5
6
7
8
9
mov ax,0
push ax
popf
mov ax,0fff0h ;ax 寄存器补码表示的 -16
add ax,0010h ;作为有符号数 -16 + 16 没有溢出,作为无符号数有进位
pushf
pop ax
and al,11000101B
and ah,00001000B

(ax) = 45h = 69

标志寄存器在 Debug 中的表示

标识 值为 1 的标记 值为 0 的标记
of OV NV
sf NG PL
zf ZR NZ
pf PE PO
cf CY NC
df DN UP

Debug Flag