汇编语言之标志寄存器
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
深信服三年半工作经验,目前就职游戏厂商,希望能和大家交流和学习,
微信公众号:编程入门到秃头 或扫描下面二维码
零基础入门进阶人工智能(链接)