Интерпетатор Brainfuck на TASM

от автора

Для начала напишем интерпретатор на высокоуровневом языке, например, на Pascal’e.

Массив data_arr будет представлять память данных, строка str_arr будет содержать команды.
Напишем программу, выводящую символ, ascii-код которого соответствует количеству + (поэтому нам нужны будут только команды + и .)

var  data_arr:array[1..10] of integer;    // массив данных  str_arr: string;                     // команды    i, j: integer;                       // индексы строки и массива begin  j:=1;                  // нумерация элементов массива (внезапно) начинается с единицы  readln(str_arr);       //считываем строку   for i:=1 to length(str_arr) do begin    // в цикле обрабатываем строку   if (str_arr[i]='+') then data_arr[j]:= data_arr[j]+1;   if (str_arr[i]='.') then write(chr(data_arr[j]));  end; end. 

Код +++++++++++++++++++++++++++++++++. выдаст ! (ascii-код символа ! равен 33 ).

Работоспособность программы можно проверить в online ide ideone.com
Вот здесь можно выполнить отладку в пошаговом режиме.

Далее, заменим цикл for оператором goto и добавим команды $-<>$
В конце будем выводить массив data_arr

LABEL prev,next; var  data_arr:array[1..10] of integer;    // массив данных  str_arr: string;                     // команды    i,j,k: integer;                       // индексы строки и массива begin  i:=1;                   j:=1;  readln(str_arr);       //считываем строку  prev:  if i>length(str_arr) then goto next;      if (str_arr[i]='+') then data_arr[j]:= data_arr[j]+1;     if (str_arr[i]='-') then data_arr[j]:= data_arr[j]-1;     if (str_arr[i]='>') then j:=j+1;     if (str_arr[i]='<') then j:=j-1;     if (str_arr[i]='.') then write(chr(data_arr[j]));        i:=i+1;  goto prev;  next: for k:=1 to 10 do begin  write(data_arr[k]); write(' '); end; end. 

Код $+>++>+++$ выдаст 1 2 3 0 0 0 0 0 0 0
Код $+>++>+++-$ выдаст 1 2 2 0 0 0 0 0 0 0
ideone.com

