同add、sub指令一样,cpu在执行cmp指令的时候,也包含两种含义:进行无符号数运算和进行有符号数运算。
所以利用cmp指令可以对无符号数进行比较,也可以对有符号数进行比较。
下面我们来看以下如果用cmp来进行有符号数比较时,cpu用哪些标志位对比较结果进行记录。
我们以cmp ah,bh为例进行说明
cmp ah,bh
如果ah=bh,则ah-bh=0,所以:zf=1
如果ah≠bh,则ah-bh≠0,所以zf=0;
所以,我们根据cmp指令执行后zf的值,就可以知道两个数据是否相等。
对于有符号数运算,在ah<bh情况下,ah-bh显然可能引起sf=1,即结果为负。
比如,
ah=1,bh=2;
即ah-bh=0ffH,0FFH为-1的补码,因为结果为负,所以sf=1.
ah=0FEH,bx=)FFH:
则ah-bh=2-(-1)=0FFH,因为结果为负,所以SF=1.
通过上面的例子,我们是不是可以得到这样的结论:
cmp 操作对象1,操作对象2
指令执行后,SF=1,就说明操作对象1<操作对象2?
当然不是。
再看两个例子
ah=22H,bh=0a0H
则ah-bh=34-(-96)=82H,82H是-126的补码,所以SF=1.
这里虽然SF=1,但是并不能说明ah<bh
因为显然34>-96
两个有符号数A和B相减,得到的是负数,那么可以肯定A<B,这个思路没有错误。
关键在于我们根据什么来断定得到的是一个负数。
cpu将cmp指令得到的结果记录在flag的相关标志位中。
我们可以根据指令执行后,相关标志位的值来判断比较的结果。
单纯的考察SF的值不可能知道结果的正负。因为SF记录的只是可以在计算机中存放的相应位数的结果的正负。
比如,add ah,al执行后,SF记录的是ah中的8位二进制信息所表示的数据的正负。
cmp ah,bh执行后,SF记录的是ah-bh所得到的8位结果数据的正负。
虽然这个结果没有在我们能够使用的寄存器或内存单元中保存,但是在指令执行的过程中,它暂存在cpu内部的暂存器中。
所得到的相应结果的正负,并不能说明,运算所应该得到的结果的正负。这是因为在运算的过程中能够可能发生溢出。
比如:
mov ah,22H
mov bh,0a0h
sub ah,bh
结果SF=1,运算实际得到的结果是ah=82h,但是在逻辑上,运算所应该得到的结果是:34-(-96)=130
就是因为130这个结果作为一个有符号数超出了-128~127这个范围,在ah中不能表示,而ah中的结果不能表示,而ah中的结果被CPU当作有符号数解释为-126.
而SF被用来记录这个实际结果的正负,所以SF=1.
但SF=1不能说明在逻辑上运算所得的正确结果的正负。
我们应该在考察SF(得知实际结果的正负)的同时考察OF(得知有没有溢出),就可以得知逻辑上真正结果的正负,同时就可以知道比较的结果。
下面,我们以cmp ah,bh为例,总结一下cpu执行cmp指令后,SF和OF的值是如何来说明比较的结果的。
1)如果SF=1,而OF=0
OF=0,说明没有溢出,逻辑上真正结果的正负=实际结果的正负
因SF=1,实际结果为负,所以逻辑上真正的结果为负。
所以ah<bh
2)如果SF=1,而OF=1.
OF=1,说明有溢出 ,逻辑上真正结果的正负≠实际结果的正负
因SF=1,实际结果为负,而又有溢出,这说明是由于溢出导致了实际结果为负。
那么逻辑上的结果必然为正。
11.9 检测比较结果的条件转移指令
转移指的是它能够修改IP,而条件指的是它可以根据某种条件,决定是否修改IP。
比如:jcxz就是一个条件转移指令,它可以检测cx中的数值,如果cx=0,就修改ip,否则什么也不做。
所有条件转移指令的转移位移都是【-128,127】
除了jcxz之外,cpu还提供了其他条件转移指令,大多数条件转移指令都检测标志寄存器的相关标志位,根据检测结果来决定是否修改ip。
他们检测的都是被cmp指令影响的那些,表示比较结果的标志位。
这些条件转移指令通常都和cmp相配合使用,就好像call和ret指令通常相配合使用一样。
因为cmp指令可以同时进行两种比较,无符号数比较和有符号数比较,所以根据cmp指令的比较结果进行转移的指令也分为两种,即:
根据无符号数的比较结果进行转移的条件转移指令,他们检测ZF、CF的值。
和根据有符号数的比较结果进行转移的条件转移指令,他们检测SF、OF和ZF的值。
我们看一下根据无符号数的比较结果进行转移的条件转移指令。
指令 含义 检测的相关标志位
je 等于则转移 ZF=1、
jne 不等于则转移 ZF=0
jb 低于则转移 CF=1
jnb 不低于则转移 CF=0
ja 高于则转移 CF=0,ZF=0
jna 不高于则转移 CF=1或ZF=1
这些指令比较常用,他们都很好记忆,他们的第一个字母都是j,表示jump,后面的:
e:表示equal
ne:表示not equal
b:表示below
nb:表示not below
a:表示above
na:表示not above
jne、jn、jnb、ja、jna等指令和cmp指令配合使用的思想和je相同。
上面讲解了根据无符号数的比较结果进行转移的条件转移指令。
根据有符号数的比较结果进行转移的条件转移指令的工作原理和无符号的相同,只是检测了不同的标志位。
11.10 DF标志和串传送指令
flag的第10位是DF,方向标志位。
在串处理指令中,控制每次操作后si,di的增减
df=0,每次操作后si,di递增
df=1,每次操作后si,di递减
格式一:
movsb
功能:(以字节为单位传送)
1)es x16+di=ds x 16+si
2)如果df=0 则:si=si+1
di=di+1
如果df=1 则:si=si-1
di=di-1
可以看出,movsb的功能是将ds:si指向的内存单元中的字节送入es:di中,然后根据标志寄存器df位的值,将si和di递增或递减
当然,也可以传送一个字,
格式2:movsw
功能:(以字为单位传送)
将ds:si指向的内存字单元中word送入es:di中,然后根据标志寄存器df位的值,将si和di递增2或递减2
movsb和movsw进行的是串传送操作中的一个步骤,一般来说,movsb和movsw都和rep配合使用,格式如下:
rep movsb
用汇编语法来描述rep movsb的功能就是:
s:movsb
loop s
rep movsw
用汇编语法来描述rep movsw 的功能就是:
s:movsw
loop s
可见,rep的作用是根据cx的值,重复执行后面的串传送指令。
由于每执行一次movsb指令si和di都会递增或递减指向后一个单元或前个单元,则rep movsb就可以循环实现(cx)个字符的传送。
由于flag的df位决定着串传送指令执行后,si和di改变的方向,
所以cpu应该提供相应的指令来对df位进行设置,从而使程序员能够决定传送的方向。
8086cpu提供下面两条指令对df位进行设置:
cls指令:将标志寄存器的df位置0
std指令:将标志寄存器的df位置1
11.11 pushf和popf
pushf:将标志寄存器的值压栈
popf:从栈中弹出数据,送入标志寄存器中
pushf和popf,为直接访问标志寄存器提供了一种方法
11.12 标志寄存器在的debug中的表示
在debug中,标志寄存器是按照有意义的各个标志位单独表示的。
在debug中,我们可以看到下面的信息