敵キャラクタについて(2):敵キャラクタの種類の決定
前回、ZANACを改造し、敵キャラクタワークエリアの利用状況をリアルタイムに見られるようになりました。
ここから、さらに敵キャラクタについての解析を進めてゆきます。


敵キャラクタ種別による分岐
敵キャラクタワーク0E3A0H〜0E65FHにおいて、この領域を32バイトで区切ったときの各々の先頭1バイト目は「敵キャラクタ種別」を表しているようです。 まずはこのことを解析の足がかりにしてゆきます。

プログラム中、ワークの先頭を取り出して参照しているのはどこでしょうか?
これはエミュレータのトレース機能を用いると探しやすいかもしれません。

ゲーム本編で初めのほうに出てくる空中敵(ランダーの次ぐらい?)、名前は判らないのですが緑色の敵が出現しますね。
このキャラクターの種別(ワーク1バイト目)は"AC"です。
トレースを取りながらゲームを開始し、緑色の敵が出現したらすかさずF6キーを押してください!(ZANACのポーズキーです)

ワークの何行目かに"AC"が表示されていますね。その位置からワークアドレスを逆算します。
3行目であれば、ワークアドレスは0E3E0Hとなります(0行目は0E3A0H、そこから一行下に行くごとに32バイト=020Hずつ増加させます)。

取得したトレースログの中には、アドレス0E3E0Hの内容を取り出し、そのときの内容が0ACHである部分が必ずあるはずです。

まあ、トレースログも巨大となって面倒だと思いますし、結論から言いますが・・・
ZANACでは0445FH近辺のルーチンがそれに当たります。

このルーチンでは、各ワークの先頭を取り出した後、それが0でなければ以下のアドレス(0だった場合、次のワークの先頭を見に行きます)

 070B7H+(敵ワーク先頭の内容を2倍した値&0FFH)

からの2バイトのデータを飛び先アドレスとしてジャンプしています。
これぞまさに「敵キャラクタ種別により分岐を行う」ルーチンです!
そして、070B7H(※注)からのテーブルが、各敵キャラクタの処理へと飛ぶアドレスとなります。

例えば、敵ワーク先頭が"AC"であれば、

 070B7H+((0ACH*2)&0FFH)=0710FH

となるので、0710FHからの2バイトが、緑色の敵の処理の開始アドレスということになります。


(※注)本当は間違いで、070B9Hからが敵処理ルーチンのアドレステーブルです。
というのも、

・このテーブルの直前、06976Hから070B8Hには、スプライトパターンと思われるデータが格納されているから。
・この分岐ルーチンでは、敵ワーク先頭データ=0のときはジャンプしないで次のワークを参照するから。
 (データ=0のとき、070B7H,070B8Hのデータを参照することは無い)


分岐ルーチン内では、ワーク先頭の敵種別を表すデータと0FFHのAND(&)演算を行っています。
これは「0FFHでマスクした」とも表現します。
16進数で考えると、この2倍して0FFHでマスク、という計算により、02CHと0ACHの最上位ビットの違いは関係なくなります。
すなわち、02CHは0ACHと同じ敵キャラクタ種別を表している、ということになります。


例)
(16進数で2Cは、2進数では101100となる。それぞれ02CH,000101100Bと表す)

【16進数】02CH の2倍 = 058H・・・(1)
【2進数】000101100B の2倍 = 001011000B・・・(2)

【16進数】0ACH の2倍 = 0158H・・・(3)
【2進数】010101100B の2倍 = 0101011000B・・・(4)

(1)と(2)を0FFHでマスクすると等しくなる。
 0158H & 0FFH = 058H

(3)と(4)も同様、0FFH=011111111Bでマスクすると等しくなる。
「0FFHでマスクする」とは、末尾の8ビットのみを取り出すことである。


実際に、敵キャラクタ種別02CH/0ACHの時の分岐の飛び先のルーチンを調べてみると、

ワーク先頭データの最上位ビットが0のとき・・・
 初期化処理(出現座標を決めたり、移動角度を設定)をした後、最上位ビットを1にする。

ワーク先頭データの最上位ビットが1のとき・・・
 現在の座標から、決められた移動角度に向かってキャラクタを移動??

という処理が行われています(いやー、たまたま単純な動きをする敵キャラクタで助かりました)。

つまり、最上位ビットは、「敵ワークをキャラクタ種別ごとに初期化したかどうかのフラグ」と考えて良さそうです。
初期化が済んだ敵キャラクタは、当たり判定も発生し、ここで初めて敵キャラクタとして画面に登場するのです。


敵キャラクタ種類を決定するルーチン
各敵キャラクタの種類により、処理を分岐させる位置は特定できました、が、 そもそもゲームに出現する敵キャラクタの種類はどのように決められているのでしょうか?
それを調査するため、先ほど取得したトレースログのうち、0E3E0Hへデータの書き込みを行っているところをざっと探してみます。

