真・女神転生Ⅰ オープニングの秘密

最近、真・女神転生5やソウルハッカーズ2、さらに真3のリマスター版も出て、すっかりメガテン気分になっていたところで、ふと「真・女神転生」(初代)のオープニングを思い出しました。

真っ黒な画面に、なにやらプログラムのコードらしきものが打ち出されていくのが印象的でしたが、実は、この画面に小さな秘密が隠されています。

なんと、打ち出されていくプログラムコードは、「真・女神転生」そのものの起動コードなのです。

まずはOP中の、テキストが打ち出された画面をご覧ください。(この下にヘブライ語らしきテキストが続きますが、プログラムとは関係ないので割愛)

これは、1行1個ずつ配置された2〜4文字の「単語」(SEICLCなど)から、アセンブリ言語で書かれていることが分かります。(「アセンブリ言語」とは、パソコンやゲーム機を動かすCPU(中央演算処理装置)が行うべき処理を直接指示するプログラム言語で、最も「機械に近い」言語として知られています。各行の最初の単語が「命令」と呼ばれ、特定の処理を指示しますが、その処理とは、「この2つの数値を足す」「この値をメモリのこの場所に書き込む」などと非常に細かいので、単純なプログラムでも、アセンブリ言語で書くとテキスト量が膨大になってしまいます。このため現代では、1行のテキストで多くの処理を表現できる「高水準言語」を使うのが一般的ですが、その代償として、より多くのメモリ量や実行時間が必要になります。ファミコンやスーファミの時代には、メモリや処理能力の制約が厳しかったので、アセンブリ言語を使わざるを得ない場面が多くありました。)

アセンブリ言語のいわゆる「方言」は、コンピュータ(CPU)の数だけ存在しますが、最初の「SEI」「CLC」「XCE」から、これはスーパーファミコンで使われたCPU「65C816」用の言語であることも分かります。(各CPU系統には、アセンブリ命令の固有な命名法則があって、熟練のプログラマーなら、アセンブリで書かれたプログラムを一目見れば、どのCPU用に書かれたのか分かる……としか説明できませんが、まあそんなものです。)

スーファミ用のプログラムコードであるなら、「じゃあメガテンそのもののプログラムでは?」とすぐ疑いました。「メガテン」のカセットからプログラムデータを読み出せば調査できますが、その容量(1.5メガバイト)に対して、画面に表示されているコードはたったの24バイトに相当するものです。どこから探せばいいのか?

ヒントは、最初の「RESET:」でした。行末のコロン(:)は「ラベル」、つまり「ある一連の処理が始まる場所」であることを示しています。そしてラベル名の「リセット」は、ゲームを「リセット」する、つまり電源投入やリセットボタンを押したときに最初に行う処理と推測されます。

ファミコンやスーファミには、パソコンや現代ゲーム機のようなOS(システムソフトウェア)がないため、電源が投入された時にどの処理からプログラムを実行すればいいのか、カセット自体に記録されています。ということは、その記録されている場所を見れば、リセット時の処理が見つかります。メガテンの場合、偶然にもカセットのメモリの一番先頭に書かれています。(じゃあ最初から見てみればよかった、といえばそれまでですが、リセット処理が必ず先頭から始まるというわけではなく、むしろメモリの最後の方に来ることが多いので、理論通りに行きました。)

さて、実際のプログラムコードは以下の通りです(解説用に色分けしてあります)。

8000: 78        sei
8001: 18        clc
8002: FB        xce
8003: D8        cld
8004: C210      rep #$10
8006: E220      sep #$20
8008: A2FF1F    ldx #$1FFF
800B: 9A        txs
800C: A900      lda #$00
800E: 8D0042    sta $4200
8011: A980      lda #$80
8013: 8D0021    sta $2100
8016: A900      lda #$00
8018: 8FFEFF7E  sta $7EFFFE
801C: 8FFFFF7E  sta $7EFFFF
8020: A20000    ldx #$0000
8023: BF738700  lda $008773,X
8027: DFE0FF7E  cmp $7EFFE0,X
802B: D008      bne $8035

上記は、「ディスアセンブラ」または「逆アセンブラ」という、プログラムを構成する命令をアセンブリ言語の形で書き出す道具を用いた結果です。左側にはメモリアドレス(番地)とプログラムデータそのもの、右側には対応するアセンブリ言語の命令が書かれています。(ディスアセンブラは、こういう研究目的のほか、ソフトウェア開発時のデバッグにもよく使われます。開発中のソフトに不具合が発生して、ディスアセンブラで調査したら、自分が書いたプログラムが正しいのに開発ツールの方にバグがあった、という迷惑な経験もあります…。)

