自作OSに挑戦する日記 12日目
「30日でできる!OS自作入門」を読んで分かったことや、とりあえず書いておきたいことなどを書いていきます。
この本はChapterが1から30まであるので、各チャプター毎に1記事書いていきます。
Chapter 12 「タイマ - 1」
今回やった内容
- タイマ割り込みを有効にする
- タイマー作成
- 複数のタイマを管理できるようにする
環境
- メインPC
- MacBook Pro (13-inch, 2017, Four Thunderbolt 3 Ports)
- macOS Mojave 10.14
- 自作OSを動作させる環境
作業記録
OS自作日記12日目はタイマです。タイマは常に一定間隔で割り込みが来るものなのですが、これが非常に重要みたいです。
タイマが無いと
- プログラム側で時間を全て管理してあげなければならない
- CPUのクロック数が OO Hz だから1クロックの処理時間は…
- 👆をいちいち計算するのは面倒
- HLTが使えない(CPUを休ませることができない)
…という様々な不都合がことがあります。
しかしタイマがあると、一定時間で割り込みが来るため特に時間を気にする必要がなくなります。ということでタイマは重要らしい(大事なことなので2回)のです。
このタイマ割り込みを有効化させるには、以前キーボード割り込みを設定した時のようにIDTを設定した後ハンドラを作ってあげます。割り込みの間隔を設定するにはPITを弄ります。このPITはIQR0に繋がっているみたいなので、その部分の割り込みを受け取れるように設定します。
/* ここからGDT, IDTの設定 */ void init_gdt_idt(void){ struct SEGMENT_DESCRIPTOR *gdt = (struct SEGMENT_DESCRIPTOR *) ADR_GDT; struct GATE_DESCRIPTOR *idt = (struct GATE_DESCRIPTOR *) ADR_IDT; // GDT初期化 for(int i = 0; i < LIMIT_GDT / 8; i++){ set_segmdesc(gdt + i, 0, 0, 0); } set_segmdesc(gdt + 1, 0xffffffff, 0x00000000, AR_DATA32_RW); // メモリ全体のセグメント set_segmdesc(gdt + 2, LIMIT_BOTPAK, ADR_BOTPAK, AR_CODE32_ER); // bootpack.hrbのためのセグメント load_gdtr(LIMIT_GDT, ADR_GDT); // IDT初期化 for(int i = 0; i < LIMIT_IDT / 8; i++){ set_gatedesc(idt + i, 0, 0, 0); } // 割り込みが発生したらasm_inthander_2*を呼び出すようにIDTを設定している // タイマ割り込みが飛んでくるのはIRQ0 set_gatedesc(idt + 0x20, (int) asm_inthandler20, 2 << 3, AR_INTGATE32); set_gatedesc(idt + 0x21, (int) asm_inthandler21, 2 << 3, AR_INTGATE32); set_gatedesc(idt + 0x2c, (int) asm_inthandler2C, 2 << 3, AR_INTGATE32); load_idtr(LIMIT_IDT, ADR_IDT); } /* タイマ割り込みの間隔設定*/ void init_pit(void){ io_out8(PIT_CTRL, 0x34); // 0x34をまずOUT io_out8(PIT_CNT0, 0x9e); // 割り込み間隔下位ビット io_out8(PIT_CNT0, 0x2c); // 割り込み間隔上位ビット }
タイマ割り込みが有効になったので、タイマを作成します。続けて複数のタイマを管理する構造体も作成します。
struct TIMER{ unsigned int timeout, flags; struct FIFO8 *fifo; unsigned char data; }; struct TIMER_CTL{ unsigned int count, next, using; struct TIMER *timer_addr[MAX_TIMER]; struct TIMER timer[MAX_TIMER]; };
構造体TIMERはタイムアウトする時間を保持しています。このタイムアウト時間を過ぎることでfifoバッファにデータが送られ、キーボードの時と同じようにmain関数でタイムアウトをキャッチすることができます。構造体TIMER_CTLは複数のタイマを管理します。
後はタイマを設定する関数などを作成し、タイマを楽に扱えるようにします。
struct TIMER *timer_alloc(void); void timer_free(struct TIMER *timer); void timer_init(struct TIMER *timer, struct FIFO8 *fifo, unsigned char data); void timer_set(struct TIMER *timer, unsigned int timeout);
…ということでタイマの基本の実装が終わりました。実際にタイマを使って色々遊んでみました。
12日目完成〜! pic.twitter.com/kjHEb5WnEN
— ゆん (@yn0014) March 6, 2019
ちゃんと時間が測れている!!
まとめ
タイマの基本が実装できました!割り込みはキーボードの時に理解できていたのであまり詰まることもなく進めることができました。着実にこの子(HariboteOS)が出来ることが増えていっていますね…。
13日目は「タイマ - 2」ということでもう少しタイマについて掘り下げていくみたいです。今の実装だと少し不安な箇所があるので、13日目でしっかり直していきたいですね〜!