X86 MOV レジスタ⇔メモリの機械語

## 前回の記事
レジスタ間のやりとりは前回の記事に纏めてあります。

[x86 MOV命令 MOV AL,BLのようなレジスタ間移動の機械語はどう生成される？]
(https://qiita.com/earthen94/items/ffa63859fbfd51d36ebf)

## オペコード
今回はレジスタ⇔メモリ間の移動に絞って書きます。
| 命令形式           | オペコード | 説明                                |
|--------------------|------------|-------------------------------------|
| `MOV r/m8, r8`     | `88 /r`    | 8ビットレジスタ → r/m8へ転送       |
| `MOV r/m16, r16`   | `89 /r`    | 16ビットレジスタ → r/m16へ転送     |
| `MOV r8, r/m8`     | `8A /r`    | r/m8 → 8ビットレジスタへ転送       |
| `MOV r16, r/m16`   | `8B /r`    | r/m16 → 16ビットレジスタへ転送     |

`r/m`はレジスタ、もしくはメモリ。`r`はレジスタを設定する。後ろの数字はビット数です。

レジスタ同士の移動だと昨日説明したように`r/m`と`r`にどちらのレジスタを設定するかによってオペコードが２種類存在しましたが、レジスタ⇔メモリ間の移動だとメモリはr/mに入れるしかないため、オペコードは一意に定まります。

## ModR/Mの構造
| 項目 | ビット数 | 説明              |
|------------|-----------|-------------------|
| Mod        | 2bit      | アドレッシングモード |
| Reg        | 3bit      | レジスタ |
| R/M        | 3bit      | レジスタ又はメモリアドレス指定 |

ModR/Mはオペランドの後につきます。
※実際の例は後から載せます。

## Modの中身
| Mod値 | ビット | 意味                       |
|--------|--------|----------------------------|
| `00`   | `00`   | オフセット無し |
| `01`   | `01`   | 8ビット符号付きオフセット付き |
| `10`   | `10`   | 16ビット符号付きオフセット付き |
| `11`   | `11`   | レジスタ間の直接転送（メモリアクセスなし） |

## 3bit値（reg または r/m）レジスタ早見表
| ビット値 | レジスタ（8bit） | レジスタ（16bit） |
|----------|------------------|-------------------|
| 000      | AL               | AX                |
| 001      | CL               | CX                |
| 010      | DL               | DX                |
| 011      | BL               | BX                |
| 100      | AH               | SP                |
| 101      | CH               | BP                |
| 110      | DH               | SI                |
| 111      | BH               | DI                |

## 3bit値（r/m　Mod ≠ 11 の場合、メモリアクセス）レジスタ早見表
| ビット値 | アドレッシングモード         | 意味                              |
|----------|------------------------------|-----------------------------------|
| 000      | `[BX + SI]`                  | 基底 + インデックス               |
| 001      | `[BX + DI]`                  | 基底 + インデックス               |
| 010      | `[BP + SI]`                  | 基底（BP）+ インデックス          |
| 011      | `[BP + DI]`                  | 基底（BP）+ インデックス          |
| 100      | `[SI]`                        | 単独インデックス                  |
| 101      | `[DI]`                        | 単独インデックス                  |
| 110      | `[direct address]`          | 16bit直値での絶対アドレス        |
| 111      | `[BX]`                        | 単独ベース                        |

オフセットが合ってなくても3bit値は同じ。
例えば`[BX + SI]`と`[BX + SI + 100]`では指定する3bit値は変わらない。

## 手動アセンブルの例
上の表を見ただけでは何のことか理解できないと思いますが、実際に上の表を参照しながら手動でアセンブリから機械語へ変換していくと理解が深まります。

### MOV AX,[BX]
オペコード = 8B `r16`←`r/m16`
Mod = 00 `メモリ`⇔`レジスタ間`、オフセット無し
reg = 000 `AX`の3bit値
r/m = 111    `[BX]`の3bit値

00 000 111 = 0x07
よって機械語は**8B 07**

### MOV [BX],AX
オペコード = 89 `r/m16`←`r16`
Mod = 00 `メモリ`⇔`レジスタ間`、オフセット無し
reg = 000 `AX`の3bit値
r/m = 111    `[BX]`の3bit値

00 000 111 = 0x07
よって機械語は**89 07**

### MOV [DI + 0x1234],CX
オペコード = 89 `r/m16`←`r16`
Mod = 10 `メモリ`⇔`レジスタ間`、オフセット16bit
reg = 001 `CX`の3bit値
r/m = 101 [DI + disp16]

10 001 101=0x8D
よって**89 8D 34 12**
※1234はオフセット。リトルエンディアンに合わせて逆にする。

### MOV DX,[SI]
オペコード = 8B `r16`←`r/m16`
Mod = 00 `メモリ`⇔`レジスタ間`、オフセット無し
reg = 010 `DX`の3bit値
r/m = 100 `[SI]`の3bit値

10 010 100 = 0x14
よって**8B 14**

### MOV [BX + 0x20],AL
オペコード = 88 `r/m8`←`r8`
Mod = 01 `メモリ`⇔`レジスタ間`、オフセット8bit
reg = 000 `AL`の3bit値
r/m = 111 `[BX]`の3bit値
オフセット = 20

01 000 111 = 0x47
よって**88 47 20**

### MOV CL,[DI + 0x30]
オペコード = 8A `r8`←`r/m8`
Mod = 01 `メモリ`⇔`レジスタ間`、オフセット8bit
reg = 001 `CL`の3bit値
r/m = 101 `[DI]`の3bit値
オフセット = 30

01 001 101 = 0x4D
よって**8A 4D 30**

### MOV [0x1234],AX
オペコード = 89 `r/m16`←`r16`
Mod = 00 `メモリ`⇔`レジスタ間`、オフセット無し
reg = 000 `AX`の3bit値
r/m = 110 `[direct address]`の3bit

00 000 110 = 0x06
よって**89 06 34 12**

### MOV DI,[BX + SI + 0x40]
オペコード = 8B `r16`←`r/m16`
Mod = 10 `メモリ`⇔`レジスタ間`、オフセット16bit
reg = 111 `DI`の3bit値
r/m = 000 `[BX + SI]`の3bit値

10 111 000 = 0xB8
よって**8B B8 40 00**
※40を0埋めすると0040となる。リトルエンディアンの形にすると00 04


## 検証
上で変換した機械語を逆アセンブル(機械語→アセンブリ言語への変換)し、正しいことを検証します。
先ずは対応表に纏めました。
| No. | アセンブリ命令                | 機械語（16進）     |
|-----|-----------------------------|-------------------|
| 1   | `MOV AX, [BX]`              | `8B 07`           |
| 2   | `MOV [BX], AX`              | `89 07`           |
| 3   | `MOV [DI + 0x1234], CX`     | `89 8D 34 12`     |
| 4   | `MOV DX, [SI]`              | `8B 14`           |
| 5   | `MOV [BX + 0x20], AL`       | `88 47 20`        |
| 6   | `MOV CL, [DI + 0x30]`       | `8A 4D 30`        |
| 7   | `MOV [0x1234], AX`          | `89 06 34 12`     |
| 8   | `MOV DI, [BX + SI + 0x40]`  | `8B B8 40 00`     |

MSDOS実機debugのe命令を用いてバイナリを打ち込みu命令で逆アセンブルした結果は以下の通りです。
機械語へ正しく変換できていることが証明できました。
![9ebd80ddc9ced9e1e93b5bbfd97c85c0.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4054974/b2435dac-129a-4634-a0eb-95350a95bf33.jpeg)

以下のように地道に入力しました。
```
e 100  8B
e 101  07
e 102  89
e 103  07
e 104  89
e 105  8D
e 106  34
e 107  12
e 108  8B
e 109  14
e 10A  88
e 10B  47
e 10C  20
e 10D  8A
e 10E  4D
e 10F  30
e 110  89
e 111  06
e 112  34
e 113  12
e 114  8B
e 115  B8
e 116  40
e 117  00
u 100
```

## 最後に
バイナリで書かれた機械語は人の読めるものではないと思っていましたが、仕組みを知ると意外と単純だと気付きました。
このやり方に慣れると、手動で逆アセンブルもできるようになります。
今後別の記事で紹介しようと思います。