Для начала напишем интерпретатор на высокоуровневом языке, например, на 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/
Добавить комментарий