MSXのドラクエIをSEGA MarkIIIに移植してみようかなーと思った話

オチを先に書くと移植はしなかったのですが、その結論に至るまでの記録です。

■ SG-1000 30周年

2013年7月15日は、SEGA SG-1000の30周年だったそうですが、当日まで知りませんでした。
n周年記念には何か仕掛けたくなるものですネ、特に根拠はないですが。とはいっても、当日に何かをするのは無理だよねーと思いながら、だったらSG-1000から2年後に発売されたSEGA MarkIIIの30周年に合わせてなにか出来るかも、とボンヤリと考えてました。まぁ、2年後も先の事ですし、MarkIIIの仕様を調べながら何をやるか考えればいいかなーと。この時点では、MarkIIIの仕様はほとんど知りませんでした。

MarkIIIの仕様を確認しながら、オリジナルのプログラムを作ってデバッグするのは荷が重いから、やっぱり他機種からの移植かなーだとしたら何がいいかなーと考えてみると、やはりハードウェアレベルで互換性の高い機種からの移植がよいかなーと。となると、Z80でPSGでTMS系のVDPを搭載したハード・・・MSX!

MSXのゲームは豊富で何を移植しようか迷いますが、知名度があって、解析が面白そうなものでぱっと思い付いたのが、「ドラゴンクエストI」でした。エニックスがMarkIII用にドラクエを販売していたら、ゲーム界の歴史はどうなったのかなーと妄想するのも楽しそうではないですか。

ちなみにこの時点では、MSXの仕様もほとんど知りませんでしたので、MSXの仕組みを調べながらドラクエのプログラムを眺めながら、途中でRaspberry PiにPSGを接続してドラクエの曲を流したりしてました。

■ なぜSG-1000用にしなかったのか?

そもそもMarkIIIの発売30周年に合わせたから、というスタートなのですが、ハードウェアの似てるさ加減でいうと、初代MSXはSG-1000と非常に似ています。かといって、MSXからSG-1000への移植はし易いのかというとそうでもありません、というか、かなり無茶な話だと思います。

SG-1000 MSX
CPU Z80A 3.579545MHz Z80A 3.579545MHz
VDP TMS9918 TMS9918
SOUND SN76489 AY-3-8910
RAM 1KByte 8~64KByte

移植が難しい理由は搭載されているRAMの容量の違いで、これはソフトウェアでどうにかなる差ではありません。

こうなると、自動的にSG-1000の上位機種であるMarkIIIを使う事になります。

MarkIIIはSG-1000のソフトも動作する上位機種という位置付けなので、RAMの容量が8Kbyteあります。これならばSG-1000に似ているMSXからMarkIIIへの移植は、上位スペックマシンへの移植というように思えます。ただ、MarkIIIがSG-1000よりもスペックが上とはいっても、MarkIIIで画面表示周りや音楽再生をMSXと同等の事をさせるということは、MarkIIIにSG-1000レベルの事をさせるという事ですから、MarkIIIの機能を活かせません。移植は楽だけどゲームの質が向上するということはないのですね。

RAMに関しては、MSXは8~64KByteなのに比べると、MarkIIIは8KByteですから、これでもまだRAMが不足しています。ただし、SRAMを搭載したROMカートリッジをmarkIIIで使えば、この点は一応はクリアできます。一応というのは、MSXとMarkIIIではメモリマップが異なるので、メモリバンクの切り替えやりくりが大変なのです。

ROMの容量については、MSXもMarkIIIもバンク切り替えが可能ですから、同じようなものです。

このあたりのハードウェアによるROMとRAMの容量の違いは、アーケードゲームを作ってきたセガと、パソコン(マイコン)文化の流れを持つMSXの違いそのものに感じられます。この頃は、ROMに比べるとRAMのチップが高価で、RAMの制御回路も複雑になりがちです。それがそのままハードウェアの価格に反映されるので、アーケード基板はRAMが少なく、その代わりにROMを大量に載せる事で鮮やかでキャラクタの豊富な画面を作り出しています。その代償として、そのハードはそのゲーム以外には使えません。
一方、マイコンはフロッピーやテープなどからプログラムを読み取って実行するという作りになっています。RAMに書かれたプログラムを実行するための機械ですから、できるだけ多くのRAMを搭載する必要があったのです。それに加えてMSXはROMカートリッジによるソフトの供給が可能でしたので、トータル的なバランスでみると、MSXはゲーム機よりも高性能なのですね。グラフックの派手さや音の豪華さで負けちゃいますが。

