LECTURE
Oeffentliches Kunst Nr.3
 お待ちかね(?)、第三回は風雅システムの出世作ともいえる「アマランス」の技術公開です。スクロールタイプのアクションRPGとして、その驚異的なスクロール速度で話題にもなりました。今回はその高速8方向スクロールに関して重点的に解説していきます。
 高速8方向スクロール
 スクロールに関するテクニックは第一回、第二回と解説してきましたが、今回はまた異なった手法が取り入れられています。
 まず、スクロール単位が16ドットであること。リボルティー2の2ドット、ビートバイスの8ドットに対して「荒く」なっています。確かに、スクロール単位が大きくなるとカクカクしたイメージになってしまいます。でもその分スクロールが高速になったらどうでしょう。プレイフィールドを1面描画するためにかかる時間が短くなれば、当然スクロール速度が上がり、カクカク感が無くなってしまうのです。広いフィールドをストレス無く走り回ることができれば、プレイヤーはより深くゲームの世界に入っていけると考えた結果でした。
二重スクロール例
 2重スクロールにより遠近感を表現。中央の川面もアニメーションしています。もちろんこれだけ表示していても高速スクロールします。
 リボルティー2やビートバイスのような"強制スクロール"とは異なり、スクロール型RPGでは静止する場合が多くなります。するとプレイヤーにじっくりとフィールド画面を鑑賞される機会も多くなります。すると鑑賞に耐える画面作りが必要になります。もちろんグラフィッカーの力の占める割合が高いのですが、プログラムもそれに応えなければなりません。木や家の裏にキャラクタが入れば、陰になる部分は隠れなければ不自然になりますし、動いているべきものは動いていないとやはり不自然です。また、臨場感を出すために遠近感も表現できるとベターですから、遠景と近景が描き分けられると効果的です。アマランスでは強力なフィールド内アニメーションと2重スクロールを実現することでこれらビジュアル効果をサポートしています。
 しかしここで問題が発生します。ビジュアル効果を上げると処理が増え、スクロールが"重く"なってしまいがちです。それはとりもなおさず、プレイ環境の悪化を意味します。折角美しいフィールドをグラフィッカが作成しても、スクロールが遅いとプレイヤーはイライラしてしまい、熱中してゲームを楽しむことができません。「いかに美しくかつ高速にスクロールさせるか。」これがプログラマに科せられた命題でした。
 64KBセグメントとの戦い
 世に8086CPUが登場し、プログラマたちがそのプログラミングをはじめたときから苦労し続けていたのが64KBの「セグメントの壁」です。PC-9801VシリーズのV30CPUもi80286CPUも8086CPU互換で動作していたので、プログラマはこのセグメントに泣かされました。つまり、データを連続でアクセスしようとしても最大で64KBという制約があるのです。ポインタにしろそのオフセットにしろ、64KBを超えることができないので、高速にデータを扱いたい場合は、一連のデータのサイズを64KB以内になるようにせねばならないのです。そうしないと、アクセスする度にセグメントの計算処理と切り替えが伴ってしまいます。V30も80286もセグメントの切り替え自体は2クロックと高速なのですが、セグメントレジスタへは汎用レジスタ経由でしか値が設定できないので、セグメント値計算と相まって実際にはもっと時間がかかります。このため、高速化のためにはデータを64KB以内に収める必要があるのです。
 アマランスの場合、16×16ドット×8色のパーツが256個同時にフィールド背景として表示できる仕様になっています。各パーツにはマスクデータ(木や建物の陰に入ったときにキャラクタを部分的に隠すため)も入っているため、これだけで32KBあります。さらに、フィールドには歩行アニメーション付きのプレイヤーキャラクタや戦闘シーン用の敵キャラクタのデータも同時に表示せねばなりません。これが32KBあります。これで済めば64KBのセグメントに収まります。
 が、しかし、さらにフィールドマップデータが必要です。仮に180×180パーツでできたフールドマップに必要なデータサイズは、180×180×2バイト=64800バイトとなり、1セグメントギリギリのサイズになります。アマランスのフィールド表示サイズは20×20パーツ分ですから、81画面分になります。これがフィールドマップサイズの限界なのです。
 8086系CPUにはCS,DS,ES,SSという4種類のセグメントがあり、それらを同時に使用することができます。ただし、CS(コードセグメント)はプログラムとデータ用。DSとESはデータ用、SSはスタック用というように決められています。画面に表示するためにはV-RAMを指すセグメントが必要ですから、ESは表示用にとっておきます。SSはスタック専用で特殊ですので確保。残るはCSとDSです。ポイントとなるのはCSの使い方です。コードセグメントにはプログラムとデータの両方を置けるので、プログラムを64KB以内に抑えると、その分だけデータに使用できるというわけです。アマランスはCコンパイラなどを一切使用せず、100%アセンブラのみで組んであるため、プログラムが小さく抑えられています。そのため、コードセグメントにもデータを置ける領域が多くあり、実際、そこにキャラクタデータパターンを収めています。
 場合分けの細分化
 一口にスクロールフィールドの描画と言っても単純な作業ではありません。先に述べたようにアニメーションする部分もあれば、樹木や建物の後ろなどに入ってキャラクタが一部または全部隠れる部分、二重スクロールの遠景が透けて見える部分など、いろいろなパターンがあります。このような描画条件はフィールドマップデータとキャラクタの現在位置によって決定されます。
 さて、ここでも高速化のための工夫があります。それは各場合ごとに細かく分けて専用ルーチンを用意するというものです。マップフィールドを描画するためには小さなマップパーツ(16×16ドット)を400個描く必要があります。すると当然プログラム中に小さなループが存在することになり、そのループの中で場合分けを実行するのは非効率的だからです。条件分岐命令はパイプラインを乱しプリフェッチキューもクリアしてしまうので、ループ中で発生すると大きなペナルティを喰らいます。そこで重複する処理(プログラム)があっても敢えて1ルーチンにまとめたりせず、早い段階(つまり小さなループに入る前)で場合分けをして専用のルーチンに処理をさせるのです。
 これは大変面倒で保守も大変なのですが、高速化には替えられませんでした。下の2本のソースリストを見てください。8086アセンブラで記述したアマランスのスクロールルーチンの一部です。(見やすいように実際のソースに若干手を入れてあります。)
