8086指令系统---控制转移指令(二)
这一组指令在循环结构的程序中用来控制一段程序(称为循环体)的重复执行,在汇编指令中循环的转向地址用标号来表示,而在机器指令中给出的是位移量,所以执行循环指令时,若满足循环条件,CPU就计算转向地址:(IP)当前+8位位移量→(IP),即实现循环。
若不满足循环条件,即退出循环,程序继续顺序执行。
循环指令都是短转移格式的指令,也就是说,位移量是用8位带符号数来表示的,转向地址在相对于当前IP值的-128 ~ +127字节范围之内。
对条件循环指令LOOPZ(LOOPE)和LOOPNZ(LOOPNE),除测试CX中的循环次数外,还将ZF的值作为循环的必要条件,因此,要注意将条件循环指令紧接在形成ZF的指令之后。
在多重循环的程序结构中,如果各层循环都使用循环指令来控制,则应注意对CX中循环计数值的保存与恢复。
循环指令均不影响条件码。
LOOP label 循环(loop)
执行操作:① (CX)←(CX)-1
② 若(CX)≠0,则(IP)←(IP)当前+位移量,否则循环结束
LOOPZ/LOOPE label 为零/相等时循环(loop while zero,or equal)
执行操作:① (CX)←(CX)-1
② 若ZF=1且(CX)≠0,则(IP)←(IP)当前+位移量,否则循环结束
LOOPNZ/LOOPNE label 不为零/不等时循环(loop while nonzero,or not equal)
执行操作:① (CX)←(CX)-1
② 若ZF=0且(CX)≠0,则(IP)←(IP)当前+位移量,否则循环结束
DATA SEGMENT
BLOCK1 DW 100 DUP(?)
BLOCK2 DW 100 DUP(?)
DATA ENDS
; - - - - - - - - - - - - - - - - - -
CODE SEGMENT
ASSUME CS:CODE,DS:DATA,ES:DATA
START:MOV AX,DATA
MOV DS,AX ; initialize the data segment
MOV ES,AX ; initialize the extra segment
CLD ; DF=0 for autoincrement
MOV CX,100 ; load the counter
MOV SI,OFFSET BLOCK1 ; address of block1
MOV DI,OFFSET BLOCK2 ; address of block2
NEXT: LODSW ; load the data of block1 into AX
ADD AX,ES:[DI] ; add the data of block2 to AX
STOSW ; store the sum to block2
LOOP NEXT ; repeat 100 times
MOV AX,4C00H ; return to DOS
INT 21H
CODE ENDS
END START
子程序是一种非常重要的计算机编程结构,它存储在存储器中,可供一个或多个调用程序(主程序)反复调用。主程序调用子程序时使用CALL指令,由子程序返回主程序时使用RET指令。由于调用程序和子程序可以在同一个代码段中,也可以在不同的代码段中,因此,CALL指令和RET指令也有近调用、近返回及远调用、远返回两类格式。
⑴ CALL NEAR PTR SUBPROUT 近调用(near call)
近调用是CALL指令的缺省格式,可以写为"CALL subrotine"。它调用同一个代码段内的子程序(子过程),因此,在调用过程中不用改变CS的值,只需将子程序的地址存入IP寄存器。CALL指令中的调用地址可以用直接和间接两种寻址方式表示。
⑵ CALL FAR PTR SUBPROUT 远调用(far call)
远调用适用于调用程序(也称为主程序)和子程序不在同一段中的情况,所以也叫做段间调用。和近调用指令一样,远调用指令中的寻址方式也可用直接方式和间接方式。
⑶ RET 返回指令(return)
RET指令执行的操作是把保存在堆栈中的返回地址出栈,以完成从子程序返回到调用程序的功能。
● CALL SUBROUT 段内直接调用
执行操作:① (SP) ← (SP)-2,((SP)) ← (IP)当前
② (IP) ← (IP)当前+16位位移量(在指令的第2、3个字节中)
● CALL DESTIN 段内间接调用
执行操作:① (SP) ← (SP)-2,((SP)) ← (IP)当前
② (IP) ← (EA) ; (EA)为指令寻址方式所确定的有效地址
● CALL FAR PTR SUBROUT 段间直接调用
执行操作:① (SP) ← (SP)-2,((SP)) ← (CS)当前
(SP) ← (SP)-2,((SP)) ← (IP)当前
② (IP) ← 偏移地址(在指令的第2、3个字节中)
(CS) ← 段地址(在指令的第4、5个字节中)
● CALL WORD PTR DESTIN 段间间接调用
执行操作:① (SP) ← (SP)-2,((SP)) ← (CS)当前
(SP) ← (SP)-2,((SP)) ← (IP)当前
② (IP) ← (EA) ; (EA)为指令寻址方式所确定的有效地址
(CS) ← (EA+2)
从CALL指令执行的操作可以看出,第一步是把子程序返回调用程序的地址保存在堆栈中。对段内调用,只需将IP的当前值,即CALL指令的下一条指令的地址存入SP所指示的堆栈字单元中。对段间调用,保存返回地址则意味着要将CS和IP的当前值分别存入堆栈的两个字单元中。
CALL指令的第二步操作是转子程序,即把子程序的入口地址交给IP(段内调用)或CS:IP(段间调用)。对段内直接方式,调转的位移量,即子程序的入口地址和返回地址之间的差值就在机器指令的2、3字节中。对段间直接方式,子程序的偏移地址和段地址就在操作码之后的两个字中。对间接方式,子程序的入口地址就从寻址方式所确定的有效地址中获得。
● RET 段内返回(近返回)
执行操作:(IP) ← ((SP)),(SP) ← (SP)+2
● RET 段间返回(远返回)
执行操作:(IP) ← ((SP)),(SP) ← (SP)+2
(CS) ←((SP)),(SP) ← (SP)+2
● RET N 带立即数返回
执行操作:① 返回地址出栈(操作同段内或段间返回)
② 修改堆栈指针:(SP) ← (SP)+N
子程序的最后一条指令必须是RET指令,以返回到主程序。如果是段内返回,只需把保存在堆栈中的偏移地址出栈存入IP即可,如果是段间返回,则要把偏移地址和段地址都从堆栈中取出送到IP和CS寄存器中。
带立即数返回指令,除完成偏移地址出栈或偏移地址和段地址出栈的操作外,还要再使SP的内容加上一个立即数N,使堆栈指针SP移动到新的位置。指令中的N可以是一个常数,也可以是一个表达式。带立即数返回指令适用于C或PASCAL的调用规则,这些规则在调用过程(子程序)前先把参数压入堆栈,子程序使用这些参数后,如果在返回时丢弃这些已无用的参数,就在RET指令中包含一个数字,它表示压入到堆栈中参数的字节数,这样堆栈指针就恢复到参数入栈前的值。
CALL指令和RET指令都不影响条件码。
0000 B8 001E MOV AX,30
0003 BB 0028 MOV BX,40
0006 50 PUSH AX ; push data1 into stack
0007 53 PUSH BX ; push data2 into stack
0008 E8 0066 CALL ADDM ; call subroutine
000B B4 02 MOV AH,2
… … …
0071 ADDM PROC NEAR ; entry point (IP)←0071=000b+0066
0071 55 PUSH BP ; save BP
0072 8B E4 MOV BP,SP ; addressing the stack with BP
0074 8B 46 04 MOV AX,[BP+4] ; get data2 from stack
0077 03 46 06 ADD AX,[BP+6] ; add data1
007A CD POP BP ; get back BP
007B C2 0004 RET 4 ; return and revert SP
007E ADDM ENDP
8086指令系统控制转移指 相关文章:
- 8086指令系统---控制转移指令(一)(11-27)
- Windows CE 进程、线程和内存管理(11-09)
- RedHatLinux新手入门教程(5)(11-12)
- uClinux介绍(11-09)
- openwebmailV1.60安装教学(11-12)
- Linux嵌入式系统开发平台选型探讨(11-09)