前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[From Nand to Tetris] 第8章 虚拟机项目 python 实现

[From Nand to Tetris] 第8章 虚拟机项目 python 实现

作者头像
Alan Zhang
发布2018-10-19 14:51:30
4750
发布2018-10-19 14:51:30
举报
文章被收录于专栏:Alan's Lab

[From Nand to Tetris] 第8章 虚拟机项目 python 实现

为防闲逛至此的看官不知所云: From Nand to Tetris 是一个在线课程,目标是指导学生从 Nand 逻辑门开始从头到尾完成一整套计算机系统

好像撸串一样的爽快:------芯片--硬件---编译原理---操作系统---应用程序------>

这里提供的是第八章的作业,以供半路摔进坑里的同学们扶一下。。。

人家老师确实是不希望扩散答案,不过我做的过程中遇到很多坑,搞半天后发现全是些脑残原因,实在是浪费时间,希望卡壳的同学们能以增进效率为目的适当参考答案。毕竟学习这种东西,有没有学到手只有自己知道。。。

注释不多,因为代码相当 self-explanatory ,就是书上那些,没自由发挥什么。

如果你是闲逛进来,而且对这块内容有兴趣的话,强烈建议点开上面的课程链接试试,我是真心非常喜欢这门课,请收下我的安利。。。

另外还有第六章的作业答案:第6章 汇编器项目 python 实现

代码语言:javascript
复制
# _*_ coding: utf-8 _*_

import sys
import os
import glob


class C_TYPE:
    '''Command Type'''
    C_ARITHMETIC, C_PUSH, C_POP, C_LABEL, C_GOTO, C_IF, C_FUNCTION, C_RETURN, C_CALL = range(9)


class Parser:

    def __init__(self, fname):
        self.finput = open(fname, 'rU')
        self.current = None
        self.commandPart = []
        self.cType = -1

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.finput.close()

    def has_more_commands(self):

        self.current = self.finput.readline()

        while self.current == '\n' or self.current[:2] == '//':
            self.current = self.finput.readline()

        return self.current != ''

    def advance(self):
        self.commandPart = self.current.strip().split(' ')[:3]

        if self.commandPart[0] == 'add' or\
                self.commandPart[0] == 'sub' or\
                self.commandPart[0] == 'neg' or\
                self.commandPart[0] == 'eq' or\
                self.commandPart[0] == 'gt' or\
                self.commandPart[0] == 'lt' or\
                self.commandPart[0] == 'and' or\
                self.commandPart[0] == 'or' or\
                self.commandPart[0] == 'not':
            self.cType = C_TYPE.C_ARITHMETIC
        elif self.commandPart[0] == 'push':
            self.cType = C_TYPE.C_PUSH
        elif self.commandPart[0] == 'pop':
            self.cType = C_TYPE.C_POP
        elif self.commandPart[0] == 'label':
            self.cType = C_TYPE.C_LABEL
        elif self.commandPart[0] == 'goto':
            self.cType = C_TYPE.C_GOTO
        elif self.commandPart[0] == 'if-goto':
            self.cType = C_TYPE.C_IF
        elif self.commandPart[0] == 'function':
            self.cType = C_TYPE.C_FUNCTION
        elif self.commandPart[0] == 'call':
            self.cType = C_TYPE.C_CALL
        elif self.commandPart[0] == 'return':
            self.cType = C_TYPE.C_RETURN

    def command_type(self):
        return self.cType

    def arg1(self):
        if self.command_type() == C_TYPE.C_ARITHMETIC:
            return self.commandPart[0]
        return self.commandPart[1]

    def arg2(self):
        return self.commandPart[2]


