汇编语言程序转移指令

程序总是放在代码段,程序的执行就是指令逐条从内存的代码段被取出,由 CPU 进行译码、执行。一段程序开始执行时,指令指针寄存器 IP 总是指向程序的第一条要执行的指令所对应的偏移地址,CPU 首先根据代码段寄存器 CS 和指令指针寄存器 IP 共同表示的地址取出该条指令,送往译码器,再执行,同时,自动修改IP寄存器的值,使其指向下一条指令所在的偏移地址。重复以上过程,程序中的指令被逐条取出执行。

以上程序的执行过程是顺序执行的形式,但通常程序中总会有判断、选择、跳转发生,从而改变程序的执行流程。常见的程序转移有以下几种指令:

  • 无条件转移指令;

  • 条件转移指令;

  • 循环指令;

  • 子程序调用指令;

  • 中断调用指令。

一、无条件转移指令与程序的可重新定位

JMP(jmp):该指令无条件转移到指令指定的地址去执行程序。指令中必须指定转移的目标地址(或称转向地址)。

根据目标地址,可以将无条件转移指令分为段内转移和段间转移。

① 转移的目标地址如果和本条跳转指令同在一个代码段,也就是说,跳转后,CS 寄存器的值并没有改变,只是 IP 寄存器有了改变,这叫段内转移。

② 如果转移的目标地址和本条跳转指令不在同一个代码段,也就是说,跳转后,CS 寄存器的值也发生了改变,这叫段间转移。

根据目标地址是否在跳转指令中直接给出,还可以将转移指令分为直接转移和间接转移。

① 如果转移的目标地址在跳转指令中直接给出,这叫直接转移。

② 如果转移的目标地址在跳转指令中通过其他方式间接给出,这叫间接转移。

下面将分别介绍段内直接转移、段内间接转移、段间直接转移以及段间间接转移。

(1)段内直接转移

格式:JMP  NEAR PTR OPR

操作:IP←IP+16位位移量

其中 NEAR PTR 为目标地址 OPR 的属性说明,表明是一个近(段内)跳转。

从指令的操作可以看出,IP 寄存器被加上 16 位位移量,IP 的值发生了改变,不再直接指向下一条指令,但 CS 寄存器并没有改变,所以是段内直接转移。

需要注意,位移量 OPR 是有符号数,这就意味着,位移量是负数时,IP+16 位位移量之后,IP 的值反而会变小,这就导致程序向后跳。如果位移量是正数时,IP 的值会增加,这就导致程序向前跳。

如果转移的目标地址 OPR 距离本条跳转指令在 -128~+127 字节范围内,也可写成所谓的短转移指令:

1
JMP  SHORT OPR

通常,目标地址 OPR 的属性说明可以省略,直接写成:JMP OPR 即可。

注意该指令的操作是 IP←IP+位移量,而不是 IP←目标地址 OPR。下面结合例 43 说明该操作相比直接赋值的优势。

例 43 程序重新定位下的转移指令。

下面的程序中有 4 条指令,每条指令都为 2 个字节长度,假定首条指令 JMP P1 的偏移地址位置为 1000H,当执行该条指令后,程序跳过其后的 2 条 MOV 指令,而跳转到标号 P1 所指示的位置去执行 ADD 指令。

1
2
3
4
5
6
7
1000: JMP P1      ; 1000H是本条指令的所在偏移地址

1002: MOV AX,BX

1004: MOV DX,CX

P1:  ADD AX,DX    ; P1是标号,其值为1006H

如果把这个程序放在内存中的另一个位置,如下所示:

1
2
3
4
5
6
7
2000: JMP P1      ; 2000H是本条指令的所在偏移地址

2002: MOV AX,BX

2004: MOV DX,CX

P1: ADD AX,DX     ; P1是标号,其值为2006H

显然这两段程序是一样的,无论在内存什么位置,都不应影响运行结果。

上述程序可在 Debug 下直接用A命令给出,如图 22 所示,分别在 1000 和 2000 两个位置。由于 Debug 下不能识别符号,所以 P1 分别用 1006 和 2006 表示,尽管如此,这两处的 JMP 指令的机器码依然一致,都是 EB04,看来这两段程序确实完全一样。

