0から作るソフトウェア開発

日々勉強中。。。

0から作るOS開発

環境準備

環境設定

ブートローダ

カーネルローダ

GRUB

カーネル

ドライバーその他

0から作るOS開発 割り込みその2 PICとIRQ

前回までの内容

これまでで、 ことがわかりました。それではカーネルを0から開発していきましょう!

今回はPICについて説明します

PIC(Programmable Interrupt Controller)

PICはその名の通りプログラム可能な割り込みコントローラです

PICにはいくつかの周辺デバイスとつながっていて、デバイスからの割り込み要求をまとめ、

周辺のデバイスが同時に割り込みを要求があった場合や、CPUが割り込み処理中の場合に

優先度の低い割り込みは、割り込みしないように”割り込みを整理”してCPUに伝えます

例えば、フロッピーディスクドライブがフロッピーディスクからデータを読み込む場合を考えてみます

FDDコントローラはPICにながっています。フロッピーディスクからのデータ読み込むが終わると

PICにIRQ信号を出します。PICは他に優先度の高いIRQが無ければ、CPUにIRQ信号をだします

信号を受け取ったCPUはIDTから受け取ったIRQに対応した番号の割り込みゲートディスクリプタを

参照します。そして、割り込みゲートディスクリプタに格納されている

割り込みハンドラのアドレスを参照し、ハンドラを実行します

FDDがデータ読み出しを完了すると、PICにIRQを送信。PICはCPUにIRQを出す。それを受けたCPUはIDTからハンドラのアドレスを読み出し、割り込み処理を行う

割り込みとPIC

パソコンにはキーボード、FDD、HDD、マウスなどなどたくさんの周辺デバイスがつながっています

これら周辺デバイスについてプログラムから制御したい場合には、ポーリングという方法が考えられました

ポーリングはプログラムから常に周辺デバイスの状態を監視する方法のことを言います

例えば、HDDにデータを書き込む場合に、HDDコントローラにデータ書き込み要求をします

要求を受けたHDDコントローラはデータをHDDに書き込みますが、かなり時間がかかります(CPUにとって)

書き込みが終わるまで、プログラムは書き込みが終わったかどうかをHDDコントローラに

問い合わせ(ポーリング)します。このようにポーリングをしているとHDDが書き込みしている間、

CPUは別の処理ができなくなってしまします。この非効率な処理をやめるために、割り込みが考えられました

CPUはHDDに要求した後は、割り込み信号が来るまで他の処理をしておきます

割り込み信号が来たら、割り込みハンドラの処理を実行することで、

格段に効率よく処理が行えるようになりました。しかし、割り込みはHDDだけではなく、

マウス、FDD、キーボードからもやってきますので、CPUで同時に割り込みを受けてしうと

何が何やらわけが分からなくなってしまいます。そこで、割り込みの信号を優先度でわけて、

優先度の高い割り込みから順にCPUに割り込みを要求するために

PICが作られました。PICからの割り込みを特にIRQといいます

PICの特徴

PICとして有名なのが8259AというIC(Integrated Circuits)です

データシートがダウンロードできますので興味の有る方は 8259Aのデータシート をダウンロードしてみてください

PIC(8259A)マイクロコントローラの特徴としては、 となります。PIC(8259A)は1つにつき8個までしかデバイスをつなげません

最初はこれでよかったのですが、マウスやら何やらデバイスがぞくぞく増えてきて8個では足らなくなってきました

そこでIBMが2個以上のPIC(8259A)をつなげることで数の問題を解決してきました

PIC(8259A)をカスケード(つなげる)ことで最大64個のIRQを持つことがでします

これにより、1つ目のPIC(8259A)をマスタと呼び、2つ目以降のPIC(8259A)をスレーブと呼びます

通常、PCには2つのPICがあります。1つ目はプロセッサ(CPUの周辺デバイスとして)に入っていて

2つ目はマザーボードに載っています(無いものもあります)

ですので、PCでは合計15個のIRQを扱うことになります

(8×2=16ではないのは、PICの1つのポートがスレーブと接続されているからです)

8259Aのハードウェア

ハードウェアに興味のない方は読み飛ばして行っても問題ありません

このブロック図が8259AのICです

8259Aのブロック図

ICの番号が付いているピンはコントローラやCPUと接続されています

