[컴퓨터구조] 16비트 컴퓨터에서 프로그래밍하기 - Assembler가 어셈블리 프로그램을 기계어로 번역하는 과정
이전 글:
[컴퓨터구조] 16비트 컴퓨터에서 프로그래밍 하기 | assembly로 쓰여진 프로그램의 구조
어셈블리 언어로 짜인 프로그램의 각 라인은 어떤 과정을 거쳐 기계어로 번역될까?
예를 들어 생각해보자.
어셈블리 프로그램의 일부에 아래와 같은 명령어 라인이 있다고 하자.
PL3, LDA SUB I
그럼 우리가 작성한 소스파일에는 위 명령이 아스키코드 기반으로 들어가있다. 각각의 문자는 8비트 아스키코드로 표현된다. 다시 말하자면 우리가 작성한 글자 하나하나마다 최상위비트는 0이고 나머지 7비트는 아스키로 표현되는 8비트로 표현된다.
참고: 아스키 코드
예시로 든 명령어 라인을 다시 보자.
PL3, LDA SUB I
이렇게 어셈블리로 표현한 프로그램을 symbolic program이라고도 말한다.
아무튼, 이 명령라인에서 문자 하나마다 8비트라고 했다. (아스키)
우리는 16비트 컴퓨터를 설계하고 있었다.
그럼 16비트 명령어를 저장하는 각 메모리공간에는 문자 두개가 들어갈 수 있다.
label 부분은 Comma(2C)를 통해 끝이 나고
연산자 부분과 주소 심볼 부분은 space(20)에 의해 끝이 나고
각 line은 CR(carriage return)(0D)에 의해 끝이 난다.
이렇게 아스키로 표현되어있는 우리가 작성한 소스파일 assemble하면 assembler가 라인라인을 읽어서 바이너리로 표현되는 obeject code를 만들어내게 된다. 그럼 어셈블러는 어떻게 어셈블할까?
two pass Assembler
일단 일반적으로 어셈블러는 "2 pass assemble"한다.
즉, Assembler는 일반적으로 2 pass Assembler다.
따라서 어셈블 단계가 첫번째 pass, 두번째 pass로 이루어지는데, 각 pass 단계에서는 무엇을 하는지 확인하자!
우리가 '이 포스팅에서' 확인했던
두 상수의 뺄셈을 하는 어셈블리 프로그램을 예시로 들자.
아래 프로그램을 어셈블해볼 것이다.
■ 1st pass : address symbol table을 만들어내는 단계
1st pass에서는 아래와 같은 일을 한다!
LC(Location counter)라는 변수가 1st pass 단계의 특징이다. 어떤 역할을 하는지 주목해보자.
LC는 현재 메모리 주소를 가지고 있는 변수다. pseudoinstruction인 ORG에 의해 초기화된다.
일단 기본적으로 LC<-0으로 시작한다. ORG가 LC를 새로 세팅할 수 있다.
[ 어떤 명령line이 1st pass를 지나는 상황을 생각해보자. ]
▷ 먼저 Label이 있는지 없는지 체크한다.
- 있으면 LC값과 해당 label의 symbol을 저장한다. (address symbol table이라는 곳에)
▷ Label이 없으면 ORG인지 체크한다.
- ORG이면 LC를 설정하고 다음 라인으로 넘어간다.
▷ ORG가 아니면 END인지 체크한다.
- END가 아니면 LC값을 증가시키고 다음 라인으로 넘어간다.
▷ END이면 1st pass를 끝내고 2nd pass로 넘어간다!
이 1st pass를 거친 결과로 address symbol table이 나온다.
아까 Label이 있는지 없는지 체크할 때, 있으면 LC값과 해당 symbol을 address symbol table에 저장한다고 했다. 그 내용이 정말 아래 table에 다 포함되어있다.!
아래 어셈블리 프로그램과 비교해보면, label 필드가 존재했던 상수인 MIN, / SUB, / DIF, 라는 symbol의 상수값이 아스키로 저장되어있고, 그 symbol에 해당하는 메모리번지(LC)또한 저장되어있다. MIN, / SUB, / DIF, 순서대로 107, 108, 109로 table에 저장되어있다.
여기서 1st pass는 끝이다. address symbol table을 만들었다!
■ 2nd pass : binary code를 만들어내는 단계
이제 여기서 실제적인 binary code를 만들어낸다!
우리가 2nd pass로 온 이 시점에서 가지고 있는 정보들은 무엇인지 생각해보자.
▷ 1st pass에서 만들어낸 address symbol table
▷ 앞서 정의한 instruction set table (MRI와 non-MRI)
▷ 앞서 정의한 Pseudo instructon table
모든 symbol은 위 테이블 중 하나에 반드시 존재하게 된다.
명령어 symbol은 명령어 테이블에 있을 것이고,
label symbol은 1st pass를 돌고 나서 만들어진 address symbol table에 있을 것이고..
이것들만 있으면 이제 모든 명령line을 binary code로 변환할 수 있다.
아래 회로가 그 과정이다.
▷ LC<-0으로 세팅하고 시작!
▷ Pseudo instruction이냐 아니냐를 먼저 판단한다.
- pseudo인경우 OMG, END, DEC, HEX중 하나다. OMG면 LC를 다시 세팅하고, END면 끝내고, 둘다 아니면 DEC또는 HEX 즉 상수니까 그냥 그 번지에 DEC, HEX값 넣어주면 된다.
▷ Pseudo가 아니라면, 이번에는 MRI냐 아니냐를 본다.
- MRI라면 opcode 세비트를 가져온다. 그리고 피연산자에 대한 정보가 1st pass에 의해 symbol address table에 담겨있을테니까, 거기서 피연산자 주소(하위 12비트에 해당) 가져오면 된다. 그리고 addressing 모드에 해당하는 I값이 1인지 0인지만 봐주면, 최종적으로 16비트 memory-reference instruction code가 작성완료된다.
▷ MRI가 아니라면, 이번에는 non-MRI냐 아니냐를 보자.
- non-MRI라면 register-reference instruction 또는 input-output instruction이다. 이 경우에는 해당하는 명령을 그냥 정의된 명령 테이블에서 16비트 그대로 가져오면 된다.
▷ MRI도 아니고 non-MRI도 아니면 Error다. (예를 들면 타이핑을 잘못한 경우, 어떤 label에 관한 정보가 있어야 하는데 없는 경우. 아무튼 이 경우에는 error 메세지 출력 등이 이루어져야 한다.)
END가 나올 때까지 계속 돌면서 next line을 실행한다.
그러면 2nd pass를 돌고 나서 최종적으로 binary code가 나온다.
이것이 실질적인 실행파일인 object 파일 내용이 된다.
여기까지..
연속으로 작성한 '16비트 컴퓨터 설계하기' 포스팅을 여기까지 다 보았다면
컴퓨터 구조는 어떤 것들로 정의되어야 하는지 알았을 것이고,
그것들의 구조도 확인했을 것이고,
이런 기초 지식들을 기반으로
어셈블리 프로그래밍하는 방법도 바로 이전 포스팅에서 보았다.
그리고 이번에는 어셈블러가 어떤 과정으로 어셈블하는지까지 확인했다.
+아직 끝나지 않았다.
다음 포스팅에서 만나자.