Low Level Brainfuck. Continuing ...
- Tutorial
Part I
Part II
Part III
We write a brainfuck on TurboAssembler.
Add the output of the data_arr array (“tape” of the Turing machine) to the screen.
Write a program that displays the elements of an arbitrary array through the function 09h interrupt 21h
On the screen we will see the ascii codes of the elements of the data_arr DB array 1,0,2,0,3,0,4,0,0,0,0,0,0,7, '$'
In order to represent the elements of an array as numbers, we will use the div operator .
The command div NUMBER divides the register AX into NUMBER and places the integer part of the division in AL , and the remainder of the division in AH ( NUMBER can be either a memory area or a general purpose register). We
derive the 1st and 2nd elements of the array
In order to display all the elements of the array, we will use the loop command .
Put in the register CX the number of ticks equal to the number of elements in the array and at each tick we add one to the index of the array i .
Next, add a loop that displays the elements of the array as numbers in the main program.
Now HelloWorld looks like this.
Since we do not process numbers greater than 99 , the number 100 is displayed incorrectly, the remaining numbers are displayed correctly.
To handle nested brackets, we will place the opening brackets on the stack, and the closing brackets will be removed from the stack.
Let's write a simple program for working with a stack in Pascal.
Took from here .
You can check here or here .
Modify the push procedure so that when size equals zero, we get a reference to the first element.
Add a "stack" to the main program.
ideone.com
If we encounter an opening bracket, then we simply put its address on the stack, when we meet the closing bracket, we retrieve its address from the stack, and if the value in the current cell is greater than zero, then we return to the opening bracket.
An example of using the normal / “standard” stack is shown in the bf51_stack.pas program .
Add a stack to the main assembler program
Link to github.
Part II
Part III
We write a brainfuck on TurboAssembler.
Add the output of the data_arr array (“tape” of the Turing machine) to the screen.
Write a program that displays the elements of an arbitrary array through the function 09h interrupt 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
INT21h
mov dx,offset data_arr ; указатель на массив символов
mov ah,09h ; вывести строку
int21h
;;;;;;;;;;
MOV AH,2 ; переходим на новую строку
MOV DL,0Ah
INT21h
mov AX, 4c00h ; завершение программы
int21h
end start
On the screen we will see the ascii codes of the elements of the data_arr DB array 1,0,2,0,3,0,4,0,0,0,0,0,0,7, '$'
In order to represent the elements of an array as numbers, we will use the div operator .
The command div NUMBER divides the register AX into NUMBER and places the integer part of the division in AL , and the remainder of the division in AH ( NUMBER can be either a memory area or a general purpose register). We
derive the 1st and 2nd elements of the array
.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 ; вывести строку
;int21h
;;выводим перое число
subAH, 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 ; выводим старший разряд
int21h
mov DL, BH ; выводим младший разряд
int21h
;выводим второе число
subAH, 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 ; выводим старший разряд
int21h
mov DL, BH ; выводим младший разряд
int21h
;;;;;;;;;;
MOV AH,2 ; переходим на новую строку
MOV DL,0Ah
INT 21h
mov AX, 4c00h ; завершение программы
int21h
end start
In order to display all the elements of the array, we will use the loop command .
Put in the register CX the number of ticks equal to the number of elements in the array and at each tick we add one to the index of the array 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 ; вывести строку
;int21h
mov CX, 0Ah
_prev:
;;выводим число
; mov BL,i
subAH, 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 ; выводим старший разряд
int21h
mov DL, BH ; выводим младший разряд
int21h
; выводим пустой символ
subDL, DLint 21h;;;
subBX,BXinci; увеличиваем счётчик
mov BL, i
loop _prev
;;;;;;;;;;
MOV AH,2 ; переходим на новую строку
MOV DL,0Ah
INT 21h
mov AX, 4c00h ; завершение программы
int21h
end start
Next, add a loop that displays the elements of the array as numbers in the main program.
.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
int21h
;;;
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]
int21h
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 тактов
subAL,AL; обнуляем AL
mov i, AL ; обнуляем счётчик
subBX,BX; обнуляем BX
_prev:
; incorrect 1st element
subAH, 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 ; выводим десятки
int21h
mov DL, BH ; выводим единицы
int21h
; выводим пробел (пустой символ)
subDL, DLint 21h;;;
subBX,BXinci; увеличиваем индекс массива
mov BL, i
loop _prev
;;;;;;;;;;
MOV AH,2 ; новая строка
MOV DL,0Ah ; новая строка
INT 21h ; новая строка
mov AX, 4c00h ; завершение программы
int21h
end start
Now HelloWorld looks like this.
Since we do not process numbers greater than 99 , the number 100 is displayed incorrectly, the remaining numbers are displayed correctly.
Nested brackets
To handle nested brackets, we will place the opening brackets on the stack, and the closing brackets will be removed from the stack.
Let's write a simple program for working with a stack in Pascal.
var
a : array[1..10] of integer;
size : integer;
procedurepush(c : integer);begin
size := size + 1;
a[size] := c;
end;
procedurepop;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.
Took from here .
You can check here or here .
Modify the push procedure so that when size equals zero, we get a reference to the first element.
procedurepush(c : integer);begin
a[size+1] := c;
size := size + 1;
end;
Add a "stack" to the main program.
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;
//Stackprocedurepush(c : integer);begin
a[size+1] := c;
size := size + 1;
end;
procedurepop;begin
size := size - 1;
end;
{---------------------------------------------------}begin
j:=1; // нумерация элементов массива начинается с единицы
i:=1;
size := 0; {Изначально стек пуст}//readln(str_arr); //считываем строку//str_arr:='+++[>+++[>+<-]<-]'; // 3*3=9
str_arr:='+++[> +++[>+++[>+<-]<-] <-]'; //3^3=27;
prev:
if i>length(str_arr) thengoto 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]='.') thenwrite(chr(data_arr[j]));
// скобкиif (str_arr[i]='[') then Push(i);
if (str_arr[i]=']') thenbegin
Pop();
if (data_arr[j]>0) thenbegin
i := a[size+1];
goto prev;
end;
end;
i:=i+1;
goto prev;
next:
for k:=1to10dobeginwrite(data_arr[k]);
write(' ');
end;
end.
ideone.com
If we encounter an opening bracket, then we simply put its address on the stack, when we meet the closing bracket, we retrieve its address from the stack, and if the value in the current cell is greater than zero, then we return to the opening bracket.
An example of using the normal / “standard” stack is shown in the bf51_stack.pas program .
Add a stack to the main assembler program
.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
int21h
;;;
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] ; да, увеличиваем значение в ячейке на 1next:
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]
int21h
next4:
cmp DL, 5Bh ; ячейка содержит [
jne next5 ; нет, переходим на метку next5
;subDX,DXmovAL, i; иначе загружаем
push AX
next5:
cmp DL, 5Dh ; ячейка содержит ]
jne next6 ; нет, переходим на метку next6
subAX,AXpopAXmovBL, jmovDL, data_arr[BX]
cmpDL, 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 тактов
subAL,AL; обнуляем AL
mov i, AL ; обнуляем счётчик
subBX,BX; обнуляем BX
_prev:
; incorrect 1st element
subAH, 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 ; выводим десятки
int21h
mov DL, BH ; выводим единицы
int21h
; выводим пробел (пустой символ)
subDL, DLint 21h;;;
subBX,BXinci; увеличиваем индекс массива
mov BL, i
loop _prev
;;;;;;;;;;
MOV AH,2 ; новая строка
MOV DL,0Ah ; новая строка
INT 21h ; новая строка
;;;;;;;;;;;;;;;
mov AX, 4c00h ; завершение программы
int21h
END start
Link to github.