C言語でカーネルを書き始める

前回はIPLを書いたので,いよいよカーネルを書き始めることが出来る。
今回からはC言語を使ってOSの開発を行う。

普段私たちは,C言語でアプリケーションプログラムの開発を行うとき,main関数からプログラムを書き始める,これはmain関数からプログラムが実行されるをこと知っているためだ(多くのC言語の教科書でそう説明されているので)。しかし実際には一番初めに実行されるのはコンパイラが暗黙のうちにプログラムの先頭にリンクしたcrt(C RunTime startup)と呼ばれるアセンブリで記述されたプログラムである。crtはCプログラム本体が実行に必要な環境(シェルから渡される引数や環境変数など)を整えた後,main関数をコールする。*1
そのため私たちはcrtを意識することなく,プログラムを書くことが出来た。しかし,OSの開発ではこのcrtを環境に合わせて用意する必要がある。

head.S

main関数へジャンプするだけの簡単なcrtプログラムを用意した:

        .text
        .code32                         // 32bit-protectmode
        .intel_syntax noprefix          // I hate AT&T-style
        .global _start
_start:
        jmp     main                    // jmp kernel main

C言語でディスプレイに文字を表示する

文字を出力するデバイス(ここでは以下ターミナルと呼ぶ)はいくつかの種類があり,私たちが普段使っている,ビデオカードに直接ディスプレイが接続されいるメモリマップ型ターミナル。RS-232インターフェースを用いたターミナル。ネットワークを経由するXターミナルなどがある。*2

今回は,一番馴染み深いメモリマップ型ターミナルの操作について述べる。

メモリマップ型ターミナルにはビデオRAM(VRAM)と呼ばれる特別なメモリ領域があり,これを介しディスプレイの操作を行う。
VRAMはメインメモリと同じアドレス空間の一部であり,メインメモリの操作と同様に操作可能である。(最近のビデオカードには高速化された専用のRAMが搭載されている。)*3
ビデオカードにはビデオコントローラーと呼ばれるチップが搭載されていて,VRAMから必要な情報を取り出しディスプレイへ信号を送り,VRAMの操作をディスプレイに反映させる。ビデオコントローラーはいくつかの表示モードを持っており,大きく分けて,次の二種類がある。

  • グラフィック表示用の,メモリとディスプレイのピクセルを対応させるグラフィックモード
  • テキスト表示用の,メモリ上のASCIIコードとディスプレイの文字を対応させるテキストモード

今回は簡単に文字を表示可能なテキストモードを使う。
ビデオコントローラーは初期設定でテキストモードになっており,これは80文字x25行の文字を表示可能である。
テキストモードで使われるVRAMはメインメモリの0xB8000にあり,画面上の一文字はメモリ上では二文字分(2バイト)を占めていて,下位バイトはASCIIコードで上位バイトは文字の色を表す値である。つまり,メモリの0xB8000に書き込んだASCIIコードは画面左上の一番目の文字と対応し,0xB8002のASCIIコードは二番目の文字に対応する。

文字の色を指定するカラーコードは次の通りである*4

Value Color
0x00 Black
0x01 Blue
0x02 Green
0x03 Cyan
0x04 Red
0x05 Magenta
0x06 Brown
0x07 Gray
0x08 Dark gray
0x09 Bright blue
0x0A Bright green
0x0B Bright cyan
0x0C Pink
0x0D Bright magenta
0x0E Yellow
0x0F White

kernel.c

したがって,画面左上から黄色い文字でhello,worldを表示するプログラムは次のようになる:

void main() {
  unsigned short *video = (unsigned short *)0xb8000;
  unsigned char  *str   = "hello,world";
  unsigned char  c;

  while(c = *str++) {
    *video++ = (0x0E << 8) | c;
  }

  while(1)
    ;
}

コンパイルとFDイメージの作成

ipl.bin

前回作ったIPL:

gcc -pipe -ffreestanding -fno-common -fno-builtin -fomit-frame-pointer -O2 -c -o ipl.o ipl.S
ld -nostdlib -Ttext=0x7c00 --oformat binary -o ipl.bin ipl.o
kernel.bin

今回作ったhead.Sとkernel.cをリンクしkernel.binを作成する:

gcc -pipe -ffreestanding -fno-common -fno-builtin -fomit-frame-pointer -O2 -c -masm=intel -o kernel.o kernel.c
gcc -pipe -ffreestanding -fno-common -fno-builtin -fomit-frame-pointer -O2 -c -o head.o head.S
ld -nostdlib -Ttext=0x4000 --oformat binary -o kernel.bin head.o kernel.o
fd.img

イメージファイルの作成。先頭セクタにipl.bin,ルートフォルダにkernel.binを書き込む。

mformat -f 1440  -C -B ipl.bin -i fd.img ::
mcopy kernel.bin -i fd.img ::

QEMUで実行

qemu -boot a -fda fd.img -m 8m

黄色い文字でhello,worldが表示できた:

参考サイト・文献

*1:西田亙 「BIOS版「Hello,World!」プログラムの作成」『インターフェース』2002年7月号 pp.69-70

*2:アンドリュー・S・タネンバウム,アルバート・S・ウッドハル 『オペレーティングシステム 第3版 設計と実装』 吉澤康文,木村信二,永見明久,峯博史訳 p.321-324

*3:VRAM 【Video RAM】 (Video Random Access Memory) | パソコン用語辞典

*4:Nätcasino för kräsna - Konsumentråd om svenska casinon | Passagen