汇编语言宏的特性

< 上一页宏的调用简述 Macro宏库详解下一页 >
通过《宏简述》一节的学习,我们已经对宏有了一定的了解,下面来介绍一下宏的一些特性。

1) 规定形参

利用 REQ 限定符,可以指定必需的宏形参。如果被调用的宏没有实参与规定形参相匹配,那么汇编器将显示出错消息。如果一个宏有多个规定形参,则每个形参都要使用 REQ 限定符。

下面是宏 mPutChar,形参 char 是必需的:
mPutchar MACRO char:REQ
    push eax
    mov al,char
    call WriteChar
    pop eax
ENDM

2) 宏注释

宏定义中的注释行一般都出现在每次宏展开的时候。如果希望忽略宏展开时的注释,就在它们的前面添加双分号 (;;)。示例如下:
mPutchar MACRO char:REQ
    push eax             ;; 提示:char 必须包含 8 个比特
    mov al, char
    call WriteChar
    pop eax
ENDM

3) ECHO 伪指令

在程序汇编时,ECHO 伪指令写一个字符串到标准输出。下面的 mPutChar 在汇编时会显示消息“Expanding the mPutChar macro” :
mPutchar MACRO char:REQ
    ECHO Expanding the mPutchar macro
    push eax
    mov al,char
    call WriteChar
    pop eax
ENDM
Visual Studio 2012 的控制台窗口不会捕捉 ECHO 伪指令的输出,除非在编写程序时将其设置为生成详细输出。设置方法如下:从 Tool 菜单选择 Options,选择 Projects and Solutions,选择 Build and Run,再从 MSBuild project build output verbosity 下拉列表中选择 Detailed。或者打开一个命令提示符并汇编程序。

首先,执行如下命令,调整 Visual Studio 当前版本的路径:   

"C:\Program Files\Microsoft Visual Studio 11.0\VC\bin\vcvars32"

然后,键入如下指令,其中 filename.asm 是程序的源代码文件名:

ml.exe /c /I "c:\Irvine" filename.asm

4) LOCAL 伪指令

宏定义中常常包含了标号,并会在其代码中对这些标号进行自引用。例如,下面的宏 makeString 声明了一个变量 string,且将其初始化为字符数组:
makestring MACRO text
    .data
    string BYTE text,0
ENDM
假设两次调用宏:

makeString "Hello"
makeString "Goodbye"

由于汇编器不允许两个标号有相同的名字,因此结果出现错误:

makeString "Hello"
1 .data
1 string BYTE "Hello",0
  makeString "Goodbye"
1 .data
1 string BYTE "Goodbye",0      ;错误!

使用 LOCAL

为了避免标号重命名带来的问题,可以对一个宏定义内的标号使用 LOCAL 伪指令。若标号被标记为 LOCAL,那么每次进行宏展开时,预处理程序就把标号名转换为唯一的标识符。下面是使用了 LOCAL 的宏 makeString:
makeString MACRO text
    LOCAL string
    .data
    string BYTE text,0
ENDM
假设和前面一样,也是两次调用宏,预处理程序生成的代码会将每个string替换成唯一 的标识符:

makeString "Hello"
1 .data
1 ??0000 BYTE "Hello",0
  makeString "Goodbye"
1 .data
1 ??0001 BYTE "Goodbye",0

汇编器生成的标号名使用了  ??nnnn 的形式,其中 nnnn 是具有唯一性的整数。local 伪指令还可以用于宏内的代码标号。

5) 包含代码和数据的宏

宏通常既包含代码又包含数据。例如,下面的宏mWrite在控制台显示文本字符串:
mWrite MACRO text
    LOCAL string            ;;local号
    .data                   ;;定义字符串
    string BYTE text,0
    .code
    push edx
    mov edx, OFFSET string
    call WriteString
    pop edx
ENDM
下面的语句两次调用宏,并向其传递不同的字符串文本:

mWrite "Please enter your first name"
mWrite "Please enter your last name"

汇编器对这两条语句进行展开时,每个字符串都被赋予了唯一的标号,且MOV指令也 作了相应的调整:

mWrite "Please enter your first name"
1 .data
1 ??0000 BYTE "Please enter your first name",0
1 .code
1 push edx
1 mov edx, OFFSET ??0000
1 call WriteString
1 pop edx
  mWrite "Please enter your last name"
1 .data
1 ??0001 BYTE "Please enter your last name", 0
1 .code
1 push edx
1 mov edx, OFFSET ??0001
1 call Writestring
1 pop edx

6) 宏嵌套

被其他宏调用的宏称为被嵌套的宏 (nested macro)。当汇编器的预处理程序遇到对被嵌套宏的调用时,它会就地展开该宏。传递给主调宏的形参也将直接传递给它的被嵌套宏。

提示:使用模块方法创建宏。保持它们的简短性,以便将它们组合到更复杂的宏内。这样有助于减少程序中的复制代码量。

【示例】下面的宏 mWritein 写一个字符串文本到控制台,并添加换行符。它调用宏 mWrite 和 Crlf 过程:
mWriteln MACRO text
    mWrite text
    call Crlf
ENDM
形参 text 被直接传递给 mWrite。假设用下述语句调用 mWriteln:

mWriteln "My Sample Macro Program"

在结果代码展开,语句旁边的嵌套级数(2)表示被调用的是一个嵌套宏:

mWriteln "My Sample Macro Program"
2   .data
2   ??0002 BYTE "My Sample Macro Program",0
2   .code
2   push edx
2   mov    edx,OFFSET ??0002
2   call WriteString
2   pop    edx
1   call Crlf

< 上一页宏的调用简述 Macro宏库详解下一页 >