/var/log/hdk.log

2022 年 9 月下旬

prev, this, next


20 (火)

%1 かようび

休み明け。 在宅勤務。 今日は休みの人が多かったみたいだ。

%2 MS-DOS

きのう動いたやつにはいろいろバグがあり急に動かなくなって何かと思えばレジスタークリア忘れていたし (訳わかんなくて DEBUG.COM で開いて CLI HLT を書き込んで保存しては QEMU で実行して QEMU monitor でレジスターを観察するなどしていた)、CLOCK デバイス実装もバグっていて四捨五入を書いたせいで上限を超えておかしなことになっていたし、たいした機能もつけていないのにバグだらけである。 なお時間の計算処理は見直していたら 64 ビットやる必要なくて 48 ビットでいけるので処理を減らすことができたし、他にも無駄な PUSH POP があったり、無駄に長い命令があったりといろいろ削ることができた。

んでついでなので最後のディスクアクセスから一定時間はディスクを交換していないと返すというやつを実装した。 時刻取得の INT 1AH は最後に呼び出してから日付が変わったかのフラグがあり、いろんなところで使うと日付のインクリメントをしそこねる可能性があるので注意が必要だ。

さて QEMU で試していると時々ディスクエラーになることがあり、なんでだろうと思っていたが、DMA の 64KiB boundary 問題であることがわかった。 エラーコード 9 が返される。 PC-98 と同じ制約、PC 互換機にもあったんだな。 PCjr と JX は DMA を搭載していなかったから、この制約は存在しなかったのである。 なおこの制約が生まれたのは、8 ビットプロセッサ向けの DMA を使っていて、アドレスが 16 ビットしか指定できず、20 ビットの 8088 で使うために残りの 4 ビットを DMA コントローラーとは別のレジスターで指定するようにした、というのが原因のひとつらしい。 もうひとつは、ケチって DMA からの割り込み線をつながなかったか何からしくて、本当なら途中で切り替えて続きをやれば良かったところ、それもできなくなってしまったらしい。 IBM PC と同時期に登場しバス幅 16 ビットの 8086 を搭載していた PC-98 まで同じ DMA コントローラーを使ったというんだから笑い話である。 いやこれ本当に面倒くさかったんだよ... まさか 2022 年にもなってあの面倒くささにハマるとは思わなかったがw

2022/09/20 のコメントを読む・書く


21 (水)

%1 すいようび

今日は午前中出社した。 急に気温が下がって肌寒い日。 特に朝は原付を走らせていたらフリースを着ていても寒かったな。

%2 DOS

DMA の 64KiB 境界問題の対策を実装していたら、あることを思い出した。 PC-98 でディスクコピーだったかファイルコピーだったかファイル読み取りだったか忘れたが、フロッピーディスクからたくさんのデータを読み取っている時、シーク音が 4 回ごとのサイクルになるのだ。 4 回ごとにちょっと間隔が開く。 これは、1232KiB フォーマットが 1 トラックあたり 8KiB, 両面で 16KiB, 4 回シークすれば 64KiB 分で、8086 でひとつのセグメントで扱える最大サイズと一致するから何となくそのせいかと思って深く考えていなかった。

しかしこれ、もしかして DMA のせいなのか、と。 フロッピーディスクドライブのコマンド的に、あるトラックのセクターを最後まで読んだら続けて違うヘッドに進むとか、次のシリンダーにシークするとか、そういうのはなかったはずで、最大でも 8KiB ずつ読み取りコマンドを発行していたに違いない。 なので 64KiB ごとに CPU が何か余計な仕事でもしない限りは、きっちり 64KiB ごとに時間が掛かる理屈は出てこない。 ...そうだ、DMA の境界問題だ。 8KiB 単位でコマンドを発行していたなら、8KiB 単位のアラインメントのアドレスでない限り、64KiB 以内に必ず境界問題が訪れるんだ。 で、自分で対策を実装してみてわかったけど、一時領域に転送してから正しいアドレスに CPU で転送をすることになり、一時領域を 8KiB もは確保したくないから、ギリギリまで普通に読み取り、またぐところだけ一時領域との間を CPU で転送し、残りはまた普通に読み取る、って形になりそう。 例えば最悪は 8KiB を 4KiB+1KiB+3KiB みたいな 3 回のコマンドに分けることになり、目的のセクターが通り過ぎてしまうからそこだけディスクが余計に 1, 2 周回ることになる。 360rpm だから 1 周は 1/6 秒、2 周で 1/3 秒はちょうどあの時の間隔分ぐらいな気がする。

for i in 1 2 3 4 5 6 7; do
for j in 1 2 3 4; do echo -n a; sleep 0.45; done; sleep 0.33; done

うん、イメージとしてはこんな感じだな。 これ書いていて思ったけど、4KiB+1KiB+3KiB の読み取りをするにしても、4KiB と 3KiB を先に読み取り、次に 1KiB の読み取りをしたほうがたぶん速い... 実はあの待ち時間はもう少し短縮できたのかも知れない。

