Assembly

#25
TIOBE#19
GitHub#29
IEEESpectrum#33
プログラミング言語低レベル言語システムプログラミング組み込み開発パフォーマンス最適化マシン語

プログラミング言語

Assembly

概要

Assembly(アセンブリ言語)は、CPUの機械語命令を人間が読みやすい形で表現した低レベルプログラミング言語です。

詳細

Assembly言語は、コンピュータのCPUが直接実行する機械語命令を、ニーモニック(助記記号)を使って人間が理解しやすい形で記述できる低レベルプログラミング言語です。各CPUアーキテクチャ(x86、ARM、MIPS、RISC-Vなど)ごとに固有の命令セットと構文を持ち、ハードウェアに最も近いレベルでのプログラミングを可能にします。メモリ管理、レジスタ操作、直接的なハードウェア制御が可能で、最高レベルのパフォーマンスと精密な制御を実現できます。組み込みシステム、デバイスドライバ、オペレーティングシステムのカーネル、リアルタイムシステム、セキュリティ研究、リバースエンジニアリングなどの分野で使用されます。現代では高レベル言語のクリティカルな部分の最適化や、特定のハードウェア機能を活用する際に使用されることが多く、C/C++との組み合わせによるインラインアセンブリも広く利用されています。

書き方の例

Hello World(x86-64 Linux)

# AT&T記法(GAS)
.section .data
    msg: .ascii "Hello, World!\n"
    msg_len = . - msg

.section .text
    .global _start

_start:
    # write システムコール
    mov $1, %rax        # sys_write
    mov $1, %rdi        # stdout
    mov $msg, %rsi      # メッセージのアドレス
    mov $msg_len, %rdx  # メッセージの長さ
    syscall

    # exit システムコール
    mov $60, %rax       # sys_exit
    mov $0, %rdi        # 終了コード
    syscall
; Intel記法(NASM)
section .data
    msg db 'Hello, World!', 0xA
    msg_len equ $ - msg

section .text
    global _start

_start:
    ; write システムコール
    mov rax, 1          ; sys_write
    mov rdi, 1          ; stdout
    mov rsi, msg        ; メッセージのアドレス
    mov rdx, msg_len    ; メッセージの長さ
    syscall

    ; exit システムコール
    mov rax, 60         ; sys_exit
    mov rdi, 0          ; 終了コード
    syscall

基本的なデータ操作

# x86-64 AT&T記法
.section .data
    number1: .quad 10
    number2: .quad 20
    result:  .quad 0

.section .text
    .global _start

_start:
    # レジスタにデータをロード
    movq number1(%rip), %rax    # number1を%raxにロード
    movq number2(%rip), %rbx    # number2を%rbxにロード
    
    # 加算演算
    addq %rbx, %rax             # %rax = %rax + %rbx
    
    # 結果を保存
    movq %rax, result(%rip)     # 結果をメモリに保存
    
    # 減算演算
    movq number1(%rip), %rcx
    subq number2(%rip), %rcx    # %rcx = number1 - number2
    
    # 乗算演算
    movq number1(%rip), %rax
    movq number2(%rip), %rbx
    imulq %rbx, %rax            # %rax = %rax * %rbx
    
    # ビット演算
    movq $0xFF, %rdx
    andq $0x0F, %rdx            # AND演算
    orq  $0xF0, %rdx            # OR演算
    xorq $0xFF, %rdx            # XOR演算
    
    # 終了
    mov $60, %rax
    mov $0, %rdi
    syscall

条件分岐とジャンプ

# x86-64における条件分岐
.section .data
    value1: .quad 10
    value2: .quad 20

.section .text
    .global _start

_start:
    # 値の比較
    movq value1(%rip), %rax
    movq value2(%rip), %rbx
    cmpq %rbx, %rax             # %rax と %rbx を比較
    
    # 条件ジャンプ
    je equal                    # 等しい場合
    jg greater                  # より大きい場合
    jl less                     # より小さい場合
    jmp end                     # 無条件ジャンプ

equal:
    # 等しい場合の処理
    movq $1, %rcx
    jmp end

greater:
    # より大きい場合の処理
    movq $2, %rcx
    jmp end

less:
    # より小さい場合の処理
    movq $3, %rcx
    jmp end

end:
    # プログラム終了
    mov $60, %rax
    mov $0, %rdi
    syscall

ループ処理

# カウンターループの例
.section .data
    counter: .quad 5
    sum:     .quad 0

.section .text
    .global _start

_start:
    movq $0, %rax               # 合計値の初期化
    movq counter(%rip), %rcx    # カウンターの設定

