TOP > プログラミング関係解説&調査 > マップタイマー > 特殊なマップタイマー

特殊なマップタイマー

[$00:0AC0]~[$00:0AC1]の2バイトで管理できるマップタイマーは合計16だが、ここではそれ以外で管理されるマップタイマーについて説明する。
[$00:0AC0]~[$00:0AC1]は各シナリオでのみ使用、かつセーブデータに記録する必要があるタイマーが管理されるのだろう、と推測できるが、複数シナリオで同じタイマーを使う場合(ポゴの匂いタイマー)、セーブデータに記録する必要がないタイマー(現代編ラストバトルに負けた時のカウントダウン)は、別に管理されているようだ。

現代編・ラストバトル敗北時カウントダウン

現代編ラストバトル(オディー・O戦)で負けると、9カウント(9秒)のカウントダウンが始まり、0になるまでにAボタンを押さない場合、ゲームオーバーとなってタイトル画面に戻る。Aボタンを押せば再戦可能。
格闘ゲームのコンティニュー画面のパロディになるが、ここでも1秒(60フレーム)毎のカウントダウンタイマーが動いている。

現代編は戦闘のみのシナリオのため、プログラムも特殊で、対戦相手選択画面や、戦闘前後の場面なども、メニュー画面と同じバンク$C2に格納されたサブルーチンで動いている。
フィールド用のバンク$C0のプログラムは、高原のフィールド用スプライトが表示される、オープニングデモや、ラストバトル前後の河原でのシーンのみ。
ラストバトル敗北時カウントダウンも、バンク$C2のサブルーチンを使っている。

メモリアドレス

カウントダウンに関係するメモリアドレスは以下。

$00:0591

現代編 ラストバトル敗北時のカウントダウンタイマー、60フレーム分のカウントダウン用の値が入る。
初期値は$3C(10進数60)で、1フレームにつき1減る。

$00:0592~$00:0593

現代編ラストバトルで負けた時のカウントダウンタイマー、残り秒数が入る。
2バイト値だが、実質$00:0592の値しか使わない。
初期値は$0Aだが、カウントダウン開始と同時に-1されて$09となり、9秒のカウントダウンを行う。
同じ値が[$7E:2E79][$7E:2E78]にコピーされて、グラフィックデータの表示処理に使用される。

$7E:0ABE

現代編戦闘開始時、対戦相手の値$00~$07が入る。
ラストバトルのカウントダウンのサブルーチンは、現代編で対戦相手に負けた時の分岐処理になるため、「誰に負けたか」の判定・分岐処理に使う。

対戦相手値
ナムキャット$00
グレートエイジャ$01
トゥーラ・ハン$02
森部 生士$03
マックスモーガン$04
J・イヤウケア$05
オディー・O$07
$7E:0ABF

現代編において、初期値$00、倒した対戦相手に対応したビットに1が立つ(Bit0~6)、オブライト戦前は%0011 1111($3F)
今回の判定には関わらないが、現代編でのフラグ判定として重要なアドレスなので参考までに。

サブルーチン

以降は、カウントダウン中にAボタンが押されず、0までカウントダウンされてゲームオーバーになる場合の処理。
途中でAボタンを押したらカウントダウンは中断される。

敗北時、対戦相手がオディー・Oという判定をまず紹介しておく。
敗北し戦闘画面が暗転した直後、以下判定が入る。

$C2/281C LDA $0ABE  [$7E:0ABE]   ;Aに[$7E:0ABF][$7E:0ABE](オディー・Oだと[$7E:0ABE]は$07)をロード
$C2/281F AND #$000F              ;Aと$000Fで論理積
$C2/2822 CMP #$0007              ;Aと$0007を減算比較(ステータスレジスタ変更のみ)
$C2/2825 BEQ $32    [$2859]      ;ゼロフラグが立っているとき[$2859]分岐
;ゼロフラグON 対戦相手がオディー・O
$C2/2859 JSR $56C8  [$C2:56C8]   ;[$C2:56C8]へジャンプ

オディー・Oと対戦中は、[$7E:0ABE]に$07が入るため、$C2/2825でゼロフラグが立ち分岐する。
ここが、ラストバトルとラストバトル以外での分岐ということでもある。

この後にサウンド処理、グラフィック処理などが挟まり、タイマー関係の処理が始まる。

$C2/286B LDA #$003C              ;Aに$003Cをロード
$C2/286E STA $90    [$00:0590]   ;Aを[$00:0591][$00:0590](タイマー/フレーム単位)に書き込み
$C2/2870 STA $91    [$00:0591]   ;Aを[$00:0592][$00:0591](タイマー/フレーム単位)に書き込み
$C2/2872 LDA #$000A              ;Aに$000Aをロード
$C2/2875 STA $92    [$00:0592]   ;Aを[$00:0593][$00:0592]に書き込み(タイマー/秒単位)
$C2/2877 LDA #$0080              ;Aに$0080をロード
$C2/287A TSB $8F    [$00:058F]   ;Aと[$00:0590][$00:058F]で論理和を取り、メモリに書き戻し
$C2/287C JSR $564F  [$C2:564F]   ;[$C2:564F]へジャンプ
$C2/286B~$C2/287Cがタイマーの初期値書き込みで、まとめると、
アドレス値内容
[$00:0590]$3Cタイマー/フレーム単位($3C = 10進数60)
[$00:0591]$3Cタイマー/フレーム単位($3C = 10進数60)
[$00:0592]$0Aタイマー/秒単位($0A = 10進数10)

