You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
brainfuck/brainfuck.asm

168 lines
3.6 KiB

; Interpreter języka Brainfuck (https://en.wikipedia.org/wiki/Brainfuck)
; Autor: Szymon Karpiński
; Wczytuje program na standardowe wejście.
; Nie sprawdza poprawności programu, zachowanie dla niepoprawnych programów niezdefiniowane.
; W ramach wstępnego przetwarzania programu paruje nawiasy kwadratowe, ale poza tym wykonuje
; program instrukcja po instrukcji, bez wstępnych optymalizacji, na przykład zamiany
; wielokrotnie powtórzonego '+' na dodawanie itd.
; maksymalny rozmiar taśmy na dane
%define PROG_SIZE (1<<20)
; maksymalny rozmiar programu
%define TAPE_SIZE (1<<20)
global main
extern putchar
extern getchar
extern gets
extern exit
extern fflush
section .bss
bf_program: resb PROG_SIZE
bf_jumps: resq PROG_SIZE*8
bf_tape: resb TAPE_SIZE
section .text:
; Podczas interpretacji programu znaczenia rejestrów są następujące:
; - rdi jest wskaźnikiem programu
; - rsi jest wskaźnikiem na aktualne pole na taśmie
; Tablica 8-bajtowych kawałków kodu, w której na miejscu 8*i znajduje się kod zakończony
; instrukcją powrotu, który należy wykonać dla znaku o kodzie ascii równym i. Operacje,
; których kod nie mieści się w 8 bajtach (etykiety *_stub) wykonują skok do pełnej wersji
; kodu.
bf_instructions:
bf_exit: ; NULL kończy program
xor rdi, rdi
call exit
align 8
times 8*('+'-1) hlt
bf_increment: ; '+' ascii 43
inc byte [rdi]
inc rsi
jmp rbx
align 8
bf_in_stub: ; ',' ascii 44
jmp bf_in
align 8
bf_decr: ; '-' ascii 45
dec byte [rdi]
inc rsi
jmp rbx
align 8
bf_out_stub: ; '.' ascii 46
jmp bf_out
align 8
times 8*('<'-'.'-1) hlt
bf_left: ; '<' ascii 60
dec rdi
inc rsi
jmp rbx
align 8
times 8*('>'-'<'-1) hlt
bf_right: ; '>' ascii 62
inc rdi
inc rsi
jmp rbx
align 8
times 8*('['-'>'-1) hlt
bf_forward: ; '[' ascii 91
; jest 16 bajtów miejsca
cmp byte [rdi], 0
jne .dont_jump
mov rsi, [8*rsi + r14]
.dont_jump:
inc rsi
jmp rbx
align 8
bf_backward: ; ']' ascii 93
; więcej niż 8 bajtów, ale jest miejsce
cmp byte [rdi], 0
je .dont_jump
mov rsi, [8*rsi + r14]
.dont_jump:
inc rsi
jmp rbx
bf_out:
push rdi
push rsi
movzx rdi, byte [rdi]
call putchar
xor rdi, rdi
call fflush
pop rsi
pop rdi
inc rsi
jmp rbx
bf_in:
push rdi
push rsi
call getchar
pop rsi
pop rdi
mov byte [rdi], al
inc rsi
jmp rbx
main:
; wczytuję program
lea rdi, [bf_program]
call gets
; Buduję tablicę skoków dla ']'. Jeśli i-ty znak programu to nawias, to na 8*i-tym miejscu w tablicy
; skoków znajdzie się adres odpowiadającego mu nawiasu.
lea rsi, [bf_program]
lea r12, [bf_jumps]
_jump_loop:
cmp byte [rsi], '['
jne .else
push r12
push rsi
jmp .end
.else:
cmp byte [rsi], ']'
jne .end
pop rax
mov qword [r12], rax
pop rax
mov qword [rax], rsi
.end:
inc rsi
add r12, 8
cmp byte [rsi], 0
jnz _jump_loop
lea rsi, [bf_program]
lea rdi, [bf_tape]
lea rbx, [_program_loop] ; stała
lea r13, [bf_program]
shl r13, 3
lea r14, [bf_jumps]
sub r14, r13 ; stała do zamiany adresu programu na adres w bf_jumps
_program_loop:
movzx rax, byte [rsi]
lea rax, [bf_instructions + 8*rax]
jmp rax
xor rdi, rdi
call exit