BIOSを使わないでディスクアクセスするぞ!

  • (by K, 2006.03.26)
  • 発展課題のページ(全部読む前にここを読むのは混乱するのでおすすめじゃないです)

FDC(フロッピーディスクコントローラ)制御の基本

  • ここで説明しているのは内蔵FDDの制御法で、USB接続やPCカード接続のものはこの方法ではたぶん制御できません(ありがたいことにBIOSがエミュレーションしてくれる場合もあります)。USB接続やPCカード接続のFDDを制御したいときは、それぞれ専用の制御方法があります。
  • FDCにはコマンドを送りつけて制御します。ほとんどのコマンドは、コマンドを送ってすぐに実行を完了するのではなくかなり時間がかかります。実行が済むと割り込みで教えてくれますのでそれまではのんびり待つかもしくは他の処理をしましょう(IRQ-06)。
  • ディスクを読み書きするときは、まずヘッドを目的のシリンダへ動かします。その後メモリのどの部分を読み書きするかをDMAC(ダイレクトメモリアクセスコントローラ)という装置に指定してから、FDCに読み書きのコマンドを発行します。DMACの都合により、0x00000000〜0x00ffffffの範囲のメモリにしかアクセスできません。この範囲外のメモリに対してFDの読み書きを実行したければ、DMACの届く範囲へメモリの転送をする必要があります。DMAはマスタのチャンネル2を使います。
  • FDCへのコマンドの送り方:
    • まず io_in8(0x03f4) & 0x11 の値が0になるのを待ちます(コマンド08だけは例外でこのときは io_in8(0x03f4) & 0x10 )。
    • 次に以下を送信したいバイト数だけ繰り返します。
      • io_in8(0x03f4) & 0xc0 が 0x80 になるのを待つ
      • io_out8(0x03f5, データ);
  • FDCからのリザルトステータスの受け取り方:
    • 以下を受信しなければいけないバイト数だけ繰り返します。
      • io_in8(0x03f4) & 0xc0 が 0xc0 になるのを待つ
      • データ = io_in8(0x03f5);
  • とりあえずこれだけ知っていればいいかなという代表的なFDCコマンド:
    • :のあとにあるデータは書き込むデータではなくFDCから受け取るデータ(リザルトステータス)です。
    • シリンダ0へのシーク(ヘッドHは多分どちらでもよい)(エラーが起きたのでもう一度なんていうときもこれを使ってヘッドの位置を調節する)
      • [07] [00] (INT)
      • (INTを受け取ったら、すぐにコマンド[08]を実行しなければいけない)
    • シリンダC・ヘッドHへのシーク
      • [0F] [H<<2] [C] (INT)
      • (INTを受け取ったら、すぐにコマンド[08]を実行しなければいけない)
    • 割り込み状態取得
      • [08] : [ST0] [C] (Cは割り込み時のシリンダ番号)
      • このコマンドはすぐに実行完了する。(INT)はない。
    • 読み込み(シリンダC・ヘッドH・セクタS)
      • [E6] [H<<2] [C] [H] [S] [02] [12] [01] [FF] (INT) : [ST0] [ST1] [ST2] [C] [H] [S] [02]
      • CとHが変わらないのなら、連続したセクタを処理することができます(註1)。処理するセクタ数はDMACの設定で指定します。
    • 書き込み(シリンダC・ヘッドH・セクタS)
      • [C5] [H<<2] [C] [H] [S] [02] [7F] [12] [FF] (INT) : [ST0] [ST1] [ST2] [C] [H] [S] [02]
      • CとHが変わらないのなら、連続したセクタを処理することができます(註1)。処理するセクタ数はDMACの設定で指定します。
  • コマンドはうまく実行できたの?:
    • リザルトステータスの、[ST0]を見ると分かります。
      • [ST0] & 0xc0 が 0x00 : コマンドの正常終了
      • [ST0] & 0xc0 が 0x40 : コマンドの異常終了
      • [ST0] & 0xc0 が 0x80 : コマンドそのものが間違っていたという異常終了
      • [ST0] & 0xc0 が 0xc0 : PC/AT互換機では発生しない模様(状態遷移割り込み)
    • エラー処理については、まず読み書きでエラーが起きたのなら、シークのやり直しをしないで、3〜5回くらいはコマンドを実行してみるべきです。それでもダメなら、ヘッドの位置がおかしくなったせいかもしれないので、一度シリンダ0へ移動させて、それから目的のシリンダに移動して、また3〜5回くらいは挑戦します。このシリンダ0への戻しのあとの読み書きを数回やってもダメなら、もうダメだと思うのでエラーにしましょう。
  • DMACの設定:
    • (注意)DMAは使わない間や設定中は極力マスクしておくこと。守らないと誤動作の原因です。
    • 最初に1度だけやっておくべき設定
      io_out8(0x00d6, 0xc0); /* マスタのch0をカスケードモードに */
      io_out8(0x00c0, 0x00); /* スレーブのDMAを許可 */
      io_out8(0x000a, 0x06); /* マスタのch2のDMAをマスク */
    • 読み込み用設定
      io_out8(0x000b, 0x06); /* モード設定:デマンド・アドレス増加方向・メモリへの書き込み・ch2 */
      io_out8(0x0005, 0xff); io_out8(0x0005, セクタ数 * 2 - 1); /* バイト数の設定 */
      io_out8(0x0004, 番地 & 0xff); io_out8(0x0004, (番地 >> 8) & 0xff); 
      io_out8(0x0081, (番地 >> 16) & 0xff); /* メモリ番地の設定 */
      io_out8(0x000a, 0x02); /* マスタのch2のDMAをマスク解除 */
        (FDCへのコマンド送信開始)
        (FDCが実行)
        (FDCからのINT)
        (FDCからリザルトステータス読み取り)
      io_out8(0x000a, 0x06); /* マスタのch2のDMAをマスク */
    • 書き込み用設定
      io_out8(0x000b, 0x0a); /* モード設定:デマンド・アドレス増加方向・メモリからの読み込み・ch2 */
      io_out8(0x0005, 0xff); io_out8(0x0005, セクタ数 * 2 - 1); /* バイト数の設定 */
      io_out8(0x0004, 番地 & 0xff); io_out8(0x0004, (番地 >> 8) & 0xff); 
      io_out8(0x0081, (番地 >> 16) & 0xff); /* メモリ番地の設定 */
      io_out8(0x000a, 0x02); /* マスタのch2のDMAをマスク解除 */
        (FDCへのコマンド送信開始)
        (FDCが実行)
        (FDCからのINT)
        (FDCからリザルトステータス読み取り)
      io_out8(0x000a, 0x06); /* マスタのch2のDMAをマスク */
  • 忘れがちだけど大事なFDDのモータの制御:
    • シークや読み書きの前に、まずFDDのモータが回っているかどうかを確認します(回っているかどうかを io_in8() などで調べるのではなく、OSが最後にどちらに設定したかを覚えておいてそれを調べる)。回っていないのなら、 io_out8(0x03f2, 0x1c); を実行してさらに最低1秒間待ちます(通電した直後は回転数が安定しないため)。もしかしたら2〜3秒くらいは待つほうがいいかもしれません。
    • 読み書きが一通り終わってもうモータを止めてもいいかなと思ったら、 io_out8(0x03f2, 0x0c); を実行します。回しっぱなしでもいいやという考え方もありますが、FDが摩擦で徐々に磨り減るでしょうし、電気代ももったいないので、3〜10秒くらいアクセスのない状態が続いたらモータを止めるようにしておくといいでしょう(OSASKでは3秒にしてあります)。

  • 大事な注釈1(2009.03.01追記):
    • 実はいつでも簡単に連続したセクタを処理できるわけではありません。まだ条件があるのです。今回はtatsuさんがこれで苦労したようです。
    • PCのDMAはアドレスレジスタが16bitしかないチップを後付回路で適当に拡張して、24bitの番地を生成しています。0-15bitまでは io_out8(0x0004, ?); で設定するのに対して、16-23bitを io_out8(0x0081, ?); で設定するのはまさにその影響です(そしてさらに23-31bitのレジスタに関しては作ることまでサボった)。そしてIBMの技術者は手抜きをしていて、なんとDMAの番地が0-15bitで一杯になって繰り上がっても、16-23bitはそのままになってしまうのです。
    • これはかなりひどいことです。つまりこういうことなんです。
      ... 0x0012fffc 0x0012fffd 0x0012fffe 0x0012ffff 0x00120000 0x00120001 ...
    • こんな風にFDCからのデータを0x0012ff00番地へ読み込ませていたら、0x0012ffffの次が0x00130000ではなくて、0x00120000になってしまうのです。
    • これに対処するにはどうしたらいいかですが、一番お手軽な方法は、メモリの16MBの範囲のどこでもいいので、9KB確保します。これはもちろん上記のような64KB境界をまたがない領域です。IDTのそばのメモリがあいていたはずです。そしてFDCからのデータは必ずそこに読み込ませることにします。そしてFDCからの読み込みが完了したら、その9KBのメモリから自分の目的の番地へコピーするようにすればいいでしょう。・・・この方法なら、目的の番地が16MBの範囲外でも問題なくなるので、一石二鳥かもしれません。
      • ここで9KBにしたのは、一度に1トラックを全部読む場合の例です。

