QEMU on Windows

This is the tentative page to provide QEMU windows port binaries as Kazu's proxy.
Please refer Kazu's site for more details.

mailto:
t-takeda@m1.interq.or.jp



QEMU/9821 on Windows

This is the experimental work to support NEC PC-9821 on QEMU.



2012/8/20
久しぶりの更新です。
取り急ぎ、修正内容だけ列挙しておきます。

640x400,256色モードでテキスト画面が表示されなかったのを修正しました。
テキスト画面のセミグラフィックをサポートしました。
テキスト画面が1ドットずれる挙動を再現しました。
デジタルパレットが正しく機能していなかったのを修正しました。
表示するグラフィック画面の切り替えが正しく機能していなかったのを修正しました。
GRCGの実装が一部間違っていたのを修正しました。
キーボードの一部キーが機能していなかったのを修正しました。
OPNAのリズム音源のwavを読み込むようになっていなかったのを修正しました。



2011/3/20
突発的にFM音源(OPNA)を実装してみました。
YM2608+CS4231Aなので、98MULTi CanBe内蔵型音源相当ですね。
コアは他のエミュレータでも使用させていただいているfmgenです。

QEMUはCで実装されていますが、そこにC++のfmgenを組み込むのが大変でした。
configure/makefileの書き方がよく判らなくて、ビルドするのにも一苦労で(苦笑)


以前に実装を試みたときは、QEMU側のタイマの精度の問題があったのですが、
今回は、qemu_get_clock_ns()をバックポートして使用しています。
この関数は、Win32環境ではQueryPerformanceCounter()から取得されますので、
環境依存ではありますが、実時間を高精度で取得できるようになっています。

実装的には、qemu_mod_timer()を使って実時間で1msec単位で割り込んでやって、
(1msec単位を指定しても、実際には2〜3msec程度になってしまいますが)
そのときにqemu_get_clock_ns()で取得した経過時間だけ音源を駆動してやります。
その他、ステータスを取得するときに、音源のタイマが駆動中かどうか判定して、
駆動中のときは、やはり音源を駆動してステータスを更新しています。

その他、フロッピーディスクI/Fにおいて、1MB/640KBのモード切り替え時に、
IRQ/DMAを変更する処理を追加しました。
ただし、ディップスイッチの設定は、従来通り1MBモード固定としています。

今回、0.10.6ベースとは別に、最新の0.14.0ベースの実装も試しています。
配布しているバイナリは従来通りの0.10.6ベースですが、ソースアーカイブには
両方のソースが入っています。
KQEMUが使えないこともあって、0.10.6ベースの方が動作は軽くなっていますが、
CPUコアやTCGなど色々改良されていますので、ご興味の方はビルドしてみてください。
mingw以外でビルドするときは、stdc++をリンクするのをお忘れなく。



2010/10/9
最近QEMUにマージされた、i386関係のパッチを幾つかバックポートした結果、
Windows 3.1が起動するようになりました。



インストール時、環境を自動認識させると、640x400, 256色と判定されますが、
これを手動で640x400, 16色に設定を変更する必要があります。


256色モードにすると、こんな感じで画面が崩れてしまいます。
グラフィックドライバが、PEGCがフル実装されていることを期待しているようです。
実機でも、後期のMATEシリーズだと、同様に画面が崩れてしまうようですね。

その他、EEPROMのバンク切り替え周りなど、色々コードを整理しています。
PC-9821Ra43のITF対応のための作業なのですが、こっちはまだまだ掛かりそうです。



2010/9/22
最近の高速なPCだと、ITFの自己診断中にTIMER ERRORとなる問題がありましたので、
パッチでエラー回避するように修正しました。
2010/1/20のバイナリを直接編集してますので、ソースについては更新していません。



2010/1/20
Windows 98を使用する上で必要な細かい修正を施しました。
音源識別IDを読み取るI/Oをサポートしたり、
システム領域を補正して、プリンタポートを強制的にレガシーにしたり、
システムポートやプリンタポートの8255のモードをちゃんと実装したり、
PCI-PC98グラフィックのブリッジチップを追加したりしています。


で、これらの修正の何が効いたのかは判らないのですが。
今までクラッシュしていたWindows 98のインストーラが起動するようになりました。


