OS自作入門 onLinux 3日目
文化祭関連の準備で忙しい。。。
10月が終われば大分楽になりそう。
3日目には手こずらされました。
何かって、まずasmhead.nasの内容が難解なのと、
筆者作の.hrbフォーマットの構造を知らずに適当に書いてたことかな。
でも、なんとか頑張ってGASに移植できました。
勿論、筆者作のedimg.exeや.hrb形式や.bim形式は一切利用しませんでした。
ちょっと4日目の内容に食い込んでますが、ここまでやらないと
成功したか確認できなかったので。
自作本中ではカラーコード15(白)でしたが、適当に10にしたら緑でした。
続きからどうぞ。
====
.equ CYLS, 10 .text .code16 jmp entry .byte 0x90 .ascii "HELLOIPL" # ブートセクタの名前 .word 512 # 1セクタの大きさ .byte 1 # クラスタの大きさ .word 1 # FATがどこから始まるか .byte 2 # FATの個数 .word 224 # ルートディレクトリのサイズ .word 2880 # このドライブの大きさ .byte 0xf0 # メディアのタイプ .word 9 # FAT領域の長さ .word 18 # 1トラックにいくつのセクタがあるか .word 2 # ヘッドの数 .int 0 # 必ず0 .int 2880 # ドライブのサイズ .byte 0, 0, 0x29 .int 0xffffffff # ボリュームシリアル番号 .ascii "HELLO-OS " # ディスクの名前 .ascii "FAT12 " # フォーマットの名前 .skip 18, 0x00 # 18バイト空ける # プログラム本体 entry: movw $0, %ax # レジスタ初期化 movw %ax, %ss movw $0x7c00, %sp movw %ax, %ds movw %ax, %es movw $0x0820, %ax movw %ax, %es movb $0x00, %ch # シリンダ0 movb $0x00, %dh # ヘッド0 movb $0x02, %cl # セクタ2 readloop: movw $0x00, %si # 失敗回数のカウンタ retry: movb $0x02, %ah # ディスク読み込み movb $0x01, %al # 1セクタ xorw %bx, %bx movb $0x00, %dl # Aドライブ int $0x13 jnc next addw $0x01, %si cmpw $0x05, %si # 5回失敗したらエラー jae error movb $0x00, %ah movb $0x00, %dl # Aドライブ int $0x13 # ドライブのリセット jmp retry next: movw %es, %ax addw $0x0020, %ax # アドレスを0x0200進める movw %ax, %es addb $0x01, %cl cmpb $18, %cl # 18セクタまで読み込む jbe readloop movb $0x01, %cl addb $0x01, %dh cmpb $0x02, %dh # 裏ヘッダを読み込む jb readloop movb $0x00, %dh addb $0x01, %ch cmpb $CYLS, %ch # CYLSシリンダまで読み込む jb readloop _load_fin: movb $CYLS, (0x0ff0) jmp 0xc200 error: movw $load_err, %si call print _error_fin: hlt jmp _error_fin # %si = string adress print: movb (%si), %al addw $1, %si cmpb $0, %al je _print_fin movb $0x0e, %ah # 一文字表示BIOSコール movw $15, %bx # カラーコード int $0x10 # ビデオBIOS呼び出し jmp print _print_fin: ret .data load_err: .string "load error\n"
IPL(ipl.s)です。前回からの変更点は、FDイメージを10シリンダまで
読み込んでいることと、最後に0xc200にジャンプしていることです。
詳細は自作本をご覧ください。
また、_load_finでCYLSの値、つまり読み込んだシリンダ数を
0x0ff0に格納しています。0x0ff0はシリンダ数を格納するようになっています。
.equ BOTPAK, 0x00280000 .equ DSKCAC, 0x00100000 .equ DSKCAC0, 0x00008000 .equ CYLS, 0x0ff0 .equ LEDS, 0x0ff1 .equ VMODE, 0x0ff2 .equ SCRNX, 0x0ff4 .equ SCRNY, 0x0ff6 .equ VRAM, 0x0ff8 .text .code16 head: movb $0x13, %al movb $0x00, %ah int $0x10 movb $0x08, (VMODE) movw $320, (SCRNX) movw $200, (SCRNY) movl $0x000a0000, (VRAM) movb $0x02, %ah int $0x16 movb %al, (LEDS) # 32ビットプロテクトモードへ移行 # PICへの割り込み禁止 movb $0xff, %al outb %al, $0x21 nop outb %al, $0xa1 cli # CPUレベルでも割り込み禁止 # A20信号線をON call waitkbdout movb $0xd1, %al outb %al, $0x64 call waitkbdout movb $0xdf, %al outb %al, $0x60 # enable A20 call waitkbdout .arch i486 # 32bitネイティブコード lgdtl (GDTR0) # 暫定GDTを設定 movl %cr0, %eax andl $0x7fffffff, %eax # bit31を0に(ページング禁止) orl $0x00000001, %eax # bit0を1に(プロテクトモード移行) movl %eax, %cr0 jmp pipelineflash pipelineflash: movw $1*8, %ax # 読み書き可能セグメント32bit movw %ax, %ds movw %ax, %es movw %ax, %fs movw %ax, %gs movw %ax, %ss # bootpackの転送 movl $bootpack, %esi movl $BOTPAK, %edi movl $512*1024/4, %ecx call memcpy # ブートセクタの転送 movl $0x7c00, %esi movl $DSKCAC, %edi movl $512/4, %ecx call memcpy # 残り movl $DSKCAC0+512, %esi movl $DSKCAC+512, %edi movl $0x00, %ecx movb (CYLS), %cl imull $512*18*2/4, %ecx subl $512/4, %ecx call memcpy # start bootpack movl $BOTPAK, %ebx movl $0x11a8, %ecx addl $3, %ecx shrl $2, %ecx jz skip movl $0x10c8, %esi addl %ebx, %esi movl $0x00310000, %edi call memcpy skip: movl $0x00310000, %esp ljmpl $2*8, $0x00000000 ########################### # function waitkbdout: inb $0x64, %al andb $0x02, %al inb $0x60, %al jnz waitkbdout ret memcpy: movl (%esi), %eax addl $4, %esi movl %eax, (%edi) addl $4, %edi subl $1, %ecx jnz memcpy ret ########################## # GDT .align 8 GDT0: .skip 8, 0x00 .word 0xffff, 0x0000, 0x9200, 0x00cf # 読み書き可能セグメント32bit .word 0xffff, 0x0000, 0x9a28, 0x0047 # 実行可能セグメント32bit .word 0x0000 GDTR0: .word 8*3-1 .int GDT0 .align 8 bootpack:
asmhead.nasを改良して、head.sとしました。
大きな変更点は、
・.arch i486で32bitコードになる
・#start bootpackの部分でレジスタに即値を代入している
・far jump(ljmp)で第2セグメントの0x00番地にジャンプしている
さて、1つ目はGASの仕様なので置いておきます。
2つ目と3つ目ですが、ともにhrbフォーマットに関わることです。
hrbフォーマットは以下のようになっています。
[ .hrbファイルの構造 ]
+ 0 : stack+.data+heap の大きさ(4KBの倍数)
+ 4 : シグネチャ "Hari"
+ 8 : mmarea の大きさ(4KBの倍数)
+12 : スタック初期値&.data転送先
+16 : .dataのサイズ
+20 : .dataの初期値列がファイルのどこにあるか
+24 : 0xe9000000
+28 : エントリアドレス-0x20
+32 : heap領域(malloc領域)開始アドレス
参考URL:http://blogs.dion.ne.jp/kazuu/archives/3051835.html
元々レジスタに代入しているのは、hrbのヘッダ部分の値です。
これは固定値なので、即値代入します。値は自作本P.170の通りです。
そして、0x00番地へのジャンプについてですが、元々は0x1bにジャンプしています。
第2セグメントはbootpack.hrbがあるわけですが(そうなるようhead.sでコピーした)、
bootpack.hrbの0x1bとは、ヘッダ部分です。
0xe9 0x90 0x00 0x00 0x00
⇒ jmp [エントリーアドレス]
つまり、bootpack.hrbのエントリーポイント、自作本ならばbootpack.cの
HariMain関数に飛んでいます。
ならば、始めからbootpack.hrbの中身に飛ばせばいいんです。
バイナリフォーマットなら、0x00番地から実行データなので、
直接0x00番地に飛ばします。
OSNAME=os ASRC=./src/asm CSRC=./src/c OBJ=./obj LS=./ls IMG=$(OSNAME).img OSSYS=$(OBJ)/$(OSNAME).sys IPL=$(OBJ)/ipl.bin BINOPT=-nostdlib -Wl,--oformat=binary QEMUOPT=-m 32 -localtime -vga std -fda $(IMG) : $(OSSYS) $(IPL) mformat -f 1440 -C -B $(IPL) -i $(IMG) :: mcopy $(OSSYS) -i $(IMG) :: $(OSSYS) : $(ASRC)/head.s $(ASRC)/func.s $(CSRC)/bootpack.c gcc $(ASRC)/head.s -nostdlib -T$(LS)/head.ls -o $(OBJ)/head.bin gcc $(CSRC)/*.c $(BINOPT) -c -o $(OBJ)/boot.o as $(ASRC)/func.s -o $(OBJ)/func.o ld -o $(OBJ)/boot.bin -e Main --oformat=binary $(OBJ)/boot.o $(OBJ)/func.o cat $(OBJ)/head.bin $(OBJ)/boot.bin > $(OSSYS) $(IPL) : $(ASRC)/ipl.s gcc $(ASRC)/ipl.s -nostdlib -T$(LS)/ipl.ls -o $(IPL) run : $(IMG) qemu $(QEMUOPT) $(IMG) debug : $(IMG) qemu -s -S $(QEMUOPT) $(IMG) -redir tcp:5555:127.0.0.1:1234 & img :; make $(IMG) clean :; rm $(OBJ)/*
Makefileです。今回は色々追加しました。
ファイル名はちょっと個人的にいくつか変えているので、対応を以下に示します。
ipl10.nas ⇒ ipl.s
asmhead.nas ⇒ head.s
naskfunc.nas ⇒ func.s
bootpack.c そのまま
asmhead.bin ⇒ head.bin
bootpack.hrb ⇒ boot.bin
haribote.sys ⇒ os.sys
haribote.img ⇒ os.img
$(IMG)、つまりイメージファイルの作成ルールでmcopyコマンドを使っています。
これはedimgにあたるもので、FDイメージにファイルを書き込みます。
$(OSSYS)の作成ルールのコマンド4行目で、自分でld(リンカ)にオブジェクトファイルを
通しています。
-eオプションでエントリポイントを選択できます。デフォルトは勿論mainですよね。
ここで、オブジェクトファイルを通す順番も変えてはいけません。
なぜなら、boot.oを最初に通さなければ、boot.oの始めに入ってくる
エントリポイントのMain関数がboot.binの0x00に来ないからです。
あとは、debugコマンドを入れておきました。
これで、gdb経由でqemuをデバッグできます。この詳細は後日。
正直、これにはとても助けられました^^;
make runを実行すると、以下のようにファイルが作成されます。
head.s ⇒ head.bin
bootpack.c ⇒ boot.o
func.s ⇒ func.o
boot.o + func.o ⇒ boot.bin
head.bin + boot.bin ⇒ os.sys
ipl.s ⇒ ipl.bin
ipl.bin + os.sys ⇒ os.img
そして、qemuにos.imgを通します。
ちょっと複雑になったので、ソースをアップしておきます。
os03.tar.gz ⇒ こちら
説明が不足しているかもしれないので、質問は遠慮なくどうぞー。