🏁 ゴール
この記事はアセンブリ言語を学ぶためのモチベーションを保つ試みです。
アセンブリ言語を学ぶメリットは大きく分けて次の2つがあります。
- 他の言語の動作に詳しくなる
- CPUの動作に詳しくなる
他の言語の動作に詳しくなる
C言語はアセンブラに翻訳されます。
アセンブラ
現代の複雑なプログラミング言語のコンパイラも、その言語の実装をコンパ
イルするために使った別のコンパイラ、というように系譜をさかのぼってい
くと、最終的に、コンピュータの黎明期に誰かが機械語で直接書いた単純な
アセンブラにたどりつくはずです。現存するすべての言語実装のある意味の
究極の祖先にあたるそのアセンブラが、単一なのか複数あったのかはわかり
ませんが、現在のコンパイラがごく少数の祖先から出発しているのは間違い
ないでしょう。
このように、プログラミング言語は別の言語に翻訳されますが、その先にはアセンブリ言語がいるのです。
CPUの動作に詳しくなる
objdumpコマンドを使用して、任意の実行ファイルを逆アセンブルし、その中の機械語をアセンブリコードとして表示してみましょう。以下は、catコマンドを逆アセンブルした結果の例です。
$ objdump -d -M intel /bin/cat
/bin/cat (architecture x86_64):
(__TEXT,__text) section
100001510: 55 push rbp
100001511: 48 89 e5 mov rbp, rsp
100001514: 41 57 push r15
100001516: 41 56 push r14
100001518: 41 55 push r13
10000151a: 41 54 push r12
10000151c: 53 push rbx
10000151d: 48 83 ec 18 sub rsp, 24
アセンブリでは、基本的に機械語1命令が1行ずつ表示される構成になっています。例として、次の行に注目してみましょう。
10000151d: 48 83 ec 18 sub rsp, 24
- 一つ目の
:の手前の数字10000151dはメモリのアドレスです。
この行の命令は10000151dに置かれるようになっていて、CPU内部のプログラムカウンタがこの値になった時にこの行が実行されます。 - 次の4つの数字は機械語です。
48 83 ec 18と書かれていますが、人間が読んでも多分不明です。 - 上の4つの数字に対応するアセンブラが、
sub rsp, 24です。 rspレジスタの値から、24を引いてます。
このように、機械語の命令と一対一対応する命令がアセンブリであり、低レイヤーな言語であることがわかります
💦 アセンブリ言語とは
機械語の一個手前の低水準プログラミング言語で、C言語よりも低級なことが特徴。
プロセッサは機械語プログラムを直接読み取り実行するが、人間にとってビット列は直観的に理解しづらいため、機械語コーディングは容易でない。
これを解決するために、ビット列に対応する文字列命令(ニーモニック)を利用するプログラミング言語の総称がアセンブリ言語である。
例えば 0x8becは機械語であり、人間には読めないがMOV BX, SPは人間には何とか読める(SPをBXに代入せよという命令)
ここで、この一行の命令のことをニーモニックと呼び、
このニーモニックの集まりがアセンブリ言語である。
また、引数のことをオペラントと呼び、引数の一つ目を第一オペラント、第二引数を第二オペラントと呼ぶ
参考 : https://www.youtube.com/watch?v=8EcHs4UGgMc
参考 : https://ja.wikipedia.org/wiki/%E3%82%A2%E3%82%BB%E3%83%B3%E3%83%96%E3%83%AA%E8%A8%80%E8%AA%9E
📃 アセンブリ言語のサンプルコード
アセンブリでの掛け算は、アーキテクチャによって異なる方法があります。以下では一般的なアセンブリ言語における掛け算の方法をいくつか紹介します。
1. 加算ループを使用する方法:
この方法では、掛けられる数を加算することで乗算を実現します。
; 例: AX = BX * AX
MOV CX, AX ; AXをカウンタレジスタCXにコピー(AX 回ループしたい)
MOV AX, 0 ; 結果を格納するレジスタAXを0に初期化(回数は CX にコピー済み→消去OK)
MUL_LOOP:
ADD AX, BX ; AXにBXを加算(AX ← AX + BX)
LOOP MUL_LOOP ; CXをデクリメント(-1)し、0でない場合はループ
- この例では、BXレジスタの値をAXレジスタに掛けた結果を求めます。
- CXレジスタはカウンタとして使用され、掛ける回数を表します。
- ループの中でAXにBXを加算し、CXをデクリメントします。
- CXが0になった時点でループを終了します。
要はAXの数だけBXを足し合わせるということ。
参考(オペコード): https://yamatyuu.net/computer/x86/index.html (4. 8086/8088命令一覧)
参考(ADD命令の詳細): https://zenn.dev/mod_poppo/articles/x86-64-machine-code#add%E5%91%BD%E4%BB%A4
2. CPUの掛け算を利用する方法
CPUによっては掛け算を用意してくれているケースもあります。
アセンブラ、アセンブリ、アセンブル
- アセンブラ:ソフトウェアのこと。
- アセンブリ:言語のこと。
- アセンブル:動詞。
これらを一つの文章にまとめると、アセンブリをアセンブラでアセンブルする。 ということになります。
アセンブリ言語の大変なこと
アセンブリ言語はCPUと一対一対応しているため、CPUごとにニーモニックが異なる
そのため、アセンブリ言語は断トツで難しい
覚える命令は少ないが...
コンパイル言語はさらに抽象化されているため、同じコードでCPUが動くし、言語がCPUによって変わることもない。
アセンブリ言語のメリット
CPUの最適化ができるのがアセンブリ言語のメリット。
ただ、速度目的ではすでにコンパイラの最適化は素晴らしいことになっているので、人間がそこに介入する余地はない。
- コンピューターの気持ちが分かるようになる。
- アセンブリで動いている低級プログラムも0じゃない。そこではアセンブリ言語が必須。
- CPU開発
- カーネル、デバイスドライバ
- コンパイラ開発