マクロ (1)

単一行マクロ
NASM は、単一行マクロと複数行マクロをサポートしています。

単一行マクロは、基本的に C 言語と同じようなスタイルになっています (# の代わりに % を使う)。

  • 単一行マクロは、%define または %idefine で定義する。
    %define は大文字小文字を区別し、%idefine は大文字小文字を区別しない。
  • %idefine は、大文字小文字のすべてのパターンを、一度に定義する。
    例えば、「%idefine foo 100」の場合、foo, Foo, FOO, fOO の名前のマクロがすべて定義される。
  • 単一行マクロの中に、別のマクロを呼び出すトークンが含まれている場合、マクロの展開は、呼び出し時に実行される。
    それとは逆に、マクロを定義した時に、内部のマクロを展開させたい場合は、%xdefine%ixdefine を使用する。
  • マクロ内で、同じマクロが展開される場合は、一度だけ展開される。
  • %undef で、マクロを解除できる。

[マクロの引数]
  • 単一行マクロには引数を指定することができるが、マクロ名と '(' の間に空白を置くことはできない。
  • 同じ名前で、引数の数が異なるマクロは、別のマクロとして定義できる。
    ただし、引数が一つもないマクロと、引数が複数あるマクロは、同時に存在できない。
  • (以下は、ver 2.15 以降)
    引数名に、空の文字列を指定できる。
  • 引数が =arg として定義された場合、渡された引数は、式として評価される。
  • &arg として定義された場合、引数は、引用符で囲まれた文字列に変換される (何で囲むかは自動的に判断される)。
    引数がすでに引用符で囲まれている場合は、さらに引用符で囲む。
  • &&arg として定義された場合は、&arg と同じだが、すでに引用符で囲まれている場合、さらに囲まれることはない。
  • arg+ として定義された場合、以降は可変引数。
    引数名を参照すると、可変引数部分のコードがそのまま (カンマも含めて) 展開される。
  • arg! として定義された場合、引数に渡された空白と { } を削除しない。

%define VALUE 100
%undef  VALUE
%define PLUS(a,b)  ((a)+(b))

%define CALC(=a) a
CALC(1+2) ; 3 (1+2 を計算した結果に置き換え)

%define STR(&a)  a
STR(123)   ; '123'
STR('123') ; "'123'"

%define STR(&&a)  a
STR(123)   ; '123'
STR('123') ; '123'

%define TEST(a,b+) a:b
TEST(1, 2, 3, 4) ; 1:2, 3, 4
%[]
コード内で %[...] を使用すると、マクロ展開が行えないような箇所でマクロを展開でき、隣接するトークンに連結されます。
マクロを展開した上で、トークンを連結させたい場合に使います。

%define VALUE 123

abc_%[VALUE] ; abc_123

※abc_VALUE では、abc_VALUE が一つのトークンになるので、マクロは展開されない。
%+
%+ をマクロ定義内で使うと、左右のトークンを連結します。
%+ の後には、常に空白が必要です。

%define CAT(a,b) a %+ b

CAT(abc, 123) ; abc123
%? と %??
%?%?? は、マクロ定義内で、自身のマクロ名を参照するために使用されます。
単一行マクロと複数行マクロの両方でサポートされています。

大文字小文字を区別するマクロでは、%? と %?? は同じです。
大文字小文字を区別しないマクロの場合、%? は呼び出された時に指定されたマクロ名で、%?? は定義時に指定されたマクロ名になります。

※%?? は ver 2.15.04 以降。

%define  TEST1 %?
%idefine TEST2 %?:%??

TEST1 ; TEST1
TEST2 ; TEST2:TEST2
test2 ; test2:TEST2
Test2 ; Test2:TEST2
%defstr, %idefstr
%defstr と %idefstr は、引数なしの単一行マクロとして定義され、定義部分を、引用符で囲まれた文字列に変換します。

%defstr STR  abc

STR ; 'abc'
%deftoc, %ideftok
%deftok, %ideftok は、引数なしの単一行マクロとして定義され、定義部分の、引用符で囲まれた文字列を、トークンに変換します。

%deftok TCONV 'mov'

TCONV ; mov
%defalias, %idefalias
%defalias, %idefalias は、別のマクロに対するエイリアス (別名) を定義します。

エイリアスとして指定された、別のマクロが定義されていない場合、エイリアスは、存在しないマクロを指すことができます。

%undefalias を使用すると、エイリアスの定義を解除できます。
%clear defalias を使用すると、すべてのエイリアスを定義解除できます (NASM によって定義された、下位互換を保つためのエイリアスも含む)。
エイリアスを無効にするには、%aliases off を使用します。

%define DEFS 123
%defalias ALI DEFS

ALI, DEFS ; 123, 123

%undef DEFS

ALI, DEFS ; DEFS, DEFS
; ALI は DEFS を示すが、DEFS は定義されていないので、
; 展開されることなく DEFS となる。

%define ALI 456
; ALI は DEFS を示すので、%define DEFS 456 と定義される

ALI, DEFS ; 456, 456

%undefalias ALI

ALI, DEFS ; ALI, 456
; ALI は定義解除されたので、展開されない
; DEFS は定義が残っている
%, (カンマ)
※ver 2.15 以降
「%,」は、条件付きカンマ演算子です。可変引数で使用します。

通常はカンマとして置き換えられますが、展開後にその位置が終端になる場合 (%, の後に可変引数を参照した時、可変引数が空で、%, の後に何も展開されない場合)、%, は空として置き換えられます。

%define TEST1(a,b,c+) a,b,c
%define TEST2(a,b,c+) a,b%,c

TEST1(1,2) ; 1,2, (最後にカンマが付く)

TEST2(1,2) ; 1,2  (カンマが付かない)
TEST2(1,2,3) ; 1,2,3 (%, は , に置き換え)
複数行マクロ
複数行マクロの定義は、%macro または %imacro の行から始まり、%endmacro で終わります。

%macro PLUS 2
    mov eax, %1
    add eax, %2
%endmacro

PLUS 10,20
; mov eax, 10
; mov eax, 20
引数
マクロ名の後の数字は、そのマクロが受け取る引数の数を指定します (引数がない場合は 0)。
マクロ定義内では、%1, %2, %3... で、各引数の値を参照できます。

複数行マクロの場合は、単一行マクロとは異なり、引数を渡す時に () で囲む必要はありません。
(対象の行が複数行として展開されるため、マクロ以降のトークンを考慮する必要がない)


複数行マクロでも、引数の数が異なる同じマクロ名を複数定義できます。

カンマ (,) が含まれた引数を複数行マクロに渡したい場合は、引数を渡すときに {} で囲みます。

%macro test1 2
%1:%2
%endmacro

test1 a, {1,2,3} ; a:1,2,3
%unmacro
複数行マクロは、%unmacro で解除できます。

ただし、%undef とは異なり、%unmacro は引数の指定を受け取り、その仕様と完全に一致するもののみ削除します。
ラベル
複数行マクロ定義内で、そのマクロ内でのみ参照されるローカルラベルとして、ラベルを定義したい場合、ラベル名の先頭に %% を付けます。

%macro test1 0
%%jlabel:
    jmp %%jlabel
%endmacro

test1
test1

このようにすると、マクロが呼び出されるたびに、異なるラベル名が付けられます。

NASM の -E コマンドでプリプロセッサを実行すると、マクロ内の各ラベルが、「..@N..jlabel」という名前になっているのがわかります。

$ nasm -E test.asm

%line 2+1 test.asm
..@0.jlabel:
 jmp ..@0.jlabel
%line 2+1 test.asm
..@1.jlabel:
 jmp ..@1.jlabel

マクロが呼び出されるたび、「..@<数値>.<LABEL>」という形式で、ラベル名が付けられます。
これはローカルラベルとして扱われるため、他の箇所で、同じような形式のラベルを定義しないようにしてください。
可変引数
引数の数の数値の後に + を付けると、その位置以降の引数が、カンマも含めて、1つの引数としてまとめられます。

%macro test1 2+
%1:%2
%endmacro

test1 1,2     ; 1:2
test1 1,2,3,4 ; 1:2,3,4
指定範囲の引数を展開
%{x:y} を使って引数を参照すると、指定範囲の引数をまとめて展開できます。

x は最初のインデックスで、y は最後のインデックスです。
正の場合は 1〜、負の場合は、-1 で最後の引数となります。

x より y の方が大きい場合は、逆順で展開されます。

%macro test1 4
%{1:2}
%{2:-1}
%{4:1}
%{-1:-3}
%endmacro

test1 1,2,3,4
; 1,2
; 2,3,4
; 4,3,2,1
: 4,3,2
デフォルト引数
引数の数を、N1-N2 という形で指定することで、受け取る引数の数の範囲を指定することができます。
N1 は最小数、N2 は最大数です。最大数を * にすると、無限大にできます。

また、その範囲内の引数に対して、省略された場合のデフォルト値を指定することができます。

引数の数の指定の後、空白で区切って、各引数のデフォルト値を、カンマで区切って指定します。
(1-3 の場合、1つ目は固定の引数なので、デフォルト値は指定できません。2つ目、3つ目の引数を順に指定します)

デフォルト値が指定されなかった場合、引数は空白となります。

; 1から3個 (1つは固定)
%macro test1 1-3 10,20
%1,%2,%3
%endmacro

test1 1,2,3 ; 1,2,3
test1 1,2   ; 1,2,20
test1 1     ; 1,10,20

また、N1-N2+ というように、可変引数と一緒に使うこともできます。

無限大と可変引数の違い
N2 を * にした場合、引数の個数は無限大になりますが、この場合、参照できるようになる引数が無限大になるだけです。

; 無限大
%macro test1 0-* 10,20,30,40
%1,%2,(%3)
%endmacro

test1 1 ; 1,20,(30)
; 引数は %1-%3 までしか使っていないので、
; 4番目のデフォルト値は使われない

一方で、N1-N2+ により、可変引数の指定も行った場合は、デフォルト値を指定しつつ、N2 以降の引数を、まとめて参照することができます。

; 0〜3個。3番目以降はまとめて扱う
%macro test1 0-3+ 10,20,30,40
%1,%2,(%3)
%endmacro

test1 1 ; 1,20,(30,40)
%0
%0 は、渡された引数の数を、数値として参照できます。

+ で、可変引数として指定されている場合は、複数の引数がまとめて1つの引数として扱われるので、可変部分の引数の個数は1になります。
N1-N2 で範囲が指定されている場合は、それぞれが1つの引数になるため、引数の数と等しくなります。

%macro test1 1+
%0
%endmacro

%macro test2 0-4
%0
%endmacro

test1 1,2,3,4 ; 1
test2 1,2,3,4 ; 4
%00
%00 は、マクロ呼び出しの前にラベルが存在する場合、そのラベルの名前を返します。

ただし、ラベルは、マクロ呼び出しと同じ行にある必要があります。
ラベルは、ローカルラベルでもよく、コロンで終わっている必要はありません。

%macro test1 0
%00
%endmacro

label1: test1 ; label1
label2: test1 ; label2
%rotate
%rotate N は、引数を左右に回転させます。
単一の数値引数を指定し、その数だけ、左 (正の場合) または右 (負の場合) に回転させます。

引数が3個で、%rotate 1 を指定した場合、%1 = %2, %2 = %3, %3 = %1 となります。

%macro test1 1-3
%1,%2,%3
%rotate 1
%1,%2,%3
%endmacro

test1 1,2,3
; 1,2,3
; 2,3,1
引数テキストの連結
マクロ内の引数参照は、そのまま前後のテキストと連結できます。

ただし、引数参照の直後に数字が続く場合、例えば %12 の場合は、12番目の引数として扱わるので、%1 の後に 2 の文字を追加したい場合は、%{1}2 のように、引数参照の数字部分を {} で囲む必要があります。

また、%[] を使うことで連結させることもできます。

%macro test1 1
abc%1
abc%{1}23
abc%[%1]23
%endmacro

test1 .
; abc.
; abc.23
; abc.23