[アセンブラ]
自作OS Advent Calendar 2016 11日目のために作成した記事です。
この記事はいろいろな用語が出てくるので、全て参考リンクを張っています。
OS自作入門でのアセンブラの使われ方
まず、なぜアセンブラが使われているのか?
- ブートローダー
- 正確には一次ブートローダーがBIOS
- 詳しくはWikipediaで Wikipedia - IBM PC互換機のブート手順
- OS自作入門の中で、このブートローダーに読み 込んでもらうファイルとして作成していくところのものを マスターブートレコード
- ipl10.nas という名前で作成するアセンブラが、このマスターブートレコード
- asmhead.nas は リアルモード と プロテクトモード の切り替えを行い、後述のbootpack.cに書かれたHariMain関数の内容 を実行する ために、そのメモリ上の場所を指し示しています]
- つまり、OS自作入門の初期に出てくるアセンブラは、PC/AT互換機共通のブートを実行するためのものなんですね
次にアプリケーションレベルの話
- libcを使わずシステムコールレベルの命令を実行
- 作りたてのOSにはシステムコールを呼び出せる関数やライブラリが存 在しません
- これはつまり標準出力に何か出したりメモリを確保したり、割り込み制御かけたりできないということで
- ということは、そういった低レイヤをまかなうためのライブラリをビルドさせるか、自作するか2択になるわけです
- OS自作入門では、bootpack.cにそういった命令のシンボル名を書き、その実装をnaskfunc.nasに書いてます
- 最終的に作ったオブジェクトファイルは作った関数のシンボル名と実装を含むことになり、処理の中で関数が呼ばれると、naskfunc.nasに書かれたアセンブラ処理を実行 する という流れになるわけです
アセンブラを実装していく方法
次に、アセンブラを実装していく方法です今回のターゲットは単にnaskの構文を理解して、構文に対応する機械語を吐ければそれで十分です
- サンプル
- 最初のMOVを[ニーモニック]()^1
- 単に命令(Instruction)と呼んでいいかもしれない
だいたい全部大文字
MOVAL,0x13; VGAグラフィックス、320x200x8bitカラー MOVAH,0x00 INT0x10
- naskはインテル記法なので
- 命令の次にまずDEST(送り元)が来る
- その次にSRC(送り先)が来る
- 送り元とか送り先の意味合いは、命令の種類によって変化します
これをもうちょい一般化すると
- MOV DEST, SRC ← 動作: DEST ← SRC
と書けそうです
準備する資料
大元の種本とすべきなのは インテルR アーキテクチャー・ソフトウェア・デベロッパーズ・マニュアル なんでしょうが、わたしはめんどくさいのでWEBサイトに載っている それ の書き下しを参考にしています。
これを参考にすれば、どういう命令を出したい時にどういう機械語を出せばよいか仕様がわかります
- インテルのデベロッパーズマニュアルへのリンク
- よく参考にしているIA32(x86)汎用命令一覧
16bit, 32bitモードで出力される機械語の仕様
オペコードから導出される機械語の見取り図
以下は、i386のCPU
(a) 16-bit instruction mode (リアルモード) _________ ___________ ____________ _________ | Opcode ||MOD-REG-R/M||Displacement||Immediate| | || || ||0-4 bytes| |1-2 bytes|| 0-1 bytes || || | | || || || | | || || || | --------- ----------- ------------ --------- (b) 32-bit instruction mode (プロテクトモード) ........ ........ _________ ___________ ......... _________ _________ :Address ::Operand :| Opcode ||MOD-REG-R/M|:Scaled :|Displace-||Immediate| :size ::size :| || |:Index :|ment ||0-4bytes | :0-1bytes::0-1bytes:|1-2 bytes|| 0-1 bytes |:0-1 bytes:| || | :Prefix :: :| || |: :| || | :67H ::66H :| || |: :| || | '''''''' '''''''' --------- ----------- ''''''''' --------- ---------
図の中の用語の説明をしておきます機能レベルの話をしだすと記事がまとまらないので、まず概要のみ書きます。残りは別の記事で。。。
16-bit/32-bit共通
(a) 16-bit instruction mode (リアルモード) _________ ___________ ____________ _________ | Opcode ||MOD-REG-R/M||Displacement||Immediate| | || || ||0-4 bytes| |1-2 bytes|| 0-1 bytes || || | | || || || | | || || || | --------- ----------- ------------ ---------
- Opcode (オペコード
- 例えば INT 0x13という命令であれば0xCD ibというオペコードがそれに対応する
- アセンブラの出力は 0xcd, 0x13 になる
- MOD-REG-R/M (ModR/M
- 命令自体のオペコードと命令の対象となるレジスタによって変化する1byteのデータ
- x86のCPUの命令が汚い原因の一つかも
- Displacement (Disp)
- 命令が間接アドレッシング・モード
- [EAX+4] とかそんな表記されたときの +4のこと]
- Immediate (即値
- 即値はそのままの数字のことです、大抵 0x が先頭につく
32-bit
(b) 32-bit instruction mode (プロテクトモード) ........ ........ _________ ___________ ......... _________ _________ :Address ::Operand :| Opcode ||MOD-REG-R/M|:Scaled :|Displace-||Immediate| :size ::size :| || |:Index :|ment ||0-4bytes | :0-1bytes::0-1bytes:|1-2 bytes|| 0-1 bytes |:0-1 bytes:| || | :Prefix :: :| || |: :| || | :67H ::66H :| || |: :| || | '''''''' '''''''' --------- ----------- ''''''''' --------- ---------
32bitモードの最初の2バイトはOverride prefixesと呼ばれる。なぜならそれらは常に存在するとは限らないからだ。 最初の1バイト目は命令に使われるオペランドのアド レス のサイズを変更する。 次の2バイト目はレジスタのサイズを変更する。
- Address size Prefix byte
- もし、CPUが16bit命令モードとして(プロテクトモードのみ)で動き、32bitレジスタが使われていればこれは無視される。もし、16bit命令が32bit命令モードで現れるのであれば、 16bitレジスタを選択するためこれが必要
- 逆に、リアルモードで32bitのアドレッシング・モードを使うときもこれが必要
- Operand size Prefix byte
- 16bit命令モードで動作中(リアルモードもしくはプロテクトモード)、32bitレジスタが使われていれば、Override prefixes(0x66)が命令の前に付加される
- Scaled Index byte (SIB)
- x86の機械語コード中にあり、実効アドレスを指定したメモリーアクセスをする際の情報を与えるためのバイト。i386の32ビット以降で追加された。ModR/Mではカバーできん部分を指し示す。
これでようやく実装のための準備終了です、次回はより具体的な話を書きたいところです。
[^1]: 不正なリンクです。