2014年9月16日火曜日

[OS作成]30日でできる!OS自作入門 3日目 (1)

次の課題は、ブートセクタの次のセクタを読み込む処理。

シリンダ、ヘッド、セクタを指定して int 0x13 を呼び出す。
エラー発生時はCF=1となるので、その場合はエラーメッセージを表示する。

今日からイメージファイル名をtinyos.imgにした。
リンカスクリプトは変更なし。

Makefile

image_file=tinyos.img

ipl.bin: ipl.s lnk.ls
 gcc -nostdlib -o $@ -Tlnk.ls ipl.s
 gcc -Tlnk.ls -c -g -Wa,-a,-ad ipl.s > ipl.lst

zero.img:
# 1474560 - 512 = 1474048
 dd if=/dev/zero of=zero.img ibs=512 count=2879

image_file: ipl.bin zero.img
 cat ipl.bin zero.img > ${image_file}

img:
 make image_file

run:
 qemu-system-i386 -m 32 -localtime -vga std -fda ${image_file}

ipl.s

/*
 ipl.s
*/
 .code16
 jmp entry
 .byte 0x90
 .ascii "TINY_IPL" # ブートセクタの名前
 .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  # ?
 .int 2880  # ドライブのセクタ数
 .byte 0, 0, 0x29 # ?
 .int 0xFFFFFFFF # ボリュームシリアル番号
 .ascii "TINY-OS    " # ディスクの名前
 .ascii "FAT12   " # フォーマットの名前
 .space 18

// プログラム
entry:
 movw $0, %ax
 movw %ax, %ss
 movw $0x7C00, %sp
 movw %ax, %ds

 // 2セクタから1セクタ分読み込む
 movw $0x0820, %ax
 movw %ax, %es
 movb $0, %ch  # シリンダ0
 movb $0, %dh  # ヘッド0
 movb $2, %cl  # セクタ2

 movb $0x02, %ah # ディスク読み込み
 movb $1, %al  # 1セクタ読み込む
 movw $0, %bx  # ES:BX Data buffer(0x8200に読み込む)
 movb $0x00, %dl # Aドライブ
 int $0x13  # BIOS interrupt call
 jc error

fin:
 hlt
 jmp fin

error: 
 movw $msg, %si
putloop:
 movb (%si), %al
 add $1, %si
 cmp $0, %al
 je fin  # メッセージの後ろの0x00で終了する
 movb $0x0E, %ah # Write Character in TTY Mode
 movw $15, %bx # カラーコード
 int $0x10  # BIOS interrupt call
 jmp putloop

// メッセージ 
msg: 
 .string "\n\nload error\n"

 .org 0x1FE
 .byte 0x55, 0xAA # 55AAでブートセクタ

$ make img
$ make run

で実行したが、エラーメッセージは出ていない。
指定したセクタを正しく読めているようであるが、本当にそうなのかは、画面からは分からない。

今日はここまで。

2014年9月15日月曜日

[OS作成]30日でできる!OS自作入門 2日目 (2)

書籍では、BIOSの解説ページ
http://community.osdev.info/?(AT)BIOS
が紹介されているが、SPAM業者?か何かに書き換えられているようで、
変なリンクがたくさん表示されおり、肝心の情報は何もない。

うーむ。どこを見れば良いかな?
探してみた。

このページが良いかな?
Interrupt Jump Table

知りたい割り込み番号をクリックすると詳細を表示できる。
例えば、今回、使っている int $0x10 は、
http://www.ctyme.com/intr/int-10.htm
であり、AX=$0x0Eについては、
http://www.ctyme.com/intr/rb-0106.htm
が、詳細情報である。

書籍と同様に、開発しやすくなるように、Makefileを作ることにしよう。

疑問点が一つある。
ブートセクタ以降に、以下のコードを書いていたが、これは何だろう。
以下のように編集して、ブートセクタ以降を全て0x00にしたイメージを使い、qemuで動作確認してみたが、問題なく動いた。
 .org 0x1FE
 .byte 0x55, 0xAA # 55AAでブートセクタ

// ブートセクタ以降
 
// .byte 0xF0, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00
 .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
 .space 4600
// .byte 0xF0, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00
 .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
 .space 1469432
とりあえず気にせずに、このまま進めよう。

作成したMakefileは以下の通り。
image_file=helloos.img

ipl.bin: ipl.s lnk.ls
 gcc -nostdlib -o $@ -Tlnk.ls ipl.s
 gcc -Tlnk.ls -c -g -Wa,-a,-ad ipl.s > ipl.lst

