開発メモ(思索ログ): へっぽこプログラマは日々こんなこと考えながらエミュ開発してます(笑) 2004/9/3 久しぶりに、EmuZ-2500を弄っています。 暫く離れていた間に、CP/M Playerとか、Emu5とか、EmuGakiとか、 斜め45度なラインナップの開発をしていた訳ですが。 これらの経験を元に、ここ数日ほどEmuZ-2500の再実装をしています。 現在の進捗は70%くらいか。 基本的には、Emu5やEmuGakiで使っている、エミュレータスケルトン(仮)が ベースです。 特徴は、I/Oデバイスの基底クラスを定義しておいて、各デバイスはその 派生クラスとして実装しているということでしょうか。 (忘年会のときにPI.さんに垣間見せていただいた、XM6のソースの記憶を 参考にしています) これを使って、例えば、以前は各デバイス内で独自にやっていたタイミング 調整を、共通のイベント管理機構で一括して管理できるようになりました。 あるデバイスから親の仮想マシンクラスに、?μ秒後に呼んでくれと イベント登録しておくと、?μ秒後にそのクラスのイベントコールバック 関数が呼ばれるようになっていて、これを使ってタイマーを駆動したり、 ディスクのヘッダ移動やデータロストなどを実現してたりします。 まだ使ったことはないけど、例えばDMAクラスに、転送元、転送先のデバイス クラスの参照を渡すようにして、任意デバイス間のDMA転送をできるような 実装にしてみるとか。←EmuZ-5500を見越してます。 という訳で、あと数日で新バージョンのリリースを予定しています。 2004/9/6 SASI周りとかプリンタ周りとか、設定関係とか、まだまだ未実装な箇所は ありますが、取り敢えず動作するようになったので公開します。 HPC2000版は、sigmarion3@533MHzで10〜12fps程度は出るようです。 GAPIは却って使わない方が速かったんだけど、何故だろう。 2004/9/7 PPC2002版への対応。 生成した画面を一旦200x320のDIBSectionに縮小して、それをbitbltするだけ。 入力系は、ボタン入力はカーソルキーおよびジョイスティックに割り当て、 キー入力はSIPから行う。 この辺りは、まあいつもやってる内容なので、今更悩むものでもなし。 メニュー項目は、240x320の画面には多すぎるので、control, fdd1, fdd2, screenの4項目に減らす。 これは、単にリソースで新規メニューとして追加してやればよい。 という訳で、割とあっさり完成。 速度的には、まあこんなもんかなあと。 2004/9/8 PI.さんより、XM6のソースの抜粋を送っていただきました。 本当に多謝です。 成る程、各デバイスを派生クラスとして実装することで、こんなことも 出来るのかと目から鱗。 早速、EmuZ-2500でも部分的に盗まさせて頂いてます。 スケジューラとかメモリ周りも、精読すると新たな発見がありそう。 その他、ディスク交換時にクラッシュする可能性があったので、仮に修正 しておきました。 後日、イベントをつかって、ディスク交換時に強制的に一定期間ディスクの 刺さっていない状態を維持するように修正します。 現在の検討課題として、メモリ、I/Oアクセス時のウェイトをどうするか。 実装するのはそれほど面倒ではないとは思いますが、処理速度的に どうかなあと。 2004/9/9 更に内部構造の改造中。 昨晩も含めて変更した点を整理します。 1. cpuクラスもdevice基底クラスの派生クラスとしました。(2.への布石) 2. deviceの派生クラスにprev_divice, next_deviceを追加して、仮想マシン クラスvmのDEVICE *first_device, *last_deviceから順にたどれるようにしました。 device基底クラスのコンストラクタで、以下のように処理しています。 DEVICE(VM* parent_vm, EMU* parent_emu) : vm(parent_vm), emu(parent_emu) { prev_device = vm->last_device; next_device = NULL; if(!vm->first_device) vm->first_device = this; if(vm->last_device) vm->last_device->next_device = this; vm->last_device = this; } これを使って、仮想マシンクラスから、 DEVICE* device = first_device; while(device) { device->reset(); device = device->next_device(); } のように、全デバイスを一括して操作することができます。 3. メモリバス、I/Oバス、割り込み管理を独立したクラスにして、cpuクラスに ポインタを渡すようにしました。 cpu->memory = memory; cpu->io = io; cpu->interrupt = interrupt; という風にクラスのポインタを渡しておいて、cpu内部では、 memory->write_data8(addr, data); return io->read_io8(addr); interrupt->do_reti(); のように使います。 これは、将来Z-80を2つ積んだMZ-3500を実装することを念頭に置いた拡張です。 cpu = new CPU(this, emu); cpu->memory = memory; : cpu_sub = new CPU(this, emu); cpu_sub->memory = memory_sub; ; こんな感じで、バスが複数系統あっても対応することが可能になります。 4. i/oバスを、従来のswitch(addr) { case: } 式の実装から、デバイスの ポインタの配列を使用する実装にしました。 各デバイスは、自分のアドレスを返すメンバ関数 iomap_write(), iomap_read()を 持っていて、これでi/oバスクラスに一括して登録します。 vmクラス内で、 DEVICE* device = first_device; while(device) { io->regist_iomap(device); device = device->next_device; } こんな感じでioに全デバイスのポインタを渡してやります。 ioクラス側は、以下のようにしてi/oマップを作成します。 (map_w, map_rは、ダミーデバイスのポインタを入れて初期化してあります) DEVICE* map_w[256]; DEVICE* map_r[256]; void IO::regist_iomap(DEVICE* dev) { // write for(int i = 0; ; i++) { int port = dev->iomap_write(i); if(port == -1) break; map_w[port] = dev; } // read for(int i = 0; ; i++) { int port = dev->iomap_read(i); if(port == -1) break; map_r[port] = dev; } } ioの読み書き関数はこんな感じになります。 void IO::write_io8(uint16 addr, uint8 data) { map_w[addr & 0xff]->write_io8(addr, data); } uint8 IO::read_io8(uint16 addr) { return map_r[addr & 0xff]->read_io8(addr); } 5. イベントの時間精度を、時間誤差1.5usec以下にまで向上させました。 次回のイベントまでのクロック数を求めておいて、その分を経過するまでは イベント状態をチェックしないようにすることで、処理速度をそれ程落とさない ようにしています。 速くなる修正もあれば遅くなる修正もあり、仮想マシンのみ空回しして計測 したところ、トータルで4%程度の速度低下になっているようです。 まあ体感できるレベルでもなし、実装がすっきりしたので良しとします。 2004/9/10 fmgenで生成した波形を受け取る部分に、重大な問題があったのを修正しました。 fmgenの生成する波形ってステレオだったんですね。←何を今更。 バッファサイズが足りてないんだから、どうりで時々落ちる訳だ。 また、生成すべきサンプル数の算出処理で、整数化することで誤差が蓄積する (しかも周波数によって挙動が違う)という問題があったので、小数点以下の 値の累積も考慮するように修正しました。 ついでに、メニュー周りを弄って、音源再生の有効無効、周波数、遅延時間の 指定をできるようにしています。 sig3では昨日までの修正で思いのほか高速化されていたようで、533MHzに クロックアップして、再生周波数3KHzでそこそこ実機並みの速度で音を鳴らす ことが出来るようになりました。 再生中は6fps程度、再生を止めれば12fps程度で動作します。 サウンド周りで設定項目が増えたので、コンフィグ周りも弄っています。 バージョン情報も入っていますので、違うバージョンのコンフィグファイルを 読み込まないようになっています。 また、ディスク交換時に、1秒ほどディスクが抜かれた状態を維持するように 変更しました。 これは、一瞬でディスクが交換されて、ソフト側がディスクが交換されたことを 検出できない、という状況を防止するためです。 fdcクラス内でコールバックイベントを使って実装してもよかったのですが、 uif周りの話なので、emuクラス内で賄っています。 2004/9/16 sasiの実装は以前と同様に。 ただしイメージ生成機能は別プログラムとしています。 イベントコールバックの登録部のバグ修正。 指定はusecで行って、登録部でCPUのクロックに換算しているのですが、 ここのロジックに問題があって実際は5/6のクロック数になってしまって いました。 これによって、超兄貴@星くずばこ14号が最後の方でエラーが発生する 問題が解決しています。 ce版のより一層の高速化のため、テキスト画面の描画不要判定を実装。 テキスト描画に関わるレジスタが変更されておらず、vramの値が変化して いなければ、描画不要とみなす、という感じで。 ただしpcgが変化している場合は本来描画しなおす必要があるが、pcgの 変更チェックをメモリアクセス部に仕込むと却って遅くなりそうなので、 誤爆覚悟でce版のみ有効としています。 リードモディファイライトで、0xbcの返り値が正しくなかったのを修正。 いったんメモリアクセスで各プレーンの値をラッチしたら、0xbd, bank=0x07で 抽出する境界色を変更したときに0xbcで読み込まれる値も更新されるのね。 …活用研究、間違ってるやん。 という訳で、ペイントの不具合直りましたです。 御報告多謝でした。>60氏 2004/9/30-10/1 ドルアーガの塔で、得点の表示エリアの背景が赤っぽい件。 CG画面の表示範囲指定は、生成したCGの仮想画面において従来範囲外を 0クリアすることで実装していました。 この場合、CGとテキストの合成処理部では、CGはパレット0の色があると いう扱いになります。 で、ドルアーガではパレットレジスタで0に暗い赤が割り付けられたため、 表示範囲外の背景が赤くなっていたと。 これはこれで正しい実装なので悩ましいところですが、取り合えず背景が 背景がパレット0でパレットレジスタ0が2の場合は、背景を0にするように パッチを仕込んでおきます。 もう1件、ドルアーガ。 アナログ/デジタルモニタ選択で、ANALOG MONITORの文字の色が変わらない という不具合。 画面生成部、割り込み、I/O周りと疑ってみたけど、原因不明。 また、M25-BASIC V2のアルゴ機能でエディタを開こうとするとフリーズする という不具合。検証したところ、開くファイル番号を入れた以降、CPUが 割り込み禁止のままになってタイマ割り込みが発生できていない模様。 画面製西部とか、割り込み管理部とかイベント管理部とか、あちこちを 疑ってみましたが。 結論としては、M1サイクルで1ウェイト入れたことが原因でした。 まさか、そんなところが影響していたとは。orz 2004/10/3 またしても、ドルアーガ。 ギルの目とか盾の黒い部分とか、ウィザードの魔法が透明になるという件。 ギルは多色PCGで表示されており、多色PCGの0の部分は必ず透明になるので どうやって黒にしているのかなあと不思議だったのですが。 要はテキスト2画面を重ねて描画するモードで、下のテキスト画面で 単色PCGとして不透明黒で影絵を表示して、その上に多色PCGでキャラを 表示していました。 そう言えばありましたねえ、テキスト2重モード。 描画速度の問題があって、実装を保留にしたまま忘れてました。 これを修正したところ、最近の懸案事項だったギャラガの背景の星が表示 されないという問題も解決つきました。あいや、めでたい。 FM音源とPSGの音量バランス。 基本的に両者の合成はfmgen内部でやっているのですが。 よく考えてみれば、実機ではym2203とdacの外のアナログ回路でミキシング している訳で、そりゃ機種毎にバランスが違ってて当たり前なんですよね。 本来は実機で聞いてみて、バランス調整すべきなんですが。 今現在、実機が部屋にないんですよね。PASOPIA7を置くために移動してて。 誰か調整よろしくです。 2004/10/10-11 日曜は、虎菊コレクションの整理の手伝いにいってきました。 で、現地でたけがみりうさんからご指摘頂いたのが、YM2203のプリスケーラの 設定周りの問題。 通常はアドレスを指定してデータを書き込んで設定完了、なんですが。 こいつだけはアドレスを指定しただけで設定完了らしくって。 で、実装してみたところ、結構色々なソフトで影響があったようです。 そうか、実機ではこんな風に鳴っていたのか。 2004/12/16 EmuPIA7やEmuLTI8にかまけてて、放置していたサポート依頼が溜まって ましたので、幾つか対応をしておこうかと。 まず、環境によってジョイスティックが動作しない件。 ジョイスティックのパラメータでx,y軸の動作範囲を指定して、現在の位置が 各々のmaxのときに、その方向に動かしたと判定しています。 しかし、環境によっては、maxまでいかない事があるようでして。 取り合えず、maxの50%以上の値が返ったら有効、と判定するように修正しました。 テキスト2画面の合成のプライオリティの件。 これが原因で、デモの画面が崩れるとのことなのですが、そのデモを持って いないので、本当に直ったのかどうか… ゼビウスで、背景の下端が欠ける件。 VDでテキスト画面を上にずらしたときに、そういう問題が発生するようです。 以前たごさんに調査して頂きましたので、それを参考に実装しています。 ユーカラの件は、取り合えず保留。 テクザーの動作がもっさりしている件。 これは、8253の動作をパッチで無理やり2倍にすることで対応しています。 副作用なければいいんですが…