FATファイルシステムは既に、Linuxで当たり前に使えるし、フリーのソースファイルもいくつかある。それと、LNF(長いファイル名)はMSの特許があって、ライセンス料を払う必要がある、、、かと思ったら、ライセンスはもう期限切れだという情報があった。Linuxのvfatは長いファイル名を使えるのはそれが理由なのかもしれない。とにかく、自前で作るのは最初から諦めて、FatFsという日本人が開発したモジュールを移植することにしていた。
そのために、FDD関連の低レベルI/Oを操作するアセンブラプログラムのソースファイルFDD_IO.sを追加したら、ブートセクターがブートモニタープログラムをロードした後、ブートモニタープログラムへ制御が移らなくなった。FDD_IO.sを削除すると、うまくいく。Makefileを調べてみたが、おかしい所はない。make cleanでまっさらにして、作り直しても、FDD_IO.sがあるとイメージファイルは問題なく構築するが、実行時にブートモニターへのljmpの先から動かなくなる。数時間、悩んだ末、makeした時の、リンカーldのオブジェクトの並びを見ていて、ブートモニターのメインルーチンのオブジェクトの前に、FDD_IO.oが割り込んでいることに気付いた。試しにファイル名を小文字に変えてみたら、アスキーコード順のソートの影響か、メインルーチンのオブジェクトが最初にリンクされるようになって、うまくいった。
これはmakefileでの指定で回避できるのか、例えば@をメインルーチンの名前に入れたりすることで回避できるのか、あるいはリンカーの設定ファイルで指定すれば発生することがないのか、よく分からなかった。とりあえず、今は時間がないので、Makefileのリンカーの設定を、「${TARGET}.o ${filter-out ${TARGET}.o,${filter %.o,$^}}」という感じでオブジェクトファイルを展開するようにしたら、先頭にメインルーチンのオブジェクトがくるようになった。取り合えず、これでやり過ごすことにした。
FDD関連の回路の初期化で、PICの割り込みのIRQ6を許可し、初期化処理でドライブAをリセット。FatFsの低レベルレイヤーとして、3つの関数を作成しなければならない。DMA転送のために、0xf000をバッファとして使うことにし、DMA回路のレジスタの設定をしたところ、思わぬ不具合が起こった。ブートモニターが思わぬところで止まってしまう現象だった。ブートモニターのロジックをあれこれ修正しても駄目だった。
不意に思い付いて、ブートモニターのバイナリのサイズを調べてみたら、いつの間にか、ブートプログラムのサイズが16K-byteを越えていた。ブートセクターでは機械的に16K-byte分、フロッピーからブートモニターのプログラムを0x7c00へロードしていたのだが、プログラムの途中までしかロードされず、実行時に暴走していたらしい。C言語などの高級言語で作成する際の典型的な失敗で、グローバル変数や参照用のテーブル情報を何も考えずに作成していたら、大して長くもないプログラムでも、データサイズだけが肥大化してしまうのだ。ローカル変数を多用する場合も同様で、スタック領域を大きめに取らないとオーバーフローして、解析の難しい実行時の不具合が発生することになる。取り敢えず、自作OSは実用性より「動くこと」を優先することにして、ロードサイズを32K-byteに増やすことで対応した。
それからずっと、FDDアクセスが全くできない状態が続いた。FDDの初期化処理でFDコントローラーをリセットすると、バッファクリアなどしてモーターを有効にした後、割り込みIRQ6が発生するはずなのだが、どうしてもIRQ6の割り込みハンドラーのプログラムを実行してくれない。VMwareでためしてみたが、タイムアウトが発生するタイミングは違うが、やはりエラーとなる。FDDとは直接関係ないPICの処理が不十分で、IRQ6が発生していないのではないか、それで、無限ループでストップするか、タイムアウトでエラーになるということではないか、確証はないが、現象的には間違いないように思って、PICの設定を試行錯誤で変えてみた。しかし、どうしてもIRQ6の割り込みハンドラーが実行されない。この問題で一週間以上、悩み続けた。
原因は単純なことだった。PICやキーボードコントローラーやフロッピーディスクコントローラーはメインルーチンの冒頭で、ハードウェア初期化処理をそれぞれ行うのだが、回路への設定中に意図しない動作や割り込みが発生しないように、CLIで割り込みマスクをした状態でI/Oアクセスをしなければならない。ところが、フロッピーディスクコントローラーは初期化処理のシーケンスの中で回路リセットを行った時に、IRQ6の割り込みを発生させ、プログラム側はそれを待たなければならない。つまり、CLIの状態で割り込みが起こっても、割り込みハンドラーは起動されないので、リセットしても無限ループかカウンターを付けてもタイムアウトでエラーとなってしまっていた。FDDコントローラーの初期化ルーチン内で一時的にSTIで割り込みを許可し、ルーチンを抜けるときにまたCLIを実行するようにしたら、無事に割り込みハンドラーの処理が行われるようになった。これに気付いたときは、身体から力が抜けて溜息が出た。
それにしても、ディスクアクセスの操作は、レジスタの設定やコマンド転送待ちや、割込み処理、ドライブの機械動作の考慮などが入り乱れて、馬鹿みたいに複雑だ。フロッピーの発展の歴史的経緯もあるかもしれないが、どうやっても正解がなく、試行錯誤でターゲットマシンで動作するように調整しながら作成するしかない気がする。私の場合は、FDDの実行ファイルをメモリにロードして実行制御を移すまでしか、ブートモニターを機能を持たせることは考えていないが、ディスク操作の実装で挫折した人が多いのではないかと思う。
それからまた、フロッピーからRAMへ1セクターだけロードするコンソールコマンドを作成しようとしたが、ディスク操作は、コマンド送信フェーズ、実行フェーズ、結果フェーズの三段階の手順が必要であり、データをリードする時は、8bit I/oレジスタへ9バイトのコマンドとパラメーターを書き込んで、IRQ6の割り込みをウェイトし、ステータスの6バイトを8bit I/Oレジスタから読み出して多項目のエラーチェックをしなければならない。作成するだけで、一週間以上かかった。
しかし、何とかIRQ6の割り込み待ちをして、正常終了するまで辿り着いたのに、フロッピーのデータが0xf000のバッファへどうしてもロードされない。DMAは発生しているらしいが、RAMへ書き込まれていないように見えた。試行錯誤を繰り返し、思いつくことを全て試してみたところ、参考にしているホームページで、「利用しない場合は特に関係ない」と書かれていた拡張ページアドレスレジスタへ、ダミーで0x00を書き込んだところ、RAMへフロッピーのセクターのデータを書き込んでくれた。
参考にしたホームページでは、0x00000000から0x0000ffffまでの64kバイト以下のDMA転送のサンプルで、ベースアドレスレジスターとベースワードカウントレジスターだけ設定していたので、私も同じことをやっていて、拡張ページアドレスレジスターへのアクセスは意味がないと思いこんでいた。もう止めようかと考えていたところでもあり、びっくりした。このためにまた一週間は浪費してしまった。この一ヵ月近くは、まさに信じる者は自分しかないという、悪戦苦闘だった。挫折の記録として、残しておく。