ラズパイでARM入門（1. イントロダクション）

次回：[2. レジスタと基本的な算術演算](https://qiita.com/GoodOldLandLine/items/e65a8e6d368db65ff3e7)
[目次（記事一覧）](https://qiita.com/GoodOldLandLine/items/bb17122fee602d1aedb9)

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

私としては特定のアーキテクチャのアセンブリを学ぶより高級言語を学ぶほうが有益だと思います。しかし、386を知る身として（※訳注　i386つまり32ビット版x86アーキテクチャ。かなりややこしいらしいです。）、楽しんでArmアセンブリを学べるのではないかと思いました。マスターすることよりその下で何が起きているかを詳しく理解することを目指していきます。

# ARMの紹介
<!--
直訳：
あまり詳しくは解説しません。実用的なものになるように努めます。
-->
役に立つことだけさらっと。

<!--
直訳：
ARMは、柔軟性という単純な目標を念頭に置いた32ビットアーキテクチャーです。このことはシステムインテグレーターにとってはいいことですが（ハードウェアの設計に多くの自由があるため）、ARMハードの違いに対処する必要があるシステム開発者にはあまり良いことではありません。したがって、この記事では、**すべてRaspbian（※訳注　現Raspberry Pi OS）を動かしているRaspberry PiモデルB（USBポートが２つ、512 MBのRAMのもの）上で実行される**と想定します（※訳注　訳者はRaspberry Pi 3モデルB+で試し結果が異なればそれを記します）。
-->
ARMは、柔軟性を目標にした32ビットアーキテクチャーです。ハードウェア設計の自由度が高いためことはシステムインテグレーターには良いことですが、システム開発者としてはハードウェアの違いに対処するのは面倒です。したがってこの記事では動作環境を**Raspberry Pi OS/Raspberry PiモデルB**とします（※訳注　訳者はRaspberry Pi 3モデルB+で試し結果が異なればそれを記します）。

いくつかの解説はARM共通ですが、ラズベリーパイにのみ当てはまるのもあります。特に明示しません。[ARM 公式サイト](developer.arm.com/documentation/)にはたくさんの資料がありますのでそれを見てください！

# アセンブリ言語の記述
<!--
アセンブリ言語は、バイナリコードの上にある薄い構文層です。

バイナリコードはコンピューターで実行できるものです。それはバイナリの表現でエンコードされた命令で構成されています（エンコード方法はARMのマニュアルに記載されています）。バイナリコードを直接書くこともできますが骨が折れます（Linux自体に関連する他のいくつかの技術を除けば、今は無視できます）。

そこで、（ARM）アセンブリ言語を記述します。コンピューターはアセンブリ言語自体は実行できないためアセンブリ言語からバイナリコードに変換してやる必要があります。アセンブリコードをバイナリコードにアセンブルするためにアセンブラを用います。

これをするためのツールを`as`と呼びます。特に、GNUプロジェクトのアセンブラであるGNUアセンブラは時に同じ理由で`gas`と呼びます。この記事ではgasをアセンブルに使っていきます。

`vim`や`nano`、`emacs`なんかのエディターを開いてください。アセンブリ言語ファイル（ソースファイルと呼ばれる）には拡張子`.s`が付きます。なぜ`.s`なのかはわかりませんが慣習でそうしています。
-->
アセンブリ言語は、バイナリコードの上にある単純な構文層です。

バイナリコードはコンピューターで直接実行できるものです。それはバイナリの表現にエンコードされた命令で構成されています（エンコード方法はARMのマニュアルに書いてあります）。バイナリコードを直接書くこともできますが骨が折れます（Linux自体に関係する技術を扱うならそうすることも必要かもしれません）。

そこで、（ARM）アセンブリ言語を記述します。コンピューターはアセンブリ言語を直接実行できないためアセンブリ言語からバイナリコードに変換してやる必要があります。アセンブリコードをバイナリコードにアセンブルするためにはアセンブラが必要です。

アセンブラを`as`と呼ぶこともあります。同じ理由でGNUプロジェクトのアセンブラであるGNUアセンブラは`gas`と呼ぶこともあります。この記事ではgasをアセンブルに使っていきます。

`vim`や`nano`、`emacs`などエディターを開いてください。アセンブリ言語ファイル（ソースファイルとも言う）には慣習で拡張子`.s`が付きます。なぜ`.s`なのかはわかりません。


# 最初のプログラム
<!--
何かから始めなくてはならないので、エラーコードを返すだけのとてつもなくシンプルなプログラムから始めます。
-->
とりあえずエラーコードを返すだけのとてつもなくシンプルなプログラムから始めましょう。

```:first.s
/* -- first.s */
/* This is a comment */
.global main /* 'main' is our entry point and must be global */

main:          /* This is main */
    mov r0, #2 /* Put a 2 inside the register r0 */
    bx lr      /* Return from main */
```

`first.s`というファイルを作成し上記の内容を書き込み保存します。

ファイルを*アセンブル*するには、次のコマンドを入力します（$の後に続くものを書いてください）。

```
$ as -o first.o first.s
```

`first.o`が作られます。実行ファイルを得るためにこのファイルをリンクします。

```
$ gcc -o first first.o
```

<!--
すべて問題なく進めば、firstファイルができます。これがあなたの最初のプログラムです。動かしてみましょう。
-->
すべて問題なく進めば、firstファイルができます。最初のプログラムができました。動かしてみましょう。

```
$ ./first
```

<!--
何も表示されないはずです。それだと少しがっかりですが、実際には何かをしています。ではそれのエラーコードを表示してみましょう。
-->
何も表示されないはずです。実際には何かをしているのにそれだと少々残念です。
というわけでエラーコードを表示してみましょう。

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

<!--
グレイト！エラーコードが2であることは偶然ではなく、アセンブリコードの#2によるものです。
-->
うまくいきました。エラーコードが2であることは偶然ではなく、アセンブリコード中の`#2`によるものです。

アセンブラを実行して、リンカを実行するという流れはじきに飽きるので、以下の`Makefile'ファイルまたは同様のファイルを作成することをおすすめします。

```:Makefile
# Makefile
all: first

first: first.o
	gcc -o $@ $+

first.o : first.s
	as -o $@ $<

clean:
	rm -vf first *.o
```

※訳注
gccは優秀なので以下の方法でも.sファイルを実行ファイルにできます。
Makefileの使い方を知らない人はこの方が楽です。
理解できればいいのは本質的には「オブジェクトコードを作ってからリンクが行われる」ということかなと思います。

```
$ gcc -o first first.s
```


# 解説
物事を少し簡単にするために少しごまかしたところがあります。`return 2;`をするだけのアセンブリコードにC言語の`main`関数を書きました。C言語ランタイムがプログラムの初期化と終了を処理するため、このようにプログラムが簡単になります。今後は常にこの手法を使っていきます。

さきほどつくった`first.s`のすべての行を確認しましょう。

```
/* -- first.s */
/* This is a comment */
```

これらはコメントです。コメントは`/*`と`*/`で囲まれています。コメントは無視されるためアセンブリコードに説明文を付け加えるのに使えます。うまく機能しなくなるので`/*`の内側に`/*`と`*/`を入れ子にしないでください。（※訳注　C言語と同じく一行コメント`//`も使えます。）

```
.global main /* 'main' is our entry point and must be global */
```

これはGNUアセンブラへのディレクティブ（疑似命令）です。ディレクティブはGNUアセンブラに特別なことをするように指示をします。ディレクティブはピリオド（.）で始まり、その後に命令の名前と引数が続きます。この場合では`main`がグローバルであるという意味です。これは、C言語ランタイムが`main`を呼び出すために必須です。グローバルにしないと、C言語ランタイムは呼び出すことができずリンクは失敗します。

```
main:          /* This is main */
```

GNUアセンブリコードにおいてディレクティブではない行はすべて、`ラベル: 命令`の形をとります。`ラベル:`または`命令`もしくはその両方は省略できます（空の行や空白文字のみの行は無視されます）。`ラベル:`のみの行は、そのラベルを次の行に適用します（この方法で同じものを参照する複数のラベルを作ることができます）。`命令`の部分はARMアセンブリ言語そのものとなります。この場合では`main`を命令なしで定義しています。

```
    mov r0, #2 /* Put a 2 inside the register r0 */
```

行の先頭の空白は無視されますが、インデントは、この命令が`main`関数に属していることを視覚化するために使用しています。

これはムーブを表す`mov`命令です。値の2を`r0`レジスタに移動します。レジスタについては次章で詳しく説明するので、今は心配しないでください。見ての通り、移動先が左にあるため扱いにくい構文です。ARM構文では常に左となり、文の意味は「r0レジスタに即値2を移す」です。ARMにおける即値の意味は次章で扱いますのでこれも心配ご無用です。

要するに、この命令は2をr0レジスタに入れるということを意味します（その時点でr0レジスタが持つ値は事実上上書きされます）。

```
    bx lr      /* Return from main */
```

この`bx`命令は、「分岐と交換」を意味します。今は、交換部分については考えないようにします。分岐とは、命令実行の流れを買えることを意味します。ARMプロセッサーは命令を順番に次に次にと実行していくため、前行の`mov`の次に`bx`が実行されます（この順次実行はARMに特有のものではなく、ほぼすべてのアーキテクチャで共通です）。分岐命令は、この暗黙の順次実行を変更するために使用されます。この場合、`lr`レジスタの示す値に分岐します。今は`lr`レジスタがもつ値は気にしないでおきましょう。この命令が`main`関数を終わらせて事実上プログラムが終了することが理解できれば十分です。

エラーコードの話はどうなったのでしょう。実は、`main`関数のリザルトはプログラムのエラーコードであり、関数を終了するときにリザルトは`r0`レジスタに格納する必要があります。つまり、`main`関数によって実行される`mov`命令が実際にはエラーコードを2に設定しているのです。

今日はここまで。


[次回： 2. レジスタと基本的な算術演算](https://qiita.com/GoodOldLandLine/items/e65a8e6d368db65ff3e7)