そして Windows がどうだったかは忘れてしまったが、FreeBSD(98) ではその現象がまったく発生せず連続してアクセスできていたことも覚えている。 プリエンプティブマルチタスクのオペレーティングシステムではフロッピーディスクの転送をしながらハードドライブの転送もできるんだから強いよなーなんて思っていたけど、そこじゃない。 カーネルに DMA の制約に応じたアドレス割り当てをする機能があったからなんだな。 まぁ、DOS 時代は最後まで RAM 容量が厳しかったから、8KiB ものアラインメントで (セグメントアドレスで 200H 単位) メモリーを割り当てるというのはあまりにももったいなくて考えられなかったけどね。 でも RAM の空き領域を全部使うディスクコピーぐらいは、できるだけ 64KiB 境界をまたがない方法でアクセスしてもよかったよね。

2022/09/21 のコメントを読む・書く


22 (木)

%1 もくようび

在宅勤務。 昼は晴れていた。

%2 フロッピーディスク

きのうのコマンド適当すぎたな。 たぶんこっちのほうが適切だ:

for i in 1 2 3 4 5 6 7; do
for j in 1 2 3 4; do echo -n a; sleep 0.5; done; sleep 0.33; done

違いは 0.5 のところ。 ポイントはこうだ。 表面 8 セクターにアクセスして、続けて裏面 8 セクターにアクセスする時は、シークがいらないし、8 セクターの後に少し隙間があるので、裏面の 1 セクター目に間に合う。 なので表面と裏面は 2 回転の間にアクセスできる。 その次はシリンダーが変わるのでシークの必要があり、シーク後にコマンドを出した時にはもう 1 セクター目を過ぎているから、1 回転待ちになる。 つまり連続アクセスでは 16 セクターごとに 3 回転、3/6 秒必要になる。 んで、64KiB 境界のところで 8 セクターを 3 回に分けて読み取ったら、追加で 2 回転必要になる、と。

で。 きのうのアイディアを実装しようとしていたら、自分の実装もいい加減過ぎた。 読み取り前提になっていたw 当たり前だけど、書き込みの場合は先にメモリーコピーをしてからコマンド発行だ。 それをしていなかった。 あまりにもレジスターのやりくりがごちゃごちゃするので、スタック上にローカル変数を確保した。 LES DI,[BP-4] みたいな書き方をする時は DWORD PTR がいるらしいw 違和感あるw

そういえば、オーシャノグラフィ II というバックアップツールがあって、これは 8KiB セクターを使用していた。 確か特にずらしてはいなかったんじゃないかな。 8KiB セクターで 1 セクター/トラックのフォーマットにすると、通常 8 セクター時に必要なセクター間のギャップ 7 個分が不要になるので、だいぶぎゅっと詰まった感じになる。 んで、シークしてもセクターの先頭に間に合う、って仕掛けだったと思う。 一度に 8KiB ずつのアクセスになるので通常のファイルシステムには向かないが、バックアップツールはどちらかというとテープ的なシーケンシャルアクセス用途だから向いていたというわけ。 今考えてもなかなか賢い仕組みだったが、容量の大きなハードドライブに対応していなかったか何かが理由で早々に引退となった。

%3 libx86emu

GitHub - wfeldt/libx86emu: x86 emulation library

MS-DOS 独自ビルドで夢が広がっているので、このへんのライブラリを使って、超広大なメモリーが使える仮想マシンとその上で動く MS-DOS というのを作りたい。 超広大ったって 1MiB なんだけど、仮想マシンだから ROM も VRAM も不要で、普通なら ROM がある領域までまるまる MS-DOS 用に使える。 MS-DOS 的にはタイマー割り込みすら不要で、データのやりとり用に I/O ポートひとつ用意すれば十分かと。 動くアプリケーションは DOS 汎用のものに限られるからかなり少ないが、フロッピーディスクより大きめのディスクイメージを準備して、そこで MS-DOS セルフビルドができます、っていうのは楽しそうじゃない?

%4 MS-DOS

