Низкоуровневый Brainfuck. Продолжение…

от автора

Часть I
Часть II
Часть III

Пишем brainfuck на TurboAssembler’e.

Добавим вывод массива data_arr («ленту» машины Тьюринга) на экран.
Напишем программу, выводящую на экран элементы произвольного массива посредством функции 09h прерывания 21h.

.model tiny                 ; ascii-decoder.asm jumps .data  data_arr DB 1,0,2,0,3,0,4,0,5,0,6,0,7,'$' ; данные  .code ORG    100h start: ;Подготовим все необходимое   mov AX, @data          ; настраиваем сегмент данных                                          mov DS,AX ;;;;;;;;;;;;;;;;  MOV    AH,2              ; переходим на новую строку  MOV    DL,0Ah       INT    21h  mov dx,offset data_arr     ; указатель на массив символов mov ah,09h		            ; вывести строку int 21h         ;;;;;;;;;;  MOV    AH,2       ; переходим на новую строку  MOV    DL,0Ah       INT    21h             mov AX, 4c00h      ; завершение программы    int 21h  end start 

На экране мы увидим ascii-коды массива data_arr DB 1,0,2,0,3,0,4,0,5,0,6,0,7,’$’


Для того, чтобы представить элементы массива в виде чисел, будем использовать опрератор div.

Команда div ЧИСЛО делит регистр AX на ЧИСЛО и помещает целую часть от деления в AL, а остаток от деления в AH (ЧИСЛОМ может быть либо область памяти, либо регистр общего назначения)

Выведем 1ый и 2ой элементы массива

.model tiny                 ; ascii-decoder.asm jumps .data  data_arr DB 10,12,0,0,0,0,0,0,0,0,'$' ; данные  .code ORG    100h start: ;Подготовим все необходимое   mov AX, @data          ; настраиваем сегмент данных                                          mov DS,AX ;;;;;;;;;;;;;;;;  MOV    AH,2              ; переходим на новую строку  MOV    DL,0Ah       INT    21h  ;mov dx,offset data_arr  	; указатель на массив символов ;mov ah,09h		            ; вывести строку ;int 21h          ;;выводим перое число sub AH, AH          ; обнуляем AH mov AL, data_arr    ; делимое mov BL, 10          ; делитель div BL              ; теперь в AL=десятки, в AH=единицы mov BX,AX add BX,3030h mov AH,2            ; функция вывода символа прерывания 21h  mov DL,BL           ; выводим старший разряд  int 21h  mov DL, BH          ; выводим младший разряд int 21h ;выводим второе число sub AH, AH           ; обнуляем AH mov AL, data_arr+1   ; делимое mov BL, 10           ; делитель div BL               ; теперь в AL=десятки, в AH=единицы mov BX,AX add BX,3030h mov AH,2            ; функция вывода символа прерывания 21h  mov DL,BL           ; выводим старший разряд  int 21h  mov DL, BH          ; выводим младший разряд int 21h ;;;;;;;;;;  MOV    AH,2       ; переходим на новую строку  MOV    DL,0Ah       INT    21h             mov AX, 4c00h      ; завершение программы    int 21h  end start 

Для того, чтобы вывести все элементы массива, будем использовать команду loop.
Поместим в регистр CX количество тактов, равное количеству элементов массива и на каждом такте будем прибавлять единицу к индексу массива i.

.model tiny                 ; ascii-decoder1.asm jumps .data  data_arr DB 3,5,6,7,0,11,12,13,0,20,'$' ; данные  i DB 0,'$'   .code ORG    100h start: ;Подготовим все необходимое   mov AX, @data               ; настраиваем сегмент данных                                          mov DS,AX ;;;;;;;;;;;;;;;;  MOV    AH,2                  ; переходим на новую строку  MOV    DL,0Ah       INT    21h  ;mov dx,offset data_arr       ; указатель на массив символов ;mov ah,09h		      ; вывести строку ;int 21h                          mov CX, 0Ah _prev: ;;выводим число ; mov BL,i  sub AH, AH             ; обнуляем AH  mov AL, data_arr[BX]    ; делимое  mov BL, 10             ; делитель  div BL                 ; теперь в AL=десятки, в AH=единицы  mov BX,AX  add BX,3030h  mov AH,2            ; функция вывода символа прерывания 21h   mov DL,BL           ; выводим старший разряд   int 21h   mov DL, BH          ; выводим младший разряд  int 21h ; выводим пустой символ sub DL, DL           int 21h  ;;; sub BX,BX inc i                ; увеличиваем счётчик           mov BL, i loop _prev ;;;;;;;;;;  MOV    AH,2       ; переходим на новую строку  MOV    DL,0Ah       INT    21h             mov AX, 4c00h      ; завершение программы    int 21h  end start 

