我在汇编时做了一个脑力操翻译(在
使用此代码:
++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.
它打印Hello World!没有问题,请注意它几乎拥有您需要的一切:嵌套循环,您可以执行的所有命令(除了)。它仍然工作得非常好。
然而,当使用这段代码时(预期的输出也是Hello World!就像第一段代码一样):
--<-<<+[+[<+>--->->->-<<<]>]<<--.<++++++.<<-..<<.<+.>>.>>.<<<.+++.>>.>>-.<<<+.
它打印“n|1f14{g”(没有任何seg错误或非法指令)。我摸不着头脑,为什么这个代码不起作用,而另一个代码工作得非常好。我想我已经处理了所有可能的指令和一些必须硬编码的异常(嵌套循环)。在编写Brainfuck解释器时,我还应该记住其他一些警告吗?嵌套循环似乎是唯一的警告,这些工作得非常好。我尝试过调试,但也没有帮助我。
这是我的代码:
.global main
format_str: .asciz "--<-<<+[+[<+>--->->->-<<<]>]<<--.<++++++.<<-..<<.<+.>>.>>.<<<.+++.>>.>>-.<<<+."
main:
pushq %rbp
movq %rsp, %rbp
movq $format_str, %rdi
call brainfuck
movq $0, %rdi
popq %rbp
call exit
brainfuck:
pushq %rbp
movq %rsp, %rbp
pushq %rbx
movq $1, %r15 # use as loop counter
movq %rdi, %rbx # move the pointer to the first char in the string to %rbx
movq $100000, %rdi # allocate 100000 bytes in the heap
call malloc # call the actual malloc function
movq %rax, %r12 # move the data pointer to the allocated bytes to r12
addq $50000, %r12 # add 50000 to the location so we start in the middle. So when starting you can also go to "negative" cells
sub $1, %rbx
.back:
add $1, %rbx # iterate one char over the string we have
.findVal:
movzbl (%rbx),%eax # move (%rbx) into %eax, only accesses memory once, speeding up the tests.
cmp $0,(%rbx) # test for end of string
je .end
cmp $'+', %al # test for plus
je .plus
cmp $'-', %al # test for minus
je .minus
cmp $'>', %al # test for right
je .right
cmp $'<', %al # test for left
je .left
cmp $'[', %al # test for opening bracket
je .openloop
cmp $']', %al # test for closing bracket
je .closeloop
cmp $'.', %al # test for dot
je .dprint
cmp $',', %al # test for comma
je .cinput
jmp .back # if none match then it is an unsupported command, so iterate once over the string and jump back
.plus:
add $1,(%r12) # add 1 to the cell the data pointer (r12) is pointing at
jmp .back
.minus:
sub $1,(%r12) # subtract 1 of the cell the data pointer (r12) is pointing at
jmp .back
.right:
add $1,%r12 # add 1 to the memory location of the data pointers, (hence moving up 1 cell)
jmp .back
.left:
sub $1,%r12 # subtract 1 to the memory location of the data pointers, (hence moving down 1 cell)
jmp .back
.openloop:
cmpb $0,(%r12) # if the current cell we are pointing at is 0, jump to closing bracket
je .jumpclose
pushq %rbx # save the opening bracket location on the stack (to deal with nested loops)
jmp .back
.closeloop:
cmpb $0,(%r12) # if the current cell we are pointing at is 0, exit the loop
je .endloop
movq (%rsp), %rbx # if it's not 0, move the top of the stack to the rbx, so we start back at the last pushed opening bracket
jmp .back
.endloop:
addq $8,%rsp # if it is the end of a loop (the cell at (%r12) is 0), then we add 8 to the stack pointer, so that the next jumping position (if there is one) is at (%r12)
jmp .back
.inc_r15:
add $1, %r15 # increase nested loop counter
.jumpclose: # finds the corresponding closing bracket
add $1, %rbx # go to next char
movzbl (%rbx),%eax # move rbx to eax so we access memory less often
cmp $']', %al # check for closing loop
je .closeloopfound
cmp $'[', %al # check for opening loop
je .inc_r15 # if there is another nested loop increase the loop counter
jmp .jumpclose # if none of those then simply jump back to iterate to the next char
.closeloopfound:
sub $1, %r15 # when encountering a closing loop, subtract 1 from the loop counter
cmpq $0, %r15 # r15 is intiated with 1 since when we enter a loop we also have 1 opening bracket, if its equal to 0 after the last subtraction that means we found the correct bracket
jne .jumpclose # if it's not 0, then jump back to jumpclose to continue finding the correct bracket
mov $1, %r15 # move 1 back into r15 to take care of the next time we have to skip a loop
jmp .back # jump back
.dprint:
movq $1, %rax # perform syscall 1 which is sys_write
movq $1, %rdi # write to stdout
movq %r12, %rsi # use the char that %r12 is pointing to, which is stored as ascii
movq $1, %rdx # write 1 byte (amount of byte)
syscall # perform the system_write (print) with syscall
jmp .back
.cinput:
movq $0, %rax # sys_read call number
movq $1, %rdi # read from stdin
movq %r12, %rsi # print the ascii value of whatever %r12 is pointing to
movq $1, %rdx # write 1 char
syscall # perform the system_read with syscall
jmp .back
.end: # exiting the function
popq %rbx # restore %rbx
popq %rbp # restore %rbp
ret # return to the proper return adress
为了清楚起见,我添加了一些注释。我只是完全迷失在代码出错的地方,我使用的是一个在线编译器,以前从未让我失望过。所以我不认为这是问题所在。
编辑:在进一步搜索之后,我发现了一个我错过的“警告”。我不是在进入循环时检查0,我只是在退出循环时检查0,就像一个真正的蠢驴。我已经更新了代码并添加了这个功能,幸运的是一些程序现在可以工作了(比如Daniel Cristofani的Sierpinski)。但有些程序仍然失败(上面的程序仍然失败)。现在我真的不知道该怎么办了。
经过几个小时的搜索,我已经修复了错误。在我正在做的加减部分
add $1, (%r12)
错误是我本应该这样做:
addb $1, (%r12)
如果我仔细想想,这是有道理的,但是我不希望整个代码因为这样的事情而无法运行。在这一点上,有些代码确实起作用了,比如mandelbrot(最复杂的代码之一)。我一直看错地方了。
众所周知,我们可以像ProjectLombook的工作人员一样,使用自定义注释和Java注释处理器自动生成代码。但是我们可以从编译的源代码中删除带注释的代码吗? 我试着在网上搜索它,但只出现了“生成代码”主题和“如何生成带有一个注释的服务器”教程。当我在寻找从prod应用程序“编译”调试消息的方法时,我想到了这一点。我可以理解,拥有调试/测试和生产代码不是一个好的实践,但有时需要保持简单。我认为这
我对此真的很困惑。如果我以编程方式删除Firest集合中的所有文档,集合本身也会被删除: 但是,如果我在线使用Firebase控制台删除所有文档,我并不总是看到集合被删除!我从字面上删除了集合中的所有文档,但它仍然在控制台中。我可以立即在集合中创建另一个文档。但有时集合确实会消失......?! 我读了一些帖子,比如删除Firestore收藏中的所有文件,但没有看到任何解释这一点的内容。 因为一个
本文向大家介绍git代码不变,删除所有commit记录,包括了git代码不变,删除所有commit记录的使用技巧和注意事项,需要的朋友参考一下 把旧项目提交到Git服务器上,会有很多以前的commit记录。 由于各种各样的原因,不希望在新的Git服务器上显示这些commit信息。 那如何删除这些commit记录,形成一个全新的仓库,并且保持代码不变呢? 参考资料: how to delete al
我有这个单声道代码: 当validate方法抛出ValidationException时,将同时调用handleValidation异常和handleException。我只希望调用handleValidationException。为什么会发生这种情况?如何防止handleException被调用?
像第七章描述的,指令cache是8k或16k。 如果代码的要害部位无法完全放进指令cache,那么可以考虑减小代码尺寸。 一般32位代码比16位代码大,因为32位代码的地址和数据常量是4个字节,16位代码是2个字节。 然而,16位代码有一些其它的惩罚诸如前缀的惩罚,同时访问邻近的字带来的问题(前述10.2章)。 减小代码尺寸的其它方法在下面讨论。 如果跳转地址,数据地址和数据常量在-128到127
我无法理解口译员和JIT之间的区别。例如,从这个答案中: JVMJava虚拟机-运行/解释/翻译字节码到本机代码 JIT是实时编译器——在运行时将给定的字节码指令序列编译为机器代码,然后在本地执行。它的主要目的是对性能进行重大优化。 两者都产生本机机器代码。然后,从另一个答案来看: 解释器为每条指令动态生成和执行机器代码指令,而不管之前是否执行过。JIT缓存以前已解释为机器代码的指令,并重用这些本