東方老桜夢の技術解説 枠付きスクロールについて

東方老桜夢では、弾幕密度を上げるためにゲーム画面を半分サイズにし、 左側にゲーム画面、右側にスコアなどの表示を行っています。

ファミコンはBG面を1つしか持たないため、ハードウェアの機能では 枠が付いた状態で1ドット単位で背景がスクロールする画面を 作ることはできません。
ソフトウェアで工夫することで実現しています。

仕組み

基本的な仕組みは、ネームテーブル書き換えのソフトウェアスクロールCHRバンク切り替えによる1ドットスクロール での補間です。

ネームテーブル書き換えのソフトウェアスクロールとは、 MSX1などのレトロPCでよくみる画面書き換えによる8ドット単位のスクロールです。
東方老桜夢では、スクロールする背景部分のネームテーブルを全面書き換えています。

ファミコンでは、1フレーム辺りのVRAMへのデータ転送量は十分にはありません。
そこで、画面を2つ用意し、片方(表面)を表示している間、もう片方(裏面)を数フレームかけて書き換え、 表面と裏面を入れ替える方法を採用しています。
いわゆるダブルバッファ法です。

CHRバンク切り替えによる1ドットスクロールとは、 1ドットずつずれたキャラクタパターンを8つのCHRバンクに格納し、
CHRバンクを順番に切り替えることで、見かけ上1ドット単位で スクロールしているように見せる手法です。
画面がスクロールするアニメーションを見せていると言ってもよいでしょう、

スクロールする方向は、生成するキャラクタパターンのずれの方向で決まります。
必要であれば、同じ画面内で異なる方向へスクロールすることも可能です。
(5面道中や、妖夢、藍のスペカ背景など)

ネームテーブル書き換えとCHRバンク切り替え
(1~8 はCHRバンクを順番に切り替えて1ドットずつずれた絵を出す。
9で表面と裏面を切り替えてネームテーブルを書き換えた絵を出す、CHRバンクも最初のバンクに戻す。)

レイヤー

スクロールする背景部分は、仮想的に4階層のレイヤーを持たせています。
各レイヤーは表示/非表示を設定でき、表示状態で最も優先度が高いレイヤーが VRAMに転送する画面となります。

レイヤーの内訳は次の通りです。

優先度VRAM転送元データの場所用途
レイヤー3WRAM自機ボム表示
レイヤー2WRAM敵スペカの弾丸表示など
レイヤー1PRG-ROM敵スペカの背景
レイヤー0PRG-ROM通常の背景

レイヤー0とレイヤー1は、自機や敵キャラの位置に依存しない背景であるため、 決められた背景データをPRG-ROMに配置し、シーケンスデータに従って VRAMに転送します。
一方、レイヤー2とレイヤー3は、自機や敵キャラの依存する背景のため、 WRAMに設けられた仮想VRAMに動的にデータを作成して、 そのデータをVRAMに転送します。

ネームテーブルの書き換えの詳細

スクロールする背景部分のネームテーブルの書き換えは、 8フレームを1単位として処理します。
これは、1フレームで1ドットスクロールするとして、 8フレームで1キャラ分(8ドット)ずれて表面と裏面が入れ替わることを 想定したためです。

スクロールする背景部分のネームテーブルの書き換えの処理は、 PRG-ROMのデータを転送する場合とWRAMの仮想VRAMから転送する場合で 違いがあります。

PRG-ROMからの転送

1単位8フレーム中の各処理は次の通りです。

フレーム処理
0フレーム目転送なし
1フレーム目画面の1/6を転送(16キャラ×4ライン)
2フレーム目画面の1/6を転送(16キャラ×4ライン)
3フレーム目画面の1/6を転送(16キャラ×4ライン)
4フレーム目画面の1/6を転送(16キャラ×4ライン)
5フレーム目画面の1/6を転送(16キャラ×4ライン)
6フレーム目画面の1/6を転送(16キャラ×4ライン)
7フレーム目アトリビュート領域を転送、表面と裏面の入れ替え

1~6フレーム目では、1フレーム辺りのVRAMへの最大転送量の半分を使って 裏面のスクロールする背景部分を書き換えます。
VRAMへの最大転送量の残り半分は、スコアなどの書き換えに使用します。

7フレーム目では、アトリビュート領域(BGのパレット指定データ)と 表面と裏面の入れ替えを同フレームで行っています。
これは、とある理由でアトリビュート領域は表面裏面で共通となっているため、 表面と裏面を入れ替えるタイミングでアトリビュート領域を更新しなければ ならないためです。

1フレームで1ドットずれるスクロールのスピードは演出的には速く見えるため、 実際には32フレームや64フレームなどの2のn乗倍遅いスピードで 使用しています。
この時、ネームテーブル書き換えの処理は、表面と裏面を入れ替える 8フレーム前から開始して、それより前のフレームでは転送処理を休止しています。

WRAMからの転送

1単位8フレーム中の各処理は次の通りです。

フレーム処理
0フレーム目画面の1/3を転送(16キャラ×8ライン)
1フレーム目画面の1/3を転送(16キャラ×8ライン)
2フレーム目画面の1/3を転送(16キャラ×8ライン)
3フレーム目アトリビュート領域の転送
(表示するレイヤー切り替え後、1回のみ)
表面と裏面の入れ替え
4フレーム目画面の1/3を転送(16キャラ×8ライン)
5フレーム目画面の1/3を転送(16キャラ×8ライン)
6フレーム目画面の1/3を転送(16キャラ×8ライン)
7フレーム目アトリビュート領域の転送
(表示するレイヤー切り替え後、1回のみ)
表面と裏面の入れ替え

WRAMからの転送では、仮想VRAMに動的に作成したデータを いち早く画面に反映させるため、4フレームで画面を書き換え、 8フレーム中2回表面と裏面を入れ替えています。

0~2、4~6フレーム目では、1フレーム辺りのVRAMへの最大転送量を全て使って 裏面のスクロールする背景部分を書き換えます。

3、7フレーム目では、表示するレイヤーを切り替えた後1回だけ アトリビュート領域を転送します。
アトリビュート領域の転送を行った以降のフレームでは、 スコアなどの書き換えを行います。

WRAMの仮想VRAMは、処理落ちが発生していなければ、更新されているため、 VRAMへの転送処理も常時稼働する動きとなります。