第一部分第二部分第三部分在本文中,我们将在TurboAssembler上编写Brainfuck编译器
在这里,您可以逐步调试bf程序。
首先,我们将使用某种高级语言(例如,Pascal)编写解释器。
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代码+++++++++++++++++++++++++++++++++++++。 会发出
! (字符
!的ASCII码是33)。
该程序可以在在线ide
ideone.com上进行检查。
接下来,用
goto替换
for循环并添加命令
最后,我们将输出
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_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.comHelloWorld代码看起来像
ideone.com让我们继续组装
要组织一个循环(循环),您需要在CX寄存器中放入循环的循环数,并在循环结束时放置一个标签,将要转换到该标签(通过loop命令)。
mov CX, 28h ; - prev: ; ; ; ; loop prev ; prev
创建一个
str_arr命令数组,放在其中
创建一个数据数组
data_arr ,(为清楚起见)将1,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的当前元素不是
然后跳到
next4标签
:在标签
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_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中断
21h添加功能
mov ah, 3fh ; mov cx, 100h ; 256 mov dx,OFFSET str_arr int 21h
到达字符串
'$'的结尾后,我们将退出循环。
为此,我们将比较当前字符与字符
“ $” cmp DL, 24h ; '$' je exit_loop
用jmp命令替换loop loop。
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
编译过程中出现错误
相对跳出0001h个字节
事实是
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:
因此,如果当前字符与
$匹配,则使用
jmp命令转到
exit_loop:标签,否则我们跳过
jmp命令。
jmp命令可以进行
段内相对较短的转换(转换小于128字节,即IP:= IP + i8)或
段内相对较长的转换 (转换小于327字节,即IP:= IP + i16)。
默认情况下,
jmp命令会进行相对较长的跳转,这正是我们所需要的(通常,您可以简单地将jumps指令添加到程序的开头)。
;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。
PS这是有关创建x86反向波兰表示法(RPN)计算器的文章。