汇编语言标志寄存器cmp 指令

  cmp 指令格式:cmp 操作对象1,操作对象2
  功能:计算 操作对象 1 - 操作对象 2 但并不保存结果,仅仅根据计算结果对标志寄存器进行设置。
  比如,指令 cmp ax,ax,做 (ax)-(ax) 的运算,结果为 0,但并不在 ax 中保存,仅影响 flag 的相关各位。指令执行后:zf=1,pf=1,sf=0,cf=0,of=0。
下面的指令

 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;
  如果 (ax)≠(bx) 则 (ax)-(bx)≠0,所以:zf=0;
  如果 (ax)<(bx) 则 (ax)-(bx) 将产生借位,所以:cf=1,zf=0;   如果 (ax)≥(bx) 则 (ax)-(bx) 不必借位,所以:cf=0;   如果 (ax)>(bx) 则 (ax)-(bx) 既不必借位,结果又不为 0,所以:cf=0 并且 zf=0;
  如果 (ax)≤(bx) 则 (ax)-(bx) 既可能借位,结果可能为 0,所以:cf=1 或 zf=1。

  现在我们可以看出比较指令的设计思路,即:通过做减法运算,影响标志寄存器,标志寄存器的相关位记录了比较的结果。

  反过来看上面的例子。

  指令 cmp ax,bx 的逻辑含义是比较 ax 和 bx 中的值,如果执行后:

  zf=1,说明 (ax)=(bx)
  zf=0,说明 (ax)≠(bx)
  cf=1,说明 (ax)<(bx)   cf=0,说明 (ax)≥(bx)   cf=0 并且 zf=0,说明 (ax)>(bx)
  cf=1 或 zf=1,说明 (ax)≤(bx)

  同 add、sub 指令一样,CPU 在执行 cmp 指令的时候,也包含两种含义:进行无符号数运算和进行有符号数运算。所以利用 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)情况下,(ah)-(bh)显然可能引起sf=l,即结果为负。 比如:
(ah)=1, (bh)=2;则(ah)-(bh)=OFFH, OFFH为-1的补码,因为结果为负,所以sf=l
(ah)=OFEH, (bh)=OFFH;则(ah)-(bh)=-2-(-1)=OFFH,因为结果为负,所以sf=l

通过上面的例子,我们是不是可以得到这样的结论:cmp操作对象1,操作对象2指令执行后,sf=l,就说明操作对象1<操作对象2? 当然不是。 我们再看两个例子
(ah)=22H. (bh)=OAOH;则(ah)-(bh)=34-(-96)=82H. 82H是-126的补码
所以sf=1
这里虽然sf--1,但是并不能说明(ah) < (bh)因为显然34>-96 两个有符号数A和B相减,得到的是负数,那么可以肯定A 比如:

 mov ah,22h
 mov bh,0A0H
 sub ah,bh

结果Sf=1,运算实际得到的结果是(ah)=82H,但是在逻辑上,运算所应该得到的结果是:34-(-96)=130。就是因为130这个结果作为一个有符号数超出了-128-127这个范围,在ah中不能表示,而ah中的结果被CPU当作有符号数解释为一126。而sf被用来记录这个实际结果的正负,所以sf=1。但sf=1不能说明在逻辑上,运算所得的正确结果的正负。
又比如:

 mov ah,08ah
 mov bh,070H
 sub ah,bh

