DOS 3.2のブートローダーとシリンダー境界による潜在的バグ
エミュ上で、FORMATコマンドを使ってDOS K3.3のシステムをハードディスクイメージに転送しても、そのイメージからDOSを起動する途中でハングするので、原因を調べていた。
原因はブートセクターにあるブートローダーがディスク上のIBMBIO .COMをメモリーにロードするとき、セグメント:オフセットアドレスがオーバーフローしてメモリー上にロードされたデータが上書きされたのが原因だった。
ブートローダーはディスクアクセスにInt 13h/AH=02hを使用している。このファンクションのパラメーターは以下の通り。
Int 13/AH=02h - DISK - READ SECTOR(S) INTO MEMORY
AH = 02h
AL = number of sectors to read (must be nonzero)
CH = low eight bits of cylinder number
CL = sector number 1-63 (bits 0-5)
high two bits of cylinder (bits 6-7, hard disk only)
DH = head number
DL = drive number (bit 7 set for hard disk)
ES:BX -> data buffer
ブートローダーがIBMBIO .COMをロードするとき、ALにはディスクのCHSパラメーターのうち”S”にあたる部分、つまり「1シリンダーあたりセクター数」を指定している。このファンクションはIBMBIO .COMのファイルサイズ分全てロードするまで複数回実行され、その度にBXレジスター(オフセット)にロードしたバイト数を加算する。
IBMBIO .COMのサイズは53.5KB。今回使用していたディスクイメージのCHSパラメーターは243-16-63。1回のInt 13hファンクション実行で63×512=31.5KB。ただし、初回のInt 13h実行時はシリンダー境界を考慮してか、31セクター(15.5KB)のみロードされる。よって、15.5KB + 31.5KB + 31.5KBの3回のInt 13h実行でIBMBIO .COMのロードが完了する。すると、3回目のInt 13h実行中にオフセットを指示するBXレジスターが16ビット最大値の65535を上回ってオーバーフローするが、セグメントアドレスはそのままなので、続いてメモリにロードされた部分の先頭からデータが上書きされていく。上書きされた中途半端なプログラムコードを実行したため、挙動が狂ったというわけ。
そもそもシリンダー境界を考慮する理由は、一部のBIOSでフロッピーディスクのマルチトラック読み取りがサポートされていないことに対する対策と、ハードディスクへのアクセスがフロッピーディスクへのアクセスと同様の手段を用いることで互換性を保っていることに起因するみたいだが、本当に面倒な話だ。(→ 後で知ったことだが、そもそもDOS 3.2はデバイスドライバーの構造上1トラックあたり40セクターまでしか対応していないらしい。DOS 3.3では63セクターまで対応するが、日本語DOS K3.3はDOS 3.2ベースなので未対応。)
この問題はCHSパラメーターを調整することで解決する。今回の場合なら、S=21にするかわりにC=729に増やす。CHSは729-16-21。総容量は変わっていない。Int 13hは1回あたり10.5KBとなり、IBMBIO .COMのロード中にオフセットがオーバーフローすることはない。DOS K3.3の動作環境にはハードディスク120MB内蔵モデル(5551-T0Bシステム装置)があるので、このモデルではCHSパラメーターがこの問題に該当しないようになっていたはずだ。
この問題はAX版MS-DOS 3.21でも発生する。後のバージョンではさすがに解決されている。そもそも、IBMBIO .COM単体でサイズが64KBを超えている。