汇编语言之标志寄存器

1、标志寄存器

CPU内部的寄存器中,有一种特殊的寄存器(对于不同的处理机,个数和结构都可能不同)具有以下3种作

用:

1. 用来存储相关指令的某些执行结果。

2. 用来为CPU执行相关指令提供行为依据。

3. 用来控制CPU的相关工作方式。

这种特殊的寄存器在8086CPU中,被称为标志寄存器。8086CPU的寄存器,在前面已经学过13个了,现

在学习最后一个寄存器FR-标志寄存器。

FR与其它寄存器不一样,其它寄存器是用来存放数据的,都是整个寄存器具有一个含义,而FR寄存器是按位

起作用的,也就是说它的每一位都有专门的含义,记录特定的信息。

8086CPU的FR寄存器的结构如下图所示:

FR的第1、3、5、12、13、14、15位是空白位,在8086CPU中没有使用,不具有任何意义,而第0、2、

4、6、7、8、9、10、11位都具有特殊的含义。

2、CF标志

7.1 CF标志

FR的第0位是CF,进位标志位。一般情况下,在进行无符号数运算的时候,它记录了运算结果的最高有效位

向更高位的进位值,或从更高位的借位值。

对于位数为N的无符号数来说,其对应的二进制信息的最高位,即第N-1位,就是它的最高有效位,而假想

存在的第N位,就是相对于最高有效位的更高位,如下图所示:

我们知道,当两个数据相加的时候,有可能产生从最高有效位向更高位的进位,比如,两个8位数据:98H+

98H,将产生进位,这个进位值就用CF标志位来保存。

比如,下面的指令:

Mov al, 98H

Add al, al

执行后,计算结果为130H,al=30H,CF=1,CF记录了从最高有效位向更高位的进位值。

两数相加,如果产生了进位,则CF=1,如果没有产生进位,则CF=0。

 

当两个数据做减法的时候,有可能向更高位借位,比如,两个8位数据:97H-98H,将产生借位,借位后相

当于计算197H-98H,而FR的CF标志位也可以用来记录这个借位值。

比如,下面的指令:

Mov al, 97H

Sub al, 98H

执行后,计算结果为197H-98H=ffH,al=ffH,CF=1,CF记录了向更高位的借位值。

两数相减,如果产生借位,则CF=1,如果没有产生借位,则CF=0。

3、adc指令

Adc是带进位加法指令,它利用了CF位上记录的进位值。

格式:adc 操作对象1,操作对象2

功能:操作对象1=操作对象1+操作对象2+CF

例1:mov ax, 1

      Add ax, ax    ;结果:ax=2,没有产生进位值,CF=0。

      Adc ax, 3     ;结果:ax=ax+3+CF=2+3+0=5。

例2:mov al, 98H

      Add al, al    ;结果=130H,产生了进位值,CF=1,al=30H。

      Adc al, 3    ;结果:al=al+3+CF=30H+3+1=34H。

可以看出,adc指令比add指令多加了一个CF位的值,为什么要加上CF的值呢?CPU为什么要提供这样一

条指令呢?

我们来看一下两个数据:0198H和0183H是如何相加的,见下图:

 

可以看出,加法可以分两步来进行:1.低位相加(98+83)。2.高位相加再加上低位相加产生的进位值(1+1

+1)。看来CPU提供adc指令的目的,就是来进行加法的第二步运算的。用adc指令和add指令相配合就可以

对更大的数据进行加法运算。

例3:计算1EF000H+201000H,结果放在ax(高16位)和bx(低16位)中。

因为两个数据的位数都大于16位,用add指令无法进行计算,我们将计算分两步进行,先将低16位

(F000H和1000H)相加,然后将高16位(1EH和20H)和进位值相加,代码如下:

Mov ax, 1eH

Mov bx, f000H

Add bx, 1000H    ;低16位相加,结果:f000H+1000H=10000H 产生了进位值,CF=1,bx=0。

Adc ax, 20H    ;高16位相加,结果:ax=ax+20H+CF=1eH+20H+1=3fH。

最终结果:ax=3fH,bx=0,1EF000H+201000H=3f0000H。

