PC-9801のゲームをIBMPCで動かす!

 

ここのところお仕事が忙しくて、レトロ系に取り組めて無い。

やりたいことはてんこ盛りに増えていくのに、なにひとつ手が出せないというのは身体にも精神的にも悪いだろう!

ということで、合間合間をぬってちょこちょこ活動していた事をブログに残す!

自分の備忘録代わりにもなるし!(^^)

 

ここ最近で取り組んでいたのは、PC-9801のゲームをPocket386へ移植する事!

Pocket386とはなんぞや……については、本当にいろんなサイトで紹介されているので割愛するけれども、今どき珍しいMS-DOSが動作する小型のパソコンだ。

 

私自身、IBMPCマシンのMS-DOS上でゲームを遊んだ覚えがない。

いや、実はあったかな……と思い出そうとしてみたが、やっぱり覚えがない。

(追記:遊んでた!これ買って遊んだ!!
https://en.wikipedia.org/wiki/Comanche:_Maximum_Overkill)

 

初めてIBMPC(GATEWAY2000)を海外から購入した時は、最初から386BSDを動かす事しか頭になくて、DOSパーティションは3日も経たないうちに消してしまっていた(^^;;;

しかもバックアップも取らず!w(後に日本語版MS-DOSを購入した)

 

当時MS-DOSで遊べなかった事が心の片隅で後悔として残っていたので、今回は思う存分遊ぶことにしたのだ!

よーし、プログラム組むぞー! ← これが私の遊び(^^)b

 

移植するプログラムの条件

PC-9801のソフトであれば、何でもかんでも移植できるってわけじゃない。

やっぱりいくつか条件があるので書き出しておきたい。

 

GDCの機能を極力使わない

割と極めつけな条件かもしれないコレ。

GDCはGraphics Display Controllerの略で、グラフィックスに関するいろんな事を請け負ってくれる便利なコントローラだ。

ゲームでは画面全体のスクロールやライン描画、表示範囲の設定などに使われるケースが多いと思う。

私自身がIBMPCのグラフィックコントローラの詳細を全く知らないので、出来る限りGDCを利用しないプログラムを選びたい!(覚える気ゼロw)

 

テキスト画面の扱いについて注意が必要

PC-9801はテキストを表示する画面とグラフィックを表示する画面が分かれている。最終的には合成された見た目で画面には表示される。

IBMPCではグラフィックとテキストが切り替えとなっていて、それぞれを合成する事ができない(できないよね??) そのため、テキストとグラフィックを巧みに混在させたゲームの移植は難しい…と思う。

画面マスクもある意味同義。

 

BASIC ROMへの依存

PC-9801のBASIC…というか標準搭載されているROMは結構な多機能で、ROM内ルーチンを使ってるものを独自で書くのは簡単ではなさそうな感じがしてる。

そのため、出来る限りROMに依存しないプログラムを探したい。

 

基本方針

バイナリパッチのみ

以前からこのブログでも書いているが、著作物に対しては最大限の敬意を払いたい。

この手の移植では外部でプログラムコードを変更するのが一般的かと思うけど、ここではオリジナルバイナリには手を付けず、メモリへロード後、メモリ上でバイナリパッチを当てて動かすようにする。

バイナリパッチで手に負えなくなったら、そこで諦め。
著作物を軽視してまで遊ぶ事じゃない(^^)b

 

BASICプログラム

BASICのプログラムについてはオリジナルを参考にしつつ1から作成する。BASICインタプリタを似せるのは無理があるので、ここは無理せず自作で(^^)

ただしC言語を用いて楽はするよ!

C言語については、LSI C-86 Ver 3.30c 試食版を利用させていただいた。

 

ダンプリスト打ち込み

雑誌掲載のゲームのみを対象とし、ゲームのバイナリはダンプリストを打ち込むことで入手する。どんな長いバイナリがあっても自作ツールがあるから平気だ!(^-^)

pocketgriffon.hatenablog.com

 

アセンブラ

自作の逆アセンブラを利用して解析を行う。

解析ツールとしてはこれしか無い!

あとは実行時にSYMDEB.EXEを活用したら、デバッグ環境としてはサイコーだ!(^-^)

ここで雑誌に掲載されているマシン語ルーチンのアドレスマップが大いに役立つ!

↑こういうアドレスマップを元に解析をしていた!

 

1、ベリコーサを移植!

まず手始めに取り組んでみたのは、工学社月刊I/O 1985年2月号に掲載されていた、PC-9801/E/F用のベリコーサ。

 

このゲームはゼビウス系のシューティングゲームで、当時の技術的な課題となっていた地上配置物と空中移動物の合成を、奇数偶数ライン別々に描画する事で解決をした作品。

力技とも言える方法だが、作者が「あえてGDCのスクロール機能を使わずに実現」と書いていたので、実験的かつ狙った方法であるのは間違いない(^^)

 

これを移植してみよう!

最初の移植で、移植方針もちゃんと定まってない状態で始めてしまったため、パッチ当て箇所は100箇所以上にも及んだ!

だけど……IBMPCのEGAの設定に慣れず、VRAMからVRAMへのコピーが思った通りできず、画面が化ける現象が出まくった!(TT)

 

解決法も考えてみたがバイナリパッチの域を超えてしまいそうなハデなモノになりそうだったため、途中で断念した!

 

2、魔獣島を移植

ベリコーサの敗因の一つはVRAM→VRAMのコピー。

そのコピーはなさそうだな…と思って次のターゲットとして選んだのは、デンジャーゾーン「魔獣島」(工学社 月刊I/O 1084年3月号掲載)。

実はベリコーサよりも前に発表されていたゲーム。

説明書きに「モニタの1ラインアセンブラで作った」と書かれていたので、あまり複雑なことはしていないんじゃないか…と思い移植(^-^)

このゲームもアドレスマップが書かれていて、移植するのは楽だろう…と思ったのだが…実際にはそれなりに苦戦した。

 

しかも……このゲームもキャラクタの表示にVRAMとのXORをしてるっぽい!(TT)  そういえばXOR描画も当時の流行りだったかもしれない!(XORで表示、XORで消去) VRAMからの読み込み方法を克服しないと、この手のソフトは全滅してしまうかもしれないなぁ…orz

 

写真右に「冬になると雪が降る」と絵が載っているが、実はこの雪、テキスト画面に書かれるアスタリスク(*)だ!恐れていたテキスト画面との合成が必要だ!(TT)

 

結局、この段階ではテキストとグラフィックを上手に(効率よく)合成する方法が思いつかず、志し半ばで挫折…orz

 

3、からくり忍者屋敷を移植

工学社 I/O別冊 ペーパーウェアPC-9801[2] に掲載されていたパズルゲーム。

このゲームからグラフィックVRAMの扱い方を変更した。

具体的には、メインメモリの空いた場所をグラフィックVRAMと見立てて操作出来るようにしたのだ!(具体的な手法はインセクトの復讐で詳しく説明)

 

さすがに……さすがにパズルゲームだったら画面とXOR取って表示したりはしないだろう…!

しかも、ゲーム画面を見る限りではテキストVRAMが使われていなさそう。

よーし、このゲームを移植して最後にするぜ!

 

……と思って取り組んだんですが…なんとグラフィック描画はBASIC ROMの機能を使ってた!

しかも!1キャラクタのサイズが横27ドット縦11ドットだ!

横が8の倍数になってない事がどれだけ大変なのか、アセンブラでキャラクタ表示ルーチンを書いたことがある方ならばご理解いただけるだろう…。

 

結局、BASIC ROMの機能を模倣することとなった。

横27ドットのキャラクタを、任意のドット単位の場所に表示する処理を書く……。

それっぽい処理をアセンブラで書いてみるが、やはり一発では動かなかった(^^;;

この移植ではSYMDEB.EXEのお世話にだいーぶなった。

この時代のデバッグ環境であっても、シンボル情報が扱えるのはとても便利だ!(^-^)

 

結局、この部分がネックとなり表示がちゃんとした頃には力尽きてしまった(TT)

 

4、インセクトの復讐を移植

今回の作業で最後に移植したのが、工学社I/O 1983年11月号に掲載されていたインセクトの復讐。ずーっと「逆襲」と勘違いして覚えていた…(^^;;

PC-9801が発表されて間もない頃に掲載されたゲーム。

見た目通りギャラクシアンな感じのシューティングゲーム(^^)

 

説明に「ハンドアセンブルで作った」と書かれているのだけど、逆アセンブルして解析をしてみると「まじ??」と思うほど整ったプログラムとなっていた。各ルーチンの後ろには4バイトほどの空きが用意されていて、コードが変更になった場合を考えられてはいるようだが…。

 

前2つの移植で失敗してるのは、VRAMからデータを読んで加工し、再びVRAMへ戻すといった処理で、VRAMからの読み込みが上手くいかない点だ。IBMPC(というかEGAとかVGAとか)にはPC-8801のALUのような複数プレーン同時書き込み等の機能が存在する。

このレジスタを正しくセットすればきっと期待通りの動きをするんだと思われるが、今現在は書籍を詳しく読み込んで理解しているような時間も惜しい(TT)

↑こんな分厚い本を読んで理解してる余裕なんてないのだよ…orz

 

仕方がないので別の方法を考える。

これまで移植してきたソフトは、主に初代PC-9801、つまり8086の5MHzで動くようになっている。それをPocket386(386SX 40MHz)で動かそうとすると、処理速度的な余裕がだいーぶある。感覚値で申し訳ないが、10倍以上の速度差を感じている。

となると、技術的困難なポイントへのアプローチもさまざまに考えられる。

 

その中で私が取った方法は「内部に仮想的なVRAMをもう1セット持つ」だ。

この1文だけを読んで「な……アホかお前!」と思った方は熟練者だろう(^^;;

つまりこういう事だ。

左側がPC-9801のメモリマップ。インセクトの復讐はこの構成で動く。
右側…というか真ん中がIBMPCのメモリマップ。

そして一番右側がインセクトの復讐で採用したメモリマップ。

ゲームのプログラムは4000:3000から置いた。8086においてプログラムのセグメントがずれる事は大きな問題ではない。が、プログラム内にデータセグメントを決め打ちされてる箇所が必ず存在するので、そこは修正が必要となる。

 

そしてテキストVRAMへのアクセスは8000セグメントへ、VRAMへのアクセスは8800セグメントへ移動するようにバイナリパッチを当てる。こうする事で、インセクトの復讐のプログラムは、 IBMPCの単なるRAMへ VRAMと思い込んで描画処理をする。

 

一連の処理が終了した時点で、仮想テキストVRAMの情報を基に、仮想グラフィックVRAMへ合成処理を行い、出来上がった画像データをA000セグメントからのVRAMへ一気に書き込んでいる(32KB * 3プレーン分)。

 

こんな膨大な処理をしてゲームが成り立つのか?と思われるかもしれないが、実際のところPocket386では速度が速すぎてゲームが成立しないほどとなっている(^^;;;

 

その後、IBMPCにおけるリアルタイムキー入力の処理を教えていただけたので、キー入力対応、ついでにBEEP対応もしたところ、PC-9801版と遜色のない出来栄えとなった!(^^)

 

ちなみにパッチを当てた箇所は30箇所程度、PC-9801のフォント描画、画面更新処理など合わせても500行程度でできてしまった!

最後はシンプルかつ効果的な移植でキマったよ!(^-^)b

 

おわりに

なんとなく頭のリハビリ感覚で移植をしていたが、パズルっぽくて楽しかった!

面白そうだな、やってみたいな!と思った方は、ぜひチャレンジしてほしい!

当然だけど8086アセンブラは読めないといけないし、書く必要も出てくるだろう。

古のCPUに翻弄されるのもまた一興かな…と思う!(^-^)/

 

ではまた次回!(^^)/