loop_start:
    cmpq $0, %rcx               # カウンターが0かチェック
    je loop_end                 # 0なら終了
    
    addq %rcx, %rax             # 現在の値を合計に加算
    decq %rcx                   # カウンターをデクリメント
    jmp loop_start              # ループの先頭に戻る

loop_end:
    movq %rax, sum(%rip)        # 結果を保存
    
    # プログラム終了
    mov $60, %rax
    mov $0, %rdi
    syscall

# whileループ風の書き方
while_loop_example:
    movq $10, %rbx              # 初期値

while_start:
    cmpq $0, %rbx               # 条件チェック
    jle while_end               # 0以下なら終了
    
    # ループ本体の処理
    # 何らかの処理...
    
    decq %rbx                   # 値をデクリメント
    jmp while_start             # ループ継続

while_end:
    ret                         # 関数復帰

関数の定義と呼び出し

# 関数の定義例
.section .text
    .global _start
    .global add_numbers
    .global factorial

# 2つの数を加算する関数
add_numbers:
    # 引数: %rdi(第1引数), %rsi(第2引数)
    # 戻り値: %rax
    movq %rdi, %rax
    addq %rsi, %rax
    ret

# 階乗を計算する関数(再帰)
factorial:
    # 引数: %rdi(n)
    # 戻り値: %rax
    
    # ベースケース: n <= 1
    cmpq $1, %rdi
    jle base_case
    
    # 再帰呼び出し
    pushq %rdi                  # nをスタックに保存
    decq %rdi                   # n-1を計算
    call factorial              # factorial(n-1)を呼び出し
    popq %rdi                   # nを復元
    
    # n * factorial(n-1)
    imulq %rdi, %rax
    ret

base_case:
    movq $1, %rax
    ret

_start:
    # add_numbers関数の呼び出し
    movq $10, %rdi
    movq $20, %rsi
    call add_numbers
    
    # 結果は%raxに格納される
    
    # factorial関数の呼び出し
    movq $5, %rdi
    call factorial
    
    # プログラム終了
    mov $60, %rax
    mov $0, %rdi
    syscall

スタック操作とローカル変数

# スタックフレームを使った関数
.section .text
    .global complex_function

complex_function:
    # 関数プロローグ
    pushq %rbp                  # 古いベースポインタを保存
    movq %rsp, %rbp             # 新しいベースポインタを設定
    subq $32, %rsp              # ローカル変数用にスタック領域を確保
    
    # ローカル変数の使用
    # -8(%rbp)  : 第1ローカル変数
    # -16(%rbp) : 第2ローカル変数
    # -24(%rbp) : 第3ローカル変数
    # -32(%rbp) : 第4ローカル変数
    
    movq $100, -8(%rbp)         # ローカル変数1 = 100
    movq $200, -16(%rbp)        # ローカル変数2 = 200
    
    # ローカル変数を使った計算
    movq -8(%rbp), %rax
    addq -16(%rbp), %rax
    movq %rax, -24(%rbp)        # 結果をローカル変数3に保存
    
    # レジスタの保存と復元
    pushq %r12                  # 使用するレジスタを保存
    pushq %r13
    
    # 何らかの処理...
    movq -24(%rbp), %r12
    imulq $2, %r12
    
    # レジスタの復元
    popq %r13
    popq %r12
    
    # 戻り値の設定
    movq -24(%rbp), %rax
    
    # 関数エピローグ
    movq %rbp, %rsp             # スタックポインタを復元
    popq %rbp                   # ベースポインタを復元
    ret

インラインアセンブリ(C言語との組み合わせ)

// C言語内でのインラインアセンブリ
#include <stdio.h>

int main() {
    int input = 10;
    int result;
    
    // GCCインラインアセンブリ
    asm volatile (
        "movl %1, %%eax\n\t"        // inputを%eaxに移動
        "imull $2, %%eax\n\t"       // 2を掛ける
        "addl $5, %%eax\n\t"        // 5を加える
        "movl %%eax, %0"            // 結果をresultに保存
        : "=r" (result)             // 出力オペランド
        : "r" (input)               // 入力オペランド
        : "eax"                     // 破壊されるレジスタ
    );
    
    printf("結果: %d\n", result);   // 25が出力される
    
    // CPUID命令の例
    unsigned int eax, ebx, ecx, edx;
    asm volatile (
        "cpuid"
        : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx)
        : "a" (0)
    );
    
    printf("CPU ID: %08X %08X %08X\n", ebx, edx, ecx);
    
    return 0;
}

SIMD命令(SSE/AVX)の使用