プラグ&プレイデバイスの検索にえらく時間が掛かりましたが、インストール成功。
これで、実機のバックアップを使わなくても大丈夫ですね。


本来、PC-9801BX4に実装できる最大メモリサイズは64MBなのですが、
システム領域を補正してやることで、512MB認識させることも可能です。
これだけメモリがあると、superπの記録も随分と伸びますね。


IE6とDirectX9C(Feldlotosさんとこのパッチで)とFlash Playerをインストールして、
ニコニコ動画を再生することだって可能です。
流石に多少の音飛びはありますが、エコノミーでしたら動画はちゃんと再生可能です。
PC-9821標準グラフィックの640x480/256色ですが、メモリウェイトのない仮想環境であれば、
CPUパワー次第で結構どうとでもなるものですね。

内部的にはCIRRUS VGAも一応実装してみたのですが、Windows 98ではまだ動作しません。
デバイスはちゃんと認識はしているのですが、画面に何も表示されない状態です。
ログを取ってみましたが、I/Oを色々叩いたまま、VRAMに何も転送していないようです。
ドライバと実装の相性の問題なのかもしれませんが…
冗談半分でVMWARE SVGA IIもサポートしてみたのですが、こっちはドライバ自体が
標準VGA相当部分のリソースも確保してしまうため動作せず。
(こっちはまったく見込みがないので、ソースからも削除しています)

GA-1024A/1280Aの技術資料が手に入るようであれば、そっちを実装した方が早いかも。
どなたか詳細な仕様をご存知の方がいらっしゃいましたらご連絡ください。



2010/1/17-2
KQEMU有効だとクラッシュすると思ってたけど、デバイスの認識をちゃんとすれば
問題なく動いてるやん(苦笑)


ということで、当時のお約束のWCPUIDとsuperπの結果です。
104万桁で1分49秒だと、Pentium IIIの1GHzと同じ程度でしょうか。
PC-9821アーキテクチャでは最速クラスと言ってもいいかな?



2010/1/17
諸般の事情でQEMU本家のツリーに9821パッチをマージするのを断念しました。
マージするために最新ツリーに追従する必要もなくなりましたので、
KQEMUとTCGに対応している0.10.6ベースで開発を継続していきます。

で、現状ではWindows 98をインストールしようとすると、不正なオペコードを実行して
Triple Faultで落ちてしまっています。
そこで、PC-9821Rv20の実機でWindows 98 SEがインストールされているドライブを、
D2Fでイメージにして、それをQEMU/9821で実行してみました。


一度セーフモードでサポートしていないデバイスを削除してから起動してみました。
一応問題なく動作しているっぽいです。
(残念ながら、KQEMUが有効だと起動中にクラッシュしてしまいますが)


IEを立ち上げて、外部のウェブサイトを閲覧することも可能です。


LGY-98は、自動認識では発見できませんでしたので、手動で追加しました。
デバイスの認識はこんな感じです。
QEMU DVD-ROMと、PC-9801/9821特有のデバイスが認識されていることが判るかと。

ちなみに、昨年追加した86音源は、MAMEのライセンス絡みの事情で削除しました。
そのかわり、CS4231音源を追加しています。
Mate-X PCMとして認識されていますが、Windowsで使うならばこっちの方が便利かと。



2009/10/29
qemu-develにV3パッチを投げました。
ついでにこっちのアーカイブも更新しておきます。
SDLのstdoutの件はまた後日にでも。



2009/10/28

86音源を実装してみました。
既存のadlibがMAMEのOPLコアを元にしているので、同じバージョンのMAMEのOPNAコアを
引っ張ってきたら、QEMUとの結合が簡単かなあと思ったのですが。
MAMEのソースアーカイブを遡って調べてみたら、Fork元のバージョンが0.59だったようで。
またえらく古いなあ(苦笑)

で、頑張って結合させてみました。

まあ一応音はなっているのですが。
OPNAのタイマを使ってFM音源を駆動してるソフトだと、再生速度が微妙に安定しないんですよね。
タイマの駆動に使っている、QEMU側のタイマのrt_clockの分解能が1/1000秒しかないので、
タイマの精度がどうしてもいまいちなのが原因だと判ってるんですが。
double型で誤差を蓄積させて、タイミングが大きくずれないようには処理しているのですが、
音の生成処理というのは実時間とちょっとでもずれると人間の耳には判っちゃうのですよね。