ちょっと話が脱線しましたが、ここまでで、条件が絞られてきて、

  • SG-1000用のカートリッジでメガロムが扱えるものがあるかどうか → ほぼ無し。あるけど入手が難しい
  • SG-1000用のカートリッジでSRAM搭載のものがあるか → 無し。RAM増設キットのようなものが一部の市場で流通していたそうですが入手できなさそう

これでSG-1000という選択肢は消えました。

■ ちょっとした葛藤

SG-1000、MarkIII、MSXについて調べてみると、ハードウェアが似ているためか、過去にも相互移植という文化?があったようです。この辺は、中二病的な表現でいうと「闇」そのもので、MSXのゲームからMarkIII、もしくはその逆に移植して、著作権者の許可を取らずに販売するという、当時のアジア圏の怪しいROMカセットの歴史があります。

ここらへんで悩みました。

ちょっとした遊びで移植しようと思ったことが、実は80,90年代の違法な行為と同じレベルなのですね。もちろん、移植したものを販売するどころかネットで配布すらする気はありませんが、これから作ろうとしているものが違法に流通しているものと同じだというのは気持ちの悪いものです。
さすがに21世紀も10年が過ぎた今ではMSXもMarkIIIも過去のもので、今になって過去の出来事と同じ事をするのは意味合いが異なるのですが、この辺の違法モノの流れはNintendoDSのマジコンのような結果につながっていくわけですから、どうにも無邪気に移植して楽しんでいいものではないような意識が出てきてしまうのですね。

これについては、今でも明確な答えが出ていません。

でもまぁ、そんな私の感情論はひとまず忘れて、調べてみるとやはりありました、MSXのゲームをMarkIIIに移植した「N in 1」。

【SG/韓国】 MrkIII / GAMBOY(韓国版MASTER SYSTEM)用カートリッジ (1986年頃~)

あと、この辺の事を調べていてみつけたのですが、MarkIIIのカセットをMSXで遊ぶための機器?があるようです。

PlaySoniq

■ 調査

いきなりMSXドラクエをMarkIIIに移植するのは壁が高かったので、簡単そうなところから、ROM容量の少ないバンク切り替えをしていなさそうなMSXのゲームを移植してみることにしました。
それなりに中古市場が安価なカセットで、せっかくなので遊び甲斐がありそうなゲームということでラリーXを選んでみました。(先のN in 1にも入っていました)

まずはMSXのBIOS部分をMarkIIIに移植してアセンブラとリンカを使ってROMイメージを作って、MarkIIIエミュレータに読み込ませてエイヤっと起動してみたのですが、まー見事にうんともすんとも動かない、全然、まるで。

動かない理由を突き止めるのが大変で、でも、原因はもう本当にしょーもない事で、この時に使ったEmukonというMarkIIIエミュレータが、ラリーXの画面モードに対応していないというものでした。

MarkIIIに搭載されているVDPには、SG-1000との互換性を維持するためのモードが用意されているのでが、大半のMarkIII用ゲームはMarkIII専用モードで動作しているからか、SG-1000互換モードを実装してないエミュレータがあるようです。Emukonはデバッガの機能が豊富だったのに残念。

他のエミュレータで試してみたところ、一発で画面が出るようになったので脱力。でもなんか化けてます。

化けタイトル

ところで、今回の移植ですが、私自身はMSXとMarkIIIの事をちっともわかっていませんし、ラリーXの解析はほとんどしていません。解析作業は、それはそれで面白そうですけど、どうせ解析するならアーケード版の方が楽しそうですが、基板を持っていないですし、アルゴリズム的な解析は、流線堂さんがされていて、解析本を読んで充分に楽しませてもらいましたから、今回はできるだけ解析をせずにラクラク移植を目指します。

