From Nand to Tetris week 8
这次回顾第八章的内容,这一章完成了虚拟机剩余部分的内容。
课程官网:
视频地址:
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()
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Doraemonzzz!
评论
ValineLivere