VMトランスレーター後半 / write_function()、write_return() / SimpleFunction.vm

Chapter 8

今度はwrite_function()write_return()とをCode_Writer.pyに追加する。Figure 8.5 (p.161)の疑似コードを実装する。テキストに用意されているSimpleFunction.vmで試す。

Code_Writer.py:

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.__write_line(self.calcs[command])

        # 二項演算子の場合:
        # さらにスタックポインタをデクリメントして、
        # スタックポインタの指し示すアドレスをAレジスタに取り出して、
        elif self.__is_binary_op(command):
            self.__decrement_pointer("SP")
            self.__write_line("A=M")

            # 比較演算子の場合:
            # 比較する手段として、今のスタックポインタの位置にある値からDレジスタの値を引いて、
            if self.__is_comparison_op(command):
                self.__write_line("D=M-D")

                true_label = "TRUE" + str(self.label_count)
                end_label = "END" + str(self.label_count)
                self.label_count += 1

                # 引き算の結果が比較演算子にtrueなら(イ)に飛んで、
                self.__write_line("@" + true_label) # (ここでどうしてもAレジスタの値が変化する)
                self.__write_line(self.jumps[command])

                # 引き算の結果がfalseなら、
                # ここに来る前にAレジスタの値が変化したのでスタックポインタの位置を取得し直して、
                # 今のスタックポインタの位置に0 (false)を書き込んで、
                # (ロ)に飛んで、抜ける。
                self.__load_a_with_pointer("SP")
                self.__write_line("M=0")
                self.__write_line("@" + end_label)
                self.__write_line("0;JMP")

                # (イ)
                # ここに来る前にAレジスタの値が変化したのでスタックポインタの位置を取得し直して、
                # 今のスタックポインタの位置に-1 (true、全ビット1)を書き込んで、
                # 抜ける。
                self.__write_line("(" + true_label + ")")
                self.__load_a_with_pointer("SP")
                self.__write_line("M=-1")

                # (ロ)
                self.__write_line("(" + end_label + ")")

            # 和、差、論理積、論理和の場合:
            # 今のスタックポインタの位置にある値と先に取り出したDレジスタの値との演算結果を
            # 今のスタックポインタの位置に上書きして、    
            else:
                self.__write_line(self.calcs[command])

        # 最後にスタックポインタをインクリメントする。
        self.__increment_pointer("SP")
        return self

    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.__write_line("D=M") # そのアドレスにある値を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の指し示すアドレスの位置に書き込む。

        return self

    def write_label(self, label):
        self.__write_line("(" + label + ")")

    def write_goto(self, label):
        self.__write_line("@" + label)
        self.__write_line("0;JMP")

    def write_if(self, label):
        # スタックの最後尾値が0でない場合(falseでない場合)にジャンプする。
        self.__decrement_pointer("SP") 
        self.__load_d_with_ram_ram_pointer("SP")
        self.__write_line("@" + label)
        self.__write_line("D;JNE")

    def write_function(self, function_name, num_vars):
        self.write_label(function_name) # ラベルを書き込んで、
        for i in range(num_vars): # num_vars個あるローカル変数を初期化する。
            self.write_push_pop("C_PUSH", "constant", 0)

    def write_return(self):
        # R14にLCLを假置きする。
        self.__write_line("@LCL")
        self.__write_line("D=M")
        self.__load_generic_regisiter_with_d("R14")

        # R15にリターンアドレスを假置きする。
        self.__write_line("@5")
        self.__write_line("A=D-A")
        self.__write_line("D=M")
        self.__load_generic_regisiter_with_d("R15")
        #self.__write_line("@R15")
        #self.__write_line("M=D")
        
        # スタック最後尾値をARG[0]に格納する(ポップにはR13を使っている)
        self.write_push_pop("C_POP", "argument", 0)
        
        # SPをARG+1にセットする。
        self.__write_line("@ARG")
        self.__write_line("D=M+1")
        self.__write_line("@SP")
        self.__write_line("M=D")

        # LCLからTHAT, THIS, ARG, LCLの順番に復元する。
        #for i, segment in enumerate(["THAT", "THIS", "ARG", "LCL"]):
        for segment in ["THAT", "THIS", "ARG", "LCL"]:
            self.__write_line("@R14")
            self.__write_line("AM=M-1")
            self.__write_line("D=M")
            self.__load_generic_regisiter_with_d(segment)
            #self.__write_line("@" + segment)
            #self.__write_line("M=D")

        # リターンアドレスにジャンプする。
        self.__load_a_with_pointer("R15")
        self.__write_line("0;JMP")

    def write_halt(self):
        self.__write_line("@HALT")
        self.__write_line("(HALT)")
        self.__write_line("0;JMP")

    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 load_ram_address_with_immediate_value(self, address, immediate_value):
        self.__write_line("@" + str(immediate_value))
        self.__write_line("D=A")
        self.__write_line("@" + str(address))
        self.__write_line("M=D")


    def __write_line(self, command_string):
        self.file.write(command_string + "\n")

    def __decrement_pointer(self, pointer_name):
        self.__write_line("@" + pointer_name)
        self.__write_line("M=M-1")
    def __increment_pointer(self, pointer_name):
        self.__write_line("@" + pointer_name)
        self.__write_line("M=M+1")
        
    def __load_d_with_immediate_value(self, immediate_value):
        self.__write_line("@" + str(immediate_value))
        self.__write_line("D=A")
    def __load_a_with_pointer(self, pointer_name):
        self.__write_line("@" + pointer_name)
        self.__write_line("A=M")
    def __load_d_with_ram_ram_pointer(self, pointer_name):
        self.__write_line("@" + pointer_name)
        self.__write_line("A=M")
        self.__write_line("D=M")
    def __load_ram_ram_pointer_with_d(self, pointer_name):
        self.__write_line("@" + pointer_name)
        self.__write_line("A=M")
        self.__write_line("M=D")
    def __load_generic_regisiter_with_d(self, ram_index):
        self.__write_line("@" + ram_index)
        self.__write_line("M=D")
    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.__write_line("@" + str(base_address)) # 当該ポインタのベースアドレス(即値で指定)をAレジスタに読み込んで、
        self.__write_line(register + "=D+A") # オフセットしたアドレスをどれかレジスタに読み込んで、


    
        
