[Nand2Tetris] Static Segment

조현호_ChoHyeonHo·2025년 1월 13일

Static segment is sharable among all classes. This is how you implement it.

If you declare a static i in a program such as Foo.vm, vm translator automatically translate it into an assembly symbol Foo.i .

그래서 내가 해야 하는 건, push/pop static index 에 대해 target을 classname.i로 번역하여 저장하도록 구현하는 것. -> 이것이 옳은 전제인지 어셈블리 코드로 한번 테스트해보자.

예제 클래스인 VMBasicTest.asm에 임시로 VMBasicTest.0을 작성하였을 때 @16으로 매핑되는 것을 위와 같이 제대로 작동함을 볼수 있다. 그럼 궁금한 점. 이 매핑을 VM Translator가 하는 것은 아니지 않나? 즉, VM translator는 push/pop static index에 대해서만 @ClassName.index 식으로 매핑하지, 이것을 다시 메모리 위치로 매핑하는 것은 누가 하는 거지?

결론: 어셈블러가 해준다. 아래는 자세한 설명


당신이 설명한 내용은 VM Translator와 실제 어셈블리 수준의 메모리 매핑 작업 간의 관계를 잘 짚어내고 있습니다. 이를 조금 더 구체적으로 분리하여 살펴보겠습니다.

  1. VM Translator의 역할

VM Translator는 VM 코드를 Hack 어셈블리 코드로 변환합니다. 예를 들어, push static 0 또는 pop static 0 같은 명령이 주어졌다면, Translator는 이를 다음과 같이 변환합니다:
• push static 0 → @ClassName.0 식의 어셈블리 코드 생성 후, 적절한 메모리 작업 (D 레지스터로 값을 가져오거나 스택에 푸시) 수행.
• pop static 0 → @ClassName.0으로 접근한 뒤, 스택에서 값을 꺼내 저장.

즉, VM Translator는 static 세그먼트를 ClassName.index라는 심볼로 변환하며, 해당 심볼을 어셈블리 코드로 출력하는 역할만 수행합니다.

  1. 심볼 → 메모리 주소 매핑

@ClassName.index 같은 심볼을 실제 메모리 주소로 매핑하는 작업은 Hack 컴파일러의 어셈블러(Assembler)가 담당합니다.
• Hack 어셈블러는 심볼 테이블을 유지하며, 심볼에 대해 매핑되지 않은 경우, 특정 메모리 영역을 할당합니다.
• 예를 들어, VMBasicTest.0이라는 심볼은 기본적으로 메모리 주소 16부터 할당되며, 이후 등장하는 새로운 심볼들은 순차적으로 다음 메모리 주소(17, 18, …)에 할당됩니다.
따라서 VM Translator는 이 심볼을 단순히 생성할 뿐, 구체적으로 어떤 메모리 주소를 사용할지는 어셈블러가 결정합니다.

  1. Static 세그먼트의 동작 방식

static 세그먼트는 파일 단위로 관리되며, 동일한 클래스 내의 여러 static 변수는 각각 고유한 심볼로 매핑됩니다.
• VM Translator는 파일 이름(클래스 이름)을 참조하여 심볼 네이밍을 관리합니다. 예를 들어, Foo.vm에서 static 0이 등장하면 Foo.0으로 변환합니다.
• 이렇게 변환된 심볼은 나중에 어셈블러가 처리하면서 실제 메모리 주소를 할당받습니다.

  1. 어셈블리 코드 테스트 결과

당신이 보여준 테스트 결과에서:
• VMBasicTest.0이 @16으로 매핑된 것은 Hack 어셈블러가 심볼을 메모리 주소로 매핑한 결과입니다.
• 이 매핑은 VM Translator가 아닌 어셈블러의 역할입니다.

  1. 정리

    VM Translator는 push/pop static index를 @ClassName.index로 변환하는 역할만 수행하며, 이를 실제 메모리 주소로 매핑하는 것은 Hack 어셈블러가 담당합니다.

즉, Translator와 어셈블러의 역할이 명확히 분리되어 있습니다.
• Translator: VM 명령어 → 어셈블리 코드
• Assembler: 어셈블리 코드 → 바이트코드 (심볼 매핑 포함)

추가적으로 궁금한 점이나 구현 과정에서의 어려움이 있으면 알려주세요!


매우 중요

static 매핑은 아주 신기하다. 그러니까

push constant 100
push constant 200
pop static 5
pop static 2

이렇게 돼 있을 경우 물리적으로

static 5 -> @16
static 2 -> @17

이렇게 어셈블러가 매핑을 한다. 그러니까 따로 인덱스를 찾는 작업이 필요 없다. 이를 푸쉬/팝 연산에서 구현하기 위해 segment_resolver와 index_resolver를 구현하여 static 변수의 index를 예외적으로 처리하도록 하였다.

    def segment_resolver(self, segment, index):
        if (segment == 'static'):
            return f"@{self.segment_mapper[segment]}.{index}\n"
        return f"@{self.segment_mapper[segment]}\n"
    def index_resolver(self, segment, index):
        result = ''
        if (segment == 'static'): return ''
        for _ in range(int(index)):
            result += f'A=A+1\n'
        return result
profile
Behold the rabbit hole

0개의 댓글