VMトランスレーター / Code_Writerを作る / pushとpopと
Chapter 7
残っていたpushとpopとを追加してChapter 7のCode_Writerは終わり。
class Code_Writer: def __init__(self, file_path): self.file = open(file_path, "w") self.label_count = 0 self.calcs = { "neg":"M=-D", "not":"M=!D", "add":"M=D+M", "sub":"M=M-D", "and":"M=D&M", "or":"M=D|M",} self.jumps = { "eq":"D;JEQ", "gt":"D;JGT", "lt":"D;JLT",} self.segments = { "local":"LCL", "argument":"ARG", "this":"THIS", "that":"THAT", "pointer":3, "temp":5, "static":16,} def write_arithmetic(self, command): # まずスタックポインタをデクリメントして、 # 今のスタックポインタの位置にある値をDレジスタに取り出して、 self.__decrement_pointer("SP") self.__load_d_with_ram_ram_pointer("SP") # 単項演算子の場合: # 演算結果を今のスタックポインタの位置に上書きして、 if self.__is_unary_op(command): self.file.write(self.calcs[command] + "\n") # 二項演算子の場合: # さらにスタックポインタをデクリメントして、 # スタックポインタの指し示すアドレスをAレジスタに取り出して、 elif self.__is_binary_op(command): self.__decrement_pointer("SP") self.file.write("A=M\n") # 比較演算子の場合: # 比較する手段として、今のスタックポインタの位置にある値からDレジスタの値を引いて、 if self.__is_comparison_op(command): self.file.write("D=M-D\n") true_label = "TRUE" + str(self.label_count) end_label = "END" + str(self.label_count) self.label_count += 1 # 引き算の結果が比較演算子にtrueなら(イ)に飛んで、 self.file.write("@" + true_label + "\n") # (ここでどうしてもAレジスタの値が変化する) self.file.write(self.jumps[command] + "\n") # 引き算の結果がfalseなら、 # ここに来る前にAレジスタの値が変化したのでスタックポインタの位置を取得し直して、 # 今のスタックポインタの位置に0 (false)を書き込んで、 # (ロ)に飛んで、抜ける。 self.__load_a_with_pointer("SP") self.file.write("M=0\n") self.file.write("@" + end_label + "\n") self.file.write("0;JMP\n") # (イ) # ここに来る前にAレジスタの値が変化したのでスタックポインタの位置を取得し直して、 # 今のスタックポインタの位置に-1 (true、全ビット1)を書き込んで、 # 抜ける。 self.file.write("(" + true_label + ")\n") self.__load_a_with_pointer("SP") self.file.write("M=-1\n") # (ロ) self.file.write("(" + end_label + ")\n") # 和、差、論理積、論理和の場合: # 今のスタックポインタの位置にある値と先に取り出したDレジスタの値との演算結果を # 今のスタックポインタの位置に上書きして、 else: self.file.write(self.calcs[command] + "\n") # 最後にスタックポインタをインクリメントする。 self.__increment_pointer("SP") def write_push_pop(self, command, segment, index): if command == "C_PUSH": # 即値をプッシュする場合: if segment == "constant": self.__load_d_with_immediate_value(index) # 即値をDレジスタに読み込んで、 self.__load_ram_ram_pointer_with_d("SP") # Dレジスタの値を今のスタックポインタの位置に書き込んで、 # 各種ポインタの指し示すアドレスにある値をプッシュする場合: else: self.__load_register_with_offsetted_address("A", self.segments[segment], index) # オフセットしたアドレスをAレジスタに読み込んで、 self.file.write("D=M\n") # そのアドレスにある値をDレジスタに読み込んで、 self.__load_ram_ram_pointer_with_d("SP") # Dレジスタの値を今のスタックポインタの位置に書き込んで、 self.__increment_pointer("SP") # 最後にスタックポインタをインクリメントする。 elif command == "C_POP": self.__decrement_pointer("SP") # まずスタックポインタをデクリメントして、 self.__load_register_with_offsetted_address("D", self.segments[segment], index) # オフセットしたアドレスをDレジスタに読み込んで、 self.__load_generic_regisiter_with_d("R13") # そのDレジスタの値をR13に假置きして、 self.__load_d_with_ram_ram_pointer("SP") # 今のスタックポインタの位置にある値をDレジスタに読み込んで、 self.__load_ram_ram_pointer_with_d("R13") # それを、R13の指し示すアドレスの位置に書き込む。 def write_halt(self): self.file.write("@HALT\n") self.file.write("(HALT)\n") self.file.write("0;JMP\n") def close(self): self.file.close() ######################################################################## def set_pointer(self, pointer_name, val): self.file.write("@" + str(val) + "\n") self.file.write("D=A\n") self.file.write("@" + pointer_name + "\n") self.file.write("M=D\n") def __decrement_pointer(self, pointer_name): self.file.write("@" + pointer_name + "\n") self.file.write("M=M-1\n") def __increment_pointer(self, pointer_name): self.file.write("@" + pointer_name + "\n") self.file.write("M=M+1\n") def __load_d_with_immediate_value(self, immediate_value): self.file.write("@" + str(immediate_value) + "\n") self.file.write("D=A\n") def __load_a_with_pointer(self, pointer_name): self.file.write("@" + pointer_name + "\n") self.file.write("A=M\n") def __load_d_with_ram_ram_pointer(self, pointer_name): self.file.write("@" + pointer_name + "\n") self.file.write("A=M\n") self.file.write("D=M\n") def __load_ram_ram_pointer_with_d(self, pointer_name): self.file.write("@" + pointer_name + "\n") self.file.write("A=M\n") self.file.write("M=D\n") def __load_generic_regisiter_with_d(self, ram_index): self.file.write("@" + ram_index + "\n") self.file.write("M=D\n") def __is_unary_op(self, command): return command == "neg" or command == "not" def __is_binary_op(self, command): return not self.__is_unary_op(command) def __is_comparison_op(self, command): return command == "eq" or command == "gt" or command == "lt" def __load_register_with_offsetted_address(self, register, base_address, offset_val): self.__load_d_with_immediate_value(offset_val) # オフセット値をDレジスタに読み込んで、 try: self.__load_a_with_pointer(base_address) # 当該ポインタのベースアドレス(ポインタで指定)をAレジスタに読み込んで、 except: self.file.write("@" + str(base_address) + "\n") # 当該ポインタのベースアドレス(即値で指定)をAレジスタに読み込んで、 self.file.write(register + "=D+A\n") # オフセットしたアドレスをどれかレジスタに読み込んで、 ######################################################################## if __name__ == "__main__": c = Code_Writer("Test.asm") c.set_pointer("SP", 256) # 天下り的に設定しておく。 c.set_pointer("LCL", 300) # 天下り的に設定しておく。 c.set_pointer("ARG", 400) # 天下り的に設定しておく。 c.write_push_pop("C_PUSH", "constant", 3000) c.write_push_pop("C_POP", "pointer", 0) c.write_push_pop("C_PUSH", "constant", 3050) c.write_push_pop("C_POP", "pointer", 1) c.write_push_pop("C_PUSH", "constant", 1234) c.write_push_pop("C_POP", "this", 1) c.write_push_pop("C_PUSH", "this", 1) c.write_push_pop("C_POP", "temp", 3) c.write_push_pop("C_PUSH", "temp", 3) c.write_push_pop("C_POP", "static", 2) c.write_push_pop("C_PUSH", "static", 2) c.write_push_pop("C_POP", "local", 5) c.write_push_pop("C_PUSH", "local", 5) c.write_push_pop("C_POP", "argument", 10) c.write_push_pop("C_PUSH", "argument", 10) c.write_push_pop("C_POP", "that", 4) c.write_push_pop("C_PUSH", "that", 4) c.write_push_pop("C_POP", "this", 0) c.write_halt() c.close()
実行結果:
↓ R13を假置きに使っているのがダサいがほかにどうすればよいのか分からない。