Skip to content

Instantly share code, notes, and snippets.

@rafaelrc7
Created November 10, 2023 18:54
Show Gist options
  • Save rafaelrc7/c604545279db88fe506b3c85719c9e87 to your computer and use it in GitHub Desktop.
Save rafaelrc7/c604545279db88fe506b3c85719c9e87 to your computer and use it in GitHub Desktop.
Brainfuck x86-64 Linux Assembly Interpreter
/* How to build
* gcc -nostdlib brainfuck.s
* brainfuck source should be in a file called "code.bf"
*/
.set alloc_step, 512
.section .rodata
source_path: .string "code.bf"
.data
source_fd: .long 0
buff: .byte 0
source_ptr: .quad 0
source_len: .quad 0
.bss
.set data_len, 32768
data_buff: .skip data_len, 0
.text
.globl _start
_start:
/**** Allocate memory for brainfuck source code ****/
/* void *source_ptr = mmap(NULL, alloc_step, PROT_READ | PROT_WRITE, MAP_ANONYMOUS, -1, 0) */
xorq %rdi, %rdi
movq $alloc_step, %rsi
movq %rsi, source_len /* source_len = alloc_step */
movq $3, %rdx /* PROT_READ | PROT_WRITE = $3 */
movq $34, %r10 /* MAP_PRIVATE | MAP_ANONYMOUS = $34 */
movq $-1, %r8 /* fd should be -1 if using MAP_ANONYMOUS */
movq $0, %r9 /* offset should be 0 if using MAP_ANONYMOUS */
movq $9, %rax
syscall
cmpq $-4095, %rax
jae exit_syscall_error
movq %rax, source_ptr
/**** Open brainfuck source code file ****/
/* int source_fd = open(source_path, O_RDONLY, O_RDONLY) */
movq $source_path, %rdi
xorq %rsi, %rsi
xorq %rdx, %rdx
movq $2, %rax
syscall
cmpq $-4095, %rax
jae exit_syscall_error
movl %eax, source_fd
/**** Read source code file into memory ****/
xorq %r12, %r12 /* counter of read bytes */
movq source_ptr, %rbx
source_read_loop:
/* read(source_fd, &buff, 1) */
movq source_fd, %rdi
movq $buff, %rsi
movq $1, %rdx
movq $0, %rax
syscall
cmpq $-4095, %rax
jae exit_syscall_error
/* EOF */
testq %rax, %rax
jz read_loop_eof
movb buff, %al
/**** Ignore invalid brainfuck characters ****/
cmpb $'>', %al
je 1f
cmpb $'<', %al
je 1f
cmpb $'+', %al
je 1f
cmpb $'-', %al
je 1f
cmpb $'.', %al
je 1f
cmpb $',', %al
je 1f
cmpb $'[', %al
je 1f
cmpb $']', %al
je 1f
jmp source_read_loop /* ignore char */
1:
/**** Store into memory ****/
movb %al, (%rbx, %r12)
incq %r12 /* increase number of read bytes */
cmpq source_len, %r12
jl 1f
/**** Double source code buffer memory ****/
/*void *source_ptr = mremap(source_ptr, source_len, source_len * 2, MREMAP_MAYMOVE) */
movq source_ptr, %rdi
movq source_len, %rsi
movq %rsi, %rdx
addq %rdx, %rdx
movq %rdx, source_len
movq $1, %r10 /* MREMAP_MAYMOVE = $1 */
movq $25, %rax
syscall
cmpq $-4095, %rax
jae exit_syscall_error
movq %rax, source_ptr
movq %rax, %rbx
1:
jmp source_read_loop
read_loop_eof:
/***** Close Source file *****/
/* close(source_fd) */
movq source_fd, %rdi
movq $3, %rax
syscall
cmpq $-4095, %rax
jae exit_syscall_error
/**** ADD NULL TERMINATOR ****/
/**** Doube buffer size if needed ****/
cmpq source_len, %r12
jl 1f
movq source_ptr, %rdi
movq source_len, %rsi
movq %rsi, %rdx
addq %rdx, %rdx
movq %rdx, source_len
movq $1, %r10 /* MREMAP_MAYMOVE = $1 */
movq $25, %rax
syscall
cmpq $-4095, %rax
jae exit_syscall_error
movq %rax, source_ptr
movq %rax, %rbx
1: /* no resize needed */
xorq %rax, %rax
movb %al, (%rbx, %r12) /* NULL TERMINATOR */
/**** SYNTAX CHECK - Check if brackets are balanced ****/
xorq %rcx, %rcx
xorq %rdx, %rdx
validate_syntax_loop:
movb (%rbx, %rcx), %al
testb %al, %al
jz validate_syntax_loop_end
cmpb $'[', %al
jne 1f
incq %rdx
jmp 2f
1:
cmpb $']', %al
jne 2f
decq %rdx
js exit_failure /* signal bit is set, number is negative, unbalanced brackets */
2:
incq %rcx
jmp validate_syntax_loop
validate_syntax_loop_end:
testq %rdx, %rdx
jnz exit_failure /* Anything other than 0 means unbalanced brackets */
/******** INTERPRETER LOOP ************/
movq %rbx, %r12 /* r12 -> instruction pointer regsiter */
movq $data_buff, %r13 /* r13 -> data pointer regsiter */
interpreter_loop:
movb (%r12), %al
test %al, %al
jz exit_success /* Finished */
cmpb $'>', %al
je inc_data_ptr
cmpb $'<', %al
je dec_data_ptr
cmpb $'+', %al
je inc_byte
cmpb $'-', %al
je dec_byte
cmpb $'.', %al
je print_byte
cmpb $',', %al
je read_byte
cmpb $'[', %al
je jmp_ifz
cmpb $']', %al
je jmp_ifnz
jmp exit_failure /* code should not reach this */
/* > */
inc_data_ptr:
incq %r13
incq %r12
jmp interpreter_loop
/* < */
dec_data_ptr:
decq %r13
incq %r12
jmp interpreter_loop
/* + */
inc_byte:
movb (%r13), %al
incb %al
movb %al, (%r13)
incq %r12
jmp interpreter_loop
/* - */
dec_byte:
movb (%r13), %al
decb %al
movb %al, (%r13)
incq %r12
jmp interpreter_loop
/* . */
print_byte:
/* 1 -> ssize_t write(int fd, const void *buf, size_t count); */
movq $1, %rdi /* stdout = $1 */
movq %r13, %rsi
movq $1, %rdx /* print single byte */
movq $1, %rax
syscall
incq %r12
jmp interpreter_loop
/* , */
read_byte:
/* 0 -> ssize_t read(int fd, void *buf, size_t count); */
movq $0, %rdi /* stdin = $0 */
movq %r13, %rsi
movq $1, %rdx /* read single byte */
movq $0, %rax
syscall
incq %r12
jmp interpreter_loop
/* [ */
jmp_ifz:
movb (%r13), %al
testb %al, %al
jnz 3f
xorq %r10, %r10 /* math counter */
1:
incq %r12
movb (%r12), %al
cmpb $']', %al
je 2f
cmpb $'[', %al
jne 1b
incq %r10
jmp 1b
2:
testq %r10, %r10
jz 3f
decq %r10
jmp 1b
3:
incq %r12
jmp interpreter_loop
/* ] */
jmp_ifnz:
movb (%r13), %al
testb %al, %al
jz 3f
xorq %r10, %r10 /* match counter */
1:
decq %r12
movb (%r12), %al
cmpb $'[', %al
je 2f
cmpb $']', %al
jne 1b
incq %r10
jmp 1b
2:
testq %r10, %r10
jz 3f
dec %r10
jmp 1b
3:
incq %r12
jmp interpreter_loop
exit_success:
call unmap_allocs
cmpq $-4095, %rax
jae exit_syscall_error
/* exit(0) */
xorq %rdi, %rdi
jmp exit
exit_failure:
call unmap_allocs
cmpq $-4095, %rax
jae exit_syscall_error
/* exit(1) */
movq $1, %rdi
jmp exit
exit_syscall_error:
pushq %rax
call unmap_allocs
/* exit(errno) */
popq %rdi
negq %rdi
jmp exit
exit:
/* exit(%rdi) */
movq $60, %rax
syscall
unmap_allocs:
/* munmap(source_ptr, source_len) */
movq source_ptr, %rdi
movq source_len, %rsi
movq $11, %rax
syscall
ret
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment