View
6장-12장은 컴퓨터의 SW적 측면에 초점을 맞춘다. 컴파일러, 간단한 객체 프로그래밍 언어, 기본적인 운영체제를 만들어보자. 여기에서 가장 기본이 되는것이 어셈블러 _assembler이다. 어셈블리어로 작성한 프로그램을 어셈블러를 이용해 2진 코드로 번역하는 과정을 배우고, Hack 어셈블러를 만들자.
기호로 된 어셈블리 명령 (ADD, JUMP)과 2진코드는 1대1 대응이므로 어셈블러 작성은 어렵지 않다. 기호를 이용해 메모리를 참조하도록 기능을 추가하는 것이 까다롭다. 기호테이블 _symbol table을 통해 사용자 정의 기호를 물리적 주소에 연결하고 관리한다.
Hack의 어셈블러를 구현해보면서 다른 어셈블러를 만들 때 사용되는 핵심 소프트웨어 공학의 원리를 맛볼 수 있다.
배경
기계어는 기호형 또는 2진 형식으로 정의된다.
32Bit 패턴은 8 Bit의 연산코드 + 8 Bit 레지스터 + 16 Bit의 주소로 구성된다.
110000010000000110000000000000111 : Mem[7]의 값을 R3에 LOAD하라
이런 기초 명령어를 수백개 지원하기에 형식이 매우 복잡해지며, 이를 피하기 위해 기호를 사용한다. LOAD R3, 7같은 약속된 문법으로 명령어를 표기한 후 기호를 2진 코드로 바꾸는 편이 쉬우므로 코드는 기호로 작성한 뒤 컴퓨터가 프로그램을 2진 코드로 번역하도록 하는 것이 합리적이다. 따라서 어셈블리 언어로 코드를 작성하고, 이를 어셈블러를 이용해 2진코드로 다시 조립_assemble 하는 것이다.
기호
물건의 무게를 나타내는 변수 weight가 있다고 해보자. 이 변수가 7이라는 메모리 위치에 매핑되어있다고 생각해보자. 2진코드 단계에서는 weight 변수에 접근하고 싶다면 주소를 정확하게 7이라고 기입해야한다. 그러나, 어셈블리에서는 7 대신에 weight라는 기호를 사용할 수 있다. 마찬가지로 LOOP 기호가 250주소를 참조하도록 정했다면 goto 250
대신 goto LOOP
를 사용할 수 있다.
어셈블리에는 두 가지의 기호 도입 방법이 있다.
-
변수 : 프로그래머가 변수명을 기호로 쓰면 자동으로 그 기호에 주소가 할당된다. 이 주소는 크게 중요하지 않다.
-
레이블 : 프로그램 내의 위치 (명령어 주소)를 기호로 마크해둘 수 있다. 분기문 (goto) 등을 통해 해당 위치로 이동이 가능하다.
텍스트만 단순하게 처리하는 일을 한다면 단순했겠지만, 기호를 사용하면 일이 복잡해진다. 사용자 정의 변수나 기호 레이블을 실제 메모리주소에 매핑을 해야하는데, 이게 복잡한 작업이다. 이는 아래와 같은 방식으로 해결한다.기호 변환 Symbol Resolution<sum = 1+2+ ... + 100 을 계산하는 프로그램>i = 1 sum = 0 LOOP: if i = 101 goto END sum = sum + i i = i + 1 goto LOOP END: goto END
생성된 기호테이블
<기호로 변환한 코드>
M[1024] = 1
M[1025] = 0
if M[1024]=101 goto 6
M[1025] = M[1025] + M[1024]
M[1024] = M[1024] + 1
goto 2
goto 6
임의의 규칙을 가정해 기호변환을 만들어보자.
번역한 명령어는 0부터, 데이터는 1024부터 주소를 할당받으며, 소스코드에 새로운 변수가 나오면 기호테이블_symbol table에 (변수명, 값)이라는 행을 추가한다. 이 테이블이 다 만들어지면 기호가 없는 프로그램으로 옮긴다.
위 규칙에 따라 앞의 소스코드의 i와 sum은 1024, 1025번 주소에 할당된다. 모든 i와 sum이 물리적으로 같은 메모리에서 값을 쓰고 읽는다면 문제가 될 것이 없다.
그러나, 짚고 넘어가야할 사항이 있다.
- 앞의 주소할당에 따르면 1024개의 명령어를 가진 프로그램이 최대 크기일 것이다. 실제 프로그램에서는 훨씬 더 명령어의 수가 많고, 시작 주소도 훨씬 더 뒤에 있다.
- 1명령어-1word의 매핑방식은 매우 단순한 명령어만 가능하다. 한 어셈블리 명령은 여러개의 기계명령어로 분리되어 여러 줄의 메모리를 차지한다. 번역기는 이때 생성되는 명령어의 수를 체크하고 PC의 값을 update 해야한다.
- 1변수 - 1메모리공간의 매핑방식도 작은 크기의 변수만 가능하다. short는 1개의 메모리주소를, double은 4개의 메모리주소를 차지한다. 번역기가 변수의 메모리공간을 할당할 때는 데이터타입과 HW의 word width를 모두 고려해야한다.
어셈블러 Assembler
어셈블리 프로그램은 어셈블러가 2진 기계어로 번역하고, ROM에 로드되어 실행된다. 기계어 명세 _machine language specification 규칙을 따르면 다음 작업을 수행하는 프로그램을 작성하기 쉽다!
- 구문분석을 통해 기호 명령의 필드를 식별한다.
- 각 필드에 대응하는 기계어 Bit 생성하기.
- 모든 기호 참조는 메모리주소를 가리키는 숫자로 바꾸기.
- 2진코드를 조립하여 완전한 기계명령어를 만든다.
마지막 규칙의 구현은 조금 어렵지만, 어셈블러의 핵심기능이 된다. 다음 글에서 Hack어셈블러의 사양을 정하고 그에 맞는 구현방법을 공부해보자.
'학부생 CS > Elements of Comp-Sys' 카테고리의 다른 글
7-1. Virtual Machine I : Stack Arithmetic - [밑바닥부터 만드는 컴퓨팅 시스템] (0) | 2019.11.29 |
---|---|
6-2. Assembler - [밑바닥부터 만드는 컴퓨팅 시스템] (0) | 2019.11.27 |
5. Computer Architecture - [밑바닥부터 만드는 컴퓨팅 시스템] (0) | 2019.11.24 |
4. Machine Language - [밑바닥부터 만드는 컴퓨팅 시스템] (0) | 2019.11.23 |
3. Sequential Logic - [밑바닥부터 만드는 컴퓨팅 시스템] (0) | 2019.11.23 |