第9章 宏
宏是程序设计的另 一个基本概念,它是把一段程序代码用一个特定标识符(即:宏名)来表示。这样,在编写源程序时,程序员就可以直接使用该标识符来代替一段代码的编写,从而 减少了重复代码的编写工作,也为减少错误,提高程序的可维护性提供了帮助。宏在高级语言(如:C/C 等)也有广泛的使用。
9.1 宏的定义和引用
通常情况下,宏是用来代表一个具有特定功能的程序段,它只需在源程序中定义一次,但可在源程序中引用多次。只要在编写程序时需要,就可以直接使用它。
9.1.1 宏的定义
在使用宏之前必须先定义宏,定义宏一般格式如下:
|
宏名 |
MACRO [形参1, 形参2, ……] |
|
… |
;宏的定义体 |
||
ENDM |
在书写宏定义时,必须遵照下列规定:
|
、MACRO和ENDM是二个必须成对出现的关键字,它们分别表示宏定义的开始和结束; |
、MACRO和ENDM之间的部分是宏的定义体,它是由指令、伪指令或引用其它宏所组成的程序片段,是宏所包含的具体内容; |
|
、“宏名”是由程序员指定的一个合法的标识符,它代表该宏; |
|
、宏名可以与指令助忆符、伪指令名相同。在这种情况下,宏指令优先,而同名的指令或伪指令都失效; |
|
、在ENDM的前面不要再写一次宏名,这与段或子程序定义的结束方式有所不同; |
|
、在宏定义的首部可以列举若干个形式参数,每个参数之间要用逗号分隔。 |
根据上述规定,我们提倡宏名尽可能不要与指令助忆符、伪指令相名,以免引起不必要的误会。
例9.1 定义一个把16位数据寄存器压栈的宏。
|
PUSHR |
MACRO |
|
PUSH |
AX |
||
PUSH |
BX |
||
PUSH |
CX |
||
PUSH |
DX |
||
ENDM |
例9.2 定义二个字存储变量相加的宏。
|
MADDM |
MACRO OPRD1, OPRD2 |
|
MOV |
AX, OPRD2 |
||
ADD |
OPRD1, AX |
||
ENDM |
上述宏定义虽然能满足题目的要求,但由于在定义体中改变了寄存器AX的值,这就使宏的引用产生了一定的副作用。为了使寄存器AX的使用变得透明,可把该宏定义改成如下形式:
|
MADDM |
MACRO OPRD1, OPRD2 |
|
PUSH |
AX |
||
MOV |
AX, OPRD2 |
||
ADD |
OPRD1, AX |
||
POP |
AX |
||
ENDM |
通过在宏定义的开始和结尾分别增加对所用寄存器的保护和恢复指令,就使得:对该宏的任意引用都不会产生任何副作用。
9.1.2 宏的引用
在源程序中,一旦定义了某宏,那么,在该程序的任何位置都可直接引用该宏,而不必重复编写相应的程序段。引用宏的一般格式如下:
宏名 [实参1, 实参2, ……]
其中:实参的位置要与形参的位置要对应,但实参的个数可以与形参的个数不相等。
例如:假设已有字变量w1和w2,并且也有例9.2中的宏MADDM,那么,如果要把w2的内容加到w1中的话,就可以在源程序的代码段中按下列方式来引用该宏(点击它,可显示宏展开后的形式):
MADDM w1, w2
注意:以上的宏展开形式可以在LST文件中看到,宏展开所得到的语句前面有一个数字(如:1),它表示宏展开的层次。
如果在源程序中按下列方式来引用了宏MADDM,那么,在宏展开时就会得到下列语句:
MADDM w1
9.1.3 宏的参数传递方式
在引用宏时,参数是通过“实参”替换“形参”的方式来实现传递的。参数的形式灵活多样,参数可以是常数、寄存器、存储单元和表达式,还可以是指令的操作码。
例9.3 定义二个字存储变量相加和相减的宏。
|
、当实参的个数多于形参的个数时,多出的实参被忽略; |
、当实参的个数少于形参的个数时,没有实参对应的形参用“空”来对应。但在宏展开时,所得到的指令必须是合法的汇编指令,否则,汇编程序将会给出出错信息。 |
下面是引用宏MOPM的二个例子,它们在宏展开时将会得到下列语句:
MOPM SUB, w1, w2
…
MOPM ADD, w1, w2