ソーサリアン〜内部解析からわかったこと〜 by PI.
はじめに
筆者は、PC-88史上に残る名作である(株)日本ファルコムのARPG、「ソーサリアン」をシャープ製パーソナルワークステーションX680x0シリーズへ移植した経験をもつ。この移植は全くのアンオフィシャルなものであり、あくまで個人の範囲内で独自に行ったものであることを予めお断りしておく。
さて、この移植にあたり、筆者らはバイナリレベルで60KBにもおよぶPC-8801mkIISR版「ソーサリアン」のほぼ全プログラムを解析するという作業を行った。この結果、今まで知られていなかった事実や、またソーサリアンのメインプログラマーである木屋 善夫氏(現在、日本アプリケーション(株)に在籍)による高度なプログラム技術を窺い知る事が出来た。
本稿では特に技術的側面にスポットをあて、ある程度PC-88の内部構造やプログラミングの知識を持つ方向けに種々の技術を解説する。また、その他にも解析により判明したこぼれ話、秘話の類も所々で書いていくので、一般のソーサリアンファンの方も是非ご一読頂きたい。
X680x0シリーズへ移植...移植作業は1993年5月4日に着手した。その後解析、開発を続け15本の基本シナリオが問題なく動作した段階でオリジナルディスクからのインストーラを準備し、1995年3月26日をもって全国の主要X68k系BBSで公開した。
解析...ここでは機械語のプログラム(バイナリプログラム)を逆アセンブルし、その結果からメモリの使われ方や各種計算式、動作上の仕組みなどを推定することをいう。
特徴
ソーサリアンのプログラム全体を概観すると、次のような特徴がある。
オールマシン語、独自プログラム
プログラムは全てアセンブラで作成されており、またIPL等の一部を除きROM内ルーチンを使用していない。起動してシステムが動き始めると、全てソーサリアンのディスク内に含まれるコードで動いている。
簡潔にして明瞭なプログラム
プログラムは全体としてかなり洗練されており、少ないステップ数で必要なことを実現できる、アルゴリズムを工夫して速く処理できる、といった特徴がある。巷では少しでも速く処理できるようクロック削りが限界までされているという噂があるが、決してそういったことはない。むしろ(本当の意味での)正攻法によって簡潔、明快に表現し、プログラムサイズを減らそうという意図がみられる。
高速性に配慮
一見前項と相反するとも考えられるが、プログラムの性質上高速性にも十分配慮がされている。たとえば隠し命令、自己書き換えの積極的な使用、サブルーチン呼び出し+リターンで終わる場合は単純ジャンプにするなどである。また重要なVRAM書き込み部分はZ80で最速とされるPUSH, POP系の命令を使って処理されている。
BIOSとメインプログラムを分離、BIOSを介したプログラムチェイン
プログラムはメモリマップ(後述)で示されるBIOSエリアと、メインプログラムエリアに分けられる。BIOSはキーやディスクなど各プログラムで共通に必要とされるルーチンを提供し、これによって保守性を高めている。またBIOSにはプログラムエリアにプログラムをロードしてジャンプする機能があり、これを使用することで次々と自分(旧メインプログラム)を殺しては新しいプログラムをロード、実行している。
IPL...Initial Program Loaderの略。ディスクをセットしてコンピュータをリセットするとディスク上のプログラムが起動するが、その取っかかりのプログラムをIPLという。通常、IPLはディスクの最も先頭の部分に存在する。
自己書き換え...プログラム自身でプログラムの一部を書き換え、振る舞いを変えながら動いていく動作。以下のようなコードがその一例。
この例では一度ENTRYに来て最後のRETを終わった後は、他の部分で(ENTRY+1)をクリアしない限りルーチンに入ってもすぐ抜けてしまう。
ROM内ルーチンを使用...PC-88にはN88-BASICが装備されているが、BASIC外のシステムからROM内の汎用性の高い一部分(=ルーチン)だけを呼び出し、目的の機能を手軽に得ることを言う。
BIOS...Basic Input and Output Systemの略。基本的な入出力部分だけを集め、汎用的に使えるようにしたルーチン群のこと。
こぼれ話(1)開発はどういった環境で行われたか?
ソーサリアンのプログラムディスクを丹念に見ていくと、ファイルの入っていない空きエリアに、「MIFES」のヘルプファイルの断片がある。このことなどから、開発は主にMS-DOSでのクロス環境で行われたと推測される。PC-9801VXなどにハードディスクをつなぎ、PC-98で2Dフロッピーに書き込んではPC-88に差し替え、リセット→起動を繰り返して作成していったのではないだろうか?
クロス環境はもちろんメリットが大きい。MS-DOSのファイルシステムが比較的高速であることや、ハードディスクが使えること、640KBメモリや漢字テキストVRAMを生かして大きなソースファイルをスムーズに編集ができること、アセンブルに要する時間を短縮できることなどである。当時MS-Cなどもそれなりの水準に達していたので、キャラクタエディタ、マップエディタなどの周辺ツールはC言語もしくはx86アセンブラで作成、PC-98で稼働させていたと思われる。
また、ソーサリアンの2年前に開発された「ザナドゥ」についてもおおよその開発環境が推測できる。ザナドゥのプログラムは何本にもわかれているが、いずれもエントリアドレスが0100hなのだ。エントリ0100hというとMS-DOSのCOMファイルがそうであり、MS-DOSが手本としたCP/MのTPA領域がやはり0100hからである。このためザナドゥもCP/M上で開発が行われたと思われる。メインメモリ後半が未使用であることもあわせて、開発途中は動作環境までCP/Mであったのではないだろうか?
仮にデバッグバージョンのザナドゥがCP/M上で動作するものだったなら、何らかのデバッグコマンドを設けてCP/MのCCPに戻れば、そのままWMや、M80,L80という開発ツールで開発を続けることができる。ソフト開発の現場で、このループ(ターンアラウンド)に要する時間を短縮することは開発効率を高める上で大きな効果がある。
ただCP/M上で動作させる場合、当然のことながら走るプログラムはCP/Mの制約を受ける。メインメモリが64KBフルに使えないこともその1つである。ソーサリアンではこの制約を嫌ったことと、クロス環境での開発が実用レベルに達するメドがついたことなどからCP/Mによる直接実行をやめ、MS-DOS上でのクロス環境に移行したのだろう。
MIFES...メガソフト製のスクリーンエディタ。REDやFINALと並び、初期のPC-9801エディタの代表的存在とされる。Windows版は現在も開発・販売されている。
エントリアドレス...プログラムの入り口(エントリ)。実行開始アドレスのこと。
CP/M...デジタルリサーチ製の8080用汎用OS。CP/M rev2.2はPC-8001,PC-8801,X1,FM-7/8+Z80カード等各種Z80マシンに移植され、国内においても大きな成功を収めた。移植の仕方によってフリーエリアが異なるのが特徴で、56K CP/Mや60K CP/Mなどと呼ばれていた。
TPA領域...CP/Mでユーザプログラムが走る領域。CP/MはシングルタスクOSであり、ユーザプログラム(コマンド)は必ず0100hからロードされ、FCBなどのワークエリアがセットされた後に0100hに制御が移る。
CCP...コマンドプロセッサ(シェル)。MS-DOSのCOMMAND.COMに相当するもの。
WM...Word Masterのコマンド名。マイクロプロ製のスクリーンエディタ。登場直後にCP/Mの標準スクリーンエディタとしての地位を確立し、英文ワードプロセッサWord Starと並んでCP/Mアプリケーションの代表的存在となった。現在もファンの多いダイヤモンドカーソル(^E, ^X, ^S, ^Dでカーソルを上下左右に動かす方式)の元祖。
M80..Macro-80のコマンド名。マイクロソフト製のZ80アセンブラ。非常に高機能でプロの開発現場では多く用いられた。
L80..Link-80のコマンド名。マイクロソフト製のZ80リンカ。上のM80と組み合わせて、大規模なプログラムを開発できる。
メモリマップ
別表にソーサリアンのメモリマップ(シナリオ実行時)を示す。このPRNO2のファイル名を持つプログラムが動作するこの状態が、ソーサリアン動作中に最もメモリを消費する局面である。
まず特徴として上げられるのは、メインCPU側にわずかながら空きメモリが存在することである。ソーサリアンのスペックとしてPC-88の資源を最大限考慮したシビアな設計になっているのだが、空きが皆無でないことはわかる。
メインCPU側を詳しく見ると、VRAMのR,G,B3バンクの最終部分を削り、仮想VRAMにしている点が目を引く。このアイデアはまさにコロンブスの卵で、ALUによって仮想VRAMの高速アクセスを行っているのだ。もちろん普通にアクセスしていてはこの内容が表示されてしまうので、CRTC・テキストVRAMを操作して表示マスクしている。
その他メインCPU側で注目されるのはフォントデータと各グラフィックパターンの配置である。後述する重ね合わせ処理によりマップグラフィック以外のキャラクタグラフィック・モンスターグラフィック・魔法グラフィックは必ず合成処理が起こり、重ね合わせ合成用バッファに合成結果が格納される。このためこれらのグラフィックパターンはR,G,B各プレーンのデータに加え重ね合わせ用のシェイプ(輪郭)パターンを持っている。これに対し、マップグラフィックはシェイプパターンを必要としない。このマップ用シェイプパターンを借りる形でマップグラフィックエリアに文字フォントを飛び飛びに配置している。
またマップグラフィックが8000hから、合成バッファがA000hから、それ以外がC000hからとなっているのも絶妙の配置といえる。PC-88のようにバンク切り替えでVRAMをアクセスする場合、同アドレスに重ねて存在するRAMが使いにくくなるのが欠点とされているが、このメモリマップではVRAMと重なるメインRAMには重ね合わせ前のグラフィックパターンしか存在しないのだから全く問題ない。このよく練られた配置がグラフィック描画時の速度アップに一役買っている。
サブCPU(ディスク)側に目を移すと、一部エリアにモンスター解説文字列が存在することがわかる。この他にサブ側に存在するのはディスク、ファイル関係の部分だけなので、モンスター情報データだけ存在するのは違和感がある。
これは推測だが、当初モンスター情報データはメイン側に持つ設計になっており、いよいよメモリが足りなくなったときに急遽サブ側へ移されたのではないだろうか。モンスター情報データはシナリオプレイ中に[M]キーにより表示されるテキストデータであり、アクセス時間が多少かかっても問題ない。そういった形で移動候補としてあがり、最終的にこのデータのみサブに持つ形に落ち着いたのではないだろうか。そして逆にメイン側が少し余ってしまった、と考えれば辻褄が合う。
またこのメモリマップからもわかるように、トータルの必要メモリサイズは64KBをゆうに超えている。PC-8801mkIISR版ではディスク、ファイル関係のプログラムやバッファなどはサブ側に持っているのでメイン側の負担は減るはずなのだが、それでもVRAMの一部を仮想VRAMにまわすなど苦肉の策がみられる。この状態では、搭載RAMが64KBぴったりしかないX1/C/D/F/G/twinや、メイン63KB+サブ5KBしかないFM-7/new7/77ではメモリ不足になってしまうのは明らかだ。X1の上位機種であるX1turboではVRAMを96KB搭載しているため一部をVRAM裏バンクにまわして実現できたが、FM-7の上位であるFM77AVには結局移植されることはなかった。FM77AVはメインRAMを128KB搭載しているためメモリ容量としては十分なのだが、X1turboと異なりCPUが6809で、全てのプログラムを書き直すリスクを考慮したためと思われる。
VRAM...ビデオRAMのこと。メインRAMと異なりVRAMはディスプレイと直結しており、VRAMに書き込んだデータがそのまま画面に表示される。言い換えれば、画面に何か表示するためにはVRAMへの書き込みを行わなければならない。
ALU...PC-8801mkIISR以降に装備されている論理演算回路のこと。ALUをうまく使うことで、画面クリアや文字出力が最大3倍速に高速化できる。またスクロールやブロック転送にも有効。
CRTC...CRTコントローラのこと。複雑なCRT制御やタイミング管理をハード的に行い、またVRAMの内容をCRT側へ送り出す機能も担う。PC-88ではテキスト画面の管理や表示もCRTCが担っている。
仮想VRAM...通常のメインRAMに置く、特殊な画面ワークエリアのこと。VRAMの内容を仮想化、縮小化したものが多い。
メイン63KB+サブ5KB...時々誤解されている方がいるが、FM-7のメインRAMは64KBではなく$0000〜$FC7Fの63KB強である。(チップとしては64KB分あるが、$FC80以降がデコードされていない) またFM-7シリーズはデュアルCPU構成となっており、サブCPUに48KBのVRAMや5KBのワークRAMが存在する。
VRAM裏バンク...X1turboでは640×400ドットの8色表示を可能とするため、X1互換のVRAMに更にバンク切替で48KBのVRAMが追加された。この追加VRAMは640×200モードでは表示対象とならないため、擬似的にワークRAMとして使うことが出来る。
FM77AVは128KB...FM77AVでのメモリ拡張はMSX2やMZ-2500,MB-S1と同様、メモリ空間を細かく分割して分割単位でマッピング、64KBを構成する方式。FM77AVではMMR(Memory Management Register)と呼ばれ、最大192KBのメイン側メモリ空間に加えてサブCPU側のメモリすらマップ、アクセスできる。(FM-7では128バイトの共有RAMを使ってのCPU間通信で、処理が煩雑になりがちだった)
BIOS
PC-88をリセットしてIPLに制御が移ると、IPLはメインCPU側へBIOSとオープニング部("PRNO0"というファイル名がつけられている)、サブCPU側へディスクBIOSの一部をロードし、システムを起動する。
BIOSは0000h-0FFFhに常駐し、システム起動後は一貫して基本的なI/Oサービスを提供する。BIOSの呼び出しはZ80の特性を生かしRST 08H, RST 18H, RST 20Hなどで行えるようになっており、高速な呼び出しが可能となっている。
BIOSでサポートしている機能として、次のようなものがある。
(1)ディスクBIOS
ソーサリアンでは伝統的なファルコムDOS(便宜的にこう呼ぶ)を採用し、高速にファイルの読み込み、書き込みが行えるようになっている。ディスクBIOSはこのファイルI/Oを行い、ディスクエラーの際はエラーメッセージをメインプログラムへ送り出す。また、1000h以降に配置されているメインプログラムを入れ替え、再度1000hへジャンプするチェイン機能を持っている。
(2)キー及びジョイスティックの読み取り
キー、ジョイスティックはV-SYNC割り込みを使って定期的に読み取られ、ワークに格納される。またV-SYNC割り込みハンドラでは、メインプログラムのためフックが用意されている。
(3)サウンドBIOS
いわゆるサウンドドライバである。ソーサリアンでは"MUCOM"と呼ばれるドライバが採用されており、また後に拡張が行われた。これについては後で特徴などを詳しく述べる。
(4)システムスタック
1000h以降はメインプログラム+データに解放されているため、スタックエリアもBIOS内に存在する。ただしスタックサイズはわずか128バイトしか取られていない。メモリ管理上の都合でこのような小サイズになったものと思われるが、サブルーチン階層の一番深い部分でサウンド割り込みがかかり、さらにその深い階層で...と考えると足りなくなる可能性も考えられる。このため開発中に一度はスタックサイズの計算、シミュレーションを行ったと思われる。
BIOSはゲーム開発者であれば誰しも必要性を感じるコンポーネントであり、特にBASIC ROMが全く当てにできないPC-88環境では必須といえる。ソーサリアンで優れているのはそのコンパクト性と柔軟性であり、わずか4KBのプログラムで必要にして十分な機能を提供している。またBIOSのワークを操作することで、強制キー入力(風に飛ばされるなどの演出効果)も容易に行えるようになっている。
RST xxH...Z80の命令の一つで、BIOSのような汎用ルーチン呼び出しに向いた命令。
チェイン機能...ソーサリアンのプログラムファイルは番号で管理され、0〜9までの番号に応じてプログラムファイルが読み込まれ実行される。この機能を利用して、オープニング→城→シナリオプレイ→トラベラーズイン→城→ドラゴンモード→エンディングのように、次々とプログラムを入れ替え実行する。入れ替えを指示するBIOSコールもメインプログラムから発行されるため、BIOS内でV-SYNC割り込みフックOFF、スタックポインタを初期化した後に1000hへジャンプする。
V-SYNC割り込み...V-SYNCは垂直同期の意味で、CRTの表示を行わない期間(垂直帰線区間)
の立ち上がりでかかる割り込み。PC-88では1秒間に約56回もしくは約60回発生する。
ハンドラ...割り込みが発生した時にジャンプしてくる処理命令群のこと。通常割り込みハンドラをプログラムし、ベクタと呼ばれるエリアにハンドラのアドレスをセットしてから割り込みを許可する。
フック...本来のプログラムに対して、後から処理を追加できる仕組みを設けること。もしくは本来のプログラムに、後付けで処理を追加すること。
スタック...ルーチンの呼び出しなどに使われる一時的なメモリ領域。呼び出されたルーチンでさらに別のルーチンを呼び出したりすると、それだけスタックが深く使用される。
BASIC ROMが当てにできない...BASIC ROMはBASICを動かす目的で設計されているため、ゲーム使用にそぐわない部分があること。たとえば[STOP]キーで止まる、ディスクアクセスが遅いなど。
サウンドドライバ
ソーサリアンではBIOSエリアにサウンドドライバが存在し、PC-8801mkIISR以降に搭載されているFM音源チップ、OPNをドライブする。OPNはFM音源を3音、SSG音源を3音発生できるが、ドライバ内ではSSG音源のうち1音もしくは2音を効果音に割り当て、BGMと重ねて演奏できる仕組みになっている(効果音割り込み)。つまり効果音演奏中はバッティングするBGMトラックの演奏を停止し、効果音終了後に復帰する。
また割り込みでは、FM音源割り込み(OPN TIMER-B)をBGMのテンポ管理に、1/600sインターバルタイマ割り込みを効果音のテンポ管理に用いている。ただしインターバルタイマ割り込みは内部でカウント処理を行っており、1/150s周期で実際の処理を行っている。
機能的特徴としては、
SSGのソフトエンベロープ機能は、内部に1バイトのサブボリュームを持ち、(SSGのボリューム)×(サブボリューム+1)/256によりボリュームを算出し、処理している。よく用いられているのは最初サブボリューム=255として規定通りの音量で鳴らし、すぐにサブボリューム=200でちょっと音量を落とし、その後ゆっくりと落として減衰させるといったパターンである。こういったエンベロープパターンは10種類がデフォルト定義され、その他に3種類BGMデータ内で定義することができる。
LFOについては比較的クセの無い処理がなされている。本ドライバでは音程LFO(ビブラート)、三角波のみサポートしている。これはPC-88ゲームの音源ドライバとして標準的なものである。
OPN...ヤマハ製FM音源チップ、YM2203の通称名。OPerator type-Nの略。PC-8801mkIISRの他PC-6601SR、PC-8001mkIISR、PC-9801-26(K)、FM77AV、MZ-2500等多くの採用機種がある。X1turboやX680x0ではYM2151(OPM)を採用しており、こちらはFM音源×8音、SSG音源×0音となっている。
BGMトラックの演奏を停止...実際にはカウント処理などを行ってシーケンスデータを通常通り進めておかないと、復帰時にズレが生じてしまう。このため本ドライバでは演奏を停止するのではなく、自己書き換えを使ってOPN出力ルーチンの先頭をRET命令に書き換えることで処理している。割り込まれたBGMトラックの処理が終わればOPN出力ルーチンを元に戻し、次のトラックへ移る。
テンポ管理...音楽演奏では一定のテンポを刻む必要があるため、正確に計時できるタイマーと、割り込み機能が不可欠である。またBGMのテンポは曲によって変化するが効果音はテンポ一定のため、2系統のタイマーを用いたドライバが比較的多い。ソーサリアンもその1つである。
ソフトウェアエンベロープ...SSG音源では16段階のボリュームをダイレクトに指定するため、音にメリハリが無くなってしまう傾向にある。このためソフト的に出始めで音を大きくしたり(アタック)、その直後に少し音量を下げる(ディケイ)、音を止めるときも減衰させながら落とす(リリース)などの効果をもたせることで、リアリティのある音づくりが出来る。
LFO...Low Frequency Oscilatorの略。ここでは、音を発声した直後に間をおいて(LFOディレイ)その後連続的に揺らぎをかけること。音程を揺らすビブラートと、音量を揺らすトレモロがあるが、実際にはビブラートが使われることが多い。トレモロの使用例としては「ザナドゥ」のトレーニンググラウンドのBGMがあげられる。
こぼれ話(2)サウンドBIOSの拡張とは?
ソーサリアンのプログラムディスクは初期バージョンのVer1.0と、後期バージョンのVer1.1がある。このうちVer1.0のディスクについては、一部ユーティリティや拡張シナリオを実行する場合に前に、城で「ユーティリティ」コマンドを実行してサウンドBIOSを拡張しておく必要があった。
この拡張を行うと、「この拡張は88のメモリー不足を補うために必要なものですので音そのものが良くなるわけではありません。」という表示がされる。では一体、どういう拡張、動作が行われるのだろうか?
結論からいうと、サウンドドライバのデータ解釈部にパッチを当て、予約(リザーブ)になっているコマンドを1つ拡張している。拡張されたコマンド番号はFDhで、動作としてはシーケンスデータの条件判定つきジャンプとなる。またこのコマンド処理部はサウンドドライバ本体の余り領域に展開される。
このコマンドを装備することにより、より小さなデータでより長い曲を演奏させることができる訳だが、異例とも言えるシステムのバージョンアップの裏にはミュージックスタッフからの強い要求があったのではないだろうか?というのは当時のパソコン環境は現在のようにハードディスク前提の環境ではなく、プログラムディスクはプロテクト処理されたフロッピーであるためVer1.0のユーザは起動処理を行う度に拡張動作を行わなければならない。この煩雑さを避けるよりも、より良い音楽を提供するというメリットの方が多との判断がなされたのであろう。
また拡張時の展開先となる空きエリアであるが、拡張前の空きエリアは14バイト、そのうち12バイトを使用し展開後は僅か2バイトが余るのみである。壮絶。
マルチウインドウ・フォント
オープニングが終わると、城の画面になる。ここではキャラクタ作成やパーティ解散などのメンテナンスが行える。またユーティリティコマンドで、別ディスクのプログラムに制御を移すことが出来る。
筆者はソーサリアンが雑誌で最初に紹介されはじめた'87年の暮頃、この城の画面写真を見て「なぜ漢字でなく仮名表示?」と思ったものだが、実はこの画面表示系は本格的なマルチウインドウ表示となっていたのだ。他のソフトでよく使われていた上へ上へと描いていく疑似ウインドウ表示でなく、ソフトウェアで作り出したマルチレイヤーである。その仕組みは次のようになっている。
キャラクタ単位で画面を分析すると、この城画面は640×192ドット、40×24のキャラクタで構成されている。仮想VRAMを考えると、文字コードで1バイト、文字色で1バイトとして40×24×2バイトあれば画面の仮想化が行える。この仮想VRAMを内部に5画面分持っている。この5画面を順位付けすると、次のような使われ方がされている。
No.0 ... 現在の画面の状態を示す
実際に描画するときは、No.4→No.3→No.2の優先順位で、「何かキャラクタがセットされていないか」と探していく。この場合FFhが未使用(透明)の意味で使われており、FFhを検出すると次の優先順位のレイヤーを探索する。こうして得られたデータとNo.0を比較し、違っていればターゲットキャラクタ、一致していればFFhをNo.1にセットする。あとはNo.1に従ってFFh以外のキャラクタを画面を描き、さらにその結果をNo.0に再設定して終了となる。
また文字フォント、文字色についても少し述べておく。文字としては数字、英大文字、ひらがな、カタカナが使えるが、これらを1バイトで表現するためソーサリアンでは独特のコード体系が使われている。またフォントサイズは横16ドット×縦8ドットで、比較的見やすいサイズ、解像度になっている。
また文字色は2色を混ぜて使う方式がとられている。これは横方向に交互に2つの色を表示することで中間色を表現する方式で、たとえば枠などに使われているクリーム色は白色と黄色で構成されている。ソーサリアンではすべての文字表示でこの2色方式がとられており、独特の文字色表現に成功している。
疑似ウインドウ...重ね合わせ処理を行わず、ウインドウを順番に上書きしたり、もしくはプッシュ・ポップにより最前面ウインドウだけがアクティブになる方式。「ファンタジアン」や「ハイドライドII」などが採用例としてあげられる。
640×192ドット...PC-8801mkIISRでは640×200ドットの画面解像度をもつが、ソーサリアンでは再下段の8ドットは使用していない。これは8ドット単位で考えた場合40×24となり、どちらも2の倍数となることや、最終8ドットを仮想VRAMとして使用することを考慮したためと思われる。
独特のコード体系...一般的に使われるASCII(ANSI)コード体系から、0x台・1x台の制御コードと6x台・7x台の小文字アルファベットを取り去り、ひらがな・カタカナを追加している。00h〜3FhはASCIIコードの20h〜5Fhにほぼ対応する。
町
城から「町へ行く」コマンドで町に移動することができる。ここでは武器を購入する、アイテムに魔法をかける、レベルアップ、修行などのアクションができる。
町での技術トピックとして面白いのが顔グラフィックの表示だろう。メニューから「武器と防具の店」「魔法使いの家」などの行き先を選ぶと、右側メニューウインドウから顔グラフィックが左側にスライドしてくる。逆にメインメニューに戻るときは、右側に移動してメニューウインドウに戻っていく。顔グラフィックはGOOD,NORMAL,EVILの3種類用意されており、キャラクタのKRMによって表情が変わるようになっている。このあたりは芸が細かい。
この表示原理を明かすと、まず町のグラフィック(背景グラフィック)は1枚絵ではない。16×8ドットのパーツによってタイル状に配置されたグラフィックであり、その配置の仕方を指示するデータ(パターンファイル)が別にある。この背景グラフィックデータ+背景パターンファイルに加えて、KRMに応じた顔グラフィックデータ+顔パターンファイルが読み込まれる。
これらのデータは仮想VRAMを使って管理、表示される。たとえば顔グラフィックを出現させる場合は、まず作業用仮想VRAMに背景パターンファイルの内容をまるごとコピーし、さらにその上の任意座標に顔パターンを上書きする。この際に、はみ出しチェックを行い、表示対象とならないようにする。メッセージ、キャラクタ名、GOLDなどを表示する部分も表示対象から外す。
あとはこの作成した作業用仮想VRAM、現状用仮想VRAMを比較し、違っているところだけ16×8ドット単位で描画する。描画が終わったら作業用仮想VRAMの内容をすべて現状用仮想VRAMにコピーして完了となる。顔パターンをセットするX座標を変えながら、上記のセット+描画処理を繰り返し行えば左から右へ、右から左へ動いているように見えるわけである。
このような仮想VRAMとパターンファイルを使った描画は色々な所で使われている。シナリオプレイ中のメイン画面は後述するようにこのテクニックの応用であるし、「ユーティリティVol.1」でのメインメニュー画面ではこの町の描画とほぼ同じことが行われている。
こぼれ話(3)初公開!死者復活でのバグ
ソーサリアンは全体の規模の割によくテスト、バグフィックスがなされており、PC-8801mkIISR版でも致命的なバグはほとんどない。ただ見えないところにバグは潜んでいる。ここで取り上げる問題もその一つで、しかもバグがいつの間にか仕様化してしまったという珍しいものだ。
冒険でHPが0になって死亡状態になると、他のキャラクタが町に行くことで死者の復活ができる。復活は「魔法使いの家」か「寺院」で取り扱っているので、ここで「死人を生き返らせて欲しい」を選ぶことで、有料もしくは無料で復活させることができる。
問題は誰も死んでいないのに、「死人を生き返らせて欲しい」を選んだ場合にある。試してみればわかるが、この場合キャラクタ選択メニューには誰の名前も表示されず、一番下に脱出用の「やっぱり止めます」という選択肢が表示され、これ以外は選べない。
実はこの動作が既にバグなのである。実際のプログラムでは「死人を生き返らせて欲しい」が選ばれた直後に、トータルの死亡人数をチェックして(死亡人数>0)を選択メニューに入るための要件としている。死亡人数=0のときに「死亡しているキャラクタはいない」旨のメッセージを表示して、選択メニューに入らないルーチンもしっかり用意されている。にもかかわらず、常に選択メニューに入ってしまうのだ。
バグの原因を具体的に説明すると、トータルの死亡人数を数えるルーチンに問題がある。ソーサリアンではキャラクタ数は最大10人になっているので、10人分の死亡フラグをチェックし数え上げればトータルの死亡人数はわかる。ところが、このルーチンではなぜか10人でなく、20人分を数えている。11人目からはキャラクタエリア外で別のデータが入っているので、常に「誰か死亡している」と判定されてしまう。そこでキャラクタ選択メニューに入るのだが、ここで再度、死亡キャラクタをメニュー項目にピックアップする段になると10人のチェックになり、誰も選択肢に入らないのだ。
このバグが明らかにならなかった理由は、仕様的にこういうものも「有り」といえるからだと思う。他のメニュー体系やユーザインタフェースからみると違和感があるのだが、取り立てて不具合があるわけでもないので開発中にバグレポートがあがらなかったのではないだろうか。発売後にバグが発覚したのかもしれないが、その時点で仕様化されたことは間違いない。なぜなら、PC-98,X1turboはもとよりPC-Engineやメガドライブにいたるまで、移植版はすべてこの「常に選択メニューに入る」動作になっているからだ。だからこの裏事情を知っているのは、当時の開発スタッフとプログラムを解析した私だけ、ということになる。
こぼれ話(4)寄進、懺悔、祈りで何ができる?
ソーサリアンで謎として残されている項目の1つに、寺院での「寄進・懺悔・祈り」がある。どれも寺院で使えるコマンドだが、具体的な動作は明らかにされないままだった。非公開としていたのはゲーム性を考えてのことと思われるが、既に発売から15年近くが経過していることもあり、このコマンドによって何がおこるか、どういった特典があるかを正確に説明する。
まず寄進だが、これによる特典は特に無いようだ。拡張シナリオではあるかもしれないが、基本シナリオの範囲ではこれによる条件判断は確認できなかった。なお寄進は年ごと1回限り有効で、年を越してしまうと寄進なしとみなされてしまう。
懺悔はKRMを上昇させる効果がある。これも年に1度限り有効で、KRMがマイナスの場合に1つだけKRMが上がる。KRMが0以上なら変化ない。
祈りは死者の無料復活である。通常は魔法使いの家で有料で復活してもらうのだが、ある条件を満たした場合に寺院で無料で復活させてもらえる。この条件とは次のようなものである。
1)死亡キャラクタのKRMがGOOD(善人)であること。具体的にはKRMが16以上あれば良い。
この祈りカウンタが曲者である。この挙動は次のようになる。
a)100以上には上がらない。
このため無料復活を行うためには、パーティの少なくとも1人がほぼ毎年欠かさず、祈りを続けておかなければならない。この厳しい条件のため、この特典が表に出てこなかったのかもしれない。
128重3D重ね合わせとは
ソーサリアンより約1年前に発売された「ロマンシア」では広告のコピーが「いくつキャラクタが重なってもチラつかない3D感覚の重ね合わせ処理(最大128重完全重ね合わせ)」となっていた。今では考えられない話だが、当時はゲームプログラム技術が急速に進歩した時期であり、こういった技術的な事柄でも十分に「売り」になったのである。
この技術はソーサリアンでも受け継がれ、PC-88上で完全な優先順位つきソフトウェアスプライトを実現している。この仕組みを詳解する。
まず前提条件として、仮想VRAMを用いた差分スクロールの概念を理解する必要がある。これはグラフィック画面を一定の矩形サイズでマス目を入れ、マス目1つ1つで書き換えを考えていく方法である。ソーサリアンではこのサイズを16×8ドットとし、画面全体では640×192ドットとして40×24要素を持つ仮想VRAMで管理している。
また、画面に表示するキャラクタ数(スプライト数)は次のように決まっている。
背景マップ...最大128種類のパーツで表現
具体的なアルゴリズムは次のようになる。
1.作業用仮想VRAMへ、背景マップを展開する。
次の「マップ圧縮の仕組み」とも関連するが、マップデータから1マップデータあたり4キャラクタを取得し、40×24のエリアへ敷き詰める。セットされるデータは0〜127のいずれかになる。同時に128以降の「使用フラグ」を全部降ろしておく。
2.パーツコードの再割り当て、パーツ合成を行う。
ここが肝心なところ。ポイントは「16×8ドットの表示パーツそのものをここで作成する」という点にある。
例として、画面の一番右下(X,Y)=(39,23)にモンスターをセットする場合を考える。
a)(39,23)に入っている仮想VRAM内データを取得する。
さらにこの上に魔法が重なるケースを考えてみよう。
a)仮想VRAM(39,23)に入っているデータを取得する。
これを繰り返し、128以降の番号に対して毎回の表示毎に異なるパターンデータを合成し、番号を割り当てる。パターンデータ合成は最大スプライト数の制限から、最大でも(252-128=124)回で終了する。この時点で重ね合わせ処理済みのパターンデータが出来たことになる。また処理順を考えることで、背景→モンスター→自キャラクタ→魔法という優先度で、後に処理するほど手前にくるように出来る。
3.表示
あとは表示するだけである。仮想VRAMスクロール方式なので、以前の仮想VRAMと比較して違ったところだけ表示すれば良い。ただし128以降の番号は毎回内容が異なることに注意しなくてはいけない。解決法は、「128以降は比較描画を行わず、必ず毎回表示する」である。
仮想VRAM...家庭用ゲーム機などで使われるBG(背景パターン)の考えをソフトでやっている、と考えれば理解しやすい。画面全体をタイル状に分割し、タイル単位で考えていく。なにより速度上のメリットが大きく、8bit機では必須の手法ともいえる。
スプライト...ゲーム等で使われるグラフィックパターン表示機構で、独立移動や重ね合わせなどができるものをいう。パーソナルコンピュータではMSX、PC-88VA、X68k、FM-TOWNSなどに載っているものが有名。これらのハードウェアスプライトに対して、ソフトウェアで構築する場合はスプライト移動時にパターン全部を描き直す必要があるなど速度面で不利になる。
マップ圧縮の仕組み
ソーサリアンというゲームは多くの機種に移植されているが、当初発売されたのはPC-8801mkIISR,PC-88VA,PC-9801,X1turboの4機種である。実際にプレイしてみるとPC-88VA,PC-9801の16bit系機種では結構速いのだが、PC-8801mkIISR,X1turboではスクロールが非常に遅い。
同じ日本ファルコムの「Ys」などのゲームに比べてもスクロールが格段に遅いのはいくつか理由があるが、最大の理由がスクロール範囲の問題だろう。裏を返せば技術的に凄く頑張っているということなのだが、ソーサリアンでは640×192ドット、画面のほぼ全部がスクロール領域となっている。これに対し「Ys」「スタートレーダー」「ドラゴンスレイヤー英雄伝説」などは、どれもスクロール範囲を限定して最大描画量を抑え、それによって速度を稼いでいる。これらのゲームではスクロール以外の部分にステータスなどを表示しているのだが、ソーサリアンではキャラステータスは伸縮式のウインドウで表示しており、全体として仕様は無理目、技術力で勝負しているといえる。
スクロール範囲が広いという事は、マップが広いという事につながる。重ね合わせの項で説明した通り1パーツは16×8ドットで構成されるので、1画面あたり40×24パーツ、1パーツ1バイトとしても960バイトが必要になる。これではあまりにメモリ効率が悪い。メモリマップではマップデータに2048バイトしか割り当てられていないので、2画面少々しか1つのマップファイルに入れることができない。これでは横に長いシーンを表現できない。
そこでマップ圧縮技法が登場してくる。ソーサリアンでは4つのマップキャラクタをセットにして、1つのセット番号で表す手法が使われている。セットにすることで2×2キャラクタ分、ドット数にして32×16ドットを1バイトで表現でき、1画面あたりのデータ数は240バイトにまで減少する。したがって1マップファイルあたり8画面以上を格納できる。
このセットパターン→4マップキャラクタへの変換テーブルは、マップファイルの最後尾を潰して設けている。またマップファイル変換を介在させることで、アニメーション(特定範囲の変換テーブルを一定の周期で回転させる)、優先度処理(特定範囲のマップキャラクタは、最前面に表示させる)などの特殊効果を追加している。
こぼれ話(5)なぜPC88VAで動かないか
ソーサリアンは1987年12月にPC-8801mkIISR版が発売され、その後でPC-9801,PC-88VA,X1turboの3機種が移植版として発売された。PC-9801版、PC-88VA版は8086ベースのコードで、X1turbo版はPC-8801mkIISRと同じくZ80ベースのコードとなっている。
ここで疑問に思うのが、PC-88VAのV2モードでPC-8801mkIISR版が動くのではないか?ということだろう。結論から言うと動かない。これは機種チェック等で弾いてあるのではなく、純粋に互換性の問題で動かないのだ。では何処がまずいのだろうか?
これはメモリマップの項で述べた、一部VRAMの表示マスクに原因がある。これはCRTCを操作し、さらにテキスト文字でグラフィックを隠すことによって実現しているのだが、このCRTC操作に互換性がないようだ。PC-88VAを使ったことのある方はご存じかもしれないが、PC-88VAではテキスト文字のフォントがPC-9801に近い8x16フォントでの表示となる。このことから、CRTCの仕様がPC-8801mkIISRと異なることは容易に想像がつく。
このCRTCアクセス部をスキップするようパッチしてやれば、PC-88VAでも動くようになる。筆者は一度試してみたことがあるが、仮想VRAMが画面下に見えてしまいビットパターンがチラチラしてあまり気持ちの良い物ではなかった。あるいは対PC-88VA専用のCRTCアクセスルーチンを持てば、正常に表示されるのかもしれない。
なお、PC-98DO/PC-98DO+ではこの反省からか、CRTCもPC-8801mkIISRと互換性のあるものが採用されている。これらの機種ではパッチ不要で、問題なく動作する。
シナリオ
ソーサリアンのシステムで最も興味がわくのがこのシナリオシステムではないだろうか?「所詮単なるフラグ立てゲーム」という評価もあるが、基本シナリオ15本だけをプレイしてもわかる通り、ともかく色々なことが出来る。またシナリオデータの構造を探ることは外部シナリオ交換の仕組みと限界を共に知ることになってなかなか興味深い。
まずシナリオデータはマップデータとは独立した関係にあるが、必ずしもそうとは言い切れない部分がある。原則としてシナリオからマップを制御する、と考えておけば良いだろう。
シナリオデータはシナリオコンパイラによって作成され、バイトデータの形でメモリ上に存在する。またシーン番号の概念があり、1つのシナリオファイルに複数のシーンが含まれている。1シーンには4つのエントリがあり、ユーザが「上」「下」「左」「右」のいずれかのキーを押したときに、それぞれ対応するエントリからインタープリット(シナリオデータの解釈)を始める。
コンパイル後のシナリオデータはバイト列になっているが、その動作から逆にコマンド名を類推することができる。筆者が勝手に命名したコマンド名を紹介すると、NEW MAP, NEW SCENARIO, GO SCENE, SET FLAG, GET FLAG, PUT ITEM, GET ITEM, JOIN NPC, SEPARATE NPC, MAKE MONSTER, GET EXP, GET GOLD, GET HARB, PLAY MUSIC, PLAY SE, PRINT MESSAGE, CHECK MEMORY, CHECK POS, PEEK, POKE, CALL, END, EXIT to CASTLEなどのコマンドがある。また実際はメモリチェック・操作系コマンドがかなりの割合を占めている。
シナリオ進行状況は主にシナリオフラグによって管理される。これは256個用意された1か0かのフラグであるが、幾つかはシステムによって使われている。逆にこのシステムフラグを操作したり、取得したりもできるようになっている。また省メモリ化のため1フラグは1bitで扱われ、シナリオフラグ全体でメモリを32バイトしか消費しないしくみになっている。
複雑なイベントはモンスターと組み合わせて処理される。シナリオから特定種類のモンスターを発生できるため、たとえば人物キャラクタを攻撃しないモンスターとして作成し、特定フラグによって設置・除去したりといったことができる。「盗賊達の塔」や「呪われたクィーンマリー号」での登場人物などはこの典型的な例だ。モンスター程度ではできないような更に複雑な処理はPEEK,POKE,CALL文の出番で、POKEによってキー入力ワークを書き換えて強制的にパーティを動かしたり、マップデータを書き換えて壁を作ったりできる。PEEKでモンスターワークを読み取り、モンスターが特定の場所にいたときだけ扉が開くようにもできる。CALLはシナリオデータ中の機械語プログラムを直接呼び出しできる。つまりある意味何でもできるのだ。
このように、「ソーサリアン言語」の言語としての自由度は高い。ただこのアセンブラ的なシナリオ構造では記述に困難を極めたことは想像に難くない。すべてのアクション、イベントにフラグを割り付け、フラグ群のAND,OR,NOTといった組み合わせで識別していかないといけないからだ。インタプリタやシナリオコンパイラの開発もさることながら、各シナリオのシナリオデータ設計と記述は大変な作業だったことだろう。
量が多くなるため全てのコマンドを詳解することはできないが、GET ITEM,PUT ITEMの処理が特徴的なのでこのコマンドの説明をしておこう。これらのコマンドは0〜7のアイテム番号と、0〜5の断片メッセージ番号を取る。このメッセージ番号では表示するメッセージの断片だけを指定するようになっている。GET ITEMでは「とり」「みつけ」「もらい」「ひろい」「てにいれ」「うばい」、SET ITEMでは「おき」「はめ」「わたし」「すて」「いれ」「うばわれ」といった6種のうち1つをはめ込んで表示する仕組みになっている。これ以外のメッセージを出したいときはメッセージ表示なしのビットを立てておき、PRINT MESSAGEコマンドで自前で全メッセージを出せば良い。ただ基本シナリオではすべて組み込みメッセージで処理されているようだ。
これで一通りの事項は説明したが、いままでの説明ではなかなかピンと来ないかもしれない。そこでサンプルとして、「あるアイテムを持っていると、扉の中に入れる。アイテムを持っていないとメッセージを出す」という処理をイメージしてみた。次のような形になるだろう。
−シナリオファイルA、シーン番号n、「上」処理での記述− モンスター
モンスターは次に述べる魔法と共に、ゲームを構成する役の一つとなっている。ソーサリアンでは個性豊かなモンスターが多く登場するが、これに限らモンスターで作られているシーン、効果は数多い。シナリオの項で述べた登場人物もその1つだし、「消えた王様の杖」での天井から落ちる滴や「暗き沼の魔法使い」でのダメージを食らう泡もモンスターで作られている。
これらの様々な動きを実現するため、モンスターはパラメータとプログラムの2つによって構成されている。たとえば速度、攻撃力(プレイヤーがどのくらいダメージを受けるか)、グラフィックパターン番号、自然発生するかのフラグ、空を飛べるかなどのフラグといった基本的な条件はパラメータで静的に規定され、主に動きにあたる部分がマシン語で書かれた個別プログラムとなっている。パラメータとモンスタープログラムは同一ファイルに格納され、ファイルの先頭にモンスタープログラムへのジャンプテーブル、その次にパラメータ、その後の残り領域にモンスタープログラム実体という構成になっている。
モンスター種別は通常シーンで最大16種類まで、ボスシーンで最大16種類までとなっており、これら16種類のパラメータ+プログラムを、1024バイトのモンスターファイル内に収める必要がある。このサイズ制限は結構つらい制約で、長いシナリオでは上手に組んでいかないとすぐメモリオーバーしてしまう。X680x0版の移植にあたっては、この制約の難しさと68000コードによる長さのオーバーヘッドを考慮して、サイズを4096バイトに拡大した経緯がある。
モンスタープログラムでのトピックは2つ、自己登録モンスターと外部コールがある。
自己登録モンスターというのはその名前の通り、モンスタープログラム処理で更に別のモンスターを発生させる処理である。通常モンスターはシナリオデータのMAKE MONSTERか自然発生フラグによる常時生成で作られるのだが、自己登録では強制的にワークエリアを書き換えモンスター登録状態を作り出してしまう。
このテクニックでの応用範囲は実に広い。たとえばボスモンスターが炎を吐く場合を考えてみよう。処理としてはボスモンスタープログラムで吐きタイミングを判断し、タイミングよく炎モンスターを自己登録すれば良い。横に長い炎は分割してたとえば10モンスター程度で構成し、炎モンスタープログラムでは炎自身の座標が親(ボス本体)に追従するようプログラムする。炎モンスターそれぞれに生存カウンタを設定し、カウンタが0で自滅するようにプログラムすれば一定時間炎を吹いて消えることになり、バッチリだ。あとは炎の当たり判定(こちらの剣・魔法が当たるかの判定)パラメータをFALSEにしておけば良い。またボス側ではボスの消滅するタイミングで生きている炎を全部殺すようにしておかないと、炎だけ残って変なことになってしまう。
このように応用例はボス弾が多いが、それ以外にも色々な所で使われている。「消えた王様の杖」のオークが落とす岩や、「ルシフェルの水門」滝シーンでの岩、「ロマンシア」「囚われた魔法使い」の飛んでくる溶岩などである。前にも述べたがシナリオデータと連携させることで、複雑な地形効果・仕掛けを表現できる仕組みになっている。
もう1つのトピック、外部コールは移植の際に泣かされた仕組みである。モンスタープログラムエリアが狭いことは既に述べたが、それをカバーするために差し替え式であるはずのモンスタープログラムからメインプログラムを絶対番地でコール、ジャンプすることがごく当たり前に行われているのだ。メインプログラム側でもそれは意識されており、なんと本体では決して使われず、モンスタープログラムから呼ばれることを前提に置かれているサービスルーチンまで存在する。
X680x0への移植にあたっては、まずこれらのコール、ジャンプ先アドレスの分析、絞り込みを行った。また絶対番地コールと同等の呼び出し感覚を実現するため、サービス番号+特定アドレス呼び出しで処理が行えるようにシステムコール風のルーチンを準備し、記述を簡単にするためアセンブラマクロまで準備した。余談だがこのマクロ名はCALLとJPとした。そのままである(笑)。ただ実際には新しいシナリオ、新しいモンスタープログラムの移植を進めるたびにシステムコールを拡張する必要があり、とにかく大変な作業だったことは良く覚えている。
さてX680x0版での苦労話はここまでにして、気になるのはオリジナルでの作成方法である。絶対アドレスがそのまま書いてあることから、やはりメインプログラムを含めて、複数のORGを挟んで一括アセンブルまたはリンクし、後からモンスタープログラムエリアだけを切り取ったとしか考えられない。ただ考えてみればわかるが、この方式ではメインプログラムに少しでも変更が入ると全部のモンスタープログラムのビルドをやり直さないとアドレスがずれてしまう。このため、実際にはもっと上手い方法があったのかもしれない。それは私にもわからないが、ちょっと気になるところではある。
ジャンプテーブル...アセンブラ(マシン語)を知らない方はちょっとイメージがつきにくいかもしれない。番号によって処理が何種類にも分かれる場合、いちいち「0だったら...」とか「1だったらとか...」と比較を連続させておくのではなく、0の場合、1の場合、2の場合の順番に入口アドレスを2バイトづつ書いておけば、計算によって該当処理の開始アドレスを知ることができる。
マクロ...ここでは記述を容易にするための別名定義という意味。CALLとJPはどちらもZ80の命令で、68000ではこの名前の命令は存在しない。このためこの名前を割り当てた。
複数のORG...ORGはアセンブラに対する指示(疑似命令)で、ロケーションカウンタと呼ばれる、この行以降の配置アドレスを設定する。複数のORGが設定できるアセンブラを使えば、メインプログラムのサイズ変更に関係なく決まった位置にモンスタープログラムが配置されることになる。
魔法
ソーサリアンというゲームを彩る役の一つがこの魔法である。パッケージや広告などでは「120種類の魔法」と宣伝されており、ユーティリティVol.1を使うとさらに種類を増やすことが出来る。魔法を使うにはアイテムに”星をかける”操作が必要で、この法則が複雑なため魔法作成法がまたプレイヤーの興味を引くことになった。
魔法システムの仕組みを説明すると、まず星と魔法との間に直接的な関係はない。星をかけることで魔法がかかるのは、内部で星ビットパターンから魔法データへの変換テーブルを使っていることによるものだが、何らかの方法で(ユーティリティVol.1を使うなど)直接魔法データを作ってやれば魔法を使うことができる。
この魔法データは細分化されたパラメータの集合であり、動き、効果、消費MP、弾グラフィック番号、貫通フラグなどから成っている。町プログラムなどには前述した魔法パラメータテーブルがあり、「魔法使いの店」などで星をかけて魔法を作る場合に一括してパラメータがセットされる。このためセーブデータを直接書き換えれば、個別データを操作して今までに無いような魔法を作ることもできる、たとえば「レーザのグラフィックキャラクタで、動きは誘導弾で、効果はHEAL」などというとんでもない物も作れるし、しっかり動作する。ただこれではあまりに現実味に欠けるため、もっともらしい組み合わせのものをデフォルトで120種類準備している、というのが実際のところだ。
動きや効果については、有りとあらゆるものとは行かないにしろかなり豊富に取り揃えられている。動きは単純な直進型に始まり、THUNDERで知られた雷や、ナパーム弾のようなものまである。効果はモンスターにダメージを与えたり、攻撃力を落とすものは当然として、モンスターが発火する、変わったところではモンスターの生命力が2倍になる、パーティ先頭のキャラクタが若返るなどのバリエーションを持っている。
また「128重3D重ね合わせとは」の項で述べたとおり、魔法のスプライト数は最大32スプライトに制限されている。これは32発を超える弾は画面上に出すことを出来ないということを意味しており、スプライト数に余裕がない場合は弾を減らして動くことになる。
以上が魔法システムの仕組みだが、ここでも設計が正攻法かつ柔軟になされていることが良くわかる。このようなアプローチをとらずに120種類の動作を固定してコツコツ作っていくのは不可能ではないのだが、非効率な上にメモリも沢山必要になる。それならば今まで述べたようにパラメータごとの処理を実装(インプリメント)し、そのデータセットで駆動(データドリブン)した方が効率が良く、後々の拡張も容易にできる。その思想に基づいてシステムを作り上げ、設計・構築していった力には恐れ入るしかない。
ソーサリアン・システム
拡張シナリオ・ユーティリティVol.1が発売された頃から、「ソーサリアン・システム」という言葉が雑誌広告などに見られるようになった。ソーサリアンというゲームを象徴する言葉でもあるが、いったいこれは何だろうか?
筆者はソーサリアン・システムの定義として、ソーサリアン+基本シナリオを広い意味でOSとして扱えることだと捉えている。プログラム上はBIOSが整備され、城からユーティリティコマンドで別プログラムを起動することができる。またOSがサポートする資源として、ユーザキャラクタ・パーティがあり城や町でのメンテナンス機能を提供している。
こう考えると、ソーサリアンで育て・冒険させたキャラクタを使って、全く別のゲームをプレイすることも十分考えられる。実際、ユーティリティVol.1に入っている「ソーサリアンすごろく」はミニゲームながら一つの可能性を示した作品であり、ARPGの枠にとらわれない本格的なゲームが登場する予感さえ感じさせた。
ただ実際は周知のとおり、ユーティリティはVol.1が最初で最後のものとなり、拡張シナリオも「ピラミッド・ソーサリアン」が若干の独自性を打ち出したのみで他は並列的な追加にとどまっている。これはやはり営業上、採算上の理由によるものではないかと考えている。
つまりソーサリアンの拡張として出していく限りソーサリアンユーザ以外の取り込みは考えられないし、値段もある程度安くせざるを得ない。逆に開発コストが急激に下がるとは考えにくいため、なかなか儲けが上げにくい構造になってしまったのではないだろうか。そうなると企業経営の観点からは、他の新作ゲームに開発資源を投入した方が得策という結論になってしまう。これが開発コストのかかるユーティリティが出せなかった原因だと思う。
OS...オペレーティング・システム。現在のPC上でよく使われるOSはWindowsを初めとしてLinux,FreeBSD,Solaris,BeOSなどがあげられる。これらのOSではウインドウなどの画面要素や、ユーザ、ネットワークなどもOSのサポートする資源であり、アプリケーションに対して色々な仕事をサービスしている。これに対してかつての主力OS、MS-DOSではOSは主にファイルシステムのみを管理し、画面などはアプリケーションからの直I/O操作に任せていた。このためDOS(ディスク・オペレーティング・システム)と呼ばれる。
こぼれ話(6)4MHz、8MHzの違い
PC-8801mkIISRシリーズにはよく知られたとおり、Z80互換のμPD780C-1が載ったクロック4MHzのもの(SR,TR,FR,MR)と、そのCMOS版であるμPD70008が載った8MHzのもの(FH,MH以降すべて、ただしVA系はμPD9002に変更)とがある。ソフトによってはこれらを区別して、処理を変えているものがあった。たとえば「ファイアーホーク」(ゲームアーツ)では4MHz動作時は速度を稼ぐため背景表示をカットしていたし、「プラジェータ」(エニックス)は最初から8MHz機のみ対象として、4MHz機ではメッセージを出して起動しないようになっていた。
ソーサリアンではこれらのソフトほど凝った分け方はしていない。勿論グラフィック描画が重い箇所を中心に速度差が出てくるのだが、ソフト側でクロック判別して...という事はメインプログラムでは行っていない。ただ唯一クロック別で処理を分けているのが、エンディング部である。
エンディングを覚えている人はどのくらいいるだろうか?洞窟が背景、右側にキングドラゴンが居て中央にパーティが整列する。そして一番手前にシナリオ名+登場人物、制作スタッフの順にメッセージがスクロールしていく。ここで一番奥に、オープニングと同様の星が流れるのが8MHz時、流れないのが4MHz時の動作となる。
この星が流れる処理はオープニング、エンディングとも星の数を除いてほぼ同じである。これはかなり重い処理で、オープニングでは音楽のテンポがもたつく程である。あるいは遅くなることを想定して、最初からテンポ早めの曲作りをしたのかもしれない。
「ソーサリアン・ミュージックギャラリー」なる音楽ディスクを持っている方も結構いると思うが、この画面でソーサリアンのタイトルロゴしか出ていないのはこういう事情がある。星を流すと他の曲もすべて遅くなってしまうからだ。
なお蛇足になるが、X68k移植の際もこの星の処理は苦労した箇所の一つで、なるべく速度を稼ぎかつPC-88と同じ流れ方になるように、両方のモニタを睨めっこしてデバッグしたのが記憶に残っている。
最後に
まだ書き足りないところはあるが、一通りのトピックについては網羅できたと思う。本稿で述べた技術はハードウェア・ソフトウェア双方の技術進歩にもかかわらず現在でも応用が効くものが多いと思われる。
私事で恐縮だが、筆者にとってもソーサリアンの解析というのはここ10年のなかで最大の技術トピックであり、筆者がプログラマーとして大きく伸びるきっかけとなったと感じている。ソーサリアンで木屋 善夫氏から、各種同人ソフトでたいにゃん氏から、多くの事を教えていただいた。いずれも直接教えを乞うた訳ではないが、洗練されたプログラムの解析を通して学んだことは多い。今はWindows時代になりアセンブラで書かれたプログラムを解析するという事はかなり少なくなっているが、プログラミングの本質はどの時代、どの言語でも変わらないと信じている。本稿がこの本質を知る手がかりとなり、「本物のわかるプログラマー」を生み出すきっかけになることを願ってやまない。
(以上)
ソーサリアンに関連するすべての画面写真、パッケージ写真の著作権は株式会社日本ファルコムに帰属します。
ENTRY:
LD A,0
AND A
RET NZ
...
LD A,FFh
LD (ENTRY+1),A
RET
(1)SSGのソフトウェアエンベロープ
(2)LFO機能
があげられる。
No.1 ... No.2〜No.4の結果を合成したものと、No.0との比較結果
No.2 ... 奥のレイヤー
No.3 ... 中間のレイヤー
No.4 ... 手前のレイヤー
2)町にいるキャラクタの祈りカウンタが10以上あること。
b)「祈り」コマンドで1年に1回だけ+1される。
c)「祈り」をしなかった年は、年越し処理で−1されてしまう。(不信心?)
モンスター...全モンスター(モンスターの出す弾なども含む)で最大68スプライト
自キャラクタ...横2スプライト、縦3スプライト、Max4人→最大24スプライト
魔法...自キャラクタが使う魔法に限る。最大32スプライト
すべて合計すると128+68+24+32=252となり、256以下であることに注意して欲しい。鋭い方はお気付きのことと思うが、この数字に「完全重ね合わせ」と言い切る鍵がある。
b)そのデータのパターンデータ(16×8ドット8色)とモンスターのパターンデータを合成し、新しい表示パターンを作成する。これを128以降の、使われていない番号へ割り当てる。(ここでこの番号を使ったことになる)
c)仮想VRAM(39,23)にこの割り当てたパターン番号をセットする。
b)先程合成したパターンデータ(16×8ドット8色)と魔法のパターンデータを合成し、新しい表示パターンを作成する。これを128以降の、使われていない番号へ割り当てる。
c)仮想VRAM(39,23)にこの割り当てたパターン番号をセットする。
CHECK POS(扉X座標、扉Y座標)
IF NOT
END
END IF
CHECK FLAG (アイテム所持フラグ番号)
IF NOT
PRINT MESSAGE "はいれません"
END
END IF
NEW MAP(扉内マップファイル名)
NEW SCENE(扉内シナリオファイル名、シナリオ番号)
END