标志寄存器(flag)
CPU内部的寄存器中,有一种特殊的寄存器(对于不同的处理机,个数和结构都有可能不同)具有以下3种作用。
1)用来存储相关指令的某些执行结果。
2)用来为CPU执行相关指令提供行为依据。
3)用来控制CPU的相关工作方式。
这种特殊的寄存器在8086CPU中,被称为标志寄存器。8086的标志寄存器有16位,其中状态的信息通常被称为程序状态字(PSW)。
标志位寄存器简称flag。
flag和其他寄存器不一样,其他寄存器都是用来存放数据的,都是整个寄存器具有一个含义。而flag寄存器是按位起作用的,也就是说,它的每一位都用专门的含义,记录特定的信息。
8086CPU的flag寄存器的结构:
15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00
OF DF IF TF SF ZF AF PF CF
ZF标志(flag第6位,0标志位)
flag的第6位是ZF位, 零标志位。它记录相关指令执行后,其结果是否为0。如果结果为0,那么zf = 1,如果结果不为0,那么zf = 0。
例:
mov ax, 1
sub ax, 1
执行后,结果为0,则zf = 1。
mov ax, 2
sub ax, 1
执行后,结果为1,则zf = 0。
mov ax, 1
and ax, 0
执行后,结果为0,则zf = 1,表示“结果是0”。
mov ax, 1
or ax, 0
执行后,结果不为0,则zf = 0,表示“结果不是0”。
在8086CPU的指令集中,有的指令的执行是影响标志寄存器的,比如,add、sub、mul、div、inc、or、and等,它们大都是运算指令(进行逻辑或算术运算);
有的指令的执行对标志寄存器没有影响,比如,mov、push、pop等,它们大都是传送指令。
PF标志(flag第2位,奇偶标志位)
flag的第2位是PF,奇偶标志位。他记录相关指令执行后,其结果的所有bit位中1的个数是否为偶数。如果1是偶数,pf = 1,如果为奇数,那么pf = 0。
例:
mov al, 1
add al, 10
执行后,结果为00001011B,其中有3个1,则pf = 0;
mov al, 1
or al, 2
执行后,结果为00000011B,其中有2个1,则pf = 1;
mov al, 1
sub al, al
执行后,结果为00000000B,其中有0个1,则pf = 1;
SF标志(flag第7位,记录结果是否为负)
flag的第7位是SF,符号标志位。它记录相关指令执行后,其结果是否为负。如果结果为负,sf = 1;如果非负,sf = 0。
计算机中通常用补码来表示有符号数据。计算机中的一个数据可以看作是有符数,也可以看成是无符号数。比如:
00000001B 可以看作为无符号数1,或有符号数 +1
10000001B 可以看作为无符号数129,也可以看作有符号数 -127
对于同一个二进制数据,计算机可以将它当作无符号数据来运算,也可以当作有符号数据来运算。
例:
mov al, 10000001B
add al, 1
结果:(al)= 10000010B
将add指令进行的运算当作无符号数的运算,那么add指令相当于计算129 + 1 = 130(10000010B)
将add指令进行的运算当作有符号数的运算,那么add指令相当于计算-127 + 1 = -126(10000010B)
CPU在执行add等指令的时候,就已经包含了两种含义,也将得到同一种信息来记录的两种结果,关键在于我们的程序需要哪一种结果。
SF标志,就是CPU对有符号数运算结果的一种记录,他记录数据的正负。
当我们将数据当作有符号数来运算的时候,可以通过他来得知结果的正负。
当我们将数据当作无符号数来运算,SF的值则没有意义,虽然相关的指令会改变SF的值。
CPU在执行add等指令时,是必然要影响到SF标志位的值的。
例:
mov al, 10000001B
add al, 1
执行后,结果为10000010B,sf = 1表示:如果指令进行的是有符号数运算,那么结果为负。
mov al, 10000001B
add al, 01111111B
执行后,结果为0, sf = 0表示:如果指令进行的是有符号数运算,那么结果为非负。
某些指令将影响标志寄存器中的多个标记位,这些被影响的标记位比较全面地记录了指令地执行结果,为相关地处理提供了所需的依据。
例
sub al,al
ZF: 1 , PF = 1 , SF = 0
CF标志(flag的第0位,进位、借位标志位,对无符号数有意义)
flag的第0位是CF,进位、借位标志位。
在进行无符号数运算的时候,它记录了运算结果的最高有效位向更高位的进位值,或从更高位的借位值。
对于位数为N的无符号数来说,其对应的二进制信息的最高为,即第N-1位,就是他个最高有效位,而假象存在的第N位,就是相对于最高有效位的更高位。
当两个数据相加的时候,有可能产生从最高有效位向更高位的进位。例如,两个8位数据:98H + 98H,将产生进位。由于这个进位值在8位数中无法保存,我们会丢弃这个进位值。其实CPU在运算的时候,并不丢弃这个进位值,而是记录在一个特殊的寄存器的某一位上。8086CPU就用CF位来记录这个进位值。
例:
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位也可以用来记录这个借位值。
例:
mov al, 97H
sub al, 98H //执行后:(al)= FFH, CF = 1,CF记录了向更高位的借位值
sub al, al //执行后:(al)= 0H, CF = 0,CF记录了向更高位的借位值
OF标志位(第11位,溢出标志位,对有符号数有意义)
在进行有符号数运算的时候,如结果超出了机器所能表示的范围称为溢出。
对于8位的有符号数据,机器所能表示的范围就是-128 ~ 127。
对于16位的有符号数据,机器所能表示的范围就是-32768 ~ 32767
如果运算结果超出了机器所能表达的范围,将产生溢出。
例:
mov al, 98
add al, 99
(al) = (al) + 99 = 99 + 98 = 197
执行后将产生溢出。
mov al, 0F0H //F0H,为有符号数-16的补码
add al, 088H //088H,为有符号数-120的补码
(al) = (al) + (-120) = -136
执行后将产生溢出。
由于在进行有符号数运算时,可能发生溢出而造成结果的错误。则CPU需要对指令执行后是否产生溢出进行记录。
flag的第11位是OF,溢出标记位。一般情况下,OF记录了有符号数运算的结果是否发生了溢出。如果发生溢出,OF = 1;如果没有,OF = 0;
CF和OF的区别:
CF是对无符号数运算有意义的标志位。
OF是对有符号数运算有意义的标志位。
CF和OF所表示的进位和溢出,分别是对无符号数和有符号数运算而言的,它们之间没有任何关系。
DF标志(flag的第10位是DF,方向标志位)和串传送指令(movsb) 和 rep
flag的第10位是DF,方向标志位。在串处理指令中,控制每次操作后si、di的增减。
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
用汇编描述:
mov es:[di], byte ptr ds:[si] //8086并不支持,这里只是描述
如果 df = 0:
inc si
inc di
如果 df = 1:
dec si
dec di
movsb的功能是将 ds:si 指向的内存单元中的字节送入 es:di中,然后根据标志寄存器df位的值,将si和di递增或递减。
格式:movsw
movsw 的功能是将ds:si指向的内存字单元中的字送入es:di中,然后根据标志寄存器df位的值,将si和di递增2或递减2。
用汇编描述
mov es:[di], word ptr ds:[si] //8086并不支持,这里只是描述
如果 df = 0:
add si, 2
add di, 2
如果 df = 1:
sub si, 2
sub di, 2
movsb 和 movsw进行的是串传送操作中的一个步骤,一般来说,movsb 和 movsw都和 rep 配合使用,格式如下:
rep movsb
用汇编描述:
s: movsb
loop s
rep的作用是根据 cx 的值,重复执行后面的串传送指令,由于每执行一次movsb指令si和di都会递增或递减指向后一个单元或前一个单元,则rep movsb就可以循环实现(cx)个字符的传送。
同理:rep movsw
相当于:
s: movsw
loop s
8086CPU提供了2条指令对df位进行设置:
cld 指令:将标志寄存器的 df 位置0
std 指令:将标志寄存器的 df 位值1
例:用传送指令,将data段中的第一个字符串复制到它后面的空间中。
assume cs:code, ds:data
data segment
db 'Welcome to masm!'
db 16 dup (0)
data ends
code segment
start: mov ax, data
mov ds, ax
mov si, 0
mov ax, 0010H
mov es, ax
mov di, 0
mov cx, 16
cld //设置df位 0 -> 递增
rep movsb
code ends
end start
例:用传送指令,将F000H段中的最后16个字符复制到data段中。//逆向传送
assume cs:code, ds:data
data segment
db 16 dup (0)
data ends
code segment
start: mov ax, 0F000H
mov ds, ax
mov si, 0FFFFH //ds: 0F000H:0FFFFH
mov ax, data
mov es, ax
mov di, 000EH
mov cx, 16
std //设置df位 1 -> 递减
rep movsb
code ends
end start
pushf 和 popf
pushf的功能是将标志寄存器的值压栈,而popf是从栈中弹出数据,送入标志寄存器中。
pushf 和 popf,为直接访问标志寄存器提供了一种方法。
标志寄存器在Debug中的表示
AX = 0000 BX = 0000 CX = 0000 DX = 0000 SP = FFEE BP = 0000 SI = 0000 DI = 0000
DS = **** ES = **** SS = **** CS = **** IP = **** NV UP EI PL NZ NA PO NC
↑ ↑ ↑ ↑ ↑ ↑
OF DF SF ZF PF CF
Debug对我们已知的标志位的表示
标志 值为1的标记 值为0的标记
of(溢出标志位) OV NV
sf(负标志位) NG PL
zf(0标志位) ZR NZ
pf(奇偶标志位) PE PO
cf(进、借标志位)CY NC
df(方向标志位) DN UP
将包含任意字符,以0结尾的字符串中的小写字符转变成大写字母
assume cs:codesg
datasg segment
db "Beginner's All-purpose Symbolic Instruction Code.",0
datasg ends
codesg segment
begin: mov ax, datasg
mov ds, ax
mov si, 0
call letterc
mov ax, 4c00H
int 21H
letterc: mov al, ds:[si]
mov cl, al
mov ch, 0
jcxz return //判断为零返回 and al, 11011111H
cmp ax, 63
jb s0 //小于跳转
cmp ax, 88
ja s0
add al, 11011111H
mov ds:[si], al
s0: inc si
jmp short letterc
return: ret
codesg ends
end begin