である。
([$00:0593]にも$00が入るが、カウントダウン中は値は変動しない)
タイマーとして実際に変動するのは[$00:0591]と[$00:0592]で、[$00:0590]はタイマー初期値として$3Cのまま残される。

$C2/2877~$C2/287Aで処理される[$00:0590][$00:058F]だが、通常$0000が入っていて、このタイミングで$0000と$0080の論理和を取り[$00:0590][$00:058F]に書き戻すため、[$00:0590]が$00、[$00:058F]が$80となる。
カウントダウン中かどうかの判定値になるようである。

$C2/564F PHP                     ;ステータスレジスタをスタックへプッシュ
$C2/5650 REP #$20                ;Aを16bit幅に変更、Mフラグをクリア
$C2/5652 LDA $92    [$00:0592]   ;Aに[$00:0593][$00:0592]をロード
$C2/5654 DEC A                   ;Aをデクリメント -1
$C2/5655 BPL $03    [$565A]      ;ネガティブフラグが立っていないとき[$565A]分岐
;ネガティブフラグOFF
$C2/565A STA $2E78  [$7E:2E78]   ;Aを[$7E:2E79][$7E:2E78]に書き込み(残り時間)
$C2/565D SEP #$20                ;MフラグON Aレジスタは8bit幅
$C2/565F LDA #$06                ;Aに$06をロード
$C2/5661 STA $2E7A  [$7E:2E7A]   ;Aを[$7E:2E7A]に書き込み
$C2/5664 LDA #$08                ;Aに$08をロード
$C2/5666 STA $2E7B  [$7E:2E7B]   ;Aを[$7E:2E7B]に書き込み
$C2/5669 JSR $5671  [$C2:5671]   ;[$C2:5671]へジャンプ

続けて、$C2/564F~で最初のカウントダウン処理が入り、[$00:0592]の値がAに呼び出され、デクリメント(-1)される。
ただし上処理では、[$00:0592]自体はデクリメントされていない、という点に注意。
$C2/5655ではデクリメント後のネガティブフラグ判定がある。初期値$0Aをデクリメントしていって、ネガティブフラグが立つのは、$00をデクリメントして$FFになった時(同時にキャリーフラグもON)である。
ここの分岐で、残り秒数が0かどうかを判定するのだろう、ということがだいたいわかる。

最初のデクリメントでは初期値$0A→$09なので、ネガティブフラグは立たない。
よって$C2/565Aに進み、残り秒数が2バイト値で[$7E:2E79][$7E:2E78]に入る。つまり$0009が書き込まれる。
実質、これがカウントダウン初期値(初期秒数)である。
[$7E:2E79][$7E:2E78]の値から、表示する数字のグラフィックなどが算出されるが、ここではグラフィック処理などは省略する。
$C2/565F~の[$7E:2E7A]、[$7E:2E7B]の書き込みもグラフィック関連である。

後は、1フレーム経過毎に、[$00:0591]の判定を行っていく。
最初の判定時は[$00:0592]にはまだ$0Aが入っている点に注意。

$C2/50D6 LDA $91    [$00:0591]   ;Aに[$00:0591]をロード(タイマー/フレーム単位)
$C2/50D8 BEQ $0B    [$50E5]      ;ゼロフラグが立っているとき[$50E5]分岐
;ゼロフラグOFF タイマー/フレーム単位が1以上
$C2/50DA BMI $09    [$50E5]      ;ネガティブフラグが立っているとき[$50E5]分岐
;ネガティブフラグOFF タイマー/フレーム単位が1以上
$C2/50DC DEC A                   ;Aをデクリメント -1
$C2/50DD BEQ $06    [$50E5]      ;ゼロフラグが立っているとき[$50E5]分岐
;ゼロフラグOFF タイマー/フレーム単位が1以上
$C2/50DF BMI $04    [$50E5]      ;ネガティブフラグが立っているとき[$50E5]分岐
;ネガティブフラグOFF タイマー/フレーム単位が1以上
$C2/50E1 STA $91    [$00:0591]   ;Aを[$00:0591]に書き込み
$C2/50E3 BRA $19    [$50FE]      ;フラグにかかわりなく常に分岐[$50FE]→処理終了
;ゼロフラグON タイマー/フレーム単位が0(またはそれ以下)まで減った
$C2/50E5 LDA $90    [$00:0590]   ;Aに[$00:0590]をロード(タイマー初期値$3C)
$C2/50E7 STA $91    [$00:0591]   ;Aを[$00:0591]に書き込み(タイマー初期値$3Cを上書き)
$C2/50E9 LDA $92    [$00:0592]   ;Aに[$00:0592]をロード(タイマー/秒単位)
$C2/50EB BEQ $0B    [$50F8]      ;ゼロフラグが立っているとき[$50F8]分岐
;ゼロフラグOFF タイマー/秒単位が1以上
$C2/50ED BMI $09    [$50F8]      ;ネガティブフラグが立っているとき[$50F8]分岐
;ネガティブフラグOFF タイマー/秒単位が1以上
$C2/50EF DEC A                   ;Aをデクリメント -1
$C2/50F0 BEQ $06    [$50F8]      ;ゼロフラグが立っているとき[$50F8]分岐
;ゼロフラグOFF タイマー/秒単位が1以上
$C2/50F2 BMI $04    [$50F8]      ;ネガティブフラグが立っているとき[$50F8]分岐
;ネガティブフラグOFF タイマー/秒単位が1以上
$C2/50F4 STA $92    [$00:0592]   ;Aを[$00:0592]に書き込み
$C2/50F6 BRA $02    [$50FA]      ;フラグにかかわりなく常に分岐[$50FA]
;ゼロフラグON/ネガティブフラグON タイマー/秒単位が0(またはそれ以下)まで減った
$C2/50F8 STZ $92    [$00:0592]   ;[$00:0592]に$00書き込み
;処理合流
$C2/50FA LDA #$01                ;Aに$01をロード
$C2/50FC TSB $8F    [$00:058F]   ;Aと[$00:058F](カウントダウン中$80)で論理和を取り、メモリに書き戻し→$01 | $80 = $81
;$C2/50E3ジャンプ先
$C2/50FE RTS                     ;サブルーチン戻り

