循环指令,子程序指令 一、循环是一种特殊的转移流程,当满足(或不满足)某条件时,反复执行一系列操作,直到不满足(或满足)条件为止。循环流成的条件一般都是循环计数,在程序中用循环计数来控制循环次数。 LOOP LABEL ;CX←CX-1;若CX≠0,循环:IP←IP+位移量;否则,顺序执行 LOOPZ/LOOPE LABEL ;CX←CX-1;若CX≠0且ZF=1,循环:IP←IP+位移量;否则,顺序执行 LOOPNZ/LOOPNE LABEL ;CX←CX-1;若CX≠0且ZF=0,循环:IP←IP+位移量;否则,顺序执行 LOOP指令首先将计数值CX减1,然后判断计数值CX是否为零。CX不为0,则继续执行循环体内的指令;CX等于0,表示循环结束,于是程序退出循环,顺序执行后面的指令。LOOPZ和LOOPNZ指令中油要求同时ZF为1或0才能循环,用于判断结果是否为零或相等,以便提前结束循环。循环指令中的操作数LABEL采用相对寻址方式,表示循环的目标地址,是一个8位位移量。循环指令不影响标志。 例17-1:在字节数组中找出第一个非零元素,并显示输出第一个非零元素的下标。 NAME LI17-1.ASM DATA SEGMENT ARRAY DB 0,0,0,7,0,0,4,34,25,30 COUNT EQU $-OFFSET ARRAY DATA ENDS CODE SEGMENT ASSUME CS:CODE,DS:DATA BEGIN: MOV AX,DATA MOV DS,AX MOV CX,COUNT MOV DI,0FFFFH NEXT: INC DI CMP ARRAY[DI],0 LOOPZ NEXT JNE OKENTRY MOV DL,'0' JMP DISPLAY OKENTRY:MOV DX,DI OR DL,30H DISPLAY:MOV AH,02H INT 21H MOV AH,4CH INT 21H CODE ENDS END BEGIN 例17-2:求两个一维字数组的和,数组元素个数为N,当计算到两个数组对应元素之和为零时就停止求和(假设数组元素为无符号二进制数)。 NAME LI17-2.ASM DATA SEGMENT ARRAY1 DW 23,34,4,5,66,76,0,345,567,23,12,67 ARRAY2 DW 34,24,3,2,44,79,0,345,56,43,21,567 N EQU $-OFFSET ARRAY2 SUM DW 15 DUP(?) DATA ENDS CODE SEGMENT ASSUME DS:DATA,CS:CODE BEGIN: MOV AX,DATA MOV DS,AX MOV AX,0 MOV SI,0FFFEH MOV CX,N SHR CX,1 NOZERO: INC SI INC SI MOV AX,ARRAY1[SI] ADD AX,ARRAY2[SI] MOV SUM[SI],AX LOOPNZ NOZERO JNZ L MOV DL,'Y' JMP Q L: MOV DL,'N' Q: MOV AH,02H INT 21H MOV AH,4CH INT 21H CODE ENDS END BEGIN 二、子程序指令 程序中有些部分可能要实现相同的功能,而且这些功能需要用到,用子程序实现这个功能是很适合的。子程序通常是与主程序分开的、完成特定功能的一段程序。当主程序(调用程序)需要执行这个功能时,就可以调用该子程序(被调用程序);于是,程序转移到这个子程序的起始处执行。当运行完子程序后,再返回调用它的主程序。子程序由主程序执行子程序调用指令CALL来调用;而子程序执行完后用子程序返回指令RET,返回主程序继续执行。CALL和RET指令均不影响标志位。 1、子程序调用指令CALL CALL指令用在主程序中,实现子程序的调用。子程序和主程序可以在同一个代码段内,也可以在不同段内。类似无条件转移JMP指令,子程序调用CALL指令也可以分成段内调用(近调用)和段间调用(远调用);同时,CALL目标地址也可以采用直接寻址或间接寻址方式。但是,子程序执行结束时要返回的,所以,CALL指令不仅要同JMP指令一样改变CS:IP以实现转移,而且还要保留下一条要执行指令的地址,以便返回时重新获取它。保护CS:IP值的方法是压入堆栈,获取CS:IP值的方法就是弹出堆栈。 CALL指令的4种格式: CALL LABEL ;段内调用,直接寻址:SP←SP-2,SS:[SP]←IP,IP←IP+16位位移量 CALL R16/M16 ;段内调用,间接寻址:SP←SP-2,SS:[SP]←IP,IP←R16/M16 CALL FAR PTR LABEL ;段间调用,直接寻址:SP←SP-2,SS:[SP]←CS,SP←SP-2,SS:[SP]←IP ;IP←LABEL偏移地址,CS←LABEL段地址 CALL FAR PTR MEM ;段间调用,间接寻址:SP←SP-2,SS:[SP]←CS,SP←SP-2,SS:[SP]←IP ;IP←[MEM],CS←[MEM+2] 根据过程伪指令,汇编程序可以自动确定是段内还是段间调用,同时可以采用NEAR或FAR伪指令强制成为近调用或远调用。 2、子程序返回指令RET 子程序执行完后,应返回主程序中继续执行,这一功能由RET指令完成。要回到主程序,只要能获得离开主程序时,由CALL指令保存于堆栈的指令地址即可。根据子程序与主程序是否同处于一个段内,返回指令分为段内返回和段间返回。 RET指令的4种格式如下: RET ;无参数段内返回:IP←SS:[SP],SP←SP+2 RET I16 ;有参数段内返回:IP←SS:[SP],SP←SP+2,SP←SP+I16 RET ;无参数段间返回:IP←SS:[SP],SP←SP+2,CS←SS:[SP],SP←SP+2 RET I16 ;有参数段间返回:IP←SS:[SP],SP←SP+2,CS←SS:[SP],SP←SP+2,SP←SP+I16 尽管段内返回和段间返回具有相同的汇编助记符,但汇编程序会自动产生不同的指令代码;也可以分别采用RETN和RETF表示段内和段间返回。返回指令还可以带有一个立即数I16,则堆栈指针SP将增加,即SP←SP+I16。这个特点使得程序可以方便的废除若干执行CALL指令以前入栈的参数。
|