;========================================
;  PCGノーマル表示プロシジャ
;----------------------------------------
;<機能>指定されたPCGをそのまま描画する
;<入力>BX = 表示オフセットアドレス
;     DS:SI = PCGデータアドレス
;<出力>なし
;<保存>BX, DX, BP, DS保存
;========================================
NCNORM  PROC  NEAR
MOV DI,BX ;ES:DI=表示ポインタ
MOV AX,VRAM_B ;
MOV ES,AX ;
MOV AX,78 ;AX=表示ポインタ増分
REPT 15 ;
MOVSW ;
ADD DI,AX ; 青プレーン描画
ENDM ;
MOVSW ;
LEA DI,[BX+8000H] ;表示ポインタ更新
REPT 15 ;
MOVSW ;
ADD DI,AX ; 赤プレーン描画
ENDM ;
MOVSW ;
MOV DI,BX ;表示ポインタ更新
MOV CX,VRAM_G ;
MOV ES,CX ;
REPT 15 ;
MOVSW ;
ADD DI,AX ; 緑プレーン描画
ENDM ;
MOVSW ;
RET ;リターン
NCNORM  ENDP
;;========================================
;  背景PCG合成表示プロシジャ
;----------------------------------------
;<機能>指定されたマスクデータ付きPCGに、指定された
;     PCGを合成して表示する
;<入力>DS:BX=被合成PCGデータアドレス
;     DS:SI=マスク付きPCGデータアドレス
;     DX=表示オフセット
;<出力>なし
;<保存>DX保存
;========================================
BACKMIX  PROC  NEAR
MOV DI,DX ;ES:DI=表示ポインタ
MOV AX,VRAM_B ;
MOV ES,AX ;
MOV BP,78 ;BP=表示ポインタ増分
REPT 16 ;
MOV CX,[SI+96] ;
LODSW ;
AND CX,[BX] ;
OR AX,CX ;青プレーン合成表示
STOSW ;
ADD DI,BP ;
ADD BX,2 ;
ENDM ;
MOV DI,DX ;表示ポインタ更新
ADD DI,8000H ;
REPT 16 ;
MOV CX,[SI+64] ;
LODSW ;
AND CX,[BX] ;
OR AX,CX ;赤プレーン合成表示
STOSW ;
ADD DI,BP ;
ADD BX,2 ;
ENDM ;
MOV DI,DX ;表示ポインタ更新
MOV CX,VRAM_G ;
MOV ES,CX ;
REPT 16 ;
MOV CX,[SI+32] ;
LODSW ;
AND CX,[BX] ;
OR AX,CX ;緑プレーン合成表示
STOSW ;
ADD DI,BP ;
ADD BX,2 ;
ENDM ;
RET ;リターン
BACKMIX  ENDP
 ご覧のように一切ループは使用せず、マクロによって展開しています。上のリストは16×16ドット×3プレーン(8色)のパーツを表示するサブルーチンですが、左はベタ表示、右はデータのマスク部分に他のパーツを合成して表示するものです。この他にもアニメーションするパーツの表示やマスク合成するパーツがアニメーションする場合(川の流れなどに使用)、キャラクタが合成される場合など、10種類以上のサブルーチンが作り込まれているのです。
 V-RAMアクセスの回避
 これがもっとも高速化に貢献している技術であるといえます。V-RAMに全くアクセスしないと画面に何も表示されないので、ここは「いかにしてV-RAMへのアクセスを減らすか」という意味にとってください。
 V-RAMというメモリはメインメモリと異なり、常にグラフィックコントローラからもアクセスされています。CPUとグラフィックコントローラの両方からアクセスされるため、周辺の回路構成も当然複雑になっています。このため、CPUがV-RAMを読み出したり書き込んだりするときは、グラフィックコントローラがアクセス(主に読み出し)していないタイミングでないといけません。もし、CPUのアクセスを優先してしまうと、そのときだけ画面表示が不定になり、ノイズとして表示されてしまうからです。するとどうしてもV-RAMへのアクセスはメインメモリに比べて時間がかかってしまうことになります。実際に調べてみると驚くほど低速で、同じサイズのデータを書き込んでみると3倍以上時間がかかっていました(程度は機種によります)。アマランスの最低対象マシンはV-RAMに一般的なメモリ素子を使用していたPC-9801VMでしたので、これは大きな問題です。ちなみに、PC-9801VXシリーズ以降のマシンだと「デュアルポートRAM」という通常の読み書き端子の他に、グラフィックコントローラ用に読み出し専用の端子(ポート)を付加したメモリ素子が使用されるようになっており、かなりアクセス速度は改善されていました。
 さて、ではどうすればV-RAMアクセスを減らせるのか。一般的な方法はメインメモリ上に仮想画面を設け、そこで読み書きして画面を作成し、最後にV-RAMへ転送するというものでしょう。これならV-RAMアクセスは1回で済み、ちらつきも少なくて済みます。そしてなによりプログラムも簡単で済みます。
 しかし、欠点もあります。まず、仮想画面用のメインメモリが消費されること。仮にそのサイズが60KBだったとしても、全メモリの1割以上にもなり、大量のデータを扱うRPGには非常に大きなストレスになります。そして速度の問題。仮想画面を使っても最終的にはV-RAMに転送せねばならず、そのぶん時間がかかります。
 「では、どうしろと?」とツッコミが入りそうですね。実はこれも「根性」が解決(?)してくれます。
 キーワードは「合成処理」でしょう。キャラクタや背景を合成する際にメモリバッファがあると便利なため、仮想画面という手法が採られるわけです。先に述べたように直接V-RAMでこれをやると遅い上にちらつくからです。であれば、メモリバッファを使わない方法で合成処理ができれば、速度やちらつき、必要メモリの問題をクリアできるのではないか・・・・と考えて、それを実現したのがアマランスのスクロールルーチンです。
 上の右側のプログラムリストを再度見てください。直接V-RAMにアクセスしていますが、同一のアドレスには1回しか書き込みを行っていません。しかし、2つのパーツを合成処理しています。そう、合成処理をCPU内の16bitレジスタのみを使用して実行しているのです。これは最低限のメモリアクセスのみでスクロールフィールドを描画していることになります。これがアマランスの高速スクロールの実体です。
