[アセンブラ] ARMの仕様を見てみる


[前回の記事（アセンブラに手を出してみる）](http://qiita.com/edo_m18/items/83c63cd69f119d0b9831)でx86、x64のアーキテクチャのアセンブラについて簡単にまとめました。
今回はARM向けの仕様を見てみようと思います。
*（ちなみに最新のiPhoneとかではarm64プロセッサを搭載しているので、ここでまとめたのとは若干異なると思います）*

**つらつらとデバッグ時に遭遇したものとかをメモしていくので随時更新です。**


## 汎用レジスタ

なにはともあれ、レジスタの状況を見ないとなにも始まらないのでまずはレジスタです。

ARMプロセッサは`r0`〜`r15`の16本、32bitレジスタを持っています。
このうち、`r15`は「プログラムカウンタ」で、実行中のメモリアドレスを保持します。
*（x86とかでは`IP`レジスタあたりが似たものでしょうか）*

`r14`はリンクレジスタで`BL`命令（サブルーチンコール）で分岐した場合の戻りアドレスを保持します。
*（x86では`bp`とかかな？（こっちの`bp`の認識も怪しいけど・・））*

x86と似たように、このレジスタも関数コール時にスタックに積まれるようです。（ひとつの戻りアドレスしか保持できないため）
（なお、通常は`r13`がスタック領域のレジスタの模様）

### 名称付き汎用レジスタ

ちなみに上記の役割があるレジスタはそれぞれ、以下のように呼ばれることもあるようです。（数字で表されるより分かりやすい）

| レジスタ| 別名 | _ | 説明 |
|:--------|:--------|:----|:----------|
| r0 | a1 | -  | 汎用レジスタ |
| r1 | a2 | - | 汎用レジスタ |
| r2 | a3 | - | 汎用レジスタ |
| r3 | a4 | - | 汎用レジスタ |
| r4 | v1 | - | 汎用レジスタ |
| r5 | v2 | - | 汎用レジスタ |
| r6 | v3 | - | 汎用レジスタ |
| r7 | v4 | wr | 汎用レジスタ |
| r8 | v5 | - | 汎用レジスタ |
| r9 | v6 | sb | 汎用レジスタ |
| r10 | v7 | sl | 汎用レジスタ |
| r11 | v8 | fp | 汎用レジスタ |
| r12 | ip | - | 汎用レジスタ |
| r13 | sp | - | 汎用レジスタ、スタックポインタ |
| r14 | lr | - | リンクレジスタ |
| r15 | pc | - | プログラムカウンタ |

`sp`などはそれぞれ名称の頭文字ですね。


### `W**`, `X**`

Xcodeでデバッグしていると見るこんな命令。

```arm
stp    x29, x30, [sp, #-16]!
```

`STP`命令は「レジスタペアストア」ということのよう。
[ドキュメント](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0802aj/STP_gen.html)


ドキュメントの一部を引用すると以下の感じ。

```
STP  Wt1, Wt2, [Xn|SP], #imm    ; 32 ビット汎用レジスタ、ポストインデクス
STP  Xt1, Xt2, [Xn|SP], #imm    ; 64 ビット汎用レジスタ、ポストインデクス

Xt1
転送される最初の汎用レジスタ（64 ビット）名を 0 ～ 31 の範囲内で指定します。
```



## コメント

「ARM」では`@`以降がコメントとして扱われます。


## 条件指定実行

ARMの仕様として、演算命令に対して条件指定することができるようです。
例えば以下。

```asm
SUBEQ a, b, c
```

`SUB`は減算命令ですね。続く`EQ`はEqualのEQです。
つまり、所定のフラグがEqualの場合に実行する、という意味になります。

### ステータスレジスタ

| bit | フラグ | 意味 | 備考 |
|:-----|:------|:------|:------|
| 31 | N | ネガティブ | ２の補数表現で負なら１ |
| 30 | Z | ゼロ | ゼロなら１ |
| 29 | C | キャリー／ボロー | 無符号加減算の桁あふれの時１ |
| 28 | V | オーバーフロー | 符号付加減算の桁あふれの時１ |
| 27 | Q | オーバーフロー | オーバーフロー、飽和(DSP)の時１ |


| 条件 (<cd>) | 意味 | フラグ条件 |
|:------------|:-----|:-----------|
| AL | 無条件に実行 | 無条件 | - |
| EQ | 等しい | Zセット | Z=1 |
| NE | 等しくない | Zクリア | Z=0 |
| MI | 負 | Nセット | N=1 |
| PL | 正かゼロ | Nクリア | N=0 |
| VS | オーバフロー | Vセット | V=1 |
| VC | オーバフローでない | Vクリア | V=0 |
| CS/HS | ≧ 大きいか等しい (符号無し) | Cセット | C=1 |
| CC/LO | ＜ 小さい (符号無し) | Cクリア | C=0 |
| HI | ＞ 大きい (符号無し) | CセットかつZクリア | C=1 AND Z=0 |
| LS | ≦ 小さいか等しい (符号無し) | CクリアまたはZセット | C=0 OR Z=1 |
| GE | ≧ 大きいか等しい (符号付き) | NとVが同じ | (N=1 AND V=1) OR (N=0 AND V=0) |
| LT | ＜ 小さい (符号付き) | NとVが異なる | (N=1 AND V=0) OR (N=0 AND V=1) |
| GT | ＞ 大きい (符号付き) | GEの条件かつZクリア | Z=0 AND N=V |
| LE | ≦ 小さいか等しい (符号付き) | LTの条件かつZクリア | Z=1 AND N!=V |


-------------------------------------------


## 演算命令

`add`

```asm
add r1, r2, r3 @ r1 = r2 + r3
```

`add`には3つのレジスタが指定できます。（3番目は定数なども指定できます）

ここで、`r1`はディスティネーションレジスタで、最終的な演算結果が代入されます。
`r2`は第1ソースオペランドレジスタ、`r3`は第2オペランドと呼びます。
前述の通り、`r3`は「レジスタ」、「メモリアドレス」、「定数」を指定することができます。

また以下のように、シフト演算も含めて指定することができるようです。

```asm
add r1, r2, r3 LSL #20 @ r1 = r2 + (r3 << 20)
```

シフト演算の命令は以下のようになります。

| 命令 | 意味 |
|:----|:----------------------------------|
| LSL | (Logical Shift Left) 左シフト |
| LSR | (Logical Shift Right) 右シフト |
| ASR | (Arithmetic Shift Right) 算術右シフト(符号を保存) |
| ROR | (Rotate Right) 右ローテイト |
| RRX | (Rotate Right with extend) 右ローテイト(含キャリーフラグ) |


ループで10を加算するプログラム例

```asm
	MOV r0, #0 @ r0 = 0
Next: BL Inc   @ 戻りアドレスをLRに入れ、Incに分岐
	CMP r0, #10 @ r0と10を比較
	BNE Next	@ r0が10でなければNextへ
	B End		@ 無条件にEndへ分岐
Inc: ADD r0, r0, #1 @ r0 = r0 + 1
	MOV PC, LR @ LRをPCに代入（つまりリターン）
End:
```


## ロード／ストア命令

レジスタからメモリへデータを転送することをストア、メモリからレジスタへデータを転送することをロード、と呼びます。
（ちなみにレジスタからレジスタへの移動をムーブ（MOV）と呼びます）

それぞれの命令は以下のようになります。

| 動作 | 命令 |
|:----------|:--------|
| レジスタ → レジスタ | MOV |
| メモリ→ レジスタ | LDR |
| レジスタ→メモリ | STR |

### プログラム例

[こちらの記事](http://www.mztn.org/slasm/arm07.html)から引用させてもらいます。

```asm:arm
#-------------------------------------
# プログラム例 list01.s
#-------------------------------------

#-------------------------------------
# text セクション
#-------------------------------------
.text
       .align  2
       .global _start           @ 必須

/* 定数に名前をつける */

sys_write = 0x900004            @ write システムコール
sys_exit  = 0x900001            @ exit システムコール

/* ここからプログラムを記述 */

_start: /* 常にこのラベルから実行開始 */


        mov     r3, #5          @ 5回繰り返す
    1:  ldr     r1, msg         @ 文字列先頭アドレス
        mov     r0, #1          @ 標準出力(fd=1)を指定
        ldr     r2, msg_sz      @ 出力データの長さ(バイト)
        swi     #sys_write
        subs    r3, r3, #1      @ カウンタを更新
        bne     1b              @ 後方のローカルラベルへ

        /* プログラム終了時に実行する */

        mov     r0, #0          @ 終了コードを0
        swi     #sys_exit       @ プログラム終了

        /* 書き換え不要な定数は text セクションに置く */

msg:    .long   msg0            @ 文字列格納アドレス
msg_sz: .long   msg_sz0         @ 文字列の長さ格納アドレス

#-------------------------------------
# data セクション
#-------------------------------------
.data
        .align  2
        /* ここは初期化が必要なデータを置く *
         * プログラムサイズに影響する       */

msg0:   .asciz  "hello, world\n"

        .align  2               @ 4バイト境界に設定
msg_sz0 = . - msg0              @ 文字列の長さを計算

data1:  .hword  12345

#-------------------------------------
# bss セクション
#-------------------------------------
.bss
        .align  2
        /* ここは初期化が不要なデータを置く *
         * プログラムサイズには影響しない   */

data:   .long   0               @ この例では使っていない
#-------------------------------------

```

どうやら最終行は空行が必要なようです。（ないとエラー）

ざっと見てみると、だいぶx86のアセンブラと似ている感じです。
`.text`セクションがいわゆるプログラムコード、`.data`セクションが変数などのデータですね。

なお、`.data`は、

>C言語では、「0以外の初期値を持つ大域変数」「0以外の初期値を持つ静的局所変数」がここに置かれます。

ということで、初期値を持つものが置かれる領域のようです。
一方、初期値を持たないものは`.bss`セクションに置かれます。

また、`.rodata`という「定数」を置くセクションもあるようです。

```asm
msg0: .asciz "hello, world\n"
```

このあたりが文字列定数の宣言ですね。
続く以下のものはアドレスから文字列長を計算している？

```asm
msg_sz0 = . - msg0
```

`.`は現在のアドレスで、そこから`msg0`分引くと差分は文字列長、ということだろうか？
x86のところでも似たような記述がありました。

こんな感じの。

```asm:x86
.len: equ $ - msg
```

`$`が現在のアドレスを表し、そこから`msg`のアドレスを引く、という計算です。