現在Windows 3.1の起動までこぎつけられないかと思ってCPUコアをいじってるのですが、まずLARインストラクションで詰まってたのでそれは単にLARを実装して突破したのですが、INT 91Hで詰まってます。
どうやら、Windows 3.1は、リアルモードのバイナリのうち処理を奪う必要がある個所に 63H (ARPL)を書き込んで、Virtual 86モードでINT 6を出させて処理をいったんカーネル側に移して、その後、IP+1から処理を再開、としているようです。最初ARPLを見たときは何かの間違いではないかと思ったのですが、最初のうちそのままで順調に進んでいるようだったので、どうやらARPLを書き込むこと自体は正しいようです。ARPLが書き込まれる場所の多くは、書き込み前は90H (NOP)になっているのも多分この書き換えが意図的なものであると考えて問題なさそうです。
INT 6ハンドラは、0048:00000102ですが、どうも169Cセグメント(DOS?)内でARPLが起きることを想定しているようで、INT 6を受けて、Exceptionが起きたインストラクションが63Hであった場合、Exception発生個所+2バイトに飛び、その場でリアルモードへの切り替えが起こるように書いてあるようです。0048セグメントはひょっとするとEMM386かも。
しかし、あるタイミングでIDTが書き換わって、INT 6をハンドルするのが0028:80006E5Cに変わり、再度0048:00000102に変わった後で、INT 91Hのハンドラの入り口(07F9:000023F4)のARPLでINT 6が出た後、00B8セグメントのあらぬ場所にジャンプしてクラッシュしてます。INT 91Hのハンドラは先頭にARPLが書かれているのですが、+2バイトではリアルモードに切り替わるように書かれていない上に、00B8セグメントのベースは169C0Hを指したままなので、違うセグメントの無意味なアドレスにジャンプということになってます。
MSDOS.SYS以外のINTのARPLは0028:80006E5Cだとうまくさばけるようなので、IDTが書き換わるべきではないのか、INT 91Hは別の処理の仕方であるべきなのか、そもそももっと前の時点で何かがこわれててINT 91Hが出るべきではないのか、なかなか難しいですね。どうもこの INT 91H (コンソールBIOS) はコンソールに1BHを書き込もうとしてるようなので、すでにコンソールは表示されていないことを考えると実はINT 91Hが出ていること自体が間違ってるのか。
なお、Core i9のPCを買いました。最近シングルコア性能は世代が変わってもほとんど変わらんと思っていたのですが、案外変わってたんですね。多分486SXの66MHz近いスピードが出てるようでStrike Commanderが快適になりました。ただ、nVidiaのGPUだったので、OpenGLのVSYNC待ち(なぜかnVidiaはデフォルトでオンにしている)をオフにしないと待ちが入ってスピードが落ちるという問題がありました。
2021-01-21 00:40:27
その後、どうやらクラッシュしてるのはWIN386.EXEが起動に失敗してDOSに戻ろうとする途中のような気がしてきました。なにやらPage Faultが起きてるんですね。そして、そのハンドラの中で同じ場所に突っ込んでPage Faultが起きて、それをハンドルしようとしたところでどうやらあきらめてDOSに戻る途中三度目同じところに突っ込んで、三度目はIDTがEMM386のIDTに書き換わっているようで対応しきれなくてクラッシュ、みたいな感じです。そのPage Faultは000C8000~のFM-R VRAMにアクセスしようとして発生しているので、その領域にアクセスしようとしたらPage Faultになること自体は正しいっぽいのですが、多分、その領域にアクセスするパスに入ってくるべきではなさそうです。
それで、WIN386.EXE (DOS6もだけど)の中で、RETFをCALLFとかJMPFとして使ってる個所がたくさんあって、コールスタックがめちゃくちゃになってしまうんですね。ということで、シンボルに情報としてJMPF_BY_RETF, CALLF_BY_RETFを記録してそこに突入したら適宜コールスタックの更新の仕方を変えてデバッガで流れが見やすくするところから始めようと思ってます。先は長そうですね。IRET_TO_VM86 (Stack Return to Virtual 86)は考えてみたら検出可能なので対応したのですが。
2021-01-23 02:25:01
↑の問題は、Page FaultでCR2にリニアアドレスを入れてないという問題でした。それを直して、今は、INT 31Hが出てるのですが、対応するIDTのエントリが壊れているらしい (80286の16-bit Trap Gateだと思ってる) というとこですね。一応、今Unit Testを流してるので、通ったらそのバージョンのソースをPUSHする予定です。
2021-01-24 07:51:07
異なるPrivilege間のINT/IRETは同じPrivilege間のINT/IRETと微妙に動作が違うことに気が付いて実装したら↑の個所は通過したんですが、INT 31H AX=0602H (Mark Real Mode Region as Pageable)の中で戻るべきリニアアドレスのページテーブルエントリのPresentビットを0にしてしまって、IRETでPage Faultになってるところで止まってます。ひょっとするとこれが正しい動作でPage Faultを出すべきなのかもしれないんですがね、ただ、そこらじゅうでPage Faultのチェックを始めるとCPUコアが明らかに遅くなることが想定されるので、やるならIRETで抜けるところでチェックかと思ってるのですが、しかしここでPage Faultを起こすのも何か変な気がするので現在 INT 31H AX=0602H の中身を見てます。いやな予感としては、メモリを十分搭載していればPage FaultでSwapは起こらないだろうとみていたのですが、V86モードのかたまりのWindows 3.1だとPage Fault起こしまくりでリアルモードメモリをスワップしてたりして。それだと、それに対応するためにTownsオリジナルアプリの動作を遅くするぐらいならWindows 3.1は対応しない方がいいかもしれないですね。ある程度はFractal Engineのために対応してはいるのですが。
2021-01-30 02:36:21
山川機長さんのお気持ちとしては、"FMTOWNSで動作していたソフトウェアは全て動作する"を目指されているのだと思うのですが、状況を見ていると仰る通りTOWNSオリジナルアプリの動作が遅くならない方法が見つかるまではWINDOWS3.1を先送りしても良いように思います。
幸いにしてTOWNSでWINDOWS3.1を動かさないとならない人もかなり少ないでしょうし、実際の所はWINDOWS3.1に対応してもインストールする人は(ほぼ?)いらっしゃらない物とも思われます。(個人的には、WINDOWS3.1<Linux<WINDOWS95の順のような気がします)
それにしても、WINDOWS3.1って相当アクロバティックな作りなんですね・・・
2021-02-01 15:27:12
まさにその通りで、Windows 3.1はとりあえずこの間実機のHDDから既にインストール済みのイメージを抜き出すことができたのでやってみている感じで、Windows 3.1そのものにはあまりモチベーションが無いんですよね。Windows 3.1が動作しなければWindows 95も動作しないと思うのでやってるというか。ひょっとするとLinuxの方が素直にできているかもしれません。
それにしても、本来であればタスクを切り替えるとき、Page Faultが起こらないようにページをマップしたうえで切り替えるべきな気がするんですけどね。試しにIRETしたところでPage Faultを出してみたのですが、これで正しくメモリがマップされて戻ってきたらPage Faultをわざと出させている可能性が高まったのですが、ぜんぜん無関係の領域がマップされたので、それほど単純でもなさそうです。
春学期も始まってしまったので、多分この問題はしばらく放置状態になりそうです。
2021-02-03 00:41:18
ありがとうございます
2021-02-04 11:18:48
うーん、どうしてもWindows 3.1が起動しない原因が解明できないですね。Windows 3.1を起動させるために本来のTOWNSアプリケーションのパフォーマンスが落ちるようであればあきらめようと思っていますが、原因がわからんというのが気に食わないんですよね。原因が解明できた上で、これをサポートする価値は無いという結論だったらそれはそれでいいのですが。
現在、
00A7:0000C7B8 INT 31H
AX=0602h Mark Real Mode Region Pageable
BX:CX=Starting Linear Address (0003:5410)
SI:DI=Size (0008:ABE0)
それで、このときはアドレスサイズ、オペランドサイズともに16という半端なプロテクテドモードで動作していて、Windows 3.1のDPMIインターフェースによると、INT 31H AX=0602Hは、リアルモードの線形アドレスをページ可能にするというもののようです。
この関数の中で、BFFFFHまでのリニアアドレスのページテーブル情報をごっそり消してしまうのですが、問題は、このINT 31Hの発射地点はリアルモードだとAE84:C7B8、物理アドレス=線形アドレス=BAFF8Hにあたり、戻ってきたときページが無くなっててそのままクラッシュになってしまいます。
ひょっとしてページフォールトを検出してマップしなおすのかという可能性も考えたのですが、多分それは正しくないように思います。スワップアウトするでもなく、ハイメモリ領域に移動するでもなく、いきなりページを消してしまったのでは、ページフォールトが起きてももともと何があったのかわからんと思うんですね。なので、使用中のページは消すべきではないと思うんですね。
その仮定が正しいとすると、
(1) INT 31Hを出すときのサイズ(8ABE0H)が間違っている。ちなみに、このサイズは起動時にチェックしたリアルメモリ実装量、TOWNSの場合はC0000Hから計算されている模様。
(2) そもそもこのコードがC0000H以降に置かれるべきコードが何かの間違いでBAFF8Hにある。
(3) INT 31H AX=0602hは本来使用中のリニアアドレスのページは消さないはずだが、間違ってBAFF8H付近は未使用と思ってる。
(4) INT 31Hから戻る前に本来であれば使用中ページはハイメモリに移動してページテーブルもそれに対応して更新してから戻るべきところが、なぜか移動とテーブル更新が起きてない。
などの可能性を考えているのですが、いかんせんWindows 3.1の内部という難敵を相手にしているもので、なかなか難しいですね。
このINT 31H AX=0602hの本来の挙動というのはどうあるべきか、誰か知ってますか?
2021-11-02 10:47:44
(ほぼ需要がゼロなのに)あきらめずにWindows 3.1が起動できないか試しているのですが、自爆しているプログラムがKRNL386.EXEであることまでわかりました。どうやら普通にINT 21H AH=4BH (プログラムのロードまたは実行)を使って起動しようとしているのですが、ただし、既にWindowsが起動しかかっているので INT を使うかわりに INT 21H ハンドラにCALLFを使って飛んでいるようです。その中で、どうやらBFFFhセグメントからバイナリサイズ/10hを差し引いたセグメントにKRNL386.EXEをロードして実行するのですが、上に書いた通り自身の中でリアルモードメモリ0~BFFFFhをページ可能にする、というシステムサービスを使って自分を見えなくしてしまって自爆します。
リアルモードメモリをページ可能にするというシステムサービスは正しい動きをしているようなので、KRNL386.EXEがC0000h未満にロードされる以上、必ずクラッシュするらしく、なぜC0000h未満にKRNL386.EXEをロードしてしまうのか、という謎は、アドレスを計算している場所は特定したものの、BFFFhの出どころがまだ解明できていないため、解決には至ってないという現状です。しかし、INT 21H AH=4Bh のデフォルトの挙動としてはプログラムをC0000h未満にロードするはずなので、何か特別なことをしてないとハイメモリには展開されない気がします。
一度、SETUP2.EXEを使ってハイメモリを有効にする必要があるのではないか、という指摘があったので、試しましたが結果は変わらなかったですね。しかし、これが設定の問題である可能性もゼロでは無いので、TOWNSでWindowsを起動するときなにか設定が必要だったとかもしもおぼえてる人がいたら情報をお願いします。
しかし、津軽でWindows 3.1を実行する需要がほぼゼロという問題はあいかわらずあるんですがね。
2022-01-29 02:05:09
自分ではTownsについては良く判かりませんので 参考になるかも判りませんがTownsで Windows 3.1を起動させているサイトの情報がありました。
http://www43.tok2.com/home/cmpslv/Konjo/019/019.htm
2022-02-15 23:26:42
ありがとうございます。参考にさせていただきます。たしかにMAMEのデバッガで正しい動作がどうなのか追ってみるのも手かもしれませんね。
2022-02-16 12:13:56
MAMEで動くのならCPUでしょうか。
CPUで気付いた点で、おそらくほとんどのプログラムに影響しないのでわざわざ報告するのもなぁと放置していた点が何点かあって、
・NEGがAフラグを変更しない
これに依存するプログラムはおそらく地球上にはないので、こっちはどうでもいいと思います
・オペランドへのストア前に状態を更新している
ちゃんと検証できていないのですが、 push/pop mem で PF が起きるとページをマップして再実行した結果、esp が本来の倍変化するのではないかなあと思っています
2022-02-17 01:26:02
なるほど!CPUコアの問題であることは間違いないと思うのですが、現状津軽のCPUコアはFractal Engineを走らすための必要最低限のExceptionしか対応していないので、多分PUSHでExceptionが起こるとそのままVMがAbortしてしまうかも。
が、考えてみるとWin3.1の問題個所ではPFが出るかと思ったらCS,EIPその他をスタックに詰めないのでDouble Faultになりそうな気がしてきました。実はここはDouble Faultを出すと先に進んだりして。というか、それだとするとやっぱり真面目にInstruction Fetchの段階でもException見ないといかんのかな。これを入れるとTownsネイティブアプリに効いてきそうでちょっとやだな。
2022-02-19 00:02:59
おそらく需要がゼロと思われるWindows 3.1対応ですが、DOSBOX上のDOS用Windows 3.1との動作比較の結果、現在ひっかかっている箇所の問題が特定できました。ページテーブルのbit 5, bit 6 (0x60)をチェックしてるから何かと思ったら、Dirty bit (書き込まれると立つ)とAccessed bit(読み書きで立つ)でした。そういえばDOS Extenderでは使って無さそうだから対応してなかった!
しかし、これ対応しようと思ったらメモリアクセスするたびにフラグ立てなきゃならなくなって、(ただでさえ遅いと言われてる津軽なのに)Townsアプリの実行スピードに影響が出ると思ったので、これはビルドするときにマクロ定義かなんかで対応するとして、Windows 3.1を動かしてみたい人は各自ビルドしてもらう方向にしようかな。
それにこのフラグに対応してもまだ起動までこぎつけるかどうかは不明だし。
2022-04-01 22:53:14