3 循环指令
这一组指令在循环结构的程序中用来操控一段程序(称为循环体)的重复履行,在汇编指令中循环的转向地址用标号来表明,而在机器指令中给出的是位移量,所以履行循环指令时,若满意循环条件,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
如图3.12所示,主程序中的两条PUSH指令将数据30和40压入仓库,CALL指令履行后,回来地址000B又压入仓库,紧接着程序操控搬运到子程序ADDM。子程序中的PUSH指令又使BP的值进栈,此刻SP指向栈顶0FFA。MOV指令将0FFA传送给BP,使BP作为寻址仓库数据的指针。(BP+4)指向的是40,(BP+6)指向的是30,取出数据后用POP指令康复了BP原先的值,此刻,(SP)=0FFC,这是RET 4指令履行前的仓库状况。