← SEARCHABLE ARCHIVE
Nand2Tetris · 2026-05-20

Nand2Tetris — 8장

VM 번역기 II: 프로그램 흐름과 함수 호출

INTERACTIVE READING MODE →

# VM Translator 2
# 입력: .vm 파일 또는 디렉터리
# 출력: .asm 파일
# 범위: program flow, function call / return
# 동작:
# bootstrap에서 SP=256 설정 후 Sys.init 호출
# call은 return-address와 LCL/ARG/THIS/THAT을 저장하고 새 프레임으로 이동함
# return은 FRAME=LCL, RET=*(FRAME-5)를 복원한 뒤 호출자 위치로 점프함

class VMWriter:
    def __init__(self):
        self.call_id = 0
        self.current_function = ""

    def scoped(self, label):
        if self.current_function:
            return f"{self.current_function}${label}"
        return label

    def bootstrap(self):
        return [
            "@256",
            "D=A",
            "@SP",
            "M=D",
        ] + self.write_call("Sys.init", 0)

    def write_label(self, label):
        return [f"({self.scoped(label)})"]

    def write_goto(self, label):
        return [
            f"@{self.scoped(label)}",
            "0;JMP",
        ]

    def write_if(self, label):
        return [
            "@SP",
            "AM=M-1",
            "D=M",
            f"@{self.scoped(label)}",
            "D;JNE",
        ]

    def write_function(self, name, local_count):
        self.current_function = name
        code = [f"({name})"]

        for _ in range(int(local_count)):
            code += [
                "@SP",
                "A=M",
                "M=0",
                "@SP",
                "M=M+1",
            ]

        return code

    def push_register(self, register):
        return [
            f"@{register}",
            "D=M",
            "@SP",
            "A=M",
            "M=D",
            "@SP",
            "M=M+1",
        ]

    def write_call(self, name, arg_count):
        return_label = f"{name}$ret.{self.call_id}"
        self.call_id += 1

        code = [
            f"@{return_label}",
            "D=A",
            "@SP",
            "A=M",
            "M=D",
            "@SP",
            "M=M+1",
        ]

        for register in ["LCL", "ARG", "THIS", "THAT"]:
            code += self.push_register(register)

        code += [
            "@SP",
            "D=M",
            f"@{int(arg_count) + 5}",
            "D=D-A",
            "@ARG",
            "M=D",
            "@SP",
            "D=M",
            "@LCL",
            "M=D",
            f"@{name}",
            "0;JMP",
            f"({return_label})",
        ]

        return code

    def write_return(self):
        return [
            "@LCL",
            "D=M",
            "@R13",
            "M=D",
            "@5",
            "A=D-A",
            "D=M",
            "@R14",
            "M=D",
            "@SP",
            "AM=M-1",
            "D=M",
            "@ARG",
            "A=M",
            "M=D",
            "@ARG",
            "D=M+1",
            "@SP",
            "M=D",
            "@R13",
            "AM=M-1",
            "D=M",
            "@THAT",
            "M=D",
            "@R13",
            "AM=M-1",
            "D=M",
            "@THIS",
            "M=D",
            "@R13",
            "AM=M-1",
            "D=M",
            "@ARG",
            "M=D",
            "@R13",
            "AM=M-1",
            "D=M",
            "@LCL",
            "M=D",
            "@R14",
            "A=M",
            "0;JMP",
        ]