🗂

Brainfuck interpreter in x86_64 assembly

に公開
2026/02/26

x86_64アセンブリで書かれた、単純なbrainfuck interpreterです。
FreeBSDのシステムコールを使っています。システムコールの仕方、コマンドライン引数の取得の仕方にテンプレとしてどうぞ。

bf.s
; Brainfuck interpreter
; FreeBSD x86_64
; Pure syscall
; Intel syntax (NASM)

; build:
; nasm -f elf64 bf.s
; ld bf.o -o bf

BITS 64
GLOBAL _start

%define SYS_EXIT   1
%define SYS_READ   3
%define SYS_WRITE  4
%define SYS_OPEN   5
%define SYS_CLOSE  6

SECTION .data
usage:      db "Usage: bf <file>",10
usage_len   equ $-usage

SECTION .bss
tape        resb 65536
prog_buf    resb 1048576

SECTION .text

_start:
    ; FreeBSD x86_64: rdi = スタックポインタ (argc, argv[0], argv[1], ...)
    ; rsp は rdi と一致することもあれば 8 バイトずれることもあるため rdi を使う
    mov  r8, rdi               ; r8 = &argc (スタック先頭)
    mov  eax, [r8]             ; argc
    cmp  eax, 2
    jge  open_file

    mov rax, SYS_WRITE
    mov rdi, 2                 ; stderr
    mov rsi, usage
    mov rdx, usage_len
    syscall

    mov rax, SYS_EXIT
    mov rdi, 1
    syscall

open_file:
    mov rdi, [r8+16]           ; argv[1]  (r8+8=argv[0], r8+16=argv[1])
    mov rax, SYS_OPEN
    xor rsi, rsi               ; O_RDONLY
    xor rdx, rdx               ; mode=0
    syscall
    jc  exit_error             ; FreeBSD: CF=1 はエラー
    mov r12, rax               ; fd

    ; read whole file
    mov rax, SYS_READ
    mov rdi, r12
    mov rsi, prog_buf
    mov rdx, 1048576
    syscall
    mov r13, rax               ; program length

    mov rax, SYS_CLOSE
    mov rdi, r12
    syscall

    ; init
    xor r14, r14               ; instruction pointer
    xor r15, r15               ; tape pointer

main_loop:
    cmp r14, r13
    jge exit

    mov al, [prog_buf + r14]

    cmp al, '>'
    je op_inc_ptr
    cmp al, '<'
    je op_dec_ptr
    cmp al, '+'
    je op_inc_val
    cmp al, '-'
    je op_dec_val
    cmp al, '.'
    je op_output
    cmp al, ','
    je op_input
    cmp al, '['
    je op_loop_start
    cmp al, ']'
    je op_loop_end

next:
    inc r14
    jmp main_loop

; ----- operations -----

op_inc_ptr:
    inc r15
    jmp next

op_dec_ptr:
    dec r15
    jmp next

op_inc_val:
    inc byte [tape + r15]
    jmp next

op_dec_val:
    dec byte [tape + r15]
    jmp next

op_output:
    mov rax, SYS_WRITE
    mov rdi, 1
    lea rsi, [tape + r15]
    mov rdx, 1
    syscall
    jmp next

op_input:
    mov rax, SYS_READ
    mov rdi, 0
    lea rsi, [tape + r15]
    mov rdx, 1
    syscall
    cmp rax, 0
    jle exit               ; EOF またはエラー → 終了
    jmp next

; ----- loops -----

op_loop_start:
    cmp byte [tape + r15], 0
    jne next

    mov rcx, 1
.find_end:
    inc r14
    cmp r14, r13
    jge exit

    mov al, [prog_buf + r14]
    cmp al, '['
    je .inc_depth
    cmp al, ']'
    je .dec_depth
    jmp .find_end

.inc_depth:
    inc rcx
    jmp .find_end

.dec_depth:
    dec rcx
    cmp rcx, 0
    jne .find_end
    jmp next

op_loop_end:
    cmp byte [tape + r15], 0
    je next

    mov rcx, 1
.find_start:
    cmp r14, 0
    jle exit
    dec r14
    mov al, [prog_buf + r14]
    cmp al, ']'
    je .inc_depth2
    cmp al, '['
    je .dec_depth2
    jmp .find_start

.inc_depth2:
    inc rcx
    jmp .find_start

.dec_depth2:
    dec rcx
    cmp rcx, 0
    jne .find_start
    jmp next

exit_error:
    mov rax, SYS_EXIT
    mov rdi, 1
    syscall

exit:
    mov rax, SYS_EXIT
    xor rdi, rdi
    syscall

Discussion