同じアセンブリ言語でも、使うツールによって書き方が微妙に変わりますが、今回使ったディスアセンブラ(フリーソフトウェアである「GNU binutils」のものを使っています)は、命令を小文字で出力しています。しかしその点を除けば、最初の4命令、「SEI」から「CLD」までは、見事に一致しています。

OPでは、これに「X16」と「M8」が続きますが、ディスアセンブリ(つまりディスアセンブラの出力)の方では、「rep #$10」、「sep #$20」となっています。しかし一致がここで崩れたのか、と考えるのは早計です。アセンブリ言語には「マクロ」というものがあり、特定の命令に好きな名前を割り振ることができます。そしてこの「X16」と「M8」は、65C816用の命令としては存在しないので、マクロである可能性が高いのです。

細かいことは省くとして、スーファミのCPUである65C816は、8ビットの値と16ビットの値の両方を操作でき、当時「16ビット値を操作できる」という機能が新しかったので「16ビットのゲーム機」という謳い文句で売り出されました。ただ内部的には、8ビットと16ビットを同時に扱うことはできず、どちらを使うのか、プログラム側で指定する必要がありました。この設定は2系統あって、片方を16ビット、もう一方を8ビットにするのが一般的でしたが、この「X16」と「M8」は、おそらく「X系統を16ビットにする」「M系統を8ビットにする」という意味で名付けられたのではないか、と思います。そしてそれに対応する「rep #$10」と「sep #$20」は、ちょうど同じ意味を持つ命令なのです。ということは、この部分も実際のプログラムと一致している、ということになります。ついでにこの後、OPと同じ「ldx #$1FFF」「txs」が続きます。(「$1FFF」と「1FFFH」は、ともに16進数の値を示すもので、大文字・小文字と同様にツールによって書き方が変わります。)

続いてOPでは「STZ」という命令が続きます。ディスアセンブリの方ではやはり「stz」が見当たらず、逆に「lda」「sta」と、2つの命令が続きますが、こちらもマクロの可能性が考えられます。ただ、先ほどの「X16」「M8」とは若干、状況が異なります。

65C816では、実は「STZ」という命令は存在するので、正確には、OPと実コードが違うということになります。ただこのSTZは、65C816の前世代CPUで、ファミコンでも使われた「6502」というCPUでは存在しなかった命令です。そのため、6502でアセンブリを覚えたプログラマーが、利便性を考えて、STZと同じ働きをする2つの命令(LDASTA)を一つのマクロして組んだとしても不思議はありません。

ちなみに同じ命令で、OPには「NMITIME」という単語がありますが、これはスーパーファミコンのハードウェアレジスタの名前です。ハードウェアレジスタとは、特定のメモリアドレスにアクセスすることでハードウェア(ゲーム機)の動きを制御したり状態を調べたりするもので、この「NMITIME」というレジスタのアドレスは、ちょうど「$4200」なので、OPの「STZ」とディスアセンブリの「sta」が対応していることが確認できます。

続く「LDA #BLANKING」「STA INIDSP」も、レジスタ(INIDSP)とそこに書き込む値(BLANKING)に名前が付けられているものの、ディスアセンブリに完全一致します。もちろん値やアドレスを直接書き込んでもいいですが、アセンブリ言語はただでさえ読みづらいので、その読みづらさをなるべく緩和するために、このようによく使う値に名前を付けて、何のための値やアドレスなのか一目で分かるようにしよう、というのがアセンブリプログラマーの心構えです。

このように、ゲームを起動するためにはまずゲーム機の基本制御から始めなければなりませんが、スーファミでも相当数のレジスタがあり、その一つを間違えただけでもゲームが起動しなかったり、途中でフリーズしたりします。現代のゲーム機やパソコンではその数が膨大になり、操作も格段に複雑になっているので、あえてアセンブリ言語で一つ一つ操作しようとする人は、まずいないでしょう。

最後の「BJSR ATLUS」だけ、該当するものが見当たりません。しかしこれも、特に驚きはしません。この命令は、「ATLUSというラベルに書かれている処理を実行してから、次の命令に移る」という意味を持っていますが、ラベル名からは、電源投入時に表示される、アトラス社のロゴ画面を描画する処理と見当を付けられます。そしてその描画を行う前に、様々な準備が必要ですが、それを全部表示していては、OPがいつまで経っても終わりません。かといって、「STA INIDSP」で終わると、プログラマーでない人にとっては完全に意味不明なので、この「BJSR ATLUS」までの命令を飛ばして、最後にアトラスの名を出すことで「ここから雰囲気が変わりますよ」とプレイヤーに伝えているのではないかと思います。

以上、うんちくでした。


アンドリュー・チャーチ - achurch@achurch.org
2024/2/5更新