class CodeWriter:

    def __init__(self, fname):
        self.foutput = open(fname, 'w')
        self.uniqueFlag = 0  # 用于构造唯一的标签,每次使用后加1
        self.currentFile = '' # 当前处理的文件的名字
        self.currentFunc = '' # 当前处理的函数的名字

    def set_file_name(self, fname):
        self.currentFile = fname

    def write_arithmetic(self, command):
        if command == 'add':
            self.foutput.write('@SP\nM=M-1\nA=M\nD=M\n@SP\nM=M-1\nA=M\nM=M+D\n@SP\nM=M+1\n')
        elif command == 'sub':
            self.foutput.write('@SP\nM=M-1\nA=M\nD=M\n@SP\nM=M-1\nA=M\nM=M-D\n@SP\nM=M+1\n')
        elif command == 'neg':
            self.foutput.write('@SP\nM=M-1\nA=M\nM=-M\n@SP\nM=M+1\n')
        elif command == 'eq':
            self.foutput.write('@SP\nM=M-1\nA=M\nD=M\n@SP\nM=M-1\nA=M\nD=M-D\n@RET_TRUE'+str(self.uniqueFlag)+'\n'
                               'D;JEQ\nD=0\n@END'+str(self.uniqueFlag)+'\n0;JMP\n(RET_TRUE'+str(self.uniqueFlag)+')\n'
                               'D=-1\n(END'+str(self.uniqueFlag)+')\n@SP\nA=M\nM=D\n@SP\nM=M+1\n')
            self.uniqueFlag += 1
        elif command == 'gt':
            self.foutput.write('@SP\nM=M-1\nA=M\nD=M\n@SP\nM=M-1\nA=M\nD=M-D\n@RET_TRUE'+str(self.uniqueFlag)+'\n'
                               'D;JGT\nD=0\n@END'+str(self.uniqueFlag)+'\n0;JMP\n(RET_TRUE'+str(self.uniqueFlag)+')\n'
                               'D=-1\n(END'+str(self.uniqueFlag)+')\n@SP\nA=M\nM=D\n@SP\nM=M+1\n')
            self.uniqueFlag += 1
        elif command == 'lt':
            self.foutput.write('@SP\nM=M-1\nA=M\nD=M\n@SP\nM=M-1\nA=M\nD=M-D\n@RET_TRUE'+str(self.uniqueFlag)+'\n'
                               'D;JLT\nD=0\n@END'+str(self.uniqueFlag)+'\n0;JMP\n(RET_TRUE'+str(self.uniqueFlag)+')\n'
                               'D=-1\n(END'+str(self.uniqueFlag)+')\n@SP\nA=M\nM=D\n@SP\nM=M+1\n')
            self.uniqueFlag += 1
        elif command == 'and':
            self.foutput.write('@SP\nM=M-1\nA=M\nD=M\n@SP\nM=M-1\nA=M\nM=M&D\n@SP\nM=M+1\n')
        elif command == 'or':
            self.foutput.write('@SP\nM=M-1\nA=M\nD=M\n@SP\nM=M-1\nA=M\nM=M|D\n@SP\nM=M+1\n')
        elif command == 'not':
            self.foutput.write('@SP\nM=M-1\nA=M\nM=!M\n@SP\nM=M+1\n')

    def write_push_pop(self, command, segment, index):
        if command == C_TYPE.C_PUSH:
            if segment == 'constant':
                self.foutput.write('@'+index+'\nD=A\n@SP\nA=M\nM=D\n@SP\nM=M+1\n')
            elif segment == 'local':
                self.foutput.write('@LCL\nD=M\n@'+index+'\nA=A+D\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n')
            elif segment == 'argument':
                self.foutput.write('@ARG\nD=M\n@'+index+'\nA=A+D\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n')
            elif segment == 'this':
                self.foutput.write('@THIS\nD=M\n@'+index+'\nA=A+D\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n')
            elif segment == 'that':
                self.foutput.write('@THAT\nD=M\n@'+index+'\nA=A+D\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n')
            elif segment == 'pointer':
                self.foutput.write('@'+str(int(index)+3)+'\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n')
            elif segment == 'temp':
                self.foutput.write('@'+str(int(index)+5)+'\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n')
            elif segment == 'static':
                self.foutput.write('@'+self.currentFile+'.'+index+'\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n')
        elif command == C_TYPE.C_POP:
            if segment == 'local':
                self.foutput.write('@LCL\nD=M\n@'+index+'\nD=A+D\n@R13\nM=D\n@SP\nM=M-1\nA=M\nD=M\n@R13\nA=M\nM=D\n')
            elif segment == 'argument':
                self.foutput.write('@ARG\nD=M\n@'+index+'\nD=A+D\n@R13\nM=D\n@SP\nM=M-1\nA=M\nD=M\n@R13\nA=M\nM=D\n')
            elif segment == 'this':
                self.foutput.write('@THIS\nD=M\n@'+index+'\nD=A+D\n@R13\nM=D\n@SP\nM=M-1\nA=M\nD=M\n@R13\nA=M\nM=D\n')
            elif segment == 'that':
                self.foutput.write('@THAT\nD=M\n@'+index+'\nD=A+D\n@R13\nM=D\n@SP\nM=M-1\nA=M\nD=M\n@R13\nA=M\nM=D\n')
            elif segment == 'pointer':
                self.foutput.write('@SP\nM=M-1\nA=M\nD=M\n@'+str(int(index)+3)+'\nM=D\n')
            elif segment == 'temp':
                self.foutput.write('@SP\nM=M-1\nA=M\nD=M\n@'+str(int(index)+5)+'\nM=D\n')
            elif segment == 'static':
                self.foutput.write('@SP\nM=M-1\nA=M\nD=M\n@'+self.currentFile+'.'+index+'\nM=D\n')

    def write_label(self, label):
        self.foutput.write('('+self.currentFunc+'$'+label+')\n') # 构造 (funcName$label) 格式的标记,网站上没提,但书里有

    def write_init(self):
        self.foutput.write('@256\nD=A\n@SP\nM=D\n')
        self.write_call('Sys.init', 0)

    def write_goto(self, label):
        self.foutput.write('@'+self.currentFunc+'$'+label+'\n0;JMP\n')

    def write_if(self, label):
        self.foutput.write('@SP\nM=M-1\nA=M\nD=M\n@'+self.currentFunc+'$'+label+'\nD;JNE\n')

    def write_call(self, function_name, num_args):
        self.foutput.write('@RETURN_ADDRESS'+str(self.uniqueFlag)+'\nD=A\n@SP\nA=M\nM=D\n@SP\nM=M+1\n'
                           '@LCL\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n'
                           '@ARG\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n'
                           '@THIS\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n'
                           '@THAT\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n'
                           '@SP\nD=M\n@'+str(int(num_args)+5)+'\nD=D-A\n@ARG\nM=D\n'
                           '@SP\nD=M\n@LCL\nM=D\n'
                           '@'+function_name+'\n0;JMP\n'
                           '(RETURN_ADDRESS'+str(self.uniqueFlag)+')\n')
        self.uniqueFlag += 1

    def write_return(self):
        self.foutput.write('@LCL\nD=M\n@R13\nM=D\n'
                           '@5\nA=D-A\nD=M\n@R14\nM=D\n'
                           '@SP\nM=M-1\nA=M\nD=M\n@ARG\nA=M\nM=D\n'
                           '@ARG\nD=M+1\n@SP\nM=D\n'
                           '@R13\nA=M-1\nD=M\n@THAT\nM=D\n'
                           '@R13\nA=M-1\nA=A-1\nD=M\n@THIS\nM=D\n'  # 蠢萌蠢萌的减n次1,比下一句还省一条指令。。。
                           '@R13\nD=M\n@3\nA=D-A\nD=M\n@ARG\nM=D\n'
                           '@R13\nD=M\n@4\nA=D-A\nD=M\n@LCL\nM=D\n'
                           '@R14\nA=M\n0;JMP\n')

    def write_function(self, function_name, num_locals):
        self.currentFunc = function_name
        commandsInitLocals = ''
        for i in range(int(num_locals)):
            commandsInitLocals += '@LCL\nD=M\n@'+str(i)+'\nA=A+D\nM=0\n'
        self.foutput.write('('+function_name+')\n'+commandsInitLocals)

    def close(self):
        self.foutput.close()


