고급 언어와 저급 언어
우리가 프로그램을 만들 때 사용하는 프로그래밍 언어는 컴퓨터가 이해하는 언어가 아닌, 사람이 이해하고 작성하기 쉽게 만들어진 언어이다. 즉, 사람을 위한 언어를 고급언어라고 한다. 대부분의 프로그래밍 언어는 고급언어이다.
반대로 컴퓨터가 직접 이해하고 실행할 수 있는 언어를 저급언어라고 한다.
컴퓨터가 이해하고 실행할 수 있는 언어는 오직 저급 언어뿐이다. 이러한 저급어는 두 가지 종류가 있다. 기계어와 어셈블리어이다.
기계어
기계어란 0과 1의 명령어 비트로 이루어진 언어이다.
기계어는 0과 1로만 이루어지기 때문에 사람이 이해하기 어렵다. 그래서 등장한 언어가 어셈블리어이다.
어셈블리어
0과 1로만 이루어진 기계어로 표현된 명령어를 읽기 편한 형태로 번역한 언어가 어셈블리어이다.
기계어 어셈블리어
0101 0101 → push rbp
0101 1101 → pop rbp
1100 0011 → ret
컴파일 언어와 인터프리터 언어
개발자들이 고급 언어로 작성한 소스코드는 결국 저급 언어로 변환된다. 그렇다면 고급 언어는 어떻게 저급 언어로 바뀔까? 이때 사용되는 방식이 컴파일 방식과 인터프리트 방식이다.
컴파일 방식으로 작동하는 프로그래밍 언어를 컴파일 언어, 인터프리트 방식으로 작동하는 프로그래밍 언어를 인터프리터 언어라고 한다.
컴파일 언어
컴파일 언어는 컴파일러에 의해 소스 코드 전체가 저급 언어로 변환되어 실행되는 고급 언어이다.
컴파일 언어로 작성된 소스코드는 코드 전체가 저급 언어로 변환되는 과정을 거치는데 이 과정을 컴파일이라고 한다. 컴파일을 수행해 주는 도구를 컴파일러라고 한다.
컴파일러가 소스코드 내에서 오류를 하나라도 발견하면 해당 소스는 컴파일에 실패한다. 반면 컴파일에 성공하면 컴퓨터가 이해할 수 있는 저급언어로 변환되는데 이렇게 컴파일러를 통해 저급 언어로 변환된 코드를 목적코드라고 한다.
인터프리터 언어
인터프리터 언어는 인터프리터에 의해 소스 코드가 한 줄씩 시행되는 고급언어이다.
소스 코드를 한 줄씩 저급 언어로 변환하여 실행해주는 도구를 인터프리터라고 한다. 인터프리터 언어는 컴퓨터와 대화하듯 소스 코드를 실행하기에 소스 코드 전체를 저급 언어로 변화하는 시간을 기다릴 필요가 없다. 또한 컴파일 언어는 하나라도 문제가 있다면 컴파일이 되지 않지만, 인터프리터 언어는 한 줄씩 실행하기 때문에 N번째 줄에 어법 오류가 있다 해도 N-1번째 까지는 정상적으로 실행이 된다.
연산 코드와 오퍼랜드
명령어는 연산 코드와 오퍼랜드로 구성된다.
연산 코드는 명령어가 수행할 연산을 의미하며, 오퍼랜드란 연산에 사용될 데이터 혹은 데이터가 저장된 위치를 의미한다.
빨간색 부분이 연산 코드이며 해당 부분은 연산 코드 필드라고 한다. 흰색 부분은 오퍼랜드가 담기는 부분으로 오퍼랜드 필드라고 한다.
오퍼랜드
오퍼랜드는 연산에 사용할 데이터 혹은 연산에 사용할 데이터가 저장될 위치를 의미한다.
따라서 오퍼랜드에는 숫자와 문자를 나타내는 데이터 혹은 레지스터 주소가 들어갈 수 있다. 보통 오퍼랜드에는 데이터를 직접 넣기보다 데이터가 저장된 위치인 메모리 주소나 레지스터 이름을 담는 경우가 일반적이다. 따라서 오퍼랜드 필드를 주소 필드라고 부르는 경우가 많다.
오퍼랜드는 명령어 안에 하나도 없을 수도 있고, 한 개만 있을 수도 있고 여러개가 있을 수도 있다.
오퍼랜드가 하나도 없는 명령어를 0-주소 명령어라고 하며, 오퍼랜드가 하나인 명령어를 1-주소 명령어, 두 개인 명령어를 2-주소 명령어라고 한다.
연산 코드
연산 코드는 명령어가 수행할 연산을 의미한다. 더해라, 빼라, 저장해라 부분이 연산 코드인 것이다.
연산 코드는 기본적으로 1) 데이터 전송. 2)산술/논리 연산. 3) 제어 흐름 변경. 4) 입출력 제어. 로 크게 4가지로 나눌 수 있다. 다 외울 필요는 없겠지만 공부하는 겸 적어보았다.
1) 데이터 전송
MOVE: 데이터를 옮겨라
STORE: 데이터를 저장하라
LOAD: 메모리에서 CPU로 데이터를 가져와라
PUSH: 스택에 데이터를 저장하라
POP: 스택의 최상단 데이터를 가져와라
2) 산술/논리 연산
ADD / SUBTRACT / MULTIPLY / DIVIDE: 덧셈 / 뺄셈 / 곱셈 / 나눗셈을 수행하라
INCREMENT / DECREMENT: 오퍼랜드에 1을 더하라 / 오퍼랜드에 1을 빼라
AND / OR / NOT: AND / OR / NOT 연산을 수행하라
COMPARE: 두 개의 숫자 또는 TRUE / FALSE 값을 비교하라
3) 제어 흐름 변경
JUMP: 특정 주소로 실행 순서를 옮겨라
CONDITIONAL JUMP: 조건에 부합할 때 특정 주소로 실행 순서를 옮겨라
HALT: 프로그램의 실행을 멈춰라
CALL: 되돌아올 주소를 저장한 채 특정 주소로 실행 순서를 옮겨라
RETURN: CALL을 호출할 때 저장했던 주소로 돌아가라
4) 입출력 제어
READ(INPUT): 특정 입출력 장치로부터 데이터를 읽어라
WRITE(OUTPUT): 특정 입출력 장치로 데이터를 써라
START IO: 입출력 장치를 시작하라
TEST IO: 입출력 장치의 상태를 확인하라
주소 지정 방식
Q. 왜 오퍼랜드 필드에 데이터를 담지 않고 메모리나 레지스터의 주소를 담는걸까?
A. 바로 명령어의 길이 때문이다.
하나의 명령어가 n비트로 구성된 경우 오퍼랜드의 길이는 연산 코드의 길이를 뺀 n-m비트가 될 것이다. 이러한 상황에서 1-주소명령어 → 2-주소명령어 → 3-주소명령어가 될수록 하나의 오퍼랜드 필드에 담을 수 있는 정보의 개수는 줄어들게 된다.
데이터 대신 메모리나 레지스터의 주소를 담으면 표현할 수 있는 데이터의 크기가 하나의 메모리에 저장할 수 있는 공간만큼 커지는 이점을 얻을 수 있다. 따라서 오퍼랜드 필드에 레지스터의 주소를 담는 것이다.
연산 코드에 사용할 데이터가 저장된 위치, 즉 연산의 대상이 되는 데이터가 저장된 위치를 유효주소라고 한다. 즉 오퍼랜드 필드에 데이터가 저장된 위치를 명시할 때 연산에 사용될 데이터의 위치를 찾는 방식을 주소 지정 방식이라고 한다.
1) 즉시 주소 지정 방식
연산에 사용할 데이터를 오퍼랜드 필드에 직접 명시하는 방식이다.
가장 간단한 형태의 주소 지정 방식이기도 하다. 데이터의 크기가 작아진다는 단점이 있지만 연산에 사용할 데이터를 찾는 과정이 필요 없기 때문에 빠르다는 장점이 있다.
2) 직접 주소 지정 방식
오퍼랜드 필드에 유효 주소를 직접적으로 명시하는 방식이다.
즉시 주소 지정방식에 비해 오퍼랜드 필드에 표현할 수 있는 범위가 커졌지만, 유효 주소를 표현할 수 있는 범위가 연산 코드의 비트 수만큼 줄어들었다. 즉 오퍼랜드의 길이가 연산 코드의 길이만큼 짧아진 것.
3) 간접 주소 지정 방식
유효 주소의 주소를 오퍼랜드 필드에 명시하는 방식이다.
직접 주소 지정 방식보단 오퍼랜드 필드에 표현할 수 있는 범위가 커졌지만, 두 번의 메모리 접근이 필요하기에 앞선 방식들보다 일반적으로 느리다.
4) 레지스터 주소 지정 방식
연산에 사용할 데이터를 저장한 레지스터를 오퍼랜드 필드에 직접 명시하는 방식이다.
외부 CPU에 접근하는 것보단 내부 CPU에 접근하는 게 빠르다. 다만 레지스터 주소 지정 방식은 표현할 수 있는 레지스터의 크기에 제한이 생긴다는 단점이 있다.
5) 레지스터 간접 주소 지정 방식
연산에 사용할 데이터를 메모리에 저장하고, 그 주소를 저장한 레지스터를 오퍼랜드 필드에 명시하는 방식이다. 메모리에 접근하는 횟수가 한 번이라는 점에서 간접 주소 지정 방식과 비슷하지만 한 번만 접근한다는 점에서 차이가 있다. 따라서 간접 주소 지정 방식보다 속도가 빠르다.
'Computer Science > 컴퓨터 구조' 카테고리의 다른 글
[컴퓨터 구조] Chapter 06 메모리와 케시 메모리 (1) | 2023.12.02 |
---|---|
[컴퓨터 구조] Chapter 05 CPU 성능 향상 기법 (0) | 2023.11.18 |
[컴퓨터 구조] Chapter 04 CPU의 작동원리 (1) | 2023.11.13 |
[컴퓨터 구조] Chapter 02 데이터 (0) | 2023.11.07 |
[컴퓨터 구조] Chapter 01 컴퓨터 구조 시작하기 (1) | 2023.10.29 |