COMMAND.COM の CLS コマンドは ESC [ 2 J を出力する。 IBM 版は ANSI.SYS を組み込まないとどうなるかというと、ビデオ BIOS を呼び出す専用コードが実装されている。 しかしちゃんとチェックが入っていて、CON じゃなかったり (リダイレクトや AUX 等)、ANSI.SYS が組み込まれていると判断したりした時には、エスケープシーケンスを使うようになっている。 IOCTL で SPECIAL ビットもチェックしているようだ。

...えっ、この IOCTL ってセットもできるのか! Cooked mode/raw mode を変えられるのはわかるけど、他のビットも変えられそうな雰囲気だ。 JX の日本語 DOS で、わざと SPECIAL ビットをクリアしてみたところ、COMMAND.COM の CLS がエスケープシーケンスをそのまま出すようになった。 へぇ。 なお SPECIAL ビットをクリアしても INT 29H は引き続き使われているようだ。 試しに console input/output のビットもクリアしたら、何も出なくなった...

自分のビルドしたものは、console input/output のビットをクリアしても引き続き動いている。 これは IBM 版じゃないからというよりは、旧システムコールを自分で実装した部分の影響だろうな。

DIR /P や MORE の実装を見ると、画面行数は 24 行か 25 行の想定のよう。 MORE が IBMVER では桁数をビデオ BIOS から取得しているのに加え、漢字の時に 24 行としているのを見つけた。 これは時期を考えると完全に 5550 向け実装と言えるのではないか。 JX は 11+1 行しか使えない基本モードがあったから、JX の日本語 DOS はそのあたりも改造されていたんだと思う。

デバッガーにも謎の IBMJAPVER 実装があったけど、あれも 5550 ってことだろうな。 5550 は PC 互換ではないと言われているけど、意外と BIOS なんかには PC 互換な部分があったってことなのかもな。

後は DOS/V 互換的なものも実装できると楽しいだろうけど... フォントに 128KiB は必要だろうから、拡張メモリー無しで考えるとメモリーがめっちゃ厳しいなw

2022/09/22 のコメントを読む・書く


23 (金)

%1 秋分の日

昼過ぎぐらいまでは晴れていたような気がする。

テレビでやってた映画『竜とそばかすの姫』。 2021 年のアニメーション邦画。 細田守監督。 同じ監督の『サマーウォーズ』に出てくるようなオンライン世界が出てくる。 世界観として非常に似たものを感じる。 どうして鍵もかからないのか、どうしてすぐ暴力沙汰なのか、どうして文字でなくいちいち話しているのか、どうにも理解しがたい。 そして、繰り返される「あなたは誰」は違和感の塊だった。 お前こそ誰だよ状態だ。 結末はいい感じにまとまっているが、なんかどうにももにょもにょする映画だった。

%2 MS-DOS 専用仮想マシン

説明が足りなくてとっつきにくい libx86emu を使い、RAM を FFFF0H まで割り当てて、超いい感じな仮想マシンをつくったんだが、仮想ディスクの挙動がおかしい。 1 クラスターに満たないファイルを作ると、なぜかディレクトリエントリだけが作られて、FAT もデータも書き込まれない。 PC 用に実装したほうではその問題は起きないので、仮想マシン用に書いた実装に問題があるようだがさっぱりわからない。 まさか libx86emu のせいってこともあるのかと、RAM ディスクドライバーを組み込んでみたらそっちは動いているからやっぱり自分の実装のせいだ。 でもなんだろうな、やっていることは PC の ROM BIOS を呼び出すより遙かに簡単なんだけど...

なお読み取りに関しては普通に動いているようで、CHKDSK は以下のように空きメモリーを報告する:

  1048560 bytes total memory
   959568 bytes free

いぇーいw

2022/09/23 のコメントを読む・書く


24 (土)

%1 どようび

雨。 夜はあがった。 涼しい日。 湿度は高かった。

%2 MS-DOS

きのう作った仮想マシンのやつで、デカいディスクが使えるぞと試していたらファイルを書き込むと壊れる事象が発生。 いろいろ試していると 1 クラスター未満のサイズのファイルを作るとディレクトリエントリだけ書き込まれて FAT もデータも書き込まれない。 なんだこれー?

というののデバッグに時間を要した。 結論はディスクのデバイスドライバーの実装をミスっていて media change のステータスをレジスターに入れて満足してメモリーに書き込んでいなかった。 まさかこんな挙動になるとはね!

そのデバッグの際に libx86emu が trap flag (TF) を無視していることが発覚した。 少しコードを確認したところ非常に簡単な回避策で対処ができ、デバッグがスムーズになった。

そんなに動くとだんだん調子をこいて、CPU 負荷低減策も導入した上に Linux KVM 用のものも作った。 負荷は DOS の仕様上プリントスプーラー用の割り込みを生成しながらポーリングする処理になっているので、その割り込みのタイミングでモニターを呼び出して 1/100 秒待つっていう対策にした。 タイマー割り込みはないからね。 Linux KVM 用は前に自分で書いたコードがあったので簡単。 何しろこの独自仮想マシンはエミュレーションが何もいらないからね。 それでコードを整理していって push.

さぁて、セルフビルドを試みよう。 バッチファイルの CALL に MSDOS 2.11 は対応していないので、何とかする。 COMMAND /C なら動くのに \COMMAND /C だと動かない謎の事象... まぁいいや、ごまかして何とかしようとしていたら MASM が時々 out of memory を出してきた。 はて? まだ問題ありか?

とまぁこっからが泥沼である。 とりあえず理由がわからないけど、MASM がロードされる時に前のメモリーが 64KiB 単位プラスアルファぐらいの割り当てになっていると動くっぽいことを発見した。 そうじゃないと out of memory どころか何もメッセージを出さずにいきなりハングすることもある。 それで対策プログラムを作り、セルフビルドが通せるようにはなった。 そして DOSBox では行入力のリダイレクトをするときはファイルに改行コード LF を入れているとだめなようだ。 CR だけにしていないと動かない。 そういえば MS-DOS は最初に LF が来たら捨てるみたいな処理があったな。

とまぁ DOSBox でもビルドが通せるようになり (遅いけど)、いいかなーと思って整えていた時に、そういえば FreeDOS でもわざと変なアドレスにロードさせれば MASM がうまく動かなくなるんだろうなと思って、対策プログラムを改造して試したら、なぜか動くのだった... はぁー? DOSBox と MS-DOS 2.11 でだめで、FreeDOS で大丈夫ってどういうことだ?? しかも Linux KVM 版と libx86emu 版でも微妙に条件が違うんだよな... まぁ両者はメモリーサイズが微妙に違うんだけど...

2022/09/24 のコメントを読む・書く


25 (日)

%1 にちようび

晴れ。 のんびり。

バイクしばらく動かしていないなと思って、メインスイッチを入れると 12.3V の表示, たぶん 4 週間経っているのでさすがに 12.4V より下がるのか。 充電をかねて南多摩から若葉台やら稲城のジョイフルやらのあたりを回ってきた。 途中、よみうりランド行きの小田急バスを見かけて、おそらく新百合ヶ丘からの路線ではないかと思うが、どこを通るんだろうと思ってついていったら、いったん坂をのぼったところからまた下る感じになるんだな。 住宅地を通る道のようで、結構たくさんのバス停でお客さんを降ろしていた。 バイクだと、バス停で止まっているバスの後ろで左端にいたら、コイツ行く気ないなってのが一発で後続車に伝わって良い感じ。

%2 DAA

8086 の packed BCD の補正命令 DAA。 16 進数の変換に使われている ADD AL,90H; DAA; ADD AL,40H; DAA は有名だし、引き算バージョンで類似の DAS 命令を用いた CMP AL,10; SBB AL,69H; DAS もあるが、それ以外にごく普通の 10 進数演算にも使える (笑) んだけど、最近その使われかたを MS-DOS のソースコードで発見してしまった。

それは CHKDSK だった。 いや他にも COMMAND や RECOVER なんかにも同じものがあるんだけど、32 ビットの整数値を 10 進数の文字列に変換する部分で使われている。 これ、昔からディスク容量で出てくる話で、ディスクの容量や空き容量は、FAT のクラスター数で表されており、クラスター当たりのセクター数とセクターサイズなんかをかけ算していくと 32 ビットが必要になる。 (4GiB 以上になるともっとだが、DOS が主流だった時代は 4GiB 以上は本当に想定外だったな。) 10 で割って余りを出すには 16 ビットずつ 2 回の割り算をしなければならず、一桁ごとにそれをやっていては 8086 では時間もかかる。 CHKDSK の方法は、ぱっとみは上の 16 進数変換みたいな魔法のコードに見えた。 しかし良く読むとごく普通の方法だった。

MS-DOS/CHKDSK.ASM at master - microsoft/MS-DOS - GitHub

もし誰かに、正の整数を 2 進数にして上から 1 桁ずつ送るから、それを受け取って 10 進数で表してくれと言われたら、目の前の電卓をクリアして、数字を受け取るごとに、かける 2、1 をもらったら 1 足す、を繰り返していくと答えは出る。 例えば 1 0 1 0 と来たら、0*2+1=1 1*2+0=2 2*2+1=5 5*2+0=10 といった具合。 それを、送る側はビットシフトとローテート、受ける側は、かける 2 を同じ数字同士の足し算にして packed BCD 補正をかけて実現したのが CHKDSK にあったコードである。 4004 由来なのか 8086 では BCD でも桁上がりを素直に扱える仕組みがあるので、実にシンプルである。 16 ビットずつ別々に取り出すならビットシフトとローテートを組み合わせる必要もないしさらに速くなるだろう。 そして、32 ビット符号無し整数の最大値は 4294967295 で 10 桁なので、packed BCD であれば 40 ビットあれば表現でき、16 ビットの汎用レジスター 3 つで足りる。 つまり RAM をごちゃごちゃ触ることもなく、レジスターをこねくり回していれば完了する。 マジかぁ!

これは 32 ビットだと 32 回のループになる。 10 で割る方法では 10 回のループになるので比較しづらいが、8086 の割り算命令は 100 クロックサイクルを軽く突破するような命令なので、それを 1 桁あたり 2 回ずつ使わざるを得ない方法と比べれば、まぁ速いのではないか。 極めて単純なアイディアだけど、思いつかなかったな... 昔の人はすごい。

でまぁ、今時のコンピューターでは AMD64 の long mode を含め BCD 演算命令は排除される傾向にあり、この技は使えない。 専用命令なしで手で BCD 演算の処理を書くとさすがにシンプルにならない気がする。 16 進数のアレも、BCD 演算無しで条件ジャンプも無しだと結構つらいんだよな。 ADD AL,F6H; SBB AH,AH; AND AX,473FH; SUB AL,6; ADD AL,AH みたいに思いつきで書いても 11 バイトだ。 まぁ条件ジャンプは使えよって話か。 今時なら conditional move もあるし。

%3 MS-DOS

MASM が不安定な謎ははっきり解決はしていないんだけど、ひとつは空きメモリーが影響していることはわかった。 システムコールを見るとメモリー割り当て関連のものは一切使われておらず、謎のソフトウェア割り込み設定の他は、普通の入出力のみ。 どうやらメモリーに関しては Program Segment Prefix (PSP) のオフセット 2 に 16 ビットで書き込まれている上限アドレスを見て使っているようだ。 デバッガーでわざとそこを別の値に書き換えて実行すると、値によって out of memory になったりならなかったりする挙動が観察できた。

んで、どうやら、少なくとも明らかに問題があるのは、この連続空きメモリーが 512KiB を超えている場合だ。 中で引き算をした結果を符号付きの値として比較してしまっているようで、空きメモリーが少ないというほうの処理に入ってしまっておかしなことになるようだ。 FreeDOS については最初からセグメントが 2000H を超えており、また QEMU の 1MiB 未満の RAM 容量が 640KiB のためこの問題が起きない。 DOSBox はめちゃくちゃ下位のアドレスを使っている上に A49BH まであるというから、658KiB か? そして自分がこしらえた専用仮想マシン上では DOS のくせに 1MB も空きメモリーがあるんだからお察し。

そうするとやり方としては、わざと上位アドレスを先に割り当てて、メモリー空間に穴が開いた状態にして MASM を呼べば、空きメモリーが 512KiB を超えるなんてことにはならなくなってうまくいく。 まぁ、うまくいくのはいったんだが... 他のアドレスでも動くのかなと余計な試行錯誤をしたら、アドレス B9000H 前後 (?) でなぜか挙動がおかしくなるところを発見した。 アドレス D0000H ぐらいまで進んでいれば OK だし、もっと下位のアドレスでも問題ない。 これが本当に変で、MASM がロードされるアドレスをどこに調整しても関係なく、どうやら DOS 自身がぶっ壊れているようだ。 MASM は通常通りシステムコールを使っているのに、途中から標準エラー出力が出なくなり、その後に異常動作である。 はぁー。 DOS のバグか? さすがにもう知らん。

全部ビルドについては、libx86emu 版の専用仮想マシンは異様に遅くて、Ryzen 7 2700 のパワーをもってしても 35 分ぐらいかかる。 DOSBox は Ctrl+F12 で速くしてやるとそこまで遅くなく、8 分ほどだ。 Linux KVM 版の専用仮想マシンはなかなかな速さで、1 分以内だ。 QEMU + Linux KVM + FreeDOS も速いことは速いんだけど、コンソールが遅いし、シリアルポートに切り替えてもここまで速くはなさそうだ。

そんな感じで、PC-98 版 IO.SYS (DOS 流に呼ぶとこれが BIOS) も作ってみたいところではあるんだが、INT DCH まではさすがに面倒見切れないなと思うので、とりあえず動くだけといったところになるかな。 PC 版は文字入力でカーソルキー等を IBM 形式で期待されているっぽいところ以外は不思議なほど普通に使えそうな感じで動いているけど、PC-98 版はそうはいかないだろうな。 とにかくこの専用仮想マシン上でめちゃ速いビルドができるので作業がやりやすくはなる。

2022/09/25 のコメントを読む・書く


26 (月)

%1 げつようび

晴れ。 暑い。

%2 16bit code

この時に Linux の modify_ldt システムコールで遊んでいた。 この時は 32 ビットセグメントの実験で、64 ビットバイナリで使えないコードを書いたのは単に手抜きだ。 このシステムコールによる 16 ビットセグメントの使用も未だに許されている。 Wine が Win16 アプリケーションを動かす時に使うらしい。 2014 年頃に廃止されかけたけど、Wine ユーザーのおかげで助かったようだ。

で。 これを使って MS-DOS 用の MASM Version 1.10 を Linux 上で簡単に動かせないかというアホなことを思いついてやってみた。 本来仮想 8086 モードが必要だが、セグメントレジスターを最初の再配置だけですませているのなら、意外と動くのでは? EXE ファイルの MZ ヘッダーは極めて単純で、16 バイト単位のヘッダーサイズ分をスキップするとそこからが本体、アドレス 0000H:0000H から始まっているとみなしたコードとデータが含まれるらしい。 再配置データはポインターの配列構造で、ひとつのエントリは 32 ビットでセグメントとオフセットを並べたもの。 それが指す 16 ビットのセグメントアドレスに開始位置を足せば再配置完了。 あとはスタックポインターがヘッダーにあるので同様に足し、開始アドレスもヘッダーにあるので同様に足して使う。 それだけ! テキストセクションもデータセクションも一切区別無し!

今回は仮想 8086 モードじゃないセグメントにマップする必要があるので、足すんじゃなくてその位置のセグメントを作って割り当てるようにした。 おっと、データとコードの区別があるんだった。 困った。 よし、全部コードにしておいて、書き込みがあったら例外が発生するからそこでセグメントを切り替えよう。 %ds%es にコードセグメントが入っていたらデータセグメントに切り替えるだけだ。 スタックポインターにだけはコードセグメントはセットできないので、機械語解釈が必要になるが、そう難しくはないだろう。 あと割り込み禁止なんかも機械語解釈で進める。 ってな感じで適当にやると意外と動きそうな感じでシステムコールをとらえ始めたんだが、致命的な問題が発生した。 どうやら、おそらく、たぶん、なんだけど、LES 命令をダブルワードのメモリーコピーにおける読み取り用に使ってやがるw なんてことをしやがるんだw それは protected mode では virtual 8086 mode を使わないと動かしようがないw あぁーw

まぁ残念な結果ではあったが、その機械語解釈処理をシグナルハンドラーとして書いた時に、ucontext_t 構造体の中身を見ていたらなんと、64 ビット用ではセグメントセレクターの要素が一部しかないことが発覚した。 modify_ldt は 64 ビットでも使えるんだが、セレクターがないので、こういう例外をとらえて云々という処理は不可能なのか。 いや、もしかしたら、シグナルハンドラーに入っても %ds%es は変更せずにそのまま来ちゃうということなのかも知れない。 賢いけど 32 ビット用と共用するコードはとても書きづらいな。

2022/09/26 のコメントを読む・書く


27 (火)

%1 かようび

晴れ。

%2 MASM

何とか MASM をフルの DOS 環境なしで動かせないか? libx86emu を使う手もあり、それでも Unix と直接つなぐなら並列実行ができるから、30 倍遅くても 15 並列で 2 倍の時間で済むみたいなことはできる。 けど、考えていると x86 で x86 を実行するのをやってみたくなってきた。

エミュレーターは何度か書いたがフラグの扱いがとても面倒だ。 どの命令はフラグを壊す、どれは壊さない、それに補助キャリーフラグやらオーバーフローフラグやら、既存の高級言語で表現困難なものが多数ある。 しかし、x86 上で x86 なら、実際のフラグレジスターを流用できる。 ただし pushf popf でなければとっておけないフラグがあり、毎回 pushf popf をやっているとさすがにパフォーマンスが厳しいのではないか? (試したことはないけどね) それで、フラグをできるだけ壊さなければいいんだ、っていう謎の結論に達したw

BCD 命令関連の都合により i386 アセンブリで書く。 フラグレジスター無しでも結構やれることは多くて、lea 命令で足し算ができるし、4 倍までかけ算ができるし、jecxz で条件ジャンプもできる。 そして 8 ビットレジスターの存在のおかげでビットの取り出しまで行える。 なかなかつよい。 ちなみに not 命令はフラグを壊さないので、not 命令併用で引き算もできる。 こんなに何命令もフラグを壊さずに進むプログラムなんて書いたことがない。 見慣れた x86 とはちょっと違う世界が広がっている。

あとはまぁ勢いだ、ずばばばー、っと書かないとなかなか難しいんだけど、フラグレジスターの苦労がないのと、BCD 命令含めほとんどの命令はそのまま使っちゃえばいいということで勢いが落ちない。 デバッグは大変なんだけどとりあえず、システムコールまでたどり着けた。 さてこれが動くようになるかな。 性能はどれぐらいになるかな。

2022/09/27 のコメントを読む・書く


28 (水)

%1 休暇

「坂本冬美特別公演 中村雅俊特別出演」っていう公演を見に行った。 農業協同組合から招待券が届いていたため。 貸し切りと書いてあったが本当に貸し切りだった。 農業協同組合の役員紹介もあったし。 農業協同組合で 3 日間貸し切っているらしい。 自分の招待券はどうやら 2 日目、夜の部のほうだ。 東京都中央区日本橋浜町にある明治座というところで行われている公演で、舞台 (演劇) と、歌のコンサートがセットになっている。

学園祭レベルならともかく、有名人が複数出ている舞台を生で見るのは間違いなく初めてだった。 劇場という場所そのものが映画館ぐらいしかなじみがなく、舞台に本格的なセットが組まれて役者が演じるのを見たのはテレビで昔やっていたお笑い番組ぐらいのもんだろう。 まぁ、実際に見ると、予想の範囲内でもあったし、百聞は一見にしかず的なところもあった。 テレビドラマなどとは異なり、舞台の場合は目の前の舞台がすべてであり、幕がおりるか照明が消されない限り場面は大きくは切り替わらない。 舞台の床にはターンテーブルが仕込まれていて、セットごと回転させることで短時間で場面が切り替えられるようになっていたが、それにしたってテレビみたいにはかえられない。 それで、場面全体が見渡せるんだな。 テレビだと演出が見せたい絵になるというか、役者の顔がどアップになったり、逆に小さくなったりなんだけど、舞台は観客が見たいところを見るスタイルなんだな。 真ん中でかけあいをしている間に、端っこで別の役者が何か話をしている風な、そういうのがあった時に、テレビドラマだとこういう表現ないな、って思った。 まぁ、情報量の違いとも言える。 テレビの解像度が 4K, 8K になったところで、舞台の全体を固定カメラで映したままで、まるで実際に見に行ったみたいな体験がー、ってレベルにはまだ情報量が足りていない。

役者からしたら、テレビで言えばめちゃ長いワンカットで生放送、みたいな演じ方をしなきゃいけないんだから、難しさがあるんだろうと思う。 ライブなんだよな。 そういう意味で音楽のライブとも通ずるところがあるんだなと思ったし、坂本冬美のように歌手から俳優活動を始める人や、中村雅俊のように演劇の世界から歌を出す人もいるってことなんだろう。 中村雅俊、歌番組に初めて出演した頃の話をしていて、下駄にジーンズでいったんだとか、演劇の人間で音楽関係の知り合いいなかったしとか、そういうことを言っていた。

第二部の歌は坂本冬美だけなのかと思いきや、中村雅俊も何曲も歌ったし、もう一人、森山愛子という人も歌っていた。 大村健二って人かな、この人は舞台に出ていただけだけど、水戸黄門に出ていた人だって話をしていた。 演歌のことはわからないけど、楽器は意外と普通にポップスと同じなんだなと、演歌の間は演奏の人達を見るのが楽しかった。 スポットライトを無視して奥の人達を観察、こういうのも、ライブって感じがする。

%2 行き帰り

きのうの国葬の影響で今日も一部交通規制ありみたいな表示があって、でもどこが規制されるのかまったく情報がなく、不安があったので身軽なスクーターで行った。 結果としては交通規制はなかったが、数回道がわからなくなり路肩でチェックしたのと、あと目的地に近づいてきたら、一方通行の罠と、公園の駐車場の入口がわからずうろうろする羽目になり、まぁ結果としては邪魔にならない & 押せば歩行者になれるスクーターで正解だったな。 時間に余裕があったので (駐車場が満車だったら秋葉原にでもとめて列車で行くことも考えていた)、あえて地図を見ずにうろうろしたら、地下駐車場はなぜかかなり遠いほうから出てしまったし、明治座の裏手のほうに着いて、表はどこだ? とマジでわからなかった。

14 時 30 分前後に着き、16 時開始で 20 時になる前ぐらいに終わったので、6 時間の駐車でバイクだったから 600 円。 車だったら 2400 円か? 帰りは道が空いていて快適。 腹が減っていたので途中でごはんを食べようかと思っていたが、微妙に雨粒がバイザーについてきたのでコンビニで弁当を買って帰った。

%3 MASM のやつ

システムコールは作ってそれからまぁバグが出るわ出るわ... 間違いだらけだ。 そもそも命令を根本的に勘違いしていたものなどあり、自分のプログラムもまともに動かなかったのでそこをまずはなおして、何とか MASM もアセンブルが始まるようにはなったが、エラーだらけ。 なんだそりゃー。 まだ何か間違っているのかよとコード整理と目視確認、疑わしいのは CMPSB か? とそのあたりを徹底的に調べていたら、なんと REPE と REPNE を逆にしていたことが発覚したww 自分で書いた JX のエミュレーターのコードを参考にしていたら、それが間違ってやがるw いや、動作はあっていたんだが名前が間違っていて、間違っている名前を参考に書いたから間違っていたw

それでアセンブルが通るようになり、LINK と EXE2BIN のシステムコールにも簡単に対応してみた。 LINK はなんとカレントドライブを勝手に出力ファイル名につけるというおせっかいをしてくれる... どうしてそうなった...

いろいろデバッグ中に発覚したけど、MASM にはなぜか命令コード 82H が使われているらしい。 これは命令コード 80H とまったく同じで、通常は 80H しか使われない。 どうしてこうなったんだろうか。

あと自分のプログラム、1 バイト単位で入力するのが致命的に遅い。 Linux のシステムコールを 1 バイト単位で呼ぶのがさすがに厳しいか。 DOSBox なんかで遅いのにも関係があるかも知れない。 改良するのがよさそう。 MS-DOS はその点 1 バイト単位でも意外と高速動作かも知れない。 システムコールといっても当時のは単なる関数呼び出しの延長みたいなもんだしな。

2022/09/28 のコメントを読む・書く


29 (木)

%1 もくようび

晴れ。 涼しい日。

きのうの公演のやつは農業協同組合がいくつかくっついて今の形になってから 30 周年の記念で招待されたものだった。 明治座は 150 周年だそうで、そちらもなかなかすごい節目という感じがする。 坂本冬美はなんと 6 年前の 2016 年にも農業協同組合の貸し切りコンサートをやっていたんだそうで、オフィシャルサイトによると 2 月 19 日に府中でやったんだな。 知らなかった。 Twitter の書き込みによれば 26 日にツイートしている方がいらっしゃる。 公演側の情報では今週月曜から木曜まで貸し切りになっており、火曜は午前中のみで午後の公演がない。 自分が水曜の招待で、スタッフさんから 3 日間あると聞いており、それに坂本冬美本人が明日も農業協同組合だということを話していたので、どうやら 2 日目だったなということはわかっていたが、1 日目がその火曜日の半日だったのか、月曜日だったのかがそのツイートではっきりした。 6 年前の縁と、150 周年記念の節目の縁で何か値引きがあったかわからないけど、一般のチケットは S 席 (1・2 階席) 13500 円、A 席 (3 階席) 6500 円となっており、貸し切りでは 3 階席は封鎖されていたので、914+390 席で 1304 席しかない。 未使用の席も多かったがチケットが届いても来なかった人の分だとしたら相当な数だし、自分がいた 2 階席は特にあいているところが多かったので来ていたのは 700〜800 人ぐらいだったのかも知れないけど、3 日間 6 回分を貸し切ったら結構な金額だよな...

明治座の 150 周年はともかく、ふと思い出して調べたら、自分の母校の小学校は来年で創立 50 周年である。 50 年かぁ! 小学校を卒業する年には 12 歳から 13 歳になっているはずだから、つまり、48 年前の 1 期の卒業生は今年 60 歳か 61 歳、還暦ということに! ひゃー! 母校の大学も来年 10 月には 50 周年になるようだ。

%2 MASM

きのうの 8086 命令実行プログラムをさらになおしていろいろ動くようにはなったが、MASM より自分のプリプロセッサが遅い。 A4-9125 環境で SYSINIT.ASM を入力してみた時に DOSBox で 30 秒ほど、自分の命令実行で 10 秒ほどかかる。 システムコールを使いすぎたせいだろうと特に調べずにバッファリングの実装をとりあえずやってみたんだが、DOSBox でも自分の命令実行でも性能向上はごくごくわずかだった。 つまり他の処理が時間を使いすぎている。 うーん!

まぁアルゴリズムはまったくほめられたものではない。 行ごとに単語を切り出し線形探索なんてやっているんだからな。 MASM は古いプログラムなのでそれなりに最適化がされていたのかも知れないが、今のプロセッサなら強引なアルゴリズムでも結構動いてしまう。 何しろ 8086 が扱える 1MiB のメモリーは今は低スペックな A4-9125 であっても全部プロセッサのキャッシュに入ってしまうからね!

最初に C で書いたコードは消しちゃったけど、プリプロセッサはネイティブで動かすほうがよさそうだな...

2022/09/29 のコメントを読む・書く


30 (金)

%1 きんようび

晴れ。 ちょっと暑い日。

テレビでやってた映画『アダムス・ファミリー』(原題: The Addams Family)。 1991 年のアメリカ映画。 絶対見たことがある感じがする不思議な世界の物語だが、中身はほとんど覚えていない。 本当にどうでもいいけど、モーティシア・アダムスの顔が浅田舞に見えてくる不思議。 どこか顔のバランスというか目の形なのかメイクなのかが似ているのかな。

%2 MS-DOS あそび

プリプロセッサの実装、1 バイトごとに関数呼び出し (PUSH, CALL, ADD) をする上に、文字種判定をする関数なども最適化無しの C コンパイラー並に無駄だらけな実装をしていたので、見直した。 直接実行だと分岐予測などが強烈に働いて速くても、間に命令解釈のインタープリターを入れてしまうとそういうのがきかなくて、その無駄な部分がきいてくるんだろうと。 これだ、と思ってすごくシンプルにしたのに、予想に反してたいして効き目がない。 うむむ。 10 秒前後の実行時間が 3 パーセントぐらい縮むかどうかレベルだ。

結局やはり探索問題なのか。 シンボル名を線形探索で見ていくから、シンボル名が増えるに従って、えーっと、指数関数的に比較回数が増えるってやつだな。 そりゃ遅いだろうと言われればそれはそう。 しかしじゃあ高速なデータ格納方式を実装しますかと言われると、DOS プログラムのメモリー使用量の制約もあってそれはそれで難しそうな気がする。 とはいってもなぁ、簡単なハッシュ関数ぐらい使わないとあれかな... 完全一致の比較だからな... ハッシュが重なったらアレだけど、これより前にはないぞ、くらいのヒントのデータはハッシュ関数で提供すべきか... とはいえ今のデータ構造、キーワードに付随するデータをシーケンシャルに並べてあるから本当に面倒な構造してんな... 誰だよこれ作ったの...

2022/09/30 のコメントを読む・書く


prev, this, next

/var/log/hdk.log コメント一覧

トップ / 日記索引 / 日記 (2022 年 9 月下旬)

Hideki EIRAKU