分岐が多くてややこしいが、まとめてみると以下のようになる。

  • [$00:0591](タイマー/フレーム単位)が2以上なら、[$00:0591]を-1して処理終了
  • [$00:0591](タイマー/フレーム単位)が1(以下)なら、[$00:0591]を-1して(残り0フレームになる)、[$00:0591]に再度初期値$3Cを上書きする。
    • [$00:0592](タイマー/秒単位)が2以上なら、[$00:0592]を-1、[$00:058F]に$81書き込み
    • [$00:0592](タイマー/秒単位)が1(以下)なら、[$00:0592]に$00書き込み、[$00:058F]に$81書き込み

[$00:0591](タイマー/フレーム単位)が$02~$3Cなら、[$00:0591]を-1するだけで、[$00:0592](タイマー/秒単位)に対して何か処理する必要はない。
だが、[$00:0591](タイマー/フレーム単位)が$01の時は、-1すると$00、つまりフレーム単位でのカウントダウンが終了となる(1秒が経過したことになる)。
この場合、[$00:0592](タイマー/秒単位)は-1し、[$00:0592](タイマー/秒単位)には$3Cを上書きして、再び60フレームのカウントダウンを開始させることになる。

ここではじめて、[$00:0592]を減少させる、という点に注意。
最初に[$00:0592]が-1されるのは、$0Aが書き込まれてから60フレーム経過後の上処理で、$09が入ることになる。

残り0秒になった時のみ、$C2/50F8の処理で[$00:0592]に$00が入る。

[$00:0592](タイマー/秒単位)を-1した時だけ、[$00:058F]に$81を書き込む処理が入るのがポイント。

$C2/0602 LDA #$7E                ;Aに$7Eをロード
;(中略 押したボタン判定)
$C2/0626 JMP ($0532)[$C2:06F4]   ;[$C2:06F4]へジャンプ(レジスタをスタックに積まない)
$C2/06F4 SEP #$20                ;MフラグON Aレジスタは8bit幅
$C2/06F6 LDA $8F    [$00:058F]   ;Aに[$00:058F]をロード カウントダウン中、$80または$81
$C2/06F8 BMI $03    [$06FD]      ;ネガティブフラグが立っているとき[$06FD]分岐
;ネガティブフラグON [$00:058F]が$80または$81
$C2/06FD AND #$01                ;Aと$01で論理積
$C2/06FF BNE $03    [$0704]      ;ゼロフラグが立っていないとき[$0704]分岐
;ゼロフラグON [$00:058F]が$80
$C2/0701 JMP $05FB  [$C2:05FB]   ;[$C2:05FB]へジャンプ(レジスタをスタックに積まない)
;ゼロフラグOFF [$00:058F]が$81
$C2/0704 LDX #$705C              ;Xに$705Cをロード
$C2/0707 LDA $42    [$00:0542]   ;Aに[$00:0542]をロード
$C2/0709 JSR $0723  [$C2:0723]   ;[$C2:0723]へジャンプ

ここで、[$00:058F]の値の分岐が発生する。
[$00:0592](タイマー/秒単位)を-1した時は、[$00:058F]が$81なので、最終的に$C2/0709まで処理が進んで、$C2/0723にジャンプする。

$C2/0723にジャンプ後は、$C2/0731~$C2/0751、$C2/0726~$C2/072Bの後、$C2/28E6 JSR $564Fで$C2/564Fにジャンプする。
つまり秒数の処理へ飛ぶ。
[$00:058F]が$80の時には、秒数処理へは飛ばない、ということでもある。

$C2/564F PHP                     ;ステータスレジスタをスタックへプッシュ
$C2/5650 REP #$20                ;Aを16bit幅に変更、Mフラグをクリア
$C2/5652 LDA $92    [$00:0592]   ;Aに[$00:0593][$00:0592](タイマー/秒単位)をロード
$C2/5654 DEC A                   ;Aをデクリメント -1
$C2/5655 BPL $03    [$565A]      ;ネガティブフラグが立っていないとき[$565A]分岐
;ネガティブフラグON 残り0秒
$C2/5657 LDA #$0000              ;Aに$0000をロード
;ネガティブフラグOFF 残り1秒以上
$C2/565A STA $2E78  [$7E:2E78]   ;Aを[$7E:2E79][$7E:2E78]に書き込み
$C2/565D SEP #$20                ;MフラグON Aレジスタは8bit幅
$C2/565F LDA #$06                ;Aに$06をロード
$C2/5661 STA $2E7A  [$7E:2E7A]   ;Aを[$7E:2E7A]に書き込み
$C2/5664 LDA #$08                ;Aに$08をロード
$C2/5666 STA $2E7B  [$7E:2E7B]   ;Aを[$7E:2E7B]に書き込み
$C2/5669 JSR $5671  [$C2:5671]   ;[$C2:5671]へジャンプ

