ラズパイでARM入門（5. 分岐命令）

前回：[4. GNU デバッガ (GDB)](https://qiita.com/GoodOldLandLine/items/4dc4309b49e677d4740c) 次回：[6. 制御構造（if文 while文）](https://qiita.com/GoodOldLandLine/items/bea06ca1f380c26d482f)
[目次（記事一覧）](https://qiita.com/GoodOldLandLine/items/bb17122fee602d1aedb9)

※この記事は[Roger Ferrer Ibáñez](https://rofi.roger-ferrer.org)さんのブログ[ARM assembler in Raspberry Pi – Chapter 5](https://thinkingeek.com/2013/01/19/arm-assembler-raspberry-pi-chapter-5/)の翻訳です。

# 分岐
これまでのサンプルコードは命令を順々に実行していました。ARMプロセッサーが順々に実行することしかできないのなら、作れるものは少なくなってしまいます。つまり、これまでに学んだことだけでは条件に従って制御フローを切り替えることができないのです。分岐命令はそれを実現するためにあります。

# 特殊レジスタ
第2章では、Raspberry PiのARMプロセッサーに16個の汎用レジスタがあり、一部はプログラムで特別な役割を担っていることを学びました。どのレジスタが特別なのかまでは当時の段階を踏まえてあえて説明していませんでした。

しかしそろそろ`r15`レジスタについて説明するべき段階が来ました。このレジスタは非常に特別なので`pc`という別名があります。混乱を招くため`r15`と表記することはほとんどありません（ARMアーキテクチャ的には正しいですが）。ここからはこの記事においても`pc`と書くようにします。

`pc`は *program couter*（プログラムカウンター）の略です。この呼称の起源はコンピューターの黎明期にあり、今日ではほとんど意味を成しません。一般に`pc`レジスタは次に実行することになる命令のアドレスを格納しています（`pc`は386やx86_64などでは`ip`：instruction pointer、命令ポインタとも呼ばれます。）。

ARMプロセッサーが命令を実行する時、実行の最後で行われることは2通りあります。命令が`pc`を変更しなかった場合（ほとんどの命令がそう）、`pc`は4加算されます（`add pc, pc, #4`とするみたいに）。4であるのは、ARMの命令が32ビットであり命令が4バイトずつ並んでいるからです。命令が`pc`を変更した場合、`pc`は変更された状態そのままで命令の実行を終えます。

プロセッサは命令を完全に実行すると、次に実行する命令のアドレスとして`pc`の値を使用します。よって、`pc`を変更しない命令の後にはメモリ内で連続する次の命令が続きます（`pc`が自動的に4加算されるから）。このように命令の一つが実行されその後にメモリ上の次の命令が実行されるということを命令の**暗黙的順次実行**と呼びます。しかし、命令が（単に4を加算する以外の方法で）`pc`を変更することでプログラムの別の命令を実行できるようになります。この`pc`の値を変更するプロセスを**分岐**と呼びます。ARMではこの操作を**分岐命令**を使って行います。

# 無条件分岐命令
無条件に分岐させる命令の書き方は`b ラベル`（branch: 分岐）です。次のプログラムを見てください。

```
/* -- branch01.s */
.text
.global main
main:
    mov r0, #2 /* r0 ← 2 */
    b end      /* branch to 'end' */
    mov r0, #3 /* r0 ← 3 */
end:
    bx lr
```

このプログラムを実行すると、エラーコードが2になることがわかります。

```
$ ./branch01 ; echo $?
2
```

ここでは`b end`命令によって`end`ラベルのある`bx lr`への分岐が起こりました。一方`mov r0`命令はまったく実行されていません（プロセッサーがその命令に到達することはありえません）。

この段階では、無条件分岐命令`b`はそれほど役立たないように見えますが、ある状況、特に条件分岐命令も使用する場合では不可欠なものとなります。条件分岐について話したいところですがその前に条件について話す必要があります。

# 条件分岐命令
ただなんとなく分岐できるだけではあまり役立ちません。*条件を満たした*ときに分岐できる方がはるかに便利です。そのため、プロセッサーはいくつかの条件を評価できる必要があります。

話を続ける前に、`cpsr`（Current Program Status Register）という別のレジスタについて明らかにする必要があります。このレジスタは少し特殊で、直接的に値を変更することに関してはこの章の範囲を超えます。とはいえ、このレジスタが保持する値は、命令を実行するときに読み取られたり変更されたりします。その値は、4つの条件コードフラグを含み、それぞれN (**n**egative)、Z (**z**ero)、C (**c**arry)、V (o**v**erflow)フラグと呼ばれます。この4つの条件コードフラグは通常、分岐命令によって読み取られます。また、これらの条件コードフラグは、算術命令や特殊なテスト命令、比較命令によって必要に応じて更新できます。

4つの条件コードフラグの意味はおおよそ次のとおりです。

- N：　命令の結果が負の数の場合、有効になる。その他の場合は無効になる。
- Z：　命令の結果がゼロの場合、有効になる。その他の場合、無効になる。
- C：　命令の結果が完全な表現のために33番目のビットを必要とした場合に有効なる。例えば加算の結果が32ビット整数の範囲からあふれた場合。減算は特殊で、繰り下がりなしの場合に有効になり、その他の場合に無効になる。つまり、大きい数字から小さい数字を引くと有効になるが、逆にして引くと無効になる。
- V：　命令の結果が32ビットの2の補数で表すことができない値である場合、有効になる。

（※訳注　Cフラグ中の「繰り下がり」は存在しない33番目の桁からの繰り下がりという意味です。また、同じ数字で引き算した場合にもCフラグは有効になります。）

これで、条件分岐するために必要な全要素がそろいました。では分岐をする前に最初に2値の比較をしましょう。比較は`cmp`命令で行います。

```
cmp r1, r2 /* updates cpsr doing "r1 - r2", but r1 and r2 are not modified */
           /* 日本語訳："r1 - r2"をしてcpsrレジスタを更新する。r1, r2レジスタ自体は変化しない。*/
```

この命令は、1つ目のレジスタの値から2つ目のレジスタの値を引きます。以下はこのコードで起こることの例です。

- `r2`が`r1`より大きい場合、Nフラグが有効化する。なぜなら、`r1-r2`が負になるからである。
- `r1`と`r2`が等しい場合、Zフラグが有効化する。なぜなら、`r1-r2`がゼロになるからである。
- `r1`が1、`r2`が0なら、`r1-r2`に繰り下がりは起きない。よってCフラグが有効化する。逆に、`r1`が0、`r2`が1なら繰り下がりが起こり、Cフラグが無効化する。
- `r1`が2147483647（$ = 2^{31}-1 $ つまり32ビットの2の補数における最大の正の整数）であり、`r2`が1なら、`r1-r2`は2147483648（$ = 2^{31} $）となるが、そのような数値は32ビットの2の補数で表現できないため、Vフラグが有効化する。

これらのフラグを利用してよく使う条件を表すことができます（以下の表を参照）。

| 条件 | 由来 | 意味 | フラグの状態（実際の値は有効は1、無効は0） |
|:-:|:-:|:-:|:-:|
| `EQ`  | **eq**ual | `==` | Zが有効|
| `NE` | **n**ot **e**qual | `!=` | Zが無効 |
| `GE` | **g**reater or **e**qual than | `>=` （2の補数表現で）| 「VとNが有効」 または　「VとNが無効」 （つまり`V==N`）|
| `LT` | **l**ower **t**han | `<` （2の補数表現で） | 「Vが有効かつNが無効」 または 「Vが無効かつNが有効」 （つまり`V!=N`。`GE`の逆。） |
| `GT` | **g**reater **t**han | `>` （2の補数表現で） | Zが無効　かつ　`N==V` |
| `LE` | **l**ower or **e**qual than | `<=` （2の補数表現で） | Zが有効 または `N!=V` ※ |
| `MI` | **mi**nus | 負 | Nが有効 | 
| `PL` | **pl**us | 正または**ゼロ** | Nがゼロ | 
| `VS` | o<strong>v</strong>erflow **s**et | - | Vが有効 |
| `VC` | o<strong>v</strong>erflow **c**lear | - | Vが無効 |
| `HI` | **hi**gher | `>` | Cが有効　かつ　Zが無効 |
| `LS` | **l**ower or **s**ame | `<=` | Cが無効　または　Zが有効　（`HI`の逆） |
| `CS/HS` | **c**arry **s**et/**h**igher or **s**ame | `>=` | Cが有効 |
| `CC/LO` | **c**arry **c**lear/**lo**wer | `<` | Cが無効 |

※　訳注：翻訳前の英語には`LE`のフラグの状態が「"Zが有効" または **"N==V"**」のように書かれていますが訳者が調べた所誤りであると確認できたので正しいものを載せています。
※　訳注：「2の補数表現で」というのはC言語などで言う「符号付き整数」のことです。

`b`命令に条件を表す文字列を組み合わせることで、新しい命令を作ることができます。例えば、`beq`命令はZフラグが1なら分岐します。条件分岐命令の条件が満たされない場合、分岐は無視され後に続く命令が実行されます。条件分岐命令の前に条件コードフラグが適切にセットされているかを確認するのはプログラマーの仕事です。

```:compare01.s
/* -- compare01.s */
.text
.global main
main:
    mov r1, #2       /* r1 ← 2 */
    mov r2, #2       /* r2 ← 2 */
    cmp r1, r2       /* update cpsr condition codes with the value of r1-r2 */
    beq case_equal   /* branch to case_equal only if Z = 1 */
case_different :
    mov r0, #2       /* r0 ← 2 */
    b end            /* branch to end */
case_equal:
    mov r0, #1       /* r0 ← 1 */
end:
    bx lr
```

このプログラムを実行すると、`r1`と`r2`が等しいためエラーコードは1になります。では5行目の`mov r1, #2`を`mov r1, #3`に書き換えてみてください。返り値のエラーコードが2になるはずです。`error_different`では、`end`へ分岐する`b`命令で`case_equal`をスキップするようにしています。これがないと、エラーコードは常時1になってしまうので注意です。

今日はここまで。

次回：[6. 制御構造（if文 while文）](https://qiita.com/GoodOldLandLine/items/bea06ca1f380c26d482f)