デバイスの割り込みを制御するために、”プログラム可能な”このICに対して”プログラム”をしていきます

8259Aのピンアサイン
シンボル ピン番号 名称 説明
VCC 28 電源 +5Vを入力します
GND 14 グラウンド グラウンド
CS 1 チップセレクト
(Lowアクティブ)
CPUとデータをやり取りするICを決定する信号です
この信号がLowのとき8259Aはデータを入出力できます
WR 2 ライト
(Lowアクティブ)
CSピンがLowで、WRもLowの場合CPUが8259Aに
データを書き込むことができます
OUT命令を使用した場合にCSとWRがLowになります
RD 3 リード
(Lowアクティブ)
CSピンがLowで、RDもLowの場合CPUは8259Aから
データを読み込むことができます
IN命令を使用したときにCSとRDがLowになります
D7-D0 4-11 双方向データバス 8ビットのデータバスです
CPUは8259Aと8ビットのデータを読み書きできます
CAS0-CAS2 12、13、15 カスケードライン 割り込みできるIRQラインを増やすために、
ここにスレーブの8259Aを接続します
SP/EN 16 SP:スレーブプログラム
EN:バッファ有効
8259AのモードによってSPかENかの機能が変わります
バッファモード時
EN:データバスの送受信を制御します(出力)
バッファモード時以外
SP:Lowの時スレーブとして、Highのときマスタとして動作します
INT 17 割り込み CPUの割り込みピンINTRに接続します
割り込みが入るとHighにすることでCPUに割り込みを通知します
IR0-IR7 18-25 割り込み要求 周辺デバイスに接続します
周辺デバイスは割り込みを要求するときに
このピンをHighからLowにし、CPUからの割り込み応答が
返ってくるまでHighのままになります
INTA 26 割り込み応答 CPUの割り込み応答ピン(INTA)に接続します
A0 27 A0アドレスライン CS、WR、RDと連結します
CPUからのコマンド、CPUへのステータスを解釈するために使用します


特に割り込み時に重要な働きをするピンについてもう少し詳しく見ていきます

8259Aをカスケード接続する

このブロック図は2つの8259Aをカスケード接続したブロック図です

2つの8259Aをカスケード接続したブロック図

このようにCAS0-CAS2をお互い接続することでIRQの数を増やしています

右のブロックがマスタで、左がスレーブです。現在では右のマスタのPICはハード的にINT信号を

CPUに接続する必要があります。現在ではプロセッサ(=CPUといくつかの周辺デバイスのパッケージ)に

マスタのPICが含まれています。スレーブのPICは多くの場合マザーボードに載っています

スレーブPICのINTラインがマスタPICのIR6番ピンに接続されています

これにより、スレーブPICに割り込み要求があったことを割り込み信号としてマスタPICに通知しています

マスタPICはIR6の割り込みが発生したので、次にCPUに通知します。プログラムはIR6で割り込みが

入るのでスレーブPICのデバイスからIRQがあったことが判断できます

