今天折腾一上午,终于 完成了 Coursera 上 From Nand to Tetris / Part I 这个课程的最后一个汇编器项目。这套课程真是没白跟,收获良多,现在已经等不及想看下一期的软件部分了,哈哈。
下面是我的 python 实现,存个档,同时给同样在看这课程的同学们参考。
注释风格看起来可能有点奇怪,拍脑袋想的,没多少 python 编码经验,还望包涵,稍微解释一下:
#-----------------#
# 大块代码用途描述 #
#-----------------#
## 分级注释
### 分级注释
#### 分级注释
import sys
import os.path
#--------#
# tables #
#--------#
## symbol table
SYMB_TABLE = {
\"SP\": 0,
\"LCL\": 1,
\"ARG\": 2,
\"THIS\": 3,
\"THAT\": 4,
\"R0\": 0,
\"R1\": 1,
\"R2\": 2,
\"R3\": 3,
\"R4\": 4,
\"R5\": 5,
\"R6\": 6,
\"R7\": 7,
\"R8\": 8,
\"R9\": 9,
\"R10\": 10,
\"R11\": 11,
\"R12\": 12,
\"R13\": 13,
\"R14\": 14,
\"R15\": 15,
\"SCREEN\": 16384,
\"KBD\": 24576
}
## comp table
COMP_TABLE = {
\"0\": \"0101010\",
\"1\": \"0111111\",
\"-1\": \"0111010\",
\"D\": \"0001100\",
\"A\": \"0110000\",
\"!D\": \"0001101\",
\"!A\": \"0110001\",
\"-D\": \"0001111\",
\"-A\": \"0110011\",
\"D+1\": \"0011111\",
\"A+1\": \"0110111\",
\"D-1\": \"0001110\",
\"A-1\": \"0110010\",
\"D+A\": \"0000010\",
\"D-A\": \"0010011\",
\"A-D\": \"0000111\",
\"D&A\": \"0000000\",
\"D|A\": \"0010101\",
\"M\": \"1110000\",
\"!M\": \"1110001\",
\"-M\": \"1110011\",
\"M+1\": \"1110111\",
\"M-1\": \"1110010\",
\"D+M\": \"1000010\",
\"D-M\": \"1010011\",
\"M-D\": \"1000111\",
\"D&M\": \"1000000\",
\"D|M\": \"1010101\"
}
## dest table
DEST_TABLE = {
\"null\": \"000\",
\"M\": \"001\",
\"D\": \"010\",
\"MD\": \"011\",
\"A\": \"100\",
\"AM\": \"101\",
\"AD\": \"110\",
\"AMD\": \"111\"
}
## jump table
JUMP_TABLE = {
\"null\": \"000\",
\"JGT\": \"001\",
\"JEQ\": \"010\",
\"JGE\": \"011\",
\"JLT\": \"100\",
\"JNE\": \"101\",
\"JLE\": \"110\",
\"JMP\": \"111\"
}
#------------------#
# helper functions #
#------------------#
## determine is Int
def isInt(str):
try:
int(str)
return True
except ValueError:
return False
## determine instruction type
def getInsType(ins):
if ins[0] == \'@\':
return \'a\'
return \'c\'
## split instruction
### instruction A
ram_variable_num = 16
def valueOfAIns(ins):
global ram_variable_num
if SYMB_TABLE.has_key(ins[1:]):
ins = SYMB_TABLE[ins[1:]]
elif isInt(ins[1:]):
ins = ins[1:]
else:
SYMB_TABLE[ins[1:]] = ram_variable_num
ram_variable_num += 1
ins = SYMB_TABLE[ins[1:]]
bin_value = bin(int(ins))[2:]
zero_count = 16 - len(bin_value)
zero_str = \'0\' * zero_count
return zero_str + bin_value
### instruction C
def splitCIns(ins):
c_parts = {}
dest_splited = ins.split(\'=\')
if len(dest_splited) == 1:
c_parts[\'dest\'] = \'null\'
jump_splited = dest_splited[0].split(\';\')
else:
c_parts[\'dest\'] = dest_splited[0]
jump_splited = dest_splited[1].split(\';\')
if len(jump_splited) == 1:
c_parts[\'jump\'] = \'null\'
else:
c_parts[\'jump\'] = jump_splited[1]
c_parts[\'comp\'] = jump_splited[0]
return c_parts
#------------#
# main logic #
#------------#
## first pass
### source file
sf_name = sys.argv[1]
sf = open(sf_name, \'r\')
### destination file
df_name = os.path.splitext(sf_name)[0] + \".tmp\"
df = open(df_name, \'w\')
line_num = 0
for ins in sf:
# comment
ins = ins.split(\'//\')[0]
# white space
ins = ins.strip()
if len(ins) == 0: continue
# label
if ins[0] == \'(\' and ins[-1] == \')\':
SYMB_TABLE[ins[1:-1]] = line_num
continue
df.write(ins + \'\\n\')
line_num += 1
sf.close()
df.close()
## second pass
### source file
sf_name = os.path.splitext(sf_name)[0] + \".tmp\"
sf = open(sf_name, \'r\')
### destination file
df_name = os.path.splitext(sf_name)[0] + \".hack\"
df = open(df_name, \'w\')
for ins in sf:
ins = ins.strip()
ins_type = getInsType(ins)
if ins_type == \'a\':
val = valueOfAIns(ins) + \'\\n\'
df.write(val)
elif ins_type == \'c\':
parts = splitCIns(ins)
val = \'111\' + COMP_TABLE[parts[\'comp\']] + DEST_TABLE[parts[\'dest\']] + JUMP_TABLE[parts[\'jump\']] + \'\\n\'
df.write(val)
sf.close()
df.close()