Operating Systems Development Series | |
8237A ISA DMAC
イントロダクションようこそ!:) この章では、DMAC(Direct Memory Access Controller)を詳しく見ていきます。DMAC は、ソフトウェアに頼らず、デバイスから直接メモリにデータブロッ クを転送する方法を提供します。 ソフトウェアではなくハードウェアが行うので、非常に高速 なデータ転送が可能になります。 今日のリストはこちらです。
概要ダイレクトメモリアクセス(DMA)は、最近のすべてのコンピュータに搭載されている機能で、デバイスがプロセッサとの相互作用なしに大きなデータブロックを移動できるようにするものです。これは、フロッピーのプログラミングの章ですでにお分かりのように、便利な機能です。デバイスがデータブロックを転送している間、プロセッサはデータがメモリや他のデバイスに転送されるのを気にすることなく、自由にソフトウェアを実行し続けることができます。 基本的な考え方は、DMAデバイスが自分自身でタスクを実行するようにスケジュールすることができるということです。クールでしょう?バスやアーキテクチャの設計によって、ダイレクトメモリアクセスの方法は様々です。今回はISAダイレクトメモリアクセスコントローラ(DMAC)に焦点を当てますが、念のため他の方式も取り上げておくことにしました。 ISA業界標準アーキテクチャ(ISA)は、Intel 8237マイクロコントローラをベースにしたコントローラを通じて、DMAリクエストのための中心的な場所を提供します。ATXマザーボードの設計では、コントローラは1つだけでした。しかし、1つのコントローラで8台のデバイスをサポートするには限界があるため、AT以降のアーキテクチャでは、コントローラが2つ存在します。プログラマブル割り込みコントローラ(PIC)がスレーブ化されているのと同じように、2つのコントローラはスレーブ化されているのです。両コントローラは常に4MHzで動作します。性能とデバイス数の制限から、新しいデバイスはPIOやUDMAを使う傾向があります。 しかし、DMAはレガシーデバイスのためにISAでまだサポートされています。 これらのデバイスはすべて、コントローラ上のチャネルに接続されています。これらのチャネルとともに、各チャネルはDACK(DMA Acknowledge)ラインとDRQ(DMA Request)ラインを持っています。 以下は、両者のDMAC(Direct Memory Access Controller)の標準的な割り当てである。
転送を開始するために、ソフトウェアは、物理メモリ内の転送を完了する場所と転送サイズをチャネルアドレスとカウントレジスタに設定します。その後、そのメモリブロックから読み出すか書き込むかを設定し、コントローラを転送完了の方向に設定します。転送が完了すると、転送を開始したデバイスが割り込み要求(IRQ)を発行し、システムソフトウェアがそれをキャッチしてさらに処理します。これは重要なことです!DMA を使用して転送を開始する場合、これらのステップを実行する必要があります。 PCIPCIデバイスは、同じDMAコントローラを共有することも、中央のDMAコントローラを持つこともありません。その後、物理メモリの読み書き要求がノースブリッジに渡され、ノースブリッジは要求をメモリ操作に変換し、メモリコントローラに操作を送出します。PCI転送は4GBの物理メモリに制限されています。しかし、デバイスとPCIブリッジがダブルアドレスサイクル(DAC)または同様の技術を実装している場合、PCIコントローラは4GB物理メモリを超える読み取りと書き込みの要求を開始することができます。 ISA DMAハードウェアダイレクトメモリアクセスコントローラ(DMAC)ISA(業界標準アーキテクチャ)は、オリジナルのIntel 8237 DMAチップをベースにしたコントローラを使用しています。ほとんどの新しいDMACは、より多くの機能を提供しますが、8237マイクロコントローラとほぼ完全に下位互換性があります。新しいPCにはより高度なDMACが搭載されていますが、すべての始まりとなったデバイスを見ることは常に素晴らしいことです。そこで、DIP (Dual Inline Package)で配布されたオリジナルの8237Aコントローラのピン配置図を示します。![]()
こちらも悪くないですね。20番ピンをグランドに、31番ピンを電源に使っています。ピン12(CLK)は、すべてのコントローラで見られるもう一つの一般的なものです。プロセッサのCLKピンと接続し、入力クロック信号として、コントローラ内の動作タイミングを制御する。CS (Chip Select)ピンは、ほとんどのコントローラで共通に使用されているピンです。データ・バス上のI/Oデバイスとしてコントローラを選択するために使用されます。RESETは、コントローラの内部レジスタ(ステータス、リクエスト、テンポラリー、コマンド)をリセットし、内部フリップフロップをクリアし、マスクレジスタを設定します。ここまでは、特に目新しいことはありませんね。ゲルネリック・アドレスライン(A0-A7)は、システム・アドレス・バスに接続されています。入力時は、CPUはA0-A3にのみデータを書き込んで、読み出すレジスタを選択することができます。すべてのピンは出力(物理メモリアドレスへの)にも使用されますが、DMAリクエストのときのみアクティブになります。最後に、システムのデータバスに接続する一般的なD0-D7ピンがあります。 次に、より興味深いピンを紹介します。DMACがシステムアドレスとデータバスに接続されていることは、多くの読者が知っていることでしょう。しかし、DMACはCPUから直接注意を受ける必要があることはご想像いただけると思います。このため、DMACがCPUと通信できるように、またその逆もできるように、CPUに接続するいくつかのラインがあります。HACK(Hold Acknowledge)は、CPUがDMACにシステムバスの完全な制御を与えたとき、Highになります。これにより、DMACはいつメモリコントローラにデータを送信しても安全であることを知ることができる。結局のところ、DMACとプロセッサの両方が同時に同じシステムバスを使おうとすることはできないわけですね。 DMACが物理メモリに直接データを転送するのは、システムバスがプロセッサによって使用されていないときだけである。DMACは、メモリ変換や物理メモリへの読み書きのために、メモリコントローラにデータを伝送するシステムバスを必要とすることになる。 なるほど、CPUはDMACにシステムバスを引き継げることを伝える手段を持っているわけだ。でも、そもそもDMACはどうやってCPUにシステムバスが必要だと伝えるのだろう?それが、HREQ(Hold Request)ラインだ。DMAに接続されたデバイス(フロッピーディスクコントローラなど)からDMAリクエスト(DRQ)が発生し、その「チャンネル」が現在無効になっていない場合、コントローラは次のクロックサイクルでHREQをHighにして、リクエストを完了するためにシステムバスの制御が必要であることをCPUに通知するのである。 ラインDR0-DR3(DMA Request Lines)は、デバイスがDMAにリクエストを通知するために使用される。たとえば、フロッピーディスク・ドライブ・コントローラ(FDC)は、通常DR2(「チャンネル2」)を使用するように接続されている。そこで、そのチャンネルを有効にして、FDCがDMACを使うようにプログラムすると、FDCにリードまたはライト・コマンドが送られたとき、FDCはRQ2ラインをアクティブにして、DMACに注意を要することを通知する。ここから先は、そのチャンネルにプログラムしたモードに応じて、DMACを通じて読み書きが行われます。 ここでもうひとつ重要なポイントがあります。DRQラインは4本しかないのです。1つのDMACに接続できるデバイスは4つだけです。これはかなり制限されていますね。i86アーキテクチャでは、2つのDMACをくっつけることで、この問題はいくらか解決されました。そのあたりは、また後日。 今のところ、すべてうまくいっています。ソフトウエアがCPUにDMACをプログラムするように指示するのはわかったとして、CPUはどうやってDMACにレジスタの読み書きが必要であることを伝えるのでしょうか?それがIOR (I/O Read)とIOW (I/O Write)ピンです。同様に、DMACは、MEMR(Memory Read)またはMEMW(Memory Write)を出力して、メモリリードまたはライト制御線を活性化することにより、メモリコントローラに読み書きを指示する。EOP(End Of Process)は、リクエストが完了したときにデバイスに信号を送るために使用されます。リクエストは、そのチャンネルのターミナルカウント(TC)に到達すると完了します。これはプログラマブルなカウンタ値です。AEN(Address Enable)は、コントローラの内部8ビット・アドレス・ラッチ・レジスタをシステム・アドレス・バスにロードすることを通知するために使用されます。ADSTB(Address Strobe)は、上位アドレスバイトを外部ラッチレジスタにストローブするために使用されます。 いろいろとあるんですね。コントローラが行う操作の詳細は、使用するモードと転送タイプに依存します。しかし、基本的な手順は同じです。デバイスがDMACに通知し、DMACがCPUにシステムバスを介して制御を通知する。DMACは制御を待ちます。DMACは制御を待つ。制御を受けると、チャネルアドレスレジスタを内部ラッチレジスタにロードする。そこから、MEMR、MEMWを設定し、必要に応じてメモリの読み書きを行う。ちょっと、何?メモリから読み出すのはわかるけど、書き込むとなるとどこに行くんだ? もう一度、FDCの章を振り返ってみてください。DMACやCPU、メモリコントローラなどが見ているのと同じデータバスに接続するD0〜D7ピンを持っていることに気がつきましたか?つまり、メモリから書き込むときは、MEMRラインをアクティブにして、アドレスをアドレスバスにアップロードし、メモリコントローラがデータを変換してデータバスに置くだけでよいのです。FDCは書き込み要求を待っているので、読み込んだデータを取り込み、FDCに送られた書き込みコマンドで設定されたディスクに書き込む。ディスクから読み出す場合も基本的には同じですが、DMACが代わりにMEMWラインをアクティブにします。メモリコントローラは、FDCから送られてきたデータバスから書き込むデータを取得し、すべてがうまくいくと、DMACはプロセッサのHREQラインを解放し、CPUが再びバスを完全にコントロールできるようにする。 重要なことは、プロセッサはDMACの終了を待つことができないことです。CPUは、再びシステムバスへのアクセスが必要になると、HACKラインをLowにする。この間、DMACはこのラインが再びHighになるまで待って処理を続けるしかないのです。 そして、読者の皆さん、お待たせしました。i86では、DMACがもう1つ追加され、使用できるチャンネル数が8になっています。まあ、そんなところです。では、見てみましょう。 x86のDMAC新しいPCには2つのDMACがあることを思い出してください。2つのDMACは、2つのPICが接続されているのと同じような方法で接続されています...ただ、逆になっているだけです。はぁーっ!?そうなんです、そうなんです:)見てみましょう。![]() DRQ4は、どちらのデバイスにも存在しますが、それぞれのDMACを接続するものです。DRQラインはDMACのシグナルとして使用されるため、プライマリとセカンダリのDMACが互いに正しいDRQラインを上げるようシグナルすることができます。つまり、DMACをプログラミングするときは、DRQ4がプライマリとスレーブのコントローラーを接続するために使用されることを忘れてはならないのです。このため、DRQ4は使用できない。上の図を振り返ると、プライマリまたはスレーブのDMACが完了した(TC(Terminal Count)ラインを上げた)場合に真を出力するORゲートも見えます。TCラインは、DMACに送った転送要求が完了したときに上がる。 さて、ここで覚えておくべき重要なことをまとめておきますので参考にしてください。
ISA DMAインタフェースポートマッピングDMAコントローラが2つあるため、ポートも2セットあります。汎用レジスタ
これらのレジスタは次のセクションでより詳細に説明される。これらのレジスタは両方の DMAC と相互作用するとき使用される。それらはポートマップドI/Oを通して読んだり書いたりすることができる。つまり、標準的なi86のinと out命令を使用する。 DMACは逆方向であることを覚えておくことが非常に重要である。DMAC 0はスレーブで、DMAC 1はマスターです。 また、ポートの範囲がどのように異なるか注意してください。スレーブは8ビットで、マスターは16ビットであることを思い出してください。 読みやすくするために、これらの醜い数字を列挙して抽象化してみましょう。
これらの値は、上の表と一致していることに注意してください。次にDMAC 2について...
それでは、レジスタを紹介します。 チャネルレジスタi86では、上記のレジスタの他に、各チャンネルのアドレスやカウンタを制御するための以下のレジスタが用意されています。
上の表をもう一度見てください。マスターDMACのChannel 0 Addressは・・・なんと!ポート0です。これはこのシリーズの歴史的瞬間であり、我々は入出力ポート0を見つけたのです。 また、マスターDMACが16ビットで、スレーブDMACが8ビットであることを思い出してください。これは重要な特性で、特にここでは、スレーブDMACには8ビットの値を読み書きできますが、マスターDMACには16ビットの値を読み書きできることを意味します。 とにかく、これらのレジスタの詳細に入る前に、まずレジスタを隠してしまいましょう。以下の列挙を見ると、何も派手なことはしていないことがわかります。これらのレジスタは、すべてポートマップドI/Oでアクセスされることを忘れないでください。つまり、x86マシン命令で 読み書きできるのです。
次にDMAC 2について。
これらのレジスタの基本的な目的は、チャンネルをどのように開始するかをDMACに伝える方法を提供することです。 各チャンネルはベースアドレスとカウンタを持ちます。ベースアドレスは読み書きを開始するメモリ上の位置で、カウンタはそのチャンネルでどれだけ転送するかをDMACに伝える。重要なのは、これらは常に物理アドレスであり、仮想ではないことです。 例を見てみましょう。チャンネルが使用するベースアドレスを設定するために必要なことは、上の表に示された正しいi/oポートに書き込むことです。DMA0_CHAN0_ADDR_REGが 0 で、DMA1_CHAN7_ADDR_REGが表の最後の値 (0xde) であると仮定すると、これは簡単なことです。すべてのサンプルコードは、この章の最後のデモにあることを忘れないでください。 非常によく似た方法で、その特定のチャンネルのカウントレジスタを設定するルーチンを書くことができます。 これらのレジスタは16ビットであることに注意することが非常に重要です。つまり、DMACは1つのチャンネルから一度に最大64kしか転送できないのです。 また、これらは物理アドレスであることに注意することも非常に重要です!システムソフトウェアがページングを有効にしている場合、チャネルが使用する場所を同じ仮想アドレスにマッピングする必要があります。 というわけで、おさらいです。8つのチャネルがあることを知り、そのチャネルのデバイスがDMACを使用できるようにした後、チャネルレジスタの1つに書き込むことによって、チャネル情報(メモリの場所と、そこから読み書きする天候)を与えれば、DMACへの読み込みまたは書き込み転送を開始することができる。このデータはどこから来るのだろうかと思うかもしれません。あるいは、読み出しの場合、データはどこに行くのでしょうか。それは、そのチャンネルを制御しているデバイス次第です。たとえば、フロッピーディスクドライブの場合、フロッピーディスクドライブコントローラ(FDC)にリードコマンドを送ると、FDCはDMACに転送を開始するよう通知する。DMACはベースとなる物理アドレス、チャンネル操作(リードかライトか、この場合はリードを希望)、バッファのサイズを取得し、あとは自分で書き込みを行う。FDCはDMACにデータを転送し続け、DMACはそのチャンネルに格納されているアドレスが指すバッファにデータを入れます。ここで、そのチャンネルのアドレスとカウントレジスタに書き込むことで、バッファの場所とサイズを設定する。 待て、待て、待て。DMACが一度に64Kしか転送できないことを覚えてる?それよりももっと悪い。各チャンネルのベースアドレスも同じ制限を受けるので、DMACは64KのRAMにしかアクセスできないことになります。これは悪い制限だと思いませんか?これを解決するのが、外部ページレジスタです。よく見てみましょう。 拡張ページ・アドレス・レジスタページレジスタは、チャンネルが設定されているメモリロケーションがどのページに存在するかを設定するために使用されます。この8ビットをチャンネルのベースアドレスに追加すると(チャンネルのベースアドレスは0xFFFFF)、実質的に8ビット増えるので、最大16MBのメモリにアクセスできるようになります。これが、ページレジスタの仕組みです。このページ・レジスタは、そのチャンネルの転送アドレスの上位8ビットだけを格納します。 これは、このページ・レジスタの値が常に64Kの倍数であることを意味するので、重要な特徴です。 案の定、ここでちょっと面倒なことになります。DMACを1つ持つオリジナルのPCは、AT/EISA/MCAや新しいコンピュータとは異なるi/oポートを使用していました。そして、新しいコンピュータは2つのDMACを持っているので、追加のレジスタが追加され、より多くのビットで拡張されました。オリジナルのPCのページレジスタは、4ビット(A16-A19)しか追加されていません。一方、新しいコンピュータでは、ベースチャネルアドレスに8ビット(A16~A23)が追加された。
さて、ちょっと立ち止まってみましょう。*上の表で気になるのは、ATにリストされたポートだけです。ページレジスタは上位8ビットだけなので、これらのレジスタの値は64kの倍数でなければならないことを意味します。例えば、フロッピーコントローラをプログラミングするとき、フロッピーはDMAチャネル2を使用することが分かっています。64kより低いバッファを格納したい場合、そのチャンネルのアドレスをどこかに設定すればいいのですね?まあ、そんなところです。そのアドレスの上位8ビットを決定するために、ページレジスタを設定する必要があります。そこで、ページレジスタを設定します。
ページテーブルでページを変更すると、DMAが読み書きするアドレスが変わることに注目してほしい。これにより、DMACは最大16MBのメモリに効率よくアクセスできるようになります。いいでしょ?まだ少し制限されていますが、64Kに制限されるよりはずっといいと思いませんか? 他のレジスタと同様に、醜いマジックナンバーを隠すことができます。これらのレジスタを設定するために必要なことは、どのレジスタに書き込まれるかを(どのチャンネルが渡されたかに基づいて)決定し、そこに値を書き込むことだけです。 注意すべきは、ケース4がコメントされていることです。チャンネル4はマスターDMACとのカスケード接続に使われることを覚えていますか? これがなければ、何も使うことができませんから。上記の各ケースは、ページを設定するチャンネルを表しています。ですから、dma_set_external_page_register (2, 0x1000);のような呼び出しは、チャンネル2ページレジスタに0x1000を設定することができるようになります。どうです? レジスタ上記のレジスタに加え、コントローラは以下のレジスタも利用可能です。コマンドレジスタこのレジスタは、DMACを制御するために使用されます。このレジスタは次のようなフォーマットになっています。
念のため、これらは本章最後のデモのdma.hヘッダーファイルに含まれています。ここでは、ビットマスクとして記述しています。
モードレジスタ(Write)このモードは、コントローラのモードを設定します。次のような形式になっています。
まず最初に、私が好きなことは、意味のある名前の後ろに醜い数字を隠すことです。しかし、これは少し違います。これらの列挙は、マスクとフラグの組み合わせです。マスクは上のリストのビットフォーマットにマッチします。フラグは、単純化するために存在します。フラグは、上記のリストの必要なビットをセットしたりクリアしたりするためのもので、オプションをビット単位で OR したりすることができます。例えば、チャンネル番号とチャンネルのモードを組み合わせて、自動初期化でシングル転送を読むように設定するには、次のようにします。 チャンネル | DMA_MODE_READ_TRANSFER | DMA_MODE_MASK_AUTO | DMA_MODE_TRANSFER_SINGLE クールでしょう? フォーマットはどちらのコントローラでも同じなので、enum は 1 つだけです。
DMA0_MODE_REGが 0x0b で DMA 0 モードレジスタ、DMA1_MODE_REGが 0xd6 で 2 番目の DMA モードレジスタとすると、特定のチャネルの DMA モードを設定するには、以下のようにすればよいことになります。
このルーチンによって、任意のチャンネルのモードを設定することができます。クールでしょう?例えば、フロッピードライブに書き込む準備をさせたい場合、dma_set_mode (2, 0x5A)を実行すればよいのです。(フロッピーはプライマリDMACのチャンネル2を使うことを覚えていますか?) そして、0x56 = 01010110 バイナリです。上のリストと比較すると、Mode=01 (Single transfer), AutoInit is set (Auto initialize after completion), transfer type=01 (Write), channel 2 (10) となります。 DMA_MODE_MASK_AUTOビットは、便利なビットです。このビットのおかげで、DMACを一度初期化すれば(コントローラをリセットし、チャンネルバッファのアドレスとカウントを設定することで)、再度気にする必要はありません。このビットがセットされていない場合は、読み出しや書き込みのたびにDMACを再初期化する必要があります。 注:AutoInitビット(DMA_MODE_MASK_AUTO)は、Virtual PCではうまくサポートされていないようです。このため、Virtual PCでの移植性を維持するために、AUTOINITを使用するよりも、読み取りまたは書き込み操作のたびにDMACを再初期化することを選択しました。他のエミュレータやマシンではサポートされていないかもしれません。 リクエスト・レジスタ(Write)このレジスタは、ソフトウェアが直接DMACに送信することを可能にします。最初の2ビットでチャンネルを選択します。 例えば、00=チャンネル0、01=チャンネル1、10=チャンネル2、11=チャンネル3です。3番目のビットは、0ならチャンネルリクエストビットをリセットします。 1ならリクエストビットをセットします。
チャンネルマスクレジスタ(Write)このレジスタを使用すると、1つのDMAチャネルをマスクすることができます。0ビットと1ビットでチャンネルを設定します(00=チャンネル0、01=チャンネル1、10=チャンネル2、11=チャンネル3)。ビット4は、チャンネルをマスクするかしないかを決定します。ビット4が0の場合、チャンネルのマスクを解除する。1であれば、マスクする。他のビットは未使用。
マスクレジスタ(Write)このレジスタは、現在どのチャネルがマスクされ、マスクされていないかの情報を含んでいます。この8ビット・レジスタの上位4ビットは常に未使用です。下位4ビットは、4チャンネルのうちの1つをマスクまたはアンマスクするために使用されます。例えば、ビット 0 はチャンネル 0、ビット 1 はチャンネル 1、...といった具合です。注:チャンネル4をマスクすると,カスケード接続によりチャンネル4,5,6,7もマスクされます。
例えば、任意のチャンネルをマスク(無効化)するルーチンを用意すると、それぞれのビットを設定するだけでよい。 同様に、チャンネルのマスクを解除するには、そのビットをクリアするだけです。 これらのルーチンは両方とも、DMA0_CHANMASK_REGが0x0a(DMACマスクレジスタのi/oポート)、DMA1_CHANMASK_REGが0xD4(第2のDMACマスクレジスタのi/oポート)であると仮定しています。 複数のチャネルを同時に設定できるため、このレジスタを使用すると、複数のチャネルを同時にマスクしたり、マスクを解除したりすることができます。 ステータスレジスタステータス・レジスタは次のようなフォーマットになっています。
ISA DMAコマンドコントローラは、ソフトウェアがコントローラに対してコマンドを送信できるようにするための特別なレジスタを提供します。これらのコマンドは、特定のビットフォーマットを全く必要とせず、単純なI/O操作で起動することができます。DMACは、アドレスバス(ラインA0-A3)のデータと、ORQ、IOWラインの状態から、コマンドを認識します。 これらのレジスタには何も特別なものはないことに注意してください。これらは、本章の冒頭の汎用レジスタの表にも記載されています。 クリアバイトポインタ・フリップフロップこれは特別なi/oアドレスポートで、8ビットDMAC(プライマリDMAC)で作業するときに、16ビット転送の間でフリップフロップを制御できるようにするためのものです。両方のDMACに2つのポートがあります。
例えば、DMA0_CLEARBYTE_FLIPFLOP_REGが0x0c、DMA1_CLEARBYTE_FLIPFLOP_REGが0xD8とすると、以下のルーチンでフリップフロップをセットまたはクリアすることができます。
リセットよく似た方法で、以下のレジスタに任意の値を書き込むことで、a DMACをリセットすることができます。
例えば、DMA0_TEMP_REGが0x0Dであると仮定した場合。
全レジスタのマスク解除さらに似たような方法で、このコマンドでも同じコンセプトが適用されます!すべてのハードウェアプログラミングコマンドがこのように簡単であれば、素晴らしいことだと思いませんか?
つまり、DMA1_UNMASK_ALL_REGを0x0Eとすると、スレーブDMACから全レジスタのマスクを解除することになる。
よーし、もう一つのデモの時間だ!悪いニュースは、このデモが前の章と全く同じに見えるということです(BochsとVirtual PCの両方で動作するようになったことを除いて)。良い点は、新しいDMAインターフェイスを使用するようにアップグレードされていることです。 新しいコードのコアは HAL -dma.hとdma.cppにあり、この章にあるすべてのコードを含んでいます。しかし、1つだけ小さな変更があります。DMACのModeレジスタのAUTOINITビットはVirtual PCではうまくサポートされていないので、我々のdma_set_readと dma_set_writeルーチンはデモコードでビットをセットしません。
読み取りセクタ操作の間、フロッピードライバのflpydsk_read_sector_impルーチンは DMAC を初期化し、読み取り操作のために DMAC を準備します。残りのルーチン(編集済み)は前章と同じで、FDCにREADコマンドを送信する役割を担っています。DMA_BUFFERは、DMAC転送に使用できる空きメモリのバッファに過ぎません。dma_initialize_floppyは、新しい DMA ミニドライバを使用して DMAC を初期化し、フロッピードライバが使用できるように準備します。(DMAC を初期化した後、チャネルFDC_DMA_CHANNEL でドライバdma_set_readルーチンを呼び出して、READ 操作のための DMAC を準備します。FDC_DMA_CHANNELはチャンネル2です (FDCがDMACのチャンネル2を使うことを思い出してください?)。
dma_initialize_floppyは、新しいミニドライバを使って DMAC を準備し、フロッピィドライバで使用できるようにする役割を担っています。ここが楽しいところです。 まず、dma_reset()を呼んで、マスターDMACをリセットします。それから、dma_mask_channel() を呼んで、(FDC が使う) チャネル 2 を無効化 (マスク) します。これは、チャネルがもはや使用されていないことを確認し、それを変更できるようにします。 さて、次は楽しいことです。チャネルが使用するアドレスを設定するために、dma_set_addressルーチンを呼び出します。これにより、チャネルのアドレスの下位と上位の部分を設定することができます。メンバのバイト成分にアクセスするのを少し簡単にするために、ユニオンを使用します。つまり、a.l をチャネルが使用するバッファに設定します。ユニオンのおかげで、a.byte[0]はその値の下位バイトを、byte[1]は2バイト目を、といったように参照できるようになりました。バッファのサイズである長さも同じようにします。したがって、dma_set_addressを呼び出してバッファのアドレスを設定し、その下位バイトと上位バイトをバッファのアドレスに設定し、同じようにdma_set_countを呼び出して長さを設定しています。クールでしょう? でも、dma_reset_flipflopの呼び出しは何ですか?フリップフロップは8ビットDMACで16ビットデータを扱うときだけ使われる。もし16ビットDMACを使うなら、フリップフロップを呼び出す必要はないでしょう。フリップフロップは、16ビットデータの上位バイトと下位バイトを選択するために使用されます。フリップフロップをリセットすると、次のバイトデータがローバイトであることをDMACに伝えることになります。フリップフロップがデフォルトの位置でない場合は、ハイ・バイトとして選択されます。DMACは8ビットのデータバスで16ビットのデータを扱うので、これは選択されなければなりません。このバイトが16ビットデータのどの部分を指しているのか、どうやって知ることができるのでしょうか? 最後に、dma_set_read()を呼んで DMAC を読み出し用に設定し、すべてのチャネルのマスクを外して、デバイスが再び使用できるようにします。これは、FDCがDMACのチャンネル2を使えるようにするために重要です。
まとめさて、もう1章が終わりましたね。この章は以前の章ほど複雑でも難しくもなかったので、いい息抜きになったのではないでしょうか? ここからは、ディスクからファイルを読み込む機能がないと、これ以上進めません。ディスクからデータを読み込む機能はありますが、ファイルではありません。これはファイルシステムドライバを使って行います。しかし、ちょっと待ってください。FAT12についてはすでに2回ほど説明しましたね。でも、FAT12はもう2回目なんです!いかに頻繁に書き直す必要があるかがわかります。同じ内容を3回目も取り上げるよりは、もう1つテーマを追加することにしましょう。仮想ファイルシステム(VFS)です。次の章では、デモプログラムを実行できるようにするかもしれません。 また、OSにグラフィカルなタッチを加えたいという読者も多いので、Vesa Bios Extensions (VBE) や Video Graphics Array (VGA) / Super VGA (SVGA) 関連の上級編もリリースするかもしれません。また、現在のシステムを本物のマイクロカーネルにするために、DLLサポート、ドライバ、およびネイティブPEリソースのサポートがあります。 たくさんのクールなものが控えています :)もし、何か取り上げて欲しいトピックがあれば、遠慮なく教えてください。 |