起動するまでの長い道のり D言語編(1) 関数呼び出しの巻
不完全ながらプロテクト・モードに移行し、通常の32ビットコードを使う準備ができた。ここでいよいよD言語を導入していくことにする。
モジュール階層の準備
D言語のソース・ファイルをこれから追加していくわけだが、そのためにモジュール階層をまず用意することにする。モジュールとは、Javaで言うパッケージとほとんど同じものだ。ファイルパスでx/y/z.dというソースファイルは、モジュールで言うとx.y.zに属することになる。1ソースファイルが1モジュールとなる。で、x.y.zというモジュール階層を作る場合はxとyディレクトリを掘らなければならない。
ここではoutlandish.os以下に各ソース・ファイルを置くことにした。つまり各ソースファイルはoutlandish.os.XXXというモジュールを作ることになる。というわけで、outlandish/osディレクトリを掘る。
ソースコードの追加
とりあえずD言語のソースコードを追加してコンパイルできるようにする。
D言語のソース・ファイルの拡張子は.dだ。とりあえずOS起動時の処理を行う予定なので、outlandish/osにstartup.dという名前のファイルを追加する。内容は以下の通りにする。
/** * 初期化コード。 */ module outlandish.os.startup; /// 起動直後の処理。 extern(C) void startup() { // 止まる。 asm {hlt;} }
moduleという行がソース・ファイルの属するモジュールを示している。
startupという関数が実際に行われる処理だ。今はインライン・アセンブラのhlt命令で単に止まるようにしてある。extern(C)という指定は、難しい話だがC言語の関数と同じ呼び出し仕様を使うよという意味だ。D言語とC言語で関数の呼び出し方とかが微妙に違ったりする。ここでC言語の仕様にしておけば、アセンブラから呼ぶのが簡単になる。
コンパイルする
上記のファイルまで用意できたところで一端コンパイルを通しておく。本当にちゃんとD言語のソースがコンパイルできるのか不安なのだ。
MakefileにD言語をコンパイルするための指定を追加する。
# Makefile 10行目 DC = gdc DFLAGS = -nostdlib -fno-builtin -frelease -Wall -O3 -inline
# Makefile 16行目 OBJS = \ ipl.o\ setup.o\ outlandish/os/startup.o\
# Makefile 25行目 # 拡張子規則 %.o : %.d $(DC) $(DFLAGS) $(INCLUDES) -o $@ -c $< %.o : %.c $(CC) $(CFLAGS) $(INCLUDES) -o $@ -c $< %.o : %.s $(AS) $(ASFLAGS) $(INCLUDES) -o $@ $<
# Makefile 60行目 outlandish/os/startup.o: outlandish/os/startup.d
因みに、以前拡張子規則(.c.oとか)だったのが微妙に変更されている。ディレクトリを掘った場合に問題があったからだ(滝汗)。まあSVNリポジトリから持ってきていれば何の不都合もないけど……。%.dとあるのがD言語ソース用のコンパイル規則だ。
以上まで追加したところでmakeしてみる。すると何事もなかったかのようにコンパイルが通るはずだ。でなきゃビビる。
リンカ・スクリプトの修正
実は今まで手抜きでipl.oとsetup.o以外のオブジェクト・ファイルをリンクしないように指定していた(汗)。リンカ・スクリプトを修正してこれから追加されるオブジェクト・ファイルもリンクされるようにする。
# ipl.ls 12行目 /* IPLを納めるセクション */ .ipl 0x1000 : { ipl.o; setup.o; *(EXCLUDE_FILE(ipl.o setup.o)*); /* これ追加 */ . = ALIGN(32); /* カーネル終端 */ _kernel_end = .; }
追加した部分にipl.oとsetup.o以外のオブジェクト・ファイルが挿入されていく。
関数の呼び出し
setup.sからstartup.dのstartup関数を呼び出すようにする。C言語の関数はアセンブラからは_(アンダースコア) + 関数名でcallできたりする。引数がなければ単にcallするだけでOKだ。
# setup.s 41行目 # D言語関数へ。 call _startup
これで終り。簡単だ。