今のところ、画面が化けていますが、なんにせよ画面が出たらキー入力を受け付けるようにしてタイトル画面から先へと進ませたくなるというものです。

MarkIIIのパッド入力も、MSX2のキー入力も、I/Oポートからの読み取りです。この手の処理は垂直同期割り込みで読み込むのが基本なので、MSXとMarkIIIの割り込みを調べてみたところ、どちらもVDPの垂直同期割り込みを使っているようでした。割り込みはZ80のINTですから0038hにジャンプしてきます。

そこでMSXの割り込み処理をMarkIIIに移植したのですが、なぜか画面が表示されなくなってしまいました。

あれこれ調べてみたところ、INT割り込み無限地獄から抜け出せず、INT割り込み→割り込み終了→INT割り込み・・・を繰り返しているようでした。割り込み終了前にはEIで割り込み許可していて、

INT:
	EI
	RET

ここのEIを消すと、前と同じようにタイトル画面が表示されます。

これはきっと、VDP割り込みが連続してかかっているのだろうなーと思ったので、SMS POWERのサイトから適当にPAD入力のサンプルコードを探してきたところ、割り込みルーチンでは

in a,($bf)

として、VDPレジスタの値を読み取らなければならないようでした。MarkIIIのプログラムを書く時の最初の注意点ですね。

INT:
	EI
	in a,($bf)
	RET

割り込みルーチンを呼び出すようにしてみたところ・・・

ゲーム中画面

metaというエミュレータではデモ画面が動き出しました!キャラクタは化けていますし、背景が正しくないからか、自車が見えない壁の中でグルグルしていますけど、動くのをみるとテンション上がります。

ただ、別のエミュレータfusionでは動かないというのが厄介です。というのも、fusionにはデバッグ機能が無いので、何が起きているのかわからないのですね。
また、MSX版ラリーXのプログラムは割り込みルーチンの記述が独特で、ぱっと見た感じでは何をやっているのかわかりにくいです。というか、もしかして割り込み発生時にしか処理が走っていないのかな?という作りです。

これはもしかすると、ワークエリアの初期化に問題があるのかもしれません。MSXでは起動時にBIOSがメモリを初期化するので、ラリーXは初期化されたメモリの状態に依存しているのかもしれませんから、その辺を先に調べる事にしました。
また、MSXとMarkIIIではRAMのアドレスが違います。MarkIIIではC000HからDFFFHの内容とまるで同じ内容がE000HからFFFFHからもアクセス出来ます(その分、容量が少ないということです)。
MSXのラリーXではE000hを基準にしてメモリアクセスをしているようですけど、もしかするとC000Hからの内容とは別の事をしているのかもしれません。ただ、それはmekaでは動いてfusionでは動かない事の理由にはなりません。

■ キャラ化け直し

まず、わかりやすそうなところで、空白文字のキャラデータから調べました。ラリーXでは、ROMのデータを直接VDPに転送せずに、一度、RAMにキャラクタデータを展開してから置いてからVDPに転送し直しているようです。

キャラクタデータが格納されるアドレスを調べたところ、画面に表示される通りにVDP内で化けていて、その前段階のRAMでも化けていました。当然ですが。ROMデータはオリジナルのままでMSXではちゃんと動いているのになんで?というところでROMデータを調べてみたところ、キャラデータの00HでなければならないところがC9Hになっているではないですか・・・。

これでピンときたのですが、ソースコードに

.EMPTYFILL $C9

という箇所が怪しく思えたので調べてみたところ、オリジナルROMを逆アセンブルした箇所が

org $6100
x6100: db	0
org $6108
x6108: db $1c,$36...

というようになっていました。これだとに7Byte分の空き領域が出来るので、そこが$C9で埋まりますね。

今回は、いつもとは違う逆アセンブラを使っていたので気がつきませんでしたが、この逆アセンブラは未使用領域と思われる00hの箇所を飛ばしてコード生成するようなのです。それをそのままアセンブラにかけると、C9Hで埋まってしまうのですね。キャラクタデータは00hが多いのでなるほどです。

