5.3. メッセージ

5.3.1. 解析
5.3.1.1. メッセージ復号
5.3.1.2. 表示サブルーチン

人々・動物・魔物の台詞やナレーション等の 「大きいフォントを用いて表示するテキスト」 (以下、単にメッセージと呼ぶ)全般の解析および結果について述べる。

5.3.1. 解析

記者は SFC 版ドラクエ 3 のメッセージ解析手法の大部分を人に教わった (それを元に SFC 版ドラクエ 5 および SFC 版ドラクエ 6 のメッセージ抽出を実現した)。 その内容は、メッセージ表示を実装している 65816 コードの解説という形でどこかに存在する。 よって、ここには解説を書かず(本物は一つでいい)、 要所要所のメモだけを残すにとどめる。

もっとも、SFC 版ドラクエ 3 のメッセージ周辺の実装は、 まったくといっていいほどSFC 版ドラクエ 6 のそれ と同じであるため、 定数(データ格納アドレスやキャラクターセット)の違いだけ読んでいくといいかもしれない。

5.3.1.1. メッセージ復号

メッセージデータは圧縮されており、これを復号しながらテキストを表示するという方式である。 これは SFC 版ドラクエ 6 の実装と同じである。 以下、要点のみを箇条書きするに留める。

  • メッセージ ID は 2 バイトで表現する。

  • プログラムはメッセージ表示直前に、その ID を $7EBDDF に格納しておく。 PAR 改造でこのアドレスの値を書き換えることが有効である。

  • メッセージ表示サブルーチンは複数存在し、 なんらかの役割に基づいてそれらを使い分けている。

  • サブルーチン $C1AFB9 はメッセージ ID から圧縮メッセージデータの格納アドレスを求める (SFC 版ドラクエ 6 のサブルーチン $C02B69 に相当)。

  • サブルーチン $C1B024 は圧縮メッセージデータを展開する (SFC 版ドラクエ 6 のサブルーチン $C02BD4 に相当)。

5.3.1.2. 表示サブルーチン

メッセージを画面下部のウィンドウ(もしくは画面中央)に出力するために呼び出すサブルーチンは複数存在する。 そのいずれもが、上述の復号サブルーチンを呼び出していると推理できるため、 すべてのメッセージ表示(描画)サブルーチンのアドレスを特定できる。

表示サブルーチン方法は、メッセージ ID の受け取り方で二系統に分類できる。 ひとつはメッセージ ID をサブルーチンの「引数」として扱うものである。 もうひとつは、アキュームレータに格納されている値を サブルーチン側でメッセージ ID として解釈するものである。

5.3.1.2.1. 呼び出しコードの直後 2 バイトをメッセージ ID とするサブルーチン

現在までに発見したものは、以下ですべてである。 ただし、これらのサブルーチンのうち、いくつかの呼び出しを薄く「ラップ」しただけのサブルーチンも存在する。 それらは言わば二軍のメッセージ表示サブルーチンだが、解析作業の点からは一軍と同様に重要だ。

JSL $C1A8D4  システムからのメッセージに利用することが多い
JSL $C1A8EA  $C737BE 呼び出し経由で呼び出し
JSL $C1A92E  店屋のメッセージに利用することが多い
JSL $C1A944  ちょっとしたイベント用か
JSL $C1A988  メッセージを中央寄せで表示する
JSL $C1A9D3  ゾーマ専用

一例を挙げてみよう。 これはたった今、記者の手許にある逆アセンブリコードから、 サブルーチン $C1A8D4 呼び出しを含むコード塊を切り取ってきたものだ。 このように、メッセージ ID とテキストの対照表さえあれば、 サブルーチン $C1A8D4 呼び出しの周辺を丁寧に解読することにより、 これが「冒険の書を消す手続き」の一部抜粋だということが容易に特定できる。

