裏CHUNSOFT > ゲーム解析入門 > 2.条件分岐

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日更新

裏CHUNSOFT > ゲーム解析入門 > 2.条件分岐