0E3E0Hの値の変化を追ってみると、初期状態の0→040H→02CH→0ACHと変化しているようです。

最後の2つ、02CHと0ACHについては、ちょうど最上位ビットが0と1の違いですが、 これは先ほど説明したとおり、敵種別は既に決定された後、未初期化/初期化済を示す違いなので無視してよいでしょう。

2番目の040Hについては・・・すみません、まだそこまで解析が進んでいません。
多分、「ワーク先頭が0でなくて040Hだったら、ここに敵キャラクタを追加していって下さい」のような指示を出すルーチンが別にあり、 その指示のために使われているのではないでしょうか?

ここで知りたいのが040H→02CHへと値を変化させているルーチンです。
トレースログによると、それは08292Hの

 8292: LD  (IX+0D),A

近辺のようです。もう少しさかのぼると、08279Hからがこのルーチンの始まりであることが判ります。
これが「敵キャラクタ種類を決定するルーチン」です。


敵キャラクタの種類の決め方 〜ALCとRレジスタが関与〜
ついに敵キャラクタ種類を決定するルーチンが特定できましたので、これを解析してゆきます。

ルーチンの冒頭、メモリの0E130Hの内容を参照している部分があります。
この0E130H、実は画面に表示されている「ALC」の末尾2ケタそのものです。

このページの読者には説明の必要は無いのかもしれませんが・・・
ALCとは"Auto Level Contoroller"(オートレベルコントローラ)の 頭文字で、ZANACのゲーム難易度をプレイヤーの腕前に合わせて変える機能です。 昨今のシューティングゲームでは既に一般的な機能ですが、 ZANACにおいては、タイトル画面の真ん中に「A.I.」"Artificial Intelligence"(人工知能)と表記してしまうほど、根幹をなす機能だったといっても良いでしょう。

ZANACの解析を進めて数年、ついにALCキター!という感じです。

ルーチンの解析に戻ると、大筋で言ってこのルーチンは、「ALC」「Rレジスタ」「0BECCHからのテーブル」を基に敵キャラクタ種別を決定する、という動作をしています。
Rレジスタとはリフレッシュレジスタというもので、ZANACでは乱数を求める際に使用しています。

テーブルを参照するアドレスは、以下の式で決定されます。


 0BECCH + [Rレジスタの内容 AND 3] + [ALC末尾2ケタを右に1ビットシフトした内容]
  (ただし、上記計算結果が060H以上だったら05FHに補正)


この式について説明すると・・・


 (1)敵キャラクタ選択時、まずALCの末尾2ケタの値を2で割る。

 (2)0〜3までの数字を乱数で適当に決める。

 (3)先に求めた(1)(2)の値を足し合わせてこれをXとする。Xが96(=060H)以上なら、X=95(=05FH)とする。

 (4)アドレス0BECCHから数えてX番目のデータを敵キャラ種別とする。
  なお、X=95のとき、0BECCHから数えてX番目のデータは043Hとなる。


実際のゲームで考えると、ゲーム開始直後はALCは全ケタが0なので、末尾2ケタも0となります。
すると(4)では、Xは0〜3の値をとることになります。ちなみに0BECCH近辺のデータは

 BECC: 40 2C 2C 2C

となっています。

敵種別が040Hとなる件については前にも少し触れましたが、種別が040Hの敵キャラクタは実際には存在しません。
また、一見、ワークを表示させながらゲームをしても、1バイト目が040Hとなることは無いようです。
この番号は欠番で、040Hがでたらもう一度敵種別決定をやり直すのかもしれません。

さて、040Hを除けば、残りの3つは02CH、これはゲーム初めに登場する緑色の敵キャラクタです。
つまり、ALC末尾が0の時には、緑色の敵キャラクタ(種別02CH)以外が選ばれることはありえないのです!

このことを利用すると、敵キャラクタ種別データと敵キャラクタがどのように対応するかを楽に調べることができます。
ZANACのROMファイルをバイナリエディタで開き、+0007ECCからの"40 2C 2C 2C"のうち、3つの"2C"を、適当な数値に変えてやればよいのです。
その後ゲームを開始すれば、出現する敵キャラクタはその中からしか選ばれないので、種別データを特定できることになります。

ちなみに、敵キャラクタ種別が043Hの敵は、突然テレポートして襲い掛かってくる強敵(バルキリー?)です。
ゲーム難易度が高くなるとバルキリーがバンバン出てくるのはこのためのようです。


今回のレポートでは、ZANACの敵キャラクタがどのように決定されるかを調査しました。
ゲーム中、完全にランダムではなく、しかも完全に決められたパターンでもなく敵キャラクタを出すのに、このような工夫が行われていたのですね。
また、ゲームにALCがどのように関わっているかについても、少しだけ理解することができました。
(2005/08/08)


ZANAC解析のトップへ戻る

トップへ戻る