程序的可重新定位

图 22 程序的可重新定位

由图 22 中可以看出,P1 的值虽然为 1006H,但指令 JMP P1 的机器指令中不是直接给出目标地址 1006H,而是位移量 4。因为 JMP 指令的下一条指令地址为 1002,而 P1 的地址为 1006,就是说跳转的距离为 4,这在汇编时就可确定。在程序执行阶段,当 JMP P1 指令被取出将要执行时,当前的 IP 的值自动调整为 1002H,指向下一条指令,而 JMP P1 指令执行的结果使得 IP←IP+4,即 IP 由 1002 变为 1006H,所以直接跳转到位于 1006H 处执行 ADD 指令。

同样的道理,位于 2000 处程序的 JMP 指令的机器码 EB04,跳转的距离为 4,当 JMP 指令被取出将要执行时,当前的 IP 的值自动调整为 2002H,指向下一条指令,而 JMP 指令执行的结果使得 IP←IP+4,即 IP 由 2002 变为 2006H,从而直接跳转到位于 2006H 处执行。

可见,由于 JMP 机器指令中不是直接给出目标偏移地址,而是给出相对于目标位置的位移量,用 IP←当前 IP+位移量的操作机制,实现了程序的可重新定位。

(2)段内间接转移

格式:JMP  WORD PTR OPR

操作:IP←(EA)

其中有效地址 EA 值由 OPR 的寻址方式确定。它可以使用除立即数方式以外的任何一种寻址方式。如果指定的是寄存器,则把寄存器的内容送到 IP 寄存器中,如果指定的是内存中的一个字,则把该存储单元的内容送到 IP 寄存器中去。

例 44 如果 BX=2000H,DS=4000H,(42000H)=6050H,(44000H)=8090H,TABLE 的偏移地址为 2000H,分析下面四条指令单独执行后 IP 的值。

1
2
3
4
5
6
7
JMP  BX           ; 寄存器寻址,IP=BX

JMP  WORD PTR[BX]      ; 寄存器间接寻址,IP=[DS:BX]

JMP  WORD PTR TABLE     ; 直接寻址,IP=[DS:TABLE]

JMP  TABLE[BX]        ; 寄存器相对寻址,IP=[DS:(TABLE+BX)]

第一条指令执行后,IP=BX=2000H。

第二条指令执行后,IP=(DS:2000H)=(40000H+2000H)=(42000H)=6050H。

第三条指令执行后,IP=(DS:2000H)=(40000H+2000H)=(42000H)=6050H。

第四条指令执行后,IP=(DS:4000H)=(40000H+4000H)=(44000H)=8090H。

(3)段间直接转移

格式:JMP  FAR PTR OPR

操作:IP←OPR的偏移地址

CS←OPR 所在段的段地址

在汇编格式中 OPR 可使用符号地址,而机器语言中含有转向的偏移地址和段地址。因为 IP 和 CS 的值都被改变,所以又叫跨段直接远转移。

(4)段间间接转移

格式:JMP  DWORD PTR OPR

操作:IP←(EA)

CS←(EA+2)

其中 EA 由 OP R 的寻址方式确定,它可以使用除立即数及寄存器方式以外的任何存储器寻址方式。根据寻址方式求出 EA 后,把指定内存字单元的内容送到 IP 寄存器,并把下个字的内容送到 CS 寄存器,这样就实现了段间的间接远跳转。

例 45 如果 BX=2000H,DS=4000H,(42000H)=6050H,(42002H)=1234H,指出下面指令执行后 IP 和 CS 的值。

1
JMP  DWORD PTR[BX]

指令执行后,IP=(DS:2000H)=(40000H+2000H)=(42000H)=6050H,CS=(42002H)=1234H。

二、条件转移指令

条件转移指令是根据上一条指令执行后,所产生的标志位来进行测试条件判别。所以在使用条件转移指令之前,应有一条能产生标志位的前导指令,如 CMP 指令。每一种条件转移指令有各自的测试条件,当满足测试条件时则转移到由指令指定的转向地址去执行那里的程序,如不满足测试条件则顺序执行下一条指令。

在汇编指令格式中,转向地址由标号来表示,在 8086 系列 16 位的机器中规定,转向地址与本条转移指令所在地址的距离应在 -128~+127 个字节的范围之内。(386 及其后继机型允许转移到段内的任何位置)。另外,所有的条件转移指令都不影响标志位。下面我们把条件转移指令分为四组来介绍。

(1)根据单个条件标志的设置情况转移。这组包括 10 种指令。它们一般适用于测试某一次运算的结果并根据其不同特征产生程序分支作不同处理的情况。

① 指令格式:JZ  OPR     ; 结果为零则转移

等效指令:JE  OPR      ; 结果相等则转移

测试条件:ZF=1,则转移。

② 指令格式:JNZ  OPR     ; 结果不为零则转移

等效指令:JNE  OPR

测试条件:ZF=0,则转移。

③ 指令格式:JS  OPR     ; 结果为负则转移

测试条件:SF=1,则转移。

④ 指令格式:JNS  OPR     ; 结果为正则转移

测试条件:SF=0,则转移。

⑤ 指令格式:JO  OPR     ; 结果溢出则转移

测试条件:OF=1,则转移。

⑥ 指令格式:JNO  OPR     ; 结果不溢出则转移

测试条件:OF=0,则转移。

⑦ 指令格式:JC  OPR     ; 进位位为 l 则转移

等效指令:JB  OPR      ; 低于则转移

等效指令:JNAE  OPR     ; 不高于等于则转移

测试条件:CF=1,则转移。

⑧ 指令格式:JNC  OPR     ; 进位位为 0 则转移

等效指令:JNB  OPR      ; 不低于则转移

等效指令:JAE  OPR      ; 高于等于则转移

测试条件:CF=0,则转移。

⑨ 指令格式:JP  OPR     ; 奇偶位为 1 则转移

等效指令:JPE  OPR      ; 偶数个 1 则转移

测试条件:PF=1,则转移。

⑩ 指令格式:JNP  OPR     ; 奇偶位为 0 则转移

等效指令:JPO  OPR      ; 奇数个 1 则转移

测试条件:PF=0,则转移。

上面 10 条指令都是根据标志寄存器中某一个标志位的状态决定是否转移,下面还有一条根据 CX 寄存器是否为零决定是否转移的指令。

(2)测试 CX 寄存器的值为零则转移

指令格式:JCXZ  OPR      ; CX 寄存器为零则转移

测试条件:CX=0,则转移。

(3)比较两个无符号数,并根据比较的结果转移。

① 指令:JC(JB,JNAE)OPR     ; 进位位为 l(低于,不高于等于)则转移

测试条件:CF=1,则转移。

② 指令:JNC(JNB,JAE)OPR    ; 进位位为 0(不低于,高于等于)则转移

测试条件:CF=0,则转移。

③ 指令:JBE(JNA)OPR       ; 低于等于(不高于)则转移

测试条件:CF|ZF=1,即 CF 与 ZF 的或为 1,则转移。

④ 指令:JNBE(JA)OPR       ; 不低于等于(高于)则转移

测试条件:CF|ZF=0,即 CF 与 ZF 的或为 0,则转移。

(4)比较两个有符号数,并根据比较结果转移。

① 指令:JL(JNGE) OPR       ; 小于(不大于等于)则转移

测试条件:SF^OF=1,即 SF 与 OF 的异或为 1,则转移。

② 指令:JNL(JGE) OPR       ; 不小于(大于等于)则转移

测试条件:SF^OF=0,即 SF 与 OF 的异或为 0,则转移。

令:JLE(JNG) OPR         ; 小于等于(不大于)则转移

测试条件:(SF^OF)|ZF=1,即 SF 与 OF 的异或为 1 或者 ZF=1,则转移。

令:JNLE(JG) OPR         ; 不小于等于(大于)则转移

测试条件:(SF^OF)|ZF=0,即 SF 与 OF 的异或为 0 且 ZF=0,则转移。