########################################################################
if __name__ == "__main__":
    c = Code_Writer("Test.asm")

    c.load_ram_address_with_immediate_value(0, 256) # SP

    c.write_label("SUTE0")
    c.write_goto("SUTE0")

    c.write_label("SUTE1")
    c.write_if("SUTE1")

    c.write_halt()
    c.close()

VM_Translator:

from Parser import *
from Code_Writer import *

# .vmファイルを入力して.asmファイルを出力する。
class VM_Translator:
    def __init__(self, vm_file_path):
        self.parser = Parser(vm_file_path)

        asm_file_path = self.__replace_file_extension(vm_file_path, "asm")
        self.code_writer = Code_Writer(asm_file_path)

    def generate_asm(self):
        while self.parser.has_more_lines():
            self.parser.advance()
            
            command_type = self.parser.command_type()
            arg1 = self.parser.arg1()
            arg2 = self.parser.arg2()
            
            if command_type == "C_PUSH" or command_type == "C_POP":
                self.code_writer.write_push_pop(command_type, arg1, arg2)

            elif command_type == "C_ARITHMETIC":
                self.code_writer.write_arithmetic(arg1)

            elif command_type == "C_LABEL":
                self.code_writer.write_label(arg1)

            elif command_type == "C_GOTO":
                self.code_writer.write_goto(arg1)

            elif command_type == "C_IF":
                self.code_writer.write_if(arg1)

            elif command_type == "C_FUNCTION":
                self.code_writer.write_function(arg1, arg2)

            elif command_type == "C_RETURN":
                self.code_writer.write_return()

        self.code_writer.write_halt()
        self.parser.close()
        self.code_writer.close()

    def __replace_file_extension(self, file_name, new_extension):
        return file_name.split(".")[0] + "." + new_extension
 

############################################################
if __name__ == "__main__":
    a = VM_Translator("SimpleFunction\SimpleFunction.vm")
    a.code_writer.load_ram_address_with_immediate_value(0, 317) # SP。天下り的にセットする。
    a.code_writer.load_ram_address_with_immediate_value(1, 317) # LCL。天下り的にセットする。
    a.code_writer.load_ram_address_with_immediate_value(2, 310) # ARG。天下り的にセットする。
    a.code_writer.load_ram_address_with_immediate_value(3, 3000) # THIS。天下り的にセットする。
    a.code_writer.load_ram_address_with_immediate_value(4, 4000) # THAT。天下り的にセットする。
    
    a.code_writer.load_ram_address_with_immediate_value(310+0, 1234) # ARG[]。天下り的にセットする。
    a.code_writer.load_ram_address_with_immediate_value(310+1, 37) # ARG[]。天下り的にセットする。
    a.code_writer.load_ram_address_with_immediate_value(310+2, 9) # ARG[]。天下り的にセットする。
    a.code_writer.load_ram_address_with_immediate_value(310+3, 305) # ARG[]。天下り的にセットする。
    a.code_writer.load_ram_address_with_immediate_value(310+4, 300) # ARG[]。天下り的にセットする。
    a.code_writer.load_ram_address_with_immediate_value(310+5, 3010) # ARG[]。天下り的にセットする。
    a.code_writer.load_ram_address_with_immediate_value(310+6, 4010) # ARG[]。天下り的にセットする。
    
    a.generate_asm()

正解:

実行結果: