标志寄存器

标志寄存器


这就是计算机中的标志寄存器,说到标志我们就应该想到flag,bool类型。而一个bool类型用1bit就可以表示了,所以我们谈论这个32位的标志寄存器,更多的时候是讨论其中的某一位。
比如0位,2位,4位,都是cpu中设定好的用途,就跟通用寄存器一样,有自己的名字。比如C位,P位,O位。

进位标志位CF(carry flag)

如果运算结果的最高位产生了一个进位或借位,那么,其值为1,否则其值为0。

三个实验:

mov al,0xff
add al,2   // c位变成1
mov al,0xfe // mov不是运算所以C位不变
// C位手动清零
add al,2 // c位又变成1
// C位手动清零
mov ax,0xff
add ax,2  // c位已经是0
// =========================重点
// 标志寄存器在学习过程中,我们一定要明白数据的宽度 
//【mov ax,0xff 】中,数据的宽度是16位,add之后,确实是进位了,但不是最高位的进位

补充:什么是运算?
mov不算是运算,其他的加减乘除异或等都是运算

奇偶标志位:PF(Parity Flag)

PF 反应运算结果中“1”的个数的奇偶性,如果1的个数是偶数,则P位是1,如果1的个数是奇数,则P位是0

实验

MOV AL,3  // 00001011 p位:0
add al,2  // 00001010 P位:1

辅助进位标志AF(Auxiliary Carry Flag)

在发生下列情况时,辅助进位标志AF的值被置为1,否则其值为0:
(1)、在字节操作时,发生低字节向高字节进位或借位时;
(2)、在字节操作时,发生低4位向高4位进位或借位时。
大白话说下,就是运算时,把位划分成两半,低位的一半的第一位如果向高位进位(加)或者借位(减)时,A位就是1
如下图,就看突出的那一位
32位运算: _ _ _ _ - _ _ _
16位运算: _ _ - _
8位运算: _ -

实验:

// 第一组
MOV EAX,0x55EEFFFF
ADD EAX,2    // A位变1
// 第二组
MOV AX,5EFE  
add ax,2  // A位又变1
// 第三组
MOV AL,4E
add al,2 // A位又变1

零标志ZF(Zero Flag)

零标志ZF用来反映运算结果是否为0
如果运算结果为0,则其值为1,否则其值为0。在判断运算结果是否为0时,可使用此标志位。

实验

XOR EAX,EAX  // Z位:1
// 这句话的作用有两个
// 1.将EAX的值变为1
// 2.将标志位Z位置为1。如果[mov eax,0] 的话就没有第二个作用了,mov就不是运算

符号标志SF(Sign Flag)

符号标志SF用来反映运算结果的符号位,它与运算结果的最高位相同。
特别注意:这里的最高位说的是二进制的最高位,所以只能是0或1

MOV AL,7F
ADD AL,2   // 1

溢出标志OF(Overflow Flag)

溢出标志OF用于反映有符号数加减运算所得结果是否溢出。
如果运算结果超过当前运算位数所能表示的范围,则称为溢出,OF的值被置为1,否则,OF的值被清为0。

最高位进位(CF)和溢出(OF)的区别?
这里主要涉及到了无符号整数和有符号整数。计算机在存储的时候,并不知道自己存储的是有符号整数还是无符号整数。只有使用的人才知道,就是程序员喽。
C位是做无符号运算时该关注的
O位是做有符号运算时该关注的

想知道现在计算的结果有没有溢出,是看CF位还是OF位?
我要看那个取决于我做的是有符号还是无符号运算。
如果你知道自己做的是无符号运算,你只用看C位,不用管O位
如果你知道自己的做的是有符号运算,只要看O位,不用管C位

好好看这个图就什么都明白了



正数+负数,一定在圈内,永远不会溢出
正数+正数,如果结果为负数(超过7F内),一定溢出。因为越界了,两个正数相加不应该是负数。

说结果之前,一定要指定到底是无符号运算还是有符号预算。


image.png

自己这个分析判断是错误的,其实只要是圈上的点可以表示的,都没有溢出,圈上的店不能表示的,就是溢出了。
只是有符号的话,只有右边区域表示正数,左边区域表示负数
无符号的话,整个区域都可以表示

和标志寄存器相关的指令

ADC指令:带进位加法

百度百科:
ADC 带进位的加法指令 ADC Reg/Mem, Reg/Mem/Imm 功能,将目的操作数和源操作数相加再加低位进位,结果送入目的的地址 dst+src+cf->dst, 受影响的标志位:AF、CF、OF、PF、SF和ZF,该指令的功能是把源操作数和进位标志位CF的值(0/1)一起加到目的操作数中。

实验

MOV AL,1
MOV CL,2
ADC AL,CL //按照我们正常的ADD指令,此时AL中应该是3
// 但是ADC指令是带进位的加法,它会把c位中的值也给加上。
// 如果此时cf中的值是1,那结果:AL中的值是4,cf的值:0

注意事项:1.两边

SBB指令:带借位减法

