; 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_stub: ; '[' ascii 91 jmp bf_forward align 8 times 8*(']'-'['-1) hlt bf_backward_stub: ; ']' ascii 93 jmp bf_backward align 8 bf_forward: cmp byte [rdi], 0 je bf_jump bf_forward_end: inc rsi jmp rbx bf_backward: cmp byte [rdi], 0 jne bf_jump bf_backward_end: inc rsi jmp rbx bf_jump: sub rsi, bf_program mov rsi, [8*rsi + bf_jumps] 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] _program_loop: movzx rax, byte [rsi] lea rax, [bf_instructions + 8*rax] jmp rax xor rdi, rdi call exit