Brainfuck de baixo nível

Parte I
Parte II
Parte III

Neste 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.com

Em 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.com

Em 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.com
O código HelloWorld se parece com ideone.com

Vamos 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.asm
A vinculação é feita pelo tlink.exe bf1.obj

Depois 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).

Source: https://habr.com/ru/post/pt423121/


All Articles