这次回顾第八章的内容,这一章完成了虚拟机剩余部分的内容。

课程官网:

https://www.nand2tetris.org/

视频地址:

https://www.coursera.org/learn/build-a-computer

本章参考资料:

https://github.com/itzhak-razi/From-Nand-to-Tetris/tree/master/08

Part 1:课程回顾

背景介绍

这章的重点都体现在项目中,这部分从略。

Part 2:项目

CodeWriter

这部分的接口如下:

初始化

由于后续需要使用,初始化函数中增加了functionName以及returnCounter属性。

def __init__(self, filename):
	#打开文件,设置文件名,文件名的格式:目录/文件名
	self.setFileName(filename)
	self.file = open(filename + ".asm", "w+")
	self.functionName = ""
	#返回计数器
	self.returnCounter = 0
writeInit

代码初始化部分

def writeInit(self):
	#SP = 256
	self.file.write("//Init\n")
	self.file.write("@256\n")
	self.file.write("D=A\n")
	self.file.write("@SP\n")
	self.file.write("M=D\n")
	self.file.write("\n")
	#call Sys.init
	self.functionName = "Sys.init"
	self.writeCall("Sys.init", 0)
writeLabel

这部分比较简单,因为汇编语言中也存在标签:

def writeLabel(self, command, label):
	self.file.write("//" + command + " " + label + "\n")
	self.file.write("(" + self.functionName + "$" + label + ")\n")
	#增加换行
	self.file.write("\n")
writeGoto

无条件Goto,使用汇编语言中存在的命令即可:

def writeGoto(self, command, label):
	self.file.write("//" + command + " " + label + "\n")
	self.file.write("@" + self.functionName + "$" + label + "\n")
	self.file.write("0;JMP\n")
	#增加换行
	self.file.write("\n")
writeIf

利用汇编语言中的条件分支即可,注意这里要更新栈顶位置:

def writeIf(self, command, label):
	self.file.write("//" + command + " " + label + "\n")
	self.file.write("@SP\n")
	#更新栈顶,容易出错
	self.file.write("M=M-1\n")
	self.file.write("A=M\n")
	self.file.write("D=M\n")
	#判断是否为False
	self.file.write("@0\n")
	self.file.write("D=D-A\n")
	self.file.write("@" + self.functionName + "$" + label + "\n")
	self.file.write("D;JNE\n")
	#增加换行
	self.file.write("\n")

其余三个功能较复杂,首先给出伪代码:

writeCall

按照上图的伪代码一步一步实现即可,这里最重要的一点是

retAddrLabel = "return_address_" + str(self.returnCounter)
self.returnCounter += 1

上述代码是为了记录返回地址,注意这里一定要使用returnCounter,因为可能出现递归的情形,完整代码如下:

def writeCall(self, functionName, numArgs):
	self.file.write("//" + functionName + " " + str(numArgs) + "\n")
	
	#递归
	retAddrLabel = "return_address_" + str(self.returnCounter)
	self.returnCounter += 1

	#push retAddrLabel
	self.file.write("@" + retAddrLabel + "\n")
	self.file.write("D=A\n")
	self.file.write("@SP\n")
	self.file.write("A=M\n")
	self.file.write("M=D\n")
	self.file.write("@SP\n")
	self.file.write("M=M+1\n")

	#push retAddrLabel, LCL,ARG,THIS,THAT
	Tag = ["LCL", "ARG", "THIS", "THAT"]
	for tag in Tag:
		#获得值
		self.file.write("@" + tag + "\n")
		self.file.write("D=M\n")
		#更新栈顶值
		self.file.write("@SP\n")
		self.file.write("A=M\n")
		self.file.write("M=D\n")
		#更新栈顶位置
		self.file.write("@SP\n")
		self.file.write("M=M+1\n")
		
	#ARG = SP-5-nArgs
	self.file.write("@SP\n")
	self.file.write("D=M\n")
	self.file.write("@" + str(numArgs) + "\n")
	self.file.write("D=D-A\n")
	self.file.write("@5\n")
	self.file.write("D=D-A\n")
	self.file.write("@ARG\n")
	self.file.write("M=D\n")
	#LCL = SP
	self.file.write("@SP\n")
	self.file.write("D=M\n")
	self.file.write("@LCL\n")
	self.file.write("M=D\n")
	#goto functionName
	self.file.write("@" + functionName + "\n")
	self.file.write("0;JMP\n")
	#(retAddrLabel)
	self.file.write("(" + retAddrLabel + ")\n")
	#增加换行
	self.file.write("\n")
writeFunction

按照伪代码实现即可

def writeFunction(self, functionName, numLocals):
	#设置函数名
	self.functionName = functionName
	
	self.file.write("//" + functionName + " " + str(numLocals) + "\n")
	#(functionName)
	self.file.write("(" + functionName + ")\n")
	for i in range(numLocals):
		self.file.write("@SP\n")
		self.file.write("A=M\n")
		self.file.write("M=0\n")
		self.file.write("@SP\n")
		self.file.write("M=M+1\n")
	#增加换行
	self.file.write("\n")
