资源描述
第5章结构化程序设计,5.1 子程序的概念 5.2 子程序的定义、调用和返回 * 5.3 子程序的现场保护与参数传递* 5.4 子程序设计 * 5.5 子程序的嵌套与递归调用 5.6 宏汇编程序设计,教学基本内容,5.1 子程序的概念,1、子程序:在程序设计中,我们会发现一些多次无规律重复的程序段或语句序列。解决此类问题一个行之有效的方法就是将它们设计成可供反复调用的独立的子程序结构,以便在需要时调用。在汇编语言中,子程序又称过程。 过程(子程序):是指功能相对独立的一段程序。 主程序和子程序间的关系:调用子程序的程序称为主调程序或主程序,被调用的程序称为子程序。,、程序中使用子程序的好处 子程序作为一个功能性模块,供一个程序甚至多个程序使用: 可以简化源程序结构; 提高程序的可读性与可维护性; 有利于代码复用; 提高程序的设计效率。,1、子程序的定义:由子程序定义伪指令PROC和ENDP来完成。其格式如下: 子程序名 PROC NEAR/FAR ;过程体 子程序名 ENDP 解释:(1)子程序名是子程序入口地址的符号表示。同标号一样,具有三种属性,即段属性、偏移地址属性以及类型属性。 (2)PROC表示子程序定义开始,ENDP表示子程序定义结束。 (3) NEAR/FAR 2、子程序的调用和返回 过程定义后,可在主程序中用CALL指令,反复调用。 过程结束,由返回指令RET返回主程序。,演示,5.2 子程序的定义、调用和返回,主程序与子程序, CALL 过程名 ,主程序, RET,子程序,回到CALL指令后的指令处返回地址,5.2 子程序的定义、调用和返回(续),子程序调用指令(CALL),CALL指令分成4种类型 CALL子程序名 ;段内直接调用 CALL r16/m16 ;段内间接调用 CALL far ptr子程序 ;段间直接调用 CALL far ptr mem ;段间间接调用 CALL指令需要保存返回地址: 段内调用偏移地址IP入栈 SPSP2,SS:SPIP 段间调用偏移地址IP和段地址CS入栈 SPSP2,SS:SPCS SPSP2,SS:SPIP,5.2 子程序的定义、调用和返回(续),子程序返回指令(RET),RETn 功能:弹出CALL指令压入堆栈的返回地址 段内返回偏移地址IP出栈 IPSS:SP, SPSP2 段间返回偏移地址IP和段地址CS出栈 IPSS:SP,SPSP2 CSSS:SP,SPSP2,注意:在使用CALL和RET时,要保证过程的正确调用和返回 说明: RET可以带参数,子程序返回指令(RET),放在子程序的末尾,它使子程序在执行完任务后将堆栈中的断点弹出,控制程序返回主程序继续执行被打断的程序。而返回地址(断点)就是子程序调用时入栈保护的断点地址IP(段内调用)或IP和CS值(段间调用)。通常,RET指令的类型是隐含的,它自动与子程序定义时的类型相匹配,如果是段内,返回时将栈顶的一个字弹给IP寄存器;如为段间,返回时先从栈顶弹出一个字给IP,接着再弹出一个字给CS。但是,当采用间接调用时,必须注意:保证CALL指令的类型与过程中RET指令的类型匹配,以免发生错误。例如CALL WORD PTRBX只能是段内调用,而CALL DWORD PTRBX能够调用一个远过程(段间调用),这样RET才能够识别返回类型。,例: NEAR 类型子程序 code SEGMENT ;code段 CALL subp ;调用指令 ,xor指令的地址入栈 xor ax,ax mov ah,4ch int 21h subp PROC NEAR ;子程序定义 RET ;返回 subp ENDP code ENDS END START,、子程序的书写形式,1多处调用完成同一功能的子程序: code SEGMENT start: CALL sub CALL sub MOV AH, 4CH INT 21H sub PROC 、 、 RET sub ENDP code ENDS end start,2模块化程序设计:多个子程序的调用 code SEGMENT begin: CALL sub1 CALL sub2 CALL sub3 MOV AH, 4CH INT 21H sub1 PROC 、 RET sub1 ENDP sub2 PROC 、 RET sub2 ENDP sub3 PROC 、 RET sub3 ENDP code ENDS END begin,子程序结构示例,子程序的位置通常在主程序的所有可执行指令之前或之后,不能放在主程序的可执行指令序列内部,否则会破坏主程序结构,5.3子程序的现场保护与参数传递,PROGPROC PUSHAX PUSHBX PUSHCX;保护现场 PUSHDX POPDX POPCX POPBX;恢复现场 POPAX RET ;返回断点处 PROGENDP,例如:若子程序PROG中改变了寄存器AX,BX,CX,DX的值,则可采用此方法保护和恢复现场。,一、信息的保护与恢复,二、主程序与过程的参数传递方式,主程序子程序的参数传递: 入口参数 也称入口条件,是指主程序调用子程序前,为子程序内部数据处理准备所需的预置值; 出口参数 也称出口条件,是子程序返回主程序后,把子程序处理的结果传递给主程序的数据。 参数传递的基本方法有: (1)寄存器法:通过CPU寄存器传递参数。传递数据方便、快捷,但所能传递的数据长度和个数都有限。 (2)变量法:通过内存单元(组)传递参数。传递数据的长度和个数可不受限制,程序设计比较灵活。 (3)堆栈法:通过堆栈传递参数。用堆栈保存所要传递的数据或存储地址,利用堆栈数据存取的特点,是常用的参数传递方法。,例5-1:分别用三种参数传递方法编写求12的和的程序。要求将结果送到内存单元,并显示。,DATA SEGMENT SUM DB 0 DATA ENDS STACK SEGMENT DB 100 DUP(?) STACK ENDS CODE SEGMENT ASSUME DS:DATA,SS:STACK,CS:CODE START: MOV AX,DATA MOV DS,AX MOV AL, 1 MOV BL, 2 CALL subprog mov ah,4cH int 21h CODE ENDS END START,Subprog PROC ADD AL, BL OR AL, 30H MOV SUM, AL Mov dl,al Mov ah,2 Int 21h RET sub ENDP,通过寄存器传送,通过变量传送,DATA SEGMENT SUM DB 0 D1 DB ? D2 DB ? DATA ENDS STACK SEGMENT DB 100 DUP(?) STACK ENDS CODE SEGMENT ASSUME DS:DATA,SS:STACK,CS:CODE START: MOV AX,DATA MOV DS,AX MOV D1, 1 MOV D2, 2 CALL SPROG MOV AH,4ch INT 21H CODE ENDS END START,SPROG PROC MOV AL, D1 ADD AL, D2 OR AL, 30H MOV SUM, AL MOV dl,al MOV ah,2 INT 21h RET SPROG ENDP END START,SprPROC PUSH BP MOV BP, SP MOV AX, BP+6 MOV BX, BP+4 ADD AL, BL OR AL, 30H MOV DL,AL MOV AH,2 INT 21H MOV SUM, AL POP BP RET Spr ENDP,DATA SEGMENT SUM DB 0 DATA ENDS STACK SEGMENT DB 100 DUP(?) STACK ENDS CODE SEGMENT ASSUME DS:DATA,SS:STACK,CS:CODE START: MOV AX,DATA MOV DS,AX MOV AL, 1 MOV BL, 2 MOV AH,0 MOV BH,0 PUSH AX PUSH BX CALL SPR POP BX POP AX MOV AH,4CH INT 21H CODE ENDS END START,通过堆栈功能最强/最灵活/最复杂,例5-2:多字节数相加的程序(寄存器传递过程参数),data segment num1 db 01h,02h,03h,04h,05h,06h num2 db 0ffh,07h,0ffh,03h,01h,06h len equ $- num2 data ends stack segment stack dw 100 dup(?) stack ends code segment assume cs:code,ds:data,ss:stack,Start: mov ax,data mov ds,ax lea si,num1 lea di,num2 mov cx,len call mpadd mov ah,4ch int 21h,Mpadd proc push ax push cx push si push di jcxz exit clc Next: mov al,di adc si,al inc si inc di loop next Exit: pop di pop si pop cx pop ax ret Mpadd endp Code ends end start,例5-3多字数相加的程序(堆栈传递子程序参数)RET指令中参数的应用例子,data segment va11 dw 9898h,7676h,5454h,3232h ;被加数 va12 dw 9898h,7676h,5454h,3232h ;加数 buffer dw 4 dup(?) ;和 len equ $-buffer data ends stack segment stack dw 100 dup(?) stack ends code segment assume cs:code,ds:data,ss:stack,es:data,;主程序为过程 MAIN PROC FAR push ds xor ax,ax push ax mov ax,data mov ds,ax mov es,ax mov ax,len/2 push ax mov ax,offset buffer push ax mov ax,offset va11 push ax mov ax,offset va12 push ax,Call add64 mov ah,4ch int 21h main endp ;主程序结束,;子过程ADD64开始 add64 proc push bp ; mov bp,sp push bx push si push di push cx pushf ;保存现场 mov si,bp+4; mov bx,bp+6 mov di,bp+8 mov cx,bp+10 ;读取参数 cld,clc again: lodsw adc ax,bx stosw inc bx inc bx loop again popf ; pop cx pop di pop si pop bx pop bp ;恢复现场 ret 8 ;返回调用点,并废除4个参数共8字节 add64 endp ;子过程结束 code ends end main,在子程序设计中 要明确地定义出这个子程序的入口参数和出口参数,使调用者能方便地使用子程序。 在子程序中要合理地保存主程序和子程序都用到的寄存器和存储单元,以使主程序能正确地运行。,子程序是提高程序设计效率的良好手段,也为模块化设计提供了很好的基础。,总结,总结,注意:前面所介绍的三种传递参数的方法,并不是固定不变的。即它们是可以综合使用的。要按照实现的需要和具体情况的不同,可以使用其中一种方式,也可以同时使用几种混合的方式,有的时候还可能并不需要参数的传递。 总之,调用程序和子程序之间的参数传递,的确十分重要,参数不宜使用太多。参数选择的适当,可以简化程序设计。 参数确定之后,就是如何进行传递的问题。无论采用哪一种传递方式,都应该保证正确无误的传递。这是调用程序和子程序的协调关系。这也是在子程序设计中需要认真解决的问题。,
展开阅读全文