脱衣ゲーム基板でイースIIの曲を流してみた

動画のタイトルとサムネがネタの全てという出オチ以前の動画ですが、ここでは技術的な補足をしてみたいと思います。

今回使った華弥生というゲーム基板は、CPUにZ80、音源チップにYM2203Cを搭載していて、この2点について見れば、NEC PC-8801mkIISR(以下、8801)と互換があると言え・・・なくもないです。実際には色々違いますが。

華弥生はダイナックスから1986年頃に発売されたゲームです。アーケードゲーム市場ではジャンク品扱いに近く、数百円程度で取引されているようです。だからといって音源チップ目当てで買いあさると絶滅しちゃうかもしれません。

DAとADPCM

YM2203CのDAにはY3014が使われていて、華弥生のおしゃべり機能はOKI msm5205でADPCM再生されます。

音楽再生のプログラムには8801版イースIIを流用しています。あっと、もちろん、オリジナルのディスクを所有していますヨ。
8801イースIIパッケージ
そういえば、これを使ってペーパークラフトを作ったっけ。

そんなわけで、著作権上の理由によりEPROMに書き込んだプログラムおよび曲データの配布は出来ません。また、元のプログラムをあれこれと書き換えていてパッチ当てが面倒なので、差分の配布も出来ませんが、こんなのを欲しがる人はいないと思います。

8801とゲーム基板が同じCPUを使ってるといっても、そのままではプログラムを実行できません。

フロッピーディスクからプログラムを読み込んで実行するパソコンのプログラムと違って、ゲーム基板は、あらかじめ書き換え不可能なROMにプログラムが焼き込まれていて、そのままプログラムを実行します。ファミコンのカセットゲームと同じですね。

8801はフロッピーディスクから本体内のRAM領域にプログラムを読みこんで実行するので、プログラム自体がRAM領域にあるということになります。これをうまく利用してプログラムの実行中にプログラム自体を自分で書き換えるというプログラミングテクニックがあるのですが、ゲーム基板の場合、プログラムはROMにあるので書き換えができません。
イースIIでも、このプログラム自体を書き換えるというプログラミングテクニックが使われているので、8801で動いているイースIIのプログラムをそのままゲーム基板のROMに書き込んでも正しく動作しないのです。
ゲーム基板にもRAMがあるので、ROMのプログラムを一旦RAMにコピーして実行すればいいのではないかと思うのですが、ゲーム基板のRAMはとてもとても小さいのです。

また、メモリの構成が違います。8801ではZ80メモリ空間を全てRAMにしたり、メモリ空間の一部をVRAMに切り換えたりすることが出来て、イースIIのプログラムもそれに合わせて作られていますが、この華弥生の基板ではメモリ領域のほぼ全てがROMで、2KByteだけRAMになっています。
もちろん、I/Oポートのアドレスも違いますし、割り込み発生源も発生方法も違います。

そんなわけで色々とプログラムの修正が必要になるのですね。

移植手順としてはベタなやり方ですが…

  • PC-8801mkIISR版のイースIIのオープニング部分のプログラムを取り出します
  • 逆アセンブラを使ってソースコードを生成します。イースIIではデータ領域を上位バイトと下位バイトに分離していて参照している箇所があって、これが8801固有のメモリアドレスになってしまうので、なんとかして探して書き換えます
  • 自己書き換えしているプログラムを修正します。これも書き換え箇所の特定が面倒なのですが、Z80エミュレータとかシミュレータで書き換えを検出するとかします
  • プログラムとワークエリアが混在している箇所を綺麗に分離します。プログラムはROMに書き込み、ワークエリアはRAMを参照するようにするためです

ちなみに8801版イースのプログラム切り出しはhootを参考にさせて頂きました。ありがとうございます。未だに8801の事はよくわかってませんがCPUが同じならなんとなく。

で、ここまでは簡単だったんですけどねー…

この状態でエミュレータ上でプログラムを実行してみたところ、最初の方は曲が流れたのですが、徐々に変な曲になってしまうのです。
曲がおかしくなったところでプログラムを停止してメモリ内を確認してみて気がついたのですが、曲データの中身を、曲の再生中に書き換えている事に気がつきました。曲データもROM領域に置いてあり、曲自体の書き換えができずに正しく再生されなくなってしまったのですね。曲データは4KByteほどなのですが、それでも華弥生基板のRAM領域には収まりませんから、曲データだけをRAMに置くこともできません。

