非常棒的计算机入门课程:公开课从与非门到俄罗斯方块(第二部分)

博客中的文章均为meelo原创,请务必以链接形式注明本文地址简书同步更新地址

Build a Modern Computer from First Principles: Nand to Tetris Part II (project-centered course)

by: Noam Nisan & Shimon Schocken

from: Hebrew University of Jerusalem

课程链接:https://www.coursera.org/learn/nand2tetris2/home

课程第一部分的介绍:http://www.cnblogs.com/meelo/p/5559453.html

伴随这门公开课,还有一本书《计算机系统要素》

在文章《从零开始设计一台计算机?记公开课从与非门到俄罗斯方块》中,我介绍了这门课的基本理念——从零开始设计一台计算机。在课程的第一部分,课程用与非门建造了算数逻辑单元,用触发器建造了内存,进而用算数逻辑单元和内存构建了一台能够运行机器指令的硬件。课程的第一部分主要涉及计算机的硬件,而第二部分将主要涉及计算机的软件。不像第一部分,课程不需要任何先修知识,这门课需要你具有高级编程语言的基础,编程作业需要用Java或者Python完成。在编程作业中,你需要设计一个编译器将Jack高级语言编译成为汇编语言。

 

上面这张图就展示了课程的全貌,课程第二部分主要针对于图片上侧的软件部分。下面将简要介绍课程每一周所学习的内容,并非一个完整的学习笔记。你可以根据所涉及的知识点,来决定是否需要来学习这一门课。

 

第一周

课程需要设计的编程语言类似于Java,引入了一个虚拟机层,高级语言需要经过双层编译成为汇编语言。这一周介绍了虚拟机的结构及虚拟机指令,以及如何将虚拟机指令转换成汇编指令。虚拟机是一种堆栈结构机器,由8个抽象的堆栈组成。虚拟机的指令有4种,分别是算数逻辑运算指令、内存分区指令、分支指令和函数指令。这一周和下一周的编程任务就是实现一个VMTranslator,将虚拟机指令转换成汇编指令,这一周需要实现算数逻辑运算指令和内存分区指令,下一周需要实现分支指令和函数指令。课程的视频中详细介绍了每一个虚拟机指令的功能,和大致的实现流程,但并没有给出具体的实现,这就是项目中最难的一部分,有的虚拟机指令可需要绞尽脑汁才能想出实现方案。

算数逻辑运算指令 add sub neg and or not eq gt lt
内存分区指令 push pop
分支指令 label goto if-goto
函数指令 function call return

四类虚拟机指令

第二周

这一周的项目需要实现上一周所剩余的分支指令和函数指令。分支指令很简单,函数指令就难很多了。函数作为高级语言很重要的一种抽象,在函数调用和返回的时候需要借助堆栈数据结构,同时需要进行很多额外的步骤来处理调用函数的参数、函数内状态的保存和函数内的局部变量。需要深入理解函数调用和返回的流程才能够用汇编指令实现出函数指令。函数调用和返回的过程非常精妙,在函数调用之前需要把函数的参数入栈到堆栈中,同时把函数的当前状态保存到堆栈,函数内的局部变量也需要事先在堆栈内开辟空间;函数返回时需要恢复调用函数的状态,保存返回值到堆栈中,最后跳转到调用函数中。

函数的调用栈

第三周

这一周会介绍针对这门课所设计的高级语言Jack。Jack是一种类似于Java的语言,拥有对象,对象有静态变量和实例变量,区分函数和方法,可以动态初始化生成实例;Jack像一般的高级语言一样拥有if语句和while语句,但是没有for语句;Jack有int、char和boolean三种基本数据类型,但是没有浮点类型;Jack包含运算符加减乘除、小于、大于、等于以及取非,但没有小于等于和大于等于。Jack语言很简陋,相比现代的商业化语言很不人性化,所有的Jack语言要求所有的的变量声明都需要以var起始,所有的赋值语句需要以let开始,所有的函数调用需要以do起始。虽说Jack各种不方便,写程序时无数次忘掉do和let,有时需要用到小于等于时,非得用非大于来实现,用着这种怪异的写法简直想骂街。但是麻雀虽小五脏俱全,所有没有的语法其实都可以用已有的语法实现,比如for语句可以用while语句实现。这一周的项目就是用Jack设计一个程序,程序的功能自由发挥。在网络上,你可以找到很多Jack语言实现的游戏,比如俄罗斯方块、2048。我也挑战了一下自己,实现了一个游戏了一个贪吃蛇,第一次做游戏,看看怎么样。

 

