学 Win32 汇编[34] - 宏汇编(1)
从接触 C 语言时, 我就不大喜欢宏; 但为了看懂别人的代码也不得不去了解.
宏可定义在源程序的任意位置, 但一般放在 .data 前面.
有些简单的宏可以用 equ、textequ 或 = 来代替, 但宏有更复杂的功能.
"宏" 的本质是 "替换", 但又像极了 "子过程";
所以即有宏过程(macro procedure)、也有宏函数(macro function).
它既以有参数(可以是: 常数、变量、寄存器、指令、表达式), 有时也需要像子过程一样声明.
宏可以指定哪些参数是必须的, 还可以给参数默认值.
宏可以包含数据(.data)和代码(.code)、还可以嵌套.
宏中的注释使用 ;; 如果只用 ; 将会被一起替换到代码中.
宏的功能很强大, 现在常用的 PrintDec、PrintHex、PrintString、PrintText 等等都是宏.
再强大它也只是 "文本替换".
"宏" 和 "子程序" 相比:
1、宏展开后顺序执行, 不像调用子程序跳来跳去, 这样程序会更 "快";
2、宏展开后会让代码量增大, 导致程序变 "大".
一个简单的宏:
; Test34_1.asm.386.model flat, stdcallinclude windows.incinclude kernel32.incinclude masm32.incinclude debug.incincludelib kernel32.libincludelib masm32.libincludelib debug.lib;定义一个 mExit 宏mExit macro? PrintLine? retendm.codesum proc v1, v2, v3? mov eax, v1? add eax, v2? add eax, v3? retsum endp;main proc? invoke sum, 11, 22, 33? PrintDec eax ;66 ;PrintLine ;ret? mExit ;mExit 将被替换为上面两行代码main endpend main
一个代替求和函数的宏:
; Test34_2.asm.386.model flat, stdcallinclude windows.incinclude kernel32.incinclude masm32.incinclude debug.incincludelib kernel32.libincludelib masm32.libincludelib debug.libmSum macro v1, v2, v3? mov eax, v1? add eax, v2? add eax, v3endm.codemain proc? mSum 11, 22, 33? PrintDec eax ;66? PrintLine? mSum 11, 22, 33, 44, 55 ;多余的参数会被忽略? PrintDec eax ;66? PrintLine? retmain endpend main
宏参数的默认值:
; Test34_3.asm.386.model flat, stdcallinclude windows.incinclude kernel32.incinclude masm32.incinclude debug.incincludelib kernel32.libincludelib masm32.libincludelib debug.lib;参数 v1、v2 通过 REQ 标识说明是必备参数;参数 v3、v4 给出了默认值mSum macro v1:req, v2:req, v3:=<33>, v4:=<44>? mov eax, v1? add eax, v2? add eax, v3? add eax, v4endm.codemain proc? mSum 11, 22? PrintDec eax ;110? PrintLine? retmain endpend main
EXITM: 退出宏
; Test34_4.asm.386.model flat, stdcallinclude windows.incinclude kernel32.incinclude masm32.incinclude debug.incincludelib kernel32.libincludelib masm32.libincludelib debug.libmPrint macro? PrintText '第一行'? PrintText '第二行'? exitm? PrintText '第三行'endm.codemain proc? mPrint ;只会输出前两行? retmain endpend main
PURGE: 取消宏
; Test34_5.asm.386.model flat, stdcallinclude windows.incinclude kernel32.incinclude masm32.incinclude debug.incincludelib kernel32.libincludelib masm32.libincludelib debug.libmPrint macro? PrintText '第一行'? PrintText '第二行'? PrintText '第三行'? PrintLineendm.codemain proc? mPrint? mPrint? purge mPrint ;可用逗号隔开取消多个宏? mPrint? ;这个宏不会展开了? retmain endpend main
宏中 local 的使用:
; Test34_6.asm.386.model flat, stdcallinclude windows.incinclude kernel32.incinclude masm32.incinclude debug.incincludelib kernel32.libincludelib masm32.libincludelib debug.lib;从三个数中求最大数的宏mMax macro v1, v2, v3? LOCAL L1, L2 ;;如没有这句, 宏在多次展开时会让 L1、L2 重名, 这样宏会把标号名称协调好? mov eax, v1? cmp eax, v2? jge L1? mov eax, v2L1: cmp eax, v3? jge L2? mov eax, v3L2:endm.codemain proc? mMax 11, 22, 33? PrintDec eax ;33? retmain endpend main
灵活的参数, 用到 & 操作符:
; Test34_7.asm.386.model flat, stdcallinclude windows.incinclude kernel32.incinclude masm32.incinclude debug.incincludelib kernel32.libincludelib masm32.libincludelib debug.lib;求最数中的最大值:mMax macro v1, v2? LOCAL L1? mov eax, v1? cmp eax, v2? jge L1? mov eax, v2L1:endm;求最数中的最小值:mMin macro v1, v2? LOCAL L1? mov eax, v1? cmp eax, v2? jle L1? mov eax, v2L1:endm;能把 JGE 或 JLE 做参数:mCom1 macro XX, v1, v2? LOCAL L1? mov eax, v1? cmp eax, v2? XX L1? mov eax, v2L1:endm;能通过参数让 J*E 变为 JGE 或 JLE:mCom2 macro X, v1, v2? LOCAL L1? mov eax, v1? cmp eax, v2? J&X&E L1 ;;这里用到特殊操作符 &? mov eax, v2L1:endm.codemain proc? mMax 11, 22? PrintDec eax ;22? ? mMin 11, 22? PrintDec eax ;11? ? mCom1 JGE, 11, 22? PrintDec eax ;22? ? mCom1 JLE, 11, 22? PrintDec eax ;11? ? mCom2 G, 11, 22? PrintDec eax ;22? ? mCom2 L, 11, 22? PrintDec eax ;11? retmain endpend main
特殊操作符: &、<>、%、!
& ;替换操作符<> ;字符串传递操作符% ;表达式操作符, 也用于得到一个变量或常量的值! ;转义操作符; Test34_8.asm.386.model flat, stdcallinclude windows.incinclude kernel32.incinclude masm32.incinclude debug.incincludelib kernel32.libincludelib masm32.libincludelib debug.lib;自定义的宏mPrint macro Text? PrintText '* &Text& *'endm.codemain proc ;该宏会把参数直接替换过去? mPrint 1234 ;* 1234 *? ;要保证参数的完整应该使用 <>? mPrint 12,34 ;* 12 *? mPrint <12,34> ;* 12,34 *? ;需要计算结果应该使用 %()? mPrint 34+12 ;* 34+12 *? mPrint %(34+12) ;* 46 *? ;用到 &、<、>、%、! 应该使用 ! 转义? mPrint 10 !% 2 = %(10/2)!! ;* 10 % 2 = 5! *? retmain endpend main