基礎知識
戦闘関係
本作は、各シナリオの主人公の名前(功夫編は拳法名)などを決定する際、文字一覧表から文字を入力して決定する。
SF編では文字入力を利用した仕掛けや謎解きも存在する。
この「文字一覧から文字を選択・決定」に関係する処理と、SF編で「入力した文字列が、規定の文字列と一致した時に正解とする」処理がどのように行われているのかを紹介する。
まず、文字入力に関係するメモリアドレスを紹介する。
どのシナリオでも、文字入力に関係するメモリアドレスは共通である。
| アドレス | 内容 |
|---|---|
$7E:2F3C | 文字入力画面 ページ数($00~、シナリオなどにより最大ページ数は変動) |
$7E:2F40 | 文字入力画面 X軸のカーソル位置 左上座標を(0,0)とする。横方向は11文字。$00~$0A |
$7E:2F42 | 文字入力画面 Y軸のカーソル位置 左上座標を(0,0)とする。縦方向は9文字。$00~$08 |
$7E:2F44 | 文字入力画面 入力した文字数(1文字で+$02) $00~$0C |
$7E:2F46~$7E:2F47 | 文字入力画面 入力欄1文字目(2バイト) |
$7E:2F48~$7E:2F49 | 文字入力画面 入力欄2文字目(2バイト) |
$7E:2F4A~$7E:2F4B | 文字入力画面 入力欄3文字目(2バイト) |
$7E:2F4C~$7E:2F4D | 文字入力画面 入力欄4文字目(2バイト) |
$7E:2F4E~$7E:2F4F | 文字入力画面 入力欄5文字目(2バイト) |
$7E:2F50~$7E:2F51 | 文字入力画面 入力欄6文字目(2バイト) |
$7E:8A00~$7E:8AC6 | 文字入力画面 文字コード(1文字2バイト、左上から右下へ、横11文字分、縦9文字分) |
順に説明する。
文字入力画面は、シナリオなどにより入力できる文字種が変わり、LRボタンで文字一覧ページを切り替えることができる。
現在表示しているページが入るメモリが$7E:2F3Cであり、最初のページを$00として、Rボタンを押すとページ数を+1、Lボタンを押すと-1する。ただしページ数が最大のページから更にRボタンを押すと0ページ目に戻るし、0ページ目でLボタンを押すと最大ページへと移動する。
文字入力画面に表示できる文字は最大90文字。
中央に区切りとして、空白(スペース)が1文字分ある。
このスペースを含めると、縦9文字×横11文字分の領域がある。
以下に例として、カタカナのページの文字一覧を表記する。
「□」はスペース部分にあたる。中央の区切りとして表示されている「□」にカーソルを合わせてAボタンを押した時も、スペースを入力したとして扱われる。
カタカナのページでいえば左上の「ア」の文字を座標の原点(0, 0)として、$7E:2F40にカーソルのX軸方向の値、$7E:2F42にカーソルのY軸方向の値が入る。
右上の「ン」にカーソルが合っているなら、$7E:2F40に$0A、$7E:2F42に$00が入る。
左下の「ラ」にカーソルが合っているなら、$7E:2F40に$00、$7E:2F42に$08が入る。
$7E:8A00~$7E:8AC6に表示しているページの文字の文字コードが入る。
2バイトで1文字分の文字データになるのだが、セリフなどの通常の文字列データである、
「ライブ・ア・ライブ」文字エンコード・デコード表
とは少し違う値が入る。
たとえば、カタカナの「ア」は、上表では「ア」はひらがな/カタカナ/記号一覧の「$61」、1バイト分で表示できているのだが、文字入力画面においては、$7E:8A00~$7E:8A01の2バイトにデータが入る。
上一覧の「アイウエオ□」の部分の値だけ抜粋すると、
| アドレス | 値 | 文字 |
|---|---|---|
$7E:8A00 | $61 | ア |
$7E:8A01 | $00 | |
$7E:8A02 | $62 | イ |
$7E:8A03 | $00 | |
$7E:8A04 | $63 | ウ |
$7E:8A05 | $00 | |
$7E:8A06 | $34 | エ |
$7E:8A07 | $00 | |
$7E:8A08 | $35 | オ |
$7E:8A09 | $00 | |
$7E:8A0A | $20 | □ |
$7E:8A0B | $00 |
となる。
ひらがな/カタカナ/記号一覧の文字の場合、最初の1バイトに該当文字1バイト、次の1バイトに$00となる。
漢字の場合、通常の文字列なら、エンコード・デコード表の法則に従い、「$1Dまたは$1Eまたは$1F」の後に$00~$FFの2バイトで表記しているが、文字一覧表の場合は、まず、後ろの「$00~$FF」を最初の1バイトに入れ、後ろの1バイトに、「$1D列の漢字は$01、$1E列の漢字は$02、$1F列の漢字は$03」を入れる。
たとえば、漢字1ページ目は、下のように$1D列の漢字が並んでいる。
最初から6文字目までなら以下のように値が入っている。
| アドレス | 値 | 文字 |
|---|---|---|
$7E:8A00 | $00 | 愛 |
$7E:8A01 | $01 | |
$7E:8A02 | $01 | 悪 |
$7E:8A03 | $01 | |
$7E:8A04 | $02 | 安 |
$7E:8A05 | $01 | |
$7E:8A06 | $03 | 暗 |
$7E:8A07 | $01 | |
$7E:8A08 | $04 | 案 |
$7E:8A09 | $01 | |
$7E:8A0A | $20 | □ |
$7E:8A0B | $00 |
漢字は$1D列に256文字、$1E列に256文字、$1F列に256文字の合計768文字が収録されており、1ページに表示できる文字数は90文字だから、漢字一覧は9ページ分に渡る。9ページ目は48文字のみ。
以上が文字一覧関係で、一覧で選んでAボタンを押した文字が、画面上部ウィンドウに表示される。表示できる文字数はシナリオなどで異なるが最大で6文字。
入力した文字数は$7E:2F44に入るが、1文字で+$02されるため、入る数値は$00~$0Cである。これは文字一覧表では1文字が2バイトの領域を使っている都合である。
$7E:2F46~$7E:2F51には、2バイトずつ、入力した文字のデータが入る。
初期値として、2バイトずつ、空白スペースを意味する「$20 $00」が入るようになっている。
実際に文字を入力した場合、文字一覧表の2バイトのデータが入るので、たとえば1文字目に「ア」を入力すると、$7E:2F46に$61、$7E:2F47に$00が入る。
まとめると、ページ数の$7E:2F3C、座標の$7E:2F40と$7E:2F42で、カーソルの位置の文字がわかり、入力文字数は$7E:2F44、入力文字列は$7E:2F46~$7E:2F51で判定することになる。
このほかにも、入力中は$7E:2000~に途中計算の値が入ることがあるが、文字一覧切り替えで上書きされたりもする。
実際にどのような手順で値が入っていくかは、下で説明していく。
以上が基本となる。
SF編では、部屋に入力する時に主人公(キューブ)の名前を入力する、特定の文字列やパスワードを入力するなど、シナリオ進行に文字入力が関わる場面がある。
この時の判定方法などのサブルーチンを紹介していく。
なお、SF編での文字入力は、どの場面でも、カタカナとアルファベット・記号しか入力できない。
文字入力用ページは、1ページ目がカタカナ一覧、2ページ目がアルファベット・記号となる。
まず、入力文字列だが、$02:9380~$02:A695に格納されているシステム関係テキスト内に、SF編関係の単語がある。
システム関係テキストのため、頻繁にアクセスする都合か、$02から$22, $42, $62にミラーリングされ、さらにそこから$82, $A2, $C2, $E2にミラーリングされている。
実際のプログラムではバンク$C2のデータをロードしているので、そちらのアドレスで掲載する。
| 開始アドレス | テキスト(デコード済) | イベント |
|---|---|---|
$C2:A076 | ワープ | 「メモリーカード」入手時 |
$C2:A07A | コロ | 偽キューブイベント |
$C2:A07D | ぎ装こう作 | ? |
$C2:A085 | JUDGE | ラストバトル後のメインコンピュータルーム入口パスワード |
$C2:A08B | OAKFDE | ホル船長のパスワード |
実際には単語と単語の間には$00が入って区切られている。
例えば、「ワープ」と「コロ」だったら、
| アドレス | 数値 | テキスト(デコード済) |
|---|---|---|
$C2:A076 | $8C | ワ |
$C2:A077 | $40 | ー |
$C2:A078 | $A8 | プ |
$C2:A079 | $00 | (区切り) |
$C2:A07A | $6A | コ |
$C2:A07B | $8B | ロ |
といったようになっている。
主人公・キューブの名前はプレイヤーが変更可能なので、上のテキスト一覧の中にはないが、本作をプレイ済みなら「ワープ」「コロ」「JUDGE」「OAKFDE」がどこで入力されるかわかるだろう。
だが、3番目の「ぎ装こう作」は何なのか。
なお、SFC版では「偽」「工」の文字は収録していないため、「偽装工作」は「ぎ装こう作」と表示するしかない。
プレイヤーがつけたSF編主人公の名前が入るアドレスは、シナリオ別キャラデータのSF編主人公のアドレス、$00:0D80~$00:0DBFの64バイトの容量の中で、最後の$00:0DB4~$00:0DBF、12バイト部分にあたる。
主人公の名前は最大6文字だが、漢字を使うと2バイトの容量が必要なので、12バイト分の領域が確保されている。
「キューブ」なら、4バイト分の容量なので、$00:0DB4~$00:0DB7に値が入っている。
文字が入っていない箇所は$00で埋まっている。空白(スペース)は$20なので別扱いである。
| アドレス | 値 | 文字 |
|---|---|---|
$00:0DB4 | $67 | キ |
$00:0DB5 | $8F | ュ |
$00:0DB6 | $40 | ー |
$00:0DB7 | $AD | ブ |
$00:0DB8 | $00 | |
$00:0DB9 | $00 | |
$00:0DBA | $00 | |
$00:0DBB | $00 | |
$00:0DBC | $00 | |
$00:0DBD | $00 | |
$00:0DBE | $00 | |
$00:0DBF | $00 |
まずは、ストーリー序盤、寄り道要素ではあるが初めてキューブの名前を入れて扉を開けるであろう、レイチェルの部屋への入室時のサブルーチンを紹介する。
主人公の名前は「キューブ」とする。よって「キューブ」と入力すると正解扱いで、それ以外の文字だと不正解になり入室できない。
名前入力画面で「キューブ」と入力してからの処理は以下のようになる。
Xの値が重要なので併記した。
最初の$C2/443C~$C2/4441で、必ずX = $0000になる。
$C2/443A STA $27 [$00:0527] ;X:0000 Aを[$00:0527]に書き込み $C2/443C LDA #$0000 ;X:0000 Aに$0000をロード $C2/443F STA $29 [$00:0529] ;X:0000 A($0000)を[$00:0529]に書き込み $C2/4441 TXA ;X:0000 Xレジスタの値をAレジスタに転送 $C2/4442 CLC ;X:0000 キャリーフラグクリア $C2/4443 ADC #$000C ;X:0000 A + $000C $C2/4446 STA $2B [$00:052B] ;X:0000 Aを[$00:052B]に書き込み $C2/4448 JSR $4388 [$C2:4388] ;X:0000 [$C2:4388]へジャンプ ; $C2/4388 PHP ;X:0000 ステータスレジスタをスタックへプッシュ $C2/4389 REP #$20 ;X:0000 Aを16bit幅に変更、Mフラグをクリア $C2/438B LDY $00 [$00:0500] ;X:0000 Yに[$00:0500]をロード ; ;シナリオ別キャラデータの ;SF編主人公の名前呼び出しサブルーチン ループ1回目 $C2/438D LDA [$27],y[$00:0DB4] ;X:0000 Aに[$00:0DB5][$00:0DB4](名前1文字目、「キ」=$67)をロード $C2/438F INY ;X:0000 Yをインクリメント +1 $C2/4390 AND #$00FF ;X:0000 Aと$00FFで論理積 $C2/4393 BEQ $22 [$43B7] ;X:0000 ゼロフラグが立っているとき[$43B7]分岐 $C2/4395 CMP #$0020 ;X:0000 Aと$0020を減算比較(ステータスレジスタ変更のみ) $C2/4398 BPL $0F [$43A9] ;X:0000 ネガティブフラグが立っていないとき[$43A9]分岐 $C2/43A9 STA $2000,x[$7E:2000] ;X:0000 Aを[$7E:2001][$7E:2000]に書き込み $C2/43AC INX ;X:0000 Xをインクリメント(+1) $C2/43AD INX ;X:0001 Xをインクリメント(+1) $C2/43AE CPX #$01FE ;X:0002 Xと$01FEを減算比較(ステータスレジスタ変更のみ) $C2/43B1 BPL $04 [$43B7] ;X:0002 ネガティブフラグが立っていないとき[$43B7]分岐 $C2/43B3 CPX $2B [$00:052B] ;X:0002 Xと[$00:052B]($01FE)を減算比較(ステータスレジスタ変更のみ) $C2/43B5 BMI $D6 [$438D] ;X:0002 ネガティブフラグが立っているとき[$438D]分岐 ;ループ1回目 ここまで
$C2/438D~$C2/43B5が、シナリオ別キャラデータから、SF編主人公の名前を1文字ずつロードするループ処理である。
16bitモードなので、Aにロードする値は2バイトずつになる。
2バイト読み込むのは、先に記した通り、漢字が使用されている場合は「$1Dまたは$1Eまたは$1F」の後に$00~$FFの2バイトで表記するためである。
まずは$00FFと論理積をとって、下1バイトのみ、つまり$C2/438Dで呼び出したアドレスの値だけにする。
$C2/4393でゼロフラグでの分岐があるが、これはロードした名前の文字が$00、つまり「存在していない」時である。
名前の1文字目には、必ず何かの文字が入るので、ループ1回目の時にゼロフラグが立つことはない。
だが、もし主人公の名前がひらがなやカタカナ1文字などの場合は、2文字目以降はすべて$00で埋まっている。
その場合は「名前の読み込みを終了」と判断してループを抜けることになる。
ここではループを抜けずに$C2/4395に進む。
$C2/4395は、呼び出した値と$0020を減算比較している。
たとえば「キューブ」の「キ」は文字データだと$67だから、$0067 - $0020である。
次はネガティブフラグの判定だが、ネガティブフラグが立つのは、減算で負の値になる時(2進数表記で、一番上の桁が1の時)である。
「キ」の場合はネガティブフラグは立たない。
逆にネガティブフラグが立つのは、値が$01~$0Fのみである。
通常、名前入力で、$01~$0Fが入るのは、漢字を入力した時の上1バイトの「$1Dまたは$1Eまたは$1F」だけである。
つまり、$C2/4398の分岐は、「漢字かどうか」の判定である。
実際、分岐先の$C2/439A~$C2/43A7では、実際に何の漢字なのかの処理が入る。
だが、SF編は、主人公の名前に漢字が使えない。入力できるのはカタカナと数字・記号類だけである。
このため、SF編においては、ここで必ずネガティブフラグが立たずに$C2/43A9に進む。
$C2/43A9では、SF編主人公の名前1文字目が、[$7E:2001][$7E:2000]に書き込まれる。
結局、名前に入っている文字が漢字以外なら、文字データ1バイトがそのまま[$7E:2000]に入り、[$7E:2001]には$00が入ることになる。
続いてXを2回インクリメントしているから、+2される。Xの初期値が$0000なので、ループ1回目で$0002に増えることになる。
$C2/43AEでは計算したX($0002)から、$01FEを減算する。
これは負の値を考慮しなければならない、つまり符号付き16ビット整数の減算になる。
答えは$FE04(負の値)で、ネガティブフラグが立つことになる。
ちなみにネガティブフラグが立たないのは、Xが$01FE以上の時になるから、ループ1回でXが+2されるこのループでは、おそらく$C2/43B1で必ずネガティブフラグが立つ。
また、$C2/43B3ではXと[$00:052B]の減算比較をしているが、[$00:052B]にはこの処理の少し前に$01FEが入ったので、実質$C2/43AEと同じ処理になる。
ただし、$C2/43B5はネガティブフラグが立っている場合分岐(ループ頭の$C2/438Dに戻る)であるから、ここで必ず$C2/438Dにジャンプする。
つまり、このループから抜けられるのは、$C2/4393でゼロフラグが立った時、つまり名前をすべてロードしたタイミングでのループになる、ということがわかる。
名前が「キューブ」なら、4ループ目までですべての文字をロードし、5ループ目の途中でループを抜けることになる。
ついでなので、ループ処理をそのまま以下に掲載する。
;ループ2回目 $C2/438D LDA [$27],y[$00:0DB5] ;X:0002 Aに[$00:0DB5](名前2文字目、「ュ」=$8F)をロード $C2/438F INY ;X:0002 Yをインクリメント +1 $C2/4390 AND #$00FF ;X:0002 Aと$00FFで論理積 $C2/4393 BEQ $22 [$43B7] ;X:0002 ゼロフラグが立っているとき[$43B7]分岐 $C2/4395 CMP #$0020 ;X:0002 Aと$0020を減算比較(ステータスレジスタ変更のみ) $C2/4398 BPL $0F [$43A9] ;X:0002 ネガティブフラグが立っていないとき[$43A9]分岐 $C2/43A9 STA $2000,x[$7E:2002] ;X:0002 Aを[$7E:2003][$7E:2002]に書き込み $C2/43AC INX ;X:0002 Xをインクリメント(+1) $C2/43AD INX ;X:0003 Xをインクリメント(+1) $C2/43AE CPX #$01FE ;X:0004 Xと$01FEを減算比較(ステータスレジスタ変更のみ) $C2/43B1 BPL $04 [$43B7] ;X:0004 ネガティブフラグが立っていないとき[$43B7]分岐 $C2/43B3 CPX $2B [$00:052B] ;X:0004 Xと[$00:052B]($01FE)を減算比較(ステータスレジスタ変更のみ) $C2/43B5 BMI $D6 [$438D] ;X:0004 ネガティブフラグが立っているとき[$438D]分岐 ;ループ2回目 ここまで ; ;ループ3回目 $C2/438D LDA [$27],y[$00:0DB6] ;X:0004 Aに[$00:0DB6](名前3文字目、「ー」=$40)をロード $C2/438F INY ;X:0004 Yをインクリメント +1 $C2/4390 AND #$00FF ;X:0004 Aと$00FFで論理積 $C2/4393 BEQ $22 [$43B7] ;X:0004 ゼロフラグが立っているとき[$43B7]分岐 $C2/4395 CMP #$0020 ;X:0004 Aと$0020を減算比較(ステータスレジスタ変更のみ) $C2/4398 BPL $0F [$43A9] ;X:0004 ネガティブフラグが立っていないとき[$43A9]分岐 $C2/43A9 STA $2000,x[$7E:2004] ;X:0004 Aを[$7E:2005][$7E:2004]に書き込み $C2/43AC INX ;X:0004 Xをインクリメント(+1) $C2/43AD INX ;X:0005 Xをインクリメント(+1) $C2/43AE CPX #$01FE ;X:0006 Xと$01FEを減算比較(ステータスレジスタ変更のみ) $C2/43B1 BPL $04 [$43B7] ;X:0006 ネガティブフラグが立っていないとき[$43B7]分岐 $C2/43B3 CPX $2B [$00:052B] ;X:0006 Xと[$00:052B]($01FE)を減算比較(ステータスレジスタ変更のみ) $C2/43B5 BMI $D6 [$438D] ;X:0006 ネガティブフラグが立っているとき[$438D]分岐 ;ループ3回目 ここまで ; ;ループ4回目 $C2/438D LDA [$27],y[$00:0DB7] ;X:0006 Aに[$00:0DB7](名前4文字目、「ブ」=$AD)をロード $C2/438F INY ;X:0006 Yをインクリメント +1 $C2/4390 AND #$00FF ;X:0006 Aと$00FFで論理積 $C2/4393 BEQ $22 [$43B7] ;X:0006 ゼロフラグが立っているとき[$43B7]分岐 $C2/4395 CMP #$0020 ;X:0006 Aと$0020を減算比較(ステータスレジスタ変更のみ) $C2/4398 BPL $0F [$43A9] ;X:0006 ネガティブフラグが立っていないとき[$43A9]分岐 $C2/43A9 STA $2000,x[$7E:2006] ;X:0006 Aを[$7E:2007][$7E:2006]に書き込み $C2/43AC INX ;X:0006 Xをインクリメント(+1) $C2/43AD INX ;X:0007 Xをインクリメント(+1) $C2/43AE CPX #$01FE ;X:0008 Xと$01FEを減算比較(ステータスレジスタ変更のみ) $C2/43B1 BPL $04 [$43B7] ;X:0008 ネガティブフラグが立っていないとき[$43B7]分岐 $C2/43B3 CPX $2B [$00:052B] ;X:0008 Xと[$00:052B]($01FE)を減算比較(ステータスレジスタ変更のみ) $C2/43B5 BMI $D6 [$438D] ;X:0008 ネガティブフラグが立っているとき[$438D]分岐 ;ループ4回目 ここまで ; ;ループ5回目($C2/4393で停止) $C2/438D LDA [$27],y[$00:0DB8] ;X:0008 Aに[$00:0DB8](名前5文字目、$00)をロード $C2/438F INY ;X:0008 Yをインクリメント +1 $C2/4390 AND #$00FF ;X:0008 Aと$00FFで論理積 $C2/4393 BEQ $22 [$43B7] ;X:0008 ゼロフラグが立っているとき[$43B7]分岐 $C2/43B7 STZ $2000,x[$7E:2008] ;X:0008 [$7E:2008] = $00 $C2/43BA PLP ;X:0008 Pレジスタにスタックからプル $C2/43BB RTS ;X:0008 サブルーチン戻り
以上のループ処理で、値が以下のように入ったことになる。
| アドレス | 数値 |
|---|---|
$7E:2000 | $67 |
$7E:2001 | $00 |
$7E:2002 | $8F |
$7E:2003 | $00 |
$7E:2004 | $40 |
$7E:2005 | $00 |
$7E:2006 | $AD |
$7E:2007 | $00 |
$7E:2008 | $00 |
また、Xは$0008まで増加した。
この値はループ回数×2であり、同時にSF編主人公の名前の文字数×2にあたる。
では、続きを見てみよう。
ここからは、$7E:2000~に入った、シナリオ別キャラデータのSF編主人公の名前と、入室時に文字入力画面で入力した文字が一致するかの判断を行う。
先に説明したが、文字入力画面関係のメモリアドレスには以下のように値が入っている。
| アドレス | 内容 |
|---|---|
$7E:2F44 | 文字入力画面 入力した文字数(1文字で+$02) $00~$0C |
$7E:2F46~$7E:2F47 | 文字入力画面 入力欄1文字目(2バイト) |
$7E:2F48~$7E:2F49 | 文字入力画面 入力欄2文字目(2バイト) |
$7E:2F4A~$7E:2F4B | 文字入力画面 入力欄3文字目(2バイト) |
$7E:2F4C~$7E:2F4D | 文字入力画面 入力欄4文字目(2バイト) |
$7E:2F4E~$7E:2F4F | 文字入力画面 入力欄5文字目(2バイト) |
$7E:2F50~$7E:2F51 | 文字入力画面 入力欄6文字目(2バイト) |
$C2/53DB CPX $2F44 [$7E:2F44] ;X:0008 Xと[$7E:2F44](入力した文字数)を減算比較(ステータスレジスタ変更のみ) $C2/53DE BNE $13 [$53F3] ;X:0008 ゼロフラグが立っていないとき[$53F3]分岐 $C2/53E0 JSR $5383 [$C2:5383] ;X:0008 [$C2:5383]へジャンプ
まず、Xと[$7E:2F44]の値を減算比較している。
この時点のXには、「SF編主人公の名前の文字数×2」の値が入っている。
そして、[$7E:2F44]には、「文字入力画面で入力した文字数×2」が入っている。
入力文字数が一致している場合、減算したら0になり、ゼロフラグが立つ。
つまり、ここでまず、「入力文字数がSF編主人公の名前の文字数と一致しているか」が判定され、一致しないなら[$53F3]にジャンプし、文字が一致しているかどうかの判定をしない。つまり、この時点で「入力ミス」と判断されることになる。
入力文字数が一致している場合は、更に、文字が一致しているかを1文字ずつ判定するサブルーチンに進むことになる。
文字数が合っていなければ、1文字ずつ調べるまでもなく当然不正解ということであるから、ここで分岐するのは合理的であるし、この後の文字一致判定用のループ回数の設定もできるという上で一石二鳥である。
$C2/5383 LDY #$0000 ;Yに$0000をロード ; ;SF編主人公の名前と ;入力文字の一致を確認するループ処理1回目 $C2/5386 LDA $2000,y[$7E:2000] ;Aに[$7E:2000]をロード $C2/5389 CMP $2F46,y[$7E:2F46] ;Aと[$7E:2F46]を減算比較(ステータスレジスタ変更のみ) $C2/538C BNE $0D [$539B] ;ゼロフラグが立っていないとき[$539B]分岐 $C2/538E CPY $2F44 [$7E:2F44] ;Yと[$7E:2F44]を減算比較(ステータスレジスタのみ変更) $C2/5391 BPL $04 [$5397] ;ネガティブフラグが立っていないとき[$5397]分岐 $C2/5393 INY ;Yをインクリメント +1 $C2/5394 INY ;Yをインクリメント +1 $C2/5395 BRA $EF [$5386] ;フラグにかかわりなく常に分岐[$5386] ;ループ1回目 ここまで
[$7E:2000](主人公の名前1文字目)と[$7E:2F46](入力した文字1文字目)を減算比較し、ゼロフラグが立つかどうか、という判定である。
文字が一致するなら、ゼロフラグが立つことになる。
一致しない場合は[$539B]に分岐、失敗判定という扱いになる。
一致してゼロフラグが立ったら、ループ前に$0000が入ったYと、入力文字数×2が入っている[$7E:2F44]を減算比較する。
ネガティブフラグが立つのは、減算で負の値になった時だから、Yと入力文字数×2が同値になるまでは減算で必ずネガティブフラグが立つことになり、先に進む。
$C2/5393~$C2/5394でYをインクリメントしているから、Yの値が+2され、ループの頭$C2/5386に戻る。
つまりループ1回でYが+2されていくため、ループ4回、「キューブ」の文字数だけループを繰り返せば、ループ5回目の$C2/5391でループから抜け出せる、という仕組みになる。
1文字でも一致しなければ$C2/538Cで失敗と判断されてループから抜ける。
成功する場合の続きのループ処理は以下の通り。
;ループ2回目 $C2/5386 LDA $2000,y[$7E:2002] ;Aに[$7E:2002]をロード $C2/5389 CMP $2F46,y[$7E:2F48] ;Aと[$7E:2F48]を減算比較(ステータスレジスタ変更のみ) $C2/538C BNE $0D [$539B] ;ゼロフラグが立っていないとき[$539B]分岐 $C2/538E CPY $2F44 [$7E:2F44] ;Yと[$7E:2F44]を減算比較(ステータスレジスタのみ変更) $C2/5391 BPL $04 [$5397] ;ネガティブフラグが立っていないとき[$5397]分岐 $C2/5393 INY ;Yをインクリメント +1 $C2/5394 INY ;Yをインクリメント +1 $C2/5395 BRA $EF [$5386] ;フラグにかかわりなく常に分岐[$5386] ;ループ2回目 ここまで ; ;ループ3回目 $C2/5386 LDA $2000,y[$7E:2004] ;Aに[$7E:2004]をロード $C2/5389 CMP $2F46,y[$7E:2F4A] ;Aと[$7E:2F4A]を減算比較(ステータスレジスタ変更のみ) $C2/538C BNE $0D [$539B] ;ゼロフラグが立っていないとき[$539B]分岐 $C2/538E CPY $2F44 [$7E:2F44] ;Yと[$7E:2F44]を減算比較(ステータスレジスタのみ変更) $C2/5391 BPL $04 [$5397] ;ネガティブフラグが立っていないとき[$5397]分岐 $C2/5393 INY ;Yをインクリメント +1 $C2/5394 INY ;Yをインクリメント +1 $C2/5395 BRA $EF [$5386] ;フラグにかかわりなく常に分岐[$5386] ;ループ3回目 ここまで ; ;ループ4回目 $C2/5386 LDA $2000,y[$7E:2006] ;Aに[$7E:2006]をロード $C2/5389 CMP $2F46,y[$7E:2F4C] ;Aと[$7E:2F4C]を減算比較(ステータスレジスタ変更のみ) $C2/538C BNE $0D [$539B] ;ゼロフラグが立っていないとき[$539B]分岐 $C2/538E CPY $2F44 [$7E:2F44] ;Yと[$7E:2F44]を減算比較(ステータスレジスタのみ変更) $C2/5391 BPL $04 [$5397] ;ネガティブフラグが立っていないとき[$5397]分岐 $C2/5393 INY ;Yをインクリメント +1 $C2/5394 INY ;Yをインクリメント +1 $C2/5395 BRA $EF [$5386] ;フラグにかかわりなく常に分岐[$5386] ;ループ4回目 ここまで ; ;ループ5回目($C2/5391で停止) $C2/5386 LDA $2000,y[$7E:2008] ;Aに[$7E:2008]をロード $C2/5389 CMP $2F46,y[$7E:2F4E] ;Aと[$7E:2F4E]を減算比較(ステータスレジスタ変更のみ) $C2/538C BNE $0D [$539B] ;ゼロフラグが立っていないとき[$539B]分岐 $C2/538E CPY $2F44 [$7E:2F44] ;Yと[$7E:2F44]を減算比較(ステータスレジスタのみ変更) $C2/5391 BPL $04 [$5397] ;ネガティブフラグが立っていないとき[$5397]分岐 $C2/5397 LDA #$0001 ;Aに#$0001をロード $C2/539A RTS ;サブルーチン戻り
主人公の名前4文字がすべて一致したら、$C2/539Aまで到達することになる。
ちなみに文字数自体が異なる場合の飛び先は$C2/53F3で、文字数が合っていても文字が異なる場合での飛び先は$C2/539Bと異なるのだが、結局失敗と判断されることに変わりはない。
成功すると上の$C2/5397のようにAに$0001をロードするのだが、失敗時は、いずれの処理でもAに$0000が入る、または、入ったままという違いにより処理が変わってくる。
;文字数が異なる場合(Aに$0000が入った状態) $C2/53F3 RTS ;サブルーチン戻り $C2/53C8 PLP ;Pレジスタにスタックからプル $C2/53C9 RTS ;サブルーチン戻り ;文字数が合っていても文字が異なる場合 $C2/539B LDA #$0000 ;Aに#$0000をロード $C2/539E RTS ;サブルーチン戻り
以上が、主人公の名前と入力した文字列が一致するかの判定サブルーチン処理になる。
順序は、
[$7E:2000]~にロードということがわかった。
主人公の名前以外の固定名詞、「ワープ」「コロ」「JUDGE」「OAKFDE」における判定も、名前一致判定と基本的な手順は変わらない。
以下では、カークの部屋で「メモリーカード」を回収するためのパスワード「ワープ」を判定する場面を紹介する。
主人公の名前の入力と異なるのは、正解である「ワープ」の文字列の呼び出しである。
| 開始アドレス | テキスト(デコード済) | イベント |
|---|---|---|
$C2:A076 | ワープ | 「メモリーカード」入手時 |
$C2:A07A | コロ | 偽キューブイベント |
$C2:A07D | ぎ装こう作 | ? |
$C2:A085 | JUDGE | ラストバトル後のメインコンピュータルーム入口パスワード |
$C2:A08B | OAKFDE | ホル船長のパスワード |
| アドレス | 数値 | テキスト(デコード済) |
|---|---|---|
$C2:A076 | $8C | ワ |
$C2:A077 | $40 | ー |
$C2:A078 | $A8 | プ |
$C2:A079 | $00 | (区切り) |
$C2:A07A | $6A | コ |
$C2:A07B | $8B | ロ |
上の通り、$C2:A076~からが「ワープ」の文字列になる。文字コードだと「8C 40 A8」。
単語と単語の間のアドレスには$00が入って区切られている。
実際の処理は以下のとおり。
$C2/42D9 PHX ;X:0000 Xをスタックにプッシュ $C2/42DA AND #$00FF ;X:0000 Aと$00FFで論理積 $C2/42DD ASL A ;X:0000 Aを算術左シフト *2 $C2/42DE TAX ;X:0000 Aレジスタの値をXレジスタに転送 $C2/42DF LDA $C2A880,x[$C2:A9F0] ;X:0170 Aに[$C2:A9F0]をロード $C2/42E3 CLC ;X:0170 キャリーフラグクリア $C2/42E4 ADC #$9380 ;X:0170 A + $9380 $C2/42E7 STA $27 [$00:0527] ;X:0170 Aを[$00:0527]に書き込み $C2/42E9 LDA #$00C2 ;X:0170 Aに$00C2をロード $C2/42EC STA $29 [$00:0529] ;X:0170 A($00C2)を[$00:0529]に書き込み $C2/42EE LDA #$01FE ;X:0170 Aに$01FEをロード $C2/42F1 STA $2B [$00:052B] ;X:0170 A($01FE)を[$00:052B]に書き込み $C2/42F3 PLX ;X:0170 Xレジスタにスタックからプル $C2/42F4 RTS ;X:0000 サブルーチン戻り ; $C2/42F8 JMP $4386 [$C2:4386] ;X:0000 [$C2:4386]へ $C2/4386 LDX $00 [$00:0500] ;X:0000 Xに[$00:0500]をロード $C2/4388 PHP ;X:0000 ステータスレジスタをスタックへプッシュ $C2/4389 REP #$20 ;X:0000 Aを16bit幅に変更、Mフラグをクリア $C2/438B LDY $00 [$00:0500] ;X:0000 Yに[$00:0500]をロード ; ;「ワープ」呼び出しサブルーチン ループ1回目 $C2/438D LDA [$27],y[$C2:A076] ;X:0000 Aに[$C2:A077][$C2:A076](「ワープ」1~2文字目、「ワ」=$8C)をロード $C2/438F INY ;X:0000 Yをインクリメント(+1) $C2/4390 AND #$00FF ;X:0000 Aと$00FFで論理積 $C2/4393 BEQ $22 [$43B7] ;X:0000 ゼロフラグが立っているとき[$43B7]分岐 $C2/4395 CMP #$0020 ;X:0000 Aと$0020を減算比較(ステータスレジスタ変更のみ) $C2/4398 BPL $0F [$43A9] ;X:0000 ネガティブフラグが立っていないとき[$43A9]分岐 $C2/43A9 STA $2000,x[$7E:2000] ;X:0000 Aを[$7E:2000]に書き込み $C2/43AC INX ;X:0000 Xをインクリメント(+1) $C2/43AD INX ;X:0001 Xをインクリメント(+1) $C2/43AE CPX #$01FE ;X:0002 Xと$01FEを減算比較(ステータスレジスタ変更のみ) $C2/43B1 BPL $04 [$43B7] ;X:0002 ネガティブフラグが立っていないとき[$43B7]分岐 $C2/43B3 CPX $2B [$00:052B] ;X:0002 Xと[$00:052B]($01FE)を減算比較(ステータスレジスタ変更のみ) $C2/43B5 BMI $D6 [$438D] ;X:0002 ネガティブフラグが立っているとき[$438D]分岐 ;ループ1回目 ここまで
$C2/4388~以降は、名前一致判定における、主人公の名前呼び出しの時と同じサブルーチンになる。
それより前の処理は、「ワープ」を呼び出すためのアドレス計算などにあたる。
計算されたアドレスは、[$00:0528][$00:0527] = A076、[$00:052A][$00:0529] = $00C2を繋げた「C2052A」であるが、詳しい説明は省く。
Xについては、$C2/4386でXに[$00:0500]をロードしているが、かなり前の処理で($C2/00C6)、[$00:0500]に$00が書き込まれており、このままループ処理に入るため、名前一致判定の時と同様にXの値が1回のループで+2されていくことになる。
ループ終わりの$C2/43AE~$C2/43B5における、$01FEとの減算比較2回も同じ。
よってループから抜け出す処理は、名前の処理と同じく、$C2/4393のゼロフラグしかない。
単語と単語の間のアドレスには$00が入って区切られているため、「ワープ」を呼び出した後の次のアドレスに「$00」が入っており、ループを抜ける仕組みである。
このあたりも主人公の名前呼び出しの時と処理は変わらない、ということである。
以降のループ処理もそのまま記しておく。
;ループ2回目 $C2/438D LDA [$27],y[$C2:A077] ;X:0002 Aに[$C2:A078][$C2:A077](「ワープ」2~3文字目、「ー」=$40)をロード $C2/438F INY ;X:0002 Yをインクリメント(+1) $C2/4390 AND #$00FF ;X:0002 Aと$00FFで論理積 $C2/4393 BEQ $22 [$43B7] ;X:0002 ゼロフラグが立っているとき[$43B7]分岐 $C2/4395 CMP #$0020 ;X:0002 Aと$0020を減算比較(ステータスレジスタ変更のみ) $C2/4398 BPL $0F [$43A9] ;X:0002 ネガティブフラグが立っていないとき[$43A9]分岐 $C2/43A9 STA $2000,x[$7E:2002] ;X:0002 Aを[$7E:2002]に書き込み $C2/43AC INX ;X:0002 Xをインクリメント(+1) $C2/43AD INX ;X:0003 Xをインクリメント(+1) $C2/43AE CPX #$01FE ;X:0004 Xと$01FEを減算比較(ステータスレジスタ変更のみ) $C2/43B1 BPL $04 [$43B7] ;X:0004 ネガティブフラグが立っていないとき[$43B7]分岐 $C2/43B3 CPX $2B [$00:052B] ;X:0004 Xと[$00:052B]($01FE)を減算比較(ステータスレジスタ変更のみ) $C2/43B5 BMI $D6 [$438D] ;X:0004 ネガティブフラグが立っているとき[$438D]分岐 ;ループ2回目 ここまで ; ;ループ3回目 $C2/438D LDA [$27],y[$C2:A078] ;X:0004 Aに[$C2:A079][$C2:A078](「ワープ 」3~4文字目、「プ」=$A8)をロード $C2/438F INY ;X:0004 Yをインクリメント(+1) $C2/4390 AND #$00FF ;X:0004 Aと$00FFで論理積 $C2/4393 BEQ $22 [$43B7] ;X:0004 ゼロフラグが立っているとき[$43B7]分岐 $C2/4395 CMP #$0020 ;X:0004 Aと$0020を減算比較(ステータスレジスタ変更のみ) $C2/4398 BPL $0F [$43A9] ;X:0004 ネガティブフラグが立っていないとき[$43A9]分岐 $C2/43A9 STA $2000,x[$7E:2004] ;X:0004 Aを[$7E:2004]に書き込み $C2/43AC INX ;X:0004 Xをインクリメント(+1) $C2/43AD INX ;X:0005 Xをインクリメント(+1) $C2/43AE CPX #$01FE ;X:0006 Xと$01FEを減算比較(ステータスレジスタ変更のみ) $C2/43B1 BPL $04 [$43B7] ;X:0006 ネガティブフラグが立っていないとき[$43B7]分岐 $C2/43B3 CPX $2B [$00:052B] ;X:0006 Xと[$00:052B]($01FE)を減算比較(ステータスレジスタ変更のみ) $C2/43B5 BMI $D6 [$438D] ;X:0006 ネガティブフラグが立っているとき[$438D]分岐 ;ループ3回目 ここまで ; ;ループ4回目($C2/4393で停止) $C2/438D LDA [$27],y[$C2:A079] ;X:0006 Aに[$C2:A07A][$C2:A079]($20)をロード $C2/438F INY ;X:0006 Yをインクリメント(+1) $C2/4390 AND #$00FF ;X:0006 Aと$00FFで論理積 $C2/4393 BEQ $22 [$43B7] ;X:0006 ゼロフラグが立っているとき[$43B7]分岐 $C2/43B7 STZ $2000,x[$7E:2006] ;X:0006 [$7E:2006] = $00 $C2/43BA PLP ;X:0006 Pレジスタにスタックからプル $C2/43BB RTS ;X:0006 サブルーチン戻り
以上のループ処理で、値が以下のように入ったことになる。
| アドレス | 数値 |
|---|---|
$7E:2000 | $8C |
$7E:2001 | $00 |
$7E:2002 | $40 |
$7E:2003 | $00 |
$7E:2004 | $A8 |
$7E:2005 | $00 |
$7E:2006 | $00 |
また、Xは$0006まで増加した。
この値はループ回数×2であり、同時に正解の文字列「ワープ」の文字数×2にあたる。
続きの処理も、名前処理時と変わらない。
$7E:2F44には文字入力画面で入力した文字数×2、$7E:2F46~以降には文字入力画面で入力した文字が1文字につき2バイトの領域を使って入っている。
$C2/53DB CPX $2F44 [$7E:2F44] ;X:0006 Xと[$7E:2F44](入力した文字数)を減算比較(ステータスレジスタ変更のみ) $C2/53DE BNE $13 [$53F3] ;X:0006 ゼロフラグが立っていないとき[$53F3]分岐 $C2/53E0 JSR $5383 [$C2:5383] ;X:0006 [$C2:5383]へジャンプ
まず、「ワープ」の文字数×2が入ったXと、入力した文字数×2が入っている[$7E:2F44]の値を比較して、ゼロフラグが立つ、つまり同一の時のみ先の処理へ進む。
$C2/5383 LDY #$0000 ;Yに$0000をロード ; ;「ワープ」と入力文字の一致を確認するループ処理1回目 $C2/5386 LDA $2000,y[$7E:2000] ;Aに[$7E:2000]をロード $C2/5389 CMP $2F46,y[$7E:2F46] ;Aと[$7E:2F46]を減算比較(ステータスレジスタ変更のみ) $C2/538C BNE $0D [$539B] ;ゼロフラグが立っていないとき[$539B]分岐 $C2/538E CPY $2F44 [$7E:2F44] ;Yと[$7E:2F44]を減算比較(ステータスレジスタのみ変更) $C2/5391 BPL $04 [$5397] ;ネガティブフラグが立っていないとき[$5397]分岐 $C2/5393 INY ;Yをインクリメント +1 $C2/5394 INY ;Yをインクリメント +1 $C2/5395 BRA $EF [$5386] ;フラグにかかわりなく常に分岐[$5386] ;ループ1回目 ここまで
この処理も、名前処理と同一である。
「ワープ」は3文字なので、ループは4回目の$C2/5391に到達した時点で終了する。
結局、プレイヤーが付けた名前と、決められた文字列とで、呼び出し方が少し異なるだけで処理自体は同一ということがわかった。
基本は、
[$7E:2000]~にロードこうなる。
気になるのは「正解がない」パターンである。
SF編の中盤、ヒューイが亡くなった直後に、カトゥーから端末室で船長室のパスワードを聞いてくるように頼まれる。
端末室では「OAKFDE」がパスワードだと教えてもらえるが、実際には間違いである。
「OAKFDE」を聞いたら、コールドスリープルームでカトゥーと合流するとストーリーが進むのだが、コールドスリープルームに行く前に、実際に船長室の扉横の端末に「OAKFDE」を入力してみることも可能。
だが、「OAKFDE」でも、他の言葉でも、間違いであり、「トウロク エラー パスワード ガ チガイマス_」と表示されて扉は開かない。
この時は入力文字をいちいち判定することもなく、不正解と表示しているのだろうか?
実は、上のタイミングでもわざわざ、「OAKFDE」と一致しているかの処理自体はしている。
なのだが、一致してもしなくても、最終的にどちらも間違いという処理にたどり着いて「トウロク エラー パスワード ガ チガイマス_」と表示されてしまうのである。
ただし、この行動を行うことで、進行フラグを記録する$00:12??のアドレスの中の$00:129EのフラグがONになり($01が立つ)、コールドスリープルームでカトゥーと合流した時に、カトゥーのセリフが少し追加される。
以上が文字入力と文字列一致判定の大まかな説明である。
$C2:A07D~の「ぎ装こう作」が使われている形跡は、今回調べた範囲ではないようである。
筆者が見逃しているだけという可能性ももちろんあるが、少なくともパスワードの類ではないだろう。SF編ではパスワード入力にひらがなと漢字が使えないようになっているからである。
SF編においては、「偽装工作」であれこれ思い当たる節がありすぎるが、どこかで何かのネタに使おうとしていたものの没となったのだろうか。