掘金 后端 ( ) • 2024-04-22 09:24

I believe that at the end of the century the use of words and general educated opinion will have altered so much that one will be able to speak of machines thinking without expecting to be contradicted. —— Alan Turing

文中提到的所有实现都可以参考:nand2tetris_sol,但是最好还是自己学习课程实现一遍,理解更深刻。

这篇文章我们来看 VM code 里的分支和函数调用。

分支

vmcode 里的分支靠 label goto 和 if-goto 实现,实现起来很简单,如下图:

唯一需要注意的就是 if-goto 语句,需要让 cond = pop,翻译成汇编语句就是判断 RAM[SP] 的结果,并 pop 掉。

函数

对于 vmcode 的函数有三个重要的 symbol call、function 和 return。理解了他们就可以直接按理解翻译成对应的汇编代码,这也就是虚拟机代码翻译器做的事情。我们具体来看看这三个关键字。

call

call 用在 caller 里,用来调用 callee。caller 和 callee 都是 function。

call 主要干了三件事(记录信息):

  1. 记住 returnaddress
  2. 记住 caller 的 segment 信息(static|pointer|temp|constant 不用保存,因为函数调用不影响这些段的地址)
  3. 更改 ARG 和 LCL 确保 callee 取用正确的信息

function

function 这里为什么要压入 nVars 的变量还没有理解,结合后面的文章理解了再回来补充。大致上应该是实参和形参的关系。

return

return 主要干了三件事(恢复信息):

  1. 通过 LCL 地址,恢复 caller 的 segments
  2. 讲 stack top(返回值)返回给 caller
  3. 返回到 caller 要执行的下一条指令

引导程序代码(Bootstrap code)

我们需要 Bootstrap code 去开始执行 vmcode,就像电脑需要开机才能运行程序一样。

这里的 Symbol 都是根据 VM Translator 和 assembler 的程序定义的,你可以更改你的程序,但是相应的 Symbol 表格也需要更改,到这里虚拟机代码翻译器就实现了,实现一个虚拟机代码翻译器可以加深对函数调用的理解。这章内容也比较抽象,如果实在理解不了,可以结合文章开头的代码实现理解,会容易一些。

下篇文章我们开始介绍高级语言 Jack,并实现编译器以及操作系统。