Featured image of post Nand2Tetris Project(6)

Nand2Tetris Project(6)

用高级编程语言写机器语言的汇编器,左脚踩右脚上天

Project 6: Assembly

简介

在本项目中我使用JAVA编程语言进行代码的编写。课程已经给我们提供了贴心的API设计,我们只需要根据API进行补充就可以了。

Assembler由以下四个类组成:

  • Parser: read an instruction from the .asm file,ignore the white space and comments,interpret the type of instruction,parse it when it is a C-instruction
  • Code: translate hack assembly languable into binary codes by using the code map
  • SymbolTable:use a map to store all the symbols and its corresponding address
  • Main: initialize a SymbolTable->create a parser->first pass->recreate a parser->second pass

这是典型的面向对象编程,不涉及算法内容,基本上就是进行简单的字符串读写,但是需要支持各个类之间较为复杂的依赖关系。用JAVA和IntelliJ IDEA进行开发是一个很不错的选择。

其实这个作业在总体统筹上的规划难度几乎是0,因为在课本里面已经给了所有的API,只需要完成每个类的API以及在Main函数中实现整个pipeline即可。所以我在这里就不放具体代码了,而是把我在完成的过程中遇到的一些问题记录一下。

一些insights

关于Java的Map

在本次作业中,为了实现一些指令到binary code的转化,我们需要使用java标准库的Map容器。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
private Map<String,Integer> symbolTable = new HashMap<>();

//add key-value to map
symbolTable.put(key,val);

//access to a value
val = symbolTable.get(key);

//check the existence of key
symbolTable.containsKey(key);

Parser的读入操作

这里还是比较复杂的,尤其是涉及一些基本的字符串处理操作。

  • 使用BufferedReader进行整行的读入。
1
reader = new BufferedReader(new FileReader(filename));
  • 关键的advance函数实现
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
    public void advance()throws IOException
    {
        currentInstruction = nextInstruction;//we define a nextInstruction for the convenience of the implementation of hasMoreCommands
        nextInstruction = null;
        String line;
        while((line = reader.readLine()) != null){
            line = line.split("//")[0].trim();//remove the comments or empty row
            if(!line.isEmpty()){
                nextInstruction = line;
                return;
            }
            // if no instruction is read in,then reader will keep on reading
        }
    }
  • 注意在一个parser对象结束工作之后及时关闭内部的reader,读者可自行设置函数完成。

关于ROM和RAM

在完成代码的时候,我曾经出现了一个混淆,就是label和variable的存储位置。我原本意味他们是存储在一起的,所以可能出现覆盖的问题。

但是事实上,label指向的是ROM里面的指令内存,而variable指向的是RAM里面的数据内存,二者可能地址的int值是一样的,但是实际上并不是存在一块内存中的,也就不涉及互相覆盖的问题。而在硬件端运行的时候,会自动根据指令是A还是C指令访问对应的内存的。

写在最后

在这里我说几句闲话,算是表达一下自己的一些关于学习方法的思考。 在我看来,如果进行一项学习活动只追求一方面的增益,那么相应的效率就是比较低下的。而如果同时追求三方面以上的增益,那么就容易陷入多线程工作造成的注意力涣散问题中去。比方说在学习data100相关课程的时候,使用pandas库进行数据处理。在这个过程中,我获得了三个方向的收益:

  1. 掌握数据处理的基本流水线
  2. Pandas的基本语法
  3. 阅读官方教程和API文档的能力

而本次Project中,由于需要使用一种高级编程语言来完成一个基础的汇编器,而对我来说python编程总有一种很随意的感觉、缺少工程的味道;而C++编程则缺少合适的IDE(Visual Studio太丑了,而用CMake则需要把大量的时间放在编译和debug上面)且代码略显复杂,因此我决定使用一种我并不熟悉的(但是很简单)的编程语言——Java.

这时候我突然想起我曾经关注的一门课——MIT:Software Construction。我想,何不就这个机会,切入软件工程这个领域,进行一些中等规模编程实训,培养自己的代码习惯和编程系统思维(古法手搓代码爱好者的选择,氛围编程用户看个乐呵即可)?因此,我从这个Project中获得的增益如下:

  1. 汇编器的运行原理
  2. java编程语法
  3. 系统编程思维 那么,这个项目对于我来说,就是有意义的、且多方面收益的学习过程。

絮絮叨叨说了这些,其实我想表达的,是心理学家斯金纳多年前就已经说过的。 Education is what remains after one has forgotten everything he learned in school. 具体的知识是无穷无尽的。我们终其一生只能居在一隅内。唯有通过在知识中实践,掌握普适的学习方法,我们才能尽自己的努力,发挥人类在泛化能力上的优势,才能不断接近“无涯”的境界。 不多说了,准备开启Nand2Tetris的第二部分。奥利给!

comments powered by Disqus