vm_clockだと1/1000000000秒の分解能ですが、いまいち実時間の流れとリニアでないみたいで。
サウンドバッファの生成は実時間とリニアなんで、FM音源の駆動源のタイマもリニアでないと、
もうそもそも再生テンポが狂いまくりになっちゃいます。

PCM音源みたいに、バッファからバッファに垂れ流すだけなら悩まないんですが。
なんとも難しいですね。
もうちょい何とかならないか調整してみます。



2009/10/25

256色モードでクラッシュする問題を修正しました。
夢幻泡影で確認しましたが、綺麗に表示できているようですね。
後は480ラインモードとCLGD5430の確認もしないと。



2009/10/24

テキスト画面で、黒色の文字が表示されていなかったのと、
カーソルが表示されていなかったのを修正しました。
DOS SHELLでちゃんと文字が表示されるようになっています。

現在の実装では、VRAMやパレットが書き換えられたり、GDCの設定が変わるなど、
必要なときだけ画面を再描画します。
しかし、カーソルの表示位置を指定するのに使用しちえるCSRWコマンドが、
本来は3bytesのパラメータを指定するところが、PC-98のBIOSはどうも2bytesしか
指定してくれないようで、GDCのコマンドの終了判定がうまくいきませんでした。

カーソルキーを押して次のCSRWを発行すると、やっと前回のCSRWが終了したと
判定して画面を更新するため、カーソルが遅れて動くように表示されるなど、
ちゃんと動作するようになるまで結構苦労してたりします。



2009/10/22
そろそろ、本家MLにv3パッチを投稿するためにソースを整理中です。
うっかりパッチが受理されると、その後の大幅な変更が困難になりそうなので、
今後の実装予定も踏まえて見直しをしています。

あとはPC-9821Rv20のITFをサポートするため、PCI周りの作業をしていました。
注意点は、QEMUのSMRAMの実装が、0xa0000-0xfffffがバンク切り替えされないことを
前提としたものになっているため、その辺の対応が必要になることと、
C-BUSブリッジが0x30番に決めうちでぶら下がっていることと、
ERRSTSの値でPARITY ERRORを検出していることと、
メモリチェックで取得したRAM容量とDRBを比較していること、でしょうか。

現状では、ITFのハードウェアチェックは突破できるようになっています。
ただBIOSに切り替わってから、あさってなアドレスにジャンプしてしまいますが(苦笑)
もうちょい調査が必要ですね。
PC-9821Rv20に対応できたら、次はPC-9821Nr300の対応もしないと。



2009/10/21
Neko Project2を参考に、PnP BIOSを潰すようにしたところ、
FreeBSD/pc98 7.2-RELEASEがインストール&ブート出来てしまいました。
2009/10/6に試してみたときにクラッシュしていたのは、
PnP BIOSで何か処理をしていたのが原因だったみたいですね。

インストール作業の動画をこちらにアップしました。
ついでにニコニコにもアップしてみたり。



んー、これがブートするのが最終目標だった筈なんですが(苦笑)
まだWindowsがインストールできない問題も残ってるし、当面は開発を継続します。


NEC純正のハードディスク起動画面も使えるらしいですが、
FreeBSD/pc98のブートセレクタをインストールしてみました。


ブート中。


rootでログインしてみたところです。


PC-9801BX4のハードディスクの容量制限を勘違いしていたため(ぉぃ)、
今回はMinimalインストールしてしまいましたが、
今度はX環境もインストールして試験してみたいですね。

あと古のPlamo Linux/98も試してみないと。
学生時代にPC-9821Rv20にインストールして遊んでいたんですよね。



2009/10/20
これまで画面の解像度を640x480固定にしていましたが、
画面モードに応じて400/480ラインに変更するようにしました。

数年前に最初にQEMUを弄りだした頃、画面の解像度を640x400にしたところ、
QEMUモニタに切り替えて復帰するとクラッシュするという問題があったため、
それ以来ずっと640x480固定にしていたのですが。
今回試してみたら、何時の間にか現象が発生しなくなっていました(苦笑)