用Jack语言编写的贪吃蛇游戏

第四周

接下来的两周的任务是实现一个编译器。编译器由三个部分组成,分别是分词器(tokenizer)、语法解析器(parser)和代码生成器(code generator)。编译器的工作经过分解,变成三个步骤后就变得简单了许多。这周的项目需要实现分词器和语法解析器,代码生成器的实现是下周的任务。高级语言里符号可以以空格、缩进符为符号的分隔符,如果可以判断为两个符号也可以不需要分隔符,分词器就是根据符号间复杂的分隔关系将源程序分割中一个个符号。编程语言的语法可以用形式化的方法来表示,下图就展示了Jack语言的一部分语法。语法解析器的功能就是根据语法的形式化描述把属于同一程序结构的token流汇聚在一起,不同的程序结构相互嵌套构成了一颗树。

Jack语言的部分语法

第五周

这一周的任务是实现编译器最后的一部分,代码生成器。在代码生成器的实现中,基本上每一条语法规则由一个函数实现。语法规则的嵌套关系可以由函数的嵌套调用所体现。课程提供了代码生成器的代码框架,所以你只需要填充每一个函数的功能就行了,这大大降低了问题的难度。一些语法规则的实现很简单,而另一些语法规则的实现会很难,最为复杂的一个语法是term,不光代码的行数最多,在实现过程中还犯了几个错误。一些语法规则的实现具有相当的启发意义,比如if语句、while语句、字符串的实现、以及实例方法的调用,这只有你亲自实现才能体会到。课程还提供了测试的方法,每一组测试用例均提供了Jack源程序和虚拟机程序,通过对比给出的虚拟机代码和用程序生成的虚拟机代码就能查找程序哪个地方写错了。

编译器的工作流程示意图

第六周

介绍了操作系统。这个操作系统可谓简陋,其实它只是个库函数,包含8个类,分别是Math、String、Array、Output、Screen、Keyboard、Memory、Sys。这一周的内容很杂,走马观花式地介绍了很多内容。Math实现了基本的数学运算,由于课程中所设计的CPU并不支持乘除法,Math类里需要实现乘除操作,课程里介绍了一个复杂度为log(n)的乘除法实现;String类实现了字符串的操作;Array类实现了数组;Output类实现了将字符显示在屏幕上的功能,为此课程还设计了一个专门的字体,同时在这还介绍了中断中cursor的抽象。Screen类处理图形的绘制,包括直线矩形和圆,没有思考过不知道,其实在屏幕上画直线其实挺难的。Keyboard类实现了键盘的相关操作,包括如何读入字符、整数;Memory类处理类初始化时的动态内存分配,一个难点是如何管理内存。

操作系统的8个库函数

 

从零开始设计一个计算机,这是一个相当有挑战的课程,它涉及了数字逻辑设计、汇编语言、编译器、高级语言等诸多的理论知识。如果你完成了全部的两门课程,我保证你肯定会有相当大的收获。课程的标题里写着以项目为导向,其实名不虚传,所有视频授课的内容都直接与项目相关,没有一点废话。这种课程的组织方式对于学习者来说十分有益,通过编程来实践课程里所涉及的知识能够检验出你是否真正理解了。可以说公开课从与非门到俄罗斯方块是一门非常不错的计算机入门课程,五星推荐给对计算机的组成或者是计算机工作原理感兴趣的人。

 

彩蛋

课程的第二部分还没有结束,老师在课程的结尾透露还会有第三部分,内容会是用FPGA将计算机真正搭建在硬件上,期待下一次美妙的学习之旅!

posted on 2017-02-21 20:36  meelo  阅读(2301)  评论(1编辑  收藏  举报