キャラクタ+背景合成アニメーション例
 川の流れのアニメーションに「橋」や「プレイヤーキャラクタ」が合成表示されています。フィールドマップ中央のキャラクタの上半身が流れる川とキレイに重なっているところがミソ。
 先の「場合分けの細分化」も実はこの手法に関連しています。部分アニメーションしている背景にキャラクタを合成するような複雑な場合もレジスタのみで処理する関係で、処理を細分化せざるを得ないのです。開発当初もこのことは予測していましたから、コーディングを開始するまで本当に悩みました。でも、「根性」を出して意を決したわけです。これもひとえにPC-9801VMユーザの方にもストレスなくスクロール型アクションRPGを「楽しんで」もらいたかったからに他なりません。ユーザの方々の感想等を耳にし、「やってよかった!」と確信したものです。
 続・ちらつき防止
 ちらつきを完全に防止するためには、ディスプレイの走査線が消えている間に描画を行わなければなりません。走査線が光っている間は画面に何かが表示されているということですから、そのときに表示の変化があるとちらつきとなって見えるからです。定石としては、最も長い時間走査線が消えている垂直帰線期間(V-BLANKING)に入るの待ち、一気に描画する手法があります。PC-9801のように裏V-RAMのある機種では、表V-RAMが表示されている間に裏V-RAMで次の描画を行い、垂直帰線期間のタイミングで表と裏のV-RAMを切り替える手法がよく使われます(フリップと呼ばれます)。実際、「アマランスII」ではこの手法を採用しています。
 ところがこの方法には欠点もあります。まず、裏V-RAMが無条件でリザーブされることになるため、他から利用することができないということです。ちなみにビートバイスではボスキャラのパターンの格納領域として利用していました。そしてもう一点はCPUのパワーが無駄になりやすいということです。これは低速な機種では大きな問題となります。つまり、垂直帰線期間に入るまでに次の画面の描画が間に合わなかった場合、さらに次の垂直帰線期間まで待たなければならなくなるのです。リアルタイムゲームの場合、これを待っている間CPUは何もできません。ビートバイスでもそうでしたが、これを回避するために頭を捻ったのです。
 アマランスで採った方法は先に述べたように、「V-RAMに一度しかアクセスしない」という手法です。これは「完全」とまではいきませんが、「ほとんど気にならない」レベルまでちらつきを低減できました。しかし、「うねり」現象は残りました。これは走査線の走るタイミングとフィールドマップを書き換えるタイミングが重なったときに、画面がうねうねとして見えるものです。比較的高速な機種ではこれもほとんど気になりませんが、V30搭載機種では、描画の重いマップが表示される場合(アニメーション部分が多かったり、連続するパターンが少なかったりした場合)、気になってしまいます。これを回避しようとすると、どうしても走査線のタイミングを見る必要があるため、高速化を主眼に置いたアマランスでは敢えて回避しなかったのです。
 テキスト画面の利用
 アマランスでもテキスト画面は利用されています。例えばフィールドマップの切り替えアニメーション。これは黒のテキストをフィールド画面に重ねることで実現しています。また、ブント砂漠の砂嵐やプファイフェ湖の水面アニメーションなどもテキスト画面と外字登録を利用しています。
 でも、プファイフェ湖でメニューを開いても、ちゃんと水面パターンの上に重なって表示されることに疑問はないでしょうか?PC-9801シリーズのテキスト画面は常にグラフィック画面の手前に表示されるのに、テキスト画面のはずの水面パターンの手前にグラフィックで描かれたメニューウィンドウが表示されるという点です。メニューが表示される部分のテキストパターンのみを消そうとしても、幾重にもウィンドウは重なりますし、そもそも上下左右16ドット単位でしか消去できません。
 実はこのとき、メニューを開く直前にフィールドマップ上にそのときのテキスト画面の水面パターンをグラフィックとして合成描画してからテキスト画面を消去しているのです。このとき一瞬画面にノイズが走ったように見えるので、お気づきになっていた方もいらっしゃるかもしれませんが。
 そして戦闘シーンでの残り体力表示。ダメージを受けたときにキャラクタの頭の上に表示されます。00〜99までの2桁数字を外字登録することで実現しています。ゼロブランクの1桁数字も含めると110個の外字を使用しています。最後に呪術アニメーション。バルムの最大級などに利用しています。
 テキスト画面は単色であったり、表示位置がキャラクタ単位であったりと制約も大きいのですが、少ないV-RAMアクセスで広く表示できるというメリットがあります。「バカとハサミは使いよう」というヤツです。(^_^;
 FM音源ドライバ
 ビートバイスに使用されたFM音源ドライバは「軽さ」に主眼が置かれていました。実際、PC-9801VM(V30-10MHz)で動作させても、平均して10%弱という低いCPU占有率を誇ります。しかし、決して多機能とはいえませんでした。何系統ものLFOを持たせたりすれば、あっという間にCPU占有率が上がってしまうからです。
 しかし、RPGとなるとシューティングとは異なり、演出上のBGMの占める割合が高くなってきます。つまり、音楽の表現力を高める必要が出てくるということです。そのため、アマランス開発にあたり、FM音源ドライバも大幅にパワーアップさせています。何重にも割り込みを処理し、深み・厚み・透明感のある音色が出せるようになっているのです。もちろん、この機能を使いこなすのは作曲者ですが・・・。結果がどうだったかは、製品をプレイされた方々はよくおわかりかと思います。CD化までいきました。(^_^o) ただし、当然のことながら、CPU占有率はPC-9801VMで最大30%前後にまで上がっています。こればかりはどうしようもありませんでした。
 それともう一点、効果音にも正式対応しています。BGMの演奏中であっても、効果音の発声要求があった場合は、発声優先順位の低いパートを一時的に止めて、効果音発声を割り込ませています。これは音色の高速切り替えなどが伴うため、FM音源LSIであるYM2203の特性を熟知していないと難しいのです。
 FG-DOS
 アマランスにはFG-DOSが組み込まれています。FG-DOSは風雅システムが独自開発したMS-DOS Ver.2.11ダウンコンパチのOSです。前作のビートバイスでは、フロッピーベースでプレイする場合、ユーザ側でMS-DOSをインストールする必要がありました。これは当時のソフトメーカーが皆頭をかかえていた問題でもありました。ユーザには購入したらすぐに手間をかけずにプレイしてもらいたい・・・でも、MS-DOSをインストールした状態で発売しようとすると価格が跳ね上がってしまう・・・そこで自社開発に踏み切ったわけです。当然、開発後は他社に販売することもでき、実際に何社かとライセンス契約もしていました。
 実際の開発は楽なものではありません。MS-DOS自体を解析してしまうと問題になるので、機能面から設計する必要があるのです。特にフロッピーディスクのアクセス関連などの処理は大変です。そんなわけでFG-DOSはフロッピーのみの対応とし、ハードディスクには対応していないのです。
 自社でOSを開発するといろいろとメリットがあります。例えばサイズが小さく、動作も軽いということ。システムコールなどもゲームで使用する機能のみをサポートすればよく、過去との互換性のためのものなどは無視できます。ハードディスク対応部分もカットできるので、実際にFG-DOSはかなりコンパクトです。当然、コンベンショナルメモリも多く確保できます。ちなみにCOMMAND.COMに相当するプログラムも独自開発していますが、これはコマンドプロンプトにすら対応していません。バッチファイル処理に特化してあります。このあたりも完全にゲーム専用OSらしい部分です。

特別講義メニューページへ
ディン:「わたしのデビュー作品よね。」