ここで、[$00:0592](タイマー/秒単位)を-1した値が、再び[$7E:2E79][$7E:2E78]に入ることになる。
ただ、先の$C2/50D6~で、[$00:0592](タイマー/秒単位)が残り0秒だと、$C2/5655でAに$FFFFが入り、ネガティブフラグが立つので、$0000を[$7E:2E79][$7E:2E78]に書き込むよう分岐処理が入る。

ちなみに、[$7E:2E7A]に書き込む$06と、[$7E:2E7B]に書き込む$08は、スプライトのオブジェクト属性におけるVRAMのスプライトのアドレス(byte2)。
カウントダウンの数字は7×5のドットを数字に見せて光らせているように表示するが、そのドットの光る丸が$06、暗い丸が$08にあたる。
座標はそのままで、1秒毎にスプライトのアドレスだけ入れ替えてドット毎に光らせたり暗くしたりしているようである。
デジタル数字用のスプライトのデータは、ラストバトル開始時にVRAMに転送させているが、ここではそのあたりは省略。

$C2/28E9 LDA $0ABE  [$7E:0ABE]   ;Aに[$7E:0ABF][$7E:0ABE]をロード
$C2/28EC AND #$000F              ;Aと$000Fで論理積
$C2/28EF CMP #$0007              ;Aと$0007を減算比較(ステータスレジスタ変更のみ)
$C2/28F2 BEQ $22    [$2916]      ;ゼロフラグが立っているとき[$2916]分岐
;ゼロフラグON 対戦相手がオディー・O
$C2/2916 RTS                     ;サブルーチン戻り

$C2/072E SEP #$20                ;MフラグON Aレジスタは8bit幅
$C2/0730 RTS                     ;サブルーチン戻り

$C2/070C SEP #$20                ;MフラグON Aレジスタは8bit幅
$C2/070E LDA #$01                ;Aに$01をロード
$C2/0710 TRB $8F    [$00:058F]   ;Aのビット反転値(%1111 1110)と[$00:058F]で論理積を取り書き戻し $81→$80
$C2/0712 LDA $92    [$00:0592]   ;Aに[$00:0592](タイマー/秒単位)をロード
$C2/0714 BEQ $03    [$0719]      ;ゼロフラグが立っているとき[$0719]分岐
;ゼロフラグOFF カウントダウン中
$C2/0716 JMP $05FB  [$C2:05FB]   ;[$C2:05FB]へジャンプ(レジスタをスタックに積まない)
;ゼロフラグON カウントダウン終了
$C2/0719 STZ $8F    [$00:058F]   ;[$00:058F]に$00を書き込み
$C2/071B LDX #$7060              ;Xに$7060をロード
$C2/071E LDA $42    [$00:0542]   ;Aに[$00:0542]をロード
$C2/0720 JMP $064E  [$C2:064E]   ;[$C2:064E]へジャンプ(レジスタをスタックに積まない)
$C2/064E JSR $0731  [$C2:0731]   ;[$C2:0731]へジャンプ

ここが、カウントダウンが続くか終了するかの分岐処理である。
$C2/28E9~$C2/2916は、[$7E:0ABE]の値で対戦相手がオディー・Oかどうかの分岐。
$C2/070C以降で分岐が発生し、[$00:058F]の値を$81から$80に書き直した後、[$00:0592]の値が0かどうかでゼロフラグ分岐となる。
[$00:0592]が1以上ならカウントダウン処理へ戻り、[$00:0592]が$00ならカウントダウン処理終了、ゲームオーバー処理へ進む。
この時、[$00:0591]にはいずれも$3Cが上書きされているが、[$00:0592]が$00の場合は[$00:0591]が$3Cのままゲームオーバー、ということになる。

なお、カウントダウン中の分岐は$C2/0716→$C2/05FBとジャンプするが、

$C2/05FB SEP #$20                ;MフラグON Aレジスタは8bit幅
$C2/05FD REP #$10                ;X/Yレジスタを16bit幅に変更、Xフラグをクリア
$C2/05FF JSR $4B22  [$C2:4B22]   ;[$C2:4B22]へジャンプ

次のジャンプ先の$C2/4B22はHDMA転送&NMIによる割り込み待ち用ループで、この後にグラフィック関係の転送処理などが入り、再びカウントダウン処理へ、とループすることになる。
以上が現代編ラストバトル敗北時カウントダウン処理である。

ポゴの匂いタイマー

ポゴが匂いを嗅ぐと(フィールドでYボタン)、360フレーム(6秒)の間、匂いが漂う。
この時のタイマーは$00:0ACA~$00:0ACBを使っている。
タイマーのカウントダウン関係の処理とともに、Yボタンを押した時の判定・分岐処理についても大まかに説明する。

匂いを嗅げるかどうか