def process_vm_file(fpath):
    '''
    处理一个 .vm 文件

    fpath: 待处理 .vm 文件的绝对路径
    '''
    parser = Parser(fpath)
    writer.set_file_name(os.path.basename(fpath.strip('.vm')))
    while parser.has_more_commands():
        parser.advance()
        if parser.command_type() == C_TYPE.C_PUSH or parser.command_type() == C_TYPE.C_POP:
            writer.write_push_pop(parser.command_type(), parser.arg1(), parser.arg2())
        elif parser.command_type() == C_TYPE.C_ARITHMETIC:
            writer.write_arithmetic(parser.arg1())
        elif parser.command_type() == C_TYPE.C_LABEL:
            writer.write_label(parser.arg1())
        elif parser.command_type() == C_TYPE.C_GOTO:
            writer.write_goto(parser.arg1())
        elif parser.command_type() == C_TYPE.C_IF:
            writer.write_if(parser.arg1())
        elif parser.command_type() == C_TYPE.C_CALL:
            writer.write_call(parser.arg1(), parser.arg2())
        elif parser.command_type() == C_TYPE.C_FUNCTION:
            writer.write_function(parser.arg1(), parser.arg2())
        elif parser.command_type() == C_TYPE.C_RETURN:
            writer.write_return()

# main program
if os.path.isfile(sys.argv[1]) and sys.argv[1].endswith('.vm'):  # 参数为 .vm 文件,只翻译一个文件
    writer = CodeWriter(os.path.splitext(sys.argv[1])[0]+'.asm')
    writer.write_init()
    process_vm_file(sys.argv[1])
elif os.path.isdir(sys.argv[1]): # 参数为文件夹,将文件夹中所有文件翻译为 hack 汇编
    writer = CodeWriter(sys.argv[1]+'/'+os.path.basename(sys.argv[1])+'.asm')
    writer.write_init()
    for f in glob.glob(sys.argv[1] + '/*.vm'):
        process_vm_file(f)
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • [From Nand to Tetris] 第8章 虚拟机项目 python 实现
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档