ついでにVGA周りも修正しています。
EGCのリセット処理で、EGC構造体を丸ごとmemset()でクリアしていたため、
VGA構造体のポインタが壊れてしまっていた(恥ずかしい)不具合の修正とか、
GDCからのVRAMアクセスに、EGC/GRCGをかませるようにしたりとか。

N88BASICでは、画面描画にGDCのコマンドを積極的に使っているのですが、
(過去の経緯、でしょうかね)
LINE/CIRCLE命令で正しく描画できない問題が解決しているようです。



2009/10/19
1.23MBのフロッピーディスクに対応できました。
READ IDで取得されるNの値を、セクタ長にあわせて変更したのと、
READ SECTORで、転送するセクタ数の計算方法を修正しました。
(これは、82078とuPD765Aの仕様の違いなのかな?)


また、ディスク交換を検出したときに発生するIRQの処理を変更しています。
ディスク交換でIRQを発生させたとき、偶にこのIRQが処理されないままとなり、
その結果、BIOSがFDCにコマンド送らなくなってしまったり、
既に割込み要求中に更にIRQが発生してしまい、ディスク交換を検出できないなど、
(特にブート時に)タイミングに依存した不具合があったため、
IRQがペンディング中でないときのみ、パルスでIRQを発行するようにしました。



2009/10/18
PC-98で標準的に使われる、1.23MBのフロッピーディスクの対応中です。
QEMUのFDCは、セクタ長が512bytesであることを前提として実装されています。
これに対して1.23MBのフロッピーは、セクタ長が1024bytesです。
この辺の対応が結構面倒くさいですね。


で、取り敢えず実装してみたのですが…
ディスクを読み込み始めるのですが、MSDOS.SYSのロード中にエラーとなります。
中々難しいですね。



2009/10/17-2
Roy Tamさんのご指摘により、一部修正を行いました。
CBUSブリッジの初期化処理の不具合と、PCIなしの場合の割り込みの修正です。
特に前者は危なっかしいので、アーカイブを修正しておきました。



2009/10/17
昨日の時点のgit repositoryをベースに、ソースを整理しました。
MLへのパッチの投稿はもうちょっと先ですが、仮にここでリリースしておきます。



特に意味はないけど、取り敢えずT2からスクリーンショットを2枚ほど。
普通に動いてます。
こうなると、FM音源の実装もしたくなってきますね。



2009/10/15


IDEのコア側の実装が、強制的にLBAとするようになっていたのを修正したり、
Initialize Drive Parametersで最大セクタ数や最大ヘッダ数をちゃんと設定するようにしたところ、
IDEハードディスクからMS-DOSがブートするようになりました。
WIN98SEのCD-ROMの中身を全部コピーしても、セクターエラーが発生しなくなっていますので、
これで実用上は問題なさそうですね。



2009/10/10
IDE BIOSの逆アセンブルした結果を、CPUトレースログ比較しながら処理を追っかけてみました。


ドライブをリセットする前でも、パワーオンの自己診断結果をステータスに出すようにしたり、
F8E8:0010にIDEドライブの接続状態を入れるようにしたり、その他色々修正した結果、
BIOSがIDENTIFYコマンドを実行して、ハードディスクの容量を取得できるようになりました。
FDISKでパーティションを作成することも可能です。


ただし、ハードディスクからのブートはまだ出来ていません。
またWin98SEのインストールを試してみたのですが、データのコピー中に失敗してしまいました。
その後SCANDISKを実行すると、不良セクタになってるようです。
IDEのコア側の実装に、CHSでのアクセスの実行について何か問題があるんじゃないかなと思います。

後は、システム領域などにあるIDEドライブの存在フラグが、ブート時に正しく設定されない件も
調査しないといけませんね。
というか、どうしてあんなにあちこちにあるんだろう(苦笑)



2009/10/6
現状の実装で、最新のFreeBSD/pc98 7.2-RELEASEがどこまで動くか実験してみました。


フロッピーディスクを何度か入換えしつつ、起動中。



ブートメニューが表示されたので、Defaultでブートしてみます。
CPUが約1.8GHzの686-classと認識されています。


結局ブート中に一般保護エラーが発生して御仕舞いです。
まだまだ先は長いですね。



