起動情報の受け取りに構造体を使う 1 2 3 4 5 6 7 ; BOOT_INFO関係 CYLS EQU 0x0ff0 ; ブートセクタが設定する LEDS EQU 0x0ff1 VMODE EQU 0x0ff2 ; 色数に関する情報。何ビットカラーか? SCRNX EQU 0x0ff4 ; 解像度のX SCRNY EQU 0x0ff6 ; 解像度のY VRAM EQU 0x0ff8 ; グラフィックバッファの開始番地
それぞれこのアドレスに格納するので、
1 2 3 4 5 struct BOOTINFO { char cyls, leds, vmode, reserve; short scrnx, scrny; char *vram; };
先頭アドレスから構造体のポインタに型変換して受け取る。
1 2 struct BOOTINFO *binfo = (struct BOOTINFO *)0x0ff0 ;xsize = binfo->scrnx;
文字表示 フォントデータ作成 著者作ツールのmakefont.exe
でhankaku.txt
からfont.bin
を作成する。さんざん著者作ツールは使わないと言ってきたが、バイナリにしただけなので容赦してほしい。
オブジェクトファイル作成 このバイナリはただのバイナリなので、オブジェクトファイルにする必要がある。
1 objcopy -I binary -O elf32-i386 -B i386 --redefine-sym _binary_font_bin_start=hankaku font.bin font.o
-I
で入力ファイル形式、-O
で出力ファイル形式、-B
でアーキテクチャを指定する。このあたりの情報はobjcopy --help
で出てくる。
--redifine-sym
は、このような構文を取る。
1 2 --redefine-sym old=new Change the name of a symbol old, to new.
このオプションを指定しないと、バイナリの開始アドレスが_binary_font_bin_start
になってしまう。今後の開発の互換性のために、本書に合わせてシンボル名をhankaku
にしている。
objdump
で中身を確認すると、
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 $ objdump -x font.o font.o: file format elf32-i386 font.o architecture: i386, flags 0x00000010: HAS_SYMS start address 0x00000000 Sections: Idx Name Size VMA LMA File off Algn 0 .data 00001000 00000000 00000000 00000034 2**0 CONTENTS, ALLOC, LOAD, DATA SYMBOL TABLE: 00000000 l d .data 00000000 .data 00000000 g .data 00000000 hankaku 00001000 g .data 00000000 _binary_font_bin_end 00001000 g *ABS* 00000000 _binary_font_bin_size
このバイナリの開始アドレスはhankaku
で取得できることがわかる。
ややこしくなってきたので、Makefileを全部載せる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 IPL_NAME=ipl10 IMG_NAME=os SYS_NAME=haribote ASM_HEAD=asmhead BOOT_PACK=bootpack OS_LINKERSCRIPT=os.ld NASK_FUNC=naskfunc FONT_NAME=font default: $(IPL_NAME) $(SYS_NAME) mformat -f 1440 -C -B $(IPL_NAME) .bin -i $(IMG_NAME) .img :: mcopy $(SYS_NAME) .sys -i $(IMG_NAME) .img :: $(IPL_NAME) : nasm $(IPL_NAME) .nas -o $(IPL_NAME) .bin $(ASM_HEAD) : nasm $(ASM_HEAD) .nas -o $(ASM_HEAD) .bin $(NASK_FUNC) : nasm -f elf $(NASK_FUNC) .nas -o $(NASK_FUNC) .o $(SYS_NAME) : $(ASM_HEAD) $(NASK_FUNC) gcc -m32 -fno-pie -nostdlib -T $(OS_LINKERSCRIPT) $(BOOT_PACK) .c $(NASK_FUNC) .o $(FONT_NAME) .o -o $(BOOT_PACK) .bin cat $(ASM_HEAD) .bin $(BOOT_PACK) .bin > $(SYS_NAME) .sys $(FONT_NAME) : objcopy -I binary -O elf32-i386 -B i386 --redefine-sym _binary_font_bin_start=hankaku $(FONT_NAME) .bin $(FONT_NAME) .o clean: rm *.bin *.sys *.img *.o run: qemu-system-x86_64 -fda .\$(IMG_NAME) .img pre: nkf --overwrite *.nas *.c sed -i s/0x7dfe-\\$$/0x01fe-\(\\$$\-\\$$\\$$\)/ ipl10.nas sed -i s/_io_/io_/g naskfunc.nas sed -i s/^\\[/\;\\[/ naskfunc.nas asmhead.nas
標準ライブラリ導入とsprintf
sprintf
は文字列をフォーマットするだけなので、フォーマットだけsprintf
にやらせてメモリに書き込んでもらい、そこにある文字列をHariboteOSの機能で出力する。
-nostdlib
を外してmakeすると早速エラー。
1 2 # gcc -m32 -fno-pie -T os.ld bootpack.c naskfunc.o font.o -o bootpack.bin /usr/lib/gcc/x86_64-linux-gnu/7/32/libgcc_s.so.1: error adding symbols: File in wrong format
シンボルとあるのでリンカでエラーがあると見て、コンパイルとリンクを分離。
1 2 3 gcc -c -m32 -fno-pie $(BOOT_PACK) .c -o $(BOOT_PACK) .o ld -m elf_i386 -T $(OS_LINKERSCRIPT) $(NASK_FUNC) .o $(BOOT_PACK) .o $(FONT_NAME) .o -o $(BOOT_PACK) .bin
makeすると、
1 2 bootpack.o: In function `HariMain': bootpack.c:(.text+0xd3): undefined reference to `sprintf'
sprintf
の定義がないらしい。試しにnaskfunc.nas
にsprintf
を定義してみると、
1 2 3 4 5 6 7 ;12行目 ;GLOBAL io_load_eflags, io_store_eflags GLOBAL io_load_eflags, io_store_eflags, sprintf ;末尾 sprintf: RET
一応ビルドは成功してイメージファイルが出来上がった。しかしsprintf
がRET
するだけなので何も起こらない。
ただし上記のMakefileのld
の部分で、naskfunc.o
がbootpack.o
よりも先に来ていることに注意。
これでsprintf
の定義が無いだけであるとわかったので、どっかからゲッツしてくればいいのだが、色々探しても無いので自分で書くことにする。
1 2 3 4 int sprintf (char * buf, const char * format, ...) { }
セグメンテーション メモリをブロック化し、そのブロックの先頭アドレスを0として扱える機能。プログラムをORG 0として作ればよくなる。
32bitではMOV AL,[DS:EBX]
は16bitのときと意味が異なり、EBX
にセグメントの開始アドレスを足す。 セグメントはGDT(Global segment Descriptor Table)(構造体の配列)で管理する。セグメントレジスタは16bitだがCPUの仕様で下位3bitが使えないので、範囲は0~8191。
管理はC言語側で行う。
IDT(Interrupt Descriptor Table) 割り込み記述子表。割り込み番号0~256に対して、発生した番号に対応する関数を呼び出す。 仕組みはGDTと同じ。
おわりに 標準ライブラリ関連が面倒だったので自分で書いたが、本書の先の方を見てもsprintf
strcmp
くらいしか使ってないようなので必要になったときに実装していこうと思う。