Далее, добавим [ и ]
Добавим ещё одну переменную i_stor.
Если текущий элемент прошёл проверку на [, то проверяем текущий элемент массива data_arr на ноль, и, если элемент больше нуля, загружаем в i_stor значение из переменной i.
При обработке закрывающей скобки ], если data_arr не ноль, в переменную i из переменной i_stor загружаем адрес открывающей скобки [

LABEL prev,next; var  data_arr:array[1..10] of integer;    // массив данных  str_arr: string;                     // команды    i,j,k: integer;                       // индексы строки и массива  i_stor: integer;  begin  j:=1;                   i:=1;  readln(str_arr);       //считываем строку  prev:  if i>length(str_arr) then goto next;      if (str_arr[i]='+') then data_arr[j]:= data_arr[j]+1;     if (str_arr[i]='-') then data_arr[j]:= data_arr[j]-1;     if (str_arr[i]='>') then j:=j+1;     if (str_arr[i]='<') then j:=j-1;     if (str_arr[i]='.') then write(chr(data_arr[j]));     if (str_arr[i]='[') then      begin         if data_arr[j]>0 then i_stor:=i;      end;     if (str_arr[i]=']') then      begin         if data_arr[j]>0 then         begin        i:=i_stor;        goto prev;        end;      end;       i:=i+1;  goto prev;  next: for k:=1 to 10 do begin  write(data_arr[k]); write(' '); end; end. 

Код $+++++[>+<-]$ переносит число 5 в соседнюю ячейку 0 5 0 0 0 0 0 0 0 0
ideone.com
Код HelloWorld выглядит так ideone.com

Перейдем к ассемблеру

Чтобы организовать цикл (loop), необходимо поместить в регистр CX количество тактов цикла и поставить метку, на которую будет сделан переход по завершении такта (по команде loop).

mov CX, 28h    ; кол-во тактов цикла prev:                ; метка цикла ; выполняем  ; операции ; внутри цикла loop prev          ; возвращаемся на метку prev 

Создадим массив команд str_arr, поместим туда $+++$
Создадим массив данных data_arr, (для наглядности) поместим туда 1,1,1,1,1,1,1,1,1,1

В цикле сравниваем текущий символ с символом $+$ и, если символы равны, увеличиваем значение в текущей ячейке на 1.

text segment                      ; bf1.asm  assume cs:text, ds:data, ss:stk begin:   ;Подготовим все необходимое   mov AX,data          ; настраиваем сегмент данных                                          mov DS,AX                mov DL, str_arr      ; загружаем в DL 1ую команду    mov CX, 0Ah          ; 10 тактов prev:                      cmp DL, 2Bh           ; ячейка содержит +  jne next              ; нет, переходим на метку next    mov BL, 00h           ; загружаем в BL индекс   inc data_arr[BX]      ; да, увеличиваем  значение в ячейке на 1  next:  inc i                 ; переходим на следующий символ массива команд  mov BL, i  mov DL, str_arr [BX]     loop prev              mov AX, 4c00h        ; завершение программы     int 21h  text ends  data segment            str_arr DB  2Bh,2Bh,2Bh,'$' ; код +++ data_arr DB 1,1,1,1,1,1,1,1,1,1,'$' ; данные i DB 0                  ;индекс элемента массива команд  data ends  stk segment stack        db 100h dup (0)       ; резервируем 256 ячеек stk ends end begin       

Ассемблирование (трансляция) выполняется командой tasm.exe bf1.asm
Линковка выполняется командой tlink.exe bf1.obj

После выполнения программы в отладчике TurboDebagger видно, что начиная с адреса 0130 расположены команды $+++$
Далее идет массив данных, в котором мы изменили первый элемент, далее идет переменная i, которая после выполнения цикла стала равна 0Ah.

Добавим команды $ -<>. $
Для того, чтобы вывести одиночный символ с помощью функции 02h прерывания int 21h, необходимо (перед вызовом прерывания) поместить код символа в регистр DL.

 mov AH,2   mov DL, код символа  int 21h 

Напишем программу целиком

text segment                     ; bf2.asm  assume cs:text,ds:data, ss:stk begin:   ;Подготовим все необходимое   mov AX,data        ; настраиваем сегмент данных                                          mov DS,AX                mov DL, str_arr    ; загружаем в DL 1ую команду    mov CX, 0Ah        ; 10 тактов prev:                      cmp DL, 2Bh         ; ячейка содержит +  jne next            ; нет, переходим на метку next    mov BL, j           ; загружаем в BL индекс данных  inc data_arr[BX]    ; да, увеличиваем  значение в ячейке на 1 next:   cmp DL, 2Dh         ; ячейка содержит -  jne next1           ; нет, переходим на метку next1    mov BL, j   dec data_arr[BX]      next1:   cmp DL, 3Eh        ; ячейка содержит >  jne next2          ; нет, переходим на метку next2    inc j              ; да, переходим на сдедующий элемент массива data_arr next2:   cmp DL, 3Ch        ; ячейка содержит <  jne next3          ; нет, переходим на метку next3    dec j              ; да, переходим на предыдущий элемент массива data_arr next3:   cmp DL, 2Eh        ; ячейка содержит .  jne next4          ; нет, переходим на метку next4    mov AH,2           ; да, выводим содержимое ячейки  mov BL, j  mov DL, data_arr[BX]  int 21h  next4:  inc i                 ; переходим на следующий символ массива команд  mov BL, i  mov DL, str_arr [BX]     loop prev             mov AX, 4c00h        ; завершение программы    int 21h  text ends  data segment            str_arr DB  2Bh,3Eh,2Bh,2Bh,'$' ; код +>++ data_arr DB 0,0,0,0,0,0,0,0,0,0,'$'  ; данные i DB 0, '$'                              ;индекс элемента массива команд  j DB 0, '$'                             ;индекс элемента массива данных data ends  stk segment stack        db 100h dup (0)       ; резервируем 256 ячеек stk ends end begin      


Цикл работает так:
если текущий элемент строки str_arr не $+$ то перепрыгиваем на метку next: (иначе выполняем $+$)
если текущий элемент строки str_arr не $-$ то перепрыгиваем на метку next1:
если текущий элемент строки str_arr не $>$ то перепрыгиваем на метку next2:
если текущий элемент строки str_arr не $<$ то перепрыгиваем на метку next3:
если текущий элемент строки str_arr не $.$ то перепрыгиваем на метку next4:
После метки next4: увеличиваем индекс строки str_arr и прыгаем в начало цикла — на метку prev:

Далее, добавим [ и ]
Добавим переменную i_stor.
Если текущий элемент прошёл проверку на [, то проверяем текущий элемент массива data_arr на ноль, и, если элемент равен нулю, перепрыгиваем дальше (на следующую метку), в противном случае загружаем в i_stor значение из переменной i.

next4:  cmp DL, 5Bh         ; ячейка содержит [  jne next5           ; нет, переходим на метку next5  mov BL, j  mov DL, data_arr[BX]  cmp DL, 00          ; да, проверяем текущий элемент data_arr на ноль    jz next5            ; если ноль, прыгаем дальше  mov DL, i           ; иначе загружаем   mov i_stor, Dl      ; в i_stor значение переменной i  next5: 

При обработке закрывающей скобки ], если data_arr не ноль, то в переменную i из переменной i_stor загружаем адрес открывающей скобки [

next5:  cmp DL, 5Dh         ; ячейка содержит ]  jne next6           ; нет, переходим на метку next6  mov BL, j  mov DL, data_arr[BX]  cmp DL, 00          ; да, проверяем текущий элемент data_arr на ноль    jz next5            ; если ноль, прыгаем дальше  mov DL, i_stor      ; иначе загружаем   mov i, Dl           ; в i_stor значение переменной i  next6: 

Проверим код $+++++[>+<-]$

text segment                     ; bf4.asm assume cs:text, ds:data, ss:stk begin:   ;Подготовим все необходимое   mov AX,data        ; настраиваем сегмент данных                                          mov DS,AX                mov DL, str_arr    ; загружаем в DL 1ую команду    mov CX, 50h        ; 80 тактов prev:                      cmp DL, 2Bh         ; ячейка содержит +  jne next            ; нет, переходим на метку next    mov BL, j           ; загружаем в BL индекс данных  inc data_arr[BX]    ; да, увеличиваем  значение в ячейке на 1 next:   cmp DL, 2Dh         ; ячейка содержит -  jne next1           ; нет, переходим на метку next1    mov BL, j   dec data_arr[BX]    ;BX, но не Bl  next1:   cmp DL, 3Eh         ; ячейка содержит >  jne next2           ; нет, переходим на метку next2    inc j               ; да, переходим на сдедующий элемент массива data_arr next2:   cmp DL, 3Ch         ; ячейка содержит <  jne next3           ; нет, переходим на метку next3    dec j               ; да, переходим на предыдущий элемент массива data_arr next3:   cmp DL, 2Eh         ; ячейка содержит .  jne next4           ; нет, переходим на метку next4    mov AH,2            ; да, выводим содержимое ячейки  mov BL, j  mov DL, data_arr[BX]  int 21h next4:  cmp DL, 5Bh         ; ячейка содержит [  jne next5           ; нет, переходим на метку next5  mov BL, j  mov DL, data_arr[BX]  cmp DL, 00          ; да, проверяем текущий элемент data_arr на ноль    jz next5            ; если ноль, прыгаем дальше  mov DL, i           ; иначе загружаем   mov i_stor, Dl      ; в i_stor значение переменной i  next5:  cmp DL, 5Dh         ; ячейка содержит ]  jne next6           ; нет, переходим на метку next6  mov BL, j  mov DL, data_arr[BX]  cmp DL, 00          ; да, проверяем текущий элемент data_arr на ноль    jz next5            ; если ноль, прыгаем дальше  mov DL, i_stor      ; иначе загружаем   mov i, Dl           ; в i_stor значение переменной i  next6:  inc i               ; переходим к следующей команде  mov BL, i  mov DL, str_arr[BX]     loop prev          ; прыгаем на метку prev:            mov AX, 4c00h      ; завершение программы    int 21h  text ends  data segment             str_arr DB  2Bh,2Bh,2Bh,2Bh,5Bh, 3Eh,2Bh,3Ch,2Dh ,5Dh, '$'   ; код ++++[>+<-]  data_arr DB 0,0,0,0,0,0,0,0,0,0,'$'  ; данные  i DB 0,'$'                              ;индекс элемента массива команд   j DB 0,'$'                            ;индекс элемента массива данных  i_stor DB 0,'$' data ends  stk segment stack        db 100h dup (0)       ; резервируем 256 ячеек stk ends end begin       

Добавим функцию ввода строки 3fh прерывания 21h

text segment                     ;bf5.asm assume cs:text,ds:data, ss: stk begin:    ;Подготовим все необходимое   mov AX,data        ; настраиваем сегмент данных                                          mov DS,AX   ;;;   mov ah, 3fh        ; функция ввода   mov cx, 100h	     ; 256 символов   mov dx,OFFSET str_arr   int 21h   ;;;                mov DL, str_arr    ; загружаем в DL 1ую команду    mov CX, 900h        ; 2304 тактов prev:                      cmp DL, 2Bh         ; ячейка содержит +  jne next            ; нет, переходим на метку next    mov BL, j           ; загружаем в BL индекс данных  inc data_arr[BX]    ; да, увеличиваем  значение в ячейке на 1 next:   cmp DL, 2Dh         ; ячейка содержит -  jne next1           ; нет, переходим на метку next1    mov BL, j   dec data_arr[BX]    ;BX, но не Bl  next1:   cmp DL, 3Eh         ; ячейка содержит >  jne next2           ; нет, переходим на метку next2    inc j               ; да, переходим на следующий элемент массива data_arr next2:   cmp DL, 3Ch         ; ячейка содержит <  jne next3           ; нет, переходим на метку next3    dec j               ; да, переходим на предыдущий элемент массива data_arr next3:   cmp DL, 2Eh         ; ячейка содержит .  jne next4           ; нет, переходим на метку next4    mov AH,2            ; да, выводим содержимое ячейки  mov BL, j  mov DL, data_arr[BX]  int 21h next4:  cmp DL, 5Bh         ; ячейка содержит [  jne next5           ; нет, переходим на метку next5  mov BL, j  mov DL, data_arr[BX]  cmp DL, 00          ; да, проверяем текущий элемент data_arr на ноль    jz next5            ; если ноль, прыгаем дальше  mov DL, i           ; иначе загружаем   mov i_stor, Dl      ; в i_stor значение переменной i next5:  cmp DL, 5Dh         ; ячейка содержит ]  jne next6           ; нет, переходим на метку next6  mov BL, j  mov DL, data_arr[BX]  cmp DL, 00          ; да, проверяем текущий элемент data_arr на ноль    jz next5            ; если ноль, прыгаем дальше  mov DL, i_stor      ; иначе загружаем   mov i, Dl           ; в i_stor значение переменной i  next6:  inc i               ; переходим к следующей команде  mov BL, i  mov DL, str_arr[BX]     loop prev          ; прыгаем на метку prev:      MOV    AH,2       ; переходим на новую строку  MOV    DL,0Ah       INT    21h          mov AX, 4c00h      ; завершение программы    int 21h  text ends  data segment              str_arr DB 100h DUP('$')	; буфер на 256 символов  data_arr DB 0,0,0,0,0,0,0,0,0,0,'$'  ; данные  i DB 0,'$'                              ;индекс элемента массива команд   j DB 0,'$'                            ;индекс элемента массива данных  i_stor DB 0,'$' data ends  stk segment para stack   db 100h dup (0)       ; резервируем 256 ячеек stk ends end begin       

Возможно, сначала надо вычислять длину строки и по этому значению вычислять количество тактов цикла (я пока не разобрался).

Ссылка на github с листингами программ.


ссылка на оригинал статьи https://habr.com/post/423121/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *