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 ⇒ こちら
説明が不足しているかもしれないので、質問は遠慮なくどうぞー。