(実際のパソコンではスレーブはマスタのIRQ2に接続されています

その他にマスタPICのIR0(IRQ0)にPIT(Programmable Interval Timer)が接続されています

ハードウェアが割り込みを要求したときの動作

ハードウェアが割り込みを要求

ハードウェアが割り込みを要求するときにIRピンをHighからLowにすることで、PICに割り込みを要求します

  1. 少し上で出てきましたPIT(Programmable Interval Timer)が割り込みを要求するときに、
    IR0のラインをLowからHighにします

  2. PICは自身が持っているが割り込み要求レジスタ(IRR:Interrupt Request Register)の
    IR0に対応するビットを1にします。割り込み要求レジスタIRRはIRQが発生したことを憶えておくレジスタです

  3. PICは自身が持っている割り込みマスクレジスタ(IMR:Interrupt Mask Register)を見て、
    デバイスからのIRQを受付するかどうか判断します
    • 受付可能の場合は、より優先度の高いIRQが発生しているか確認します。優先度が
      高いIRQが発生中であれば、そのIRQが終わるまで保留します
    • 受付可能で、優先度の高いIRQが無い場合はそのIRQを受け付けます

  4. PICはINTピンをLowからHighにすることで、CPUに割り込みが入ったことを通知します

CPUがPICから割り込ハンドラを受け付ける

PICからINTピンで割り込みが入ったことが分かると、CPUは以下の処理を行います
  1. CPUはEFLAGSのIF(割り込みフラグ)をチェックします
    • IFが1の場合:INTAピンにパルスを出力して割り込みを受け付けることをPICに通知します
    • IFが0の場合:割り込みを無視します

  2. 通知を受け取ったPICはD0-D7データバスに割り込みベクタ番号を出力します
    (割り込みベクタ番号はPICの初期化時に送信するICW2(Initialize Control Word2)
    コマンドで取得します)

  3. PICは続けてD0-D7データバスにIRQ番号を出力し、
    割り込み中レジスタ(ISR:In-Service Register)の
    対応するIR0に対応するビットをP1にします。そしてIRRのIR0に対応するビットを0にします
ここまでで、CPUはハンドラを実行するために必要な割り込みベクタ番号と、IRQ番号を取得しています

割り込みハンドラを実行する

  1. CPUは現在実行中の処理(プロセスの実行)を途中で中断します
    中断すると、現在の実行中だったプロセスのEFLAGS、CS、EIPをスタックに積み上げます

  2. CPUは取得したベクタ番号に対応したIDTのゲートディスクリプタを参照します

  3. ゲートディスクリプタのセグメントセレクタの値を読み出しCSレジスタに格納します

  4. ゲートディスクリプタのOffsetの値(ハンドラのアドレス)を読み出し命令ポインタEIPに格納します
    ページングが有効になっている場合は、ハンドラのアドレス(ライナーアドレス)を物理アドレスに変換します

  5. CPUはCSレジスタに格納されたセグメントセレクタが実行可能かどうかチェックします

  6. CPUはCS:EIPのアドレスにある割り込みハンドラを実行します

この一連の処理はCPUのハードウェアで自動的に実行されます

そして割り込みハンドラが実行される

割り込みハンドラは、要求されたIRQに応じた処理を行います。この例ではタイマの割り込みが

発生しているので、タイムをカウントします。また、フロッピーディスクコントローラからのIRQであれば

ディスクから読みだしたデータを転送した後に割り込みが入りますので、次のセクタを読みに行ったりします

割り込みハンドラが実行されている間は、PICの割り込みマスクレジスタIMRでその他のIRQは

現在の割り込みが終わるまで、PICは保留したままとなっています。割り込みハンドラの処理が

終われば、PICに終了したことを通知します。ハンドラは割り込み処理の最後で、

割り込み終了(EOI:End Of Interrupt)コマンドをPICに送信することで通知します

割り込み終了EOIコマンドを受けたPICは割り込み中IRRレジスタの対応するビットをクリアし、その他の

割り込みを受け付ける状態になります。通常の関数処理から戻る場合はRET命令で戻りますが、

割り込みハンドラの場合はIRETD命令で戻る必要があります。IRETD命令は割り込み時に

スタックにPUSHしたEFLAGS、CS、EIPレジスタの値をPOPします

これにより元のプロセスの処理に戻ります

8259Aのレジスタ構成

8259AのPICを制御するためコマンドレジスタ、ステータスレジスタ、割り込み要求レジスタIRR、

割り込み中レジスタ(ISR)、割り込みマスクレジスタ(IMR)があります

コマンドレジスタ

書き込み専用のレジスタです。このレジスタにコマンドを書き込むことで

PICの動作を制御することができます

コマンドには8259aが持つレジスタの値を読み込むものや、初期化、

割り込み終了EOIコマンドのようにPICを制御するコマンドがあります

ステータスレジスタ

読み込み専用のレジスタです。コマンドレジスタにコマンドを書き込み、

PICを制御した結果などが格納されています

多くのICではこのようにICを制御するコマンドレジスタとその結果が

格納されているステータスレジスタがあります

割り込み要求レジスタ(IRR)

割り込み要求レジスタ(IRR)は周辺デバイスが割り込みを要求中かどうかを

対応するビットで記憶しているレジスタです

このレジスタはPIC内部のレジスタであることに注意してください

割り込み要求レジスタ(IRR)は下記表のようにIRQ番号が

各ビットに割り当てられています

割り込み要求レジスタ(IRR)
ビット IRQ番号
(マスタ)
IRQ番号
(スレーブ)
0 IRQ0 IRQ8
1 IRQ1 IRQ9
2 IRQ2 IRQ10
3 IRQ3 IRQ11
4 IRQ4 IRQ12
5 IRQ5 IRQ13
6 IRQ6 IRQ14
7 IRQ7 IRQ15


各ビットの値は0であれば、そのIRQ番号に対応するデバイスは割り込みを要求していないことになります

値が1であれば、デバイスは割り込みを要求中であることになります

割り込み中レジスタ(ISR)

割り込み中レジスタ(ISR)は、CPUはそのIRQ番号に対応するデバイスの割り込みを

処理中かどうかを対応するビットで記憶しているレジスタです

割り込み中レジスタ(ISR)
ビット IRQ番号
(マスタ)
IRQ番号
(スレーブ)
0 IRQ0 IRQ8
1 IRQ1 IRQ9
2 IRQ2 IRQ10
3 IRQ3 IRQ11
4 IRQ4 IRQ12
5 IRQ5 IRQ13
6 IRQ6 IRQ14
7 IRQ7 IRQ15


ビットが1の場合に、CPUは割り込み処理中であることを意味しています

PICはこのビットで現在処理中のIRQを判断しています

割り込みマスクレジスタ(IMR)

割り込みマスクレジスタ(IMR)は、周辺デバイスからのIRQを無効にするかどうかを

対応するビットで記憶しているレジスタです

割り込みマスクレジスタ(IMR)
ビット IRQ番号
(マスタ)
IRQ番号
(スレーブ)
0 IRQ0 IRQ8
1 IRQ1 IRQ9
2 IRQ2 IRQ10
3 IRQ3 IRQ11
4 IRQ4 IRQ12
5 IRQ5 IRQ13
6 IRQ6 IRQ14
7 IRQ7 IRQ15


ビットの値が0の場合は対応するIRQは無効となります

逆に1の場合はそのIRQは有効となります

8259Aのポートアドレス

ポートアドレスのマッピングをカーネルローダその3で示しました

ポートアドレスマッピングのPICマスタとPICスレーブのポートアドレスを使用していきます

周辺デバイスにはIN命令、OUT命令でポートを通してレジスタにアクセスします

8259Aのポートアドレス
ポートアドレス 説明
0x20 マスタPICのコマンドレジスタとステータスレジスタです
0x21 マスタPICの割り込みマスクレジスタとデータレジスタです
0xA0 スレーブPICのコマンドレジスタとステータスレジスタです
0xA1 スレーブPICの割り込みマスクレジスタとデータレジスタです


コマンドレジスタとステータスレジスタは同じあどれすですが、

IN命令とOUT命令によって、役割が違ってきます

OUT命令のときに0x20に書き込むと8259Aはコマンドレジスタとして動きます

IN命令で0x20にアクセスするとステータスレジスタとして動きます

ポートにアクセスする関数を作る

ポートへのアクセス方法はCPUアーキテクチャが異なれば、それぞれのCPUによって

違う命令が必要です(そもそも無いものが多い)。ですので、標準のC言語には

用意されていないので、アセンブラ言語で記述する必要があります

OUT命令

GASの記法でインラインアセンブラとしてOUT命令をC言語で記述します


/*
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
	Funtion     :outPortByte
	Input       :unsigned short address
		    < output port address >
		     unsigned char value
		    < output value for port >
	Output      :void
	Return      :void

	Description :output a value to port
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
*/
void outPortByte( unsigned short address, unsigned char value )
{
	__asm__ __volatile__( "out %%dx, %%al" : : "d"(address), "a"(value) );
}



インラインアセンブラを記述するには__asm__ __volatile__キワードを使います

DXに格納されているポートアドレスに対して、ALの値を書き込みしています

GASのAT& T記法では%をつけるとレジスタとしてアクセスできます

例えば、EAXなら%eax、EBPなら%ebpと書きます

このインラインアセンブラはGASの拡張インラインアセンブラで書いています

拡張インラインアセンブラは


	__asm__ ( "アセンブラ言語" : 出力オペランド : 入力オペランド : ワークレジスタ );



と書きます。オペランド、ワークレジスタは省略してもいいです

出力オペランドは規則を書いていきます


	"=規則"(C言語の変数)



と記述するとアセンブラ言語の結果をC言語の変数に格納します

(C言語の変数として”規則”のレジスタが割り当てられます)

入力オペランド、ワークレジスタも同様に


	"規則"(C言語の変数)



と書くことでC言語の変数を利用することができます。=がつくのは出力オペランドだけです

規則には下記表を使用します

拡張インラインアセンブラで使用する規則
規則 説明
"a" EAXレジスタを使用します
"b" EBXレジスタを使用します
"c" ECXレジスタを使用します
"d" EDXレジスタを使用します
"s" ESIレジスタを使用します
"D" EDIレジスタを使用します
"q" EAX、EBX、ECX、EDXの中から空いているレジスタを
自動的に割り当てます
"r" EAX、EBX、ECX、EDX、ESI、EDIの中から空いているレジスタを
自動的に割り当てます
"g" EAX、EBX、ECX、EDXの中から空いているレジスタか、
メモリを自動的に割り当てます
"m" メモリを割り当てます
"0"、"1"、"2"、・・・ 入力オペランド、出力オペランドで割り当てられたレジスタや
メモリを割り当てられた順に指します


OUT命令の関数を例にしますと、


	"d"(address)



で、変数addressにEDXを割り当てています。ここではunsigned short型ですので

下位16ビットのDXレジスタを使用することになります


	"a"(value)



で、変数valueにEAXを割り当てています。ここではunsigned char型ですので

EAXの下位16ビットAXレジスタの更に下位8ビットのALレジスタを使用することになります

アセンブラ言語にオペランドで規則を与えるときにはもうひとつ%を付け加えます


	out %%dx, %%al



このように%dxは%%dx、%alは%%alと書きます

IN命令

同様にIN命令についてもC言語のなかに記述していきます


/*
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
	Funtion     :inPortByte
	Input       :unsigned short address
				 < read port address >
	Output      :void
	Return      :unsigned char
				 < read value from port >

	Description :read a value from port
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
*/
unsigned char inPortByte( unsigned short address )
{
	unsigned char data;

	__asm__ __volatile__( "mov %%dx, %%ax" : : "a"(address) );
	__asm__ __volatile__( "in %al, %dx" );
	__asm__ __volatile__( "mov %%bl, %%al" : "=b"(data) );
	return( data );
}



この関数では変数addressで指定したポートアドレスから1バイト読み取り

変数dataに格納して、dataを返します


	__asm__ __volatile__( "mov %%dx, %%ax" : : "a"(address) );



変数addressの値をDXレジスタに格納します


	__asm__ __volatile__( "in %al, %dx" );



IN命令を実行します


	__asm__ __volatile__( "mov %%bl, %%al" : "=b"(data) );



IN命令で値がALレジスタに格納されたので、EBXレジスタのdata変数に

格納します。次の文でreturnします



この例は効率の悪い例ですので、実際に実装する際は効率よく

書き直したほうがいいと思います

とりあえず、これでC言語からIN命令、OUT命令を関数呼び出しで

使用することができるようになりました

この関数を使ってPICのコマンドレジスタにコマンドを書き込みます

8259Aの初期化コマンド

コマンドレジスタに制御コマンドを書き込みます。制御コマンドで、PICを

初期化したり、割り込みマスクの設定を行います

初期化制御コマンドICW(Initialization Control Words)

PICを初期化するにはICW(Initialization Control Words)というコマンドを

コマンドレジスタに書き込みます。ICWは4種類あり、マスタPICとスレーブPICにそれぞれ

コマンドを書き込む必要があります

ICW1(Intialization Control Word1)

PICを初期化するコマンドです。次のフォーマットに従ってコマンドレジスタに書き込みます

PICに送信するICW1コマンドのフォーマット

ICW1(Intialization Control Word1)のフォーマット
ビット シンボル 説明
0 IC4 PICがICW4コマンドを読み込むかどうかを設定します
0:PICはICW4コマンドを読み込みません
1:PICはICW4コマンドを読み込みます
1 SNGL SINGLEビット。8259Aがカスケード接続されるかどうか設定します
0:カスケード接続されます。ICW3コマンドを書き込む必要があります
1:8259Aは1つしかありません。ICW3コマンドを書き込む必要はありません
通常カスケード接続されているので0を設定します
2 ADI コールアドレスインターバル(ADdress Interval)を設定します
0:インターバルは8になります
1:インターバルは4になります
x86では設定しても無視されます
3 LTIM IRピンのIRQ信号検出方法を設定します
0:PICはIRQをエッジトリガします
1:PICはIRQをレベルトリガします
通常のマイコンでは割り込みはエッジトリガとします
4 予約 1固定です
5-7 Interrupt Vector Address 割り込みアドレスの下位3ビットです
MCS-80/85システム時のみ使用します
x86では無視されます


ICW1コマンドを実際にPICに書き込みます


	outPortByte( 0x20, 0x11 );	/* マスタPICのコマンドレジスタにICW1を書き込み		*/
	outPortByte( 0xA0, 0x11 );	/* スレーブPICのコマンドレジスタにICW1を書き込み	*/



ここではICW1のコマンドを0x11でマスタとスレーブに書き込みました

ICW2(Initialization Control Word2)

ICW2は、PICの初期化コマンドのなかで特に重要です

割り込みその1で見た割り込みベクタでベクタ番号の

32-255番が空いていました。IRQのベクタ番号は通常32以降を使います

ベクタ番号の32(16進数では0x20)以降を使うことをPICに教えるためにICW2コマンドを使用します

ICW2コマンドのフォーマットは以下になります

PICに送信するICW2コマンドのフォーマット

ICW2(Intialization Control Word2)のフォーマット
ビット シンボル 説明
0-2 Interrupt Vector Address
(MCS-80/85)
割り込みベクタアドレスの上位3ビットです
このビットはMCS-80/85システムで使用します
x86システムでは無視されます
3-7 Interrupt Vector Address
(x86)
x86の割り込みベクタアドレスです
アドレスと書かれていますが実際には
ベクタ番号を書き込みます


ICW2コマンドはICW1の後に続けて書き込む必要があります

更に、PICのコマンドレジスタに書き込むのではなくデータレジスタに書き込みます

ICW2以降のコマンドはICW1の引数みたいなもので全部データレジスタに書き込みます

上で書きました通りIRQで使用するベクタ番号は32番から使用します

マスタPICではIR0からIR7までの合計8個のIRQ0からIRQ7が使用できますので

マスタPICのIRQに使用する割り込みベクタ番号は32番(0x20)から39番まで使用するように設定します

スレーブPICもまた8個のIRQ8からIRQ15が使用できますので、

スレーブPICには割り込みベクタ番号40番(0x28)から48番までを使用するように設定します

設定例を見てみましょう


	outPortByte( 0x21, 0x20 );	/* マスタPICのデータレジスタにICW2を書き込み	*/
	outPortByte( 0xA1, 0x28 );	/* スレーブPICのデータレジスタにICW2を書き込み	*/



これで周辺デバイスから割り込み要求があれば、IDTの32番目以降の

ゲートディスクリプタが参照されるようになります

ICW3(Initialization Control Word3)

ICW3コマンドはスレーブPICがマスタPICのIRラインのどのラインにつながれているのかを

設定するコマンドとなります。

PICに送信するICW3コマンドのフォーマット

この図のフォーマットのようにICW3はマスタPICとスレーブPICでは別のフォーマットになっています

マスタPICに書き込むICW3は

マスタPICのICW3(Intialization Control Word3)のフォーマット
ビット シンボル 説明
0 S0 0:IR0はスレーブPICとつながっていません
1:IR0はスレーブPICとつながっています
1 S1 0:IR1はスレーブPICとつながっていません
1:IR1はスレーブPICとつながっています
2 S2 0:IR2はスレーブPICとつながっていません
1:IR2はスレーブPICとつながっています
3 S3 0:IR3はスレーブPICとつながっていません
1:IR3はスレーブPICとつながっています
4 S4 0:IR4はスレーブPICとつながっていません
1:IR4はスレーブPICとつながっています
5 S5 0:IR5はスレーブPICとつながっていません
1:IR5はスレーブPICとつながっています
6 S6 0:IR6はスレーブPICとつながっていません
1:IR6はスレーブPICとつながっています
7 S7 0:IR7はスレーブPICとつながっていません
1:IR7はスレーブPICとつながっています


一方スレーブPICに書き込むICW3コマンドのフォーマットは

スレーブPICのICW3(Intialization Control Word3)のフォーマット
ビット シンボル 説明
0-2 ID 接続されているマスタPICのIR0からIR7のIRQ番号を2進数で書き込みます
000b:IR0(IRQ0)
001b:IR1(IRQ1)
010b:IR1(IRQ2)
011b:IR1(IRQ3)
100b:IR1(IRQ4)
101b:IR1(IRQ5)
110b:IR1(IRQ6)
111b:IR1(IRQ7)
3-7 0 0固定です
ICW1コマンドのSGNLビットを1にしている場合はICW3コマンドを書き込む必要があります

カスケード接続する場合、スレーブPICのINTライトをマスタPICのIR0-IR7ラインに接続します

また、マスタ・スレーブPICのCAS0-CAS2を接続することでマスタとスレーブ間で通信を行います

マスタとスレーブがどのIRラインに接続されているのかを設定するのがICW3コマンドの役割です

x86システムではマスタPICのIR2(IRQ2)にスレーブPICが接続されています

ですので、マスタPICのICW3コマンドの値は0x04となります

また、スレーブPICのICW3コマンドの値は0x02となります(スレーブPICは2進数を16進数にコード化します)

ICW3コマンドの設定例を見て行きましょう。ICW3もデータレジスタに書き込みます


	outPortByte( 0x21, 0x04 );	/* マスタPICのデータレジスタにICW3を書き込み	*/
	outPortByte( 0xA1, 0x02 );	/* スレーブPICのデータレジスタにICW3を書き込み	*/



ICW4(Initialization Control Word4)

ICW4コマンドはPICの動作を設定するコマンドです

ICW1コマンドのIC4ビットを1に設定している場合はICW4コマンドを書き込む必要があります

ICW4コマンドのフォーマットは以下のようになります

PICに送信するICW4コマンドのフォーマット

ICW4(Intialization Control Word4)のフォーマット
ビット シンボル 説明
0 μPM 8259Aの動作モードを設定します
0:8086/8088(x86)モードで動作します
1:MCS-80/85モードで動作します
1 AEOI 割り込み終了コマンドEOI読み取りモードを設定します
0:自動モード。CPUからのIRQ受付応答パルスを受信すると、
 割り込み終了コマンドEOIを読み込んだとみなします
1:CPUは割り込み終了時に割り込み終了コマンドEOIを書き込みます
2 M/S ビット3のBUFが1に設定しているときにこのビットが使われます
ビット3のBUTが0の場合は無視されます
0:バッファモードのマスタとして動作します
1:バッファモードのスレーブとして動作します
3 BUF 8259Aをバッファモードに設定します
0:バッファモードで動作しません
1:バッファモードで動作します
4 SFNM IRQをネスト(多重割り込みを許可)するかどうか設定します
多数のスレーブを接続している場合にはIRQをネストする場合があります
0:IRQをネストしません
1:IRQをネストします
5-7 0 0固定です


ビット3のBUFはバッファモードに設定するビットですが、とりあえず現状は0に設定しておきます

現状のところビット0を1に設定してPICをx86モードで動作させます

それではICW4コマンドの設定例を見ていきます


	outPortByte( 0x21, 0x01 );	/* マスタPICのデータレジスタにICW4を書き込み	*/
	outPortByte( 0xA1, 0x01 );	/* スレーブPICのデータレジスタにICW4を書き込み	*/



これで8259AのPIC初期設定することができました。初期設定をすることで、

割り込みベクタ番号32番-47番のIRQが入るようになりますので、IDTの設定が必要となります

8259Aの操作コマンド

初期設定が終わるとPICは割り込みを受け付けるようになり、通常動作します

通常動作時にPICを制御するコマンドをOCW(Operation Command Words)と言います

OCWはOCW1からOCW3の3種類のコマンドがあります

今回はOCW1、OCW2コマンドについて見ていきます

(より詳しく知りたい方は8259Aのデータシートをご覧ください)

OCW1(Operation Command Word1)

OCW1の設定内容は割り込みマスクレジスタIMRに格納したい値そのものとなります

PICに送信するOCW1コマンドのフォーマット

OCW1(Operation Command Word1)
ビット IRQ番号
(マスタ)
IRQ番号
(スレーブ)
0 IRQ0
0:IRQ0のマスクを解除します
1:IRQ0をマスクします
IRQ8
0:IRQ8のマスクを解除します
1:IRQ8をマスクします
1 IRQ1
0:IRQ1のマスクを解除します
1:IRQ1をマスクします
IRQ9
0:IRQ9のマスクを解除します
1:IRQ9をマスクします
2 IRQ2
0:IRQ2のマスクを解除します
1:IRQ2をマスクします
IRQ10
0:IRQ10のマスクを解除します
1:IRQ10をマスクします
3 IRQ3
0:IRQ3のマスクを解除します
1:IRQ3をマスクします
IRQ11
0:IRQ11のマスクを解除します
1:IRQ11をマスクします
4 IRQ4
0:IRQ4のマスクを解除します
1:IRQ4をマスクします
IRQ12
0:IRQ12のマスクを解除します
1:IRQ12をマスクします
5 IRQ5
0:IRQ5のマスクを解除します
1:IRQ5をマスクします
IRQ13
0:IRQ13のマスクを解除します
1:IRQ13をマスクします
6 IRQ6
0:IRQ6のマスクを解除します
1:IRQ6をマスクします
IRQ14
0:IRQ14のマスクを解除します
1:IRQ14をマスクします
7 IRQ7
0:IRQ7のマスクを解除します
1:IRQ7をマスクします
IRQ15
0:IRQ15のマスクを解除します
1:IRQ15をマスクします


OCW1コマンドでマスクしたいIRQ番号のビットを1にすると、割り込みがマスクされ

割り込みが入らないようになります

OCW2(Operation Command Word2)

OCW2コマンドでPICの操作を行います

OCW2コマンドのフォーマットは以下になります

PICに送信するOCW2コマンドのフォーマット

OCW2(Operation Command Word2)
ビット シンボル 説明
0-2 L ビット6のSLビットが1の場合
現在の割り込み優先度を変更します
3-4 0 0固定です
5 EOI 割り込み終了コマンドEOIとして動作します
6 SL 選択(SeLection)ビットです
ビット0-2のLビットで指定した優先度に変更します
7 R ローテーション(Rotation)ビットです
あるデバイスの割り込みが受付けられた後
IRQ0-7の割り込み優先度をローテーションさせ
(低いものを高く、高いものを低くする)
均等に割り込みが入るようにします
特に重要なのがビット5のEOI(End Of Interrupt)ビットです

このビットを1にすると割り込み終了コマンドEOIコマンドとして機能します

ビット5-7はそれぞれのビットを1か0にすることで特別なコマンドとして機能します

ビット5-7の値の組み合わせで下記表のように機能します

OCW2コマンド機能(ビット5-7の組み合わせ)
ビット7
R
ビット6
SL
ビット7
EOI
説明
0 0 0 自動EOIモードでの優先度ローテーションを解除します
0 0 1 通常のEOIコマンドとして動作します
0 1 0 このコマンドを受けても特に動作はしません
0 1 1 EOIコマンドを受け付けると同時に優先度を変更します
1 0 0 自動EOIモードでローテーションを開始します
1 0 1 通常のEOIコマンドでローテンションします
1 1 0 現在処理中の割り込みの優先度を変更します
0 0 0 通常EOIコマンドとして動作し、優先度を変更して、
ローテーションします
OCW2コマンドの機能が色々ありますが、これらは特に細かい調整をしたいとき

(システムの応答が遅いから割り込み優先度を変えたいなど)

に使用します。通常はRビット=0、SLビット=0、EOIビット=1として

通常のEOIコマンドとして機能させます

EOIコマンド

これまで見てきましたように、割り込み処理中はIRQ番号に対応する

割り込み中レジスタISRのビットが1になります。このビットをクリアする

コマンドがEOIコマンドです。EOIコマンドを書き込まない限りは

割り込み中レジスタISRのビットはクリアされませんので、現在割り込み中の

IRQより低いIRQは入ってこなくなってしまいます。IRQの割り込みハンドラは

かならずEOIコマンドを書き込む必要があります。また、EOIコマンドは

マスタPIC、スレーブPIC両方に送る必要があります

(マスタのみに接続されたデバイスのIRQはマスタPICのみでOK)

EOIコマンド(OCW2コマンド)の例を見ていきます


	outPortByte( 0x20, 0x20 );	/* マスタPICのコマンドレジスタにEOIを書き込み	*/
	outPortByte( 0xA0, 0x20 );	/* スレーブPICのコマンドレジスタにEOIを書き込み	*/



長くなりましたので、今回はこのあたりまで。。。

次回はPICのまとめとPIT(Programmable Interval Timer)について説明し、

割り込みハンドラを作成します

inserted by FC2 system