资源目录
压缩包内文档预览:(预览前20页/共82页)
编号:21836146
类型:共享资源
大小:11.91MB
格式:ZIP
上传时间:2019-09-06
上传人:QQ24****1780
认证信息
个人认证
王**(实名认证)
浙江
IP属地:浙江
25
积分
- 关 键 词:
-
大学
汇编
语言程序设计
相伟
课件
ppt
- 资源描述:
-
大学汇编语言程序设计-相伟-课件PPT,大学,汇编,语言程序设计,相伟,课件,ppt
- 内容简介:
-
21世纪高等院校规划教材,汇编语言程序设计,相 伟 主编 徐小平 李珍香 副主编,中国水利水电出版社,第6章 宏功能设计和子程序设计,本章主要讲解宏功能与子程序的基本知识和设计方法。通过本章学习,读者应掌握以下内容: 宏定义与宏调用 子程序定义、调用与返回 主程序与子程序的参数传递 子程序设计方法,6.1 宏功能程序设计,在汇编语言源程序中,有时会多次用到这样的程序段,它们完成相同的功能,实现功能的语句或完全相同,或仅有某些变量差异,为了不重复书写这些程序段,提高编写程序的效率,可以使用宏汇编语言中的宏。 宏就是源程序中一段有独立功能的程序代码,它只需要在源程序中定义一次,就可以多次调用。调用时只需要用一个宏指令语句就可以。,6.1.1 宏定义与宏调用,宏指令的使用要经过以下三个步骤: (1)宏定义:即用一组伪指令将重复的语句序列定义成宏指令。定义宏指令时可以根据需要设置形式参数,并指定宏指令名。 (2)宏调用:在程序中需要的地方通过带实参的宏指令来调用宏定义。 (3)宏展开:由宏汇编程序用宏定义中的语句序列来代替宏调用中的宏名,用实参代替形参。 其中,前两步工作必须由用户自己完成,而第三步则 由宏汇编程序在汇编期间完成。, 宏定义,宏定义是用一组伪指令来实现的。 定义格式为: 宏指令名 MACRO 形式参数表 宏定义体 ENDM 其中,MACRO和ENDM是一对伪指令,分别表示宏定义的开始和 结束,中间是宏定义体,即一组有独立功能的程序代码。宏指令名 给出该宏定义的名称,调用时就使用宏指令名来调用该宏定义。宏 指令名的第一个字符必须是字母,后面可以跟字母、数字或下划线 等字符。形式参数表给出了宏定义用到的形式参数(简称形参), 参数与参数之间用逗号隔开。注意参数可以缺省。,例如,在某程序中,需要多次输出缓冲区中的字符串,这就要反复进行9号功能调用:, LEA DX , BUF1 MOV AH , 9 INT 21H LEA DX , BUF2 MOV AH , 9 INT 21H LEA DX , BUF3 MOV AH , 9 INT 21H ,上述三次功能调用语句格式完全相同,只是每次输出缓 冲区首址不同。为缩短程序行,简化程序设计,我们可 以将9号功能调用的过程定义成宏指令,将输出缓冲区首 址作为形式参数。定义格式如下: OUTPUT MACRO A LEA DX , A MOV AH , 9 INT 21H ENDM 其中OUTPUT是宏指令名,A是形式参数。, 宏调用,经宏定义定义后的宏指令就可以在源程序中调用。 调用格式:宏指令名 实在参数表 其中,宏指令名必须与宏定义中的宏指令名一致,实在参 数表中的参数与宏定义中的形式参数表中的参数按位置关 系一一对应。实在参数简称实参。 按此格式,对前面宏定义的三次调用可写成下面形式: OUTPUT BUF1 OUTPUT BUF2 OUTPUT BUF3 , 宏展开,当源程序被汇编时,汇编程序将对每个宏调用进行宏展开。宏展开就是用宏定义体取代源程序中的宏指令名,并用实参取代宏定义中的形参。在取代时,实参和形参是一一对应的。另外,展开后所得到的语句应该是有效的,否则汇编程序将指示出错。,对于上面的三次宏调用,展开后形式为:, + LEA DX , BUF1 + MOV AH , 9 + INT 21H + LEA DX , BUF2 + MOV AH , 9 + INT 21H + LEA DX , BUF3 + MOV AH , 9 + INT 21H 注意在汇编列表文件中,宏展开后留下的宏体语句在每行的第31列用符号“+”标志。 下面我们再用一个例子来说明宏定义、宏调用和宏展开的情况。,例6-1:用宏指令定义两个字操作数相乘。,源程序如下: 宏定义 MULTY MACRO OPR1,OPR2,RESULT PUSH DX PUSH AX MOV AX,OPR1 IMUL OPR2 MOV RESULT,AX MOV RESULT+2,DX POP AX POP DX ENDM 宏调用 MULTY BX,CX,BUF1 MULTY N1,N2,BUF2,宏展开 + PUSH DX + PUSH AX + MOV AX,BX + IMUL CX + MOV BUF1,AX + MOV BUF1+2,DX + POP AX + POP DX + PUSH DX + PUSH AX + MOV AX,N1 + IMUL N2 + MOV BUF2,AX + MOV BUF2+2,DX + POP AX + POP DX 以上两次宏调用中,第一次用BX、CX寄存器存放实参,第二次用N1、N2两个存储单元存放实参,都可以正确地进行参数传递。这大大增加了宏汇编程序设计的灵活性。,6.1.2 宏库的建立与使用, 宏库的建立 例如,建立一个宏库文件INOUT.LIB,该库文件中包含以下内容: INPUT MACRO A LEA DX , A MOV AH , 10 ;10号系统功能调用,接收键盘输入字 INT 21H ENDM OUTPUT1 MACRO A LEA DX , A MOV AH , 9 ; 9号系统功能调用,输出DS:DX指向 INT 21H ;以$结尾的字符串 ENDM OUTPUT2 MACRO A MOV DL , A MOV AH , 2 ;2号系统功能调用,显示DL中的字符 INT 21H ENDM RETURN MACRO MOV AH , 2 MOV DL , 0AH INT 21H ;输出回车换行 MOV DL , 0DH INT 21H ENDM, 宏库的使用,当程序中需要调用宏时,应首先将宏库文件加入到源文件中,然后按宏库中各宏定义的格式调用即可。 将宏库加入源文件一起进行汇编可用伪指令INCLUDE实现。 指令格式:INCLUDE 宏库文件名 功能:将指定的宏库文件从本行起加入汇编,直到该宏库文件的最后一行汇编完后,再继续汇编INCLUDE后面的语句。 下面我们通过具体例子介绍宏库的调用方法。,例6-2:从键盘输入一串字符到BUF缓冲区,试编程将其中大写字母转换成小写字母(其余字符不变)后在显示器上输出。,分析:本例首先进行9号功能调用输出一行提示信息,提示用户从键盘输入一串字符,然后进行10号调用接收用户从键盘输入的一串字符到BUF缓冲区,将缓冲区中的字符判断处理完后,进行2号调用将转换后的字符(如不是大写字母,则不转换)逐个输出。为了保持屏幕格式清晰,在输出时适当加入回车换行。所有这些操作均可调用宏库文件INOUT.LIB中的宏定义INPUT、OUTPUT1、OUTPUT2、RETURN实现。以下源程序中直接将宏库文件INOUT.LIB加入,库文件内容见前面介绍。,源程序如下:,INCLUDE INOUT.LIB ;将宏库文件加入源文件一起汇编 STACK SEGMENT STACK DB 200 DUP(?) STACK ENDS DATA SEGMENT INPUT1 DB PLEASE INPUT A STRING : $ BUF DB 80 DB 0 DB 80 DUP(0) DATA ENDS CODE SEGMENT ASSUME DS:DATA,CS:CODE,SS:STACK BEGIN: MOV AX , DATA MOV DS , AX ;数据段首址送DS OUTPUT1 INPUT1 ;输出提示信息“PLEASE INPUT A STRING:” INPUT BUF ;输入一字符串到BUF缓冲区 RETURN ;输出回车换行 LEA BX , BUF+2 ;BX字符串首地址 MOV CL , BUF+1 ;CX字符个数 MOV CH , 0 NEXT1: MOV DL , BX ;DL从输入串中取一字符 CMP DL , A ;若该字符不是大写字母,直接转NEXT2 JB NEXT2 CMP DL , Z JA NEXT2 ADD DL , 20H ;将大写字母转换为小写字母 NEXT2: OUTPUT2 DL ;在显示器上显示一个字符 INC BX ;BX增1,指向下一字符 DEC CX ;CX(CX)1 JNE NEXT1 ;若(CX)不等于0,转NEXT1继续循环 RETURN ;输出回车换行 MOV AH , 4CH INT 21H ;程序结束 CODE ENDS END BEGIN,该程序运行时,首先显示一行提示信息: PLEASE INPUT A STRING : 假设我们输入字符串: Mov AH,4CH 则在屏幕下一行上将显示: mov ah,4ch 以上我们简要介绍了宏功能程序设计,宏功能是宏汇编语言独具的特色之一,是汇编语言程序设计的一种有力工具。熟练灵活地使用宏功能可以减少由于重复书写而引起的错误,缩短源程序的长度,扩充硬指令的功能,使源程序编写得像高级语言一样清晰、简洁,有利于阅读、修改与调试程序,从而简化程序设计的工作,提高编程效率。 下面再介绍一种重要的程序设计技术子程序设计。,6.2 子程序设计,子程序设计是宏汇编语言程序设计中最重要的方法与技术之一。本节着重介绍子程序的基本概念、子程序的设计方法、主程序与子程序的参数传递等问题。通过本节的学习,读者应能熟练地掌握子程序设计技术,独立地编写子程序。,6.2.1 子程序概述,子程序又称为过程(Procedure)。在程序设计中,往往把多次重复出现、具有通用性、能够完成特定处理任务的程序段编写成独立的程序模块,这就是子程序。一般子程序具备如下几个特性: (1)可重复性 (2)可通用性 (3)可浮动性 (4)模块化 调用子程序的程序称为主程序(或称调用程序)。在主程序中,如果执行到调用子程序语句,就把控制转移到子程序。子程序执行完后,再把控制返回给主程序。主程序与子程序之间的调用关系如图6-1所示。,图中主程序在K处和J处均调用子程序A。当主程序调用子程序后,CPU就转去执行子程序,执行完毕,则自动返回到主程序的断点处继续往下执行。断点是指转子指令的直接后继指令的地址。如在K处调用子程序A,断点就是DK,子程序A执行完后,返回到DK处继续向下执行。,在设计子程序时,主要应解决如下几个问题:,(1)主程序是怎样调用子程序的? (2)子程序执行完后,怎样才能返回到主程序的断点处继续向下执行? (3)主程序与子程序之间用什么方式互相传递数据? (4)如何编写子程序? 下面分别讨论这些问题。,6.2.2 子程序调用与返回指令,为了实现主程序调用子程序以及子程序执行完后能返回主程序,在8086/8088指令系统中专门设置了子程序调用指令CALL和返回指令RET。,1子程序调用指令CALL,主程序调用子程序需要通过调用指令来实现。调用指令的基本功能是将子程序的返回地址(即CALL指令的下一条指令的地址,简称断点地址,包括段地址和偏移地址)压入堆栈,以便执行完子程序后能返回主程序,并从断点处继续往下执行,然后按照某种寻址方式转向子程序的入口地址去执行子程序。 主程序和子程序可以在同一个代码段中,也可以在不同的代码段中。前者称为段内调用,后者称为段间调用。 不管是段内调用还是段间调用,若子程序入口地址直接出现在调用指令中(用子程序名表示),则为直接调用;若调用指令中出现的是存放子程序入口地址的寄存器名或存储单元的地址,则为间接调用。 下面分别介绍各种调用格式:,(1)段内直接调用,指令格式:CALL 过程名 执行操作: (SP) (SP)2 ;修改堆栈指针 (SP1),(SP)(IP) ;IP入栈 (IP)(IP)16位偏移量 ;转向子程序入口 即首先把子程序的返回地址(CALL指令的下一条指令地址)压入堆栈中,然后转移到子程序的入口地址,执行该子程序。16位偏移量是子程序入口地址与CALL指令的下一条指令地址之间的差值。,例6-3:分析下列指令。,CALL SUB1 其中,SUB1为子程序名。假设该指令第一个字节的地址为2000H:1000H,段内直接调用指令为三字节,则其返回地址是2000H:1003H。再假设子程序SUB1的入口地址为2000H:3000H,则该指令执行后,堆栈、IP和CS的内容如图6-2所示: 调用前: (CS)= 2000H (IP)= 1003H 调用后: (CS)= 2000H (IP)= 3000H,(2)段内间接调用,指令格式:CALL dest 或 CALL WORD PTR dest ; DEST为通用字寄存器或字存储单元。 执行操作: (SP) (SP)2 ;修改堆栈指针 (SP1),(SP)(IP) ;IP入栈 (IP)(dest) ;转向子程序入口 该指令与段内直接调用类似,区别是将字寄存器或字存储单元内容作为子程序入口地址送入IP。,例6-4:,下面两条指令均是段内间接调用。 CALL BX CALL WORD PTR BX 第一条指令的操作数为寄存器BX,即BX的内容作为子程序的入口偏移地址。第二条指令的操作数为字存储单元。寄存器BX所指的一个字存储单元中的内容作为子程序的入口偏移地址。,(3)段间直接调用,指令格式:CALL FAR PTR 过程名 执行操作: (SP) (SP)2 ;修改堆栈指针 (SP1),(SP)(CS) ;CS入栈 (SP)(SP)2 ;修改堆栈指针 (SP1),(SP)(IP) ;IP入栈 (IP)过程入口地址的偏移量 (CS)过程入口地址的段地址 该指令执行时先把返回地址的段地址压入堆栈,再把返回地址的偏移量压入堆栈,达到保存返回地址的目的。然后把过程入口地址的段地址和偏移量分别送入CS和IP,达到转移的目的。,例6-5:分析如下调用指令。,CALL FAR PTR SUB2 其中SUB2是子程序名,且过程SUB2与调用指令不在同一段中。 假设该指令第一个字节的地址为2000H:0200H,段间直接调用指令为5字节。那么其返回地址为2000H:0205H。再假设子程序SUB2的入口地址为4000H:0100H,那么该指令执行后,堆栈、IP和CS的内容如图6-3所示: 调用前: (CS)= 2000H (IP)= 0205H 调用后: (CS)= 4000H (IP)= 0100H,(4)段间间接调用,指令格式:CALL DWORD PTR dest ;dest为双字存储单元操作数。 执行操作: (SP) (SP)2 ;修改堆栈指针 (SP1),(SP)(CS) ;CS入栈 (SP)(SP)2 ;修改堆栈指针 (SP1),(SP)(IP) ;IP入栈 (IP)dest的低字值 (CS)dest的高字值 该指令的执行与段间直接调用类似,区别是根据寻址方式求出有效地址后,把指定存储单元的低字内容送到IP寄存器,高字内容送到CS寄存器。,例6-6:下面指令都是段间间接调用。,CALL DWORD PTR BX CALL DWORD PTR DST 这两条指令的操作数均为双字存储单元类型。其中第一条指令由BX的内容指定数据段中的一个双字存储单元地址,第二条指令是由DST指定数据段中的一个双字存储单元地址。在连续的两个字单元中分别存放着子程序的入口偏移地址和入口段地址。,以上几条调用指令在使用时应注意:, 宏汇编程序MASM能根据定义过程时所指定的过程类型决定采用段内直接调用还是段间直接调用。如果先调用后定义,为了调用远过程,必须在过程名前加类型说明符“FAR PTR”,调用近过程可以在过程名前加类型说明符“NEAR PTR”或省略。 对于间接调用,如果操作数是16位操作数,则汇编成段内间接调用;如果操作数是32位操作数,则汇编成段间间接调用。如果发生调用指令语句在前,有关变量定义伪指令在后的情况,则需要在调用语句中用PTR等操作符加以说明。,2子程序返回指令RET,子程序执行完后需要返回主程序,这个功能由子程序返回指令RET来实现。RET的功能是从堆栈的栈顶弹出返回地址。返回指令总是与调用指令配套使用,并且返回指令通常放在子程序的末尾(即出口处),使子程序执行完毕能够返回主程序,继续执行原来的程序。 与调用指令相对应,返回指令也有段内返回和段间返回两种形式。段内返回是从栈顶弹出一个字数据送入IP,段间返回是从栈顶弹出两个字数据分别送入IP和CS,从而控制程序返回到主程序。,(1)段内返回指令,指令格式:RET 执行操作: (IP)(SP+1),(SP) ;栈顶内容送入IP (SP)(SP)+2 ;修改栈顶指针 该指令弹出当前栈顶的一个字内容送入IP,作为偏移地址。段内返回指令用于子程序被定义成近过程时。,(2)段间返回指令,指令格式:RET 执行操作: (IP)(SP+1),(SP) ;栈顶内容送入IP (SP)(SP)+2 ;修改堆栈指针 (CS)(SP+1),(SP) ;栈顶内容送入CS (SP)(SP)+2 ;修改堆栈指针 该指令把当前栈顶的两个字内容依次送入IP和CS,分别作为偏移地址和段地址。段间返回指令用于子程序被定义为远过程时。,(3)带立即数的返回指令,指令格式: RET n ;n为立即数或数值表达式 带立即数的返回指令又分为带立即数的段内返回和带立即数的段间返回两种形式。它们都是在完成基本返回指令的功能后,再执行(SP)(SP)n操作,即修改堆栈指针,跳过堆栈中的一些已经无用的数据。n一般是偶数。,例6-7:分析如下返回指令,RET 4 假设在执行该指令前(SP)= 0D253H 若为段内返回,则执行该指令后,(SP)= 0D253H+2H+4H = 0D259H; 若为段间返回,则执行该指令后,(SP)= 0D253H+4H+4H = 0D25BH。 汇编程序MASM能根据RET指令所在过程的类型决定采用段内返回,还是段间返回。 注:CALL指令和RET指令均不影响标志位。,3子程序定义格式,汇编语言中,子程序也叫过程,其定义格式为: 子程序名 PROC NEAR | FAR 子程序名 ENDP 说明: 子程序名是用户自定义的标识符,用以标识不同的过程,命名原则要遵从系统对标识符的定义规则。PROC与ENDP是子程序定义伪指令。NEAR或FAR是过程的类型说明符,NEAR类型的过程只允许段内调用,FAR类型的过程允许段间调用。过程的缺省类型为NEAR。,6.2.3 主程序与子程序的参数传递,主程序在调用子程序前,必须把需要子程序处理的原始数据传递给子程序,即为子程序准备入口参数。子程序根据入口参数进行一系列处理之后得到处理结果,该结果必须送给调用它的主程序,即提供出口参数以便主程序使用。这种主程序为子程序准备入口参数,子程序为主程序提供出口参数的过程称为参数传递。 主程序与子程序之间传递参数的方式是事先约定好的。每一个子程序设计之前,必须确定其入口参数到哪里去取,处理后的结果送往何处。一旦子程序按此约定设计出来,无论在何处对它进行调用都必须满足子程序的要求,否则,子程序将无法正常运行,或者得不到正确的结果。 传递参数的多少反映程序模块间的耦合程度。根据实际情况,子程序可以只有入口参数或只有出口参数,也可以入口参数和出口参数同时存在。常用的参数传递方式有三种:寄存器传递、存储单元传递和堆栈法传递。,1用寄存器传递参数,用寄存器传递参数就是将子程序的入口参数和出口参数都放在约定的寄存器中。此法的优点是信息传递快,编程也较方便,并且节省内存单元。但由于寄存器个数有限,而且在处理过程中要经常使用寄存器,如果要传递的参数很多,将导致无空闲寄存器供编程用,所以此法只适用于要传递的参数较少的情况。,例6-8:,设ARRAY是一个含有10个元素的数组,每个元素都是8位字节数据。试用子程序计算各数组元素的“累加和”。(注:为了简化程序设计,以下程序中的“累加和”是指不记进位的累加)。,分析:,我们编写子程序来完成求数组元素的累加和,主程序需要向它提供入口参数,使得子程序能够访问数组元素。子程序需要将求和结果这个出口参数传给主程序。本例我们采用寄存器法实现参数的传递。 寄存器和存储单元分配如下: BX:用来存放数组首地址; CX:用来存放数组元素个数; AL:用来存放数组元素累加和; RESULT:用来最终存放数组元素累加和的存储单元。,源程序如下:,STACK SEGMENT STACK DB 200 DUP(0) STACK ENDS DATA SEGMENT ARRAY DB 12H,36H,48H,5,17H,29H,8,26H,59H,35H COUNT = 10 RESULT DB ? ;存放累加和 DATA ENDS CODE SEGMENT ASSUME CS:CODE,DS:DATA,SS:STACK BEGIN: MOV AX,DATA MOV DS,AX MOV BX,OFFSET ARRAY ;BX数组的偏移地址 MOV CX,COUNT ;CX数组元素个数 CALL PROC_SUM ;调用求和子程序 MOV RESULT,AL ;RESULT(AL) MOV AH,4CH INT 21H ;子程序名:PROC_SUM ;功能:计算数组元素的累加和 PROC_SUM PROC XOR AL,AL ;累加器清0 SUM: ADD AL,BX ;求和 INC BX ;指向下一字节 LOOP SUM ;(CX)0转SUM继续求和 RET PROC_SUM ENDP CODE ENDS END BEGIN,本程序中,用BX、CX寄存器作为入口参数,分别将主程序中数组的偏移地址和数组元素的个数传递给子程序,在子程序中用AL寄存器作为出口参数,将求得的数组元素的累加和返回给主程序。 本程序运行完后,在RESULT存储单元中存放的数组元素累加和是91H。,2通过存储单元传递参数,用存储单元传递参数就是将入口参数和出口参数都放到事先约定好的存储单元之中。此法的优点是参数传递的数量不受限制,每个参数都有独立的存储单元,编写程序时不易出错。缺点是要占用一定数量的存储单元。,例6-9:要求同上,现在用存储单元传递参数,计算数组元素的累加和。,分析:采用存储单元传递参数,例中主程序和子程序共用ARRAY和RESULT两个存储单元。主程序只要设置好数据段DS,就可以调用子程序了,子程序直接通过存储单元存取数组元素。,源程序如下:,STACK SEGMENT STACK DB 200 DUP(0) STACK ENDS DATA SEGMENT ARRAY DB 12H,36H,48H,5,17H,29H,8,26H,59H,35H COUNT = 10 RESULT DB ? DATA ENDS CODE SEGMENT ASSUME CS:CODE,DS:DATA,SS:STACK BEGIN: MOV AX,DATA MOV DS,AX CALL PROC_SUM ;调用求和子程序 MOV AH,4CH INT 21H ;子程序名:PROC_SUM ;功能:计算数组元素的累加和 PROC_SUM PROC XOR AL,AL ;累加器清0 MOV BX,OFFSET ARRAY MOV CX,COUNT SUM:ADD AL,BX INC BX LOOP SUM ;(CX)0转SUM继续求和 MOV RESULT,AL ;累加和送RESULT保存 RET PROC_SUM ENDP CODE ENDS END BEGIN,主程序和子程序的参数传递读者可自己分析。程序运行后,在RESULT存储单元中存放的累加和是91H。,3通过堆栈传递参数,该方法是利用堆栈作为主程序和子程序之间传递参数的工具。优点是参数不占用寄存器,也无需另开辟存储单元,将参数存放在公用的堆栈区,处理完之后堆栈恢复原状,不影响其他程序段使用堆栈。缺点是由于参数和子程序的返回地址混杂在一起,访问参数时必须准确地计算它们在堆栈内的位置。如果操作不慎,在执行RET指令时,栈顶存放的可能不是返回地址,从而导致运行混乱。由此可见,使用该方法编制程序比较复杂。,例6-10:要求同上,现在用堆栈传递参数,计算数组元素的累加和。,分析:通过堆栈传递参数,主程序将数组的偏移地址和数组元素个数压入堆栈,然后调用子程序;子程序通过BP寄存器从堆栈相应位置取出参数(非栈顶数据),求和后用AL返回结果。 例中用堆栈传递入口参数,但出口参数仍利用寄存器传递。,源程序如下:,STACK SEGMENT STACK DB 200 DUP(0) STACK ENDS DATA SEGMENT ARRAY DB 12H,36H,48H,5,17H,29H,8,26H,59H,35H COUNT = 10 RESULT DB ? DATA ENDS CODE SEGMENT ASSUME CS:CODE,DS:DATA,SS:STACK BEGIN: MOV AX,DATA MOV DS,AX MOV AX,OFFSET ARRAY PUSH AX ;数组的偏移地址进栈 MOV AX,COUNT PUSH AX ;数组元素个数进栈 CALL PROC_SUM ;调用求和子程序 MOV RESULT,AL ;保存数组元素累加和 MOV AH,4CH INT 21H,PROC_SUM PROC PUSH BP ;保护BP MOV BP,SP ;BP指向当前栈顶,用于取入口参数 MOV BX,BP+6 ;BX数组的偏移地址 MOV CX,BP+4 ;CX数组的元素个数 XOR AL,AL ;累加器清0 SUM:ADD AL,BX ;求和 INC BX LOOP SUM POP BP RET 4 PROC_SUM ENDP CODE ENDS END BEGIN,程序中多次用到了PUSH、POP指令,读者可自己分析堆栈的变化情况。本程序运行后,在RESULT存储单元中存放的累加和是91H。 以上介绍了三种常用的参数传递方式,实际应用中到底采用哪一种,要根据具体情况而定。有时是几种方式混合使用。不管采用什么方式,主程序与子程序的配合一定要默契。,6.2.4 子程序设计方法,子程序是供主程序调用的。为了便于分工合作,使不同的程序员在不需要了解子程序的内部结构及其算法的情况下,就能方便地调用子程序,一个完整的子程序应当包括以下几部分内容:子程序调用方法说明、保护现场和恢复现场、子程序定义、参数传递方法等。,1子程序调用方法说明,为了使子程序便于阅读、便于维护和使用,一般应提供子程序调用方法说明,主要包括:子程序名、子程序功能、寄存器与存储单元分配、入口参数、出口参数、子程序调用示例等,以注释的形式出现在子程序清单中。 2保护现场和恢复现场 保护现场和恢复现场是子程序设计时必须考虑的问题。子程序中需要使用的寄存器,有可能在调用子程序前主程序正在使用,其值在从子程序返回主程序后还要继续使用。因此子程序执行前要保护现场,返回时要恢复现场,否则会破坏现场数据。 保护现场和恢复现场的工作可以在主程序中完成,也可以在子程序中完成。一般情况下,是在子程序的开始安排一串保护现场的语句,在子程序的返回指令前再恢复现场。这样处理,主程序在转子程序前不必考虑保护现场、恢复现场的工作,其处理流程显得非常清晰。 保护现场和恢复现场的最简洁方法是利用堆栈指令,保护时将寄存器内容及状态标志寄存器的内容压入堆栈,恢复时再从堆栈中弹出。,例如,若子程序SUB1中改变了AX、BX、CX、DX四个寄存器的内容,则可以用如下方法实现保护现场和恢复现场的工作:,SUB1 PROC PUSH AX ;以下4条指令用于保护现场 PUSH BX PUSH CX PUSH DX ;子程序处理部分 POP DX ;以下4条指令用于恢复现场 POP CX POP BX POP AX RET SUB1 ENDP 注意:恢复现场时寄存器出现的顺序应与保护现场时寄存器出现的顺序 相反,即依照“后进先出”的原则从堆栈中弹出信息。 下面我们通过具体例子来说明子程序的设计方法。,例6-11:编写一个求两个正整数最大公约数的子程序。,分析:求两个数的最大公约数可以采用辗转相除法。具体算法如下: 取两个正整数M和N; 用M除以N,求得余数R(0RN); 若R=0,则N的当前值即为求得的最大公约数,算法结束;否则,更新M和N的值,即将NM,RN,转继续求解。,子程序清单:,;子程序名:GCD ;功能:求两个正整数的最大公约数。 ;入口参数:AX、BX,用来分别存放两个正整数。 ;出口参数:CX,用来存放求得的最大公约数。 GCD PROC PUSH AX PUSH BX ;保护现场 PUSH DX AGAIN:XOR DX,DX ;DX清0,扩充被除数 DIV BX ;商存入AX,余数存入DX AND DX,DX ;判断余数是否为0 JZ EXIT ;余数为0转EXIT MOV AX,BX ;否则,更新被除数 MOV BX,DX ;更新除数 JMP AGAIN ;继续辗转相除 EXIT: MOV CX,BX ;最大公约数存入CX POP DX POP BX ;恢复现场 POP AX RET ;子程序返回 GCD ENDP,例6-12:假设在A、B、C、D四个字单元中各存放一个正整数,试编写程序分别求出A与B的最大公约数M,C与D的最大公约数N,再求出M与N之和,将结果存入RESULT字单元中。,分析:本程序需两次调用求最大公约数子程序,将两次返回结果求和再存入RESULT字单元中。,源程序如下:,STACK SEGMENT STACK DB 200 DUP(0) STACK ENDS DATA SEGMENT A DW 720 B DW 81 C DW 1150 D DW 125 RESULT DW ? DATA ENDS CODE SEGMENT ASSUME CS:CODE,DS:DATA,SS:STACK BEGIN:MOV AX,DATA MOV DS,AX MOV AX,A MOV BX,B CALL GCD ;调用子程序,求A、B的最大公约数 MOV RESULT,CX ;结果存入RESULT单元 MOV AX,C MOV BX,D CALL GCD ;调用子程序,求C、D的最大公约数 ADD RESULT,CX ;将结果加到RESULT单元 MOV AH,4CH ;返回 INT 21H,;求最大公约数子程序 GCD PROC PUSH AX PUSH BX PUSH DX AGAIN:XOR DX,DX ;DX清0,扩充被除数 DIV BX ;商存入AX,余数存入DX AND DX,DX ;判断余数是否为0 JZ EXIT ;余数为0转EXIT MOV AX,BX ;否则,更新被除数 MOV BX,DX ;更新除数 JMP AGAIN ;继续辗转相除 EXIT: MOV CX,BX ;最大公约数存入CX POP DX POP BX ;恢复现场 POP AX RET GCD ENDP CODE ENDS END BEGIN,本程序运行时,第一次调用求最大公约数子程序GCD,求出720和81的最大公约数为9,送入RESULT字单元中;第二次调用求最大公约数子程序GCD,求出1150和125的最大公约数为25,然后将9和25的和34送入RESULT字单元中,所以最终在RESULT字单元中存放22H。 读者可以自己调整A、B、C、D的值,分析程序的执行过程。,例6-13:十进制到十六进制数转换程序。,要求从键盘输入一个无符号十进制数,然后将该数以十六进制形式在屏幕上显示出来。 例如: 输入32,输出20H; 输入100,输出64H; 输入1248,输出4E0H。,分析:我们采用子程序结构,用一个子程序DECTOBIN实现从键盘输入十进制数,并把它转换为二进制数;另一个子程序BINTOHEX将二进制数以十六进制数的形式在屏幕上显示出来。另外为显示清晰,用RETURN子程序达到回车换行效果。 整个程序流程如图6-4所示。,源程序如下:,STACK SEGMENT STACK DB 200 DUP (0) STACK ENDS DATA SEGMENT MSG DB 0DH,0AH,PLEASE INPUT:$ BUF1 DB 6 DB ? DB 6 DUP(0) BUF2 DB 6 DUP(0) DAT DW 10 DATA ENDS CODE SEGMENT ASSUME CS:CODE,DS:DATA,SS:STACK BEGIN:MOV AX,DATA MOV DS,AX REPEAT:LEA DX,MSG MOV AH,9 INT 21H CALL DECTOBIN ;调用键盘输入子程序 CALL RETURN ;调用回车换行子程序 CMP BX,-1 JZ EX CALL BINTOHEX ;调用转换显示子程序 CALL RETURN EX: MOV AH,4CH INT 21H,;子程序DECTOBIN ;功能:接收从键盘输入的十进制数,并将其转换为二进制数存放在BX寄存器中。 ;无入口参数。出口参数为BX寄存器。,DECTOBIN PROC MOV BX,0 ; BX清0 INPUT: LEA DX,BUF1 ;键盘接收十进制数输入,送入以BUF1+2为首址的字节存储区中 MOV AH,10 INT 21H LEA SI,BUF1+2 ;SI指向BUF1字节存储区 MOV CL,BUF1+1 ;CL保存输入十进制数的位数 MOV AX,0 NEXT: MOV BL,SI CMP BL,30H JB EXIT ;若不在09之间,则退出 CMP BL,39H JA EXIT SUB BL,30H MOV BH,0 MUL DAT JO EXIT ADD AX,BX JC EXIT INC SI DEC CL JNE NEXT MOV BX,AX ;将数据最终放在BX寄存器中 JMP EXIT1 EXIT: MOV BX,-1 EXIT1: RET DECTOBIN ENDP,;子程序BINTOHEX ;功能:将BX寄存器中的二进制数转换为十六进制数并显示出来。 ;入口参数:BX寄存器。 ;无出口参数。,BINTOHEX PROC LEA DI,BUF2 MOV CH,4 ;CH转换次数4 AGAIN: MOV CL,4 ;每次转换四位 ROL BX,CL MOV AL,BL AND AL,0FH ;将十六进制数转换为ASCII码,若不在09之间,则加07H调整,否则直接转OUTPUT ADD AL,30H CMP AL,3AH JL OUTPUT ADD AL,7H OUTPUT:MOV DI,AL ;将转换后的字符存入BUF2存储单元中 INC DI DEC CH JNZ AGAIN MOV DI,BYTE PTR H MOV DI+1,BYTE PTR $ LEA DX,BUF2 MOV AH,9 INT 21H RET BINTOHEX ENDP,;RETURN子程序,用来输出回车换行,RETURN PROC MOV AH,2 MOV DL,0AH INT 21H MOV DL,0DH INT 21H ;输出回车换行 RETURN ENDP CODE ENDS END BEGIN 本程序运行过程请读者自己分析。,6.2.5 子程序的嵌套,主程序可以调用子程序,子程序还可以再去调用另一个子程序,这种情况称为子程序的嵌套。子程序嵌套的层数没有限制,只要堆栈空间允许即可。 嵌套子程序的设计与调用方法与前面介绍的子程序设计与调用方法一致。 要特别注意以下几点: (1)调用指令CALL与返回指令RET必须成对使用。 (2)用寄存器传递参数或作为工作单元时,要及时保存和恢复寄存器内容。 (3)若程序中用到堆栈,要注意堆栈的正确操作方法。 下面我们来看一个例子。,例6-14:假设在BUF1开始的存储区中放有若干个无符号字节数据,试编写程序,找出其中的最大值,并以十六进制的形式在屏幕上显示出来。,分析:本程序用子程序嵌套方法来实现。子程序MX_DISP用来求若干个无符号字节数的最大值,并调用DISPLAY子程序进行显示。子程序DISPLAY用来以十六进制的形式在屏幕上输出字符。,源程序如下:,STACK SEGMENT STACK DB 200 DUP (0) STACK ENDS DATA SEGMENT BUF1 DB 24,12,53,84,36,120,58 N = $-BUF1 DATA ENDS CODE SEGMENT ASSUME CS:CODE,DS:DATA,SS:STACK BEGIN: MOV AX,DATA MOV DS,AX MOV CX,N-1 LEA SI,BUF1 CALL MX_DISP MOV AH,4CH INT 21H,;子程序MX_DISP,;功能:用来求若干个无符号字节数的最大值,并调用DISPLAY子程序进行输出。 ;入口参数:SI寄存器,指向存放无符号字节数的存储区。 ; CX寄存器,存放进行比较的次数,取值为N1。 ;出口参数:DL寄存器,存放要显示的字符。 MX_DISP PROC MOV BL,SI NEXT1:INC SI CMP BL,SI JAE NEXT2 MOV BL,SI NEXT2:DEC CX JNZ NEXT1 MOV CL,4 MOV DL,BL SHR DL,CL CALL DISPLAY MOV DL,BL AND DL,0FH CALL DISPLAY MOV DL,H MOV AH,2 INT 21H RET MX_DISP ENDP,;子程序DISPLAY,;功能:用来以十六进制的形式在屏幕上输出字符。 ;入口参数:DL寄存器,存放要显示的字符。 ;出口参数:无 DISPLAY PROC CMP DL,9 JBE DISP1 ADD DL,07H DISP1:ADD DL,30H MOV AH,2 INT 21H RET DISPLAY ENDP CODE ENDS END BEGIN 本程序运行时,将在屏幕上显示最大值120的十六进制形式78H。,6.2.6 子程序
- 温馨提示:
1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
2: 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
3.本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

人人文库网所有资源均是用户自行上传分享,仅供网友学习交流,未经上传用户书面授权,请勿作他用。