第 3 和第 4 两组指令分别用于无符号数的比较和有符号数的比较,测试条件是完全不同的。道理很简单,例如 8 位二进制数 A=10101001,B=00110101,如果把它们看成无符号数,则 A>B,如果把它们看成有符号数,则 A<B

无符号数比较的转移指令的测试条件是易于理解的。当两个无符号数相减时,CF 位的情况说明了是否有借位的问题,有借位时,CF=1,这就是 JC,JB 和 JNAE 是等效指令的原因。

有符号数比较的转移指令的测试条件比较复杂,下面分析 JL 指令的情况,先比较两个有符号数的大小,CMP 通过相减而产生的标志位作出判断。例如:

1
2
3
CMP AX, BX

JL  P1        ; 如果 AX<BX,则转移到 P1

有四种情况,如下所示。

① 正数 A—正数 B,如结果为负数,说明 A<B;如结果为正数,说明 A≥B。不会溢出。

② 负数 A—负数 B,如结果为负数,说明 A<B;如结果为正数,说明 A≥B。不会溢出。

③ 负数 A—正数 B,显然 A<B,结果应为负数;如结果为正数,说明溢出。

④ 正数 A—负数 B,显然 A>B,结果应为正数;如结果为负数,说明溢出。

以上分析结合标志位如表 3 所示。

有符号数的比较判断条件

表 3 有符号数的比较判断条件

由表格可以看出,只要 SF 和 OF 不相同,就说明两个有符号数 A,B 的比较中 A<B。所以可以得出 JL 指令的测试条件为 SF^OF=1。

例 46 有一个长为 19 字节的字符串,首地址为 MESS。查找其中的‘空格’(20H)字符,如找到则继续执行,否则转标号 NO。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
MOV  AL,20H

MOV  CX,19

MOV  DI,-1

LK:  INC  DI

DEC  CX

CMP  AL,MESS[DI]     ; AL-[MESS+DI],不回送结果,置标志位CF/ZF

JCXZ  NO          ; 判断CX是否为0

JNE  LK          ; 判断ZF标志位是否为0,为0则跳转

YES:

JMP EXIT

NO:

EXIT:MOV AH,4CH

INT 21H

AL-[MESS+DI] 的结果为 0,即 AL 的内容与内存单元内容相等时,CMP 的运行结果会将 ZF 标志位置1,当执行到 JNE 时则不会,继续执行下一条指令。

三、循环指令

为了方便循环程序的设计,80x86 提供了以下循环指令。

  • LOOP(loop)循环

  • LOOPZ/LOOPE(loop while zero,or equal)当为零/相等时循环

  • LOOPNZ/LOOPNE(loop while not zero,or not equal)当不为零/不相等时循环

① 指令:LOOP  OPR    ; 循环

测试条件:CX≠0,则循环

② 指令:LOOPZ  OPR    ; 当为零时循环

测试条件:ZF=1 且CX≠0,则循环

等效指令:LOOPE  OPR

③ 指令:LOOPNZ  OPR   ; 当不为零时循环

测试条件:ZF=0 且CX≠0,则循环

等效指令:LOOPNE  OPR

以上循环指令的操作均是:

首先执行 CX 寄存器减1,然后根据测试条件决定是否转移。

例 47 在首地址为 MESS 长为 19 字节的字符串中查找‘空格’(20H)字符,如找到则继续执行,否则转标号 NO。用循环指令实现程序的循环。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
MOV  AL,20H

MOV  CX,19

MOV  DI,-1

LK: INC  DI

CMP  AL,MESS[DI]

LOOPNE LK  ; 当CX≠0且ZF=0时跳转

JNZ  NO  ; 当ZF=0时跳转

YES: 

JMP EXIT

NO: 

EXIT: MOV AH,4CH

INT 21H


当 LOOPNE LK 结束时有两种可能,即 CX=0 或者 ZF=1,而这两种可能对应的结果是不相同的,因此在 LOOPNE 下方紧跟着一条 JNZ 的跳转指令,用以区分这两种情况。需特别注意 CX=0 与 ZF=1 同时成立的情况,即比较的最后一个字符是相同的,所以区分以上两种情况时,需优先判断 ZF=1 是否成立。

请参阅

(完)

comments powered by Disqus