Далее, добавим цикл, отображающий элементы массива в виде чисел, в основную программу.

.model tiny jumps .data  str_arr DB 256h DUP('$')	          data_arr DB 0,0,0,0,0,0,0,0,0,0,'$'     i DB 0,'$'                             j DB 0,'$'                             i_stor DB 0,'$'  .code ORG    100h start:    mov AX, @data                                                 mov DS,AX   ;;;   mov ah, 3fh             mov cx, 100h	          mov dx,OFFSET str_arr   int 21h   ;;;                mov DL, str_arr       prev:  cmp DL, 24h            je  exit_loop                     cmp DL, 2Bh                                  jne next               mov BL, j                          inc data_arr[BX]      next:   cmp DL, 2Dh                                  jne next1               mov BL, j   dec data_arr[BX]      next1:   cmp DL, 3Eh           jne next2              inc j                next2:   cmp DL, 3Ch           jne next3              dec j                next3:   cmp DL, 2Eh           jne next4             mov AH,2              mov BL, j  mov DL, data_arr[BX]  int 21h next4:  cmp DL, 5Bh           jne next5             ;mov BL, j  ;mov DL, data_arr[BX]  ;cmp DL, 00              ;jz next5              mov DL, i              mov i_stor, Dl       next5:  cmp DL, 5Dh           jne next6             mov BL, j  mov DL, data_arr[BX]  cmp DL, 00              jz next6              mov DL, i_stor         mov i, DL             next6:  inc i                 mov BL, i  mov DL, str_arr[BX]  ; loop prev            jmp prev  exit_loop:   ;;;;;;;;;;;;;;;;  MOV    AH,2         ; новая строка  MOV    DL,0Ah       ; новая строка  INT    21h          ; новая строка  ; output data_arr     mov CX, 0Ah          ; 10 тактов sub AL,AL            ; обнуляем AL mov i, AL            ; обнуляем счётчик sub BX,BX            ; обнуляем BX _prev: ; incorrect 1st element  sub AH, AH             ; обнуляем AH  mov AL, data_arr[BX]   ; делимое  ;mov AL, data_arr+1   mov BL, 10             ; делитель  div BL                 ; частное  AL=десятки и AH=единицы  mov BX,AX  add BX,3030h  mov AH,2            ; функция вывода 2 прерывания 21h   mov DL,BL           ; выводим десятки    int 21h   mov DL, BH          ; выводим единицы  int 21h                ; выводим пробел (пустой символ) sub DL, DL           int 21h  ;;; sub BX,BX inc i                ; увеличиваем индекс массива mov BL, i loop _prev ;;;;;;;;;;  MOV    AH,2       ; новая строка  MOV    DL,0Ah     ; новая строка  INT    21h        ; новая строка      mov AX, 4c00h     ; завершение программы  int 21h  end start 

Теперь HelloWorld выглядит так

Поскольку мы не обрабатываем числа больше 99, то число 100 отображается некорректно, остальные числа отображаются корректно.

Вложенные скобки

Для обработки вложенных скобок будем открывающие скобки помещать в стэк, а закрывающие извлекать из стека.

Напишем простую программу работы со стеком на Паскале.

var  a : array[1..10] of integer;  size : integer;  procedure push(c : integer);  begin   size := size + 1;   a[size] := c;   end;    procedure pop;  begin   size := size - 1;  end;  begin   size := 0;    Push(1);   writeln(a[size]);   Push(2);   writeln(a[size]);   Push(3);   writeln(a[size]);   Pop();   writeln(a[size]);   Pop();   writeln(a[size]); end. 

