Parte IParte IIParte IIINeste artigo, escreveremos um compilador Brainfuck no TurboAssembler
Aqui você pode depurar os programas bf em um modo passo a passo.
Primeiro, escreveremos o intérprete em alguma linguagem de alto nível, por exemplo, em Pascal.
A matriz
data_arr representará a memória de dados, a string
str_arr conterá os comandos.
Escreveremos um programa que exibe um caractere cujo código ascii corresponde ao número
+ (portanto, precisamos apenas dos comandos
+ e
. )
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.
código bf +++++++++++++++++++++++++++++++++++. vai desistir
! (o código ascii do personagem
! é 33).
O programa pode ser verificado no site ide
ideone.comEm seguida, substitua o loop
for por
goto e adicione os comandos
No final, produziremos a matriz
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.
Código
dará 1 2 3 0 0 0 0 0 0 0
Código
dará 1 2 2 0 0 0 0 0 0 0
ideone.comEm seguida, adicione
[ e
]Adicione outra variável
i_stor .
Se o elemento atual passou no teste em
[ , então verificamos o elemento atual da matriz
data_arr como zero e, se o elemento for maior que zero, carregue o valor da variável
i no
i_stor .
Ao processar o colchete de fechamento
] , se
data_arr não
for zero, carregaremos o endereço do colchete de abertura na variável
i da variável
i_stor [Em seguida, vá para o comando
i: = i + 1;Se antes disso um valor de
i_stor foi carregado em
i (durante a verificação
) ), depois do despejo estaremos em
[ (caso contrário estaremos em
] )
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; end; end; i:=i+1; goto prev; next: for k:=1 to 10 do begin write(data_arr[k]); write(' '); end; end.
Código
transfere o número 5 para a próxima célula 0 5 0 0 0 0 0 0 0 0 0
ideone.comO código HelloWorld se parece com
ideone.comVamos para o assembler
Para organizar um loop (loop), é necessário colocar o número de ciclos de clock no registro CX e colocar um rótulo para o qual a transição será feita no final do ciclo (pelo comando loop).
mov CX, 28h ; - prev: ; ; ; ; loop prev ; prev
Crie uma matriz de comandos
str_arr , coloque lá
Crie um array de dados
data_arr , (para maior clareza) coloque lá 1,1,1,1,1,1,1,1,1,1,1,1
Em um loop, compare o caractere atual com o caractere
e, se os caracteres forem iguais, aumente o valor na célula atual em 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
A montagem (tradução) é realizada pelo
comando tasm.exe bf1.asmA vinculação é feita pelo
tlink.exe bf1.objDepois de executar o programa no depurador TurboDebagger, você pode ver que a partir do endereço 0130, os comandos estão localizados
A seguir, é a matriz de dados na qual alteramos o primeiro elemento, depois a variável
i , que após a execução do loop se torna igual a 0Ah.

Adicionar comandos
Para gerar um único caractere usando a função de interrupção
02h int 21h , é necessário (antes de chamar a interrupção) colocar o código do caractere no registro
DL .
mov AH,2 mov DL, int 21h
Vamos escrever o programa inteiro
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

O loop funciona assim:
se o elemento atual de
str_arr não
for depois pule para o
próximo rótulo
: (caso contrário, execute
)
se o elemento atual de
str_arr não
for então pule para o rótulo
next1:se o elemento atual de
str_arr não
for então pule para o rótulo
next2:se o elemento atual de
str_arr não
for então pule para o rótulo
next3:se o elemento atual de
str_arr não
for então pule para o rótulo
next4:Após o rótulo
next4: aumente o índice da string
str_arr e pule para o início do loop - para o label
anterior:Em seguida, adicione
[ e
]Adicione a variável
i_stor .
Se o elemento atual passou no teste em
[ , então verificamos o elemento atual da matriz
data_arr para zero e, se o elemento é zero, saltamos mais (para o próximo rótulo); caso contrário, carregamos o valor da variável
i no
i_stor .
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:
Ao processar o colchete de fechamento
] , se
data_arr não
for zero, carregue o endereço do colchete de abertura na variável
i da variável
i_stor [ next5: cmp DL, 5Dh ; ] jne next6 ; , next6 mov BL, j mov DL, data_arr[BX] cmp DL, 00 ; , data_arr jz next6 ; , mov DL, i_stor ; mov i, Dl ; i_stor i next6:
Verifique o código
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 next6 ; , 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

Adicionar função à linha de entrada
3fh interromper
21h mov ah, 3fh ; mov cx, 100h ; 256 mov dx,OFFSET str_arr int 21h
Sairemos do loop após atingir o final da string
'$' .
Para fazer isso, compararemos o caractere atual com o caractere
'$' cmp DL, 24h ; '$' je exit_loop
Substitua o loop do loop pelo comando jmp.
text segment 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, 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 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 next6 ; , mov DL, i_stor ; mov i, Dl ; i_stor i ; prev: next6: inc i ; mov BL, i mov DL, str_arr[BX] ; loop prev ; prev: jmp prev exit_loop: MOV AH,2 ; MOV DL,0Ah INT 21h mov AX, 4c00h ; int 21h text ends data segment 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,'$' data ends stk segment para stack db 100h dup (0) ; 256 stk ends end begin
Durante a compilação, obtemos um erro
Salto relativo fora do intervalo em 0001h bytes
O fato é que os comandos
je / jne podem saltar apenas após algumas linhas do programa (cada linha ocupa de 1 a 5 bytes na memória).

Saltos longos até o final do programa
je / jne não podem ser feitos.
Portanto, substituímos a expressão
cmp DL, 24h ; '$' je exit_loop ... exit_loop:
expressão
cmp DL, 24h ; '$' jne exit_ jmp exit_loop exit_ ... exit_loop:
Portanto, se o caractere atual corresponder a
$ , vá para o label
exit_loop: com o comando
jmp , caso contrário,
pularemos o comando
jmp .
O comando
jmp pode fazer uma transição
curta relativa intra-segmento (transição menor que 128 bytes, ou seja, IP: = IP + i8) ou uma
transição longa relativa intra-segmento (transição menor que 327 bytes, ou seja, IP: = IP + i16).
Por padrão, o comando
jmp faz um salto em distância relativo, que é o que precisamos (em geral, você pode simplesmente adicionar a diretiva jumps ao início do programa).
;jumps text segment 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, 100h ; 256 prev: cmp DL, 24h ; '$' ;je exit_loop jne l1 jmp SHORT exit_loop l1: 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 next6 ; , mov DL, i_stor ; mov i, Dl ; i_stor i ; prev: next6: inc i ; mov BL, i mov DL, str_arr[BX] ; loop prev ; prev: jmp prev exit_loop: MOV AH,2 ; MOV DL,0Ah INT 21h mov AX, 4c00h ; int 21h text ends data segment 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,'$' data ends stk segment para stack db 100h dup (0) ; 256 stk ends end begin
Link para o github com listas de programas.
PS Aqui está
um artigo sobre a criação de uma calculadora de notação polonesa reversa x86 (RPN).