zero.img:
# 1474560 - 512 = 1474048
 dd if=/dev/zero of=zero.img ibs=512 count=2879

image_file: ipl.bin zero.img
 cat ipl.bin zero.img > ${image_file}

img:
 make image_file

run:
 qemu-system-i386 -m 32 -localtime -vga std -fda ${image_file}

リスティングファイル(アセンブル結果)を出力するには、gccの-Wa,-aオプションを渡して、asコマンドのリスティング出力を有効にする。

書籍では、独自コマンドを使って、イメージを作成していたが、
catコマンドで
(1)gccで作成した512バイトのブートセクタイメージ
(2)1.44Mバイト-512バイト(ブートセクタ分)のサイズを0で埋めたファイル
を連結して作成している。


これで、次のターゲットを利用できるようになった。

イメージファイルを作成する。
$ make img

作成したイメージをqemuを起動する。
$ make run

参考


[OS作成]30日でできる!OS自作入門 2日目 (1)

今度はプログラム部分を分かり易く置き換えていく。

helloos3.s

/*
 helloos3.s
*/
 .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  # ?
 .int 2880  # ドライブのセクタ数
 .byte 0, 0, 0x29 # ?
 .int 0xFFFFFFFF # ボリュームシリアル番号
 .ascii "HELLO-OS   " # ディスクの名前
 .ascii "FAT12   " # フォーマットの名前
 .space 18

// プログラム
entry:
 movw $0, %ax
 movw %ax, %ss
 movw $0x7C00, %sp
 movw %ax, %ds
 movw %ax, %es

 movw $msg, %si
putloop:
 movb (%si), %al
 add $1, %si
 cmp $0, %al
 je fin  # メッセージの後ろの0x00で終了する
 movb $0x0E, %ah # Write Character in TTY Mode
 movw $15, %bx # カラーコード
 int $0x10  # BIOS interrupt call
 jmp putloop
fin:
 hlt
 jmp fin

// メッセージ 
msg: 
 .string "\n\nhello, world\n"

 .org 0x1FE
 .byte 0x55, 0xAA # 55AAでブートセクタ

// ブートセクタ以降
 
 .byte 0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
 .space 4600
 .byte 0xF0, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00
 .space 1469432
起動時はリアルモードなので、.code16で、16bitコードを生成するように指定する。

ELFヘッダをスキップしたイメージを作るには、リンカスクリプトを使えば良いことが分かった。
NASMのORGに相当するものはGASには無いので、ORGで指定するアドレスはリンカスクリプトで設定する。

lnk.ls

OUTPUT_FORMAT("binary");

IPL_BASE = 0x7C00;

SECTIONS {
  . = IPL_BASE;
}
今までは、asコマンドとddコマンドでイメージを作成していたが、これからは、gccコマンドでイメージを作成できる。
$ gcc -nostdlib -o helloos3.img -Tlnk.ls helloos3.s
今まで通り、qemuで確認してOK。
$ qemu-system-i386 -m 32 -localtime -vga std -fda helloos3.img
ここを参考にすると、リンカスクリプトを
OUTPUT_FORMAT("binary");
IPLBASE = 0x7c00;

SECTIONS {
        . = IPLBASE;
        .text        : {*(.text)}
        .data        : {*(.data)}
        . = IPLBASE + 510;
        .sign        : {SHORT(0xaa55)}
}
のように書くと 0x1FEからの0xAA,0x55も定義できるようだけど、試していない。
今のところ、.text, .dataの設定は不要だった。
そのうち必要になるのかな?

Z80のアセンブラには慣れているせいか、GASの標準であるAT&T構文ではsourceとdestinationが逆なのは、ちょっと気持ち悪く感じる。

参考


2014年9月14日日曜日

[OS作成]30日でできる!OS自作入門 1日目 (3)

書籍と同様に、ディレクティブを使って、もう少し分かりやすく書き直した。
/*
 helloos2.s
*/
 .byte 0xEB, 0x4E, 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  # ?
 .int 2880  # ドライブのセクタ数
 .byte 0, 0, 0x29 # ?
 .int 0xFFFFFFFF # ボリュームシリアル番号
 .ascii "HELLO-OS   " # ディスクの名前
 .ascii "FAT12   " # フォーマットの名前
 .space 18