Adc指令执行后,也可能产生进位值,所以也会对CF位进行设置,由于有这样的功能,我们就可以对任意大

的数据进行加法运算。

例4:计算1EF0001000H+2010001EF0H,结果放在ax(最高16位),bx(次高16位),cx(低16位)

中。

计算分3步进行:

1.     先将低16位(1000H和1EF0H)相加,完成后,CF记录本次相加的进位值。

2.     再将次高16位(F000H和1000H)和CF(来自低16位的进位值)相加,完成后,CF记录本次相加

的进位值。

3.最后最高16位(1EH和20H)和CF(来自次高16位的进位值)相加,完成后,CF记录本次相加的进位

值。

代码如下:

Mov ax, 1eH

Mov bx, f000H

Mov cx, 1000H

Add cx, 1ef0H    ;低16位相加,结果:1000H+1ef0H=2ef0H,没有产生进位值,CF=0,cx=2ef0H。

Adc bx, 1000H    ;次高16位相加,结果:f000H+1000H+0=10000H,产生进位值,CF=1,bx=0。

Adc ax, 20H    ;最高16位相加,结果:ax=ax+20H+CF=1eH+20H+1=3fH,没有产生进位,CF=0。

最终结果:ax=3fH,bx=0,cx=2ef0H。1EF0001000H+2010001EF0H=3F00002EF0H。

4、sbb指令

Sbb是带借位减法指令,它利用了CF位上记录的借位值。

格式:sbb 操作对象1,操作对象2

功能:操作对象1=操作对象1-操作对象2-CF。

Sbb指令执行后,将对CF进行设置,利用sbb指令和sub指令配合使用可以对任意大的数据进行减法运算。

例1:计算3E1000H-202000H,结果放在ax(高16位),bx(低16位)。

计算分两步进行,先将低16位(1000H和2000H)相减,然后将高16位(3EH和20H)和借位值相减,

代码如下:

Mov bx, 1000H

Mov ax, 3eH

Sub bx, 2000H    ;低16位相减,结果:1000H-2000H=11000H-2000H=f000H,产生了借位值,

                    CF=1,bx=f000H。

Sbb ax, 20H    ;高16位相减,结果:ax=ax-20H-CF=3eH-20H-1=1dH,没有产生借位值,CF=0。

最终结果:ax=1dH,bx=f000H,3E1000H-202000H=1DF000H。

例2:计算6E4F0031C0H-1FA2002700H。结果放在ax(最高16位),bx(次高16位),cx(低16

位)。

计算分3步进行:

1. 先将低16位(31C0H和2700H)相减,完成后,CF记录本次相减的借位值。

2. 再将次高16位(4F00H和A200H)和CF(来自低16位的借位值)相减,完成后,CF记录本次相减的

借位值。

3. 最后将最高16位(6EH和1FH)和CF(来自次高16位的借位值)相减,完成后,CF记录本次相减的借

位值。

代码如下:

Mov ax, 6eH

Mov bx, 4f00H

Mov cx, 31c0H

Sub cx, 2700H    ;低16位相减,结果:31c0H-2700H=ac0H,没有产生借位值,CF=0,cx=ac0H。

Sbb bx, a200H    ;次高16位相减,结果:14f00H-a200H=ad00H,产生借位值,CF=1,bx=ad00H。

Sbb ax, 1fH    ;最高16位相减,结果:ax=ax-1fH-CF=6eH-1fH-1=4eH,没有产生借位值,CF=0。

最终结果:ax=4eH,bx=ad00H,cx=ac0H,6E4F0031C0H-1FA2002700H=4EAD000AC0H。

 

 

5、ZF标志

FR的第6位是ZF,零标志位。它记录相关指令执行后,其结果是否为0。如果(真),结果为0,那么ZF=1;

如果(假),结果非0,那么ZF=0。

对于ZF的值,我们可以这样来看:在计算机中1表示逻辑真,表示肯定,所以当结果为0的时候,ZF=1;在

计算机中0表示逻辑假,表示否定,所以当结果不为0的时候,ZF=0。

下面的指令:

Mov ax, 5

