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와 실제 어셈블리 수준의 메모리 매핑 작업 간의 관계를 잘 짚어내고 있습니다. 이를 조금 더 구체적으로 분리하여 살펴보겠습니다.
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라는 심볼로 변환하며, 해당 심볼을 어셈블리 코드로 출력하는 역할만 수행합니다.
@ClassName.index 같은 심볼을 실제 메모리 주소로 매핑하는 작업은 Hack 컴파일러의 어셈블러(Assembler)가 담당합니다.
• Hack 어셈블러는 심볼 테이블을 유지하며, 심볼에 대해 매핑되지 않은 경우, 특정 메모리 영역을 할당합니다.
• 예를 들어, VMBasicTest.0이라는 심볼은 기본적으로 메모리 주소 16부터 할당되며, 이후 등장하는 새로운 심볼들은 순차적으로 다음 메모리 주소(17, 18, …)에 할당됩니다.
따라서 VM Translator는 이 심볼을 단순히 생성할 뿐, 구체적으로 어떤 메모리 주소를 사용할지는 어셈블러가 결정합니다.
static 세그먼트는 파일 단위로 관리되며, 동일한 클래스 내의 여러 static 변수는 각각 고유한 심볼로 매핑됩니다.
• VM Translator는 파일 이름(클래스 이름)을 참조하여 심볼 네이밍을 관리합니다. 예를 들어, Foo.vm에서 static 0이 등장하면 Foo.0으로 변환합니다.
• 이렇게 변환된 심볼은 나중에 어셈블러가 처리하면서 실제 메모리 주소를 할당받습니다.
당신이 보여준 테스트 결과에서:
• VMBasicTest.0이 @16으로 매핑된 것은 Hack 어셈블러가 심볼을 메모리 주소로 매핑한 결과입니다.
• 이 매핑은 VM Translator가 아닌 어셈블러의 역할입니다.
정리
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