NASM 宏参数个数说明

4.3.3 不确定的宏参数个数. 
通常,定义一个宏,它可以在接受了前面的几个参数后, 把后面的所有参数都
作为一个参数来使用,这可能是非常有用的,一个相关的例子是,一个宏可能
用来写一个字符串到一个MS-DOS的文本文件中,这里,你可能希望这样写代码:
              writefile [filehandle],"hello, world",13,10
            
NASM允许你把宏的最后一个参数定义成"贪婪参数", 也就是说你调用这个宏时
,使用了比宏预期得要多得多的参数个数,那所有多出来的参数连同它们之间
的逗号会被作为一个参数传递给宏中定义的最后一个实参,所以,如果你写:
      %macro  writefile 2+
    
              jmp     %%endstr
        %%str:        db      %2
        %%endstr:
              mov     dx,%%str
              mov     cx,%%endstr-%%str
              mov     bx,%1
              mov     ah,0x40
              int     0x21
    
       %endmacro
那上面使用''writefile''的例子会如预期的那样工作:第一个逗号以前的文本
[filehandle]会被作为第一个宏参数使用,会被在''%1''的所有位置上扩展,而
所有剩余的文本都被合并到''%2''中,放在db后面.
这种宏的贪婪特性在NASM中是通过在宏的''%macro''一行上的参数个数后面加
上''+''来实现的.
如果你定义了一个贪婪宏,你就等于告诉NASM对于那些给出超过实际需要的参
数个数的宏调用该如何扩展; 在这种情况下,比如说,NASM现在知道了当它看到
宏调用''writefile''带有2,3或4个或更多的参数的时候,该如何做.当重载宏
时,NASM会计算参数的个数,不允许你定义另一个带有4个参数的''writefile''
宏.
当然,上面的宏也可以作为一个非贪婪宏执行,在这种情况下,调用语句应该
象下面这样写:
                writefile [filehandle], {"hello, world",13,10}
NASM提供两种机制实现把逗号放到宏参数中,你可以选择任意一种你喜欢的
形式.
有一个更好的办法来书写上面的宏,请参阅5.2.1
  4.3.4 缺省宏参数.

  NASM可以让你定义一个多行宏带有一个允许的参数个数范围.如果你这样做了,
  你可以为参数指定缺省值.
比如:
      %macro  die 0-1 "Painful program death has occurred."
    
              writefile 2,%1
              mov     ax,0x4c01
              int     0x21
    
      %endmacro
这个宏(它使用了4.3.3中定义的宏''writefile'')在被调用的时候可以有一个
错误信息,它会在退出前被显示在错误输出流上,如果它在被调用时不带参数
,它会使用在宏定义中的缺省错误信息.
通常,你以这种形式指定宏参数个数的最大值与最小值; 最小个数的参数在
宏调用的时候是必须的,然后你要为其他的可选参数指定缺省值.所以,当一
个宏定义以下面的行开始时:
      %macro foobar 1-3 eax,[ebx+2]
它在被调用时可以使用一到三个参数, 而''%1''在宏调用的时候必须指定,''%2''
在没有被宏调用指定的时候,会被缺省地赋为''eax'',''%3''会被缺省地赋为
''[ebx+2]''.

你可能在宏定义时漏掉了缺省值的赋值, 在这种情况下,参数的缺省值被赋为
空.这在可带有可变参数个数的宏中非常有用,因为记号''%0''可以让你确定有
多少参数被真正传给了宏.
这种缺省参数机制可以和''贪婪参数''机制结合起来使用;这样上面的''die''宏
可以被做得更强大,更有用,只要把第一行定义改为如下形式即可:
      %macro die 0-1+ "Painful program death has occurred.",13,10

最大参数个数可以是无限,以''*''表示.在这种情况下,当然就不可能提供所有
的缺省参数值. 关于这种用法的例子参见4.3.6.
  4.3.5 `%0'': 宏参数个数计数器.
对于一个可带有可变个数参数的宏, 参数引用''%0''会返回一个数值常量表示
有多少个参数传给了宏.这可以作为''%rep''的一个参数(参阅4.5),以用来遍历
宏的所有参数. 例子在4.3.6中给出.
  4.3.6 `%rotate'': 循环移动宏参数.
Unix的shell程序员对于''shift'' shell命令再熟悉不过了,它允许把传递给shell
脚本的参数序列(以''$1,''$2''等引用)左移一个,所以, 前一个参数是‘$1''的话
左移之后,就变成’$2''可用了,而在''$1''之前是没有可用的参数的。
NASM具有相似的机制,使用''%rotate''。就象这个指令的名字所表达的,它跟Unix
的''shift''是不同的,它不会让任何一个参数丢失,当一个参数被移到最左边的
时候,再移动它,它就会跳到右边。
''%rotate''以单个数值作为参数进行调用(也可以是一个表达式)。宏参数被循环
左移,左移的次数正好是这个数字所指定的。如果''%rotate''的参数是负数,那么
宏参数就会被循环右移。
所以,一对用来保存和恢复寄存器值的宏可以这样写:
      %macro  multipush 1-*
    
        %rep  %0
              push    %1
        %rotate 1
        %endrep
    
      %endmacro
这个宏从左到右为它的每一个参数都依次调用指令''PUSH''。它开始先把它的
第一个参数''%1''压栈,然后调用''%rotate''把所有参数循环左移一个位置,这样
一来,原来的第二个参数现在就可以用''%1''来取用了。重复执行这个过程,
直到所有的参数都被执行完(这是通过把''%0''作为''%rep''的参数来实现的)。
这就实现了把每一个参数都依次压栈。
注意,''*''也可以作为最大参数个数的一个计数,表明你在使用宏''multipush''的
时候,参数个数没有上限。
使用这个宏,确实是非常方便的,执行同等的''POP''操作,我们并不需要把参数
顺序倒一下。一个完美的解决方案是,你再写一个''multipop''宏调用,然后把
上面的调用中的参数复制粘贴过来就行了,这个宏会对所有的寄存器执行相反
顺序的pop操作。
这可以通过下面定义来实现:
      %macro  multipop 1-*
    
        %rep %0
        %rotate -1
              pop     %1
        %endrep
    
      %endmacro
这个宏开始先把它的参数循环右移一个位置,这样一来,原来的最后一个参数
现在可以用''%1''引用了。然后被pop,然后,参数序列再一次右移,倒数第二个
参数变成了''%1'',就这样,所以参数被以相反的顺序一一被执行。

你可能感兴趣的:(NASM 宏参数个数说明)