IA32（x86）汎用命令対応のアセンブラ実装方法(1)

[自作OS Advent Calendar 2016](http://www.adventar.org/calendars/1666) 11日目の @Hiroyuki-Nagata です．
前の人は 自分でした，次の人も たぶん自分です．

この記事はいろいろな用語が出てくるので、全て参考リンクを張っています。

# OS自作入門でのアセンブラの使われ方

まず、なぜアセンブラが使われているのか？

* [ブートローダー](https://ja.wikipedia.org/wiki/%E3%83%96%E3%83%BC%E3%83%88#.E3.83.96.E3.83.BC.E3.83.88.E3.83.AD.E3.83.BC.E3.83.80)
  * 正確には一次ブートローダーが[BIOS](https://ja.wikipedia.org/wiki/Basic_Input/Output_System)なので、２次ブートローダーと言うべきでしょうか
  * 詳しくはWikipediaで [Wikipedia - IBM PC互換機のブート手順](https://ja.wikipedia.org/wiki/%E3%83%96%E3%83%BC%E3%83%88#IBM_PC.E4.BA.92.E6.8F.9B.E6.A9.9F.E3.81.AE.E3.83.96.E3.83.BC.E3.83.88.E6.89.8B.E9.A0.86)
  * OS自作入門の中で、この[ブートローダー](https://ja.wikipedia.org/wiki/%E3%83%96%E3%83%BC%E3%83%88#.E3.83.96.E3.83.BC.E3.83.88.E3.83.AD.E3.83.BC.E3.83.80)に読み込んでもらうファイルとして作成していくところのものを [マスターブートレコード](https://ja.wikipedia.org/wiki/%E3%83%9E%E3%82%B9%E3%82%BF%E3%83%BC%E3%83%96%E3%83%BC%E3%83%88%E3%83%AC%E3%82%B3%E3%83%BC%E3%83%89) と呼ぶ（←Linuxユーザーならよく聞くやつですね）
  * `ipl10.nas` という名前で作成するアセンブラが、この[マスターブートレコード](https://ja.wikipedia.org/wiki/%E3%83%9E%E3%82%B9%E3%82%BF%E3%83%BC%E3%83%96%E3%83%BC%E3%83%88%E3%83%AC%E3%82%B3%E3%83%BC%E3%83%89)
  * `asmhead.nas` は [リアルモード](https://ja.wikipedia.org/wiki/%E3%83%AA%E3%82%A2%E3%83%AB%E3%83%A2%E3%83%BC%E3%83%89) と [プロテクトモード](https://ja.wikipedia.org/wiki/%E3%83%97%E3%83%AD%E3%83%86%E3%82%AF%E3%83%88%E3%83%A2%E3%83%BC%E3%83%89) の切り替えを行い、後述の`bootpack.c`に書かれた`HariMain`関数の内容を実行するために、そのメモリ上の場所を指し示しています
  * つまり、OS自作入門の初期に出てくるアセンブラは、PC/AT互換機共通のブートを実行するためのものなんですね

次にアプリケーションレベルの話

* libcを使わずシステムコールレベルの命令を実行
    * 作りたてのOSには[システムコール](https://ja.wikipedia.org/wiki/%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%82%B3%E3%83%BC%E3%83%AB)を呼び出せる関数やライブラリが存在しません
    * これはつまり標準出力に何か出したりメモリを確保したり、割り込み制御かけたりできないということで
    * ということは、そういった低レイヤをまかなうためのライブラリをビルドさせるか、自作するか２択になるわけです
    * OS自作入門では、`bootpack.c`にそういった命令のシンボル名を書き、その実装を`naskfunc.nas`に書いてます
    * 最終的に作ったオブジェクトファイルは作った関数のシンボル名と実装を含むことになり、処理の中で関数が呼ばれると、`naskfunc.nas`に書かれたアセンブラ処理を実行するという流れになるわけです

# アセンブラを実装していく方法

次に、アセンブラを実装していく方法です
今回のターゲットは単に`nask`の構文を理解して、構文に対応する[機械語](https://ja.wikipedia.org/wiki/%E6%A9%9F%E6%A2%B0%E8%AA%9E)を吐ければそれで十分です

* サンプル
    * 最初の`MOV`を[ニーモニック]()[^1]と呼ぶ
    * 単に命令(Instruction)と呼んでいいかもしれない

だいたい全部大文字

```
		MOV		AL,0x13			; VGAグラフィックス、320x200x8bitカラー
		MOV		AH,0x00
		INT		0x10
```

* `nask`はインテル記法なので
    * 命令の次にまず`DEST(送り先)`が来る
    * その次に`SRC(送り元)`が来る
    * 送り元とか送り先の意味合いは、命令の種類によって変化します

これをもうちょい一般化すると

* `MOV DEST, SRC` ← 動作： `DEST ← SRC`

と書けそうです

## 準備する資料

大元の種本とすべきなのは `インテル® アーキテクチャー・ソフトウェア・デベロッパーズ・マニュアル` なんでしょうが、わたしはめんどくさいのでWEBサイトに載っているそれの書き下しを参考にしています。

これを参考にすれば、どういう命令を出したい時にどういう機械語を出せばよいか仕様がわかります

* インテルのデベロッパーズマニュアルへのリンク
    * [デベロッパーズ・マニュアル、上巻: 基本アーキテクチャー](http://www.intel.co.jp/content/dam/www/public/ijkk/jp/ja/documents/developer/IA32_Arh_Dev_Man_Vol1_Online_i.pdf)
    * [デベロッパーズ・マニュアル、中巻 A: 命令セット・リファレンス A-M](http://www.intel.co.jp/content/dam/www/public/ijkk/jp/ja/documents/developer/IA32_Arh_Dev_Man_Vol2A_i.pdf)
    * [デベロッパーズ・マニュアル、中巻 B: 命令セット・リファレンス N-Z](http://www.intel.co.jp/content/dam/www/public/ijkk/jp/ja/documents/developer/IA32_Arh_Dev_Man_Vol2B_i.pdf)
    * [デベロッパーズ・マニュアル、下巻: システム・プログラミング・ガイド](http://www.intel.co.jp/content/dam/www/public/ijkk/jp/ja/documents/developer/IA32_Arh_Dev_Man_Vol3_i.pdf)

* よく参考にしているIA32（x86）汎用命令一覧
    * [IA32（x86）汎用命令一覧](http://softwaretechnique.jp/OS_Development/Tips/IA32_instructions.html)

* x86の命令を表にしたものがあるようです、これから実装される方はこれを参考にしたら簡単かも
    * [ハンド (逆) アセンブルを補助するための PDF リスト](http://dl.dropbox.com/u/2476414/TechResources/x86_opcodemap_1_b4.pdf)
    * [ハンド (逆) アセンブルのための x86 ニーモニックの覚え方](http://d.hatena.ne.jp/a4lg/20120225/1330180431)


## 16bit, 32bitモードで出力される機械語の仕様

### オペコードから導出される機械語の見取り図

以下は、[i386のCPU](http://e-words.jp/w/i386.html)を動作させるときの機械語（バイナリ）の並びです。

```
(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 ([オペコード](https://ja.wikipedia.org/wiki/%E3%82%AA%E3%83%9A%E3%82%B3%E3%83%BC%E3%83%89))
    * 例えば `INT 0x13`という命令であれば`0xCD ib`というオペコードがそれに対応する
    * アセンブラの出力は `0xcd, 0x13` になる
* MOD-REG-R/M ([ModR/M](http://www.wdic.org/w/SCI/ModR/M))
    * 命令自体のオペコードと命令の対象となるレジスタによって変化する1byteのデータ
    * x86のCPUの命令が汚い原因の一つかも
* Displacement (Disp)
    * 命令が間接[アドレッシング・モード](https://ja.wikipedia.org/wiki/%E3%82%A2%E3%83%89%E3%83%AC%E3%83%83%E3%82%B7%E3%83%B3%E3%82%B0%E3%83%A2%E3%83%BC%E3%83%89)を使用している際に使われるデータ
    * `[EAX+4]` とかそんな表記されたときの `+4`のこと

* Immediate ([即値](http://e-words.jp/w/%E5%8D%B3%E5%80%A4.html))
    * 即値はそのままの数字のことです、大抵 `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モードの最初の２バイトは`Override prefixes`と呼ばれる。なぜならそれらは常に存在するとは限らないからだ。 最初の１バイト目は命令に使われるオペランドのアドレスのサイズを変更する。 次の２バイト目はレジスタのサイズを変更する。

* `Address size Prefix byte`
    * もし、CPUが16bit命令モードとして（プロテクトモードのみ）で動き、32bitレジスタが使われていればこれは無視される。もし、16bit命令が32bit命令モードで現れるのであれば、 16bitレジスタを選択するためこれが必要
    * 逆に、リアルモードで32bitのアドレッシング・モードを使うときもこれが必要

* `Operand size Prefix byte`
    * 16bit命令モードで動作中（リアルモードもしくはプロテクトモード）、32bitレジスタが使われていれば、Override prefixes(0x66)が命令の前に付加される

* `Scaled Index byte` ([SIB](http://www.wdic.org/w/SCI/SIB%E3%83%90%E3%82%A4%E3%83%88))
    * x86の機械語コード中にあり、実効アドレスを指定したメモリーアクセスをする際の情報を与えるためのバイト。i386の32ビット以降で追加された。ModR/Mではカバーできん部分を指し示す。


これでようやく実装のための準備終了です、次回はより具体的な話を書きたいところです。

[^1]: [Difference between: Opcode, byte code, mnemonics, machine code and assembly](http://stackoverflow.com/questions/17638888/difference-between-opcode-byte-code-mnemonics-machine-code-and-assembly) ... MNEMONIC: English word MNEMONIC means "A device such as a pattern of letters, ideas, or associations that assists in remembering something.". So, its usually used by assembly language programmers to remember the "OPERATIONS" a machine can do, like "ADD" and "MUL" and "MOV" etc. This is assembler specific.