スタックを使う
スタックは後入れ先出しの領域です。pushで積み、popで最後に積んだ値を取り出します。
積んだ順番と逆に取り出すASSEMBLY
mov rax, 10
mov rbx, 20
push rax
push rbx
pop rcx ; rcx = 20
pop rdx ; rdx = 10callとretで関数に分ける
callは戻り先をスタックへ積んでラベルへ移動し、retは戻ります。retの前にスタックを壊さないことが重要です。
2倍する関数(complete.asm)ASSEMBLY
section .text
global _start
_start:
mov rdi, 6
call double_value
mov rdi, rax
mov rax, 60
syscall
double_value:
mov rax, rdi
add rax, rax
ret実行結果OUTPUT
echo $?
12callの直後に関数本体を置く場合も、関数から戻った後に終了処理へ進む必要があります。
Linuxへ依頼する
CPU命令だけでは端末表示はできません。x86-64 Linuxではraxへシステムコール番号を置き、rdi以降へ引数を置いてsyscallを実行します。
| 処理 | 番号 | 主な引数 |
|---|---|---|
write | 1 | fd、アドレス、長さ |
exit | 60 | 終了コード |
環境依存に注意 番号と引数の規則はOS・CPU・ABIに依存します。
表示処理を関数にする
writeを呼ぶ処理を関数へ分けると、メイン処理の流れを読みやすくできます。なおsyscallはrcxとr11を壊すため、必要な値は退避します。
print_messageASSEMBLY
section .data
message db "Assembly function", 10
message_length equ $ - message
section .text
global _start
_start:
call print_message
mov rax, 60
xor rdi, rdi
syscall
print_message:
mov rax, 1
mov rdi, 1
mov rsi, message
mov rdx, message_length
syscall
ret実行結果OUTPUT
Assembly function