Teil I.Teil IITeil IIIIn 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.comAls nächstes fügen Sie
[ und
] hinzuFü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.comHelloWorld-Code sieht aus wie
ideone.comFahren 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ührtDie Verknüpfung erfolgt über
tlink.exe bf1.objNachdem 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
] hinzuFü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).