Image: 231221 PS/55エミュ製作 [22]DOS K3.3のFORMATコマンドによるメモリー破壊を解決

DOS K3.3のFORMATコマンドでスイッチにFDDのドライブ名のみを指定して実行するとDOSが異常終了する不具合の原因を調べていた。調査を進めていくうちにFORMATコマンドの内部だけでなくIO.SYS (IBM DOSではIBMBIO .COM)の初期化ルーチンやIOCTLの内部まで追っていくことになって、解決するのに丸3日かかった。解決方法はソースコードにたった1行追加するだけだったのに、とんでもなく時間かかったな。

まあでも、気になっていた部分はスッキリ解決したので、これでようやく一息つけるわ🥱

症状

DOS K3.3のFORMATコマンドでスイッチにドライブ名のみを指定して実行すると、文字化けしたメッセージが表示された後に「記憶域の割振りエラーです。COMMAND .COMをロードできません。DOSを再起動してください」というメッセージが表示される。しかし、フォーマットを指定するスイッチを付けてFORMATコマンドを実行すると、正常にフォーマットできる。

解析

DOS 3.2以降ではFORMATコマンドのハードウェア依存コードは分離され、OEM先のPCメーカーによってIO.SYSにドライバーとして実装される。(ただし、完全に汎用というわけではなく、機種特有のフォーマット形式への対応など独自の実装は若干ある。)PS/55はディスプレイ周り以外はPS/2と互換性があり、ディスク関係の処理もPC/AT互換機とほとんど同じ。唯一の例外は、DOS K3.xのFORMATコマンドがマルチステーション5550の初期に使われていた5.25インチ640KB (2DD, 8セクター/トラック)をサポートしている点だろうか。

FORMATコマンドはInt 21h (DOS Calls), AH=44h (IOCtl), AL=0Dh (Generic Block Device Request), CL=60h (Get Device Parameters)ファンクションによって指定ドライブの装置パラメーターDPB (Device Parameter Block)、特にその中のBPB (BIOS Parameter Block)を取得する。ここにセクター数などディスク構造に関するデータが含まれている。DOSはこのファンクションが呼ばれると、その論理ドライブ(A:, B:, …)に対応するデバイスドライバーとIOCtlによって通信を行う。DOS起動時に認識されているドライブはIO.SYSに組み込まれたドライバーが扱うので、今回のFDDへのアクセスもIO.SYSに対するIOCtlによって行われる。このファンクションが返すBPBには、デフォルトのBPBと実際のBPBの2種類があり、今回はデフォルトのBPBを取得している。デフォルトのBPBはIO.SYSが管理するBDS (BIOS Data Structure)というメモリ上のデータから読み取られる。実際のBPBはディスク上の最初のセクターに記録されている。

IO.SYSは初期化ルーチン、つまり最初のDOS起動時に、ROM BIOSのInt 13h Ah=08h (Read Drive Parameters)を呼び出す。このファンクションはBLレジスターにドライブの種類を格納する。ドライブの種類はハードウェア的に何か検出するわけではなく、BIOS設定(CMOS)データから読み取られる。この内容は次の通り。

(BL) - Bits 3 to 0 - Valid drive type value in CMOS
01h    360KB, 5.25 inch, 40 track
02h    1.2MB, 5.25 inch, 80 track
03h    720KB, 3.5 inch, 40 track
04h    1.44MB, 3.5 inch, 80 track
06h    2.88MB, 3.5 inch, 80 track

IO.SYSのドライバーはこの数値をそのまま使うのではなく、一度テーブル(対応表)でDeviceType(あるいはFormFactor)というIDに変換した上で、それに対応するBPBをBDSに格納する。このIDは次のように決まっている。

0 = 48 TPI (320/360KB, 5.25 inch)
1 = 96 TPI (1.2MB, 5.25 inch)
2 = 720KB, 3.5 inch
3 = 8 inch Single Density
4 = 8 inch Double Density
5 = Fixed Disk
6 = Tape Drive
7 = Others (1.44MB, 3.5 inch)

