2.3.5 控制转移指令
程序控制指令可以控制程序的流向,可以实现转移、循环、调用、返回等功能。前几节介绍的指令都不能改变程序顺序执行的特点。程序控制指令包括转移指令、循环指令、子程序调用及返回指令、中断调用及返回指令。
1.无条件转移指令JMP
格式:JMP DST
功能:无条件转移到DST所指向的地址。
说明:DST为转移的目标地址(或称转向地址),转移地址分为段内和段间转移。段内转移指令只改变IP值,不改变CS值,所以只能实现同段内的转移。而段间转移除了改变IP值,还要改变CS值,所以可以实现段间转移。
(1)段内直接短转移。
格式:JMP SHORT LABEL
功能:无条件转移到标号(LABEL,目标地址)处。
说明:标号与JMP指令在同段内,使用段内直接短转移寻址方式。指令长度短,可用于实现短距离的段内转移。
【例2-55】
…
JMP SHORT ABC;无条件转移到ABC标号处
ADD AX,BX
ABC:SUB AX,CX
…
(2)段内直接转移。
格式:JMP LABEL
或 JMP NEARPTRLABEL
功能:无条件转移到标号处。
说明:标号与JMP指令在同段内,使用段内直接寻址方式。在操作数长度为16位的情况下,可以实现距离当前IP值的±32K B范围内的转移。
【例2-56】
…
JMP ABC1;无条件转移到ABC1标号处
ADD AX,CX
…;距离超出+127字节,但仍在本段内
ABC1:SUB AX,CX
…
(3)段内间接转移。
格式:JMP REG/MEM
功能:无条件转移,其转向地址在通用寄存器或内存单元中。
说明:使用段内间接寻址方式。由于寄存器或内存单元中存放段内偏移量,所以必须是16位长。可实现段内转移。
【例2-57】 对于例2-56,可以把ABC1的偏移量送给通用寄存器,通过寄存器实现段内间接转移,程序段如下所示:
…
LEA BX,ABC1
JMP BX;转向地址ABC1在BX中
ADD AX,CX
…
ABC1:SUB AX,CX
…
【例2-58】 对于例2-57,可以把ABC1的偏移量送给内存单元,通过内存单元实现段内间接转移,程序段如下所示:
VARDW ?;为存放标号ABC1的偏移量预留一个字类型内存变量
…
MOV VAR,OFFSET ABC1;把ABC1的偏移量送给变量VAR
JMP WORD PTRVAR;转向地址在VAR变量中
ADD AX,CX
…;距离超出+127字节,但仍在本段内
ABC1:SUB AX,CX
…
(4)段间直接转移。
格式:JMP FARPTRLABEL
功能:无条件转移到标号(LABEL)处。
说明:标号与JMP指令分别处在不同段中,使用段间直接寻址方式。
【例2-59】
CODE1 SEGMENT
…
JMP FARPTRABC2;无条件转移到CODE2段中的ABC2标号处
…
CODE1 ENDS
CODE2 SEGMENT
…
ABC2:SUB AX,BX
…
CODE2 ENDS
(5)段间间接转移。
格式:JMP DWORD PTRMEM
功能:实现无条件段间间接转移。
说明:使用段间间接寻址方式。
【例2-60】 对于例2-59,若把ABC2的双字长地址指针放在变量VAR2中,即可通过VAR2实现段间间接转移,程序段如下所示:
VAR2 DD ABC2;初始化ABC2的偏移量在VAR2中,段基址在VAR2+2中CODE1 SEGMENT
…
JMP DWORD PTRVAR2
;通过VAR2无条件转移到CODE2段的ABC2标号处
…
CODE1 ENDS
CODE2 SEGMENT
…
ABC2:SUB AX,CX
…
CODE2 ENDS
当然,变量VAR2的地址也可以通过寄存器间接寻址方式、基址变址寻址方式等存储器操作数寻址方式得到。
2.条件转移指令
8086/8088系统提供了多个条件转移指令,执行这类指令时通过检测由前边指令已设置的标志位确定是否转移,所以它们通常跟在影响标志的算术和逻辑运算指令之后。这类指令本身并不影响标志。
条件转移指令的通用汇编格式:Jx LABEL
功能:如果条件为真,则转向标号(LABEL)处,否则顺序执行下一条指令。
说明:其中x为条件,LABEL是要转向的标号。在8086/8088系统中,该地址应在与当前IP值的-128~+127范围内,即只能使用与转移地址有关的寻址方式的段内短转移格式,其位移量占用一个字节。
下面分组讨论条件转移指令。
(1)检测单个标志位实现转移的条件转移指令。这组指令根据一个标志位的设置情况决定是否转移,表2-10给出了指令的汇编格式、功能和测试条件。
表2-10 检测单个条件标志位转移指令
注:当能实现同一功能但指令助记符有两种形式时,在程序中究竟选用哪一种,这视习惯或用途而定。例如,对于指令JZ/JE LABEL,当比较两数相等转移时常使用JE,当比较某数为0转移时常使用JZ。
【例2-61】 比较AX和BX寄存器中的内容,若相等执行LP1,不等执行LP2。
方法1方法2
CMP AX,BX CMP AX,BX
JE LP1JNE LP2
LP2:…LP1:…
LP1:…LP2:…
(2)根据以实现两个带符号数的比较转移、两个带符号数的比较结果实现转移的条件转移指令。利用表2-11中提供的指令,可以实现两个有符号数的比较转移。
表2-11 根据两个带符号数的比较结果实现转移的条件转移指令
注:G=G reater,L=Less,E=Eq ual,N=Not。
显然,表2-10中的指令JZ/JE LABEL和JNZ/JNE LABEL同样可以用于两个带符号数的比较转移。当能实现同一功能但指令助记符有两种形式时,在程序中究竟选用哪一种,这视习惯或用途而定。
(3)根据两个无符号数的比较结果实现转移的条件转移指令。利用表2-12中提供的指令,可以实现两个无符号数的比较转移。
表2-12 根据两个无符号数的比较结果实现转移的条件转移指令
注:A=Above,B=Below,C=Carry,E=Eq ual,N=Not。可以看出,这里的高于相当于带符号数的大于,低于相当于带符号数的小于。
显然,表2-10中的指令JZ/JE LABEL和JNZ/JNE LABEL同样可以用于两个无符号数的比较转移。当能实现同一功能但指令助记符有两种形式时,在程序中究竟选用哪一种,这视习惯或用途而定。
【例2-62】 比较两个无符号数(在AX和BX中),把较大的数存放到AX中,把较小的数存放在BX中。
…
CMP AX,BX
JAE LP
XCHG AX,BX
LP:…
如果要比较两个数是有符号数,则程序段可改为:
…
CMP AX,BX
JG E LP
XCHG AX,BX
LP:…
同是一组数据,解释为带符号数和无符号数,其比较转移所选用的指令是不同的。对于无符号数,各部分均使用无符号数比较转移指令,对于带符号数,应使用有符号数比较转移指令。
3.测试CX值为0转移指令
这类指令不同于以上介绍的条件转移指令,因为它们测试的是CX寄存器的内容是否为0,而不是测试标志位。这类指令只能使用与转移地址有关的寻址方式的段内短转移格式,即位移量只能是8位的。
格式:JCXZ LABEL
功能:测试CX寄存器的内容,当CX=0时则转移,否则顺序执行。
注意:此指令经常用于在循环程序中判断循环计数的情况。
4.循环指令
循环指令可以控制程序的循环。对于Intel 80x86系列,所有循环指令的循环入口地址都只能在当前IP值的-128~+127范围内,即位移量只能是8位的,所以它们只能使用段内短转移格式。所有循环指令都用CX作为循环次数计数器,它们都不影响标志。
(1)循环指令LOOP。
格式:LOOP LABEL
功能:(CX)-1→CX,若(CX)≠0,则转向标号处执行循环体,否则顺序执行下一条指令。
注意:当初始化CX为0时,使用LOOP指令要循环64K次而不是0次。
(2)相等循环指令LOOPE/LOOPZ。
格式:LOOPE/LOOPZ LABEL
功能:(CX)-1→CX,若(CX)≠0and ZF=1,则转向标号处执行循环体,否则顺序执行下一条指令。
该指令常用于比较两个字符串是否相等的情况,前边的字符相等才有必要继续比较,否则终止比较。
(3)不等循环指令LOOPNE/LOOPNZ。
格式:LOOPNE/LOOPNZ LABEL
功能:(CX)-1→CX,若(CX)≠0and ZF=0,则转向标号处执行循环体,否则顺序执行下一条指令。
在LOOPNE或LOOPNZ指令前,应先把循环计数的初始值送给CX。该指令对在数据块中查找信息很有用,当未找到指定字符时继续查找,找到时退出。
5.子程序调用与返回指令
以下介绍8086/8088系统的子程序调用与返回指令,它完全适用于80386及以上CPU的实模式环境。子程序调用指令的详细介绍和使用,请参考第4章的内容。
(1)子程序调用指令CALL。
格式:CALLDST
功能:调用子程序。执行时先把返回地址压入堆栈,再形成子程序入口地址,最后把控制权交给子程序。
注意:其中DST为子程序名或子程序入口地址,其目标地址的形成与JMP指令类似,可以有段内直接/间接调用、段间直接/间接调用之分,只是不能使用段内直接寻址方式的SHORT格式。CALL指令的执行结果也是无条件转到标号处,但它与JMP指令的不同之处在于:前者转移后要返回,所以要保存返回地址;而后者转移后不再返回,所以不必保存返回地址。段内子程序调用指令实现同一段内的子程序调用,它只改变IP值,不改变CS值。段间子程序调用指令可以实现段间调用,执行时既改变IP值,也改变CS值。
①段内直接调用。
格式:CALLPRO1
或 CALLNEARPTRPRO1
功能:调用PRO1子程序。执行时先把当前IP值(返回地址)压入堆栈,再使IP=(IP)+DISP16,最后把控制权交给子程序。
说明:这种指令使用段内直接寻址方式。
【例2-63】 设子程序PRO1与CALL指令在同一段内,则调用PRO1子程序的指令是:
CALLPRO1
或 CALLNEARPRO1
②段内间接调用。
格式:CALLREG/MEM
功能:调用子程序。执行时先把当前IP值(返回地址)压入堆栈,再把指令指定的16位通用寄存器或内存单元的内容送给IP,最后把控制权交给子程序。
说明:这种指令使用段内间接寻址方式,指令指定的通用寄存器或内存单元中存放段内偏移量。
【例2-64】 可以把子程序入口地址的偏移量送给通用寄存器或内存单元,通过它们实现段内间接调用。
CALLBX;子程序入口地址的偏移量在BX寄存器中
CALLWORD PTR[BX];子程序入口地址的偏移量在数据段的BX所指向;的内存单元中
CALLWORD PTR[BX][SI];子程序入口地址的偏移量在数据段的BX+SI所;指向的内存单元中
③段间直接调用。
格式:CALLFARPTRPRO1
功能:调用PRO1子程序。执行时先把返回地址(当前IP值和当前CS值)压入堆栈,再把指令中的偏移量部分送给IP,段基址部分送给CS,最后把控制权交给子程序。
说明:这种指令使用段间直接寻址方式。
【例2-65】 设子程序PRO1与CALL指令不在同一段内,则段间直接调用PRO1子程序的指令是:
CALLFARPTRPRO1
④段间间接调用。
格式:CALLMEM
功能:调用子程序。执行时先把返回地址(当前IP值和当前CS值)压入堆栈,再把MEM的低字送给IP,高字送给CS,最后把控制权交给子程序。
说明:这种指令使用段间间接寻址方式,其中MEM为内存的双字长地址指针,低字部分为16位的偏移量,高字部分为段基址。
【例2-66】 对于例2-65,若子程序PRO1的入口地址(偏移量和段基址)放在变量VAR中,即可通过VAR实现段间间接调用,指令如下所示:
CALLDWORD PTRV AR;从V AR变量中得到子程序PRO1的入口地址实现
;调用
当然,变量VAR的地址也可以通过寄存器间接寻址方式、基址变址寻址方式等存储器操作数寻址方式得到。
【例2-67】 CALLDWORD PTR8[BX][SI]
(2)子程序返回指令RET。
执行这组指令可以返回到被调用处。有两条返回指令,它们都不影响标志。
格式:RET
功能:按照CALL指令入栈的逆序,从栈顶弹出返回地址(弹出一个字到IP,若子程序是FAR型还需再弹出一个字到CS),然后返回到主程序继续执行。
注意:无论子程序是NEAR型还是FAR型,返回指令的汇编格式总是用RET表示。但经汇编后会产生不同的机器码。在DEBUG中,段间返回指令被反汇编成RETF。
6.中断调用指令与中断返回指令
中断就是使计算机暂时挂起正在执行的进程而转去处理某种事件,处理完后再恢复执行原进程的过程。对某事件的处理实际上就是去执行一段例行程序,该程序被称为中断处理例行程序或中断处理子程序,简称为中断子程序。实模式下的中断处理方式和8086处理器相同。在8086/8088系统中,中断分为内中断(或称软中断)和外中断(或称硬中断),本节只介绍内中断的中断调用指令,其他内容在第5章中详细介绍。
(1)中断调用指令INT。
中断调用指令是程序员根据需要在程序的适当位置安排的,完全受程序控制,不像外中断那样随机产生。
格式:INT n;n为中断类型号。
功能:中断当前正在执行的程序,把当前的FLAG S、CS、IP值依次压入堆栈(保护断点),并从中断向量表的4n处取n类中断向量中断处理子程序入口地址,其中(4n)→IP,(4n+2)→CS,转去执行中断处理子程序。
【例2-68】 INT 21H
21H为系统功能调用中断,执行时把当前的FLAG S、CS、IP值依次压入堆栈,并从中断向量表的84H处取出21H类中断向量,其中(84H)→IP,(86H)→CS,转去执行中断处理子程序。
【例2-69】 INT 03H;断点中断
(2)中断返回指令IRET。
当从中断处理子程序返回时要使用中断返回指令,中断返回指令应放在中断处理子程序的末尾。
格式:IRET
功能:从栈顶弹出3个字分别送入IP、CS、FLAG S寄存器(按中断调用时的逆序恢复断点),控制返回到原断点继续执行。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。