基礎知識
戦闘関係
スーパーファミコンにおけるグラフィックは、背景(BG)とキャラのドット絵(スプライト)に分かれる。
背景はそのまま、マップの画像であり、透過部分も設定可能。セリフ表示のウィンドウなどは一番上に表示させるなど、最大4枚まで重ねることができる。
その背景の上に載せて動かすことができる小さめサイズの画像がスプライトである。
RPGであればキャラクターのドット絵に使われることでおなじみである。
他には、マップ背景であっても扉など小さめで動かす画像もスプライトを利用している。
戦闘ならダメージ量を表示する数字や技エフェクト、コマンドカーソルなどもスプライトである。
スプライトは解説サイトだとOBJと表記されることも多い。
スプライトや背景は表示するだけではなく、動かしたり、スーパーファミコンで行える各種処理で回転させたり、モザイクをかけたり、色味を変更することも可能である。
そういった各種処理を解説していくが、筆者は専門家でも何でもないので、説明に間違いが含まれているかもしれないし、用語の使い方もおかしいかもしれないが、ご了承いただきたい。
ネットを探しても、ある程度噛み砕いた説明はほとんど英語で、日本語での説明はあまりなく(あるかもしれないが筆者が見つけられたのはごく少ない)、ここでは「ライブ・ア・ライブ」を元にして、スーパーファミコンの仕様を説明してみる。
まず基本として以下をお読みいただきたい。
スーパーファミコンの仕様では、背景を最大4枚、スプライトを最大128体設置できる。
これら画像データは、
というような順序で送信し、処理をしている、らしい。
グラフィックデータ用のメモリが3種類もあるが、
となっている。
上の3種類のメモリは、スーパーファミコン本体のメモリ領域にアドレスが割り振られておらず、I/Oポート用のアドレスを通さないとデータのやりとりができない。
グラフィックのデータは量が多い上、1フレームに1回更新していかなければならないので、スーパーファミコンのCPUを通さずにデータの読み書きをする、DMA(Direct Memory Access)転送という高速の転送方法を使うのが一般的(らしい)。
通常のプログラムだと、メモリのアドレスを指定してA(アキュムレータ)レジスタなどに値をロードし、それを別のメモリアドレスに書き込んでコピー、というような方法で値を移すが、これでは画像データの処理には間に合わないのである。
スーパーファミコンの画面は、横256ピクセル×縦224ピクセルサイズである。
この画面を、1フレーム、つまり1/60秒に1回更新する。つまり描き直している。
例え画面が一切変化しない状況でも(例えばメニュー画面を出したままなどでも)更新は行っている。同時にプレイヤーのコントローラ入力待ちなども行っているので、ルーチンとして1フレームに1回処理をし続けているのである。
画面を描く時は、画面左上から順に、1ピクセルの幅の線を右へ横一直線に256ピクセル描き、書き終わったら2行目の線を左から右へ256ピクセル描き……を繰り返し、最後に224行目の線を256ピクセル描いて終了となる。
この横1行分をスキャンラインとか走査線とか、色々な呼び方をする。
以下でもスキャンラインとしておく。
スキャンラインを1行描いてから、次の行を描くまで、ほんの僅かだが空き時間がある。
この空き時間をHBlank(Horizontal blanking interval, 水平帰線区間)という。
(HBlankの時間は12,664ナノ秒らしい。短すぎて筆者にもよくわからない)
また、224行目のスキャンラインを描いて画面の描画が終了してから、次の画面描画までの空き時間はVBlank(Vertical blanking interval, 垂直帰線区間)という。
なお、HBlankやVBlankはスーパーファミコン特有の仕様ではなく、かつてスーパーファミコンを接続するために主に使用していたアナログテレビの描画方法に合わせた仕様である。
垂直帰線区間 - Wikipedia
アナログテレビの描画の仕様は日本などのテレビで採用していたNTSC(NTSC - Wikipedia)と、ヨーロッパなどで採用されたPAL(PAL - Wikipedia)に大きく分かれており、スキャンライン数の違いなどから、日本のスーパーファミコンをPAL方式のアナログテレビに接続すると色々と不具合があるらしいのだが、筆者にとって門外漢の話であるし、ここではそこまで踏み込まない。詳細は調べていただきたい。
話を元に戻すが、HBlankやVBlankではない時間、つまり画面描画中に、VRAMなど画像関係のメモリにデータを送ると、画面描画が乱れる(らしい)。
このため、画像データをDMA(Direct Memory Access)転送でVRAMなどに送る場合、HBlankとVBlankのどちらかのタイミングで送る必要がある。
また、描画中は描画とは関係のない計算処理を行うことになる(たとえば、戦闘なら技のダメージ量計算や、乱数計算、状態異常の発生判定、などなど……)。
スーパーファミコンの話は書かれていないが、一般的なデータ転送手段としてのDMAについてはWikipediaにもある。
Direct Memory Access(DMA) - Wikipedia
DMA転送は、GDMA(General Direct Memory Access)転送とHDMA(HBlank Direct Memory Access)転送に分かれるが、HDMA転送は名前の通りにHBlankのタイミングでデータを送る転送のことを指す。
それ以外はGDMA転送、と当コンテンツでは使い分けることにする(だいたいの解説サイトでも同じように使い分けているようである)。
HDMA転送は、画像データを1行送った後のわずかな時間を利用するが、そのわずかな時間で、1行分のデータにちょっと手を加えることに使うことがほとんどのようである。
本作でいえば、メニュー画面で、選択していないコマンドの背景が半透明になって透けているが、この処理などはHDMA転送を利用した「カラーマス(Color Math, 色演算)」という処理になる。
GDMA転送では、キャラのスプライトや背景用の画像、色(カラーパレット)のデータなど、大きめのデータを送ることになる。
グラフィックデータの転送量が多すぎると、画面の切り替えに時間がかかることになる。
リメイク版発売の2020年代のゲームソフトでは、画面切り替えにロード画面が表示されるなど、データのロードや転送にある程度の時間がかかることがごく当たり前になったが(それでもリメイク版「ライブアライブ」はかなりロードが速いが)、ファミコン・スーパーファミコンの時代では、多少の暗転時間があってもロードが一瞬で終わるのが当たり前であった。
とはいえ、スーパーファミコンの時代でも、画面のロードが遅い、と感じるゲームソフトは幾つかあったが、「ライブ・ア・ライブ」についてはそういった問題は見られず、グラフィック関係の処理がうまく行われていることがうかがえる。
VRAMをはじめ、カラーパレットがあるCGRAM、スプライトの配置を指定するOAMといったメモリには、I/Oポート用のアドレスを通して通信する。
これらメモリへのデータ転送につかうGDMA転送についてざっと説明する。
GDMA転送に使用する主なI/Oレジスタは以下。
| アドレス(I/Oレジスタ) | 内容 |
|---|---|
[$00:43n0] | GDMA / HDMA設定レジスタ |
[$00:43n1] | 転送先指定 $04=OAM, $18=VRAM, $22=CGRAM, $80=WRAM |
[$00:43n3][$00:43n2] | データ元アドレス(開始位置) |
[$00:43n4] | データ元バンク |
[$00:43n6][$00:4305] | 転送するデータ量 |
[$00:420B] | GDMA(General Direct Memory Access)チャネルレジスタ |
[$00:2117][$00:2116] | 転送先 VRAMアドレス |
HDMA転送は、だいたい同じ命令方法だが異なる部分もあるので、別途説明をしている。カラーマス(色演算)のページを参照していただきたい。
転送先指定の部分だが、I/Oレジスタの[00:21XX]の「XX」部分が入ることになる。
上に記したのはグラフィック関係の代表的な転送先である。
VRAMやCGRAMのアドレス指定の時、アドレスをbyteではなくwordで指定するため注意が必要。
2byte(バイト)で1word(ワード)になる。
$00:43n?のnは0~7の値をとり、それぞれでGDMA(HDMA含む)転送を設定できる。
上だと、n=0である。
つまりGDMA転送は最大8つ設定できる(8チャネル)。
以降、「GDMA転送チャネル0」というような書き方をする。
複数のGDMAチャネルの設定をしてから、[$00:420B]に複数のチャネルの対応ビットに1を書き込むと、対応したチャネルのGDMA転送が行われる。
HDMA転送の場合は[$00:420C]に該当チャネルの対応ビットに1を書き込むことになる。
複数のGDMA・HDMA転送が指示された時は、HDMA転送が最優先、後はチャネル番号が小さいほど優先で転送を行う仕組みになっている。
ちなみにDMAで転送中は、スーファミ自体のCPUは一時停止する(計算を一時停止する)。
GDMA / HDMA設定レジスタの詳細など、詳しくは
snes-docs-ja/memory/dma
snes-docs-ja/memory/dma/ioreg.md
を参照していただきたい。
詳細はこの先で順に説明していく。
まずはスプライトについてをお読みいただきたい。