2009/10/5
FDISKを実行しても、IDE関連のI/Oにアクセスせずに「装置がありません」になってしまうのは、
そもそもブート前にドライブを認識してないのが問題なのかなあと。

その辺の検証のために、ITFからBIOSに処理が移るタイミングで、システム領域の0000:0457,
0000:055Dを書き換えて、無理やりドライブを認識した状態にしてみました。


HDUで一応ドライブの存在を認識できているようです。
でもちゃんと初期化を経ていないからか、シリンダ数や容量がおかしなことになっています。




DISKINITでドライブを初期化して、FDISKでパーティションを設定しようとすると、
run-time内で0割りが発生して強制終了してしまいました。
やっぱり容量を認識できていないと駄目なようです。



仕方ないので、ディスクイメージを直接バイナリエディタで書き換えて、無理やりパーティションを
作成してみたところ、一応はFORMATしてデータの読み書きが出来るようになりました。

IDEコア側の実装は問題なさそうなので、後は何故ブート時にドライブを認識できないかの追及ですね。
取り敢えず、CPUのトレースログを取ってみましたが…
あれ、ブート前にIDE BIOS内のコードを全然実行していない? あれ、あれれ???



2009/10/4-2

IDEの実装の検証中です。
ATAPI CD-ROMは正常に動作しているようで、MS-DOSから普通にアクセスできます。
純正のNECCD.SYSとMSCDEX.EXEで大丈夫です。

IDE HDDはまだ駄目です。
FDISKでも「装置がありません」とエラーメッセージが出てしまいます。
セクタサイズの問題かなとは思うのですが…

スクリーンショットでは判りませんが、地味にビープも実装しました。



2009/10/4

RTCについては、従来はuPD4990として実装していましたが、PC-9801BX4のBIOS側は
uPD4993Aとしてアクセスしており、時間を正しく取得できていませんでした。

uPD4993Aのデータシートを発見できなかったのですが、タイムリード時に0.1秒の桁?の
4bitをLSBに追加するのと、シリアルコマンドに対応してやることで、一応動くように。
常にホスト側の時間の値を返すだけで、ゲスト側の時間設定はできません。

sfish様にLGY-98のI/O周りの情報をご提供いただきました。
わざわざご調査いただき、ありがとうございます。
で、実装を修正した上で、LGY-98設定ディスクで確認をしてみました。
MELCHKを実行してみましたが、認識できてないみたいです。


LGYSETUPでもやっぱり駄目でした。
そもそもLGY-98のI/Oにアクセスしてないみたいですが、IRQ関係かなあ。



2009/10/2

ne2000-isaベースでLGY-99を実装してみました。
PicoBSD(98)で見てみる限り、認識している…のかな?


SENSE DEVICE STATUSで、ドライブの状態(ドライブの有無、ディスクの有無)に応じて
返るステータスを修正したり、ディスク交換時に割込みを発生させるようにした結果、
MS-DOS上でディスクの交換を検出できるようになりました。

DALKで試してみましたが、取り敢えず問題なく動いているっぽいです。



2009/10/1-2

CG WINDOWの実装を見直した結果、DALKで文字が表示できるようになりました。
そういやテキストもグラフィック画面に描画してるんでしたっけ(汗)
あとバスマウスもちゃんと動作しているみたいです。





フロッピーベースのPico BSD(98)も、何となくブートするようになりました。
ハードウェアの検出中に止まってしまうのですが、Ethernet関係かな?
NE2000の実装をベースに、LGY98をサポートすることを考えています。



2009/10/1
せっかくMS-DOSがブートするようになったので、てけとーにソフトの動作確認など。




だからって、何故これで動作確認するかなあ(苦笑)
文字が表示できていないですが、グラフィック画面は一応動作しているようですね。



2009/9/30
QEMU本家へのマージを目標に、コードの整理中です。
target-i386に統合して、-M pc98pciでPC-98x1を指定するようにしました。


FDCの修正の結果、MS-DOS 7がブートするようになりました。
現状では1.44MBフォーマットのフロッピーからのみブート可能です。
FDCがセクタ長512bytes固定なので、1.2MBフォーマット対応は難しそう。



