← SEARCHABLE ARCHIVE
Nand2Tetris · 2026-05-20

Nand2Tetris — 7장

VM 번역기 I: 스택 연산과 메모리 세그먼트

INTERACTIVE READING MODE →

# VM Translator 1
# 입력: .vm 파일
# 출력: .asm 파일
# 범위: push / pop / arithmetic 명령어
# 동작:
# stack pointer는 SP가 가리키고,
# push는 *SP=value 후 SP++,
# pop은 SP-- 후 value를 해당 segment에 저장함

import sys


SEGMENT_BASE = {
    "local": "LCL",
    "argument": "ARG",
    "this": "THIS",
    "that": "THAT",
}


class VMTranslator:
    def __init__(self):
        self.label_id = 0

    def push_d(self):
        return [
            "@SP",
            "A=M",
            "M=D",
            "@SP",
            "M=M+1",
        ]

    def pop_to_d(self):
        return [
            "@SP",
            "AM=M-1",
            "D=M",
        ]

    def write_arithmetic(self, command):
        if command == "add":
            return ["@SP", "AM=M-1", "D=M", "A=A-1", "M=M+D"]
        if command == "sub":
            return ["@SP", "AM=M-1", "D=M", "A=A-1", "M=M-D"]
        if command == "and":
            return ["@SP", "AM=M-1", "D=M", "A=A-1", "M=M&D"]
        if command == "or":
            return ["@SP", "AM=M-1", "D=M", "A=A-1", "M=M|D"]
        if command == "neg":
            return ["@SP", "A=M-1", "M=-M"]
        if command == "not":
            return ["@SP", "A=M-1", "M=!M"]

        # eq, gt, lt는 비교 결과를 true(-1) 또는 false(0)로 stack top에 저장함
        jump = {"eq": "JEQ", "gt": "JGT", "lt": "JLT"}[command]
        true_label = f"TRUE_{self.label_id}"
        end_label = f"END_{self.label_id}"
        self.label_id += 1

        return [
            "@SP",
            "AM=M-1",
            "D=M",
            "A=A-1",
            "D=M-D",
            f"@{true_label}",
            f"D;{jump}",
            "@SP",
            "A=M-1",
            "M=0",
            f"@{end_label}",
            "0;JMP",
            f"({true_label})",
            "@SP",
            "A=M-1",
            "M=-1",
            f"({end_label})",
        ]

    def write_push(self, segment, index, file_name):
        if segment == "constant":
            return [f"@{index}", "D=A"] + self.push_d()

        if segment in SEGMENT_BASE:
            return [
                f"@{SEGMENT_BASE[segment]}",
                "D=M",
                f"@{index}",
                "A=D+A",
                "D=M",
            ] + self.push_d()

        if segment == "temp":
            return [f"@{5 + int(index)}", "D=M"] + self.push_d()

        if segment == "pointer":
            return [f"@{'THIS' if index == '0' else 'THAT'}", "D=M"] + self.push_d()

        if segment == "static":
            return [f"@{file_name}.{index}", "D=M"] + self.push_d()

        raise ValueError(segment)

    def write_pop(self, segment, index, file_name):
        if segment in SEGMENT_BASE:
            return [
                f"@{SEGMENT_BASE[segment]}",
                "D=M",
                f"@{index}",
                "D=D+A",
                "@R13",
                "M=D",
            ] + self.pop_to_d() + [
                "@R13",
                "A=M",
                "M=D",
            ]

        if segment == "temp":
            return self.pop_to_d() + [f"@{5 + int(index)}", "M=D"]

        if segment == "pointer":
            return self.pop_to_d() + [f"@{'THIS' if index == '0' else 'THAT'}", "M=D"]

        if segment == "static":
            return self.pop_to_d() + [f"@{file_name}.{index}", "M=D"]

        raise ValueError(segment)