Sub ax, ax

执行后,结果为0,表示真,则ZF=1。

Mov ax, 5

Sub ax, 1

执行后,结果不为0,表示假,则ZF=0。

6、cmp指令

Cmp是比较指令,它的功能相当于sub指令,只是不保存结果。Cmp指令执行后,将对标志寄存器产生影

响,其它相关指令通过识别这些被影响的标志位来得知比较结果。

指令格式:cmp 操作对象1,操作对象2

功能:计算操作对象1-操作对象2,但并不保存结果,仅仅根据计算结果对标志寄存器的标志位进行设置。

Cmp指令执行后,依据标志位的值就可以看出比较结果。

比如,cmp ax, bx执行后:

如果ZF=1,说明ax=bx,因为ax-bx=0,那么ax必定等于bx。

如果ZF=0,说明ax≠bx,因为ax-bx≠0,那么ax与bx必定不相等。

如果CF=1,说明ax<bx,因为ax-bx产生了借位,那么ax必定小于bx。

如果CF=0,说明ax≥bx,因为ax-bx没有产生借位,那么ax必定大于或等于bx。

如果CF=0,并且ZF=0,说明ax>bx,因为ax-bx没有产生借位,并且ax-bx≠0,那么ax必定大于bx。

如果CF=1或ZF=1,说明ax≤bx,因为ax-bx产生了借位,又或者ax-bx=0,那么ax必定小于或等于

Bx。

 

7、检测比较结果的条件转移指令


转移指的是它能够修改IP,而条件指的是它可以根据某种条件,决定是否修改IP,所有条件转移指令都是短

转移,转移的位移范围为﹣128~127。

大多数条件转移指令都检测标志寄存器的相关标志位,根据检测的结果来决定是否修改IP,它们所检测的标

志位都是被cmp指令影响的那些表示比较结果的标志位。

下面是常用的根据无符号数的比较结果进行转移的条件转移指令。

指令    检测的相关标志位          与cmp配合使用的逻辑含义

Je      如果ZF=1则转移               如果等于则转移

Jne    如果ZF=0则转移                如果不等于则转移

Jb      如果CF=1则转移              如果低于则转移

Jnb    如果CF=0则转移               如果不低于则转移

Ja      如果CF=0且ZF=0则转移       如果高于则转移

Jna    如果CF=1或ZF=1则转移       如果不高于则转移

以上这些条件转移指令是根据检测相关的标志位来决定是否转移,比如:je是检测ZF的值来决定是否转移,

如果ZF=1则转移,至于根据逻辑含义来决定是否转移,则需要与cmp指令配合使用,这个在下一节会讲到。

8、cmp与条件转移指令配合使用

上一节介绍的条件转移指令,所检测的标志位都是cmp指令进行无符号数比较的时候,记录比较结果的标志

位,比如,je检测ZF位,当ZF=1时转移,如果在je前面使用了cmp指令,那么je对ZF的检测,实际上是间

接地检测cmp的比较结果是否为两数相等。

请看下面一段代码:

Cmp ax, bx

Je s

Add ax, bx

Jmp short ok

  S:add ax, ax

Ok: …

上面的代码执行时,如果ax=bx,则cmp ax, bx使ZF=1,而je检测ZF是否为1,如果为1,则转移到标号

S处执行指令add ax, ax,我们也可以这样说,cmp比较ax, bx后所得到的相等的结果使得je指令进行转移,这

种说法很好地体现了je指令的逻辑含义,即“相等则转移“。

“相等则转移”这种逻辑含义是通过和cmp指令配合使用来体现的,我们用cmp指令与条件转移指令配合使

用的时候,不必再考虑cmp指令对相关标志位的影响和je等指令对相关标志位的检测,因为相关的标志位只是为

Cmp和je等指令传递比较结果,我们可以直接考虑cmp与je等指令配合使用时,表现出来的逻辑含义。

请看下面的指令:

Cmp byte ptr [bx], 8    ;和8比较

Je 标号                 ;如果等于则转移

Cmp byte ptr [bx], 8    ;和8比较

Jne 标号                ;如果不等于则转移

