宏是汇编器一项功能强大的预处理命令.它和其他的预处理命令一样,在汇编器开始汇编代码之前就对宏进行了处理.它的语法规则如下:
Name MACRO [parameter[:tag]] [,parameter[:tag]]…]
[LOCAL varlist]
Statements
[EXITM[textitem]]
ENDM
Name是一个独一无二的标识符,在后面的程序代码中将会使用此标识符来调用该宏.Parameter是宏的过程里面使用的变量,它就像函数里面的参数.Tag可以是:REQ,:=default和:VARARG里面的一个.
如果在变量(parameter)后面使用:REQ的话,就表示该parameter不能为空,否则汇编器将会报告出错.
如果变量(parameter)后面使用:=default的话,那么当调用该宏的时候,如果没有给:=default前面的parameter传递参数的话,汇编器将会使用default的内容来作为默认值.
如果在变量(parameter)后面使用:VARARG的话,那么这个parameter只有在作为MACRO关键字的最后一个参数的时候才会起作用.
Statement可以是任何符合宏汇编语法的汇编语言过程.
Textitem可以作为整个宏过程的一个可选择返回值.这里的可选择的意思是,可以有此textitem也可以没有.
宏除了可以用来替代一些字符串之外,它还可以进行复杂的运算,扩充指令等用途.下面将详细地介绍有关宏的关键字用法.
宏的基础语法关键字
MACRO
此关键字标志着整个宏的开始,在MACRO关键字的前面必须有一个name,这是该宏的名字,在汇编器汇编的时候将把程序里面出现该name的地方都用该name对应的宏里面的内容来代替这个name.在MACRO关键字的后面可以跟上需要的参数.
例:
RGB MACRO red, green, blue
XOR EAX, EAX
MOV AL, blue ;blue
ROL EAX, 8
MOV AL, green ;green
ROL EAX, 8
MOV AL, red ;red
ENDM
上面的这个例子宏的名字 使用 了RGB 标识符,这个宏带了三个参数,分别为red, green, blue. 该宏的作用是把这三个参数按顺序放到EAX寄存器里面,最后的返回值默认为EAX.这个API函数的返回方式是一致的.
在程序中使用上面宏的时候,宏的参数可以是三个立即数,三个内存变量,三个字节型的寄存器或是任意的混合形式,如下所示:
RGB 125, 175, 225
RGB byte1, byte2 ,byte3
RGB cl, ch, dl
RGB 125, byte, al
在宏的嵌套方面,过程宏和函数宏可以被嵌套40层,文字宏可以被嵌套20层.一个过程宏可以被重新定义,但定义的内容只会影响重新定义后面的代码的内容.而函数宏一旦使用过后就不能被重新定义了.
ENDM
结束一个宏,与MACRO关键字相配对
EXITM
该关键字用来结束一个宏,功能就像ENDM关键字一样,并且在该关键字和ENDM之间的代码将都不被汇编,因此该关键字主要用于条件汇编.
例:EXITM 关键字的使用
WORDS MACRO count
If count eq 0
EXITM
Endif
Word count dup (?)
ENDM
.data
Myword1 dw 8
WORDS 8
WORDS 0
上面的例子中,共定义了9个字的内存空间.其中语句WORDS 8 定义了8个字的内存空间,而语句 WORDS 0未定义出任何内存空间.
另外,当EXITM 关键字后面带有返回值Textitem 时,该宏即为一个函数宏,它带有返回值.如下所示:
.386
.model flat,stdcall
@retVal MACRO
LOCAL txt
Txt TEXTEQU <this is a function macro>
EXITM txt
ENDM
%echo &retVal()
.code
A:
End a
在上面的例中,定义了一个函数宏 @retVal,在宏中定义了一个局部变量,并对该变量赋一个字符串,在这个宏结束的时候,把该局部变量作为返回值返回.Echo伪指令用来在命令行输出信息.
宏里面的循环指令关键字
FOR
该关键字使用格式如下:
FOR parameter[:REQ|:=default],<argument [,argument]…>
statements
ENDM
该关键字作用是用每个argument来代替statements里面的parameter并执行一遍statements过程.
上述格式中各个部分的含义如下:
Parameter 可以是任意的一个有效的变量名.
如果在变量(parameter)后面使用:REQ的话,则表示该parameter不能为空,否则汇编器将会报告出错.
如果在变量(parameter)后面使用:default的话,那么当调用该宏的时候,如果没有给:=default 前面的parameter传递参数的话,汇编器将会使用default内容来作为默认值.
Argument 可以是一个text ,标识符,字符串,或是一个数字常量.如果一个字符串argument里面有空格或逗号的话,就需要在这个字符串的argument两边用尖括号(<>)括起来.
Statement 可以是任何符合宏汇编语法的汇编语言过程.
例:使用FOR关键字
FORS MACRO count
For value,<1,2,3,4,5,4,3,2,1>
If value GT count
Exitm
Endif
Byte value
ENDM
ENDM
.data
Var1 dw 11h
FORS 3
Var2 db offh
上面的例子中定义了一个FORS 宏,该宏用于定义一串字节型内存空间,并给出内存空间的初始值,显然,该宏只能用于.data数据段.
FORC
该关键字使用格式如下:
FORC parameter ,<string>
statements
ENDM
该关键字的作用是用string里面的每个字符来代替statements里面的parameter并执行一遍statements过程.
上述格式中各个部分含义如下:
Parameter 可以是任意的一个有效的变量名.它在statements里被执行的时候会被string参数里面的不同的字符代替.
String 可以是一个字符串,也可以是一个字符串变量.字符串里面的变量也被作为一个字符在statements里面执行一遍.最后需要在这个字符串string两边用尖括号(<>)括起来.
Statements 可以是任何符合宏汇编语法的汇编过程.
GOTO
该关键字的使用格式如下:
GOTO macrolabel
该关键字的作用是让汇编器跳往macrolabel所标识的地方,这个标识符只可以存在于MACRO ,FOR ,FORC REPEAT ,WHILE模块里.
GOTO关键字的使用:
Myname MACRO var1
XOR EAX,EAX ;----------1
GOTO label ;----------2
MOV EBX,var1 ;-------------3
:Label ;------------4
MOV EAX,var1 ;--------------5
ENDM
在上面中,在执行完第一行之后发现第二行是一个GOTO语句,所以直接找到LABEL 的位置并继续执行,即开始执行第五行.
REPEAT
该关键字的使用格式如下:
REPEAT expression
statements
ENDM
该关键字的作用是让汇编器把statements里面的内容汇编expression遍.
Expression是一个常数,表示要重复汇编的次数.
WHILE
该关键字的使用格式如下:
WHILE expression
statements
ENDM
该关键字的作用是让汇编器把statements 里面的内容,重复地汇编直到表达式expression 的内容为零(false)
Expression 是一个表达式,该表达式的值会在汇编的时候改变.
条件测试关键字
IF ,ELSEIF ,ELSE ,ENDIF
例:
Retval MACRO var
IF var EQ 0
XOR EAX, EAX
ELSE
MOV EAX,var
ENDIF
ENDM\
当汇编器在汇编源程序的时候,发现程序里有retval关键字,然后它根据宏的内容判断var的值是否为零,如果是的话,那么汇编器就把XOR EAX,EAX这条语句放到retval出现的地方.注意在宏里面的条件表达式不能够使用 ==,!= , >, < 等符号.
这些条件测试语句可以被嵌套20多次.
宏定义中条件表达式里面的关系操作符
EQ 1 EQ 2 两表达式相等返回真,否则返回假
NE 1 NE 2 不相等返回真,相等为假
GT 表达式1大于表达式则为真,否则为假
LT 和上面相反
GE 表达式1大于等于表达式2返回真,否则为假
LE 表达式1小于等于表达式2返回真,否则返回假
宏里面的其他关键字:
<text>
我们在使用FOR关键字的时候经常会在后面的参数里用到尖括号(<>),因为有些参数里面包含逗号,空格,分号等特殊的符号,当想把它们作为一个参数来处理时,就需要用到尖括号,如下:
尖括号的使用:
MyMacro <one,two,three> ;传递一个参数给MyMacro宏
MyMacro one,two,three ;传递三个参数给MyMacro宏
在上面的中MyMacro 是一个已经定义过的宏,例子中的第一种方法传递了一个参数给MyMacro宏,而第二种方法传递了三个参数给MyMacro宏.
LOCAL
有些时候我们可能会希望在宏里面定义一些局部变量或是一些局部标志符,这时就可以使用关键字LOCAL 来定义这个宏里面的局部变量或局标志符.这个关键字和WIN32汇编程序里面定义局部变量的关键字是一样的.具体定义格式如下:
LOCAL localname [,localname]…
其中localname 是要定义的局部变量的变量名.
PUSHCONTEXT 和 POPCONTEXT
在宏里面,当我们需要保存汇编器当时的一些状态值到堆栈时,不需要用PUSH 和POP机器指令把值一个一个压入和弹出堆栈,我们可以用汇编器提供的PUSHCONTEXT和POPCONTEXT两个关键字来方便地处理这些工作.具体使用格式如下:
PUSHCONTEXT context
POPCONTEXT context
在上面的格式中,PUSHCONTEXT 关键字把context的内容都依次地压入到堆栈里面,而POPCONTEXT关键字则把context的内容从堆栈中恢复.这两个关键字可以嵌套10层.
ASSUMES 寄存器状态值
RADIX 当前默认的基数
LISTING 所有标识寄存器的内容
CPU 处理器和协处理器里面的指令
ALL 上面所有的内容
&parameter&
有时候我们需要在宏里面把一个变量插入到一个字符串里面,这个时候我们可以用&符号来完成这个目的.该符号的使用格式如下:
String & parameter & string
其中string 为任意的字符串,parameter为我们想要插入的变量名.如果想在字符串里面显示一个&符号,就需要在这个符号前面加上感叹号(!).
例:
.386
.model flat,stdcall
Guard MACRO Pisonernum:REQ,Prisonername := <Anonymouse>
LOCAL txt
&ECHO there are &Prisonernum& &Prisonername& have arrived at @time.
Txt textequ <Prisonernum&&Prisonernum>
EXITM txt
ENDM
%echo Guard(2)
.code
Strcat:
End strcat
其中@TIME表示引用系统时间.
VARARG (macro)
当我们希望定义一个参数个数不定的宏的时候,可以使用VARARG关键字,来定义一个具有VARARG属性的变量.当具体使用该宏的时候只需要把这些参数用逗号隔开就行了.具有该属性的参数只能放在宏的参数的最后面,例:
.386
.model flat,stdcall
ArgCount MACRO parmlist:VARRG
LOCAL count
Count = 0
FOR parm,<parmlist>
Count = count+1
ENDM
EXITM<count>
EMDM
.code
Var:
MOV EAX,ArgCount(1,2,3,4) ;EAX结果为4
MOV EBX,ArgCount(a,b) ;EBX结果为2
End var
上面定义了一个名为ArgCount的宏,该宏含有一个属性为VARARG的变量,在这个宏的过程当中使用了FOR循环语句,把传递进来的参数的个数进行统计.在代码段中使用该宏的语句传入了4个参数,所以该宏的返回值为4,即在EAX寄存器存入4;同理,第二条语句传给该宏两个参数,所以返回值为2.
用于字符串操作的预定义宏
Name MACRO [parameter[:tag]] [,parameter[:tag]]…]
[LOCAL varlist]
Statements
[EXITM[textitem]]
ENDM
Name是一个独一无二的标识符,在后面的程序代码中将会使用此标识符来调用该宏.Parameter是宏的过程里面使用的变量,它就像函数里面的参数.Tag可以是:REQ,:=default和:VARARG里面的一个.
如果在变量(parameter)后面使用:REQ的话,就表示该parameter不能为空,否则汇编器将会报告出错.
如果变量(parameter)后面使用:=default的话,那么当调用该宏的时候,如果没有给:=default前面的parameter传递参数的话,汇编器将会使用default的内容来作为默认值.
如果在变量(parameter)后面使用:VARARG的话,那么这个parameter只有在作为MACRO关键字的最后一个参数的时候才会起作用.
Statement可以是任何符合宏汇编语法的汇编语言过程.
Textitem可以作为整个宏过程的一个可选择返回值.这里的可选择的意思是,可以有此textitem也可以没有.
宏除了可以用来替代一些字符串之外,它还可以进行复杂的运算,扩充指令等用途.下面将详细地介绍有关宏的关键字用法.
宏的基础语法关键字
MACRO
此关键字标志着整个宏的开始,在MACRO关键字的前面必须有一个name,这是该宏的名字,在汇编器汇编的时候将把程序里面出现该name的地方都用该name对应的宏里面的内容来代替这个name.在MACRO关键字的后面可以跟上需要的参数.
例:
RGB MACRO red, green, blue
XOR EAX, EAX
MOV AL, blue ;blue
ROL EAX, 8
MOV AL, green ;green
ROL EAX, 8
MOV AL, red ;red
ENDM
上面的这个例子宏的名字 使用 了RGB 标识符,这个宏带了三个参数,分别为red, green, blue. 该宏的作用是把这三个参数按顺序放到EAX寄存器里面,最后的返回值默认为EAX.这个API函数的返回方式是一致的.
在程序中使用上面宏的时候,宏的参数可以是三个立即数,三个内存变量,三个字节型的寄存器或是任意的混合形式,如下所示:
RGB 125, 175, 225
RGB byte1, byte2 ,byte3
RGB cl, ch, dl
RGB 125, byte, al
在宏的嵌套方面,过程宏和函数宏可以被嵌套40层,文字宏可以被嵌套20层.一个过程宏可以被重新定义,但定义的内容只会影响重新定义后面的代码的内容.而函数宏一旦使用过后就不能被重新定义了.
ENDM
结束一个宏,与MACRO关键字相配对
EXITM
该关键字用来结束一个宏,功能就像ENDM关键字一样,并且在该关键字和ENDM之间的代码将都不被汇编,因此该关键字主要用于条件汇编.
例:EXITM 关键字的使用
WORDS MACRO count
If count eq 0
EXITM
Endif
Word count dup (?)
ENDM
.data
Myword1 dw 8
WORDS 8
WORDS 0
上面的例子中,共定义了9个字的内存空间.其中语句WORDS 8 定义了8个字的内存空间,而语句 WORDS 0未定义出任何内存空间.
另外,当EXITM 关键字后面带有返回值Textitem 时,该宏即为一个函数宏,它带有返回值.如下所示:
.386
.model flat,stdcall
@retVal MACRO
LOCAL txt
Txt TEXTEQU <this is a function macro>
EXITM txt
ENDM
%echo &retVal()
.code
A:
End a
在上面的例中,定义了一个函数宏 @retVal,在宏中定义了一个局部变量,并对该变量赋一个字符串,在这个宏结束的时候,把该局部变量作为返回值返回.Echo伪指令用来在命令行输出信息.
宏里面的循环指令关键字
FOR
该关键字使用格式如下:
FOR parameter[:REQ|:=default],<argument [,argument]…>
statements
ENDM
该关键字作用是用每个argument来代替statements里面的parameter并执行一遍statements过程.
上述格式中各个部分的含义如下:
Parameter 可以是任意的一个有效的变量名.
如果在变量(parameter)后面使用:REQ的话,则表示该parameter不能为空,否则汇编器将会报告出错.
如果在变量(parameter)后面使用:default的话,那么当调用该宏的时候,如果没有给:=default 前面的parameter传递参数的话,汇编器将会使用default内容来作为默认值.
Argument 可以是一个text ,标识符,字符串,或是一个数字常量.如果一个字符串argument里面有空格或逗号的话,就需要在这个字符串的argument两边用尖括号(<>)括起来.
Statement 可以是任何符合宏汇编语法的汇编语言过程.
例:使用FOR关键字
FORS MACRO count
For value,<1,2,3,4,5,4,3,2,1>
If value GT count
Exitm
Endif
Byte value
ENDM
ENDM
.data
Var1 dw 11h
FORS 3
Var2 db offh
上面的例子中定义了一个FORS 宏,该宏用于定义一串字节型内存空间,并给出内存空间的初始值,显然,该宏只能用于.data数据段.
FORC
该关键字使用格式如下:
FORC parameter ,<string>
statements
ENDM
该关键字的作用是用string里面的每个字符来代替statements里面的parameter并执行一遍statements过程.
上述格式中各个部分含义如下:
Parameter 可以是任意的一个有效的变量名.它在statements里被执行的时候会被string参数里面的不同的字符代替.
String 可以是一个字符串,也可以是一个字符串变量.字符串里面的变量也被作为一个字符在statements里面执行一遍.最后需要在这个字符串string两边用尖括号(<>)括起来.
Statements 可以是任何符合宏汇编语法的汇编过程.
GOTO
该关键字的使用格式如下:
GOTO macrolabel
该关键字的作用是让汇编器跳往macrolabel所标识的地方,这个标识符只可以存在于MACRO ,FOR ,FORC REPEAT ,WHILE模块里.
GOTO关键字的使用:
Myname MACRO var1
XOR EAX,EAX ;----------1
GOTO label ;----------2
MOV EBX,var1 ;-------------3
:Label ;------------4
MOV EAX,var1 ;--------------5
ENDM
在上面中,在执行完第一行之后发现第二行是一个GOTO语句,所以直接找到LABEL 的位置并继续执行,即开始执行第五行.
REPEAT
该关键字的使用格式如下:
REPEAT expression
statements
ENDM
该关键字的作用是让汇编器把statements里面的内容汇编expression遍.
Expression是一个常数,表示要重复汇编的次数.
WHILE
该关键字的使用格式如下:
WHILE expression
statements
ENDM
该关键字的作用是让汇编器把statements 里面的内容,重复地汇编直到表达式expression 的内容为零(false)
Expression 是一个表达式,该表达式的值会在汇编的时候改变.
条件测试关键字
IF ,ELSEIF ,ELSE ,ENDIF
例:
Retval MACRO var
IF var EQ 0
XOR EAX, EAX
ELSE
MOV EAX,var
ENDIF
ENDM\
当汇编器在汇编源程序的时候,发现程序里有retval关键字,然后它根据宏的内容判断var的值是否为零,如果是的话,那么汇编器就把XOR EAX,EAX这条语句放到retval出现的地方.注意在宏里面的条件表达式不能够使用 ==,!= , >, < 等符号.
这些条件测试语句可以被嵌套20多次.
宏定义中条件表达式里面的关系操作符
EQ 1 EQ 2 两表达式相等返回真,否则返回假
NE 1 NE 2 不相等返回真,相等为假
GT 表达式1大于表达式则为真,否则为假
LT 和上面相反
GE 表达式1大于等于表达式2返回真,否则为假
LE 表达式1小于等于表达式2返回真,否则返回假
宏里面的其他关键字:
<text>
我们在使用FOR关键字的时候经常会在后面的参数里用到尖括号(<>),因为有些参数里面包含逗号,空格,分号等特殊的符号,当想把它们作为一个参数来处理时,就需要用到尖括号,如下:
尖括号的使用:
MyMacro <one,two,three> ;传递一个参数给MyMacro宏
MyMacro one,two,three ;传递三个参数给MyMacro宏
在上面的中MyMacro 是一个已经定义过的宏,例子中的第一种方法传递了一个参数给MyMacro宏,而第二种方法传递了三个参数给MyMacro宏.
LOCAL
有些时候我们可能会希望在宏里面定义一些局部变量或是一些局部标志符,这时就可以使用关键字LOCAL 来定义这个宏里面的局部变量或局标志符.这个关键字和WIN32汇编程序里面定义局部变量的关键字是一样的.具体定义格式如下:
LOCAL localname [,localname]…
其中localname 是要定义的局部变量的变量名.
PUSHCONTEXT 和 POPCONTEXT
在宏里面,当我们需要保存汇编器当时的一些状态值到堆栈时,不需要用PUSH 和POP机器指令把值一个一个压入和弹出堆栈,我们可以用汇编器提供的PUSHCONTEXT和POPCONTEXT两个关键字来方便地处理这些工作.具体使用格式如下:
PUSHCONTEXT context
POPCONTEXT context
在上面的格式中,PUSHCONTEXT 关键字把context的内容都依次地压入到堆栈里面,而POPCONTEXT关键字则把context的内容从堆栈中恢复.这两个关键字可以嵌套10层.
ASSUMES 寄存器状态值
RADIX 当前默认的基数
LISTING 所有标识寄存器的内容
CPU 处理器和协处理器里面的指令
ALL 上面所有的内容
&parameter&
有时候我们需要在宏里面把一个变量插入到一个字符串里面,这个时候我们可以用&符号来完成这个目的.该符号的使用格式如下:
String & parameter & string
其中string 为任意的字符串,parameter为我们想要插入的变量名.如果想在字符串里面显示一个&符号,就需要在这个符号前面加上感叹号(!).
例:
.386
.model flat,stdcall
Guard MACRO Pisonernum:REQ,Prisonername := <Anonymouse>
LOCAL txt
&ECHO there are &Prisonernum& &Prisonername& have arrived at @time.
Txt textequ <Prisonernum&&Prisonernum>
EXITM txt
ENDM
%echo Guard(2)
.code
Strcat:
End strcat
其中@TIME表示引用系统时间.
VARARG (macro)
当我们希望定义一个参数个数不定的宏的时候,可以使用VARARG关键字,来定义一个具有VARARG属性的变量.当具体使用该宏的时候只需要把这些参数用逗号隔开就行了.具有该属性的参数只能放在宏的参数的最后面,例:
.386
.model flat,stdcall
ArgCount MACRO parmlist:VARRG
LOCAL count
Count = 0
FOR parm,<parmlist>
Count = count+1
ENDM
EXITM<count>
EMDM
.code
Var:
MOV EAX,ArgCount(1,2,3,4) ;EAX结果为4
MOV EBX,ArgCount(a,b) ;EBX结果为2
End var
上面定义了一个名为ArgCount的宏,该宏含有一个属性为VARARG的变量,在这个宏的过程当中使用了FOR循环语句,把传递进来的参数的个数进行统计.在代码段中使用该宏的语句传入了4个参数,所以该宏的返回值为4,即在EAX寄存器存入4;同理,第二条语句传给该宏两个参数,所以返回值为2.
用于字符串操作的预定义宏