プログラム例

  • FDC制御専用のタスクを作る(そのほうがプログラムが書きやすいので)。
  • このタスクのFIFOに、他のタスクから次のようなコマンドを送る。
    • (コマンドを送るときは後続のデータが分断されないようにcliのままでまとめて送ること)
    • データリード: [1] [セクタ番号] [セクタ数] [番地] [FIFO32*] [データ]
    • データライト: [2] [セクタ番号] [セクタ数] [番地] [FIFO32*] [データ]
      • セクタ番号(0〜2879) = C * 36 + H * 18 + (S - 1)
      • FIFOは読み書きが完了したときに、終わったことを伝えるためのもの
  • プログラム(fdc.c)
    struct FIFO32 *fdc_fifo;
    
    void inthandler26(int *esp)
    /* FDCからの割り込み */
    {
        io_in8(0x03f4); /* から読み:IRQにCPUが気づいたことをFDCへ教えてあげる */
        io_out8(PIC0_OCW2, 0x66); /* IRQ-06を終了 */
        fifo32_put(fdc_fifo, 3);
        return;
    }
  • 工事中

こめんと欄

  • サンプルソース速く書いてくださいね! -- ソーラン 2006-03-26 (日) 16:01:21
  • サンプルって必要ですか? -- Clover 2006-05-07 (日) 06:02:47
  • 僕は当然必要に思うのですが、Cloverさんが僕の代わりにサンプルを作ってくれますか? -- K 2006-05-07 (日) 14:11:03
  • 作っていいのですか?いいならやってみますけど・・・。みなさんが理解できるか分かりませんが。でも、Kさんが書いたのになぜ分からないのかがフシギですが。 -- Clover 2006-05-07 (日) 15:15:04
  • こっちは、理解できるのにBIOSを使わずに時計を使うところのサンプルが必要と思うのはすこしおかしいと思いますか?ちなみに、作るのしばらく時間かかりますよ。 -- Clover 2006-05-07 (日) 15:16:20
  • 作っていただけるのでしたらとても助かるのでよろしくお願いします。まあ2ヶ月くらいはのんびりお待ちしております。なお、できてもルール上、このページをCloverさんが勝手に書き換えてはいけませんので、適当なところにプログラムをアップロードしてダウンロードURLをご連絡ください。 -- K 2006-05-07 (日) 16:06:43
  • 分かりました。勉強もあるので暇を見つけて完成させます。 -- Clover 2006-05-08 (月) 16:01:28
  • 今、作成中なんですが・・・。どのようかサンプルがいいかこちらが決めてもいいですか?たとえば、独立ソース系とか -- Clover 2006-05-20 (土) 15:12:30
  • なんでもいいです。サンプルとして有効かどうかは僕ができたものを見て判断しますので。だから出来上がるまであれこれ質問はしないでください。 -- K 2006-05-23 (火) 00:18:48
  • 少々雑ですが、FDCが完成しました。こちらのOSははりぼてOSをベースにしているので少し改良するだけでhrbでも動くはずです。どうでしょうか?どこに公開しておけばよろしいでしょうか? -- tatsu 2008-03-02 (日) 07:46:04
  • とりあえずここでなければどこでもいいのですが、tatsuさんはwebサイトを既に持っているのでそちらを使うというのはどうでしょうか? -- K 2008-03-02 (日) 09:42:00
  • 適当な場所に今度アップしておきます。ちょっと変更がないとhrbosでは動かないので最大で2週間待っていてください。出来ましたら個人ページでお知らせします。 -- tatsu 2008-03-02 (日) 11:12:29
  • ところで( INT )は、どのように設定すればいいですか?そして、 FDCを実行するにはどうすればいいですか? -- Kor-LeeHeeRak 2008-10-28 (火) 16:35:16
  • きっと日本語だから分かりにくいのだと思います。ごめんなさい。・・・キーボードやマウスの割り込み処理を参考に、是非自分で考えてみてください。うまくできた人がたくさんいますよ。 -- K 2008-10-29 (水) 17:08:10
  • Kさんやその他だれでも構わないのですがサンプルソースをいただけないでしょうか。すでにtatsuさんのサイトはダウンしており現在tatsuさんのOSのソースコードが入手できない状態です。ネット上にFDCに関する資料が少なくてとても自分一人ではうまくいきそうにありません -- montblanc 2013-05-07 (火) 21:37:17
  • USB接続のFDD制御方法も載せて下さい! -- explosea 2017-01-20 (金) 19:13:21

コメントお名前NameLink

リロード   新規 編集 差分 添付   トップ 一覧 検索 最終更新 バックアップ   ヘルプ   最終更新のRSS
Last-modified: 2017-01-20 (金) 19:13:21 (912d)