Exercismのx86-64トラックでは、アセンブラーとしてNASM(The Netwide Assembler)を使います。
NASMでは、コメントの先頭にはセミコロン(;)を付けます。
コメントはプログラムのどこにでも書くことができ、;より後ろはすべてアセンブラーに無視されます。
NASMでは、equを使ってアセンブラー時に評価される定数を定義できます。
たとえば、これはUNIVERSEという名前の定数を値42で定義します:
UNIVERSE equ 42
定数は定義されたときに一度だけ評価され、あとから再定義することは_できません_。
アセンブリでは、変数の代わりに、ふつうはレジスタを使って値を保存します。 CPUレジスタは、コンピューターのプロセッサー内部にある高速なメモリーです。 ほとんどの計算は、CPUのレジスタで行われます。
レジスタの一部は、さまざまな計算に使われ、**汎用レジスタ(GPR)**と呼ばれます。 それ以外のレジスタには、特別な、あるいは専用の役割があります。
x86-64には16個の64ビット汎用レジスタ(GPR)があり、32ビット、16ビット、8ビットとしてもアクセスできます。
汎用レジスタは以下のとおりです。ここでrnのnは8から15の範囲です:r8、r9、r10、r11、r12、r13、r14、r15。
| 64ビット | 32ビット | 16ビット | 8ビット |
|---|---|---|---|
| rax | eax | ax | ah/al |
| rbx | ebx | bx | bh/bl |
| rcx | ecx | cx | ch/cl |
| rdx | edx | dx | dh/dl |
| rsi | esi | si | sil |
| rdi | edi | di | dil |
| rbp | ebp | bp | bpl |
| rsp | esp | sp | spl |
| rn | rnd | rnw | rnb |
64ビット未満を使う場合、アクセスされるビットはふつうレジスタの下位部分です。
この規則の例外はah、bh、ch、dhで、これらはレジスタの16ビット部分のうち上位8ビットにアクセスします。
raxレジスタでビットにアクセスする様子を示した図です:
+--------+---------------------------------------+
| 64-bit | rax |
+--------+-------------------+-------------------+
| 32-bit | | eax |
+--------+-------------------+---------+---------+
| 16-bit | | ax |
+--------+-----------------------------+----+----+
| 8-bit | | ah | al |
+--------+-----------------------------+----+----+
これらのレジスタの一部は、関数呼び出しをまたいで保存しなければなりません:rbp、rsp、rbx、r12、r13、r14、r15。
保存し損ねると、エラーや未定義の動作を引き起こす可能性があります。
それ以外は保存する必要がなく、自由に使えます:rax、rcx、rdx、rdi、rsi、r8、r9、r10、r11。
この先シラバスの中で、レジスタを保存する方法を学びます。
今のところは、2つ目の種類のレジスタだけを使いましょう:rax、rcx、rdx、rdi、rsi、r8、r9、r10、r11。
命令とは、CPUが実行できる計算の単位です。 命令はふつう、次のような形をしています:
name destination, source
つまり、最初に命令の名前を置き、そのあとに少なくとも1つの空白、次に宛先のオペランド、カンマ(,)、最後にソースのオペランドを書きます。
命令によって変更されるのは、ふつうは宛先のオペランドだけで、ソースのオペランドは変更されません。
特に断りのない限り、両方のオペランドは同じサイズでなければなりません。 たとえば、ソースオペランドが16ビットなら、宛先オペランドも16ビットでなければなりません。
たとえば、値をレジスタに保存するには、mov命令を使います:
mov rax, rdx ; rax = rdx
; mov is the name, rax is the destination operand and rdx is the source operand
上のコードは、ソースオペランドであるrdxの64ビットすべての内容を、この命令の宛先オペランドであるraxの64ビットすべてにコピーします。
ほとんどの命令では、整数の定数をソースオペランドとして使えます(ただし宛先オペランドとしては使えません):
mov rcx, 42 ; rcx = 42
; rcx is the destination operand and 42 is the source operand
32ビットレジスタに書き込むと上位ビットもクリアされるため、mov eax, 42はmov rax, 42と同じであることに注意してください。
これは16ビットレジスタと8ビットレジスタには当てはまりません。
加算、減算、乗算といった算術演算には、add、sub、imul命令を使います:
add rax, rsi ; rax = rax + rsi
imul rax, rdi ; rax = rax * rdi
sub rax, r8 ; rax = rax - r8
命令は関数の中にまとめられます。 すべての関数は、ソースファイルのsection .textに置きます。
関数の宣言は次のもので構成されます:
:。ret。このトラックではSystem V AMD64 ABIの呼び出し規約を使い、最初の6つの整数引数はレジスタで関数に渡されます。
引数は次の順番で渡されます:rdi、rsi、rdx、rcx、r8、r9。
整数の値はraxレジスタで関数から返されます。
関数を呼び出すには、call命令を使います。
関数は同じソースファイルのどこからでも呼び出せます。
関数を他のソースファイルから見えるようにするには、globalディレクティブを使う必要があります。
たとえば、これはsumという関数を宣言します:
section .text ; functions are placed here
global sum ; sum is visible to other source files
sum:
; first argument is passed in rdi
; second argument is passed in rsi
; return value is placed in rax
mov rax, rdi ; rax is now equal to rdi
add rax, rsi ; rax = rax + rsi
ret ; function return
そして、これは引数3と5でsum関数を呼び出します:
mov rdi, 3 ; First argument in rdi
mov rsi, 5 ; Second argument in rsi
call sum
; The rax register now contains the value 8 (3 + 5), after sum returns
この演習では、お気に入りの料理本に載っている最高のラザニアを作るために、コードを書いていきます。
取り組むタスクは4つあり、どれもラザニアを調理するのにかかる時間に関するものです。
このコンセプトで説明されている命令は次のとおりです:
| 命令 | 説明 |
|---|---|
| mov a, b | b の内容を a にコピーします |
| add a, b | a = a + b |
| sub a, b | a = a - b |
| imul a, b | a = a * b |
| call a | 関数 a を呼び出します |
| ret | 関数から戻ります |
引数を一切取らず、ラザニアをオーブンに入れておくべき時間を分で返すexpected_minutes_in_oven関数を定義します。
料理本によると、想定されるオーブン加熱時間は40分です。
expected_minutes_in_oven();
// => 40
ラザニアが実際にオーブンに入っていた分数を仮引数として受け取り、前のタスクで求めた想定オーブン加熱時間をもとに、ラザニアをあと何分オーブンに入れておく必要があるかを返すremaining_minutes_in_oven関数を定義します。
remaining_minutes_in_oven(30);
// => 10
ラザニアに重ねた層の数を仮引数として受け取り、1層につき2分かかるものとして、ラザニアの準備に何分かかったかを返すpreparation_time_in_minutes関数を定義します。
preparation_time_in_minutes(2);
// => 4
次の順番で2つの仮引数を受け取るelapsed_time_in_minutes関数を定義します:
この関数は、ラザニアの調理に何分かかったかを返す必要があります。 これは、準備にかかった分数と、その時点でラザニアをオーブンに入れておいた分数の合計です。
elapsed_time_in_minutes(3, 20);
// => 26
Exercismに登録すれば、22個のコンセプト130個の演習、そして本物の人間によるメンタリングとともに、x86-64 Assemblyを学んでマスターできます。すべて無料です。