Raspberry Pi 3でアセンブラをはじめから〜基礎編〜

## はじめに

Web系エンジニアも**低レイヤーを学ぶべき**と巷で言われていますが、本当なのだろうか。。。

**基本情報技術者試験**を勉強するという選択肢もありましたが、個人的に分かりにくかったので（レジスタはCPU内部にある記憶装置って言われてもピンとこない）、アウトプット主体で学んで、その都度理解できない概念を調べてインプットしていくことにしました。

アウトプットの方法として、前から触ってみたかった**Raspberry Pi 3（ラズパイ）**を購入してアセンブラを約1ヶ月ほど勉強してみました。同じような方々の助けになれば幸いです。

今記事ではラズパイを使用するため**ARM**での実装です。
「ARMとは？」と言った**アーキテクチャ**から**発展編**では簡単な**アルゴリズム**を実装するまで解説しています。

間違っているところがあればご指摘いただけるとありがたいです。:bow_tone1:

現在の記事 → Raspberry Pi 3でアセンブラをはじめから〜基礎編〜
作成中 → [Raspberry Pi 3でアセンブラをはじめから〜発展編〜](https://qiita.com/okamotoyu/e343190576a289b47a21)

## セットアップ
具体的なセットアップ方法は[こちら](https://qiita.com/Higemal/items/c817b96c3806f23b35f6)

私が用意したものは以下の7点です。
・[ラズパイ](https://www.amazon.com/gp/product/B01C6EQNNK/ref=ppx_yo_dt_b_asin_title_o03_s00?ie=UTF8&psc=1)(Raspberry Pi 3を使用)
・[microSDカード](https://www.amazon.com/gp/product/B07K81Z6DF/ref=ppx_yo_dt_b_asin_title_o02_s00?ie=UTF8&psc=1)(スターターキットで既に付いている場合は不要)
・[アダプター](https://www.amazon.com/gp/product/B07L63Z54G/ref=ppx_yo_dt_b_asin_title_o01_s00?ie=UTF8&psc=1)(スターターキットで既にmicroSDカードにOSがインストールされている場合は不要)
・モニター
・HDMLケーブル
・有線マウス
・有線キーボード

＊時々死んでしまうため、OSの再インストールが必要な場合は[こちら](https://sozorablog.com/reinstall/)


## 前提知識

### そもそもアセンブラとは
C言語の**コンパイル**は```gcc main.c```をするだけで可能ですが、実際は以下の4つの手順を踏んでいます。
1. main.cを**preprocessor**でコンパイラの前処理をする。簡単にいうと別のC言語「.i」に変換している(preprocessorとは#include \<stdio.h>のようなディレクティブのこと)
```$gcc -E main.c```で**「.i」**ファイルができるが基本的に2をすれば自動的に1もされる。
2. **「.i」**をアセンブラ**「.s」**に直す。コンピュータが解釈できるアセンブリ言語の形式に変換。
```$gcc -S main.c```
3. **「.s」**を機械語**「.o」**に直す。
```$gcc -o {実行ファイル名} main.s```
4. 実行ファイルに直す。
```$./{実行ファイル名}```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/648295/0803625c-514f-e43e-712e-501367529941.png)

アセンブラは**「.s」**拡張子なので、1, 2を飛ばして3から始めます。
機械語に近い**低水準言語**を学ぶことで、コンピューターの生の動作を意識した無駄のないプログラムを高水準言語でも作れるようになります。

### Instruction set architecture（ISA）
CPUのアーキテクチャの種類としてx86とARMが2大巨塔です。
有名どこだと、2020年以降Macは**Intel製のx86アーキテクチャ**から自社開発の**ARMアーキテクチャ**に切り替わりました。

x-86: 高性能+消費電力大 
ARM: 性能にばらつきあり+省電力
という違いがあるそうです。だからM1 Macからバッテリー駆動時間が爆発的に伸びたのかな？

ちなみに、x-86なのかARMなのかで、**Dockerイメージ**を使い分けないといけません。M1 Macに変えてこれまでのDockerfileが使えないなんてよく騒がれてましたが、CPUのアーキテクチャが原因なので対応すれば普通に使えます。

https://zenn.dev/suzuki_hoge/books/2021-12-m1-docker-5ac3fe0b1c05de/viewer/2-arm

### レジスタ
r0からr15までの16個のレジスタがあり、limited memoryのようなイメージ。一時記憶領域のため、プログラムが終了すると消えてしまうが最も高速。
<dl>
  <dt>r0~r10: 一般的な目的で使われる</dt>
  <dd>r0~r3に第1~第4引数までセットされ、r0に戻り値が入る。それ以降はスタック</dd>
  <dd>r4以降は普通のレジスタ</dd>
  <dt>r11~r15: 特別な目的で使われる</dt>
  <dd>r11(fp): frame pointer</dd>
  <dd>r12(ip): intra-pocedure call scratch pointer（よく分からない）</dd>
  <dd>r13(sp): stack pointer</dd>
  <dd>r14(lr): link register（BL命令で分岐した場合の戻りアドレスを保持）</dd>
  <dd>r15(pc): program counter（実行中のメモリアドレスを保持）</dd>
</dl>

### ディレクティブの定義
<dl>
  <dt>.text</dt>
  <dd>テキストセグメントの開始を指定する．ARM命令などの実行コードはテキストセグメントに配置する必要がある。</dd>
  <dt>.align 2</dt>
  <dd>Raspberry Pi 3は32ビットOSがインストールされているため、4byte単位でメモリが作られる。</dd>
  <dt>.global func</dd>
  <dd>外部ファイルからもアクセス可能にする。</dd>
</dl>


### ARM命令セット

| 命令フォーマット | 説明 |
|:-----------:|:------------:|
| **mov** r1, #4      | r1 = 4 **説明あり* |
| **add** r1, r2, r3       | r1 = r2 + r3       |
| **sub** r1, r2, r3     |  r1 = r2 - r3      |
| **mul** r1, r2, r3     |  r1 = r2 * r3      |
| **udiv** r1, r2, r3      |  r1 = r2 / r3      |
| **b** branch   | branch（自分で命名）にジャンプ      |
| **bl** func   | func（自分で命名もしくは既存のものを使用）関数の呼び出し      |
| **bx** lr   | レジスタlrの指す命令へジャンプ      |
| **ldr** r0, address  | レジスタ←memory(load command)    |
| **str** r0, address  | レジスタの値をaddressにストア(store command)  |
| **ldr** r0, address  | addressをレジスタに移動(load command)    |
| **cmp** r0, r1  | r0とr1を比較 **説明あり*  |

#### movの説明
数字は**#**を使用しなくてもいいが、古いバージョンでは必要なので数字の前に書いておく。
アセンブリ言語は**高水準言語**と違って、1行で四則演算をできないため分けて書く必要がある。
また、udivを使う際は下記の**実践**編で仕様している```.cpu cortex-a53, .fpu neon-fp-armv8```を記述する必要がある、

#### cmpの説明
cmpの後に比較結果を**判別**する必要がある。

```
mov r0, #10
cmp r0, #4 @r0と4を比較する
bgt b big @r0>4であれば自分で命名したinner関数にジャンプ

big: 
 @inner関数のなかみ
```
| 比較方法  | イメージ | 説明 |
|:-----------:|:------------:|:------------:|
| bne branch       | r0 != 4の時branchにジャンプ       | not equal|
| beq branch       | r0 == 4の時branchにジャンプ       | equal|
| blt branch     | r0 < 4の時branchにジャンプ   | less than   |
| bgt branch     | r0 > 4の時branchにジャンプ   | less than   |
| ble branch     | r0 <= 4の時branchにジャンプ   | less or equal   |
| bge branch     | r0 >= 4の時branchにジャンプ   | grater or equal   |
この条件に合わない場合は、その行をスキップして次の行に進う。
また```bXX branch```は```b + 比較```なので、ジャンプするbranchを記述している。

## 実践
### Hello Worldを実行してみる
```main.s
@この2行はおまじないとして書いている
.cpu cortex-a53 @浮動小数点アーキテクチャ
.fpu neon-fp-armv8 @コンパイラオプション

.data
prompt: .asciz "Hello World!\n" @メモリ

.text
.align 2
.global main @main関数をglobalにして外からでも呼べるように
.type main, %function

main:

mov r4, lr @r4=lr
ldr r0, =prompt @メモリからレジスタに移動 r0="Hello World!\n"
bl printf @branch labeled: printするfunctionを呼ぶ

mov r0, #0 @r0=0
mov lr, r4 @lr=r4
bx lr @branch x link register: returnする
```
```bl printf```で表示されるのは**r0**の内容なので、Hello World!が表示。
![スクリーンショット 0003-12-02 0.54.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/648295/54c7305c-71fe-10f2-a02e-208ffaa041ca.png)

### インプット

```main.s
.cpu cortex-a53
.fpu neon-fp-armv8

.data
prompt: .asciz "Enter the number: "
inp: .asciz "%d"
outp: .asciz "The number that you input is %d\n"

.text
.align 2
.global main
.type main, %function

main:

mov r7, lr
ldr r0, =prompt
bl printf

@ scanf("%d", &sp);
sub sp, sp, #4 @ アドレスが4byte単位なので、inputのデータ保持する空箱をストアする。
ldr r0, =inp @ 第一引数"%d"をr0に
mov r1, sp @ 第二引数にアドレスspを入れる。
bl scanf @ scanfメソッド発火。
ldr r4, [sp] @ spはアドレスなのでdereferenceする。

@ printf("The number that you input is %d\n", r1);
ldr r0, =outp
mov r1, r4
bl printf

@ return 0;
mov r0, #0
mov lr, r7
bx lr
```

![スクリーンショット 0003-12-03 0.23.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/648295/f0b7189d-4ce9-9bd9-67cc-cbba2b5bcfce.png)

## 発展編へ
発展編ではarrayなどをARMアセンブラを用いて解説しています。

https://qiita.com/okamotoyu/e343190576a289b47a21



## 参考文献
[プログラムはなぜ動くのか](https://www.amazon.co.jp/dp/4822283151)
[ARMについて](https://www.aps-web.jp/academy/ca/234/)
[You Can Learn ARM Assembly Language in 15 Minutes | ARM Hello World Tutorial
](https://www.youtube.com/watch?v=FV6P5eRmMh8)