Взял отсюда
Проверить можно здесь или здесь.

Изменим процедуру push так, чтобы при size равном нулю мы получали ссылку на первый элемент.

procedure push(c : integer);  begin   a[size+1] := c;    size := size + 1;  end; 

Добавим «стэк» в основную программу.

Program bf5_stack;    LABEL prev,next; var  a : array[1..10] of integer;  size : integer;  data_arr:array[1..10] of integer;    // массив данных  str_arr: string;                     // команды    i,j,k: integer;                      // индексы строки и массива  i_stor: integer;   //Stack procedure push(c : integer);  begin   a[size+1] := c;    size := size + 1;  end;    procedure pop;  begin   size := size - 1;  end; {---------------------------------------------------} begin  j:=1;   // нумерация элементов массива начинается с единицы  i:=1;  size := 0; {Изначально стек пуст}  //readln(str_arr);       //считываем строку  //str_arr:='+++[>+++[>+<-]<-]'; //2*2=4  str_arr:='+++[> +++[>+++[>+<-]<-] <-]'; //3^3=9;    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 Push(i);                if (str_arr[i]=']') then       begin       Pop();       if (data_arr[j]>0) then         begin         i := a[size+1];         goto prev;        end;       end;  i:=i+1;  goto prev;  next: for k:=1 to 10 do begin  write(data_arr[k]); write(' '); end; end. 

ideone.com
Если мы встречаем открывающуб скобку, то просто помещаем её адрес в стэк, когда мы встречаем закрывающую скобку, то извлекаем её адрес из стека, если при этом значение в текущей ячейке больше нуля, то возвращаемся на открывающую скобку.
Пример использования нормального/«стандартного» стэка показан в программе bf51_stack.pas

«Добавим» стэк к основной ассемблерной программе

.model tiny                       ; bf7_stack_decoder.asm                       jumps .data  str_arr DB 256h 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,'$'  .code ORG    100h start:  ;Подготовим все необходимое   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, 100h        ; 256 тактов prev:  cmp DL, 24h ; символ '$'  je  exit_loop  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  ;sub DX,DX  mov AL, i           ; иначе загружаем  push AX  next5:  cmp DL, 5Dh         ; ячейка содержит ]  jne next6           ; нет, переходим на метку next6  sub AX,AX  pop AX  mov BL, j  mov DL, data_arr[BX]  cmp DL, 00          ; да, проверяем текущий элемент data_arr на ноль    jz next6            ; если ноль, прыгаем дальше  mov i, AL           ; в i_stor значение переменной i  mov BL, i  mov DL, str_arr[BX]     jmp prev next6:  inc i               ; переходим к следующей команде  mov BL, i  mov DL, str_arr[BX]     jmp prev  exit_loop:    ;Выод ascii-символов чисел  MOV    AH,2         ; новая строка  MOV    DL,0Ah       ; новая строка  INT    21h          ; новая строка  ; output data_arr     mov CX, 0Ah          ; 10 тактов sub AL,AL            ; обнуляем AL mov i, AL            ; обнуляем счётчик sub BX,BX            ; обнуляем BX _prev: ; incorrect 1st element  sub AH, AH             ; обнуляем AH  mov AL, data_arr[BX]   ; делимое  ;mov AL, data_arr+1   mov BL, 10             ; делитель  div BL                 ; частное  AL=десятки и AH=единицы  mov BX,AX  add BX,3030h  mov AH,2            ; функция вывода 2 прерывания 21h   mov DL,BL           ; выводим десятки    int 21h   mov DL, BH          ; выводим единицы  int 21h                ; выводим пробел (пустой символ) sub DL, DL           int 21h  ;;; sub BX,BX inc i                ; увеличиваем индекс массива mov BL, i loop _prev ;;;;;;;;;;  MOV    AH,2       ; новая строка  MOV    DL,0Ah     ; новая строка  INT    21h        ; новая строка   ;;;;;;;;;;;;;;;          mov AX, 4c00h      ; завершение программы    int 21h  END    start 

Ссылка на github.


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


Комментарии

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

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