曲データの構造を調べてみたところ、曲のリピート部分でリピート回数を曲内部に書き込んでいるようでしたから、その処理を書き換えることにしましたが、まーこれが調べるのも書き換えるのも面倒でした。曲の再生プログラムだけでなく、曲データ自体にも手を加えることに。
また、リピート回数のチェックだけでなく、リピートの最後にリピートの途中で抜け出す処理にも手を加えてあります。要するに、MDXのMMLでいうところの…

[~]# ‘[]‘で囲まれた間を指定回数だけ繰り返します。範囲2~255。
/ ‘[]‘の中を演奏中、最後の繰り返しで’/'以降のデータを無視します。

この処理を行うのに、曲データ内に直接リピート回数を書き込んでいるという仕組みなのですね、合理的。
さらに曲データ自体がZ80メモリ空間の固定アドレスに配置されるようになっていたので、その箇所も書き換えています。

最初の動作確認とデバッグは、ハードウェア構成が似ていて使い慣れているPC-6001mkIISRエミュレータ上で行いました。つまり、PC-6001mkIISR/6601SRでもイースIIの曲が再生できていることになるのですが、これについては、bookwormさんがインタフェース付きのすばらしいプログラムを作られています。

8801と華弥生基板ではハードウェアレベルでの大きな違いが2つほどあります。

その一つは、Z80割込み方法です。8801ではYM2203Cから割り込みがかかるのに対して、華弥生基板ではYM2203Cからの割り込みが取れません。また、華弥生基板では8KHzタイマー割り込みと垂直同期割り込み(約1/60秒間隔)の2つの割込みの2種類しかなく、イースIIで使われているYM2203CのTimer-B割込みとは間隔が異なるのです。これはYM2203Cのステータスを常時監視する方法で対処しました(ポーリング)。これが原因で、曲の再生中は曲の再生以外の事がほとんどできないゲーム基板ということになります。もちろん、曲の再生プログラムを8MHzタイマーを使ったものに書き換えれば、本来の基板の性能を発揮できますから、この辺は手抜きです。

もう一つは、YM2203Cに供給されるマスタークロックの違いです。マスタークロックはタイマー割り込みの間隔や音の音程に影響を与えます。8801のYM2203Cには4MHzのクロックが供給されていますが、華弥生基板では1.25MHzとなっています。この違いも曲の再生プログラムで吸収すればいいのですが、YM2203のTimerや周波数の設定値の資料が見当たらなかったので(みんなどうしてるのかな?)、基板のYM2203を外して子亀基板を乗っけて供給クロックを切り換えるように改造しました。オマケにYM2203だけを取り外して別の事にも流用できますし。

YM2203C

ちなみに華弥生はMAMEでも動いているのですが、ソースをみると、Z80への供給クロックが5MHz、YM2203Cへは2.5MHzになっていますけど、これはそれぞれ2.5MHzと1.25MHzが正しいようです。基板にオシロつないで調べましたし、初めて基板上で曲を流した時に、ものすごく重くて遅い曲になったのでこれは何かが違うなーと。Z80を5MHzで動かすにはZ80BかZ80Hが必要なのですが、基板上のZ80はノーマルタイプっぽいので変だとは思ったんですよね。

水晶振動子 Z80

MAMEのソースについていえば、DIPスイッチ周りが実装されていなかったり、この基板のRAMはバッテリーバックアップされていてハードリセットも可能ですが、これも実装されていないようです。
とはいっても、今回のデバッグではMAMEのデバッガを何度も利用させてもらいましたから、先駆者の方々には本当に頭が上がりません。せっかくなので華弥生基板のDIP表をUpっておきます。

DIP表

さて、動画でもわかるように、曲を流すだけでは寂しかったので、8801版のオープニングデモ画像を流用して表示してみました。このゲーム基板の描画機能がなかなか面白くも面倒でもあったのでまとめておきます。

VRAMはbitmap構造なのですが、CPUであるZ80側から描画IC側にある画像データ(VRAM)に直接アクセスする事ができません。CPU側からは描画ICに対して、「この位置(ソースアドレス)からの画像データをこの座標に表示セヨ」というコマンドを送る事しかできません。Z80と描画ICはI/Oポートで薄くつながっている感じです。表示できる画像は矩形データで、ソースアドレスというのは描画ICに直結されたグラフィックデータROMのアドレスです。

この仕様では、CPU側から画像データを送ったり、グラフィックROM内の画像データを書き換えるということが出来ないので、あらかじめ決められた画像データしか表示できません。ゲーム基板では、画像データを書き換えられないのは珍しいことではないのですが(大抵のゲーム基板はスプライトデータを動的に書き換えられないのと一緒)、せっかくのbitmap構造なのに任意の位置に任意のデータを書き込めないのがもったいないというかなんというか。

