続・PCエンジン時代の圧縮
8ビット時代の圧縮の話をブログに書いたところ、いろんな人が「俺も使っていた」とlzssについてゲロっているのを読んで笑ってしまったのだけど、ちょっとここで、つぶやかれていた質問「どうしてファミコン時代には圧縮がほとんど行われていなかったのか?」について書いておきたい。
初期のファミコンではキャラクタの圧縮は全くできなかった。なぜならキャラクタは別のROMとして搭載され、直にPPUがアクセスする形式だったからだ。
コレについて詳しく説明するのは面倒くさいので『ファミコンメガロムの開発とPCエンジンのアセンブラ』という、僕と『ZANAC』とかPCエンジンの『ガンヘッド』とか書いた広野さんの話のまとめでも読んでほしい。
ともかくPPU側がキャラジェネに直接アクセスする形式だったので、初期のファミコンでは圧縮はマップしかできなかったのだけど、これまた厳しい制限があった。
というのも、圧縮されたものを展開しておいておくためのRAMがさっぱりなかったからだ。
ファミコンのプログラム側で使えるRAMはたったの2KB(2048バイト)。そのうちの256バイトがゼロページ(というものがあると思って欲しい)で、256バイトがスタック。残りは1536バイト。
ここですでに25%のメモリを失う。
今のゲームを作ってる人には想像もできない数字だろうが、本当にそうだったのだ。
で、マップを圧縮すると考える。
ファミコンの世界は1セル、すなわち四角の4キャラクタを単位として扱う必要がある。これまた説明するのはクソ面倒な話で、一度『スターソルジャー30周年記念(3)』で説明しているので読んでほしい。
ファミコンは横の1ラインは32キャラクタなので、縦スクロールするゲームだと32*4=128バイト。カラーアトリビュートを除いても、残りの1536バイトのうちいきなり8%ほどのメモリを使ってしまう。
加えて、例えばスターソルジャーみたいなゲームを作ったとして、敵が最大64体出るとすると、最低(X,Y,敵のコード)が必要なので、これで192バイト。またまた10%ほどメモリがなくなってしまう。
と、こんな風に恐ろしくファミコンはワーキングRAMがプアなので256バイト、すなわち12.5%もメモリをバッファに使う圧縮(展開ルーチン)など、とても使えるような代物ではなかったわけだ。
ほんとにファミコン時代のワークの足りなさはとんでもなくて、挙句には下のような騒ぎになる。
■飛田さん
アトリビュートのコピーの128バイト?はスタック領域の後ろを使ってました。
スタックエリアの後方あたりになると「まあ使わないだろう」という範囲で、事故が起こるリスクがあるところなんだから、フツーなら使おうなんて思わないだろうが、ファミコン時代はもちろん使うことになったわけだ。
ちなみにPCエンジンで搭載されているワーク用のRAMは8KB、8192バイトで、ファミコンと比べれば圧倒的に楽だった。これなら256バイトのバッファも使えるというものだ。
あと、前の話で簡単に書いていたけれど、全く説明しなかったので、ロードランナーで使っていたと野沢さんが言っていたビットパックについて解説しておきたい。なぜビットパックについては説明するのかというと、これまた8ビット時代の超セコいテクニックがあるからだ。
中本さんが移植したロードランナーでは空白・梯子・棒・金塊・床・棒・プレイヤー・敵の7種類の状態がある。7種類ということは3ビットで表すことが出来る(プレイヤーと敵は座標で置かれている可能性もあるが、ソースは読んでないのでわからない)。
ということは、1バイトは8ビットなのでマップの2マス分のデータを1バイトの中に詰め、かつ2ビット余る。
要は種類に必要なビット幅にして、それをビット単位で詰め込めば小さくなるって話だ。
例えば3ビットでいくと、オリジナルのAPPLE版はたぶんマップは1バイトなので3/8で、37.5%に縮むことになる。ディスク128KBがいっぱいだったと考えても37.5%に縮むと48KBになり、全く入らない、とは言えない範囲にいきなり縮む。
ところで2ビット余るのは、もちろんもったいないので詰め込みたいが、ビットストリームで扱うと、めっちゃ速度が遅い。ビット操作はコストが高いのだ。
で、どうするのかというが下の図。
最上位1ビットを8つ集めて、1バイトにし、2ビットを8つで2バイトにすると、単純なキャリー付きのシフト操作で簡単にデータを切り出すことが出来、かつ、あまりなくデータをビットに詰め込むことが出来るから、この方法なら24bitのデータに8つのマップデータを低コストに突っ込むことが出来る。
こんな話で分かったと思うけれど、当時のゲーム開発者にとってはデータ圧縮はとても大事で、まあ一生懸命詰め込むものだったわけだけど、ここで『天外Ⅱ』を作っていたとき、桝田さんから聞いた爆笑エピソードを、たぶん、世の中でこの話を知っているのは数人しかいない話なので、紹介する形で残しておきたい。
当時、桝田さんは『天外Ⅱ』を作りつつ、DECO(データイースト)で、いろいろなゲームの手伝い…というか顧問みたいなものをやっていた。だから野島さんの話なんかを聞けたわけだが、そんなある日、桝田さんが面白そうに圧縮の話を始めた。
「この前DECOでさ、プログラマーが新しい圧縮を思いついたってわけさ」と桝田さん。
「へえ、どんな方法なの?」とは僕。
「そんなの僕にわかるわけないじゃん。ともかくすごい圧縮だってわけよ」
「ふうん」
「で、圧縮サイズが表示されるようになっていって、これが遅いんだけどさ、どんどん数字が小さくなっていくわけさ。それでみんなで、おおっ、おおっってなってて…最後はなんとサイズ0になって、全員爆笑」
僕、爆笑。
■追記
誤解している人がいるので、ちょっとだけ追記すると、ファミコン時代でも、もちろんビットパック以外にランレン(Run-Length法)など、メモリ負荷が低い圧縮は使われていた。
ただそういう圧縮は圧縮率が悪いという問題が常につきまとうので、圧縮が本格的に使えるようになったのは、ワーキングRAMがそれなりに豊富で、なおかつ今でいうテクスチャメモリがRAMになったPCエンジン・メガドライブ・スーパーファミコンの世代になってからだったということだ。
また綾部和さんの話によると、ROMの上にスライド辞書を固定した、なんちゃってスライド辞書法を使って圧縮していたこともあったということだ。