Edited at

30日でOS自作入門挑戦記(Linux) Day2


概要

30日でOS自作入門挑戦記(Linux) Day1の続きです。

GitHubリポジトリ


環境


  • ArchLinux

  • Vim

  • zsh


2日目(11/7)


Chapter2 『アセンブラ学習とMakefile入門』

いよいよ、とりあえず書いていたアセンブラの学習が始まるようです。

まずはテキストエディタが紹介されています。筆者様のおすすめのテキストエディタはTerapadだそうですが、私はVimを使っていきます。

image.png

(正しいVimのロゴ)

前回のアセンブラではバイナリの内容をアセンブラ内にベタ書きしていた部分を、今回のパートではアセンブラ命令に直していくようです。

以下に示すのが、書中のnaskアセンブラをNASM向けに修正したアセンブラです。


helloos.asm

; hello-os

ORG 0x7c00 ; このプログラムがどこに読み込まれるのか

; 以下は標準的なFAT12フォーマットフロッピーディスクのための記述

JMP entry
DB 0x90
DB "HELLOIPL" ; ブートセクタの名前を自由に書いてよい(8バイト)
DW 512 ; 1セクタの大きさ(512にしなければいけない)
DB 1 ; クラスタの大きさ(1セクタにしなければいけない)
DW 1 ; FATがどこから始まるか(普通は1セクタ目からにする)
DB 2 ; FATの個数(2にしなければいけない)
DW 224 ; ルートディレクトリ領域の大きさ(普通は224エントリにする)
DW 2880 ; このドライブの大きさ(2880セクタにしなければいけない)
DB 0xf0 ; メディアのタイプ(0xf0にしなければいけない)
DW 9 ; FAT領域の長さ(9セクタにしなければいけない)
DW 18 ; 1トラックにいくつのセクタがあるか(18にしなければいけない)
DW 2 ; ヘッドの数(2にしなければいけない)
DD 0 ; パーティションを使ってないのでここは必ず0
DD 2880 ; このドライブ大きさをもう一度書く
DB 0,0,0x29 ; よくわからないけどこの値にしておくといいらしい
DD 0xffffffff ; たぶんボリュームシリアル番号
DB "HELLO-OS " ; ディスクの名前(11バイト)
DB "FAT12 " ; フォーマットの名前(8バイト)
TIMES 18 DB 0 ; とりあえず18バイトあけておく

; プログラム本体

entry:
MOV AX,0 ; レジスタ初期化
MOV SS,AX
MOV SP,0x7c00
MOV DS,AX
MOV ES,AX

MOV SI,msg
putloop:
MOV AL,[SI]
ADD SI,1 ; SIに1を足す
CMP AL,0
JE fin
MOV AH,0x0e ; 一文字表示ファンクション
MOV BX,15 ; カラーコード
INT 0x10 ; ビデオBIOS呼び出し
JMP putloop
fin:
HLT ; 何かあるまでCPUを停止させる
JMP fin ; 無限ループ

msg:
DB 0x0a, 0x0a ; 改行を2つ
DB "helloos, day2."
DB 0x0a ; 改行
DB 0

TIMES 0x7dfe-($-$$)-0x7c00 DB 0 ; 0x7dfeまでを0x00で埋める命令

DB 0x55, 0xaa

; 以下はブートセクタ以外の部分の記述

DB 0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
TIMES 4600 DB 0
DB 0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
TIMES 1469432 DB 0


1日目同様、RESB命令はTIMES命令とDB命令のコンボに置き換えています。

0x7dfeまで0で埋める命令を記述した箇所で、0x7c00による減算を行っていますが、これはORG(疑似)命令でメモリ上のどこにこのアセンブラの吐いた機械語が展開されるかを指定したことによるものです。じゃあ指定しなければいいじゃないかという話に思えますが、ORG命令による指定が行われていなければ動作しない命令が存在するため必要な作業となっているようです。

上のアセンブラから生成したイメージがちゃんとブートできるか試してみます。

DeepinScreenshot_select-area_20181108001625.png

OKですね。

この後、アセンブラに関する説明が書かれています。ここは特にLinux特有な箇所はないと思われます。

続いて、開発を便利にするために、ディスクイメージ全体をNASMで作成するのではなく、512byteのブートセクタのみを開発して空のディスクイメージに結合して利用する方式に切り替えるようです。書中では作者様が作成したディスクイメージ管理ツールを用いていますが、要は512byteのバイナリデータと数Mbyteの変更の必要がないディスクイメージテンプレートを結合するだけなので、以下のシェルスクリプトでいけます。shellはつよいので、これに権限を与えて


mkimg.sh

$ cat ./$1 > $2

$ cat ./disktmp.bin >> $2

↓これをアセンブルしたdisktmp.binを同一ディレクトリに配置し、


disktmp.asm

;disk tmplate asm

DB 0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
TIMES 4600 DB 0
DB 0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
TIMES 1469432 DB 0

スクリプトを実行すれば使えます。catの可能性は無限大(リダイレクトが強い)。

$ ./mkimg.sh [ブートセクタバイナリファイル] [出力イメージ名]

そして、これらの機能を総合したシェルスクリプトを作成しました。テンプレートのディスクイメージをいちいち用意するのも面倒なので、イメージをtarで圧縮して、そのバイナリデータをシェルスクリプトに埋め込むことでスクリプト単体で動作可能にしました。埋め込みの都合によりここに記載できなくなってしまったため、ファイルのリンクを載せておきます。

mkimg.sh