この変換処理ではテーブルの先頭アドレスにファンクションの返値であるBLレジスターではなく、BXレジスター(上位8ビットがBH、下位8ビットがBLで構成される16ビットレジスター)を加算している。OADGテクニカル・リファレンスによれば、BHは「常に0」と書かれているが、DOSBoxの実装ではBHに対して特に何も処理しないのでその内容はその時々で変わる。このため、テーブルの外の値を拾ってDeviceTypeが想定外の値となり、BPBがBDSにセットされなかった。

FORMATコマンドはIOCtlを通じてデータがゼロ埋めの不正なBPBを受け取り、CXループでドライバーのTrackLayoutフィールド(1トラック上のセクター・レイアウトを示す可変長テーブル)を初期化する。本来ならCXには1トラックあたりセクター数が代入されるが、今回はCX=0となり、このときLOOP命令ではオーバフローにより65536回のループとなる。TrackLayoutフィールドはDOS 3.2の仕様では最大40(DOS 3.3でも63)セクターの分しか確保されていないので、メモリー上のそれ以降のデータが上書きされていく。これによりDOSのプロセス管理部分が破壊された。

FORMATコマンドにフォーマット形式を指定するスイッチが指定された場合、FORMATコマンドは自身が持つBPBの集合からその形式に対応したBPBで強制的にフォーマットを試みる。このため、不具合が再現しなかった。

解決方法

Int 13h Ah=08hの処理ルーチンに次の1行を入れる。

reg_bh = 0x00;

たったこれだけのことなんだけど、この解にたどり着くまで滅茶苦茶大変だった。

Image: FORMAT

一つ注意点があるとすれば、DOSBoxは基本的にBOOTコマンドでネイティブのDOSをロードした環境をあまり想定していないので、Ctrl+F4による切り替え以外ではディスクイメージの交換ができない。DOSVAXではWindows APIの「開く」ダイアログを呼び出してディスクイメージを交換できるように改造しているが、ドライブの種類は一度DOSを起動してしまうとその時にBDSへセットされた値で固定されるので、5.25インチフォーマットと3.5インチフォーマットの混合利用ができない。(実際はアクセス可能だが、フォーマットしようとするとスイッチがドライブタイプに対して無効というエラーが出る。)

DOSVAX 変更点 (4483PS12→ 4483PS13)

日本語で書き直すのだるいのでGoogle翻訳を使用。

  • Build 4483PS13 (2023/12/20)
    • JEGA描画機能における文字の点滅を修正しました。
    • エミュレータの動作中にフロッピー イメージをマウントするためのダイアログを表示する新しいキー バインドを追加します。(実験的)
    • ディスク ジオメトリ リストに 5.25 インチ 640 KB DSQD フォーマットのパラメータを追加します (このフォーマットは IBM DOS K3.x でのみ認識できます)。
    • DOS K3.3のFORMATコマンドでメモリデータが壊れる問題を修正。 これは、DOS 初期化で Int 13h、AH = 08h が呼び出され、BH レジスタがクリアされないときにブロック データ構造 (BDS) が破損するためです。
    • 一部の Int 13h 関数があいまいなエラー コードを返す問題を修正しました。

ダウンロード

GitHub - akmed772/dosvax: A folk of DOSBox that emulates the Japanese AX and PS/55 computers.


※コメント欄が表示されない場合はdisqusについてJavascriptが有効であることを確認して下さい。コメントはスパム防止フィルターによる承認制のため、投稿してもすぐに反映されない場合があります。

管理人 : Akamaki (akm)

は、PCとVTuberに夢中になっている電気技術者です。

私はレトロコンピューティングの愛好家ですが、そのようなリグはもう収集していません。

私の活動はトップページで見ることができます。読んでくれてありがとう!