writeReturn
def writeReturn(self):
	self.file.write("//return\n")
	#endFrame = LCL
	self.file.write("@LCL\n")
	self.file.write("D=M\n")
	self.file.write("@endFrame\n")
	self.file.write("M=D\n")
	#retAddr = *(endFrame – 5)
	self.file.write("@5\n")
	self.file.write("A=D-A\n")
	self.file.write("D=M\n")
	self.file.write("@RET\n")
	self.file.write("M=D\n")
	#*ARG = pop()
	self.file.write("@SP\n")
	self.file.write("A=M-1\n")
	self.file.write("D=M\n")
	self.file.write("@ARG\n")
	self.file.write("A=M\n")
	self.file.write("M=D\n")
	
	#SP = ARG + 1
	self.file.write("@ARG\n")
	self.file.write("D=M\n")
	self.file.write("@SP\n")
	self.file.write("M=D+1\n")
	#更新
	Tag = ["THAT", "THIS", "ARG", "LCL"]
	for i in range(len(Tag)):
		self.file.write("@" + str(i+1) + "\n")
		self.file.write("D=A\n")
		self.file.write("@endFrame\n")
		self.file.write("A=M-D\n")
		self.file.write("D=M\n")
		self.file.write("@" + Tag[i] + "\n")
		self.file.write("M=D\n")

	#goto retAddr
	self.file.write("@RET\n")
	#地址
	self.file.write("A=M\n")
	#self.file.write("@return-address\n")
	self.file.write("0;JMP\n")
	
	#增加换行
	self.file.write("\n")

VMTranslator

此处需要处理单独文件和一个目录文件分别处理:

from Parser import Parser
from CodeWriter import CodeWriter
import sys
import os

if __name__ == "__main__":
    if len(sys.argv) != 2:
        print("Error")
        sys.exit(1)
    #划分输入
    data = sys.argv[-1].split("/")
    #文件名
    filename = data[-1]
    #划分文件名
    Name = filename.split(".")
    #单个文件
    if len(Name) == 2:
        #不带后缀的文件名    
        name = Name[0]
        #输入
        name1 = './' + '/'.join(data)
        name2 = './' + '/'.join(data[:-1] + [name])
        #构造类
        parser = Parser(name1)
        codewriter = CodeWriter(name2)
        
        while parser.hasMoreCommands():
            parser.advance()
            command = parser.command[0]
            #print(command)
            if parser.commandType() == "C_ARITHMETIC":
                codewriter.writeArithmetic(command)
            elif parser.commandType() == "C_PUSH" or parser.commandType() == "C_POP":
                segment = parser.arg1()
                index = parser.arg2()
                codewriter.writePushPop(command, segment, index)
            elif parser.commandType() == "C_LABEL":
                label = parser.arg1()
                codewriter.writeLabel(command, label)
            elif parser.commandType() == "C_GOTO":
                label = parser.arg1()
                codewriter.writeGoto(command, label)
            elif parser.commandType() == "C_IF":
                label = parser.arg1()
                codewriter.writeIf(command, label)
            elif parser.commandType() == "C_CALL":
                functionName = parser.arg1()
                numArgs = int(parser.arg2())
                codewriter.writeCall(functionName, numArgs)
            elif parser.commandType() == "C_FUNCTION":
                functionName = parser.arg1()
                numLocals = int(parser.arg2())
                codewriter.writeFunction(functionName, numLocals)
            else:
                codewriter.writeReturn()
                
        codewriter.close()
    #目录
    else:
        path = os.listdir(sys.argv[-1])
        #不带后缀的文件名    
        name = Name[0]
        #输入
        file = './' + '/'.join(data + [name])
        #构造类
        codewriter = CodeWriter(file)
        #初始化
        codewriter.writeInit()
        #遍历文件
        File = []
        for i in path:
            filename = i.split(".")
            if filename[-1] == "vm":
                File.append(filename[0])
        if "Sys" in File:
            File.remove("Sys")
            File = ["Sys"] + File
        #循环
        for filename in File:
            name = '/'.join(data) + '/' + filename + '.vm'
            #设置文件名
            codewriter.setFileName(filename)
            parser = Parser(name)
            while parser.hasMoreCommands():
                parser.advance()
                command = parser.command[0]
                #print(command)
                if parser.commandType() == "C_ARITHMETIC":
                    codewriter.writeArithmetic(command)
                elif parser.commandType() == "C_PUSH" or parser.commandType() == "C_POP":
                    segment = parser.arg1()
                    index = parser.arg2()
                    codewriter.writePushPop(command, segment, index)
                elif parser.commandType() == "C_LABEL":
                    label = parser.arg1()
                    codewriter.writeLabel(command, label)
                elif parser.commandType() == "C_GOTO":
                    label = parser.arg1()
                    codewriter.writeGoto(command, label)
                elif parser.commandType() == "C_IF":
                    label = parser.arg1()
                    codewriter.writeIf(command, label)
                elif parser.commandType() == "C_CALL":
                    functionName = parser.arg1()
                    numArgs = int(parser.arg2())
                    codewriter.writeCall(functionName, numArgs)
                elif parser.commandType() == "C_FUNCTION":
                    functionName = parser.arg1()
                    numLocals = int(parser.arg2())
                    codewriter.writeFunction(functionName, numLocals)
                elif parser.commandType() == "C_RETURN":
                    codewriter.writeReturn()
        
        codewriter.close()