1. 元の MSX2 版のスクロール
1.1 動き方
| 版 | 1歩の動き | 1歩にかかる時間 |
|---|---|---|
| ファミコン版 DQ1 | 1ドットずつ | 16フレーム(Mesen で実測) |
| MSX2 版 DQ1 | 1歩ごとに画面全体を VDP のコピーで1マス分一度に動かす | 約10フレーム |
| MSX2 版 DQ2 | 半歩(8ドット)ごとに、画面を裏のページに8ドットずつ写し、新しい列を描いて表示ページを切り替える | 25〜28フレーム(半歩 約13フレーム) |
DQ2 は、動きが8ドット単位でカクカクするうえ、1歩がファミコン版より大幅に遅い。
1.2 遅さの理由
移植者の川俣晶氏の著書「ENIX移植プログラマー戦記」によると、ファミコン(6502)から MSX(Z80)への移植は「基本的にソースコードは書き換えない、その代わり、マクロを用意して6502の命令をZ80の命令郡に置き換えてしまう」方法で行われ、「懸念事項はサイズと速度と互換性の3点だ」とされている。
ROM を読むと、実際にゲームの大部分は、6502 の命令1つごとに補助ルーチンを呼ぶ形になっていた(X レジスタを B、Y レジスタを C に当て、補助ルーチンの写しが各バンクにある)。容量は抑えられるが、命令ごとに呼び出しと戻りが入るので遅い。スクロールを滑らかにしても、一歩ごとのゲームの処理が間に合わず、引っかかりとして見える。速度の改善はここが中心になった。
2. DQ1(練習)
- 歩いている間だけ、表示を 512x256 の面(V9958 の SP2 モード。ページ 2/3)に切り替え、割り込みで縦スクロール(R#23)と横スクロール(R#26・R#27)を毎フレーム進める
- 歩き終わっても面の表示のまま使い回し、ゲームが窓を開いたり VDP コマンドを使ったりする直前に、面の画面をページ 0 に書き戻して元の表示に戻る
- NPC も、ゲームの中の本当の位置(マス×16 + 1ドット単位の移動量)で表示する。元の MSX2 版はマス単位でしか表示しないので見えていなかった、移動の周期のずれも補正した
- 1歩のフレーム数は 8〜16 で選べる(16 がファミコン版と同じ)。横スクロール中に左端に出る乱れは R#25 の MSK で隠せる
- V9938(MSX2)では元のままの動き
エミュレータ(openMSX)でのみ確認しており、実機ではまだ確かめていない。
3. DQ2 のスクロール
3.1 仕組み
- 半歩ごとに、画面の外の新しい1列(1行)だけを描き、縦・横のスクロールレジスタを1フレームに1ドットずつ進める。半歩8フレーム、1歩16フレームで、歩き続けると途切れずに進む
- ゲームはページ 0 が画面である前提で窓や文字を描くので、描く直前に面の画面をページ 0 に戻す
- スプライトは縦スクロール(R#23)と一緒に動いてしまうので、スクロール中はゲームが書くスプライトの表を別の場所に向け、割り込みでずれを補正して本物の表に送る。主人公と仲間は画面に固定し、NPC は背景と一緒に動かす
- V9958 が1ラインに出せるスプライトは8枚(4人分)まで。5人以上並んだときは、主人公以外の表示する人を毎フレーム入れ替えて点滅で見せる
- 左端の乱れは MSK(R#25)で常に隠す。起動時から入れて、見え方を一定にした
3.2 速さ
| 改善 | 内容 | 効果(エミュレータでの測定) |
|---|---|---|
| 補助ルーチンの置き換え | 6502 の命令をまねる補助ルーチンの呼び出し約 3,100 か所を、同じ働きの Z80 命令に直接置き換え | 町の中の一歩ごとの引っかかりが減る |
| NPC の移動処理 | NPC の位置・向き・ぶつかり判定の処理を Z80 で書き直し。元の処理と呼び出しごとに突き合わせて差がないことを確認 | 町を40秒歩いたとき、NPC の処理が全体の 16.1% → 9.2%。1フレーム以上待たされた回数 30回 → 3回 |
| 混雑時の処理 | 5人以上並んだときに隠す人を選ぶ処理を高速化(結果は変えない) | その処理が約15% → 約6% |
| 窓 | 窓の表示・消去で描き直す部品の処理を高速化 | コマンド窓とステータス窓の表示 約48 → 約32フレーム |
| R800 版 | turbo R では最初の一歩から R800 で動かす | 町での引っかかりがなくなる |
3.3 操作の反応
- 短い押し: ゲームは、フレームの数え方が決まった値(16フレームに1回)のときしか歩き出さない。それまでに方向キーを離すと歩かなかった。離した方向を覚えておき、その時に押されていたものとして渡すようにした
- 歩き出しの待ち: 止まっている間、NPC が何かをするのは数え方が 0・4・8 のときだけで、その間のフレームは何もしない。方向キーが押されたら、次の 0・4・8 まで一気に進めるようにした。押してから歩き出すまで平均13 → 7フレーム。8 でも歩き出せるようにする案は、NPC が半マスずれるので採らなかった
- メッセージの速さ: 戦闘のメッセージの速さを、ゲーム中いつでも F1 / F2 / F3 で切り替えられるようにした
4. V9968 への対応
V9968 は HRA! 氏が FPGA で作った V9958 上位互換の VDP で、カートリッジ版は I/O 88h で使う。
- VDP の自動判定: ゲームが最初に VDP に触る場所で、88h に VDP があるか、V9968 か(拡張レジスタのロックを外し、R#21 を 0 にして ID が 3 になるか)を調べ、使うポートを決める。ROM 1本で、V9938・V9958・V9968 カートリッジ・V9968 内蔵の機種に対応する。MSX1 で V9968 がない場合は、本体の画面に案内を出して止める
- 1ラインに16枚: V9968 の R#20 の S16 を使い、1ラインに8人まで点滅せずに出す。ただし、ゲーム自身がスプライトの表を書いている間(画面の切り替え直後)に S16 を立てていると、主人公の2枚の片方が出なかった。表に終わりの印がなく32枚すべてを調べる場合に起きる FPGA 側の問題と考えられ、テストプログラムで実機で再現した。パッチでは、自分で表を送るスクロール中だけ S16 を立てる
- 本体の画面: カートリッジの V9968 にゲームを出している間、本体の VDP にはゲームのフォントで案内画面を出す。メッセージの速さの選択中の位置に ▶ を付け、F1〜F3 で動く
- タイトルのごみ: タイトルの盾のスプライトは21枚なのに、ゲームは22枚分を送っていて、22枚目にプログラムの一部が入っていた。本体の VDP では見えないが、V9968 では盾の上にごみとして出た。正しい枚数にした
- R800: R800 では V9968 カートリッジの VRAM の読み出しが化ける(実機で確認)が、ゲームもパッチも VRAM を読まないので、R800 版でも使える
5. 高速モード
- turbo R: R800 版では、最初の一歩から R800 にする。起動時に「1」を押していれば Z80 のまま(MSX-DOS2 と同じ操作)。R800 を選んだときは、起動した時点で高速モードのランプを点ける
- Panasonic の MSX2+: turbo R 以外では、起動時に「2」を押していれば、最初の一歩から FS-A1WX / FS-A1WSX / FS-A1FX の高速モード(5.37MHz)にする。機種は判定せず、Panasonic の切り替え I/O に高速化の命令を送るだけなので、ほかの機種では何も起きない。3倍速改造機(10.74MHz)でも動くことを確かめた
6. 元の ROM の版
MSX2 版ドラゴンクエストII には、3 種類の吸い出しが知られている。違いは 2〜3 か所で、そのうちの1つ([568] と呼ばれるもの)だけ、効果音を鳴らし始める処理の誤り(足し算に使うレジスタの取り違え。ROM のバンク切り替えのレジスタに 0 を書いていた)が直っていた。ほかの版との違いがすべて「1 のビットを 0 にした」形であることなどから、これを最後の修正版とみなし、パッチはどの版から当てても、この版と同じ中身にそろえてから改造する。
7. パッチプログラム
配布するのは、Python で書いたパッチプログラム1本(DQ1・DQ2 それぞれ)。
- ゲームの ROM の内容は含まない。パッチで変えたバイトだけを持ち、ゲームの ROM と同じ並び(16バイト以上)の部分は、当てるときに利用者の ROM から写す。V9968 の案内画面のフォントも、利用者の ROM から写す
- 当てた結果が、開発で作った ROM とまったく同じになることを、チェックサムで確かめてから書き出す
- DQ2 のオプション
--r800: R800 版にする(§5)--names 王子,王女: 仲間2人の名前を固定する。元のゲームは主人公の名前の文字コードの和から、表の中の名前を選んでいる--debugpw ファイル: デバッグ起動用の復活の呪文を4つまで入れる。タイトル画面(ロゴと START・CONTINUE などの選択肢が表示されているところ)で F1 を押しながら 1〜4 の数字キーを押すと、その呪文を自動で入力して再開する(入力はキーを順に押したことにして行う)
- DQ1 のオプション: 1歩のフレーム数(8〜16)と、左端の隠し方(MSK 0〜2)
8. 確認
| 項目 | 確認 |
|---|---|
| DQ2 の滑らかなスクロール、NPC、混雑時の点滅 | 実機 turbo R(Z80・R800)、openMSX |
| DQ2 の V9968(カートリッジ) | 実機 V9968 カートリッジ(Z80・R800) |
| DQ2 の V9968 内蔵、MSX1 + V9968 | openMSX のみ(V9968 内蔵の機種は、VDP を V9968 にした試験用の機種設定) |
| DQ2 の Panasonic の高速モード | 実機(3倍速改造機)、openMSX FS-A1WX |
| DQ2 の V9938 での元の動き | openMSX(C-BIOS MSX2) |
| DQ1 | openMSX のみ |