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.
172 lines
3.6 KiB
172 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) ret
|
|
bf_increment: ; '+' ascii 43
|
|
inc byte [rdi]
|
|
inc rsi
|
|
ret
|
|
align 8
|
|
bf_in_stub: ; ',' ascii 44
|
|
jmp bf_in
|
|
align 8
|
|
bf_decr: ; '-' ascii 45
|
|
dec byte [rdi]
|
|
inc rsi
|
|
ret
|
|
align 8
|
|
bf_out_stub: ; '.' ascii 46
|
|
jmp bf_out
|
|
ret
|
|
align 8
|
|
times 8*('<'-'.'-1) ret
|
|
bf_left: ; '<' ascii 60
|
|
dec rdi
|
|
inc rsi
|
|
ret
|
|
align 8
|
|
times 8*('>'-'<'-1) ret
|
|
bf_right: ; '>' ascii 62
|
|
inc rdi
|
|
inc rsi
|
|
ret
|
|
align 8
|
|
times 8*('['-'>'-1) ret
|
|
bf_forward_stub: ; '[' ascii 91
|
|
jmp bf_forward
|
|
align 8
|
|
times 8*(']'-'['-1) ret
|
|
bf_backward_stub: ; ']' ascii 93
|
|
jmp bf_backward
|
|
align 8
|
|
|
|
bf_forward:
|
|
cmp byte [rdi], 0
|
|
jne bf_forward_end
|
|
jmp bf_jump
|
|
bf_forward_end:
|
|
inc rsi
|
|
ret
|
|
|
|
bf_backward:
|
|
cmp byte [rdi], 0
|
|
je bf_backward_end
|
|
jmp bf_jump
|
|
bf_backward_end:
|
|
inc rsi
|
|
ret
|
|
|
|
bf_jump:
|
|
sub rsi, bf_program
|
|
mov rsi, [8*rsi + bf_jumps]
|
|
inc rsi
|
|
ret
|
|
|
|
bf_out:
|
|
push rdi
|
|
push rsi
|
|
movzx rdi, byte [rdi]
|
|
call putchar
|
|
xor rdi, rdi
|
|
call fflush
|
|
pop rsi
|
|
pop rdi
|
|
inc rsi
|
|
ret
|
|
|
|
bf_in:
|
|
push rdi
|
|
push rsi
|
|
call getchar
|
|
pop rsi
|
|
pop rdi
|
|
mov byte [rdi], al
|
|
inc rsi
|
|
ret
|
|
|
|
|
|
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]
|
|
_program_loop:
|
|
movzx rax, byte [rsi]
|
|
lea rax, [bf_instructions + 8*rax]
|
|
call rax
|
|
jmp _program_loop
|
|
|
|
xor rdi, rdi
|
|
call exit
|
|
|
|
|
|
|
|
|