标志寄存器又称程序状态字(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).

 

分类: 编程