それに加えて、ICに繋がれた画像データはBitmap構造ではなく、圧縮された画像形式になっています。圧縮形式はランレングス圧縮です。同じ色が横に何個並んでいるかを記録するという方式で、例えば色が2色で、それぞれを0と1で表すとしたら、11110000という8ドットの画像は、1404みたいな感じで、色番号+個数+色番号+個数というデータになります。
同じ色が並んでいた方が圧縮効率がよくなる反面、ドット毎に色が違うとデータ量が増えるという欠点もあります。

この基板では16色が使えるので色情報が4bitです。長さの情報は11ドットまでは4bitを使っていいので、長さ11ドットまでの横線は1バイトで表せます。つまり、横方向に同じ色が並んでいれば、1ドットも11ドットも1バイトで表せます。
12ドット以上の場合には、別の方法で表現することもできて、色コード4bit+0x0Cという1バイトと続く1バイトの合計2バイトで最長255ドットまで表現することもできます。つまり、0から0x0b(10進数で11)までで表す方法と、0x0C(10進数で12)を使う方法があるのですね。この0x0Cというのがコマンドのようなもので、他にも0x0D, 0x0E, 0x0Fといったコマンドがあり、指定位置から横線を描く、描画ページを変更する、改行する、といった機能があります。
指定位置から横線を描くという機能を使うと、すでに描いてあるドットを飛び越える事ができるので、透明色のように使えます。

同じ色が横に並んでいるほど圧縮効率がよくなる、ということは、1ドット毎に色が違うと圧縮効率は最悪になります。1ドットを1バイトで表すとなると、320×240ドットの画像は150KByte・・・かな。これはZ80メモリ空間に収まらないどころか、1Dのフロッピーディスク1枚をほぼ使い切っちゃいますね。

1ドット毎に色が違うと最悪、ということは・・・8801のタイルペイントとの相性が最悪だー!ということに。

ただ、このゲーム基板は面白い仕様になっていて、2枚の画像レイヤーを1ドットずらして表示することができるみたいなのです。どうやら、2ドットを一度に描くか、左のレイヤーにだけ描くか、右のレイヤーにだけ描くかみたいな指定ができるっぽいです。どういうことかというと、左レイヤーだけに描くように指定してから、圧縮された画像を展開すると、1ドットおきに絵が描かれます。続けて同じラインに、右レイヤーだけに描くように指定して画像を展開すると、先に描いた左レイヤーには影響せずに、1ドットおきに絵が描かれることになって、1ドットずれた2つのレイヤーが表示されて、これで1ライン分の画像ができます。つまり、2色が交互に現れるタイルペイントとの相性は最高に良いのですね。
この基板仕様は3ドットタイルペイントの相性が最悪ですし、ドットをバラバラとちりばめて打ってあるようなイースIIのタイトル画面はどうにもなりません。
また、改行コードを使えばすだれ表示になりますし、表示開始位置を1ライン下にして同じ絵を転送すれば2ライン同じ状態の絵を描けるので、8801の240ラインを480ラインに見せかけるような事も可能です。

最初、このへんの仕様をわかっていなくて、タイルペイントだと圧縮効率が悪くなるものだと思い込んでいたので、ドットを打ち直した画像を用意したのですが、

ベタ塗り

無駄になってしまいました・・・。

素直に8801版のパレットを置き換えるとこんな感じです。

88系カラー

華弥生基板は16色パレットが6本あって固定です。さすがに脱衣系の基板だけあって、お肌の色が三色も用意されていますが、反面、水色や黄色のバリエーションが少ないのが厳しいです。

パレット

黄色も肌の色だと割り切って置き換えるとこんな感じになります。

色調整

華弥生の基板性能としては、がんばればYSIのミネアの町の中を動き回るくらいまでは再現できるかもしれません。
音楽再生用基板としては、音声の再生にOKIのmsm5205が搭載されているので、YM2203C+msm5205の組み合わせを考えると、華弥生基板を2枚使って、ダライアスのステレオ再生も可能かもしれません。
あと、基板上のYM2203CのI/Oポートがいくつか空いているようにみえるので(端子をちゃんとおっかけていませんが空きDIPソケット用っぽい?)、ここを使えばPCとかとUSB経由でデータのやり取りが可能かも。
もちろん妄想レベルなのでやりませんけどネ。

コメントは受け付けていません。