// プログラム

 .byte 0xB8, 0x00, 0x00, 0x8E, 0xD0, 0xBC, 0x00, 0x7C
 .byte 0x8E, 0xD8, 0x8E, 0xC0, 0xBE, 0x74, 0x7C, 0x8A
 .byte 0x04, 0x83, 0xC6, 0x01, 0x3C, 0x00, 0x74, 0x09
 .byte 0xB4, 0x0E, 0xBB, 0x0F, 0x00, 0xCD, 0x10, 0xEB
 .byte 0xEE, 0xF4, 0xEB, 0xFD

// メッセージ 

 .string "\n\nhello, world\n"

 .org 0x1FE
 .byte 0x55, 0xAA # 55AAでブートセクタ

// ブートセクタ以降
 
 .byte 0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
 .space 4600
 .byte 0xF0, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00
 .space 1469432
動作確認。
$ as --32 -o helloos2 helloos2.s
$ dd if=helloos2 of=helloos2.img skip=52 ibs=1 count=1474560
$ qemu-system-i386 -m 32 -localtime -vga std -fda helloos2.img

GASを使っていて、分かったこと

・コメントは3種類の書き方がある。
// コメント
/* コメント */
# コメント
・.stringディレクティブを使うと文字列の最後に0x00を付ける。
・.string,.asciiの文字列にはエスケープシーケンスが使える。

その他、分かったこと

・最初のセクタの最後の2バイトが0x55,0xAAの場合に、ブートセクタと判断し、プログラムを実行する。

ふーん。そういうふうに起動ディスクか判断していたのね。
ハードディスクの場合も同様かな?

やっと一日目が完了。先は長いな...

2014年9月13日土曜日

[OS作成]30日でできる!OS自作入門 1日目 (2)

数日経ったが、まだ1日目である。むぅぅぅ...

「30日でできる! OS自作入門」のサポートページにはLinux用のツールが公開されているが
筆者の独自ツールを使っているようだった。
あまり独自ツールは使いたくなかったので、いろいろと検索してみると、GASとGCCでも開発できるようだったので、
こちらを試してみた。
OS自作入門では、NASKというNASMベースのアセンブラを使っているが、GASとは構文が全く違うので、
書籍のコードを、いちいちGAS用に変換しなければならず、少し面倒だ。

NASM Intel構文
GAS AT&T構文

GASでは .intel_syntax ディレクティブを使うと、Intel構文にできるようであるが、とりあえずデフォルトのAT&T構文で開発を進めよう。
と言うことで、作成したのがこれ。
helloos1.s
.byte 0xEB, 0x4E, 0x90, 0x48, 0x45, 0x4C, 0x4C, 0x4F
.byte 0x49, 0x50, 0x4C, 0x00, 0x02, 0x01, 0x01, 0x00
.byte 0x02, 0xE0, 0x00, 0x40, 0x0B, 0xF0, 0x09, 0x00
.byte 0x12, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00
.byte 0x40, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x29, 0xFF
.byte 0xFF, 0xFF, 0xFF, 0x48, 0x45, 0x4C, 0x4C, 0x4F
.byte 0x2D, 0x4F, 0x53, 0x20, 0x20, 0x20, 0x46, 0x41
.byte 0x54, 0x31, 0x32, 0x20, 0x20, 0x20, 0x00, 0x00

.space 0x10

.byte 0xB8, 0x00, 0x00, 0x8E, 0xD0, 0xBC, 0x00, 0x7C
.byte 0x8E, 0xD8, 0x8E, 0xC0, 0xBE, 0x74, 0x7C, 0x8A
.byte 0x04, 0x83, 0xC6, 0x01, 0x3C, 0x00, 0x74, 0x09
.byte 0xB4, 0x0E, 0xBB, 0x0F, 0x00, 0xCD, 0x10, 0xEB
.byte 0xEE, 0xF4, 0xEB, 0xFD, 0x0A, 0x0A, 0x68, 0x65
.byte 0x6C, 0x6C, 0x6F, 0x2C, 0x20, 0x77, 0x6F, 0x72
.byte 0x6C, 0x64, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00

.space 368

.byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xAA
.byte 0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00

.space 4600

.byte 0xF0, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00

.space 1469432
参考にしたBlogでは .skipディレクティブを使っていたが、GASのマニュアルを参照すると、

.skip
.skip is recognized on the 680x0 platform as a synonym for .space.
.space
Syntax: .space size[, fill]
This directive emits size bytes, each of value fill. Both size and fill are absolute expressions. If the comma and fill are omitted, fill is assumed to be zero.

ということなので、RESBに対応するディレクティブは.skipではなく、.spaceを使用した。

$ as --32 -o helloos1 helloos1.s

