Low Level Brainfuck

Teil I.
Teil II
Teil III

In diesem Artikel schreiben wir einen Brainfuck-Compiler auf TurboAssembler
Hier können Sie bf-Programme schrittweise debuggen.

Zuerst werden wir den Interpreter in einer höheren Sprache schreiben, zum Beispiel in Pascal.

Das Array data_arr repräsentiert den Datenspeicher, die Zeichenfolge str_arr enthält die Befehle.

Wir werden ein Programm schreiben, das ein Zeichen anzeigt, dessen ASCII-Code der Zahl + entspricht (daher benötigen wir nur die Befehle + und . )

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. 

bf code ++++++++++++++++++++++++++++++++++++++. wird geben ! (Der ASCII-Code des Zeichens ! ist 33).

Das Programm kann unter online ide ideone.com überprüft werden

Ersetzen Sie als nächstes die for- Schleife durch goto und fügen Sie die Befehle hinzu <>
Am Ende geben wir das Array data_arr aus

 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. 

Code +>++>+++ergibt 1 2 3 0 0 0 0 0 0 0 0
Code +>++>+++ergibt 1 2 2 0 0 0 0 0 0 0 0
ideone.com

Als nächstes fügen Sie [ und ] hinzu
Fügen Sie eine weitere i_stor- Variable hinzu.
Wenn das aktuelle Element den Test am [bestanden hat , überprüfen wir das aktuelle Element des data_arr- Arrays auf Null. Wenn das Element größer als Null ist, laden Sie den Wert aus der Variablen i in i_stor .

Wenn data_arr bei der Verarbeitung der schließenden Klammer nicht Null ist, laden Sie die Adresse der öffnenden Klammer in Variable i aus der Variablen i_stor [

Gehen Sie als nächstes zum Befehl i: = i + 1;
Wenn zuvor ein Wert von i_stor in i geladen wurde (während der Überprüfung ] ), befinden wir uns nach dem Dump in [ (andernfalls befinden wir uns in ] ).
 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. 

Code +++++[>+<]überträgt die Nummer 5 in die nächste Zelle 0 5 0 0 0 0 0 0 0 0
ideone.com
HelloWorld-Code sieht aus wie ideone.com

Fahren wir mit dem Assembler fort


Um eine Schleife (Schleife) zu organisieren, müssen Sie die Anzahl der Zyklen des Zyklus in das CX-Register eintragen und eine Bezeichnung einfügen, zu der der Übergang am Ende des Zyklus erfolgt (mit dem Befehl loop).

 mov CX, 28h ; -   prev: ;   ;  ;  ;   loop prev ;    prev 

Erstellen Sie dort ein Array von str_arr- Befehlen +++
Erstellen Sie ein Datenarray data_arr (aus Gründen der Übersichtlichkeit)

Vergleichen Sie in einer Schleife das aktuelle Zeichen mit dem Zeichen +und wenn die Zeichen gleich sind, erhöhen Sie den Wert in der aktuellen Zelle um 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 

Das Zusammenbauen (Übersetzen) wird mit dem Befehl tasm.exe bf1.asm durchgeführt
Die Verknüpfung erfolgt über tlink.exe bf1.obj

Nachdem Sie das Programm im TurboDebagger-Debugger ausgeführt haben, können Sie sehen, dass sich die Befehle ab Adresse 0130 befinden +++
Als nächstes folgt das Datenarray, in dem wir das erste Element geändert haben, dann die Variable i , die nach der Ausführung der Schleife gleich 0Ah wird.



Befehle hinzufügen <>.
Um ein einzelnes Zeichen mit der 02h- Interrupt-Funktion int 21h auszugeben , muss (vor dem Aufrufen des Interrupts) der Zeichencode in das DL- Register eingefügt werden .

  mov AH,2 mov DL,   int 21h 

Wir werden das ganze Programm schreiben

 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 



Die Schleife funktioniert folgendermaßen:
wenn das aktuelle Element von str_arr nicht ist +dann zum nächsten Label springen : (andernfalls ausführen +)
wenn das aktuelle Element von str_arr nicht ist Springe dann zum nächsten Label1:
wenn das aktuelle Element von str_arr nicht ist >dann springe zum next2 label :
wenn das aktuelle Element von str_arr nicht ist <dann springe zum next3 label :
wenn das aktuelle Element von str_arr nicht ist .dann springe zum next4 label :
Nach dem Label next4: Erhöhen Sie den Index des Strings str_arr und springen Sie zum Anfang der Schleife - zum Label prev:

Als nächstes fügen Sie [ und ] hinzu
Fügen Sie die Variable i_stor hinzu .

Wenn das aktuelle Element den Test am [bestanden hat , überprüfen wir das aktuelle Element des data_arr- Arrays auf Null, und wenn das Element Null ist, springen wir weiter (zur nächsten Bezeichnung), andernfalls laden wir den Wert aus der Variablen i in 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: 

Wenn data_arr bei der Verarbeitung der schließenden Klammer nicht Null ist, laden Sie die Adresse der öffnenden Klammer in Variable i aus der Variablen 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: 

Überprüfen Sie den Code +++++[>+<]

 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 



Funktion zur Eingabezeile 3fh Interrupt 21h hinzufügen
  mov ah, 3fh ;   mov cx, 100h ; 256  mov dx,OFFSET str_arr int 21h 


Wir verlassen die Schleife, nachdem wir das Ende der Zeichenfolge '$' erreicht haben .
Dazu vergleichen wir das aktuelle Zeichen mit dem Zeichen '$'.
 cmp DL, 24h ;  '$' je exit_loop 

Ersetzen Sie die Schleifenschleife durch den Befehl 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 

Während der Kompilierung erhalten wir eine Fehlermeldung
Relativer Sprung außerhalb des Bereichs um 0001h Bytes

Tatsache ist, dass je / jne- Befehle nur nach wenigen Zeilen des Programms springen können (jede Zeile benötigt 1 bis 5 Bytes im Speicher).


Weitsprünge bis zum Ende des je / jne- Programms sind nicht möglich.
Deshalb ersetzen wir den Ausdruck
  cmp DL, 24h ;  '$' je exit_loop ... exit_loop: 

Ausdruck
 cmp DL, 24h ;  '$' jne exit_ jmp exit_loop exit_ ... exit_loop: 


Wenn das aktuelle Zeichen mit $ übereinstimmt, gehen Sie mit dem Befehl jmp zur Bezeichnung exit_loop:: Andernfalls springen wir über den Befehl jmp .
Der Befehl jmp kann einen relativ kurzen Übergang innerhalb eines Segments (Übergang weniger als 128 Bytes, d. H. IP: = IP + i8) oder einen relativ langen Übergang innerhalb eines Segments (Übergang weniger als 327 Bytes, d. H. IP: = IP + i16) durchführen.
Standardmäßig macht der Befehl jmp einen relativen Weitsprung, was wir brauchen (im Allgemeinen können Sie stattdessen einfach die Direktive jumps am Anfang des Programms hinzufügen).
 ;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 zu Github mit Programmlisten.

PS Hier ist ein Artikel zum Erstellen eines RPN-Rechners (x86 Reverse Polish Notation).

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


All Articles