程序控制指令能控制程序执行的流程,可构成转移、循环、调用和返回。
转移指令分无条件转移和条件转移两类,它们本身执行后都不影响FL寄存器的状态标志。
(1)无条件转移指令。
无条件转移指令JMP<TARGET>,控制程序转移到TARGET(目标)标号处去执行。按TARGET目标地址单元的属性有4种转移范围、5种指令格式。
JMP SHORT<TARGET> ; 段内直接短转移
JMP NEAR PTR<TARGET> ; 段内直接近转移
JMP WORD PTR<TARGET> ; 段内间接转移
JMP FAR PTR<TARGET> ; 段间直接转移
JMP DWORD PTR(TARGET) ; 段间间接转移
段内转移只改变IP值,CS(代码段基址)不变。段内直接转移为相对寻址,将IP指向的(下条)指令距目标TARGET的偏移量(字节距离)与IP的当前值相加后送IP,其中,段内短转移的偏移量为8位,(以当前指令位置为准的)转移范围为129~-126(若以IP当前值为准则为127~-128);段内直接近转移的偏移量为16位、(以当前指令位置为准的)转移范围为32769~-32766(若以IP当前值为准则为32767~-32768);段内间接转移是绝对寻址,TARGET是一个16位的寄存器或存储器单元,将其内容直接赋给IP;段间转移跨段,段间直接转移将指令中给出的TARGET偏移量和段基址赋给IP和CS,段间间接转移将指令中给出的TARGET存储单元开始的4个字节分别赋给IP和CS,从而实现跨段转移。
无条件转移指令的汇编格式、语句操作、转移范围如表9-1所示。
表9-1 无条件转移指令
指令种类 |
格 式 |
操 作 |
转移范围 | |
段内直 接转移 |
短转移 |
JMP SHORT<TARGET> |
IP←IP+Disp8 |
-128~127 |
近转移 |
JMP NEAR PTR<TARGET> |
IP←IP+Disp16 |
-32768~32767① | |
段内间接转移 |
JMP<Reg16> |
IP←Reg16 |
当前代码段内 | |
JMP<Mem16> |
IP←Mem16 | |||
段间直接转移 |
JMP FOR PTR<TARGET> |
IP←TARGET偏移量 CS←TARGET段基址 |
跨段,整个内存 | |
段间间接转移 |
JMP DWORD PTR <TARGET> |
IP←(EA),CS←(EA十2) |
注:①在当前代码段内,<……>内为符号地址或寄存器或存储单元的具体内容。
(2)条件转移指令。
条件转移指令测试标志寄存器(FL)的当前状态标志,满足条件就转移到目标地址去执行,否则顺序向下执行。条件是对一个或两个或3个状态标志位的状态判断。
8086/8088所有条件转移指令都是段内直接短转移,转移的范围是-128~127个字节距离。指令长度为两个字节,第一字节为操作码,第二字节为距TARGET(目标)的8位偏移量。
条件转移指令的汇编格式、转移条件、应用场合、指令编码等如表9-2所示。
【例9-4】一个16位数的数组存储在以ARRAY为首地址的数据段中,长度为n(小于256)。编程求数组中正数、0及负数的个数,分别存放在BH、BL、DI中。
MOV CX,<n> ; 数组长度送计数器CX
MOV AX.0
MOV SI,AX ; 数组指针初始化为0
MOV BX,AX ; 结果寄存器清零
MOV DI.AL
AGAIN: CMP AX,ARRAY[SI] ; 比较
JG PLUS
Jz. ZERO
INC DI ; 是负数,负数个数寄存器加l
JMP NEXT
ZER0: INC BL ; 是0,0个数寄存器加1
JMP NEXT
PLUS: INc BH ; 是正数,正数个数寄存器加1
NEXT: ADD SI,2 ; 指向下一个数组元素
DEC CX
JNZ AGAIN ; 未结束则继续
表9-2 条件转移指令
判断 |
指令名称 |
汇编格式 |
转移条件 |
应用 |
等价助记符 |
单标志位判断 |
结果为零(相等)转移 |
JZ<TARGET> |
ZF=1 |
相等 比较 |
JE<TARGET> |
结果不为零(不相等)转移 |
JNZ<TARGET> |
ZF=0 |
JNE<TARGET> | ||
结果为负转移 |
JS<TARGET> |
SF=1 |
正负 判断 |
| |
结果为正转移 |
JNS<TARGET> |
SF=0 |
| ||
结果溢出转移 |
JO<TARGET> |
OF=1 |
溢出 判断 |
| |
结果不溢出转移 |
JNO<TARGET> |
OF=0 |
| ||
结果偶特性转移 |
JP<TARGET> |
PF=0 |
奇偶 判断 |
JPE<TARGET> | |
结果奇特性转移 |
JNP<TARGET> |
PF=1 |
JPO<TARGET> | ||
低于(不高于等于)转移 |
JB<TARGET> |
CF=1 |
无符 号数 比较 |
JNAE<TARGET> JC<TARGET> | |
不低于(高于等于)转移 |
JNB<TARGET> |
CF=0 |
JAE<TARGET> JNC<TARGET> | ||
双标志位判断 |
低于等于(不高于)转移 |
JBE<TARGET> |
CF∨ZF=1 |
有符 号数 比较 |
JNA<TARGET> |
不低于等于(高于)转移 |
JNBE<TARGET> |
CF∨ZF=0 |
JA<TARGET> | ||
小于(不大于等于)转移 |
JL<TARGET> |
SF xor 0F=1 |
JNGE<TARGET> | ||
不小于(大于等于)转移 |
JNL<TARGET> |
SF xor 0F=0 |
JGE<TARGET> | ||
三标志位判断 |
小于等于(不大于)转移 |
JLE<TARGET> |
(SF xor 0F) ∨ZF=1 |
JNG<TARGET> | |
不小于等于(大于)转移 |
JNLE<TARGET> |
(SF xor 0F) ∨ZF=0 |
JG<TARGET> |
注:<……>内为目标的具体符号地址名称为∨指or,∧指and。
从80386开始扩大了条件转移的范围,实模式下能够转移到代码段的任何位置。
循环程序非常普遍,用条件指令就可实现;为方便编程,基本指令集特意提供了4条循环指令。
循环指令放在循环程序的开头或结尾,通过测试CX是否为0以及ZF标志的状态来控制循环结束与否。循环指令长度都是两个字节,一个字节操作码加8位偏移量,循环转移范围为-128~127。循环指令执行不影响标志寄存器(FL)的状态标志。
循环指令的汇编格式、测试条件、语句操作如表9-3循环控制指令所示。
表9-3 循环控制指令
指令名称 |
汇编格式 |
测试条件 |
语句操作 |
循环 |
LOOP<TARGET> |
CX≠0 |
CX←CX-1, 满足条件则循环执行,否则顺序向下执行(退出循环) |
为零(相等)循环 |
LOOPZ<TARGET> LOOPE<TARGET> |
(ZF=1)∧(CX≠0) | |
不为零(不相等)循环 |
LOOPNZ<TARGET> LOOPNE<TARGET> |
(ZF=0)∧(CX≠0) | |
CX为零循环 |
JCXZ<TARGET> |
CX=0 |
同上,但CX不减1 |
注:<……>内为目标的具体符号地址名称。
【例9-5】一字符串长度(<256)存放在ASCII STR单元,ASCII STR单元之后接着存放该字符串。试编程判断该字符串中是否有“空格”字符,若有DI置1,否则DI清零。
MOV BX,0 ; 地址指针初始化为0
MOV DI,BL ; 预置没有“空格”标志
MOV AL,20H ; “空格”字符送AL
MOV CX,ASCIISTR[BX] ; 串长度送CX
NEXT: INC BX
CMP AL,ASCIISTR[BX] ; 判断是否为空格
LOOPNZ NEXT
JNZ D0NE
MOV DI,01H ; 置有“空格”标志
DONE:… ; 没有“空格”,继续运行
32位的80x86微处理器还可以使用ECX作为隐含的循环计数器。即便是80486,循环指令也只能在短距离内进行循环,即相对于当前循环指令的地址,转移的范围为-129~126。
子程序调用不同于转移指令,首先要把CALL指令的下一条指令地址(即返回地址)入栈保存,然后转向子程序的首地址去执行。调用分段内调用和段间调用。段内调用只对IP操作(CS不变),段间调用要同时对CS和IP操作。段内/段间调用又分直接调用和间接调用。调用和返回指令都没有条件判断。
调用和返回指令的汇编格式、语句操作如表9-4所示。
返回指令根据调用的距离属性自动执行段内返回或段间返回。带立即数返回指令废除栈顶若干个(偶数)字节单元,使堆栈指针SP向栈底(高地址方向)移动<Exp>个字节单元,常用于以堆栈传送参数的子程序调用。
表9-4 子程序调用和返回指令
指令名称 |
汇编格式 |
语句操作 |
段内直接调用 |
CALL<PROC> |
SP←SP-2,(SP)←IP,IP←IP+Disp16 |
段内间接调用 |
CALL<Reg16> CALL<Mem16> |
SP←SP-2,(SP)←IP,IP←Reg16 SP←SP-2,(SP)←IP,IP←Mem16 |
段间直接调用 |
CALL<PROC> |
SP←SP-2,(SP)←CS,SP←SP-2,(SP)←IP, IP←偏移地址,CS←段基址 |
段间间接调用 |
CALL<Mem16> |
SP←SP-2,(SP)←CS,SP←SP-2,(SP)←IP,IP←(EA),CS←(EA+2) |
段内返回 |
RET |
IP←(SP),SP←SP+2 |
段内带立即数返回 |
RET<Exp> |
IP←(SP),SP←SP+2,SP←SP+<Exp> |
段间返回 |
RET |
IP←(SP),SP←SP+2,CS←(SP),SP←SP+2 |
段间带立即数返回 |
RET<Exp> |
IP←(SP),SP←SP+2,CS←(SP),SP←SP+2,SP←SP+<Exp> |
注:<……>内为具体名称(过程名、寄存器、存储器或表达式,Exp表达式的值为16位无符号偶数。CALL指令格式中可加属性操作符,以指明或修改操作数的属性,参见JMP指令)。
CALL和RET指令都不影响标志寄存器(FL)的标志位。
中断指令(INT)是一种特殊的调用指令(调用例行中断子程序),是段间调用;与段间CALL指令相同的是须先把CS:IP入栈保存,不同的是还要首先把标志寄存器(FL)入栈保存。中断返回是段间返回,执行IRET指令,依次恢复IP、CS和FL。
INT和IRET指令汇编格式、语句操作如表9-5所示。
表9-5 中断指令和中断返回指令
指令名称 |
汇编格式 |
语句操作 |
中断指令调用 |
INT<Exp> |
SP←SP-2,[SP]←FL,SP←SP-2,[SP]←CS SP←SP-2,[SP]←IP IP←[n×4],CS←[n×4+2] |
断点中断调用 |
INT或INT 3 | |
溢出中断调用 |
INT 0或INT 4 | |
中断返回 |
IRET |
IP←[SP],SP←SP+2,CS←[SP],SP←SP+2, FL←[SP],SP←SP+2 |
注:<Exp>表达式结果是n,n为中断类型号1~255,不能为0。