标志寄存器又称程序状态字(PSW)
CPU内部的寄存器中,有一种特殊的寄存器(对于不同的处理机,个数和结构都可能不同)具有以下三种作用:
- 用于存储相关指令的某些执行结果
- 用来为CPU执行相关指令提供行为依据
- 用来控制CPU的相关工作方式
这种特殊的寄存器在8086CPU中,称为标志寄存器。8086CPU的标志寄存器有16位,其中存储的信息通常被称为程序状态字(PSW).标志寄存器和其他寄存器不一样,其它寄存器是用来存放数据的,都是整个寄存器具有一个含义.而标志寄存器是按位起作用的,也就是说,它的每一位都有专门的含义,记录特定的信息.
8086CPU的标志寄存器如下图所示:
一、ZF标志位
标志寄存器的第6位是ZF(Zero Flag),零标志位。它记录相关指令执行后,其结果是否为0.如果结果为0,那么ZF=1(ZR/Zero);如果结果不为0,那么ZF=0(NZ/Not Zero).
例如如下指令执行后ZF标志位为1(实验在DEBUG中进行,字面量默认为16进制):
0AE0:0100 mov ax,1
0AE0:0103 sub ax,1
例如如下指令执行后ZF标志位为(实验在DEBUG中进行,字面量默认为16进制)0:
0AE0:0100 mov ax,2
0AE0:0103 sub ax,1
例如如下指令执行后ZF标志位为1(实验在DEBUG中进行,字面量默认为16进制):
0AE0:0100 mov ax,ffff
0AE0:0103 inc ax
特别注意:mov push,pop等传送指令不改变标志寄存器的值.
二、PF标志位
标志寄存器的第2位是PF(Parity Flag),奇偶标志位,它记录相关指令执行后,其结果的所有bit位中1的个数是否为偶数.如果1的个数为偶数,PF=1(PE/Parity Even);如果为奇数,PF =0(PO/Parity Odd)
例如如下指令执行后PF标志位为1(实验在DEBUG中进行,字面量默认为16进制):
0AE0:010C mov ax,1
0AE0:010F add ax,2
例如如下指令执行后PF标志位为0(实验在DEBUG中进行,字面量默认为16进制):
0AE0:0100 mov ax,1
0AE0:0103 inc ax
三、SF标志位
标志寄存器的第7位是SF(Sign Flag),符号标志位.它记录相关指令执行后,其结果是否为负.如果结果为负,SF=1(NG/Negative);如果非负,SF=0(PL/Positive).
例如如下指令执行后SF标志位为0(实验在DEBUG中进行,字面量默认为16进制):
0AE0:0100 mov ax,0
0AE0:0103 add ax,1
例如如下指令执行后SF标志位为1(实验在DEBUG中进行,字面量默认为16进制):
0AE0:0100 mov ax,0
0AE0:0103 add al,ff
四、CF标志位
标志寄存器的第0位是CF(Carry Flag),进位标志位.一般情况下,在进行无符号数运算的时候,它记录了运算结果的最高有效位向更高位的进位值,或从更高位的借位值.若发生借位或进位CF=1(CY/Carry),否则CF=0(NC/No Carry)
例如如下指令执行后CF标志位为0(实验在DEBUG中进行,字面量默认为16进制):
0AE0:0106 mov ax,1
0AE0:0109 add ax,f
例如如下指令执行后CF标志位为1(实验在DEBUG中进行,字面量默认为16进制):
0AE0:0100 mov ax,1
0AE0:0103 add ax,ffff
注意:inc和loop指令不影响CF标志位
五、OF标志位
标志寄存器的第11位是OF(Overflow Flag),溢出标志位,一般情况下,OF记录了有符号数的运算结果是否发生了溢出,如果发生了溢出,OF=1(OV/Overflow);如果没有则OF=0(NV/Not Overflow)
注意OF标志位和CF标志位的区别,例如如下指令执行后OF标志位为1,CF标志位为0(这里的字面量是10进制数):
mov al,98
add al,99
例如如下指令执行后OF标志位为0,CF标志位为1(实验在DEBUG中进行,字面量默认为16进制):
0AE0:0104 mov al,f0
0AE0:0106 add al,78
使用neg指令也会造成溢出:
mov al,-128
neg al
可以看出OF标志位和CF标志位没有任何关系
六、ADC指令
ADC是带进位加法指令,它利用了CF位上记录的进位值,比如指令,adc ax,cx相当于ax=ax+cx+CF
下面实现一个64位整数相加的函数(这里用到32位寄存器,C++与汇编混用),函数原型如下:
extern "C"
// 将a+b的值放在c所在的地址中
void addLong(long long a, long long b,long long *c);
汇编实现如下(32位寄存器与16位寄存器用法):
;.386
.model flat,c
;public test_
.code
addLong proc
push ebp
push ebx
mov ebp,esp
; 12 20 28
mov ebx,[ebp+28]
mov eax,[ebp+12]
mov dword ptr [ebx],eax
mov eax,[ebp+20]
add dword ptr [ebx],eax
mov eax,[ebp+24]
mov dword ptr [ebx+4],eax
mov eax,[ebp+16]
adc dword ptr [ebx+4],eax
pop ebx
pop ebp
ret
addLong endp
end
主函数如下(这里要声明函数,并配置Visual Studio对asm文件的编译方式):
#include "iostream"
using namespace std;
extern "C"
// 将a+b的值放在c所在的地址中
void addLong(long long a, long long b,long long *c);
int main()
{
long long a = INT32_MAX;
long long b = INT32_MAX;
long long c = 0;
addLong(a, b, &c);
cout << c << endl;
system("pause");
return 0;
}
输出结果:
4294967294
七、SBB指令
ADC是带借位减法指令,它利用了CF位上记录的借位值,比如指令,sbb ax,cx相当于ax=ax-cx-CF
八、CMP指令
CMP是比较指令,CMP的功能相当于减法指令,只是不保存结果.CMP指令执行后,将对标志寄存器产生影响.
比如下面的指令:
mov ax,8
mov bx,3
cmp ax,bx
执行后AX=8,ZF=0,PF=1,SF=0,CF=0,OF=0
其实我们通过CMP指令执行后相关标志位的值就可以看出比较结果.对于无符号数,cmp ax,bx:
- 如果AX=BX,则AX-BX=0,所以ZF=1(条件转移指令:JE/JZ)
- 如果AX≠BX,则AX-BX≠0,所以ZF=0(条件转移指令:JNE/JNZ)
- 如果AX<BX,则AX-BX将产生借位,所以CF=1(条件转移指令:JB/JNAE)
- 如果AX≥BX,则AX-BX不必产生借位,所以CF=0(条件转移指令:JNB/JAE)
- 如果AX>BX,则AX-BX既不必产生借位,结果又不为0,所以CF=0并且ZF=0(条件转移指令:JA/JNBE)
- 如果AX≤BX,则AX-BX将产生借位,结果可能为0,所以CF=1或者ZF=0(条件转移指令:JNA/JBE)
对于有符号数cmp ah,bh:
- 如果SF=1,OF=0,所以AH<BH(JL/JNGE)
- 如果SF=1,OF=1,因为溢出导致的实际结果为负,那么逻辑上真正的结果为正,所以AH>BH(JG/JNLE)
- 如果SF=0,OF=1,因为溢出导致的实际结果为正,那么逻辑上真正的结果为负,所以AH<BH(JL/JNGE)
- 如果SF=0,OF=0,所以AH≥BH(JGE/JNL)
九、DF标志和串传送指令
标志寄存器的第10位是DF,方向标志位.在串处理指令中,控制每次操作后SI和DI的增减.DF=0(UP),每次操作后SI和DI递增;DF=1(DN/Down)每次操作后SI和DI递减.
下面我们来看两个串传送指令movsb,执行该指令后相当于进行下面几个步骤(DF=0):
mov byte ptr es:[di],ds:[si]
inc si
inc di
movsw,执行该指令后相当于进行下面几个步骤(DF=0):
mov word ptr es:[di],ds:[si]
inc si
inc si
inc di
inc di
movsb和movsw一般都和rep配合使用,rep movsb等价于下面指令:
s:movsb
loop s
CLD(Clear Director),STD(Set Director)指令分别是设置DF=0和DF=1
下面实现一个类似于C语言中的memcpy函数作为例子:
;.386
.model flat,c
;public test_
.code
memoryCopy proc
; 在80386保护模式中ES和DS具有相同的值且无法改变它们
push ebp
push ecx
push esi
push edi
mov ebp,esp
mov edi,[esp+20]
mov esi,[esp+24]
mov ecx,[esp+28]
cld
rep movsb
pop esi
pop edi
pop ecx
pop ebp
ret
memoryCopy endp
end
#include "iostream"
using namespace std;
extern "C"
void memoryCopy(void *dst, void *src, size_t size);
int main()
{
int a = 10, b = 20;
memoryCopy(&a,&b,sizeof(int));
cout << a << endl;
system("pause");
return 0;
}
注意:在80386保护模式中ES和DS具有相同的值且无法改变它们
十、PUSHF和POPF指令
PUSHF指令的功能是将标志寄存器的值压栈,POPF的指令是从栈中弹出数据,送入标志寄存器中.
十一、AF标志位
辅助进位标志位(Auxiliary Carry Flag)。当执行一个加法(或减法)运算,使结果的低4位向高4位有进位(或借位)时,AF=1(AC/Auxiliary Carry);否则AF=0(NA/No Auxiliary Carry).