الجزء الأولالجزء الثانيالجزء الثالثفي هذه المقالة ، سنكتب مترجم Brainfuck على TurboAssembler
هنا يمكنك تصحيح برامج bf في وضع خطوة بخطوة.
أولاً ، سنكتب المترجم ببعض اللغات عالية المستوى ، على سبيل المثال ، بلغة باسكال.
سوف تمثل مصفوفة
data_arr ذاكرة البيانات ،
وستحتوي سلسلة
str_arr على الأوامر.
سنكتب برنامجًا يعرض حرفًا يتوافق رمز ascii الخاص به مع الرقم
+ (لذلك نحتاج فقط إلى الأوامر
+ و
. )
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 +++++++++++++++++++++++++++++++++++++. سيعطي
! (رمز أسكي للحرف
! 33).
يمكن التحقق من البرنامج على الإنترنت
ideone.comبعد ذلك ، استبدل الحلقة
for بـ
goto وأضف الأوامر
في النهاية ،
سنخرج صفيف
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.
كود
سيعطي 1 2 3 0 0 0 0 0 0 0
كود
سيعطي 1 2 2 0 0 0 0 0 0 0
ideone.comبعد ذلك ، أضف
[ و
]أضف متغير
i_stor آخر.
إذا اجتاز العنصر الحالي الاختبار في
[ ، فإننا نتحقق من العنصر الحالي في صفيف
data_arr إلى صفر ، وإذا كان العنصر أكبر من الصفر ،
فقم بتحميل القيمة من المتغير
i إلى
i_stor .
عند معالجة قوس الإغلاق
] ، إذا لم يكن
data_arr صفرًا ،
فإننا نقوم بتحميل عنوان قوس الفتح في المتغير
i من المتغير
i_stor [بعد ذلك ، انتقل إلى الأمر
i: = i + 1؛إذا تم تحميل قيمة من
i_stor قبل ذلك إلى
i (أثناء التحقق
] ) ، فعندئذٍ بعد التفريغ سنكون في
[ (وإلا سنكون في
] )
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.
كود
ينقل الرقم 5 إلى الخلية التالية 0 5 0 0 0 0 0 0 0 0
ideone.comيبدو رمز HelloWorld مثل
ideone.comدعنا ننتقل إلى المجمع
لتنظيم حلقة (حلقة) ، تحتاج إلى وضع عدد دورات الدورة في CX ووضع تسمية يتم الانتقال إليها في نهاية الدورة (بواسطة الأمر حلقة).
mov CX, 28h ; - prev: ; ; ; ; loop prev ; prev
قم بإنشاء مجموعة من أوامر
str_arr ، ضعها هناك
إنشاء مصفوفة بيانات
data_arr ، (للتوضيح) ضع هناك 1،1،1،1،1،1،1،1،1،1،1
في حلقة ، قارن الحرف الحالي بالحرف
وإذا كانت الأحرف متساوية ، قم بزيادة القيمة في الخلية الحالية بمقدار 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
يتم تنفيذ
التجميع (الترجمة) بواسطة
الأمر tasm.exe bf1.asmيتم الربط بواسطة
tlink.exe bf1.objبعد تشغيل البرنامج في مصحح أخطاء TurboDebagger ، يمكنك رؤية أنه بدءًا من العنوان 0130 ، توجد الأوامر
التالي هو مجموعة البيانات التي قمنا فيها بتغيير العنصر الأول ، ثم المتغير
i ، الذي يصبح بعد تنفيذ الحلقة مساوياً لـ 0Ah.

أضف أوامر
لإخراج حرف واحد باستخدام وظيفة المقاطعة
02h int 21h ، من الضروري (قبل استدعاء المقاطعة) وضع رمز الحرف في سجل
DL .
mov AH,2 mov DL, int 21h
سنكتب البرنامج بأكمله
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

تعمل الحلقة على النحو التالي:
إذا لم يكن العنصر الحالي من
str_arr ثم انتقل إلى الملصق
التالي: (وإلا ، نفذ
)
إذا لم يكن العنصر الحالي من
str_arr ثم القفز إلى تسمية
next1 التالية:إذا لم يكن العنصر الحالي من
str_arr ثم القفز إلى تسمية
next2:إذا لم يكن العنصر الحالي من
str_arr ثم القفز إلى التسمية
next3:إذا لم يكن العنصر الحالي من
str_arr ثم
انتقل إلى التسمية التالية 4
:بعد الملصق
next4: قم بزيادة فهرس السلسلة
str_arr وانتقل إلى بداية الحلقة - إلى الملصق
prev:بعد ذلك ، أضف
[ و
]أضف متغير
i_stor .
إذا اجتاز العنصر الحالي الاختبار في
[ ، فإننا نتحقق من العنصر الحالي في مصفوفة
data_arr إلى الصفر ، وإذا كان العنصر صفرًا ، فإننا نقفز أكثر (إلى الملصق التالي) ، وإلا فإننا نحمل القيمة من المتغير
i إلى
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:
عند معالجة قوس الإغلاق
] ، إذا لم يكن
data_arr صفرًا ،
فقم بتحميل عنوان قوس الفتح في المتغير
i من المتغير
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:
تحقق من الرمز
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

إضافة وظيفة إلى خط الإدخال
3fh المقاطعة
21 ساعة mov ah, 3fh ; mov cx, 100h ; 256 mov dx,OFFSET str_arr int 21h
سنخرج من الحلقة بعد الوصول إلى نهاية السلسلة
"$" .
للقيام بذلك ، سنقوم بمقارنة الحرف الحالي بالحرف
"$" cmp DL, 24h ; '$' je exit_loop
استبدل حلقة الحلقة بالأمر 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
أثناء التجميع نحصل على خطأ
القفز النسبي خارج النطاق بمقدار 0001 ساعة بايت
والحقيقة هي أن أوامر
je / jne يمكن أن تقفز فقط بعد بضعة أسطر من البرنامج (يأخذ كل سطر من 1 إلى 5 بايت في الذاكرة).

لا يمكن إجراء قفزات طويلة حتى نهاية برنامج
je / jne .
لذلك ، نستبدل التعبير
cmp DL, 24h ; '$' je exit_loop ... exit_loop:
التعبير
cmp DL, 24h ; '$' jne exit_ jmp exit_loop exit_ ... exit_loop:
لذا ، إذا كان الحرف الحالي يتطابق مع
$ ، فانتقل إلى
exit_loop: label بالأمر
jmp ، وإلا فإننا نقفز فوق الأمر
jmp .
يمكن للأمر
jmp إجراء انتقال
قصير نسبي داخل المقطع (انتقال أقل من 128 بايت ، أي IP: = IP + i8) أو
انتقال طويل نسبي داخل المقطع (انتقال أقل من 327 بايت ، أي IP: = IP + i16).
بشكل افتراضي ، يقوم الأمر
jmp بعمل قفزة طويلة نسبيًا ، وهو ما نحتاجه (بشكل عام ، بدلاً من ذلك ، يمكنك ببساطة إضافة التوجيهات السريعة إلى بداية البرنامج).
;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
رابط إلى github مع قوائم البرامج.
ملاحظة هنا
مقال حول إنشاء حاسبة x86 عكس التدوين البولندي (RPN).