--32 を付けて 32ビットコードを生成するようにした。

30日でできる!OS自作入門を Linux & GAS で行う (1日目) を参考に、同様にして、ELFヘッダのサイズを調べる。
$ readelf -h helloos1
ELF ヘッダ:
  マジック:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 
  クラス:                            ELF32
  データ:                            2 の補数、リトルエンディアン
  バージョン:                        1 (current)
  OS/ABI:                            UNIX - System V
  ABI バージョン:                    0
  型:                                REL (再配置可能ファイル)
  マシン:                            Intel 80386
  バージョン:                        0x1
  エントリポイントアドレス:               0x0
  プログラムの開始ヘッダ:          0 (バイト)
  セクションヘッダ始点:          1474664 (バイト)
  フラグ:                            0x0
  このヘッダのサイズ:                52 (バイト)  ← ◆これ
  プログラムヘッダサイズ:            0 (バイト)
  プログラムヘッダ数:                0
  セクションヘッダ:                  40 (バイト)
  セクションヘッダサイズ:            7
  セクションヘッダ文字列表索引:      4

ヘッダのサイズが52バイトなので、ddコマンドで52バイトスキップしてイメージを作成する。
$ dd if=helloos1 of=helloos1.img skip=52 ibs=1 count=1474560
1474560+0 レコード入力
2880+0 レコード出力
1474560 バイト (1.5 MB) コピーされました、 7.76736 秒、 190 kB/秒
最初に作った helloos0.imgと比較する。
$ cmp helloos0.img helloos1.img

qemuで動作確認。
$ qemu-system-i386 -m 32 -localtime -vga std -fda helloos1.img
違いが無いので、当然動く。問題無し。

GASやGCCでの開発方法をBlogで公開してくださった方々に感謝。

参考


2014年9月11日木曜日

[OS作成]30日でできる!OS自作入門 1日目 (1)

書籍ではWindowsを使っているが、
VirtualBox上のUbuntu14.04で開発することにした。

最初はバイナリエディタを使って、FDイメージを作成し、このディスクを使ってマシンを起動すると、
hello, world!
を表示できるようにするのが課題。

VirutalBoxでもqemuは動くのだろうか。
まずはqemuをインストールだ。

$ sudo apt-get install qemu

コンソールベースのバイナリエディタは何か良いのないかなと探してみたら、
Emacsのhexl-modeが、それらしい。
さっそく試してみる。

$ echo "hoge" > hoge.txt

emacs から M-x hexl-find-file で hoge.txt を開く。



標準で、こんな機能があったのか。
もっと早く知りたかった...

FDの容量の1.44MBまで00を埋めるのは面倒なので、ddコマンドで作ってしまおう。

$ dd ibs=1024 count=1440 if=/dev/zero of=helloos0.img
1440+0 レコード入力
2880+0 レコード出力
1474560 バイト (1.5 MB) コピーされました、 0.0289785 秒、 50.9 MB/秒

Emacsでhelloos.imgを開いて、hexl-modeで編集する。
このhexl-modeは1バイトをhexで入力するには、いちいちC-M-xで入力しないと駄目なのね...
面倒くさいと思いつつ、書籍の通りに入力。
ダンプリストを打ち込むなんて何年ぶりだろう。



qemuで起動してみる。

$ qemu-system-i386 -m 32 -localtime -vga std -fda helloos0.img



動いた!
hello, worldが表示された。

参考

バイナリファイルを編集するには

2014年9月10日水曜日

[OS作成]30日でできる!OS自作入門 0日目

30日でできる!OS自作入門という書籍がある。
発売当初は気になっていたのだけど、すっかり存在を忘れていた。
先日、マイナビ Booksを見ていたら、電子書籍化されていることが分かり、
また、キャンペーンらしく、少し安くなっていたので、購入してみた。

書籍紹介の内容を引用すると
プログラミングの基礎からはじめて、30日後にはウィンドウシステムを有する32bitマルチタスクOSをフルスクラッチで作り上げるという入門書。
一からOSを作ろうという内容。

章立ては Chapter0(0日目) から Chapter31(31日目) まである。
まずは、最初の0日目からスタート。

OSとは何?
何を作っていくかの説明。

文体はくだけた感じで、気軽に読める。
最初はアセンブラを使っていくようだ。
アセンブラを使うのは久しぶりだ。

投げ出さずに最後まで行けるかな?
mrubyTinySchemeを動くようにすると楽しそうだ。

参考

30日でできる!OS自作入門