2009/9/8
昨日の時点でROM BASICが起動中にフリーズしていたのは、
GDCのステータスの、FIFO EMPTYの挙動の問題だったようです。

従来の実装では、実行中のコマンドに必要なパラメタが全て入力されるまで
EMPTYじゃない扱いにしていたのですが、パラメタ数の多いコマンドなどにおいて、
パラメタ転送中でもEPTYになるまで待機するような場合に問題になってました。
取り敢えず、EMPTY立ちっぱなしにしたところ、ROM BASICが立ち上がって、
操作ができるところまでいきました。

ディスクをまったく読まずに、ROM BASICにいってしまう問題はありますが、
曲りなりにもブートしたのは目出度い限り。



FDC上で、SPECIFY+RECALIB+SENCE INTSTATコマンドを実行して、ドライブの有無と
ディスクの挿入チェックは出来ているっぽいのですが・・・



2009/9/7
FDCで、RECALIBを実行したあとのSENCE INTSTATの挙動を修正中。
取り敢えずN88BASICがブートしようとするところまで来ました。



…これで、やっと前回頓挫したところまで追い付いただけ(苦笑)

その他、TARGET_I386に寄生して実装していたのを、
コードをTARGET_PC98に分離して別バイナリにしたりしてました。

QEMUでは、同じCPUで異なるアーキテクチャのマシンを実装することを
余り考慮していないみたいで、TARGET_I386でCPUコアのコードを括ったり、
PC/AT仮想マシンのコードを括ったりしてるのが面倒です。

今回は、CPUコアを括っている部分の#if defined(TARGET_I386)を、
defined(TARGET_I386)||defined(TARGET_PC98)に置き換えて済ませましたが、
CPUの種類とアーキテクチャの種類を示すdefineを分離することを
提案した方がいいかもしれませんね。



2009/9/5
ITFも随分と処理が先に進むようになりました。
RAMウィンドウを実装することで、BIOSやIDE-BIOSを裏RAMにコピーして
そっちに切り替える処理も通るようになりました。
TIMER INTERRUPT ERRORの問題も、パッチなしで動作するようになっています。
後はプロテクトモードチェックが通れば、もうBIOSに処理が移るところまで
いきそうです。

で、ITFの処理を追っかけていて、起動時点での外字領域の値によって
実行される処理を幾つか見つけました。




ITFやBIOSの書き換え用ルーチン?
この処理に入る前に、やはり外字領域の値によって、FLASH ROMの電源電圧を
5Vから12Vに変更する処理もありました。




ハードウェア情報やBIOSのバージョンチェック。
こちらはそのままメモリカウントに移行するようです。



2009/9/3
前回のTIMER INTERRUPT ERRORで引っかかってるチェック処理。

 xor ah,ah
 sti
 test ah,FF
 jnz 849E

stiの直後に割り込みが発生することを期待してるんだろうけど、
この辺はQEMUのCPUとPICの実装次第だしなあ。
ということで、取り敢えずITFにパッチを当てて無理やり突破。

$B0000-$BFFFFのEMSは素直に実装。
VRAMと同じくメモリマップドI/O扱いなので、裏RAM上のコードを実行すると
クラッシュしてしまう筈だけど、まあ仕方ないですね。




うーん。
PC-98のA20ラインマスクって、PC/ATのようにA20だけマスクでなく、
A20より上が全部マスクという仕様で良かったでしたっけ?


起動時にA20ラインをマスクしないとエラー。
まあそりゃそうだろうなあ(苦笑)



2009/9/2
外字領域、不揮発メモリ、ソフトウェアディップスイッチの初期値を設定してやることで、
メモリチェックに進むようになりました。


EMSエラーはまだ未実装なので仕方ないですね。
TIMER INTERRUPT ERRORが発生するのはちょっと解せないです。



2009/8/28
PC-9801BX4をターゲットに仕切り直し。
現状では、正面からITFのハードウェアチェックを突破すべく、
少しずつ実装を進めているところです。


外字領域を何かしらのフラグに使っているようで、起動した時点で
然るべき値が入っていないとSET THE SOFTWARE DIP-SWITCHとなります。


こっちは描画処理の虫取りがまだ済んでいなかったときの画像。



TAKEDA, toshiya's HOME PAGE