今天折騰一上午,終於 完成了 Coursera 上 From Nand to Tetris / Part I 這個課程的最後一個彙編器項目。這套課程真是沒白跟,收穫良多,如今已經等不及想看下一期的軟件部分了,哈哈。python
下面是個人 python 實現,存個檔,同時給一樣在看這課程的同窗們參考。編碼
註釋風格看起來可能有點奇怪,拍腦殼想的,沒多少 python 編碼經驗,還望包涵,稍微解釋一下:spa
#-----------------# # 大塊代碼用途描述 # #-----------------# ## 分級註釋 ### 分級註釋 #### 分級註釋
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()