こういうのは失敗してみないとわからないので、いちいち挫けてられません。気を取り直して、ROMファイルを再度書き出し直してみたら、

ちゃんとしたタイトル画面 ちゃんとしたゲーム画面

本来のタイトル画面が出てきました!それに、しばらく放置しておくと画面が動き出しました。自車がガスを出しっぱなしでまっすぐ走ってしまいますけど。
これは最初、デモ画面かと思ったのですけど、元々のMSX版にはデモプレイ画面がないのです。つまり、タイトル画面で勝手にスペースキーが押されて、ゲームが始まってしまっているのですね。

また、なぜか、いままで動作しなかったエミュレータfusionでも動くようになりました。

画面が出たのは嬉しいですが、これだけ苦労して、やっとMSXと同じ画面が出ただけという物足りなさはあります。

■ ここまでのまとめ

やったことは…

  • ラリーXで使われているMSXのBIOSルーチンを、MARK III用のルーチンとして作成。BIOSのエントリアドレスは揃えるけど、処理自体はMarkIII用に修正。修正といっても、I/Oポートのアドレスを揃えるくらい。
  • 0番地からの起動ルーチンとBIOS用ワークエリアの初期化処理の記述。4000Hからのアドレスに配置してあるラリーXのコードへ移動するルーチンの記述。
  • ラリーXのROMで、スタックポインタのアドレスを設定している箇所(2箇所)を修正。
  • INT割り込み処理ではMSX BIOSと処理を同じにすると同時に、VDPの割り込みをリセットするように命令を追加(これが最重要項目)

です。

ラリーXのROMへのパッチ当てはスタックポインタアドレスの修正が2箇所=4バイトだけという、なんというハードウェア互換の高さ。MSX BIOSの設計の良さによる効果が大きいですね、

さて、残りの課題は大きく2つで

  • キー入力

です。

■ キー入力

markIIIのパッド入力→MSXのキー入力処理変換処理を書いたら、あっさりと動いてしまいました。この時点でゲームができちゃう状態に。

MSXのラリーXはキーボードだけでなく、AY-3-8910を利用したジョイスティックにも対応していて、BIOSを利用していますから、MarkIIIのパッド入力を、ジョイスティックポートのデータに変換しても動きそうです。いまのところ、AY-3-8910のポート読みだし処理は、常にジョイスティックは未入力状態として返すようにしています。このやり方だと、音楽演奏処理側で問題がでるかもしれませんが、ざっとラリーXのプログラムをチェックしたところ、AY-3-8910のレジスタ状態読み取りはジョイスティックにしか使っていないようですから大丈夫っぽいです。

■ 音を出す

残りの課題は音楽の再生です。MSXとMarkIIIの両方の音源を比較してみましょう。機能的には出来ることが似ているようにも見えるのですが、実際に両方の音源を使ってみると、この2つを共にPSGと呼ぶのには抵抗を感じましたから、区別するためにもSN76489をDCSGと表した方がいいと思いました。

それぞれ個性があるので、どちらが優れているのかを単純には比較できないのですが、SN76489の音をAY-3-8910に持って行くことはできても、その逆は苦しい場合がありそうです。

まず、SN76489にはハードウェアエンベロープがありませんから、SN76489ではソフトウェアで表現しなければなりません。MarkIIIにはMSXと同じ垂直同期割り込み(1/60秒間隔)だけでなく、水平同期割り込みがあるので、音量を変化させることは出来そうですが、それなりに負荷がかかります。MSXのAY-3-8910ではハードウェアがやっていた処理をMarkIIIではZ80がソフトウェアで処理するとなると、処理負荷が変わってきてしまい、ゲームそのものの速度に影響が出そうです。

今回はエンベロープを再現せず、AY-3-8910でエンベロープ指定があった場合は、単に最大音量で出力するようにしました。音が消える波形のエンベロープの場合は、
一定時間で音量を0にするような仕組みにした方がよさそうですが、ラリーXではエンベロープを使っていないようですから、問題なしです。

もう一つの大きな違いは、再生可能な周波数の範囲です。周波数の設定式を比較してみると、

  • AY-3-8910: P = 1789770 / 16*H
  • SN76489: P = 3579540 / 32*H

