単一行マクロ
NASM は、単一行マクロと複数行マクロをサポートしています。
単一行マクロは、基本的に C 言語と同じようなスタイルになっています (# の代わりに % を使う)。
[マクロの引数]
単一行マクロは、基本的に 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 以降。
単一行マクロと複数行マクロの両方でサポートされています。
大文字小文字を区別するマクロでは、%? と %?? は同じです。
大文字小文字を区別しないマクロの場合、%? は呼び出された時に指定されたマクロ名で、%?? は定義時に指定されたマクロ名になります。
※%?? は 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 を使用します。
エイリアスとして指定された、別のマクロが定義されていない場合、エイリアスは、存在しないマクロを指すことができます。
%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... で、各引数の値を参照できます。
複数行マクロの場合は、単一行マクロとは異なり、引数を渡す時に () で囲む必要はありません。
(対象の行が複数行として展開されるため、マクロ以降のトークンを考慮する必要がない)
複数行マクロでも、引数の数が異なる同じマクロ名を複数定義できます。
カンマ (,) が含まれた引数を複数行マクロに渡したい場合は、引数を渡すときに {} で囲みます。
マクロ定義内では、%1, %2, %3... で、各引数の値を参照できます。
複数行マクロの場合は、単一行マクロとは異なり、引数を渡す時に () で囲む必要はありません。
(対象の行が複数行として展開されるため、マクロ以降のトークンを考慮する必要がない)
複数行マクロでも、引数の数が異なる同じマクロ名を複数定義できます。
カンマ (,) が含まれた引数を複数行マクロに渡したい場合は、引数を渡すときに {} で囲みます。
%macro test1 2 %1:%2 %endmacro test1 a, {1,2,3} ; a:1,2,3
%unmacro
複数行マクロは、%unmacro で解除できます。
ただし、%undef とは異なり、%unmacro は引数の指定を受け取り、その仕様と完全に一致するもののみ削除します。
ただし、%undef とは異なり、%unmacro は引数の指定を受け取り、その仕様と完全に一致するもののみ削除します。
ラベル
複数行マクロ定義内で、そのマクロ内でのみ参照されるローカルラベルとして、ラベルを定義したい場合、ラベル名の先頭に %% を付けます。
このようにすると、マクロが呼び出されるたびに、異なるラベル名が付けられます。
NASM の -E コマンドでプリプロセッサを実行すると、マクロ内の各ラベルが、「..@N..jlabel」という名前になっているのがわかります。
マクロが呼び出されるたび、「..@<数値>.<LABEL>」という形式で、ラベル名が付けられます。
これはローカルラベルとして扱われるため、他の箇所で、同じような形式のラベルを定義しないようにしてください。
%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 の方が大きい場合は、逆順で展開されます。
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つ目の引数を順に指定します)
デフォルト値が指定されなかった場合、引数は空白となります。
また、N1-N2+ というように、可変引数と一緒に使うこともできます。
一方で、N1-N2+ により、可変引数の指定も行った場合は、デフォルト値を指定しつつ、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つの引数になるため、引数の数と等しくなります。
+ で、可変引数として指定されている場合は、複数の引数がまとめて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 となります。
単一の数値引数を指定し、その数だけ、左 (正の場合) または右 (負の場合) に回転させます。
引数が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 のように、引数参照の数字部分を {} で囲む必要があります。
また、%[] を使うことで連結させることもできます。
ただし、引数参照の直後に数字が続く場合、例えば %12 の場合は、12番目の引数として扱わるので、%1 の後に 2 の文字を追加したい場合は、%{1}2 のように、引数参照の数字部分を {} で囲む必要があります。
また、%[] を使うことで連結させることもできます。
%macro test1 1 abc%1 abc%{1}23 abc%[%1]23 %endmacro test1 . ; abc. ; abc.23 ; abc.23