C3/BEA4:    8578        STA $78
C3/BEA6:    9C82BE      STZ $BE82
C3/BEA9:    1A          INC A
C3/BEAA:    8D81BE      STA $BE81
C3/BEAD:    22D4A8C1    JSR $C1A8D4         1 ぼうけんのしょ[BB]を[AD]消してもいいですか?[AC]
C3/BEB1:    2701
C3/BEB3:    225122C3    JSR $C32251         2 「はい・いいえ」
C3/BEB7:    4C
C3/BEB8:    22C622C3    JSR $C322C6
C3/BEBC:    4C
C3/BEBD:    B0DB        BCS $BE9A
C3/BEBF:    C90000      CMP #$0000
C3/BEC2:    D0D6        BNE $BE9A
C3/BEC4:    22AC63C4    JSR $C463AC         3
C3/BEC8:    78
C3/BEC9:    22D4A8C1    JSR $C1A8D4         4 ぼうけんのしょ[BB]を[AD]消しました。[AC]
C3/BECD:    2801
C3/BECF:    22F740C3    JSR $C340F7
C3/BED3:    4C91BD      JMP $BD91

1 4

JSR $C1A8D4 によりメッセージを表示する。 メッセージの ID を次の 2 バイト値埋め込みにより指定していることに注目する事。 #$0127, #$0128 はともにメッセージ ID だ。

2

JSR $C32251 はウィンドウの表示を行う。 ウィンドウの ID は後続の埋め込み 1 バイト値で指定する。 これは処理の文脈上そう判断しただけだが、 今調べたいことはウィンドウではないので、これで構わない。

#$4C を指定する意味は、「はい・いいえ」ウィンドウを表示するためだということが容易く想像できる。

3

前後のメッセージ内容や、プレイヤーの応答分岐から推理すれば、 サブルーチン $C463AC で冒険の書を削除するに違いない。

5.3.1.2.2. アキュームレータにメッセージ ID をセットしてから呼び出すサブルーチン

表示するメッセージが動的に決定する処理においては、こちらの方式でメッセージ表示を実装している。 特に、配列の形でメッセージ ID をプログラムに埋め込んでおき、 以下に示すいずれかのメッセージ表示サブルーチン呼び出しの直前で、 その配列から A に ID 値をロードするというのが定番の実装になっている。

JSL $C1A900  詳細不明
JSL $C1A917  $C737CB 呼び出し経由で呼び出し
JSL $C1A95A  店屋のメッセージに利用することが多い
JSL $C1A971  $C737B1 呼び出し経由で呼び出し
JSL $C1AA21  詳細不明
JSL $C1AA6B  詳細不明
JSL $C1AAB2  詳細不明

先程と同様にして、手許にある逆アセンブリ済みコード片から拾ったJSL $C1A87A の呼び出し例を記す。 どうやらコマンドメニューの「どうぐ→わたす」辺りだろう。

C2/8265:    0A          ASL A
C2/8266:    AA          TAX
C2/8267:    BF7082C2    LDA $C28270,X       1
C2/826B:    227AA8C1    JSR $C1A87A         2
C2/826F:    60          RTS

C2/8270:    B100        [C1]は [C3]から[AD][B5]を だした。[AC]
C2/8272:    B500        [CB]は [C3]の[AD][B5]を いれかえた。[AC]
C2/8274:    B600        [C3]の [B5]と[AD][C1]の [C7]を[AD]交換した。[AC]
C2/8276:    B700        [CB]は [C3]の[AD][B5]と [C1]の[AD][C7]を 交換した。[AC]
C2/8278:    B800        装備しますか?[AC]
C2/827A:    B900        [C0]は 道具を[AD]持っていない。[AC]
C2/827C:    BA00        [C0]は [B5]を[AD]道具として 使ってみた。[AF][AC]
C2/827E:    BB00        [C0]は [B5]を[AD]道具として 使ってみた。[AC]
C2/8280:    BC00        [C0]は[AD][B5]を 使った![AF][AC]

1

LDA $C28270 でメッセージ ID を A にロードする。 配列 $C28270 は 2 バイト値の配列ゆえ、LDA long,X 命令の直前に ASLTAX をセットで用いていることに着目する事。

2

A がメッセージ ID を示すという事前条件の下、 JSR $C1A87A は メッセージの表示を処理する。

SFC 版ドラクエ 3 の解読済みメッセージリストは付録 付録 B データ を参照。