百度百科的解释很明确
SBB:带借位减法, 格式:SBB DST,SRC, 执行的操作:(DST)←(DST)-(SRC)-CF,其中CF为进位的值。 SBB Compents :他们组成了Application .他们以何种方式组合是由SLEE来确定的。

实验

MOV AL,3
MOV CL,1
SBB AL,CL 
// 分两种情况:
// 1. 如果cf是0,那么结果和我们预想的一样,2
// 2. 如果cf是1,那么结果还会减1,结果al变为1了

XCHG指令:交换数据

百度百科
交换指令XCHG是两个寄存器,寄存器和内存变量之间内容的交换指令,两个操作数的数据类型要相同,可以是一个字节,也可以是一个字,也可以是双字

说明什么?
我们交换的是两个容器,不能是立即数
而且交换的两个容器不能都是内存

// 实验:
MOV AL,1
MOV CL,3
XCHG AL,CL  // AL变成3,CL变成了1

XCHG DWORD PTR DS:[0X0019FF74],EAX

MOVS指令:移动数据


看了一下通用寄存器中EDI和ESI的分工:

再回顾一下通用寄存器的划分:
https://xuxiaofeng.gitlab.io/2018/12/12/study-%E7%BC%96%E7%A8%8B%E8%BE%BE%E4%BA%BA-%E6%BB%B4%E6%B0%B4%E9%80%86%E5%90%913%E6%9C%9F%E5%AD%A6%E4%B9%A0-2018-12-12-%E8%BF%9B%E4%B8%80%E6%AD%A5%E5%AD%A6%E4%B9%A0%E5%AF%84%E5%AD%98%E5%99%A8/

现在我们来解释MOVS指令

movs指令是用来复制一个数据项(字节,字或双字)从源字符串到目标字符串。原字符串又ESI/SI/DH 指出,目标字符串又EDI/DI/BH指出。(具体又那个指出,我们需要看使用时定义的单位)

这里还有一段解释:
MOVS指令可以把由(SI)指向的数据段中的一个字(或字节)传送到由(DI)指向的附加段中的一个字(或字节)中去,同时根据方向标志(df标志寄存器)及数据格式(字 或字节)对SI和DI进行修改

在执行该指令前,应该先做好以下 准备工作:
1) 把存放于数据段中的源串首地址(如反向传送则应是末地址)放入SI寄存器中;
2) 把将要存放数据串的附加段中的目的串首地址(或反向传送时的末地址)放入DI寄存器中;
3) 建立方向标志。

实验
首先说明一下,我们之前碰到内存的时候,我们总是这样表示DS:[],没错。但是碰到EDI表示内存地址单元时,我们应该这样ES:[],所以往下,我们会经常看到这样的字符:ES:[EDI] (这里涉及到段、页的概念)

 1.直接输入movs,点击确定,编辑器就会自动给我们生成下面的额语句
  充分说明了这个指令是针对EDI和ESI的
MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]  
mov edi,0019ff88
mov esi,0019ff80
MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]  
结果
edi:0019ff8c
esi:0019ff84
并且刚才edi对应内存编号中的值和dsi对应内存编号中的值确实做了交换
我们发现EDI和ESI中的值都+4 了。这是有D位(df标志寄存器决定的)

所以这个MOVS指令有两个主要影响,一是真的交换了寄存器对应内存地址单元中的值,二是根据DF标志寄存器中的值,让ESI和EDI都进行一些位移。

STOS指令:讲Al/AX/EAX的值存储到[EDI]指定的内存单元

到底是复制AL还是AX还是EAX,是又我们指定的内存宽度决定的
。复制完毕后,EDI的变化方向是由我们的D位(df标志寄存器)决定的。

// 实验
mov eax,12345678
mov edi,0019ff88
直接数据stos,确定酒水弹出下面的值
STOS DWORD PTR ES:[EDI]
运行结果(D位此时是0)
DEI:0019FF8C
内存单元0019FF88中的值:12345678

REP指令:按计数寄存器 (ECX) 中指定的次数重复执行字符串指令

这里有一个把movs和rep指令糅合在一起的解释

MOVS指令可以把由(SI)指向的数据段中的一个字(或字节)传送到由(DI)指向的附加段中的一个字(或字节)中去,同时根据方向标志及数据格式(字 或字节)对SI和DI进行修改。当该指令与前缀REP联用时,则可将数据段中的整串数据传送到附加段中去。这里源串必须在数据段中,目的串必须在附加段 中,但源串允许使用段跨越前缀来修改。在与REP联用时还必须先把数据串的长度送到CX寄存器中,以便控制指令结束。因此在执行该指令前,应该先做好以下 准备工作:
1) 把存放于数据段中的源串首地址(如反向传送则应是末地址)放入SI寄存器中;
2) 把将要存放数据串的附加段中的目的串首地址(或反向传送时的末地址)放入DI寄存器中;
3) 把数据串长度放入CX寄存器;
4) 建立方向标志。
在完成这些准备工作后就可使用串指令传送信息了

MOV ECX,10
REP MOVSD
REP STOSD

你可能感兴趣的:(标志寄存器)