结果Sf=0,运算(ah)-(bh)实际得到的结果是1AH,但是在逻辑上,运算所应该得到的结果是:(-118)-112= 2300 .sf记录实际结果的正负,所以sf=0。但sf=-0不能说明在逻辑上,运算所得的正确结果。
但是逻辑上的结果的正负,才是cmp指令所求的真正结果,因为我们就是要靠它得到两个操作对象的比较信息。所以cmp指令所作的比较结果,不是仅仅靠sf就能记录的,因为它只能记录实际结果的正负。
我们考虑一下,两种结果之间的关系,实际结果的正负,和逻辑上真正结果的正负,它们之间有多大的距离呢?从上面的分析中,我们知道,实际结果的正负,之所以不能说明逻辑上真正结果的正负,关键的原因在于发生了溢出。如果没有溢出发生的话,那么,实际结果的正负和逻辑上真正结果的正负就一致了。
所以,我们应该在考查sf(得知实际结果的正负)的同时考查of(得知有没有溢出),就可以得知逻辑上真正结果的正负,同时就可以知道比较的结果。

  下面我们以 cmp ah,bh 为例,总结一下 CPU 执行 cmp 指令后,sf 和 of 的值是如何来说明比较的结果的。
  (1) 如果 sf=1,而 of=0
  of=0,说明没有溢出,逻辑上真正结果的正负=实际结果的正负;
  因 sf=1,实际结果为负,所以逻辑上真正的结果为负,所以 (ax)<(bx)。   (2) 如果 sf=1,而 of=1
  of=1,说明有溢出,逻辑上真正结果的正负≠实际结果的正负
  因 sf=1,实际结果为负。
  实际结果为负,而又有溢出,这说明是由于溢出导致了实际结果为负,简单分析一下,就可以看出,如果因为溢出导致了实际结果为负,那么逻辑上真正的结果必然为正。(比如 130 二进制为 1000 0010 B,溢出导致结果为负数,需要注意的是,溢出和进位不一样)
  这样,sf=1,of=1,说明了 (ah)>(bh)。
  例如:00000000B - 10000000B
  (3) 如果 sf=0,而 of=1
  of=1,说明有溢出,逻辑上真正结果的正负≠实际结果的正负;
  因 sf=0,实际结果非负。而 of=1 说明有溢出,则结果非 0,所以,实际结果为正。
  实际结果为正,而又有一处,这说明是由于溢出导致了实际结果非负,简单分析一下,就可以看出,如果因为溢出导致了实际结果为正,那么逻辑上真正的结果必然为负。(比如 -130 二进制为 0111 1110 B,溢出导致结果为正)
  这样,sf=0,of=1,说明了 (ah)<(bh)。   sf=0,of=1,说明最高位相同,并且有溢出,这样说明了 (ax) 必然小于 (bx)。  (4) 如果 sf=0,而 of=0
  of=0,说明没有溢出,逻辑上真正结果的正负=实际结果的正负;
  因 sf=0,实际结果非负,所以逻辑上真正的结果非负,所以 (ah)≥(bh)。
  另外一种理解方式是,其实减法也可以转换为加法,只要把第二个数取反(十进制意义的取反),
  比如:
  溢出导致结果为正,实际结果必然为负数(cmp 实际上等于两个负数相加)。

   mov ah,08Ah
  mov bh,070h
  cmp ah,bh

 
  结果 sf=0,of=1,因为上面的 (ah)-(bh) 可以看作 1000 1010B + 1001 0000B,这里最高位都为 1,负数相加产生溢出,实际结果为负数,但是低 8 位是正数,所以 sf=0。

  另外一个例子:
  溢出导致

  mov ah,22H
  mov bh,0A0H
  cmp ah,bh

  结果 sf=1,of=1,上面的代码可以看作 0010 0010B + 0110 0000B,这里两个正数相加变负数,也产生了溢出,所以 of=1,sf=1。

  溢出导致结果为负数 => 最高位 其实不是符号位,所以我们应该把结果当作正数看待;溢出导致结果为正数 => mov ah,0; add al,bl; 最高位本来是1(负数),但是相加之后最高位进位到更高位,这就超出了负数所能表达的最大范围(-128) ,实际结果为 - 2ˆ8 + 低8位正数。

  好像上面的说法都不太好理解,也许可以简单看作,cmp 的时候,把第二个数转换为正数;两个数最高为 1 的时候,为两个负数相加,但是会溢出,结果变正数,但实际结果应该是负数;两个数最高位为 0 的时候,如果产生溢出,并且得到结果是负数,那么说明,超过了数字正数的最大范围(8 位为 127),所以实际结果应该是正数。


发布日期:

所属分类: 易语言 标签: