From Nand to Tetris week 8

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

课程官网:

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属性。

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

代码初始化部分

1
2
3
4
5
6
7
8
9
10
11
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

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

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

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

1
2
3
4
5
6
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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
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

按照伪代码实现即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
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()

本文标题:From Nand to Tetris week 8

文章作者:Doraemonzzz

发布时间:2019年06月26日 - 19:20:00

最后更新:2019年06月26日 - 19:10:35

原始链接:http://doraemonzzz.com/2019/06/26/From Nand to Tetris week 8/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。