読者です 読者をやめる 読者になる 読者になる

OS自作入門 onLinux 2日目

OS自作、2日目です。試験中なのは秘密
2日目は結構楽だったので、まとめていきます。

実は、IPLを読み込む位置がうまくいかず、バイナリを解読してたんですが…
==== まずはソースコードをGAS用に変換します。

.code16
.text
        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 $msg, %si
putloop:
        movb (%si), %al
        addw $1, %si
        cmpb $0, %al
        je fin
        movb $0x0e, %ah                # 一文字表示BIOSコール
        movw $15, %bx                # カラーコード
        int $0x10                # ビデオBIOS呼び出し
        jmp putloop
fin:
        hlt
        jmp fin

.data
msg: .string "Hello, world!\n"

まずは、コメントがついてなかったので、すべてつけました。
今回はちょっとずつGASのソースも解説していきます。

1行目.code16ディレクティブは、16bit用のバイナリを吐きます。
.byte、.word、.int、.ascii、.skipは前回説明したので省略。

26行目からプログラム本体です。
mov命令"w""b"とついていますが、GASではオペランドが2バイト(16bit)なら"w"を、
1バイト(8bit)なら"b"を命令のあとにつけることになってます。
ちなみに、4バイト(32bit)なら"l"、8バイト(64bit)なら"q"です。
あと、GASではデフォルトではAT&T記法なので、Intel記法のアセンブラ
ソースオペランドとデスティネーションオペランドが逆になっているのに注意。

(例) AT&T:movw $0, %ax → Intel:mov ax, 0

33行目でmsgのアドレスをsiレジスタに入れていますが、
ラベルのアドレスをレジスタにぶち込むときは"$"をつける。

35行目、siレジスタの数値をアドレスをして利用するときは
nasmでは[](大括弧)でくくるが、GASでは()(中括弧)でくくる。
※これはアドレッシングモードという使い方だが、厳密な説明は省く

48行目で.stringディレクティブをつかっているが、これと.asciiの違いは
「最後に自動で"\0"が入るかどうか」だけ。
.stringなら、勝手にヌルバイトが入る。

あとはOS自作本通りの説明です。



このIPLのソースだけでは、OS自作本にあるように0x7c00にロードしたり
できません。最後の0x55AAも消しましたし、1440KBまで埋めるような記述もありません。

まずは、リンカスクリプトを拡張します。

OUTPUT_FORMAT("binary");
IPLBASE = 0x7c00;

SECTIONS {
        . = IPLBASE;
        .text        : {*(.text)}
        .data        : {*(.data)}
        . = IPLBASE + 510;
        .sign        : {SHORT(0xaa55)}
}

2行目に、IPLBASEという変数を定義します。
リンカスクリプトは、3行目から始まるSECTIONSコマンドで、バイナリを制御できます。
詳しいことはあまり触れませんので、興味があったらこちらをどうぞ。



さて、それではMakefileを作ります。

IMG=os.img
IPL=ipl.bin

all : ipl.s
        make ipl
        make img
        make run

os.img : $(IPL)
        mformat -f 1440 -C -B $(IPL) -i $(IMG) ::

ipl.bin : ipl.s ipl.ls
        gcc ipl.s -nostdlib -Tipl.ls -o $(IPL)

run : $(IMG)
        qemu -m 32 -localtime -vga std -fda $(IMG)

ipl :;        make ipl.bin
img :;        make os.img

ファイル名は以下のようになっています。
IPLのソース:ipl.s
リンカスクリプト:ipl.ls
OSイメージ:os.img

OSイメージを作る時に、mformatコマンドを用いています。
これで1440KBまで埋めます。

また、QEMUに新しいオプションを色々つけています。
-m:RAMのサイズ(MB単位)
-localtime:ホストの時刻をゲストに設定する
-vgaVGAグラフィックスカードを設定する(stdで標準)



これで準備は整いました。

$ make run

ddec4484.png

前回と何の変化もありませんが、できました。
3日目はちょっと大変なんですよね。頑張ります。