2.条件分岐
CMP命令は、"A"と引数の差を計算して、その結果に応じてC,Z,Nを更新する命令です。 "A"の値は変わりません。
フラグ | 0になるとき | 1になるとき |
---|---|---|
C | "A" - (引数) < 0 | "A" - (引数) >= 0 |
Z | "A" - (引数) != 0 | "A" - (引数) = 0 |
N | "A" - (引数) の最上位ビットが0 | "A" - (引数) の最上位ビットが1 |
"A"ではなく"X","Y"と引数の差を計算するCPX,CPYという命令もありますが同様です。 演算命令でレジスタを更新した時にもこれらのフラグは更新されますが、レジスタの値はそのままで単に比較だけしたい時にはCMP,CPX,CPY命令が便利です。
これだけでは条件分岐できないので、C,Z,Nの値によって条件分岐する以下の命令とセットで使います。
全て符号付き1バイトの引数をとり、ジャンプする場合は次の命令の1バイト目のアドレスに引数を加えたアドレスにジャンプします。
命令の名前を見て何か気付いたことはないでしょうか。 例えば、Z=1になるのは"A"-(引数)=0、すなわち"A"=(引数)のときです。 だから"BEQ(Branch EQual)"なのです。 他も同様です。
それでは、実際の例を見ていきましょう。 水溜まりが出現するかどうかを決定するルーチンです。
038011 08 PHP ; 038012 E2 30 SEP #$30 ; 038014 22 E6 27 C6 JSL $0627E6 ; ダンジョンBの値を取得するだけのルーチン、{7E0000}={7ED5F3} 038018 A5 00 LDA $00 ; "A"={7E0000} 03801A C9 01 CMP #$01 ; "A"-01を計算して、その結果に応じてC,Z,Nを更新 03801C D0 16 BNE #$16 ; Z=0のとき$038034へ
PHP,SEPはもう良いでしょう。 5行目でCMPが、6行目でBNEによる条件分岐が出て来ます。 BNEとはZ=0のときに条件分岐する命令で、Z=0はCMP命令で"A"-(引数)=0、つまり"A"と引数が等しくない時になるものでした。 "A"はダンジョンBで、引数は01なので、分岐するのはダンジョンBが01でない、つまりこばみ谷ではないときです。
条件分岐命令は次の命令の1バイト目を基点とする相対ジャンプで、引数は符号付き1バイトなので、ジャンプ先は03801E+16=038034となります。 もし引数が80〜FFなら、前に戻るということになります。 dis65816ではジャンプ先のアドレスも出力してくれるので普段は自分で計算する必要はありませんが、逆アセンブルのミスを自分で手直しする時の為に符号付き1バイトの計算をできるようにしておいて下さい。
ということで、03801E〜038033はこばみ谷限定で実行される処理です。
03801E 22 71 27 C6 JSL $062771 ; 現在の階数を取得するだけのルーチン、{7E0000}={7ED5F2} 038022 A5 00 LDA $00 ; "A"={7E0000} 038024 C9 08 CMP #$08 ; "A"-08を計算して、その結果に応じてC,Z,Nを更新 038026 90 14 BCC #$14 ; C=0のとき$03803Cへ 038028 C9 1E CMP #$1E ; "A"-1Eを計算して、その結果に応じてC,Z,Nを更新 03802A B0 10 BCS #$10 ; C=1のとき$03803Cへ 03802C C9 0F CMP #$0F ; "A"-0Fを計算して、その結果に応じてC,Z,Nを更新 03802E 90 04 BCC #$04 ; C=0のとき$038034へ 038030 C9 16 CMP #$16 ; "A"-16を計算して、その結果に応じてC,Z,Nを更新 038032 90 08 BCC #$08 ; C=0のとき$03803Cへ
BCCはC=0のときに、BCSはC=1のときに分岐するものでした。 そして、CMP命令では、"A"が引数未満のときにC=0、引数以上のときにC=1になりました。 従って、上から順に見ていくと、次のようになります。
全く相対ジャンプしないと038034に来るので、以上を1つの文にまとめると、「"A"が08未満または1E以上または0F以上16未満のときに$03803Cへ」ということになります。 $03803Cにジャンプすると何もせずに元の命令に戻ることになり、水溜まりは出現しません。 "A"は現在の階数だったので、これをシレンの言葉に直すと、「1〜7Fまたは30F以上または15〜22Fのときに水溜まりは出現しない」、つまりこばみ谷で水溜まりが出現するのは8〜14Fと22〜29Fです。
038034 AF 8E BE 7E LDA $7EBE8E ; "A"={7EBE8E}(部屋の数) 038038 AA TAX ; "X"="A" 038039 CA DEX ; "X"-- 03803A 10 02 BPL #$02 ; N=0のとき$03803Eへジャンプ 03803C 28 PLP ; ここに来ると何もせずに元の命令へ、つまり水溜まりは出現しない。 03803D 60 RTS ; 元の命令へ(JSRに対して) 03803E以降では、部屋のサイズを調べて、縦・横の何れかが5以上の奇数でなければ$038039に戻って来る。
今度は実際にレジスタを更新してフラグを更新しています。 Nは結果の最上位ビットと等しくなるので、3行目で"X"から1減じた結果が80〜FFになった時に1になります。
部屋の数は1以上ありますから、1回目は必ず$03803Eへジャンプします。 $03803E以降では、部屋番号"X"の部屋のサイズを調べて、縦も横も5以上の奇数ならその部屋に水溜まりを設置し、そうでなければ$038039に戻って来ます。 部屋番号00の部屋まで調べて水溜まりを設置できなければ、"X"--で"X"=FFとなりN=1となって水溜まりは出現せずに元のルーチンに戻ることになります。 つまりこれは高級言語でも当たり前のループです。 何回ループするかがややこしいこともありますが落ち着いて考えましょう。 ここでは部屋の数だけループしています。
このルーチンはRTSで終わっているので、030000番台からJSR命令しか呼び出すことができません。 しかし、水溜まりを出現させる処理はダンジョン形成の過程でしか呼び出されませんし、ダンジョン形成の処理は030000番台に集中しているのでこれで良いのです。
2010年5月9日更新