본문 바로가기
Computer Science/Computer Architecture

[ 혼자 공부하는 컴퓨터구조 + 운영체제 ] Chapter03. 명령어

by seoyeonnn 2024. 5. 16.

03-1. 소스 코드와 명령어

(1) 고급 언어와 저급 언어

고급 언어: 사람을 위한 언어로, 대부분의 프로그래밍 언어가 고급 언어에 속한다.

저급 언어: 컴퓨터가 직접 이해하고 실행할 수 있는 언어

 

고급 언어로 작성된 소스 코드가 실행되려면 반드시 저급 언어, 즉 명령어로 변환되어야 한다.

 

저급 언어에는 기계어와 어셈블리어 두 가지 종류가 있다.

기계어(machine code): 0과 1로 이루어진 명령어로 구성된 저급 언어

기계어는 오로지 컴퓨터만을 위해 만들어진 언어이기 때문에 사람이 읽으면 그 의미를 이해하기 어렵다.

어셈블리어(assembly language): 0과 1로 이루어진 기계어를 읽기 편한 형태로 번역한 저급 언어

어셈블리어를 통해 컴퓨터가 프로그램을 어떤 과정으로 실행하는지, 즉 프로그램이 어떤 절차로 작동하는지를 가장 근본적인 단계에서부터 하나하나 추적하고 관찰할 수 있다.

 

(2) 컴파일 언어와 인터프리터 언어

개발자들이 고급 언어로 작성한 소스 코드가 저급 언어로 변환되는 방식은 크게 컴파일 방식인터프리트 방식이 있다.

 

컴파일 언어: 컴파일러에 의해 소스 코드 전체가 저급 언어로 변환되어 실행되는 고급 언어

Ex) C언어

컴파일(compile): 컴파일 언어로 작성된 소스 코드 전체가 저급 언어로 변환되는 과정

컴파일러(compiler) :컴파일을 수행해 주는 도구

컴파일러가 소스 코드 내에서 오류를 하나라도 발견하면 해당 소스 코드는 컴파일에 실패한다.

목적 코드(object code): 컴파일러를 통해 저급 언어로 변환된 코드

 

인터프리터 언어: 인터프리터에 의해 소스 코드가 한 줄씩 실행되는 고급 언어

Ex) Python

소스 코드 전체가 저급 언어로 변환되는 컴파일 언어와 달리, 인터프리터 언어는 소스 코드를 한 줄씩 차례로 실행한다.

인터프리터(interpreter): 소스 코드를 한 줄씩 저급 언어로 변환하여 실행해 주는 도구

소스 코드를 한 줄씩 실행하기 때문에 소스 코드 N번째 줄에 문법 오류가 있더라도 N-1 번재 줄까지는 올바르게 수행된다.

 

※ 하나의 언어가 반드시 컴파일 방식과 인터프리트 방식 중 하나의 방식으로만 작동하는 것은 아니며, 현대의 많은 프로그래밍 언어 중에서는 컴파일 언어와 인터프리터 언어 간의 경계가 모호한 경우가 많다.

 

목적 코드가 실행 파일이 되기 위해서는 링킹(목적 파일에 없는 외부 기능을 연결짓는 것)이라는 작업을 거쳐야 한다.

 

 

03-2. 명령어의 구조

(1) 연산 코드와 오퍼랜드

명령어는 연산 코드오퍼랜드로 구성되어 있다.

연산 코드(operation code): '명령어가 수행할 연산'으로, '연산자'라고도 부른다.

오퍼랜드(operand): '연산에 사용할 데이터' 또는 '연산에 사용할 데이터가 저장된 위치'로, '피연산자'라고도 부른다.

 

오퍼랜드 필드에는 숫자와 문자 등을 나타내는 데이터 또는 메모리나 레지스터 주소가 올 수 있다.

다만 연산에 사용할 데이터를 직접 명시하기보다는 많은 경우 데이터가 저장된 위치를 명시하기 때문에 주소 필드라고 부르기도 한다.

 

0-주소 명령어: 오퍼랜드가 하나도 없는 명령어

1-주소 명령어: 오퍼랜드가 하나인 명령어

2-주소 명령어: 오퍼랜드가 두 개인 명령어

3-주소 명령어: 오퍼랜드가 세 개인 명령어

 

연산 코드는 기본적으로 네 가지 유형으로 나눌 수 있다.

 

1. 데이터 전송

- MOVE: 데이터를 옮겨라

- STORE: 데이터를 저장하라

- LOAD(FETCH): 메모리에서 CPU로 데이터를 가져와라

- PUSH: 스택에 데이터를 저장하라

- POP: 스택의 최상단 데이터를 가져와라

 

2. 산술/논리 연산

- ADD / SUBSTRACT / 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: 입출력 장치의 상태를 확인하라

 

(2) 주소 지정 방식

오퍼랜드 필드에 데이터가 아닌 메모리 주소가 담긴다면 표현할 수 있는 데이터의 크기는 하나의 메모리 주소에 저장할 수 있는 공간만큼 커지고, 레지스터 이름을 명시할 때도 해당 레지스터가 저장할 수 있는 공간만큼 커진다.

때문에 오퍼랜드 필드에 메모리나 레지스터의 주소를 담는 경우가 많다.

 

유효 주소(effective address): 연산의 대상이 되는 데이터가 저장된 위치

주소 지정 방식(addressing mode): 연산에 사용할 데이터 위치를 찾는 방법. 즉, 유효 주소를 찾는 방법

 

즉시 주소 지정 방식

: 연산에 사용할 데이터를 오퍼랜드 필드에 직접 명시하는 방식

 

표현할 수 있는 데이터의 크기가 작아지는 단점이 있지만, 연산에 사용할 데이터를 메모리나 레지스터로부터 찾는 과정이 없기 때문에 다른 주소 지정 방식들보다 빠르다.

 

직접 주소 지정 방식

: 오퍼랜드 필드에 유효 주소를 직접적으로 명시하는 방식

 

표현할 수 있는 데이터의 크기는 즉시 주소 지정 방식보다 더 커졌지만, 여전히 유;효 주소를 표현할 수 있는 범위가 연산 코드의 비트 수만큼 줄어들었다.

 

간접 주소 지정 방식

: 유효 주소의 주소를 오퍼랜드 필드에 명시하는 방식

 

직접 주소 지정 방식보다 표현할 수 있는 유효 주소의 범위가 더 넓어졌으나, 두 번의 메모리 접근이 필요하기 때문에 일반적으로 느린 방식이다.

 

레지스터 주소 지정 방식

: 연산에 사용할 데이터를 저장한 레지스터를 오퍼랜드 필드에 직접 명시하는 방식

 

일반적으로 CPU 외부에 있는 메모리에 접근하는 것보다 CPU 내부에 있는 레지스터에 접근하는 것이 더 빠르기 때문에, 직접 주소 지정 방식보다 빠르게 데이터에 접근할 수 있다.

하지만 직접 주소 지정 방식과 마찬가지로 표현할 수 있는 레지스터 크기에 제한이 생길 수 있다.

 

레지스터 간접 주소 지정 방식

: 연산에 사용할 데이터를 메모리에 저장하고, 그 주소를 저장한 레지스터를 오퍼랜드 필드에 명시하는 방식

 

 

유효 주소를 찾는 과정이 간접 주소 지정 방식과 비슷하지만, 메모리에 접근하는 횟수가 한 번으로 줄어든다는 장점이 있다.

메모리에 접근하는 것이 레지스터에 접근하는 것보다 느리기 때문에 레지스터 간접 주소 방식은 간접 주소 지정 방식보다 빠르다.