雑記

いち情報系大学生

OS学習メモ6(30日OS自作入門6日目)

やり残したGDTRの説明から。

_load_gdtr:		; void load_gdtr(int limit, int addr);
		MOV	AX,[ESP+4]		; limit
		MOV	[ESP+6],AX
		LGDT	[ESP+6]
		RET

出鼻をくじくようだけどもうこのコードに関してはこれ以上説明することがなさそう…と思ったけど、そういえば初見のときはリミットってなんだろうって思ってたような気がする。GDTRは48ビットのレジスタだがそのうち下位16ビットはこのリミットというのを表していて、これはセグメントディスクリプタテーブル(GDT)の大きさのこと。上位32ビットはGDTのある番地を表している。

では次に、GDTにセグメントの情報を設定するset_segdmesc関数を見ていく。

void set_segmdesc(struct SEGMENT_DESCRIPTOR *sd, unsigned int limit, int base, int ar)
{
	if (limit > 0xfffff) {
		ar |= 0x8000; /* G_bit = 1 */
		limit /= 0x1000;
	}
	sd->limit_low    = limit & 0xffff;
	sd->base_low     = base & 0xffff;
	sd->base_mid     = (base >> 16) & 0xff;
	sd->access_right = ar & 0xff;
	sd->limit_high   = ((limit >> 16) & 0x0f) | ((ar >> 8) & 0xf0);
	sd->base_high    = (base >> 24) & 0xff;
	return;
}

5日目の記事にも書いたが、ここで入れるべき情報はセグメントの「①大きさ②番地③属性」の3つ。これらを1つのセグメントにつき8バイトで表現する。

①はセグメントが何バイトであるかを表す”リミット”だが、セグメントの最大サイズが4GBであることを考えると、大きさを表現するだけで32ビットの数値になってしまってこれだけで用意された8バイトの半分を使ってしまう。ということで、リミットを20ビットで表現する別の方法がある。後述するセグメント属性にはGビットというフラグがあって、これを1にすると、リミットをバイト単位ではなくページ単位であると解釈するようになる。PCのCPUでは1ページ4KBなので,これでちゃんと4GBを表現できるようになった。
②はセグメントのある位置を表すが、メモリは4GBなのでこれを表現しようとするとどうしても32ビットになる。
③セグメント属性は、セグメントのアクセス権を表す。12ビット。
これでちゃんと8バイト。

これで、長かったGDTとIDTの説明が全部片付いたので早速マウスを動かしていこう...と思ったらまだPICの初期化が終わってない。PICはprogrammable interrupt controller(設定可能な割り込みコントローラ)の略で、8個の割り込み信号を1つの割り込みにまとめる装置。CPUは単独では1つしか割り込みを扱えないのでPICを使って制御しなくてはならない。

そんなPICの初期化のプログラム↓

void init_pic(void)

{
	io_out8(PIC0_IMR,  0xff  );  //全ての割り込みを受け付けない
	io_out8(PIC1_IMR,  0xff  ); //全ての割り込みを受け付けない

	io_out8(PIC0_ICW1, 0x11  ); //エッジトリガモード
	io_out8(PIC0_ICW2, 0x20  );//IRQ0-7をINT20-27で受ける
	io_out8(PIC0_ICW3, 1 << 2); //PIC1とIRQ2で接続
	io_out8(PIC0_ICW4, 0x01  ); //ノンバッファモード

	io_out8(PIC1_ICW1, 0x11  ); //エッジトリガモード
	io_out8(PIC1_ICW2, 0x28  ); //IRQ8-15をINT28-2fで受ける
	io_out8(PIC1_ICW3, 2     ); //PIC1とIRQ2で接続
	io_out8(PIC1_ICW4, 0x01  );  //ノンバッファモード

	io_out8(PIC0_IMR,  0xfb  ); //PIC1以外の割り込みを受け付けない
	io_out8(PIC1_IMR,  0xff  ); //全ての割り込みを受け付けない

	return;
}

IMRはinterrupt mask register(割り込み目隠しレジスタ)の略。全8ビットで、それぞれIRQ信号(interrupt request つまり割り込み要求信号)の8個に対応しており、このビットが1になると対応しているIRQ信号の割り込みを無視する。ICWはinitial control word(初期化制御データ)の略。ICWは1~4まであって合計で4バイトのデータになる。ICW1,4に関しては割り込み信号の電気的特性に関することなのでいじらない。ICW3は何個のIRQにスレーブが繋がれているかを8ビットで設定するもの。一方でスレーブの方では、それがマスタの何番に付いているかを3ビットで設定するがこれも変にはいじれないのでこの値のまま変更しない。この辺り詳しいことは「はじめてよむ486」が参考になる。

まぁまぁそんなわけでICW2のみを設定していく。これは、IRQをどの割り込み番号としてCPUに通知するかを決めるもので、今回の設定ではIRQ0-15の通知をINT 0x20-0x2fで受けるようにしている(0x00-0x0fは別の処理で使うため割り込みを受けるためには使えない)

これ以降はもう本通りに進めて…
f:id:unyamahiro:20180504203150p:plain
キーボードを押すと割り込み処理で文字列表示
f:id:unyamahiro:20180504203153p:plain

マウスはまだ動きません。7日目に。
それではまた〜