标志寄存器
EFLAGS寄存器
进位标志CF(Carry Flag)
如果运算的最高位产生了一个进位或借位,那么,其值位1,否则其值为0
mov AL,0FF
ADD AL,1
0X80+0X40
0X80:0 1000 0000
0X40:0 0100 0000
结果为:1100 0000 最高位并没有发生变化,于是CF位为0
0x80-0x40
1000 0000
0100 0000
注意这里产生了借位1000 0000中的加黑部分
而非0 1000 0000这里的最高位
结果为:0100 0000 最高位并没有发生变化,于是CF位0
0x80-0x81
0x80: 1000 0000
0x81: 1000 0001
结果为:1111 1111=-1,最高位被借位,于是CF位为1
奇偶标志PF(Parity Flag)
奇偶标志PF用于反映运算结果中最低有效字节中"1"的个数的奇偶性
如果"1"的个数为偶数,则PF的值为1,否则其值为0
指令 | 指令执行后的AL的结果 | PF |
---|---|---|
MOV AL,3 | 0011 | 1 |
ADD AL,3 | 0110 | 1 |
ADD AL,2 | 1000 | 0 |
例: |
MOV AX,803
ADD AX,1
0x803:0000 1000 0000 0011
执行结果:
0x804:0000 1000 0000 0100 总共2个1,PF应为1,但实际运行结果PF为0
因为PF是根据最低有效字节来看,即804后面04的这部分
04: 0000 0100总共1个1,所以PF为0
辅助进位标志AF(Auxiliary Carry Flag)
在发生以下情况时,辅助进位标志AF的值被置为1,否则其值为0:
- 在字操作时,发生低字节向高字节进位或借位时
- 在字节操作时,发生低4位向高4位进位或借位时
AF与数据宽度相关
32位时 FFFF F FFF
16位时 FF F F
8位 F F
加黑的字体位AF标志位判断的位置,如果该位置要向前进位则AF为1,否则为0,和CF相似,不过判断的位置不同
32位例:
MOV EAX,55EEFFFF
ADD EAX,2
16位例:
MOV AX,5EFE
ADD AX,2
8位
MOV AL,4E
ADD AL,2
零标志ZF(Zero Flag)
零标志ZF用来反映运算结果是否为0
如果运算结果为0,则其值为1,否则其值为0
作用:在判断运算结果是否为0时,可以使用此标志位。
例子:
XOR EAX,EAX
通过xor将eax清零,会改变zf标志位为1
MOV EAX,0
通过MOV将EAX赋值为0,非运算,不改变zf标志位
符号标志SF(Sign Flag)
符号标志SF用来反映运算结果的符号位,它与运算结果的最高位相同
例子:
MOV AL,7F
ADD AL,2
溢出标志OF(Overflow Flag)
溢出标志OF用于反映有符号数加减运算所得结果是否溢出
注意与CF区分
最高位进位与溢出得区别:
进位标志表示无符号数运算结果是否超出范围。
溢出标志表示有符号数运算结果是否超出范围。
溢出主要是给有符号数使用的,在有符号号的运算中,有如下的规律:
-
正+正 = 正 如果结果是负数,则说明有溢出。
-
负 + 负 = 负 如果结果是正数,则说明有溢出。
-
正 + 负 永远不会有溢出。
无符号、有符号都不溢出例
MOV AL,8
ADD AL,8
AL的数据宽度为8,即
无符号数范围为0FF即0255
8 + 8 = 16在0~255内 不溢出
有符号数的范围为:
正数:0~7F 即0~127
负数:80~FF 即-128~0
8+8=16 在0~127内 两个正数相加的结果仍然为正数,不溢出。
无符号溢出,有符号不溢出例
MOV AL,0FF
ADD AL,2
无符号数时
FF+2 = 255+2 = 257 在0~255外,溢出
有符号数时
FF+2 =-1+2 = 1
正 +负 永远不会溢出。
无符号不溢出,有符号溢出例
MOV AL,7F
ADD AL,2
无符号数时
7F+2=127+2=129 在0~255内 不溢出
有符号数时
7F+2=0X81在80~FF(负数范围)内,两正数相加结果为负数,溢出
无符号、有符号都溢出
MOV AL,0FE
ADD AL,80
无符号数时
FE+2=254+2=256=0X100 在0~255外溢出
有符号数时
FE+2=0X100在0~FF外,溢出
CPU如何计算OF位
首先引入两个概念:
- 符号位有进位
- 最高有效数值位向符号位产生的进位
对于一个有符号数:如0X80和0XC0
符号位有进位
0x80:1 000 0000
0XC0: 1100 0000
最高有效数值位向符号位产生的进位
0X80: 1 0 00 0000
0Xc0:1 1 00 0000
接下来看一组汇编指令
MOV AL,80
ADD AL,0C0
就是运算0x80+0xc0
0x80: 1 0 00 0000
0xc0: 1 1 00 0000
符号位1+1有产生进位,于是符号位有进位为1
最高有效数值位向符号位产生的进位0+1没有产生进位,于是最高有效数值为向符号位产生的进位为0
OF = 符号位有进位xor最高有效数值位向符号位产生的进位
OF = 1 xor 0 = 1 所以此时OF=1
方向标志DF(Direction Flag)
DF:方向标志位
cld指令 清除DF标志位,结果为0 std指令设置DF标志位DF,结果为1,
DF = 1时串操作作为减 地址方式 DF = 0 为增地址方式
符号 | 含义 |
---|---|
r | 寄存器 |
m | 内存 |
imm | 立即数 |
r8 | 8位通用寄存器 |
m8 | 8位内存 |
imm8 | 8位立即数 |
ADC指令:带进位加法
格式:ADC R/M,R/M,R/M//IMM两边不能同时为内存,数据宽度要一样
MOV AL,1
MOV CL,2
手动修改为CF为1
ADC AL,CL
计算结果为4,原本1+2=3,但是现在变成了4,注意与ADD的区别就在于进位
SBB指令:带错位减法
格式:SBB R/M,R/M/IMM两边不能为内存数据宽度要一样
MOV AL,4
MOV CL,2
手动修改CF为1
SBB AL,CL
计算结果为1,原本4-2=2,但是现在变成了1,注意与SUB的区别就在于进位
XCHG指令:交换数据
格式:XCHG R/M,R/M两边不能同时为内存,数据宽度要一样
XCHG AL,CL
XCHG DWORD PTR DS:[12FFC4],EAX
XCHG DWORD PTR DS:[12FFC4],AL
例
MOV AL,1
MOV CL,2
XCHG AL,CL
执行前:AL=1 CL=2
执行后:AL=2 CL=1
MOVS指令:移动数据 内存-内存
BYTE/WORD/DWORD
MOVS指令常用与复制字符串
MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[EST] 简写成MOVSB
MOVS WORD PTR ES:[EDI],WORD PTR DS:[ESI] 简写成MOVSW
MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI] 简写为:MOVSD
例
MOV EDI,12FFD8
MOV ESI,12FFD0
MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]
执行后,EDI内存里的值被修改为ESI内存里的值,且EDI和ESI各加4
为什么各加4?
和DWORD数据宽度相关,如果为WORD则各加2
为什么执行完是加而不是减?
由DF(Direction Flag)方向标志位决定,当DF位为1时为减,当DF位为0时,则为加
STOS指令
将AL/AX/EAX的值存储到[EDI]指定的内存单元,和数据宽度相关
STOS BYTE PTR ES:[EDI] 将AL存储到[EDI]
STOS WORD PTR ES:[EDI] 将AX存储到[EDI]
STOS DWORD PTR ES:[EDI] 将EAX存储到[EDI]
注意这里使用的是ES:之前写的都是DS:
当后面为[EDI]时要使用ES: 这和后面学的段寄存器相关,先记住
存储完数据后EDI地址的变化也受DF标志控制,1减 0增
REP指令
按计数寄存器(ECX)中指定的次数重复执行指令
MOV ECX,10
REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI] 也可以写成 REP MOVSD
这里的10为十六进制,也就是0x10=16
代码将会重复执行16次,并且不会往同一个地方覆盖,因为每执行一次EDI和ESI都会变化,变化方向由DF决定。