です。Hは周波数で、Pは音源ICに与えるパラメータ値です。式が似ているどころか同じなのですが、パラメータ値の範囲に違いがあり、AY-3-8910は12bitで、SN76489は10bitです。つまり、AY-3-8910の方がSN76489よりも低い周波数の音を出すことができることになります。具体的に、MSXとMarkIIIでは、

IC low high
AY-3-8910 54Hz(?) 111.86KHz
SN76489 109Hz 111.86KHz

このようになります。人間の耳では、20Hzくらいから20KHzの音を聴けるそうですから、109Hzはもちろん、AY-3-8910が出す54Hzを聴き取ることができます。
SN76489では109Hzまでの音しか出せないとなると、それ以下の周波数の音を出せるAY-3-8910を再現することが出来ません。
ラリーXでも、109Hz以下の音を出しているようなので、再現できないのです。
これはどう解決したらいいのか悩ましいところで、周波数を4倍して2オクターブ上げるとかダサいですが、まぁ聴ける曲にはなります。
あと、ラリーXでは、3チャンネル中の1チャンネル分だけが低音でポコポコと音を出していたので、このチャンネルだけはオクターブを上げると、メインメロディには影響が出ない感じの曲になりました。

あと、機能的な違いとしてはノイズ発生機能がありますが、これはSN76489の方が優れているので、さほど問題にはならなさそうです。というのも、今回の移植ではノイズ機能を使いませんでした。

■ 完成?

エミュレータ上では完全に遊べるレベルになりました。

エミュレータ上で動作

と同時に、ちょっと空しくなりました。オリジナルのプログラムにはほとんど手を加えずに動いているのでプログラミングの楽しさがなかったのと、作った結果がMSXとまるで同じということと、MarkIII本来の能力を活かしていない(RAMの増えたSG-1000程度)なので、モチベーションが下がる方向に動いてしまったなと。

あと、ちょろっと移植して違法に販売してたであろう、当時のN in 1の安易さみたいなものが垣間見えて、これはいかんなーとか。

■ SG-1000/SC-3000版のこと

更に調べてみたらありました、SG-1000用のラリーX。namcoのライセンス品かどうかはわかりません。
ここに”Requires DahJee RAM extension adapter to play on SG-1000/SC-3000 series systems.”と書かれていますね。やはりSG-1000ではRAMが足りないのでRAM増設アダプタというものが必要になるようです。
スクリーンショットをみると、タイトル画面が「PUSH SPACE KEY」となっているので、MSXから持ってきたんだろうなーというのが想像つきます。

MSX版のラリーXがどの程度のRAMを使っているのかは調べていません。大雑把にみた感じでは2KByte程度なので、作り込み方によってはノーマルのSG-1000でも動いたのかも。

■ 実機で!

せっかくなので実機で動かしてみたいところです。調べてみると、市販カートリッジのROMをEEPROMに交換するという方法で自作のソフトを動かすことができるようで、SRAMを搭載しているファンタシースターのカートリッジを入手したのですが、まずは実験用ということで、グレートフットボールのROMを使ってみました。

グレートフットボールカセット
カセット正面のラベルの下にネジ2個でケースを固定しているので分解してみました。

カセット分解正面 カセット分解裏面

どうやら、1MBitROMのバンク切り替え機能も内蔵しているカスタムROMのようで、単純にEEPROMに置き換えることはできなさそうです、残念。
ただ、メガロムとしては使えなくても、32KByteか64KByteの外部ROMとして使えるのではないかなと端子番号からROMの端子を調べてみました。

ICs

