第7章宏和多模块程序设计7.1宏7.2宏嵌套7.3重复汇编伪指令和条件汇编伪指令7.4宏汇编语言源程序举例和宏与子程序的区别7.5多模块程序设计7.6汇编语言与高级语言的连接7.1宏7.1.1宏定义7.1.2宏调用7.1.3宏展开7.1.4宏的参数传递7.1.5特殊宏操作符7.1.6宏中的变量和标号返回首页7.1.1宏定义一、在使用宏之前必须先定义宏,宏的定义是使用伪指令MACRO和ENDM来实现的。1.宏定义的一般格式:宏名MACRO[形参1,形参2,……]……………………;宏的定义体ENDM(1)宏名:由程序员指定的一个合法的标识符,它代表该宏。宏名可以与指令助忆符、伪指令名相同,在这种情况下,宏指令优先。建议宏名尽可能不要与指令助忆符、伪指令相名,以免引起不必要的误会。(2)MACRO和ENDM是二个必须成对出现的关键字,它们分别表示宏定义的开始和结束,在ENDM的前面不要再写一次宏名,这与段或子程序定义的结束方式有所不同;(3)“宏体”:MACRO和ENDM之间的部分,由指令、伪指令或引用其它宏所组成的程序片段,是宏所包含的具体内容;(4)在宏定义的首部可以列举若干个形式参数,每个参数之间要用逗号分隔。(形式参数:定义中仅指明了使用哪些参数(参数名),但未具体说明参数值。实参数:参数值在引用宏时给出。)【例7-1】定义一个实现两个16位的带符号数存储变量相加的宏。宏名为MADDM。解:程序段如下:MADDMMACROOPRD1,OPRD2;定义一个宏名为MADDM,;形参为OPRD1和OPRD2的宏MOVAX,OPRD2;本句和下句为宏体中的内;容ADDOPRD1,AX;实现两数相加,结果送;OPRD1变量ENDM;宏定义结束上述宏定义虽然能满足题目的要求,但由于在定义体中改变了寄存器AX的值,这就使宏的引用产生了一定的副作用。为了使寄存器AX的使用变得透明,可把该宏定义改成如下形式:MADDMMACROOPRD1,OPRD2;定义一个宏名为MADDM,形参为OPRD1和OPRD2的宏PUSHAX;将AX寄存器的内容压入堆栈;保护AX存器的值MOVAX,OPRD2;ADDOPRD1,AXPOPAX;恢复AX寄存器的值ENDM;宏定义结束通过在宏定义的开始和结尾分别增加对所用寄存器的保护和恢复指令,就使得对该宏的任意引用都不会产生任何副作用。返回本节7.1.2宏调用一、在源程序中,一旦定义了某宏,那么,在该程序的任何位置都可直接引用该宏,而不必重复编写相应的程序段。1.引用宏的一般格式如下:宏名[实参1,实参2,……]说明:(1).宏名必须与宏定义中的宏名一致;(2).实参的位置要与形参的位置要对应,但实参的个数可以与形参的个数不相等。当实参的个数多于形参的个数时,多出的实参被忽略;当实参的个数少于形参的个数时,没有实参对应的形参用“空”来对应。但在宏展开时,所得到的指令必须是合法的汇编指令,否则,汇编程序将会给出出错信息。例如:调用例7-2所定义的宏DISPMSG,实现字符串的输出。程序段如下:………………DISPMSGMESSAGE1………………DISPMSGMESSAGE2………………DISPMSGMESSAGE3返回本节7.1.3宏展开一、定义在源程序中,由于宏定义在前,宏调用在后,宏汇编程序在汇编期间,将先扫描宏定义,将宏名、形参、宏体均填入宏定义表中,以后遇到宏调用时,则嵌入宏体,并用实参按位置对应关系一一替换宏体中的形参,这一过程称为宏展开。宏展开是在汇编期间由汇编程序自动完成的。针对上述的三次宏调用,其展开后得到下列语句:第一次宏调用后,展开的语句序列。………………PUSHDXPUSHAXLEADX,MESSAGE1MOVAH,09HINT21HPOPAXPOPDX第二次宏调用后,展开的语句序列。………………PUSHDXPUSHAXLEADX,MESSAGE2MOVAH,09H;INT21HPOPAXPOPDX第三次宏调用后,展开的语句序列。………………PUSHDXPUSHAXLEADX,MESSAGE3MOVAH,09H;INT21HPOPAXPOPDX………………宏调用时,在汇编期间被展开,调用一次展开一次。虽然多占用一些空间,但是使用方便,适合短代码程序段。返回本节7.1.4宏的参数传递一、在引用宏时,参数是通过“实参”替换“形参”的方式来实现传递的。注意:(1)参数可以是常数、寄存器、存储单元和表达式,还可以是指令的操作码。(2)由以上程序段可知,宏使用的参数非常灵活,但在宏展开后,所得到的指令必须是合法的汇编指令--如下例:【例7-3】定义一个实现两个带符号的16位数相乘的宏,宏名为MULTIPLY,并在源程序中调用该宏。解:程序段如下:;宏定义MULTIPLYMACROOPR1,OPR2,RESULT;定义一个宏名为;MULTIPLY形参为:;OPR1,OPR2,RESULTPUSHDXPUSHAXMOVAX,OPR1;将乘数送AX寄存器IMULOPR2;将(AX)与OPR2相乘,结果送寄存器DX和AXMOVRESULT,AX;将结果的低16位送RESULT存储单元MOVRESULT+2,DX;将结果的高16位送RESULT的下一个存储单元POPAXPOPDXENDM;宏定义结束;宏调用MULTIPLYCX,VAR,,YZ[BX];形参分别为寄存器,;存储单元和存储单元以上宏调用后,宏展开的语句序列为:PUSHDXPUSHAXMOVAX,CXIMULVAR;VAR不能为立即数MOVXYZ[BX],AXMOVXYZ[BX]+2,DXPOPAXPOPDX形参OPR2所对应的实参VAR可以是寄存器操作数或存储器操作数,但不能为立即数和段寄存器,否则,汇编程序将会给出出错信息。【例7-4】定义一个能实现对存储变量进行的加、减操作合并在一起的宏。宏名为MOPM。解:程序段如下:;宏定义MOPMMACROOP,OPRD1,OPRD2MOVAX,OPRD2OPOPRD1AXENDM;宏调用MOPMSUBA1,A2;形参分别为指令,变量和变量MOPMADDB1,B2;形参分别为指令,变量和变量它们在宏展开时将会得到下列语句:MOVAX,A2SUBA1,AXMOVAX,B2ADDB1,AX返回本节7.1.5特殊宏操作符1.字符串整体传递运算符“”在宏调用中,有时实参是一串带间隔符空格或逗号的字符串,为了不致混淆(把一个实参看成几个实参),应该用尖括号将它们括起来。【例7-5】定义一个宏,实现堆栈段的定义。解:程序段如下:;宏定义:STMACROASTACKSEGMENTSTACKDBASTACKENDSENDM………………;宏调用ST500DUP(0);实参为“500DUP(0)”,宏调用实现功能:建立一个500个字节,赋初值为0的堆栈段。说明:在以上宏调用时,由于实参中间带有空格,因此要用尖括号括起来,说明该语句为一个实参,以上宏扩展后的语句序列为:STACKSEGMENTSTACKDB500DUP(0)STACKENDS2.计算表达式运算符“%”有时需要以实参符号的值而不是符号本身替换形参,这种参数的替换称为数字参数的替换用操作符“%”将其后的表达式转换成它所代表的数值,并将些数值的ASCII码字符嵌入到宏扩展中。例如,有以下宏定义:DATAMACROA,B,CDBA,BDBCDUP(0)ENDM如果存在以下宏调用:X=1Y=2DATAX,4,X+YDATA%X,4,%X+Y则宏扩展后的语句序列为:X=1Y=2DBX,4DBX+YDUP(0)DB1,4DB3DUP(0)%后的符号一定是直接用EQU或等号赋值的符号常量或者汇编时能计算出来的表达式而不能是变量名和寄存器名。3.连接运算符“&”在宏定义中,如果想将形式参数与其它字符连接在一起,构成一条指令或一个操作数或一个字符串,那么,就必须使用连接运算符“&”【例7-6】定义一个宏DATADEF,通过连接运算符“&”,把前后两个符号合并成一个变量名。解:程序段如下:;宏定义DATADEFMACRONAMEVARDATA&NAMEBYTEVARENDM;如果有以下宏调用DATADEFTEST,10DATADEF1,–1则宏展开后,得到以下语句序列:DATATESTBYTE10DATA1BYTE–1【例7-7】定义一个转移宏JUMP,其一个参数决定转移类别,另一个参数指定转移目标。解:程序段如下:;宏定义:JUMPMACROCON,HEREJ&CONHEREENDM;假设存在下面二个引用语句。JUMPL,NEXT1JUMPG,NEXT2宏扩展后的语句序列为:JLNEXT1;小于则转NEXT1JGNEXT2;大于则转NEXT24.字符原意运算符“!”在宏的定义和调用时,可能含有特殊字符&、<>、%、!,如果需要使用这些字符的本身,不将它们当做特殊字符来处理,就可以在这些特殊字符前面加上一个“!”例如,有以下宏定义DEFSTRMACROSTRNAMESTRVALSTRNAMEDB‘STRVAL’,0DH,0AH,’$’ENDM如果存在以下宏调用:DEFSTRTHXYInputonenumber(90):宏展开后的语句为:THXYDB‘Inputonenumber’,0DH,0AH,'$‘我们希望第一个“>”做大于号处理,则我们可以采用原意运算符“!”第一个“>”与字符“<”相匹配,构成一个字符串整体传递运算符“<>”,并不会把第一个“>”做大于号字条处理。把以上宏调用改为:DEFSTRTHXYInputonenumber(!90):则宏展开后的语句为:THXYDB‘Inputonenumber(90):’,0DH,0AH,'$'返回本节7.1.6宏中的变量和标号编写程序时有一个规则,要求在一个程序中,标号必须唯一的,即一个标号不允许在一个程序中的标号域出现两次以上,否则视为重复定义,在汇编时将会出现错误。如果在宏定义中有标号,那么多次宏调用后在标号域中必然会多次重复出现同一个标号。【例7-8】定义一个宏,求1+2+3+....+n的值解:程序段如下:;宏定义SUMMACRONDATAMOVAX,0;初始化AX寄存器的值,AX保存累加结果MOVCX,N;初始化CX寄存器的值NEXT:ADDAX,CXLOOPNEXT;CX寄存器内容不等于0,则跳转到标号NEXT处执行MOVDATA,AX;将累加结果送DATA存储单元保存ENDM;宏调用SUM10X;求1+2+....+10的值,结果送X变量SUM100Y;求1+2+....+100的值,结果送Y变量汇编程序在汇编时,宏扩展后的语句序列为:MOVCX,10MOVAX,0NEXT:ADDAX,CXLOOPNEXTMOVX,AXMOVCX,100MOVAX,0NEXT:ADDAX,CXLOOPNEXTMOVY,AX标号NEXT出现了两次,出现重复定义的错误为了解决这个问题,可以采取以下两种方法。(1)把标号定义为参数形式在宏定义中把标号定义形参,在宏调用中用实参代替它,每次宏调用时,必须使用不同的实参。(2)在宏定义中声明标号为局部标号在宏定义中声明标号为局部标号须用说明局部标号伪指令LOCAL。局部标号伪指令的一般格式为:LOCAL标号1[,标号2]…说明:标号1,标号2,…是宏体中出现的标号。LOCAL只允许在宏定义中使用,且必须是MACRO面的第一个语句。修改程序段;宏定义SUMMACRONDATALOCALNEXTMOVCX,N;初始化寄存器CX的值MOVAX,0;初始化寄存器AX的值,AX保存累加结果NEXT:ADDAX,CXLOOPNEXT;(CX)不等于0,则跳转到标号NEXT处执行MOVDATA,AX;将累加结果送DATA存储单元保存ENDM;宏调用SUM10X;求1+2+....+10的值SUM100Y;求1+2+....+100的值汇编程序在每次进行宏扩展时,总是把由LOCAL说明的标号用一个唯一的标号(从??0000到??FFFF)来代替,从而避免标号