Yボタンを押した時にポゴが匂いを嗅げるかどうかのフラグは、[$00:0AC7]に入る。
原始編を開始した時点では$00が入っており、序盤の狩場でのイベントでYボタンで匂いが嗅げるようになったタイミングで(長老がYボタンを押すことを教えてくれるタイミング)、以下の処理が入る。

$C0/27FB LDA $000AC7[$00:0AC7]   ;Aに[$00:0AC7]($00)をロード
$C0/27FF AND #$01                ;Aと$01で論理積 $00 & $01 = $00
$C0/2801 EOR #$01                ;Aと$01で排他的論理和XOR $00 XOR $01 = $01
$C0/2803 STA $000AC7[$00:0AC7]   ;Aを[$00:0AC7]に書き込み

以降は[$00:0AC7]に$01が入ったままで、最終編開始時にも引き継がれる。
(途中で原始編をプレイし直すと、開始時に$00が入るが、原始編含め7つのシナリオをクリアしていないと中世編・最終編が出現しないから、最終編開始時は$01が入ったまま)

だが、最終編、魔王山山頂のオディオが出現すると、同じサブルーチンで[$00:0AC7]に$00が書き込まれる。

$C0/27FB LDA $000AC7[$00:0AC7]   ;Aに[$00:0AC7]($01)をロード
$C0/27FF AND #$01                ;Aと$01で論理積 $01 & $01 = $01
$C0/2801 EOR #$01                ;Aと$01で排他的論理和XOR $01 XOR $01 = $00
$C0/2803 STA $000AC7[$00:0AC7]   ;Aを[$00:0AC7]に書き込み

このため、オディオが出現すると、パーティにポゴがいる状態でYボタンを押しても匂いが嗅げない。
(アキラがいる時は、Yボタンで心が読めない時はAボタンと同じく話しかける役割になるので、オディオに隣接してYを押すと会話が始まる)
以降、ピュアオディオを撃破してからエンディングまでは[$00:0AC7]に$00が入ったままのため、匂いが嗅げなくなる。

オディオ出現後は手前に(下に)引き返して階段に入ると、異次元空間へ飛ばされるが、この時には以下処理が入って、[$00:0AC7]に再び$01が入る。
これ以降は匂いが嗅げるようになる。

$C0/061B LDA $0AC7  [$00:0AC7]   ;Aに[$00:0AC7]($00)をロード
$C0/061E AND #$7F                ;Aと$7Fで論理積
$C0/0620 STA $0AC7  [$00:0AC7]   ;Aを[$00:0AC7]($00)に書き込み
$C0/0623 JSR $9A06  [$C0:9A06]   ;[$C0:9A06]へジャンプ
;
;(中略)
;
$C0/27FB LDA $000AC7[$00:0AC7]   ;Aに[$00:0AC7]($00)をロード
$C0/27FF AND #$01                ;Aと$01で論理積 $00 & $01 = $00
$C0/2801 EOR #$01                ;Aと$01で排他的論理和XOR $00 XOR $01 = $01
$C0/2803 STA $000AC7[$00:0AC7]   ;Aを[$00:0AC7]に書き込み

[$00:0AC7]はポゴが匂いを嗅ぐ能力を持っているかどうか、というフラグなので、最終編の場合、Yボタンを押せば必ず発動ではなく、ポゴがパーティにいるかどうかはもちろん、アキラの「心を読む」が優先になるか、といった分岐処理が発生する。
この先は[$00:0AC7]に$01が書き込まれている前提で、匂いを嗅いだ時のタイマー処理について記す。

Yボタンを押して匂いを嗅ぐ

匂いを嗅ぐ処理は、原始編でも最終編でも以下で共通。

;(前略)
$C0/139C JSR $1B74  [$C0:1B74]   ;[$C0:1B74]へジャンプ
;
;(匂いかぐモーション処理)
;
$C0/139F JSR $468F  [$C0:468F]   ;[$C0:468F]へジャンプ
;
;($C0/468F~$C0/476B)
;
$C0/13A2 LDA $24    [$00:0024]   ;Aに[$00:0024]をロード
$C0/13A4 BEQ $0E    [$13B4]      ;ゼロフラグが立っているとき[$13B4]分岐
$C0/13A6 LDA $0AC7  [$00:0AC7]   ;Aに[$00:0AC7]($01)をロード
$C0/13A9 ORA #$80                ;Aと$80で論理和
$C0/13AB STA $0AC7  [$00:0AC7]   ;Aを[$00:0AC7]($81)に書き込み
$C0/13AE LDX #$0168              ;Xに$0168をロード
$C0/13B1 STX $0ACA  [$00:0ACA]   ;Xを[$00:0ACB][$00:0ACA]に書き込み
$C0/13B4 RTS                     ;サブルーチン戻り

$C0/13AE~$C0/13B1で、[$00:0ACB][$00:0ACA]に$0168(10進数360)が入り、1フレームに1ずつ減って6秒のカウントダウンとなり、6秒間は匂いが漂う。
また、匂いが表示されている間だけ、[$00:0AC7]に$81が書き込まれる。

$C0/139Cまでの処理は原始編・最終編で分岐処理がある。
その説明は後回しにして、カウントダウン処理について説明する。
1フレームに1回、以下処理が入る。

