下から上へ 並んでいます。
今まで、日記の流れが上から下だったのですが、下から上に変えてみました。
これで、更新されたかどうかがすぐにわかるようになると思われます。
注意 初めての方は、このページの下のほうから読んでください。
Win版で、徐々に、GUIの搭載をはじめています。 ただ、もうちょっとしたら、忙しくなるそうなので、それまでに、なんらかの形を出したいと思っていますが、なかなかねぇ・・。(^^;
とりあえず、必要最低限ということで、テープの交換と、ディスクの交換が出来ます。 あと、設定パネルで 機種選択と、FDDの接続のオンオフと、音源のオンオフが出来ます。とはいえ、ほとんど、再起動しないと行けませんが・・。(^^;;
あと、FDDの接続のオンオフですが、接続していて、ディスクが入っているときは、当然使えますが、接続していても、ディスクが入っていないときは、DISK BASIC 自体は使えるけど、Error を返すようにしています。
あとは、ROMが無かったときの挙動を決めないと・・。(汗) ROMが無いからといって、安易に終了していたら、GUIで設定出来ませんからね。(^^;;
音源使用時に、メモリー使用量が増えていく件ですが、やっと分かりました。 まず、マルチスレッドは、関係がないようです。 同じようなプログラムを、シングルスレッドにして、テストしても、同じような問題があることが判明しました。(汗)
Webを探していたんですが、なかなか見つからず、途方にくれていたんですが、やっとこさ見つけてきました。(^^; waveOut系を使うときは、次のようにしなければ鳴らないようです。(汗)
結局、メモリー使用量が増えていくのは、4の waveOutUnprepareHeaderを省略していたからです。(汗) うーん。Webページによっては、waveOutUnprepareHeader は、クローズの前だけでよいとかかれているときも有るようですが、何回も実行するときは、必ず、実行しないと、メモリー使用量が増えていきます。(^^;
これは私の推測ですが、、waveOutPrepareHeader でメモリーを lock して、waveOutUnprepareHeader で、unlock しているんじゃないでしょうか? だから、waveOutPrepareHeaderだけだと、lock しているメモリーが使えないので、どんどん新しい領域を確保していくのでは?
でも、もとはといえば、なんとなーく動いてしまう、Windowsにも、問題があるんですよねぇ・・。(^^; エラーでとまってくれたほうが、なんぼかましです・・。(苦笑)
SRで、タイマー割り込みの周期が遅くなるというバグを調査しました。
void SetTimerIntClock(long pow) { TimerInt_Clock = BaseTimerClock * (pow + 1) / 4; }
上記のプログラムを見てください。powが設定値で、TimerInt_Clockが タイマー割り込みの周期になります。 非SRでは、powの値がデフォルトで3になるんだけど、SRでは、127になるので、TimerInt_Clockの値が 異常に大きくなるのが原因。 しかし、何故、127に変更されたのかは、不明だったり・・。(汗)
しかも、モード6の間は、127だけど、モード1-5になった瞬間に、3になるようだ・・。 やっぱり、内部的には ハードウエアが2つ用意されているんだろうか・・。(^^;
実機で調べてみました。実際には、out&hf6,xx という命令を実行するんだけど、xxが powの値にあたります。 powの値を小さくすればするほど、カーソルの点滅が速くなっていく・・。そして、10あたりから、キー入力が困難になり、4か5ぐらいで、キー入力できなくなって、ギブアップ。 リセットを押す羽目になってしまった。
非SRの初期値が3ということは、BaseTimerClock に1を掛けることになります。ということは、SRモードのときは、powに1を足して、128で割っておけばいいのかな?ということで、組んだのが、以下のプログラム。 pow が4か5ぐらいで エミュレータがとまってしまうのも、再現されたので、おそらくこれでいいと思いますが、何故、割らないといけないのかは、謎のままだったりします。(汗)
void SetTimerIntClock(long pow) { if(!sr_mode) TimerInt_Clock = BaseTimerClock * (pow + 1) / 4; // 非SR else TimerInt_Clock = BaseTimerClock * (pow + 1) / 128; // SR }
キー入力といえば、今のバージョンだと、BASICでゆっくりキー入力しないと、キーを取りこぼしてしまうという、かなり痛いバグがあります。(汗) もともと、Unix版のキー入力は、Z80 CPUがキーボードをチェックするときに、イベントから読み込んで、キー入力されているかどうか判別して、キーが押されているというイベントがあれば、押されているという処理をしていたんですが、
Windows版では、メッセージループの中で、キー押下のメッセージ(WM_KEYDOWNなど)が来たら、一度 拙作のリングバッファに貯めて置いて、Z80 CPU の中から、キー入力のチェックをするときに、リングバッファから読んでいます。
ということは、Z80 CPU等時間のかかる処理を実行中にすばやくキー入力した場合、WM_KEYDOWNも受け取れないので、取りこぼしてしまうのでは?と考えられます。 まぁ、今までの実装は、とりあえず 動くようにしただけとはいえ、これは痛いですね。なんとかしないと・・・・。(汗)
あと、Caps Lock が反応しないというバグがあります。(汗)
キークリック音を出したくて、色々調べていたんですが、結局、エミュレータで、以下のプログラムを実行すれば、鳴るみたい・・。
上のプログラムは、何をしているかというと、ノイズのCH1を繰り返し、オンオフしているんですが、これで、あの独特のクリック音が鳴るんですね。知りませんでした。(汗)
実際のキー割り込み処理では、もっと短時間に PSGレジスターを変化させているんだけど、今の音源ルーチンでは、音データを作ってない瞬間があるので、きわめて短時間にPSGレジスターを変化させても、反映されないことのほうが多く、結果的に鳴らないんだろうと思う・・。しかし、たまに反映されるときがあるので、それが ぷすっていうノイズになって聞こえてしまったりするときも有るとか・・。(汗) この辺は、悩み中です・・。うーむ。
前から、気づいていたんだけど、Windows 版でサウンドを有効にしていると、鬼のようにメモリ使用量が増えていきます。(汗) タスクマネージャを見ていると、すごいです。 たまになら、そんなに気にならないんですが、約1秒ごとに、4KBも増えてくれるとは・・。(汗) おまけに、ハンドルも消費(浪費)してくれます。(汗) これでは、長時間起動できないじゃないですか・・。(汗)
やっぱり、音源スレッドが怪しいみたい・・。初めは、CreateThread()と、Cランタイムルーチンを同時に使っているから?と思っていたんですが、Cランタイムルーチンを使わない、単体テストをしても、同じ症状・・。
原因となるのは、パイプのReadFile の箇所だったり、waveOutWriteだったりするんだけど、これらに共通するのは、内部でバッファを使っていることかな? もしかすると、ヒープ関係なのかな?
Borland コンパイラのヒープ関係は、スレッドセーフじゃないのだろうか? 一応、BeginThread というラッパー関数が有って、これを呼べば、スレッドを作成しても、ヒープがスレッドセーフになります。という記述を見つけましたが、なんと、DelphiのSystemモジュールなので、SDKプログラムからは、呼べないです。 やっぱり、素直に C++Builder & VCL で組めば良かったのでしょうか?(^^;;
やっぱり、Visual C++ 系でないといけないのかなぁ・・。(汗) ただ、Visual C++ って、どれを買えばいいのか分からない上に、ばり高いのでちょっと躊躇しています。(^^; できれば、SDKによるプログラムも楽々 デバッグできるやつがいいなぁ・・。 Turbo Debugger は、頼りなさすぎだし、C++Builderでは、SDKプログラムを デバッグできないようですし・・。(できなくはないようですが・・。)
というわけで、なかなか、Release 4 に向かえないのは、このせいだったりします。申し訳ないです。(_) ちなみに、もし、リリースするとすれば、音源を無効にしたままリリースすることになってしまうでしょう・・。(汗) ただ、それだと、意味が無いんですよねぇ・・。(^^;;
なんと、ドアドアmk2が動きました。(汗) ロード中の音楽も鳴るし、ゲームも出来ます。 でも、iP6Winでは、もっと苦労されていたのに、なんで動くんでしょうか? おかしい・・。(汗)
でも、もしかすると、たまたまなのかも知れませんし、まだまだ予断を許さないですが・・。 もしかすると、2倍モードだとだめなのかも・・。謎ですね。 ただ、これをテストするたびに、長時間のロード時間が必要になるのが、ちょっと・・。(^^;
というわけで、スクリーンショットを載せておきます。 しかし、相変わらず ボーナス面の次の面がどうしてもクリアーできない、不甲斐ない自分を発見してみたり・・。(^^;
ちなみに、タイトルバーに書いてある、Rel.4 RC1 とは、Release 4 のリリース候補版その1という意味ですが、実際には、まだRC1は出ていません。(^^; なんだか、Mozillaみたいですね。
2002年10月27日追記とおもっていましたが、やっぱり、たまたまだったようです・・。(汗) でも、ゲームまで出来たのは、本当ですが・。(汗)
Z80 CPUのタイミングを改善したことで、なんとかTAPEが読めるようになったみたい・・。確かに今まで、BASICのプログラムさえよめなかったのが、ちゃんと読み込んでいる! (^^ 調子に乗って、ドアドアmk2を読んでみると、ロード途中の音楽(たらったたらた たたたたた・・)が流れてきて、画面も出始めてきた!
やったーーーー。と思ったのもつかの間、ロード途中で、音がノイズだけに支配されてしまい、一気に、ブルーに。(^^;; 画面の絵のロード自体は、ちゃんと行われているときも有るみたいだが、最終的にゲームまでにはいかないよう。 まぁ、この辺は 元のiP6 for X11から、ほとんど変わってないので、当然といえば当然かもしれない・。(失礼・・。) この辺は、色々実装しなければ鳴らないところや、大きく作り変えなければならないところが多いらしいので、前途は多難です。というか、本当に出来るんだろうか(汗)
ちなみに、ドアドアmk2というゲームは、テープから読み込むようになっていたのですが、テープから読み込み中に、音楽を鳴らしながら、テープから読み込んだデータで、画面に一枚絵を表示したり、プログラムを後ろの番地から書き込んでいったりするという、とんでもないプログラムだったりします。(^^; これを考えた人は、本当にすごいですね・。
ドアドアは、中村光一さん作です。復刻してくれないかなぁ・・。宇宙人をドアに閉じ込めるというパズルアクションゲームなのですが、この斬新なアイディアは、今でも十分通用すると思われます。(^^;
今まで、Z80 CPUは WM_TIMERメッセージを受け取ってから、起動していたんですが、WM_TIMERって、一番精度が悪いし、何より、Pentium 2-400MHz でも、Pentium 4でも、同じぐらいの速度しか出なくて、それ以上速くするのは無理。 これじゃいけないということで、色々模索していたんですが、iP6winの作者様から紹介が有ったのもあって、98エミュレータの neko project のソースリストを解読・・。
これって、すごいですね。(^^; メッセージループのアイドル部分に CPUのエミュレータが入っているんですが、GetTickCount()で、タイミングをとっていて、なにやら細かい処理もしています・・。
全く同じじゃないけど、Neko に似た方法で、タイミングをとるようにしてみたら、だんだんそれっぽい速さになってきました。 Z80 CPUの実行前から計測して、10ms 待つというのを 60回繰り返してみる・・。 本当は、16ms を60回で、約1秒のような気がするんだけど、それをすると、遅くなってしまう・・。何故だろう?
このパッチが、SR改造パッチという名前なのに、モード6 (SR MODE) で全く音が鳴らないのは、問題があるので、ハックしてみました。
ちなみに、SR以前は、AY-8910というPSG音源が使われていましたが、SRでは、YAMAHAのYM-2203 というFM音源が使われています。
BIOSを見てみると、YM-2203 がbusy 状態だと、データ出力はしないようになっています。 普通なら、readyになるのを待つとかいう処理があると思うんですが、そうなってませんね。(^^; ということで、ずっと ready状態になるように変えたら、鳴ってくれました。嬉しいです。
とはいうものの、AY-8910だけなので まだ、PSG音源だけなんですよねぇ・・。 FM音源を鳴らすためには、またしても、音源関係をいじる必要があります。(^^;;
そこで、FM音源エミュレータは、最新のfmsxから取り込んだのを入れるか、M88エミュレータのfmgen を入れるかは、色々駆け引きがあったりします・・。出来れば、fmgenを入れてみたいですね。 あと、SR以前の機種だと今のPSG音源のままで、SRなら FM音源エミュレータで鳴らしたり・・。という風になっていると、さらに楽しいかも。(って、そんな面倒くさいことを・。(^^;;)
ちなみに、SRでは、play ,,,"c","e","g"とすれば鳴るようになります。
ついに、音が正常に鳴りました。(^^; やったー ばんざーい。(^^; どうやったかというと、どうしても分からなかったので、fmsx の最新のソースリストから、アイディアを拝借してきました。(^^; 本当に、感謝感謝です。
簡単にいうと、一定時間毎に、音データを作成して、それをすぐにデバイスに出力する。これだけです。
まぁ、考えてみれば、当然のことなんだけど、Unix版がそうなっていたので、ずっと次のように組んでいたのが敗因でした。(^^;
今までは、とにかくフルスピードで、音データを作成して、その合間に 音データを出力していましたが、明らかに 出力のほうが間に合ってませんでした。(汗) スピード調節をしても、上手くいかないし・・。まぁ、考えてみれば当然かもしれませんが・・。(^^;
具体的には、音源スレッドでは、メッセージ処理(GetMessage)で、waveoutのDONE (データ出力の終了通知)が来るのを 待っています。 DONE が来たら、音を作成して、デバイスに出力します。
あと、PSGレジスターを書き換えるのは、最新のソースにはなぜか載ってなかったので、メッセージがこない間にするようにしました。(PeekMessageで確認するやつです。)
苦節何年・・。これで、音が正常に鳴りました。(^^; あとは、急に音が長くなったり、短くなったりする問題を解決しないといけませんね。 長い曲を鳴らすと かなり気になります。ゲームの効果音ならそれほど気にならないんですが・・。 おそらく、Z80 CPU の動作が 一定していないためだと思われます。 これを直すのも、大変だと思います。
とはいえ、ここまで 猛スピードでやってきたので、色々弊害もでてきているので、本当は、その辺の修正もしないといけないのですが・・。(^^;
ちょっとややこしくなってきたので、音源関係が、どういう処理をしているか、箇条書きにしてみました。 ちなみに、エミュレータ本体は、メインスレッドで動作、音源は 音源スレッドで動作しています。
という、面倒くさいことをやっているんですが、実をいうと、ループバッファの読み込みより、書き込みのほうが圧倒的に速いことがわかりました。(汗) ということは、読み込みが追いついてない分、データが欠落していて、それが結果的に 音をへろへろにしているみたいです。(汗)
うーん。これは由々しき問題ですね。(^^; 対処するためには、 書き込みポインタより早く、読み込ませる必要があります。
一応、対処療法として、書き込み中にリングバッファが一杯になったら、少し Sleep させることにしました。(汗) (って、めっちゃ対処療法ですけど・・。)
これで、音自体はかなりまともに鳴りました。 ただ、出だしがめっちゃ遅かったりして、かなり変ですが・・。(汗)
ゲームの音楽を聴いてみると、かなりおかしいですね。(^^; まず、出だしが遅いので、絵と音が合っていません。それから、遅いときもあれば、逆に速いときも有ります。
速かったり、遅かったりするのは、Z80 CPUが、正しく定期的に動作していないのもあるかも?と思われます。 今は、WM_TIMERで、定期的に呼んでいますが、実は、WM_TIMER って精度がかなり低いらしいですね。(汗) それに、Windowsはマルチタスクなので、ほかのアプリの影響も受けます。 この辺は、ほかの方法を模索する時期にきているのかもしれません・・。(汗)
ちなみに、リングバッファをもっと大きくする!という方法も考えましたが、メモリーの使用量が増えただけで、根本的に無理なようです・・。(汗)
ついに、エミュレータで音が出ました。 とはいうものの、かなり妖しいですが・・。(^^;
本来なら、連続で発声されないといけないのに、ぶつぶつと切れてしまいますし、 反応も かなり悪いです。
これは、バッファをある程度溜め込んでから、一気に出力しているからです。 だから、出だしが遅れるし、間に合わないようです。(汗)
バッファを縮めて、10段バッファリングを使うようにしたら、かなりましになりました。 しかし、音自体は かなり へろへろになってしまいましたが。(汗) 本当に、へろへろなんです。 やっぱりこの辺は、まだまだテストを繰り返さないと、アルファバージョンも出せないですね・・。(汗)
あと、家に本があったので、一応、Direct Sound も読んで見てみたのですが、なんか読んでいるうちに くらくらしてきました。(^^;;; あれほどややこしいと思っていた waveOut 系のほうが 簡単に思えてしまうほどです。(^^;
M88エミュレータの FM音源エミュレータfmgenというライブラリがあります。 これって、かなり性能が良くて、色々実装実績が多いのですが、謎もかなり多いです。
もともと、M88の中で使われていた FM音源エミュレータの部分だけを 切り出したものなので、仕方ないといえばそうなのですが・・・。
私は、FM音源は時期尚早と思い、PSG音源からやろうとしました。 しかし、初期化するときに 音源の動作クロックを指定する必要があるんですが、一体、何クロックで初期化するのか、さっぱりわかりません。(汗) できれば、省略したときの デフォルト値が欲しかったです・・。(^^; これは。M88のソースリストを解析することでわかりました。
それから、Mix 関数で、波形を作ります。呼び出すときに、出力用の配列と、サイズのようなものを渡すんですが、ここにも落とし穴が・・。(汗) 実は、Mix 関数では、指定したサイズの2倍の配列を 渡す必要があります。(汗) ということは、これはサイズではなく、何個データを出力するか?ということだったんです。
実は、opn(FM音源)ドライバには 2倍の領域が必要と、コメントで明記してありましたが、psgのほうには 何も書いてないので、見事にはまりました。(汗) 何回も、落ちると思っていたんですが、なんと領域破壊を繰り返していたようです。(劇汗)
まぁ、デバッガを使わずに、素でプログラム(エディタで編集して、コンパイル&実行して・・。)していた私も悪いんですが、デバッガで開いてみて初めてわかり、愕然となりました。(それでも、びくともしないOSもすごいと思うけど・・。(汗))
あと、波形データを入れる配列が32ビットというのも気になります。というのも、普通、PCM音源といえば 16ビットですから、あきらかにオーバーしています。 これは、初め、面食らっていたんですが、 おそらく合成するときに 32ビットのほうが精度がいいからでしょう。 あとで、16ビットに落とす処理を 独自に行う必要があるようです。(汗)
あと、何度やっても波形が出ない・・。というか、全く同じ16進数コードしか出ないんですけど・・。と思っていたら、PSG音源の mixer をいじるのを忘れていました。(^^; BASIC上でやるときは、予め設定されているので、やる必要がなかったんですね。勉強になりました。(って、一ヶ月ぐらいこれで悩んでいたけど・。)
まぁ、苦労の甲斐あって、なんとか PCMファイルに落として、プレイヤーで聞くことが出来ました。(^^; やはり、良い音ですね。
いつか、SRエミュレータで FM音源を実装するときに、これでやりたいですねぇ・・。(汗)
Windows版は、サウンド機能がまだないです。へろへろでもいいから、つけたくなりますよね。実は、iP6 にも 音源エミュレータがついています。 このエミュレータは 元々 fMsxのもので、かなり古いものなんですが、それでも一応、聞けるはずです
結局、音源部分は、別スレッドで動かすことにしました。 マルチスレッドは初めてだったんですが、一応(?)、動いているようです。 ここから先は、エミュレータ本体を親、別スレッドを、子とします。
しかし、一番悩んだのは、親から子へ データを転送する方法です。 一番手っ取り早いのは、パイプを使うことですが、Windowsのパイプって、かなり種類が多くて、NT系オンリーでしか使えないやつがあるので、注意が必要です。 ただ、NT系オンリーのほうが便利なんですけど、それだけでやってしまうと、98系を切り捨てることになります。この辺は、まだまだ悩むことになりそうです。(汗)
あと、面食らったのが、子の方で データを受信まちしていると、指定された数のデータがくるまで、ずっと、待っていることです。(汗) いわゆる、バッファリングが有効になっています。 データがないときでも、絶えず波形を作らないといけないんですが、これだと、受信待ちでとまっているので、いつまでたっても、波形を作ることが出来ないんですよね。(^^; 結局、バッファリングしないというのを設定できる APIを見つけたのですが、もしかすると、NT系でないと使えないかもしれません・・。(汗)
とはいえ、まだエミュレータで演奏するところまで いってないです。 まだ、エミュレータ自体は、PCMファイルを出力するだけです。 あとで、pcmファイルを開いてみると、ちゃんと演奏されたので、波形は 正しく出ていることがわかりました。ただ、波形が出ても、あとは、エミュレータ自体で演奏できるかどうか?が 最大の問題です。(汗)
思えば、iP6Winでも、かなり苦労されていたので、上手くいくかどうか・・。(汗)