https://github.com/kubosho/my-os/tree/master/day1
アセンブリとバイナリエディタを初めて体験した。 はじめにバイナリエディタで16進数をひたすら入力して、それをimgファイルとして保存した。 その後アセンブリで書いたものをnasmでimgファイルに変換して、それをバイナリエディタで見てみた。
結果2つのファイルは多少違いこそあれど、QEMUなどの仮想マシン上にマウントして起動したらHello, worldと表示するだけのOSができた。
これがなかなか面白い。動作確認自体はqemuコマンドが brew install qemu
としてもqemuコマンドがnot foundになってしまい、確認できなかったが…
しかし DB
を展開すると data byte
となるのがちょっと面食らってしまった。
DB
と言えば data base
の略という世界から始まった身だからだ…
RESB
が reserve byte
の略というのは分かる。
なぜここまで省略するのだろうと思ったが、アセンブリ言語ができた年代を考えるとむしろ理解できるギリギリまでキーワードを削ったのかなと予測できる。
DW
と DD
の2つの命令に関してもそれぞれ data word
と data double-word
の略で data word
は16ビット(2バイト)を表すもの。
data double-word
は32ビット(4バイト)を表すもの、らしい。
ブートセクタはフロッピーディスクの最初のセクタを表すとのこと。検索してみたらHDDもそう呼ぶらしい。
フロッピーディスクは512バイトずつ読み書きしていて、その512バイトを1セクタと呼ぶとのこと。
またブートセクタにはパーティションブートレコード(PBR)とマスターブートレコード(MBR)の2種類がある(Wikipedia調べ)。
PCが起動したとき、ディスクの最初のセクタつまりブートセクタを読んで、最後の2バイト(16ビット)が 55 AA
であれば、セクタの先頭からプログラムを実行するとのこと。
ブートセクタには IPL(initial program loader)
と呼ばれる、ブートセクタからOS本体を読み込むプログラムが組み込まれているとのこと。
そしてブートという言葉は bootstrap
の略で「自力で成し遂げる」という意味があるらしい。
ディスクにOSが入っているが、そのOSを読み込むプログラム(IPL)も一緒にディスクに入っているということだ。
ちなみにブートストラップと検索するとWebフレームワークのほうが多く引っかかってしまい、本当に邪魔だ。
https://github.com/kubosho/my-os/tree/master/day2
新たにORG命令、JMP命令、MOV命令、ラベル宣言を学んだ。
ORG命令は origin
の略で、開始点という意味で使うということだ。
今回はメモリの 0x7c00
に読み込まれるということを示している。この 0x7c00
にも理由があるようだ。
JMP命令はなんとなく想像できるが、jump
の略で、C言語でいう「goto文」にあたる。
ラベルはJMP命令の飛び先の指定に使えるということで、さっそく JMP entry
というラベルへjumpしている。
MOV命令は代入文とあるが、http://doomo.main.jp/x86asm/2_1.htm を見ると「データの複写」とある。
MOV命令は move
の略だが、moveという言葉が示すようにデータの移動が起こるわけではなく、コピーされるだけらしい。
だったら CP
命令のほうが良かったのでは…
とりあえず文句は置いておいて MOV AX,0
とすることで AX
に対し 0
が入る。
このAXというのは、CPU内にあるレジスタという記憶回路を示し、x86アーキテクチャだと次の8つのレジスタを備えているとのこと。
https://ja.wikibooks.org/wiki/X86%E3%82%A2%E3%82%BB%E3%83%B3%E3%83%96%E3%83%A9/x86%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3
- AX: accumulator(算術演算操作の結果を格納するために使われる)
- CX: counter(シフトローテート命令とループ命令に使われる)
- DX: data(算術演算操作とI/O操作に使われる)
- BX: base(セグメントモードでのDSに格納されたデータを指し示すために使われる)
- SP: stack pointer(スタックのトップを指し示すために使われる)
- BP: base pointer(スタックのベースを指し示すために使われる)
- SI: source index(ストリーム操作でのソースへのポインタとして使われる)
- DI: destination index(ストリーム操作でのデスティネーションへのポインタとして使われる)
これら全てのレジスタは16bitで16桁の2進数を記憶できるとのこと。正直あまりよく分かってない。
余談となるが SI
と DI
を見てビルドツールのGulpを思い出した。
演算にAXを使った場合、CXを使うのに比べて機械語レベルで簡潔になる。 そのCXについては回数を数えるのに使うと便利で、BXはメモリの番地計算の基点に使うと便利。
またCPUには8ビットのレジスタも8個あり、それぞれ次のようになる。
- AL: アキュムレータロウ
- CL: カウンタロウ
- DL: データロウ
- BL: ベースロウ
- AH: アキュムレータハイ
- CH: カウンタハイ
- DH: データハイ
- BH: ベースハイ
LowとHighの違いはAXレジスタの0ビットから7ビットまでの部分をALといい、8ビットから15ビットの部分をAHという。
なお、32bitの場合は各レジスタの頭に E
が付く。
たとえばEAXは下位16bitがまさにAXそのものということだ。ただ上位16bitには名前がなく、レジスタ番号もない。
そのため、上位の16bitを使いたい場合は、下位16bitのほうにずらす命令を使って下位にずらしてからでないと使えない。
またMOV命令は MOV SI,msg
のように msg
ラベルを SI
にコピーするということだが、実際は msg
はメモリ空間の番地を分かりやすくラベリングしただけだ。
なので実際は SI
レジスタに msg
の値が入っている(たとえば 0x7c50
という具合)。
putloop
ラベル内の MOV AL, [SI]
は []
がメモリを意味している。メモリはまさにあのメモリだ。
メモリはCPUからすると外部記憶装置で、CPUとメモリがデータのやりとりをしている。
CPUからするとメモリはレジスタと比べて遠い場所にあるので、レジスタをそのまま使うよりかは遅い。
[]
はデータの転送先や転送元としてメモリを使うと指定することになる。
[]
でメモリの番地を指定するときにレジスタも使える。ただし使えるのは BX
, BP
, SI
, DI
のみとなる。
結論としては MOV AL, [SI]
は「メモリのSI番地の1バイトの内容をALに読み込む」というコードになる。
ADD命令は足し算をする命令になる。ADD SI, 1
は SI = SI + 1
ということだ。
CMP命令は「compare」からきており、比較命令となる。
JE命令は条件ジャンプ命令で、比較命令の結果によってジャンプしたりしなかったりする。
JE命令は「jump if equal」の略となる。
なので次のコードはその次のC言語によるコードと同じことだといえる。 なおfinはアセンブリの仕様にあるわけではなく、ただのラベルだ。
CMP AL, 0
JE fin
if (AL == 0) { goto fin; }
BIOSは「basic input output system」の略で、OS開発者のためにある関数の集まりだ。
INT 0x10
という割り込み命令で 0x10
番にある関数を呼び出している。
これはビデオカード制御に関する関数らしい。