$C0/4669 LDA $0AC7  [$00:0AC7]   ;Aに[$00:0AC7](カウントダウン中$81)をロード
$C0/466C CMP #$81                ;Aと$81を減算比較(ステータスレジスタ変更のみ)
$C0/466E BNE $13    [$4683]      ;ゼロフラグが立っていないとき[$4683]分岐
;ゼロフラグON 6秒のカウントダウン中
$C0/4670 LDX $0ACA  [$00:0ACA]   ;Xに[$00:0ACB][$00:0ACA]をロード
$C0/4673 DEX                     ;Xをデクリメント -1
$C0/4674 STX $0ACA  [$00:0ACA]   ;Xを[$00:0ACB][$00:0ACA]に書き込み
$C0/4677 BNE $0A    [$4683]      ;ゼロフラグが立っていないとき[$4683]分岐
;ゼロフラグON カウントダウン終了
$C0/4679 LDA $0AC7  [$00:0AC7]   ;Aに[$00:0AC7]($81)をロード
$C0/467C AND #$7F                ;Aと$7Fで論理積 $81 & $7F = $01
$C0/467E STA $0AC7  [$00:0AC7]   ;Aを[$00:0AC7]に書き込み
$C0/4681 BRA $01    [$4684]      ;フラグにかかわりなく常に分岐[$4684]
;ゼロフラグOFF カウントダウン続行 
$C0/4683 RTS                     ;サブルーチン戻り

;$C0/4681ジャンプ先(カウントダウン終了処理)
$C0/4684 STZ $20    [$00:0020]   ;[$00:0020]に$00を書き込み
(中略)
$C0/476B RTS                     ;サブルーチン戻り

[$00:0AC7]に$81が入っている時のみ、[$00:0ACB][$00:0ACA]を-1して、

  • 0まで減少したら(ゼロフラグがONなら)、[$00:0AC7]を$01に戻しカウントダウン終了処理$C0/4679へ
  • 1以上なら(ゼロフラグがOFFなら)、カウントダウン継続の$C0/4683へ

と、分岐する処理になる。
カウントダウン処理自体はシンプル。
ややこしいのは、匂いを嗅ぐまでの分岐処理である。
カウントダウンの処理とは直接関係はないが、以下で解説しておく。

匂いを嗅ぐまでの分岐処理

ポゴがパーティにいて、原始編イベント後にYボタンを押すと匂いを嗅げるようになったタイミング以降でも、匂いが嗅げない時がある。
また、最終編では、本能のダンジョン関係の分岐もある。
それらの判定をざっと紹介する。

まず重要なのが[$00:0A0F](Yボタン判定フラグ)である。

Yボタン判定フラグ

最終編で本能のダンジョンまたは心のダンジョンに入ると、最終編開始時に初期値で$00が入っていた$00:0A0Fに$01が書き込まれ、以降は変更されない。

$C0/1C62 LDA #$01                ;Aに$01をロード
$C0/1C64 STA $000A0F[$00:0A0F]   ;Aを[$00:0A0F]に書き込み
$C0/1C68 BRL $FF36  [$1BA1]      ;フラグにかかわりなく常に分岐[$1BA1]

ちなみに、近未来編では開始時に同処理で[$00:0A0F]に$01が入るが、アキラ主人公で最終編を始めても[$00:0A0F]に$01は入らない。

本能のダンジョンと心のダンジョンでは、アキラがいるとYボタンで心が読める場面があるが、それ以外ではポゴの匂いを嗅ぐ方が優先される。
この優先順位判断用のフラグ値である。
ということで、便宜上、「Yボタン判定フラグ」という名称にしておく。

サブルーチン

フィールド上でYボタンを押した時の処理からざっと説明していく。