Cmp byte ptr [bx], 8    ;和8比较

Jb 标号                 ;如果低于则转移

Cmp byte ptr [bx], 8    ;和8比较

Jnb 标号                ;如果不低于则转移

Cmp byte ptr [bx], 8    ;和8比较

Ja 标号                 ;如果高于则转移

Cmp byte ptr [bx], 8    ;和8比较

Jna 标号                ;如果不高于则转移

上面的指令,用[bx]中的数值和8比较,“如果怎么怎么样则转移”,我们在修改游戏时,可以根据这些逻辑含

义(即:如果怎么怎么样则转移),选择合适的条件转移指令。

9、其它标志位

标志寄存器的大部分标志位,我们都不必深入地去学习,因为这和修改游戏没有多大关系,我们只需简单了解

一下即可,FR一共有9个标志位,前面已学习了ZF和CF这两个标志位,现在讲讲余下的7个标志位。

PF:奇偶标志位。它记录相关指令执行后,其结果的所有二进制位中1的个数是否为偶数,如果(真),1的

个数为偶数,PF=1,如果(假),1的个数为奇数,PF=0。

比如,某些指令执行后,其结果二进制值为01001011,有4(偶数)个1,则PF=1;某些指令执行后,其

结果二进制值为00001011,有3(奇数)个1,则PF=0。

SF:符号标志位。它记录相关指令执行后,其结果是否为负,如果(真),结果为负,SF=1,如果(假),结

果非负,SF=0。

OF:溢出标志位。一般情况下,OF记录了有符号数运算的结果是否发生了溢出,如果(真),发生了溢出,

OF=1,如果(假),没有发生溢出,0F=0。

什么是溢出?在进行有符号数运算的时候,如结果超过了机器所能表示的范围称为溢出。那么,机器所能表示

的范围是多少呢?对于8位的有符号数据,机器所能表示的范围就是﹣128~127;对于16位的有符号数据,机器

所能表示的范围就是﹣32768~32767。如果运算结果超出了机器所能表达的范围,将产生溢出。

比如,指令:

Mov al, 98

Add al, 99

执行后,al=98+99=197,197超出了机器所能表示的8位有符号数的范围:﹣128~127,所以产生了溢出。

DF:方向标志位。在串处理指令中,控制每次操作后SI、DI的增减。

DF=0,每次操作后SI、DI递增;DF=1,每次操作后SI、DI递减。

DF标志位与串传送指令(movsb、movsw)有关,而串传送指令与游戏修改无关,所以就不讲解了。

TF:跟踪标志位。用于程序调试。

如果TF=1,则CPU处于单步执行指令的工作方式,此时,每执行完一条指令,就显示CPU各个寄存器的当

前值及CPU将要执行的下一条指令。如果TF=0,则处于连续工作模式。

AF:辅助进位标志位。在下列情况下,AF的值被设置为1,否则其值为0。

1. 在字操作时,发生低字节向高字节进位或借位时。

2. 在字节操作时,发生低4位向高4位进位或借位时。

IF:中断允许标志位。用来决定CPU是否响应CPU外部的可屏蔽中断发出的中断请求,当IF=1,响应中断

请求,当IF=0,不响应中断请求。
 

10、lea和nop指令

Lea为有效地址传送指令。

格式:lea 操作对象1,操作对象2

功能:将源操作数给出的有效地址传送到指定的寄存器中。

说明:操作对象1为目的操作数,可为任意一个16位的通用寄存器,操作对象2为源操作数,可为地址表达

式。

比如,指令:

Lea ax, [217a]

执行后,ax=217aH。

Lea ax, [bx+si+200]

执行后,ax= bx+si+200H。

 

Nop为空操作指令。格式:nop。

功能:本指令不产生任何结果,仅消耗几个时钟周期的时间,接着执行后续指令,常用于程序的延时等。

在修改游戏的时候,可用于锁定某些数据的数值。

 

 

作者:chen.yu
深信服三年半工作经验,目前就职游戏厂商,希望能和大家交流和学习,
微信公众号:编程入门到秃头 或扫描下面二维码
零基础入门进阶人工智能(链接)