微机原理课件第四章宏汇编语言程序设计.ppt
第四章 宏汇编语言程序设计,汇编语言是利用指令的助记符、符号地址、标号来编写的语言,它是机器语言的符号表示,是较低级的语言。,利用汇编语言编写的程序称为汇编语言源程序,上一章讲到的指令系统中的每条指令都是构成源程序的基本语句。但机器不能识别源程序,要通过汇编程序翻译成二进制代码的浮动目标程序,然后由连接程序将目标文件与库文件相连,最后得到可执行的程序,才可在机器上直接运行。,基本概念:,汇编语言:用指令的助记符、符号地址、标号 等来编写的语言。,汇编语言源程序:用汇编语言编写的程序(*.asm),汇编程序:将汇编语言源程序翻译成目标目标程序(代码),这个翻译过程称为汇编,翻译软件就叫汇编程序。,一个完整的汇编语言的语句由下列几部分组成:标号和变量、指令助记符、界符、常数和注释,所有这些都称为标记。,4-1 汇编语言的标记,一、标号和变量 表示指令性语句的符号地址或表示一个数据单元的符号地址。,对标号或变量要注意以下几点:标号或变量可以由数字、字母、下划线或其它特殊字符组成。,标号或变量不能以数字开头,但可出现在标号的其它地方。,标号或变量最大长度不能超过31个字符。,二、指令助记符指出指令的性质(功能),三、界符作为一个程序中或一条指令中两个部分的分隔符号用。,四、常数二进制:01001101B十进制:2000或2000D八进制:1700Q十六进制:1200H,0FFH串常数:ABCD,五、注释 用;开头后面的内容可随意,用来增加程序可读性的。,表达式由运算对象和运算符组成,在汇编时由汇编程序对它进行运算,运算结果作为一个语句中的操作数来使用。,4-2 汇编语言中的表达式,运算对象:常数、标号或变量,一、算术运算符 有:+、-、*、/、MOD(模,即取除法运算结果之余数)、SHL(左移,左移1位相当于乘2)、SHR(右移,右移1位相当于除以2)。,二、逻辑运算符有:AND(与)、OR(或)、NOT(非)、XOR(异或),逻辑运算符是按位运算的,只能对常数进行运算,得到结果也是常数。,例4-5 IN AL,PORT AND DX,PORT AND 0FEH OUT DX,AX前一个AND是指令助记符,而后一个AND是逻辑运算符。,三、关系运算符 有:EQ(相等)、NE(不等)、LT(小于)、GT(大于),LE(小于或等于)、GE(大于或等于)。,四、数值返回运算符数值返回运算符也经常称作分析运算符有:OFFSET、SEG、TYPE、LENGTH、SIZE 5种,它们加在变量或标号前,返回运算对象的某个参数值,例如偏移地址值、段地址值、类型属性、变量包含的单元数等。,1.OFFSET格式:OFFSET 变量或标号OFFSET 返回标号或变量的偏移地址值。,2.SEG格式:SEG 变量或标号SEG 返回标号或变量的段地址值。,3.TYPE格式:TYPE 变量或标号,TYPE加在变量前,返回变量的类型属性。,TYPE加在标号前,返回标号的距离属性。,例4-9 A1 DB 1,2,3;变量A2 DW 1234H;变量A3 DD 6 DUP(?);变量 L1:MOV AH,TYPE A1;标号MOV BH,TYPE A2MOV CH,TYPE A3MOV DH,TYPE L1MOV DX,TYPE L1,4.LENGTH格式:LENGTH 变量,5.SIZE格式:SIZE 变量,五、修改属性运算符修改属性运算符也经常称作综合运算符有:段操作符、PTR、THIS、HIGH、LOW、SHORT 6种,可以在程序运行过程中,通过修改属性运算符来修改变量或标号的属性,包含段属性、偏移地址属性、类型属性等。,1.段操作符格式:段前缀:变量或地址表达式段前缀有段寄存器CS、DS,ES,SS后跟冒号:,用来表示某个变量或地址被修改到哪个段寄存器提供的段地址。,2.PTR格式:类型/距离:PTR 变量或标号,3.THIS格式:变量或标号 EQU THIS 类型或距离,4.SHORT格式:SHORT 标号SHORT用来说明转移类指令中转向地址的属性,指出转向的目标地址与本指令之间的距离在-128+127之间,即限制在短转移范围内。,5.HIGH和LOW格式:HIGH/LOW 变量或标号HIGH和LOW称为字节分离运算符,对一个数或地址表达式,HIGH从中分离出高位字节LOW从中分离出低位字节。,六、其它运算符有:、()、.、MASK和WIDTH等,七、优先级表达式是常数、变量、标号和运算符的组合,在计算表达式时,应按优先级高低进行计算,同时遵循同级运算从左到右的原则计算。,4-3 伪指令语句,伪指令语句没有对应的机器代码,并不像指令语句那样由CPU来执行,它是MASM汇编程序对源程序汇编期间进行处理的。主要完成变量定义、存储器分配、指示程序开始和结束、段定义、段分配等。伪指令有以下几种类型:,一、数据定义语句格式1:变量名 助记符 操作数,操作数格式2:变量名 助记符 n DUP(操作数,操作数),例4-21 操作数是常数或表达式DA1 DB 10H,20HDA2 DW 1122H,34HDA3 DD 5*10H,1234H,例4-22 操作数是字符串FIRST DB HELLOSECOND DW OKTHIRD DB OK,注意:用DW定义字符串时,只允许包含两个字符,多于两个字符时,只能用DB来定义。,例4-23 操作数用?定义不确定值的变量,用作保留存储空间,以便存放运算结果。M1 DB?,?M2 DW 1234H,?,例4-24 操作数用DUP来定义重复变量ONE DB 5 DUP(0)TWO DW 10 DUP(?)THREE DB 3 DUP(1,2),FOUR DB 2 DUP(1,3 DUP(10H);DUP 嵌套,操作数是变量或标号:用伪指令DW和DD可以将变量或标号的偏移地址存入存储器中,当用DD来定义时,原变量或标号的偏移地址存入低位字中,原变量或标号的段地址存入高位字中。,例4-25 PP DB 1,2,3;变量PPAD1:MOV AX,BX;标号AD1、AD2AD2:MOV BX,CX,假设变量的PP的偏移地址为1000H,标号AD1的偏移地址为2000H,标号AD2的偏移地址为3000H,段地址为4000H。,二、表达式赋值语句 表达式赋值语句有两种,赋值语句EQU和等号语句=,它们均不占用内存。,1.赋值语句EQU格式:符号名 EQU 表达式功能:用来给变量、标号、常数、指令、表达式等定义一个符号名,程序中用到EQU左边的变量、标号时可用右边的常数值或表达式代替,但一经定义在同一个程序模块中就不能重新定义。,2.等号语句=等号语句=与EQU语句具有相同功能,区别在于EQU中左边的标号不允许重新定义,而用=定义的语句允许重新定义。,三、段定义语句,前面讲过,存储器的物理地址由段地址和偏移地址组合而成,任何一个逻辑段,无论是代码段,数据段,堆栈段,附加段,都必须进行段定义,以便连接程序把不同段和模块连成一个可执行程序。此外还必须明确段和段寄存器之间的关系,这可使用段分配语句来完成。,1.段定义语句 SEGMENTENDS,功能:将一个逻辑段定义成一个整体。,段名 SEGMENT 定位类型 组合类型 分类名 段名 ENDS,2.段分配语句ASSUME 在8086系统中存储器采用分段结构,各段容量64K字节,用户可以设置多个逻辑段,但只允许4个逻辑段同时有效,段分配语句用来完成将逻辑段分别定义成代码段、堆栈段、数据段和附加段。,ASSUME为伪指令助记符,放在代码段的开始,不可省略。提供给汇编程序,说明当前代码段,数据段,堆栈段和附加段4个如何定义。,2.段分配语句ASSUME,例4-29通过表转换指令来实现将57的7段显示段码送到BX寄存器中。,四、过程定义语句,过程也称子程序。在主程序中,经常要用到一些程序段,程序段的功能和结构相同,仅有一些变量赋值不同,此时可以将这些程序段独立编写用过程定义语句进行定义,然后在主程序中对它进行过程调用。这样既节省了内存空间,也便于进行模块化程序设计,使编程清晰,使用灵活。,格式:过程名 PROC 属性;过程内容RET N 过程名 ENDP,四、过程定义语句,在汇编语言源程序中,使用CALL指令调用过程,过程调用允许嵌套和递归调用。嵌套调用指在一个被调用的过程中,又调用另一个过程;递归调用是指在一个被调用的过程中,又调用了本身的过程。嵌套与递归的深度由堆栈段的容量决定因为过程调用时必须将当前的地址压入堆栈保护起来,使调用返回时能返回到正确的返回地址。另外在子程序入口也有许多参数要保护,以免影响主程序原来的运行状态。,例4-30 近过程定义及调用格式CCODE SEGMENT ABC PROC NEAR RETABC ENDP CALL ABC CCODE ENDS,例4-31 远过程定义及调用格式C1CODE SEGMENT KKK PROC FAR RETKKK ENDP C1CODE ENDSC2CODE SEGMENT CALL KKK C2CODE ENDS,例4-32 过程嵌套调用格式CCODE SEGMENT KKK PROC NEAR CALL LLL RETKKK ENDP LLL PROC NEAR RETLLL ENDPCCODE ENDS,五、程序开始和结束语句,1.NAME格式:NAME 程序名功能:为源程序目标模块赋名字。2.TITLE格式:TITLE 文本名功能:将文本名赋给源程序目标模块作名字。,3.ORG格式:ORG 表达式,4.END格式:END 标号名功能:标记汇编源程序结束,六、结构定义语句,七、外部伪指令及对准伪指令,1.外部伪指令 程序中包含多个模块时,有些程序或数据在各个模块间要相互共享,可用外部伪指令PUBLIC和EXTRN来实现此功能。其中PUBLIC用来定义共享模块,EXTRN用来引用共享模块。,例4-42DATA SEGMENTA1 DB 10H,20HA2 DW 4 DUP(0)A3 EQU 1000HDATA ENDSCODE SEGMENTASSUME CS:CODE,DS:DATASTART:MOV AX,DATATMF LABEL FARPUBLIC A2,A3,TMF CODE ENDS,PDATA SEGMENTP1 DB 0AH,0BHP2 DB 2 DUP(?)PDATA ENDSPCODE SEGMENTEXTRN A2:WORD,A3:ABS,TMF:FARMAIN:MOV AX,PDATAMOV BX,OFFSET A2MOV CX,A3JMP TMF PCODE ENDSEND MAIN,2.对准伪指令格式:EVEN,3.LABLE(相当于THIS伪指令),LABLE伪指令给已定义的变量或标号取一个名字,并可重新定义它的类型属性,使同一变量或标号在不同地方被引用时,可采用不同的名字,具有不同的类型属性,这样提高了程序的灵活性。,(1)LABLE与变量连用LABLE与变量连用时,给下一个变量取一个别名,类型属性可修改成BYTE、WORD等。,(2)LABLE与标号连用 LABLE与标号连用时,给下一个语句定义的标号取一个别名,并可改变距离属性为FAR或NEAR。,4-4 DOS系统功能调用和BIOS中断调用,一、DOS系统功能调用,DOS系统功能调用分别实现设备管理、文件读/写、文件管理和目录管理等功能。每个子程序对应一个功能号,所有的系统功能调用的格式是一致的,按下面4步进行:,(1)系统功能号送到AH寄存器;,(2)入口参数送到指定寄存器中;,(3)用INT 21H指令执行功能调用;,(4)根据出口参数分析功能调用执行情况。,1.DOS键盘功能调用,键盘提供了字符键、功能键和控制键。每个键都有对应的键值,即标准ASCII码值,通过DOS功能调用可读入键值到AL寄存器或存储器中,表4-7列出DOS键盘功能调用的有关命令。,表4-7 DOS键盘功能调用,(1)键入单字符,1号功能调用:从键盘输入字符并显示,调用命令为:MOVAH,1INT21H,例4-51 交互式程序中用户按下数字键1,2,3,程序转入相应的服务子程序,若按下其它键就继续等待。KEY:MOVAH,1;读入键值AL INT21H CMPAL,1;键值为1 吗?JEONE CMPAL,2;键值为2 吗?JETWO CMPAL,3;键值为3 吗?JETHREE JMPKEY;其它键则继续等待按键ONE:TWO:THREE:,8号功能调用:从键盘输入字符但不回显,命令为:MOVAH,8INT21H,6号功能调用:直接控制台输入/输出,命令为:MOVDL,0FFHMOVAH,6INT21H,7号功能调用:直接控制台输入/输出但无回显,命令格式为:MOVAH,7INT21H,(2)输入字符串,0AH功能调用:能从键盘接收字符串到内存的输入缓冲区。,例4-52 开辟一个缓冲区,从键盘输入一个字符串,将输入的字符数CL寄存器,并将指针指向字符串的第一个字符。,(3)检验键盘状态 0BH功能调用:检验是否有键按下,若有键按下AL=0FFH,若没有键按下,AL=0,无论检测到是否有键按下,程序将继续执行下一条指令。,(4)清除键盘缓冲区 0CH功能调用:先清除键盘缓冲区,然后执行AL中指定的功能,AL中可以指定1,6,7,8或0AH功能号,使程序在输入字符前将以前键入的字符清掉。,2.DOS显示功能调用 DOS显示功能调用能够显示字符或字符串,这些功能都自动向前移动光标,表4-8给出了DOS显示功能调用的有关命令。,(1)单字符显示,2号功能调用:2号功能调用实现将字符送到屏幕显示出来。它要求将要显示字符的ASCII码值送到DL寄存器中。,6号功能调用:是直接控制台输入/输出调用,除前面谈到的键盘输入功能外,在DL不等于0FFH时,表示向屏幕输出。它要求将要显示字符的ASCII码值送到DL寄存器中。,(2)字符串显示 9号功能调用:显示字符串,要求DS:DX指向串地址首址,并且字符串必须以$字符为结束符。若要求显示字符串后光标自动回车换行,则在$字符前再加上0DH(回车),0AH(换行)字符。,4-5 程序设计方法,前面几章已讨论了指令系统和汇编语言设计基础,而设计出一个好的程序不仅要能正常运行,完成要求的功能,还应该具有下列特点:,(1)程序结构模块化,程序易读,易调试及 维护。,(2)执行速度快。,(3)占用内存空间小。,尤其是结构化设计,在程序复杂的情况下尤为重要。一般来说设计汇编语言源程序的基本步骤如下:,(1)分析问题,抽象出描述问题的数学模型,并确定实现数学模型的算法。,(2)绘制程序流程图,通常先画粗框图,在结构模块中再细画框图。框图一般有起始框,执行框,判断框和终止框。,(3)分配存储空间及工作单元。分配数据段,堆栈段,代码段各在内存什么位置,各个寄存器主要起什么作用。,(4)按流程图设计编写程序。,(5)静态检查,上机调试。,(6)程序运行,结果分析。,在进行汇编语言源程序设计时,通常用到四种程序结构:顺序结构;分支结构循环结构;子程序结构。下面分别加以说明。,一、顺序结构,顺序结构的程序一般是简单程序,程序顺序执行,无分支,无循环,也无转移,图中没有判断框。,例4-64 内存中TABLE开始存放09的平方值,通过人机对话,当任给定一个数X(09),查表得X的平方值,放在AL中。(见程序流程图),.MODEL SMALL.386.STACK 100H.DATATABLE DB 0,1,4,9,16,25,36 DB 49,64,81BUF DB Please input one number(09):DB 0DH,0AH,$.CODE.STARTUP,MOVDX,OFFSET BUF;显示字符串MOV AH,9INT 21HMOVAH,1;1号功能调用,键入数送 AL中INT 21HMOVAH,0;查表得键入数的平方值AND AL,0FHADD BX,AXMOV AL,BX.EXIT 0END,二、分支结构,1.分支结构 一般情况下,程序顺序执行,但经常要求程序根据不同条件选择不同的处理方法,这就需要用到分支结构。,例4-65 编程实现以下函数:,核心代码如下:,MOVAL,XCMP AL,0JG ZHSHUSUBAL,5JMPOUTZHSHU:ADDAL,3OUT:MOVY,AL,2.多分支,有的分支结构为多分支,可以利用多个条件转移指令来实现,依次测试条件是否满足,若满足转入相应分支入口,若不满足继续向下测试,直到全部测试完。这种方法编程简单、直观,但运行速度慢,要依次检查才能进入要求的入口。,例4-66 有8个加工子程序,入口地址分别为P1,P2,P8编程实现检测键盘输入命令,使系统分别转向8个加工子程序。(键值为1转向P1,键值为2转向P2,等等。),MOV AH,1 INT 21H CMP AL,1 JE P1CMP AL,2JE P2,CMP AL,8 JE P8 JMP ST P1:P2:P3:P8:ST:HLT,3.跳转表实现多分支,利用跳转表实现多分支,就克服了上面的缺点,可以直接找到相应入口。利用这种方法要在存储器中先建立一个跳转表,表中包括每个分支的入口地址,跳转指令或关键字,利用此表就可以实现分支结构。,(1)根据表中入口地址实现分支,跳转表中存放了每个分支程序的入口地址,只要找到表地址,在将其内容取出,即可得到每个分支的入口地址。表地址=跳转表的首地址+偏移量,例4-67 将例4-66 中程序改成用跳转表来实现:,BASE DW P1,P2,P3,P4 DW P5,P6,P7,P8,(2)根据表中指令机器码实现分支,跳转表中存放的是转移指令机器码,查表后程序转到相应的子程序。图4-15给出了转移指令跳转表存放形式。,例4-68 将例4-66 程序用跳转表存放转移指令机器码实现分支。,MOV AH,1INT 21HAND AL,0FHMOV AH,0MOV BL,AL;将键值保存到BL中ADD AL,ALADD AL,BL;偏移量=键值3MOV BX,OFFSET BASEADD BX,AXJMP BX,跳转表中存放关键字,及相应分支地址,图4-16给出了关键字跳转表的格式,图4-17给出了关键字分支流程图。,(3)根据表中关键字实现分支,例4-69 将例4-66用关键字跳转表方式实现分支,BDATASEGMENTBASEDB 31H;关键字DW P1;P1入口地址DB 32H DW P2 DB38HDWP8BDATAENDS,LOP:MOV AH,1INT 21HCMP AL,0JE LOPMOV BX,OFFSET BASENEXT:CMP AL,BXJE DOADD BX,3;加3调整指针JMP NEXTDO:JMP WORD PTR BX+1,三、循环程序结构,1.循环程序结构形式,循环程序有两种结构形式:一种是“先执行,后判断”结构,另一种是“先判断,后执行”结构。图4-18给出了两种循环程序结构框图。,无论哪种循环结构都包括以下四个部分:,(1)初始化:为循环作准备,设置循环计数值,设置变量 初值。,(2)循环体:循环部分的核心,包括循环的全部执行指令。,(3)修改参数:修改操作数地址,为下次循环作准备。,(4)循环控制:修改计数器值,判断循环控制条件,决定是否跳出循环。,程序流程图如下所示:(先执行,后判断结构),例4-70 将BX中的16进制数转换为ASCII码,存放到BUF开始的内存单元中去,并在屏幕显示出数值。,MOVSI,OFFSET BUFMOV CH,4NEXT:MOV CL,4 ROLBX,CLMOV AL,BL AND AL,0FHADDAL,30HCMPAL,3AHJLSTOREADDAL,7STORE:MOV SI,AL MOV AH,2 MOV DH,ALINT21HINCSIDECCH JNZNEXTHLT,例4-71 AX寄存器中有一个16位二进制数,编程统计其中1的个数,结构放到CL寄存器中。,MOV CL,0;初始化L1:AND AX,AX;控制循环 JZ STOP SALAX,1;循环体 JNCL2 INCCLL2:JMP L1STOP:HLT,此程序采用先判断,后执行的循环结构。,2.用逻辑尺的方法控制循环,循环控制条件是循环程序设计的关键,必须结合对算法的分析来选择控制条件。有时程序要求按不同次序处理两种函数操作,可以采用逻辑尺的方法控制循环。,DATASEGMENTLOGRULEQU0011010110000000BCOUNT EQU10;循环次数BUFDB 20 DUP(?);采集数据 BLOCK DB 20 DUP(?);处理后数据DATA ENDS,MOV DX,LOGRUL;逻辑尺DX MOV CX,COUNT;设循环次数 MOV SI,OFFSET BUF MOV DI,OFFSET BLOCKNEXT:MOV AX,WORD PTR SI ROL DX,1;左移一位 JC FUN2;进位为1转FUN2FUN1:ADD AX,5 JMP NEXT1FUN2:SUB AX,3NEXT1:MOV WORD PTRDI,AX;送结果 INC SI;修改地址指针 INC SI INC DI INCDI LOOP NEXT,3.多重循环,(2)内循环可以嵌套在外循环中,也可几个内循环并列在外循环中,但各层循环之间不能交叉,可以从内循环跳到外循环,不可以从外循环中直接跳进内层循环。,有些循环结构比较复杂,需要用多重循环完成。多重 循环设计方法与单循环设计方法相同,但应注意:,(1)各重循环的初始控制条件及程序实现。,(3)防止出现死循环,即不能让循环回到初始条件,引起死循环。,例4-72 存储器数据段从BUF开始存放一个字数组,数组中第一字是存放该数组的长度N,编制一个程序使此数组中的数据按照从小到大的次序排列。,采用冒泡排序算法。从第一个数据开始相邻的数进行比较,若次序不对,两数交换位置。第一遍比较(N-1)次后,最大的数已到了数组尾,第二遍仅需比较(N-2)次就够了,共比较(N-1)遍就完成了排序,这样共有两重循环。图4-20给出了程序流程图。,开始,数I数I+1?,结束,初始化数组起始地址BX内循环次数N-1CX外循环次数N-1DX,I=0,地址加2,两数位置交换,内循环计数CX-1,外循环计数DX-1,CX=0?,DX=0?,A,A,Y,N,N,N,Y,Y,图4-20 数组排序冒泡算法流程图,ADATASEGMENTBUFDW N,15,37,8600,0A768HDW3412H,1256H,76HADATAENDSASTACKSEGMENT STACK STACK;定义堆栈段SADB 100 DUP(?)TOP LABEL WORDASTACK ENDSACODESEGMENT ASSUME CS:ACODE,DS:ADATA,SS:ASTACKMAINPROC FARSTART:MOV AX,ASTACK;将堆栈段段地址送SSMOV SS,AXMOV SP,OFFSET TOP;堆栈指针指向栈顶PUSH DS;为返回DOS作准备SUB AX,AXPUSH AX,MOV AX,ADATA;将数据段段地址送DSMOV DS,AXMOV BX,0;指向BUF第一个字MOV CX,BUFBXDEC CX;设计数器CX,内循环次数L1:MOV DX,CX;设计数器 DX,外循环次数L2:ADDBX,2 MOV AX,BUFBX;取BUFI CMP AX,BUFBX+2;若BUFI BUFI+2转JBE CONTIXCHG AX,BUFBX+2;否则两数交换MOV BUFBX,AXCONTI:LOOP L2;内循环MOV CX,DX;外循环次数CXMOV BX,0;地址返回第一个数据LOOP L1;外循环,RETMAINENDPACODEENDSEND START,四、子程序结构,1.子程序使用,(1)功能描述:子程序的名称、功能及性能(2)子程序中用到的寄存器和存储单元(3)子程序的入口参数,出口参数(4)子程序中调用其他子程序的名称,例4-74 有一个子程序说明如下:;名称:BCD2BIN;功能:将一个字节的的BCD码转换成二进制数;所用寄存器:CX;入口参数:AL存放两位BCD码;出口参数:AL存放二进制数;调其他子程序:无,子程序形式如下:BCD2BINPROC NEAR(FAR)PUSH CXMOV CH,ALAND CH,0FH;取BCD码低位MOV CL,4;设置移位次数,SHRAL,CL;取BCD码的高位MOVCL,10MULCL;高位10+低位ADDAL,CHPOPCXRETBCD2BINENDP,过程调用时,主程序使用CALL指令,调用中要处理好三个问题:,(1)保护调用程序的返回地址,这一点由CALL指令本身来完成,CPU执行CALL指令时会自动将当前断点的偏移地址值IP入栈。若是段间调用,将段基址CS和偏移地址值IP入栈。当子程序返回时,遇到子程序中RET指令,则自动将当前栈顶值弹出到IP及CS寄存器中,因此要特,别注意堆栈的使用,防止弹出地址值错误。,(2)保护某些寄存器内容,子程序中要用到某些寄存器,为了不破坏寄存器中原有的信息,要将需要保护的寄存器内容入栈,一般安排在子程序开头,用一组PUSH指令,在子程序结尾处,相应安排一组POP指令,将所保护的寄存器原来的内容恢复。注意堆栈工作方式为先进后出,所以要注意PUSH和POP指令组的次序。,(3)主程序与子程序相互之间参数的传递,由于相互之间可以传递参数才使子程序更灵活,更具有通用性,参数传递的方法有3种:1.用寄存器传递参数:适合于参数较少的场合。,2.用存储器传递参数:适合参数较多的场合,需要事先在存储器中建立一个参数表。3.用堆栈传递参数:适合参数较多的场合,尤其在子程序嵌套与递归调用的情况下,比较不容易出错。,下面举例说明参数传递的方式。例4-75 数据段定义两个数组,编程实现数组段分别求和(不计溢出)DATASEGMENTARY1 DW 100 DUP(?);定义数组1SUM1 DW?ARY2 DW 100 DUP(?);定义数组2SUM2 DW?DATAENDS,STACKSEGMENT STACKSADW 50 DUP(?)TOP EQU LENGTH SASTACKENDSCODESEGMENT ASSUME CS:CODE,DS:DATA,SS:STACKMAINPROC FARSTART:MOVAX,DATAMOV DS,AXMOV AX,STACKMOVSS,AXMOV SP,TOPLEA SI,ARY1;数组1首地址,入口参数MOV CX,LEGHTH ARY1;数组1长度,;入口参数CALL SUM;调用求和子程序,LEASI,ARY2;数组2首地址,入口参数MOV CX,LENGTH ARY2;数组2长度,;入口参数CALL SUM;调用求和子程序RETMAINENDPSUMPROC NEAR;SUM子程序XOR AX,AX;AX清0L1:ADDAX,WORD PTRSI;加数组元素INCSIINCSILOOP L1MOVWORD PTRSI,AX;数组和保存MOV AH,4CHINT21HRET;子程序返回SUMENDP,CODE ENDSEND START 本例是通过存储器来传递参数的,需要传递的数组的数保留在存储器中,调用前只需将数组偏移地址放入SI寄存器,在过程中通过寄存器间接寻址就可取得存储器中的操作数,运算结果直接由过程写回存储器中,回送给调用程序。,例4-76 通过堆栈传递参数,实现十进制数数组求和,要求主程序和子程序不在同一代码段中,要进行段间调用。源程序如下:MDATASEGMENTARY1 DB 20 DUP(?);定义数组1SUM1 DW?,ARY2 DB100 DUP(?);定义数组2SUM2 DW?MDATAENDSMSTACKSEGMENT STACKSB DW 100 DUP(?)TOP LABEL WORDMSTACKENDSMCODE SEGMENT;主程序段 ASSUME CS:MCODE,DS:MDATA,SS:MSTACKMAINPROC FARSTART:MOV AX,MSTACKMOV SS,AXMOV SP,OFFSET TOP;SP指向栈顶PUSH DS;为返回DOS作准备MOV AX,0PUSH AXMOV AX,MDATA,MOV DS,AXMOV AX,OFFSET ARY1;PADD过程;入口参数进栈PUSH AXMOV AX,SIZE ARY1PUSH AXCALL FAR PTR PADDMOV AX,OFFSET ARY2PUSH AXMOV AX,SIZE ARY2PUSH AXCALL FAR PTR PADDRETMAINENDPMCODE ENDSPCODESEGMENT;过程段,ASSUME CS:PCODE,DS:MDATA,SS:MSTACKPADDPROC FAR;PADD子程序PUSH BX;寄存器保护PUSH CX PUSH BP MOV BP,SPPUSHF;标志寄存器入栈MOV CX,BP+10;数组长度CX(?)MOV BX,BP+12;数组首地址BXMOV AX,0NEXT:ADD AL,BX;数组元素相加DAAMOV DL,ALMOV AL,0ADC AL,AHDAAMOV AH,AL,MOVAL,DLINCBXLOOP NEXTMOV BX,AX;保存数组元素之和POPFPOP BPPOP CXPOP BXRET 4;返回作废参数PADDENDPPCODEENDSEND START,这是一个用堆栈传递参数,完成段间过程调用的实例,这里要注意几点:,(1)使用堆栈传递参数,调用前要给过程传递两个参数,主程序中将数组的偏移地址值及数组长度压入堆栈,然后调用过程,过程完成数组求和运算。(2)程序设计要求段间过程调用,因此进入过程时要重新定义代码段,使之指向当前有效代码段PCODE,在过程运行中可直接调用堆栈中参数,完成累加运算,并将结果送到指定的存储单元。(3)过程返回时用返回指令RET 4,要将堆栈中由CALL指令之前传递过来的4个字节作废,然后才能返回主程序,以保证下面过程调用时参数传递正确。(4)在整个程序运行中,堆栈中数据变化见黑板。,2.子程序嵌套与递归调用,子程序本身又可调用其他子程序,称为子程序嵌套,嵌套的层数不限,只要堆栈空间足够就可以。但要注意寄存器的保护和恢复,避免各层子程序之间寄存器使用冲突,造成程序出错。图5-12给出了子程序嵌套示意图。,子程序调用子程序本身,称为子程序递归调用。下面举例说明程序嵌套与递归调用:,例4-X3 已知两个无符号数125和368,求它们的和并将和转换成十六进制数在屏幕上显示。,DATASEGMENTP DW 125,368SUM DW?DATAENDSCODESEGMENT ASSUME CS:CODE,DS:DATAMAINPROC FARSTART:PUSH DS MOV AX,0PUSH AX,MOV AX,DATAMOV DS,AXMOV SI,OFFSET PCALL PADDRETMAINENDPPADDPROC NEAR;两数相加子程序PUSH AXPUSH BXPUSH CXPUSH DXMOV AX,SIADD AX,SI+2MOV SUM,AXCALL DISP;调用显示子程序POP DX,POP CXPOP BXPOP AXRETPADDENDPDISPPROC NEAR;显示子程序MOVBX,SUMMOV CH,4L1:MOVCL,4ROLBX,CLMOVAL,BLAND AL,0FHADDAL,30H;变成ASCII码CMPAL,3AHJL L2ADD AL,07HL2:MOV DL,AL,MOV AH,2INT21HDECCHJNZL1RETDISPENDPCODEENDSEND START 此程序中有两个子程序,其中两数相加子程序中又调用了显示子程序,实现子程序嵌套。下面看一个子程序递归调用的例子。,例4-77 要求计算N!(N0),这就是一个递归调用的计算方法,N!=N(N-1)(N-2)1 N!=1,N=0 N!=N(N-1)!,N0 由上面公式知道求N!即为计算N(N-1)!,而(N-1)!要调用N!子程序,只要将调用参数修改即可。可以通过堆栈将调用参数、寄存器内容等保护起来,每调用一次将(N-1)有关信息入栈,直到N=0为止,然后开始返回,返回时将N乘以(N-1)!,直到N为设置值为止。,N!子程序说明:;名称:FACT;功能:阶乘子程序;入口参数:AL=N;出口参数:DX=N!,ADATASEGMENTD1DB 4;N=4D2DW 2 DUP(?);存放运算结果ADATAENDSASTACKSEGMENTPARA STACK STACKSADW 100 DUP(?)TOPLABEL WORDASTACKENDS,ACODESEGMENT ASSUME CS:ACODE,DS:ADATA,SS:ASTACKMAINPROC FARSTART:MOVAX,ASTACKMOVSS,AXMOVSP,OFFSET TOP;SP指向栈顶PUSHDSMOV AX,0PUSHAXMOVAX,ADATAMOVDS,AXMOVDX,0MOV AH,0MOVBX,OFFSET D1MOVAL,BX;AL=NCALLFACTMOVBX+1,DX,RETMAINENDPFACTPROC NEAR;N!子程序CMPAL,0JNZCHNMOV DL,1;若N=0,则N!=1RETCHN:PUSHAX;N入栈DECAL;N-1CALLFACT;递归调用FACT子程序POPAX;N弹出MULDL;N(N-1)!MOVDX,AX;送结果到DXRETFACTENDPACODEENDSEND START,五、综合举例,4-6 宏汇编和条件汇编,一、宏汇编,在汇编语言程序设计中,有的程序段要多次使用,除了以前谈到的过程调用的方法外,还可以用宏汇编的方法实现,尤其在子程序段本身较短,而传递的参数较多的情况下,使用宏汇编更加有效。宏是源程序中一段独立的程序段,首先对它进行定义,然后就可以用宏指令语句多次调用它了。,1.宏定义 指令使用前必须先进行宏定义,宏定义格式为:,宏指令名 MACRO 形式参数,形式参数,宏体 ENDM宏指令名:宏定义的名字,不可缺省,宏调用时要使用它,第一个符号必须是字母,其后可以上字母或数字MACROENDM:宏定义伪指令助记符,不可缺省。它 们成对出现,表示宏定义的开始和结束,ENDM 前不带宏指令名。宏体:一段有独立功能的程序代码段形式参数:又称哑元,各个哑元之间用逗号隔开,可以缺 省。,2.宏调用,经宏定义后的宏指令可以在源程序中调用,宏调用格式为:宏指令名 实参,实参 宏调用只需有宏指令名,若宏定义中有形参,那么宏调用时必须带有实际参数来替代形参,实际参数的个数、顺序类型与形参一一对应,各个实参之间用逗号隔开。原则上实参的个数与形参的个数相等,但汇编程序不要求它们必须相等,若实参个数大于形参个数,则多余的实参不予考虑,若实参个数小于形参个数,则多余的形参作空处理。,3.宏展开 汇编程序在对源程序汇编时,对每个宏调用作宏展开,即用宏定义中的宏体取代宏指令名,并用实参一一对应代替形参,每条插入的宏体指令前带上加号+。下面举例说明宏定义、宏调用及宏展开。,例4-83 不带参数的宏定义,用宏指令来实现将AL中内容右移4位。宏定义:SHIFT MACRO MOV CL,4 SAR AL,CL ENDM,宏调用:SHIFT宏展开:+MOV CL,4+SAR AL,CL,4.宏调用中参数传递 宏定义中的参数可以有多个,实参可以是数字,寄存器或操作码。下面分别举例说明:,例4-84 宏定义带一个参数,用宏指令实现将AL中内容右移任意次(256)。宏定义:SHIFTMACRO NMOV CL,N,SARAL,CLENDM 宏