# SIMD(Single Instruction, Multiple Data)命令の例
.section .data
    # 4つの32ビット浮動小数点数の配列
    array1: .float 1.0, 2.0, 3.0, 4.0
    array2: .float 5.0, 6.0, 7.0, 8.0
    result: .space 16                   # 結果用領域

.section .text
    .global simd_example

simd_example:
    # SSE命令を使った並列演算
    movups array1(%rip), %xmm0          # array1をXMMレジスタにロード
    movups array2(%rip), %xmm1          # array2をXMMレジスタにロード
    
    addps %xmm1, %xmm0                  # 4つの浮動小数点数を同時に加算
    
    movups %xmm0, result(%rip)          # 結果を保存
    
    # AVX命令の例(256ビット)
    # vmovups array1(%rip), %ymm0       # 8つの32ビット浮動小数点数
    # vmovups array2(%rip), %ymm1
    # vaddps %ymm1, %ymm0, %ymm0        # AVX加算
    
    ret

特徴的な機能

直接的なハードウェア制御

# ポート入出力(x86固有)
.section .text
    .global port_io_example

port_io_example:
    # ポートからデータを読み取り
    movw $0x3F8, %dx               # COMポートアドレス
    inb %dx, %al                   # ポートから1バイト読み取り
    
    # ポートにデータを書き込み
    movb $0x48, %al                # 'H'のASCIIコード
    outb %al, %dx                  # ポートに1バイト書き込み
    
    # メモリマップドI/O
    movq $0xB8000, %rdi            # VGAテキストメモリのアドレス
    movb $0x41, (%rdi)             # 'A'を画面に表示
    movb $0x07, 1(%rdi)            # 属性(白文字、黒背景)
    
    ret

割り込みハンドラ

# 割り込みハンドラの例
.section .text
    .global interrupt_handler

interrupt_handler:
    # レジスタの保存
    pushq %rax
    pushq %rbx
    pushq %rcx
    pushq %rdx
    pushq %rsi
    pushq %rdi
    pushq %r8
    pushq %r9
    pushq %r10
    pushq %r11
    
    # 割り込み処理
    # ここに実際の割り込み処理を記述
    
    # EOI(End of Interrupt)の送信
    movb $0x20, %al
    outb %al, $0x20                # PICにEOI送信
    
    # レジスタの復元
    popq %r11
    popq %r10
    popq %r9
    popq %r8
    popq %rdi
    popq %rsi
    popq %rdx
    popq %rcx
    popq %rbx
    popq %rax
    
    iretq                          # 割り込みからの復帰

アトミック操作

# マルチプロセッサ環境でのアトミック操作
.section .data
    shared_counter: .quad 0
    lock_var: .quad 0

.section .text
    .global atomic_increment

atomic_increment:
    # アトミックなインクリメント
    lock incq shared_counter(%rip)  # LOCK prefixでアトミック性を保証
    
    # Compare and Swap (CAS)
    movq $0, %rax                   # 期待値
    movq $1, %rbx                   # 新しい値
    lock cmpxchgq %rbx, lock_var(%rip)
    
    # Test and Set
    movq $1, %rax
    lock xchgq %rax, lock_var(%rip)
    
    ret

アーキテクチャ別の特徴

ARM Assembly(AArch64)

// ARM64アセンブリの例
.global _start

.section .data
    msg: .ascii "Hello, ARM!\n"
    msg_len = . - msg

.section .text
_start:
    // システムコール番号と引数の設定
    mov x8, #64          // sys_write
    mov x0, #1           // stdout
    ldr x1, =msg         // メッセージアドレス
    mov x2, #msg_len     // メッセージ長
    svc #0               // システムコール実行
    
    // プログラム終了
    mov x8, #93          // sys_exit
    mov x0, #0           // 終了コード
    svc #0

// ARM64での条件実行
conditional_example:
    cmp x0, x1
    b.eq equal           // 等しい場合
    b.gt greater         // より大きい場合
    b.lt less            // より小さい場合
    
equal:
    mov x2, #1
    ret
    
greater:
    mov x2, #2
    ret
    
less:
    mov x2, #3
    ret

バージョンと実装

アーキテクチャ 命令セット 主な用途 特徴
x86-64 x86_64, AMD64 デスクトップ、サーバー CISC、豊富な命令
ARM ARMv8-A (AArch64) モバイル、組み込み RISC、省電力
RISC-V RV32I, RV64I 学術、組み込み オープンソース、モジュラー
MIPS MIPS32, MIPS64 組み込み、教育 RISC、シンプル
PowerPC PowerPC64 サーバー、組み込み RISC、高性能

参考ページ

公式ドキュメント

学習リソース

開発ツール