第15章
条件付きアセンブル
条件付きアセンブル(conditional assembly)を使うと、TATARAがソースを読みながら判断する条件によって、ソースの一部の行をアセンブルし、ほかの行を飛ばすことができます。これにより、1つのソースから複数のプログラムを作れます。たとえば、テスト用のチェックを入れた版と入れない版、MSX1用とMSX2用の版を、1行を変えるだけで切り替えられます。
この章では、条件付きアセンブルの疑似命令と、すべての条件の前提となる、TATARAがソースを2回読むしくみ(2つのパス)を説明します。
15.1 IF、ELSE、ENDIF
IFのあとには式を書きます。その値が0でなければ、あとに続く行がアセンブルされ、0ならば飛ばされます。ENDIFはIFが制御する行の終わりを示します。省略できるELSEは、条件が偽のときに代わりにアセンブルする行の始まりを示します。IFEは逆の判定で、値が0のときに行がアセンブルされます。
debug equ 1 cseg if debug db 'D' else db 'R' endif ife debug db 'E' endif
ここではdb ’D’がアセンブルされ、db ’R’とdb ’E’は飛ばされます。最初の行をdebug equ 0にすると、これが入れ替わります。リスティングにはすべての行が表示されますが、飛ばされた行にはアドレスもバイトもありません。
0001 debug equ 1 cseg if debug 0000' 44 db 'D' else db 'R' endif ife debug db 'E' endif
飛ばされた行をリスティングから完全に除く方法は、18章で説明します。
0以外の値はすべて真とみなされます。8章の比較演算子は、真のときに0FFFFh、偽のときに0を返すので、if size gt 256のようにそのまま使えます。
これらの疑似命令のいくつかには別の名前があり、M80向けに書かれたソースをそのままアセンブルできるように、TATARAはそれらも受け付けます(表15.1)。
M80はENDIFの別名としてENDCも受け付けますが、TATARAは受け付けません。M80向けに書かれたソースでは、ENDIFに書き換えてください。
15.2 2つのパス
ラベルは、それを定義する行より前でも使えます。次の最初の行がその例です。
jp later later: ret
TATARAがjp laterに来たとき、まだlaterを見ていないので、そのアドレスはわかりません。TATARAはこれを、ソースを2回読むことで解決します。1回ごとの読み込みをパス(pass)と呼びます。
- 第1パス(first pass)では、TATARAはそれぞれの行が何バイトになるかを調べ、それによってすべてのラベルのアドレスとすべての名前の値を求めます。何も書き出しません。
- 第2パス(second pass)では、すべての値がわかっているので、TATARAはプログラムのバイト、リスティング、オブジェクト・ファイルを作ります。
図15.1の上半分は、この2行に対する2つのパスを示しています。M80を含め、ほとんどのアセンブラーはこのように動きます。
第1パスで見つけた名前と値は、第2パスでもそのまま使われます。そのため第2パスでは、第1パスで定義されたすべての名前が、ソースのもっとあとで定義されるものも含めて、すでに定義されています。
これは条件付きアセンブルに関わってきます。TATARAは、それぞれの条件を両方のパスで評価するからです。条件が第2パスで違う答えになると、第2パスでは第1パスと違う行がアセンブルされ、そのあとのラベルがずれます。図15.1の下半分がその例です。IFDEFはlaterが定義されているかどうかを調べます(15.3節)。第1パスではまだ定義されていないので、db 1は飛ばされ、afterは0000hになります。第2パスでは定義されているので、db 1がアセンブルされ、afterは0001hになってしまいます。TATARAはafterの行で止まります。
DEFLATE.AS(6): ERROR: phase error - this label had a different value on pass 1.
第1パスで定義したラベルを条件のせいで第2パスが飛ばした場合は、13.6節のメッセージになります。
したがって、条件は両方のパスで同じ答えにならなければなりません。確実にそうするいちばん簡単な方法は、条件より上で定義された名前だけを使うことです。IFでは、TATARAがそれを要求します。あとで定義される名前を使うと、第1パスで止まります。
; FWD.AS - IF of a name defined later. cseg if later db 1 endif later equ 1
FWD.AS(3): ERROR: undefined symbol in an expression.
15.3 IFDEFとIFNDEF
IFDEFのあとには名前を書き、その名前が定義されていれば行がアセンブルされます。IFNDEFは、定義されていなければアセンブルします。EXTRNで宣言した名前は、定義されているものとみなされます。
よくある使い方は、ソースが自分で値を決められ、決めなければ既定値になるようにするものです。
ifndef bufsize bufsize equ 128 endif
別の大きさにしたいソースは、これらの行より前でbufsizeを定義します。必ず前でなければなりません。15.2節で見たように、もっとあとで定義される名前は、第1パスでは未定義、第2パスでは定義済みになり、2つのパスで答えが変わってしまうからです。
同じ理由で、IFNDEFを使ってインクルード・ファイルが2回読まれるのを防ぐことはできません(13.6節)。
15.4 IF1とIF2
IF1は第1パスでだけ、IF2は第2パスでだけ、その行をアセンブルします。
これらはM80から来たもので、M80のソースをアセンブルできるように残してあります。リスティングがそのままプリンターに出力されていた時代に、2回ではなく1回だけ行いたいことのために使われていました。TATARAではほとんど役に立ちません。リスティングは第2パスを表示するので、IF2の行は表示されますが、IF1の行は表示されません。また、IF1やIF2の中でラベルを定義したりバイトを作ったりすると、2つのパスが違うものになり、15.2節の決まりに反します。
; PASSLAB.AS - a label inside IF1. cseg if1 here: nop endif
PASSLAB.AS(4): ERROR: this label was defined on pass 1 and not on pass 2 - a conditional skipped it.
15.5 IFB、IFNB、IFIDN、IFDIF
この4つは、値ではなくテキストを調べます。
- IFBはテキストが空のときに、IFNBは空でないときに、行をアセンブルします。
- IFIDNは2つのテキストが同じときに、IFDIFは違うときに、行をアセンブルします。2つのテキストはコンマで区切ります。
それぞれのテキストは山かっこ(<>)で囲み、かっこはテキストに含まれません。空白もコンマも含まないテキストでは、かっこを省略できます。
ifb <> ; true: blank ifb <x> ; false ifnb <x> ; true ifdif <abc>,<abd> ; true ifidn <abc>,<ABC> ; false: the case differs ifidn abc,abc ; true
比較は、M80と同じように大文字と小文字も含めて完全に一致するかどうかで、/Cの有無は関係ありません。
このようにテキストをそのまま書いたのでは、答えはいつも同じなので、これらの疑似命令はそれだけでは役に立ちません。マクロの中で、使うたびに変わる引数をテキストとして使います(16章)。
15.6 入れ子
条件付きアセンブルは、別の条件付きアセンブルの中に入れることができます。飛ばされている行の中のIFは評価さえされず、条件がどうであれ、その行はどれもアセンブルされません。
if 0 if 1 db 1 ; skipped: the outer IF is false else db 2 ; skipped too endif else if 1 db 3 ; assembled else db 4 endif endif
それぞれのELSEとENDIFは、まだ閉じていないいちばん内側のIFに対応します。TATARAは、同時に16個までの条件付きアセンブルを開いておけます。
IF、ELSE、ENDIFの行にも、ほかの行と同じようにラベルを書けます。M80と同じように、TATARAがその行に来たときにアセンブル中であれば、ラベルはロケーション・カウンターのアドレスで定義され、飛ばしている最中であれば定義されません。そのため、飛ばされた行のあとのELSEに書いたラベルは定義されず、飛ばされている行の中のIFに書いたラベルも定義されません。ラベルは、独立した行に書くのがいちばん安全です。
15.7 メッセージ
条件付きアセンブルについてのメッセージを、それぞれの原因となる行とともに表15.2に示します。
行 |
メッセージ |
| ELSE or ENDIF without a matching IF. |
|
| conditional not closed by ENDIF. |
|
| conditionals nested too deeply. |
|
式のないif |
bad expression. |
if
later |
undefined symbol in an expression. |
endc |
not a directive, a macro or an instruction. |
閉じられていない条件付きアセンブルにTATARAが気づくのはソースの終わりでですが、見つけやすいように、メッセージはIFの行を示します。複数が開いたままのときは、いちばん外側のものを示します。