一度テキストエディタ等で開くと起動できなくなることがあるので注意です。(というかアセンブラのが分量少ないので、自動でアセンブルして結合したほうが賢かったかも?)

また、書中で触れられているipl.lstにあたる、アセンブラ上のどの命令がどのような機械語に変換されたかのリストファイルもNASMでは以下のコマンドで出力できるようです。

$ nasm [filename.asm] -l [filename.lst]


boot.lst

     1                                  ; hello-os

2
3 ORG 0x7c00 ; このプログラムがどこに読み込まれるのか
4
5 ; 以下は標準的なFAT12フォーマットフロッピーディスクのための記述
6
7 00000000 EB4E JMP entry
8 00000002 90 DB 0x90
9 00000003 48454C4C4F49504C DB "HELLOIPL" ; ブートセクタの名前を自由に書いてよい(8バイト)
10 0000000B 0002 DW 512 ; 1セクタの大きさ(512にしなければいけない)
11 0000000D 01 DB 1 ; クラスタの大きさ(1セクタにしなければいけない)
12 0000000E 0100 DW 1 ; FATがどこから始まるか(普通は1セクタ目からにする)
13 00000010 02 DB 2 ; FATの個数(2にしなければいけない)
14 00000011 E000 DW 224 ; ルートディレクトリ領域の大きさ(普通は224エントリにする)
15 00000013 400B DW 2880 ; このドライブの大きさ(2880セクタにしなければいけない)
16 00000015 F0 DB 0xf0 ; メディアのタイプ(0xf0にしなければいけない)
17 00000016 0900 DW 9 ; FAT領域の長さ(9セクタにしなければいけない)
18 00000018 1200 DW 18 ; 1トラックにいくつのセクタがあるか(18にしなければいけない)
19 0000001A 0200 DW 2 ; ヘッドの数(2にしなければいけない)
20 0000001C 00000000 DD 0 ; パーティションを使ってないのでここは必ず0
21 00000020 400B0000 DD 2880 ; このドライブ大きさをもう一度書く
22 00000024 000029 DB 0,0,0x29 ; よくわからないけどこの値にしておくといいらしい
23 00000027 FFFFFFFF DD 0xffffffff ; たぶんボリュームシリアル番号
24 0000002B 48454C4C4F2D4F5320- DB "HELLO-OS " ; ディスクの名前(11バイト)
24 00000034 2020
25 00000036 4641543132202020 DB "FAT12 " ; フォーマットの名前(8バイト)
26 0000003E 00<rept> TIMES 18 DB 0 ; とりあえず18バイトあけておく
27
28 ; プログラム本体
29
30 entry:
31 00000050 B80000 MOV AX,0 ; レジスタ初期化
32 00000053 8ED0 MOV SS,AX
33 00000055 BC007C MOV SP,0x7c00
34 00000058 8ED8 MOV DS,AX
35 0000005A 8EC0 MOV ES,AX
36
37 0000005C BE[7400] MOV SI,msg
38 putloop:
39 0000005F 8A04 MOV AL,[SI]
40 00000061 83C601 ADD SI,1 ; SIに1を足す
41 00000064 3C00 CMP AL,0
42 00000066 7409 JE fin
43 00000068 B40E MOV AH,0x0e ; 一文字表示ファンクション
44 0000006A BB0F00 MOV BX,15 ; カラーコード
45 0000006D CD10 INT 0x10 ; ビデオBIOS呼び出し
46 0000006F EBEE JMP putloop
47 fin:
48 00000071 F4 HLT ; 何かあるまでCPUを停止させる
49 00000072 EBFD JMP fin ; 無限ループ
50
51 msg:
52 00000074 0A0A DB 0x0a, 0x0a ; 改行を2つ
53 00000076 68656C6C6F6F732C20- DB "helloos, day2."
53 0000007F 646179322E
54 00000084 0A DB 0x0a ; 改行
55 00000085 00 DB 0
56
57 00000086 00<rept> TIMES 0x7dfe-($-$$)-0x7c00 DB 0 ; 0x7dfeまでを0x00で埋める命令
58
59 000001FE 55AA DB 0x55, 0xaa

続いて、Makefileの作成です。Makefileは環境のセットアップやソフトウェアのインストールなどをmakeコマンドで簡単に行えるように設定ができるファイルです。今回は本の字面だけ読んで、私の環境に合わせて同じ動作をするであろうものをオリジナルで作成しました。作成したMakefileは以下のものです。


Makefile

boot.bin : boot.asm Makefile

nasm boot.asm -o boot.bin -l boot.lst

halloos.img : boot.bin Makefile
sh ../../my_tools/mkimg.sh boot.bin halloos.img

img :
make -r halloos.img

asm :
make -r boot.bin

vdi : halloos.img
rm ./halloos.vdi -f
sh ../../my_tools/convert.sh halloos.img halloos.vdi

clean :
ls | grep -v -E 'Makefile|boot.asm|halloos.img|halloos.vdi' | xargs rm -f

src_clean :
ls | grep -v -E 'Makefile|boot.asm' | xargs rm -f


ディレクトリ構造などは私の開発環境(GitHubリポジトリで確認できる)に対応していますので、変な場所でmakeしようとしても相対パス等を利用している箇所でエラーになると思います。

また、私は検証にVirtualBoxを利用しているので、make vdiで簡単にVirtualBoxで起動可能な仮想ドライブイメージを作成できるようにしています。

DeepinScreenshot_select-area_20181108030208.png

ちゃんと利用できました。

今日はここまで、Chapter2も終了です。

明日はChapter3/3日目です。