27C256(32KBbyte),512(64KByte)と似ていますね。違いは1,20,22番ピンです。
MPR-10576ってROMなのになんでWR端子があるのかというと、MarkIIIではバンク切り替えをする時にROMに対して書き込みをするとバンクを切り換えるという仕様になっているのです。ソフトでバンク切り替えをしないのであれば(64KByte以下で動かすのであれば)、この端子は使いませんし、27C256では+5VかGNDにつなげばよさそうです。
27C512の場合も同様ですが、27C512では64Kbyteをフルに使う事はできないっぽいです。というのも、MPR-10576ではMREQ端子しか読み込み信号がありません。図でA15となっている端子は、これは実際にはMarkIIIからのROM選択信号になっているようで、MREQとA15の二つでROMの読み込みを選択するようです。
つまり、MarkIIIからのMREQを27C256/512のOEにつなぎ、MarkIIIからのA15を27C256/512のCEにつなぐことになるので、本来のA15信号は使えなくなります。MarkIIIからは、チップ選択用の信号が出ているのですが、グレートフットボールの基板では、その信号が配線されていませんでした。ですので、27C512を使っても、前半32KByte分しか利用できないことになります。

最初、CEをGNDにつないでMREQをOEにつなぐ方法でやってみたのですが、アクセスタイミングの都合か何かで動きませんでした。

ROMカートリッジの端子については、Enriさんのページに詳しく書かれています。

実機で動かしてみた結果は動画で。

んんーゲーム中の背景画面が化けますね、なんでだろう。タイトル画面はちゃんと出ているので、ラリーXのスクロール処理でキャラ書き換えをしている箇所でつまづいているのかな、よくわかりませんが、原因不明で調べるのが大変そうですし、あとあと音楽ももうちょっと頑張りたいところですが、モチベーション切れなのでここまで。

EEPROM化 実機ではバケバケ

■ ドラクエどうする?

MSX版ドラクエの解析は楽しいですが、これをそのままMarkIIIに持ってくる作業は楽しく無さそうに思えたのでやめました。メモリマップの違いから、移植作業は楽ではないと思いますが、単にメモリのやりくり変更作業だけになりそうですし、MarkIIIならもっと表現力豊かなドラクエを動かせるはずで、MSX版のドラクエでは役不足です。

参考までに、MarkIIIとMSXのメモリマップ比較。

MemoryMap

一番の違いは、MSXのバンク切り替え単位が8Kbyteなのに対して、MarkIIIでは16KByte単位ということです。あと、RAMの容量が違いますね。MSXのドラゴンクエストは16KByteのRAMが必要で、RAMが8KByteのMarkIIIでは無理があるように思えるのですが、ドラゴンクエストのプログラムを調べてみたところ、16KByte中の8KByteはカートリッジ内のサウンドルーチンをRAMに展開してROM領域のようにして使っていました。
MSXのBIOS領域はMarkIIIのROM領域として使えますし、MSXのBIOSを全てを移植する必要はないので空き領域が出来ますから、ここにサウンドルーチンを移動させれば、RAM不足の問題は解消できそうです。
あとは4000HからBFFFHのROMのBANK切り替えのやりくりですが、MSX版のドラクエは8KByte単位でバンク切り替えしていても、結構、16KByte単位で使っている箇所が多かったです。まるでSG-1000/MarkIIIへの移植を想定していたのかのようなくらい。戦闘プログラム8KByte+モンスター画像データ8KByteとか。MarkIIIの4M ROMを使って、全ての組み合わせでバンクを持つという無駄きわまりない方法で解決する方法もありですね。

そんなわけで、MarkIII 30周年に何か作ろうマイイベントは仕切り直しということで。

■ MarkIII ゲーム開発について不明点

現状での不明点はバンク切り替えを行うROMの開発手法です。バンク切り替えの原理はわかるので、手動であれば、バンク別にアセンブラで作成したバイナリファイルを連結してROMイメージを作れるのですが、アセンブラレベルで別バンクのラベル(アドレス)を参照したり、バンクアドレスを想定したメモリ配置をするとか、その辺の開発技法がわからないです。
バンク切り替えによるキャラデータなどのデータ領域の差し替えくらいなら問題ないのですが、バンクをまたぐプログラムを書く手法がわからないと。16KByteを超えたプログラムを書くかというと、なかなか難しいかもしれませんが、大容量ROMを活かしたコーディングも考えられるのですね。POP命令をひたすら並べて転送とか。

あと、現時点ではMarkIIIのSG-1000互換レベルでしかVDPを理解していないので、パレット制御やスクロール周りをちゃんと抑えて、MarkIII専用のゲームを移植するか自作するかしたいです。

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