;ボタン判定
$C0/B83E LDA #$01                ;Aに$01をロード
$C0/B840 BIT $4212  [$00:4212]   ;Aと[$00:4212]で論理積(ステータスフラグ変更のみ)
$C0/B843 BNE $FB    [$B840]      ;ゼロフラグが立っていないとき[$B840]分岐
$C0/B845 REP #$20                ;Aを16bit幅に変更、Mフラグをクリア
$C0/B847 LDA $4218  [$00:4218]   ;Aに[$00:4219][$00:4218]をロード コントローラ1のボタン
$C0/B84A BIT #$000F              ;Aと$000Fで論理積(ステータスフラグ変更のみ)
$C0/B84D BEQ $03    [$B852]      ;ゼロフラグが立っているとき[$B852]分岐
;ゼロフラグON 何かのボタンが押された
$C0/B852 TAX                     ;Aの値をXレジスタに転送
$C0/B853 EOR $00    [$00:0000]   ;Aと[$00:0001][$00:0000]で排他的論理和XOR コントローラ1のボタン
$C0/B855 STX $00    [$00:0000]   ;Xを[$00:0001][$00:0000]に書き込み
$C0/B857 AND $00    [$00:0000]   ;Aと[$00:0001][$00:0000]で論理積
$C0/B859 STA $04    [$00:0004]   ;Aを[$00:0005][$00:0004]に書き込み コントローラ1のボタン
$C0/B85B STA $08    [$00:0008]   ;Aを[$00:0009][$00:0008]に書き込み コントローラ1のボタン
$C0/B85D LDA $421A  [$00:421A]   ;Aに[$00:421B][$00:421A]をロード コントローラ2判定
$C0/B860 BIT #$000F              ;Aと$000Fで論理積(ステータスフラグ変更のみ)
$C0/B863 BEQ $03    [$B868]      ;ゼロフラグが立っているとき[$B868]分岐
$C0/B868 TAX                     ;Aの値をXレジスタに転送
$C0/B869 EOR $02    [$00:0002]   ;Aと[$00:0003][$00:0002]で排他的論理和XOR
$C0/B86B STX $02    [$00:0002]   ;Xを[$00:0003][$00:0002]に書き込み
$C0/B86D AND $02    [$00:0002]   ;Aと[$00:0003][$00:0002]で論理積
$C0/B86F STA $06    [$00:0006]   ;Aを[$00:0007][$00:0006]に書き込み
$C0/B871 STA $0A    [$00:000A]   ;Aを[$00:000B][$00:000A]に書き込み
$C0/B873 SEP #$20                ;MフラグON Aレジスタは8bit幅
$C0/B875 LDA $01    [$00:0001]   ;Aに[$00:0001]をロード
$C0/B877 BIT #$08                ;Aと$08で論理積(ステータスフラグ変更のみ)
$C0/B879 BEQ $16    [$B891]      ;ゼロフラグが立っているとき[$B891]分岐
;ゼロフラグON 上キーではない
$C0/B891 LDA $01    [$00:0001]   ;Aに[$00:0001]をロード
$C0/B893 BIT #$04                ;Aと$04で論理積(ステータスフラグ変更のみ)
$C0/B895 BEQ $16    [$B8AD]      ;ゼロフラグが立っているとき[$B8AD]分岐
;ゼロフラグON 下キーではない
$C0/B8AD LDA $01    [$00:0001]   ;Aに[$00:0001]をロード
$C0/B8AF BIT #$02                ;Aと$02で論理積(ステータスフラグ変更のみ)
$C0/B8B1 BEQ $16    [$B8C9]      ;ゼロフラグが立っているとき[$B8C9]分岐
;ゼロフラグON 左キーではない
$C0/B8C9 LDA $01    [$00:0001]   ;Aに[$00:0001]をロード
$C0/B8CB BIT #$01                ;Aと$01で論理積(ステータスフラグ変更のみ)
$C0/B8CD BEQ $16    [$B8E5]      ;ゼロフラグが立っているとき[$B8E5]分岐
;ゼロフラグON 右キーではない
$C0/B8E5 RTS                     ;サブルーチン戻り

自動ジョイパッド読み取り機能($00:4200の下1ビット)がONの時に、[$00:4219][$00:4218]にコントローラ1のボタンの状態が出力されるので、もし何かしらのボタンが押されていたら$C0/B852~の処理へ進む。
Yボタンのみ押された場合、[$00:4219][$00:4218]に$4000が出力される。

$C0/B875~以降で十字ボタン(方向キー)が押されていないかの判定が行われ、Yボタンの場合は判定を抜けて$C0/B8E5でサブルーチンが終わり、次の処理に進む。

$C0/0C86 LDA $70    [$00:0070]   ;Aに[$00:0070]をロード
$C0/0C88 BNE $14    [$0C9E]      ;ゼロフラグが立っていないとき[$0C9E]分岐
;ゼロフラグON デモ画面ではない
$C0/0C8A LDA $0200  [$00:0200]   ;Aに[$00:0200]をロード
$C0/0C8D BEQ $0E    [$0C9D]      ;ゼロフラグが立っているとき[$0C9D]分岐
;ゼロフラグON キャラをフィールド上で操作可能
$C0/0C9D RTS                     ;サブルーチン戻り

$C0/0831 LDA $05    [$00:0005]   ;Aに[$00:0005]をロード
$C0/0833 BIT #$40                ;Aと$40で論理積(ステータスフラグ変更のみ)
$C0/0835 BEQ $03    [$083A]      ;ゼロフラグが立っているとき[$083A]分岐
;ゼロフラグOFF コントローラ1のYボタンが押されている
$C0/0837 JSR $135C  [$C0:135C]   ;[$C0:135C]へジャンプ

$C0/0C86~$C0/0C9Dはデモ画面やイベントシーンではないか(プレイヤーがフィールドで何かしらの操作をできる状態であるかどうか)の判定。
$C0/0831~から、十字ボタン以外のボタン判定になる。
最初に[$00:4219]のコピー先である[$00:0005]と$40(%0100 0000)の論理積を取ってゼロフラグ判定だが、Yボタンを押すと[$00:0005]のBit6に1が立つから、ここで必ずゼロフラグが立たず、$C0/0837に進んで$C0/135Cにジャンプする。

つまり、十字ボタンの次に、Yボタンの判定が優先になり、もしYボタンとXやAボタンなどを同時押しした場合、Yボタンの処理をまず行う、ということになる。
プレイして試してもらえればわかるが、Yボタンで何らかのコマンドが発動する時(匂いを嗅ぐ以外、心を読む、隠れ蓑使用も含む)、同時にXボタンを押してみると、メニュー画面は開かず、Yボタンのコマンドが実行されるはずである。

それはさておき、次の$C0/135C~がフィールドでYボタンを押した時の処理である。
ポゴやアキラなどYボタンでコマンドを発動できるキャラがいるかどうかの判定は最後の方。
パーティメンバーが誰であっても、Yボタンを押すと以下の判定が必ず行われる。

;Yボタンを押した時の判定
$C0/135C LDA $0A0F  [$00:0A0F]   ;Aに[$00:0A0F]をロード Yボタン判定フラグ
$C0/135F BEQ $05    [$1366]      ;ゼロフラグが立っているとき[$1366]分岐
;ゼロフラグOFF 近未来編または最終編でYボタン判定フラグが立っている
$C0/1361 STA $0201  [$00:0201]   ;Aを[$00:0201]に書き込み
$C0/1364 BRA $52    [$13B8]      ;フラグにかかわりなく常に分岐[$13B8]
;ゼロフラグON 原始編 または最終編でYボタン判定フラグOFF または幕末編隠れ蓑
$C0/1366 LDA $0AC7  [$00:0AC7]   ;Aに[$00:0AC7]をロード
$C0/1369 BEQ $49    [$13B4]      ;ゼロフラグが立っているとき[$13B4]分岐
;($C0/13B4 RTS                     ;サブルーチン戻り)
;ゼロフラグOFF ポゴが匂いを嗅ぐ能力を所持している([$00:0AC7]が$01)
$C0/136B LDA $0A00  [$00:0A00]   ;Aに[$00:0A00]をロード シナリオID
$C0/136E CMP #$08                ;Aと$08を減算比較(ステータスレジスタ変更のみ)
$C0/1370 BEQ $07    [$1379]      ;ゼロフラグが立っているとき[$1379]分岐
;ゼロフラグOFF 原始編
$C0/1372 LDA #$01                ;Aに$01をロード
$C0/1374 STA $01B8  [$00:01B8]   ;Aを[$00:01B8]に書き込み
$C0/1377 BRA $23    [$139C]      ;フラグにかかわりなく常に分岐[$139C]
;ゼロフラグON 最終編でYボタン判定フラグOFF
$C0/1379 LDA $0A14  [$00:0A14]   ;Aに[$00:0A14]をロード パーティの人数
$C0/137C INC A                   ;Aをインクリメント +1
$C0/137D STA $30    [$00:0030]   ;Aを[$00:0030]に書き込み
$C0/137F LDA #$01                ;Aに$01をロード
$C0/1381 STA $31    [$00:0031]   ;Aを[$00:0031]に書き込み
$C0/1383 LDX #$0A10              ;Xに$0A10をロード
$C0/1386 LDA #$01                ;Aに$01をロード
;ループ処理(ポゴがいるか)
$C0/1388 CMP $00,x  [$00:0A10]   ;Aと[$00:0A10]~[$00:0A13]を減算比較(ステータスレジスタ変更のみ)
$C0/138A BEQ $09    [$1395]      ;ゼロフラグが立っているとき[$1395]分岐
$C0/138C INX                     ;Xをインクリメント +1
$C0/138D INC $31    [$00:0031]   ;[$00:0031]をインクリメント +1
$C0/138F DEC $30    [$00:0030]   ;[$00:0030]をデクリメント -1
$C0/1391 BNE $F5    [$1388]      ;ゼロフラグが立っていないとき[$1388]分岐
;ループ処理ここまで
$C0/1393 BRA $1F    [$13B4]      ;フラグにかかわりなく常に分岐[$13B4]
;処理合流
$C0/1395 LDA $31    [$00:0031]   ;Aに[$00:0031]をロード
$C0/1397 STA $01B8  [$00:01B8]   ;Aを[$00:01B8]に書き込み
$C0/139A LDA #$F1                ;Aに$F1をロード
$C0/139C JSR $1B74  [$C0:1B74]   ;[$C0:1B74]へジャンプ

分岐が何度もあるが、整理してみると、以下のようになる。
ややこしくなるので、ここでは匂いを嗅ぐ関係の分岐中心で記す。

  • 近未来編または最終編でYボタン判定フラグが立っている([$00:0A0F]が$01)
    →近未来編なら、マップの座標から対象がいるか判定し、アキラの「心を読む」
    →最終編なら、マップの座標とパーティメンバーから、アキラの「心を読む」かポゴの「匂いを嗅ぐ」のいずれかの処理へ
  • Yボタン判定フラグなし、かつ、ポゴが匂いを嗅ぐ能力を所持している([$00:0AC7]が$01)、かつ原始編
    →匂いを嗅ぐ処理へ
  • Yボタン判定フラグなし、かつ、最終編でYボタン判定フラグOFF
    →ポゴがパーティにいるか判定して、ポゴがいるなら匂いを嗅ぐ処理へ

というような感じになる。
最終編においてポゴがいるかどうかの判定は、$C0/1379~$C0/1393で行っている。
ループ処理を見るとわかるが、Aに$01をロードし、[$00:0A10]~[$00:0A13]と減算比較している。
$00:0A10~$00:0A13は最終編における現在のパーティメンバーのキャラIDであり、$01はポゴだから、「ポゴがパーティ内にいるかどうか」をループ処理で判定している。
ポゴがいなければ$C0/1393→$C0/13B4と抜けて、何もしない。
ポゴがいる場合は、$C0/138A→$C0/1395とループ処理を抜けて、匂いを嗅ぐ処理へ進むことになる。

最終編でYボタン判定フラグが立っている時が一番ややこしく、マップと座標から「心を読む」か「匂いを嗅ぐ」が発動可能と判定した場合、上の$C0/1364から$C0/13B8へジャンプする。
マップと座標の計算については端折るが、最終的に、匂いが嗅げる場合は$C0/1366へ処理が戻り、後の処理は共通となる。



このページをシェアする

上へ