CS(Computer Science)/Computer Architecture

[컴퓨터 구조] 3. 명령어

seunghwaan 2023. 6. 26. 18:08
반응형

이번 포스트에서는 컴퓨터 구조의 명령어에 대해 정리하겠습니다.

내용은 "혼자 공부하는 컴퓨터 구조+운영체제" 책을 참고하였습니다!

소스 코드와 명령어

[ 고급 언어와 저급 언어 ]

  • 고급 언어 -> 사람이 이해하고 작성하기 쉽게 만들어진 언어. 대부분의 프로그래밍 언어가 고급 언어에 속한다.
  • 저급 언어 -> 컴퓨터가 직접 이해하고 실행할 수 있는 언어.

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

 

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

 

기계어 -> 0과 1의 명령어 비트로 이루어진 언어이다. 다시 말해 기계어는 0과 1로 이루어진 명령어의 모음이다. 아래 그림은 컴퓨터는 0과 1로 이루어진 이 기계어를 이해하고 실행한다. 다만, 기계어를 이진수로 나열하면 너무 길어지기 때문에 가독성을 위해 십육진수로 표현하기도 한다.

 

어셈블리어 -> 0과 1로 표현된 명령어(기계어)를 읽기 편한 형태로 번역한 언어가 어셈블리어다. 기계어는 오로지 컴퓨터만을 위해 만들어진 언어이기 때문에 사람이 읽으면 그 의미를 이해하기 어렵기 때문이다. 아래 그림의 어셈블리어 한 줄이 명령어이다. 어셈블리어는 0과 1로 이루어진 기계어를 읽기 편하게 만든 저급 언어일 뿐이므로, 개발자가 어셈블리어를 이용해 복잡한 프로그램을 만들기란 쉽지 않다.

[ 컴파일 언어와 인터프리터 언어 ]

  • 고급 언어가 컴퓨터가 이해할 수 있는 저급 언어로 변환하는 방식을 크게 컴파일 방식인터프리트 방식 두가지로 나눌 수 있다.

[ 컴파일 언어 ]

  • 컴파일 언어는 컴파일러에 의해 소스 코드 전체가 저급 언어로 변환되어 실행되는 고급 언어이다. 대표적으로 C언어가 있다.

컴파일 -> 컴파일 언어로 작성된 소스 코드 전체가 저급 언어로 변환되는 과정을 말한다.

 

컴파일러 -> 컴파일을 수행해 주는 도구를 컴파일러라고 한다. 개발자가 소스 코드 전체를 쭉 훑어보며 소스 코드에 문법적인 오류는 없는지, 실행 가능한 코드인지, 실행하는 데 불필요한 코드는 없는지 등을 따지며 소스 코드를 처음부터 끝까지 저급 언어로 컴파일한다. 이때 컴파일러가 소스 코드 내에서 오류를 하나라도 발결하면 해당 소스 코드는 컴파일에 실패한다.

 

목적 코드 -> 컴파일이 성공적으로 수행되면 개발자가 작성한 소스 코드는 컴퓨터가 이해할 수 있는 저급 언어로 변환된다. 이렇게 컴파일러를 통해 저급 언어로 변환된 코드를 목적 코드라고 한다.

 

[ 인터프리터 언어 ]

  • 인터프리터 언어는 인터프리터에 의해 소스 코드가 한 줄씩 실행되는 고급 언어이다. 대표적으로 python이 있다.

 

인터프리터 -> 소스 코드 전체가 저급 언어로 변환되는 컴파일 언어와는 달리, 인터프리터 언어는 소스 코드를 한 줄씩 차례로 실행한다. 그리고 소스 코드를 한 줄씩 저급 언어로 변환하여 실행해 주는 도구이다. 인터프리터 언어는 컴퓨터와 대화하듯 소스 코드를 한 줄씩 실행하기 때문에 소스 코드 전체를 저급 언어로 변환하는 시간을 기다릴 필요가 없다. 그리고 소스 코드 내에 오류가 하나라도 있으면 컴파일이 불가능했던 컴파일 언어와는 달리, 인터프리터 언어는 소스 코드를 한 줄씩 실행하기 때문에 소스 코드 N번째 줄에 문법 오류가 있더라도 N-1번째 줄까지는 올바르게 수행된다.

 

인터프리터 언어가 컴파일 언어보다 빠르다고 생각할 수도 있지만, 일반적으로 인터프리터 언어는 컴파일 언어보다 느리다. 컴파일을 통해 나온 결과물, 즉 목적 코드는 컴퓨터가 이해하고 실행할 수 있는 저급 언어인 반면, 인터프리터 언어는 소스 코드 마지막에 이를 때까지 한 줄씩 저급 언어로 해석하여 실행해야 하기 때문이다.

 

목적 파일 -> 목적 코드로 이루어진 파일(이미지로 이루어진 파일은 이미지 파일(.jpg), 텍스트로 이루어진 파일은 텍스트 파일(.txt), 실행 코드로 이루어진 파일은 실행 파일(.exe))

 

링킹 -> 목적 코드가 실행파일이 되기 위해서 거치는 작업. 컴퓨터가 이해하는 저급 언어로 만들어준다. 즉, main.c의 목적 파일은 main.o가 되는데, 이 저급 언어로 변환된 파일은 목적 파일에 없는 외부 기능들을 연결 짓는 과정이 필요하다. (main.o 자체만으로는 실행 불가능하고 링킹 과정을 거치면 실행 파일이 만들어진다.)

명령어의 구조

[ 연산 코드와 오퍼랜드 ]

  • 명령어 -> 연산 코드 오퍼랜드로 구성되어 있다.
  • 연산 코드 -> 명령어가 수행할 연산. 연산자라고도 부른다.
  • 오퍼 랜드 -> 연산에 사용할 데이터가 저장된 위치. 피연산자라고도 부른다.

기계어와 어셈블리어 또한 명령어이기 때문에 연산 코드와 오퍼랜드로 구성되어 있다.

[ 오퍼랜드 ]

  • 오퍼랜드연산에 사용할 데이터 혹은 연산에 사용할 데이터가 저장된 위치를 말한다.
  • 그래서 오퍼랜드 필드에는 숫자와 문자 등을 나타내는 데이터 또는 메모리나 레지스터 주소가 올 수 있다.
  • 다만, 오퍼랜드 필드에는 수자나 문자와 같이 연산에 사용할 데이터를 직접 명시하기보다는, 연산에 사용할 데이터가 저장된 위치, 즉 메모리 주소나 레지스터 이름이 담긴다. 그래서 주소 필드라고 부른다.

오퍼랜드는 명령어 안에 하나도 없을 수도 있고, 한 개만 있을 수도 있고, 두 개 또는 세 개 등 여러개가 있을 수도 있다.

 

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

1-주소 명령어 -> 오퍼랜드가 한 개인 명령어

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

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

 

 

[ 연산 코드 ]

  • 연산 코드는 명령어가 수행할 연산을 말한다. ex) '더해라', '빼라'

[ 연산 코드의 유형 ]

  • 데이터 전송
  • 산술/논리 연산
  • 제어 흐름 변경
  • 입출력 제어

데이터 전송

  • MOVE -> 데이터를 옮겨라
  • STORE -> 메모리에 저장하라
  • LOAD(FETCH) -> 메모리에서 CPU로 데이터를 가져와라
  • PUSH -> 스택에 데이터를 저장하라
  • POP -> 스택의 최상단 데이터를 가져와라

산술/논리 연산

  • ADD / SUBTRACT / MULTIPLY / DIVIDE -> 덧셈 / 뺄셈 / 곱셈 / 나눗셈을 수행하라
  • INCREMENT / DECREMENT -> 오퍼랜드에 1을 더하라 / 오퍼랜드에 1을 빼라
  • AND / OR / NOT -> AND / OR / NOT 연산을 수행하라
  • COMPARE -> 두 개의 숫자 또는 TRUE / FALSE 값을 비교하라

제어 흐름 변경

  • JUMP -> 특정 주소로 실행 순서를 옮겨라
  • CONDITIONAL JUMP -> 조건에 부합할 때 특정 주소로 실행 순서를 옮겨라
  • HALT -> 프로그램의 실행을 멈춰라
  • CALL -> 되돌아올 주소를 저장한 채 특정 주소로 실행 순서를 옮겨라
  • RETURN -> CALL을 호출할 때 저장했던 주소로 돌아가라

입출력 제어

  • READ(INPUT) -> 특정 입출력 장치로부터 데이터를 읽어라
  • WRITE(OUTPUT) -> 특정 입출력 장치로 데이터를 써라
  • START IO -> 입출력 장치를 시작하라
  • TEST IO -> 입출력 장치의 상태를 확인하라

 

주소 지정 방식

  • 주소 지정 방식은 연산에 사용할 데이터 위치를 찾는 방법이다. 다시 말해 주소 지정 방식은 유효 주소를 찾는 방법이다. 오퍼랜드 필드에 데이터가 저장된 위치를 명시할 때 연산에 사용할 데이터 위치를 찾는 방법을 말한다. 
  • 오퍼랜드 필드에 메모리나 레지스터의 주소를 담는 경우가 많은데 명령어의 길이가 길어질 수 있기 때문이다.
  • 오퍼랜드 필드 안에 메모리 주소가 담긴다면 표현할 수 있는 데이터의 크기는 하나의 메모리 주소에 저장할 수 있는 공간만큼 커진다.

유효 주소 -> 연산 코드에 사용할 데이터가 저장된 위치, 즉 연산의 대상이 되는 데이터가 저장된 위치

첫 번째 그림의 경우 유효 주소는 10번지, 두 번째 그림의 경우 유효 주소는 레지스터R1이 된다.

[ 즉시 주소 지정 방식 ]

  • 즉시 주소 지정 방식(immediate addressing mode)은 연산에 사용할 데이터를 오퍼랜드 필드에 직접 명시하는 방식. 즉, 가장 간단한 형태의 주소 지정 방식이다.
  • 앞에서 언급했듯이 이런 방식은 표현할 수 있는 데이터의 크기가 작아지는 단점이 있지만, 연산에 사용할 데이터를 메모리나 레지스터로부터 찾는 과정이 없기 때문에 이하 설명할 주소 지정 방식들보다 빠르다.

 

[ 직접 주소 지정 방식 ]

  • 직접 주소 지정 방식(direct addressing mode)은 오퍼랜드 필드에 유효 주소를 직접적으로 명시하는 방식이다.
  • 오퍼랜드 필드에서 표현할 수 있는 데이터의 크기는 즉시 주소 지정 방식보다 더 커졌지만, 여전히 유효 주소를 표현할 수 있는 범위가 연산 코드의 비트 수만큼 줄어들었다.
  • 다시 말해 표현할 수 있는 오퍼랜드 필드의 길이가 연산 코드의 길이만큼 짧아져 표현할 수 있는 유효 주소에 제한이 생길 수 있다.

[ 간접 주소 지정 방식 ]

  • 간접 주소 지정 방식(indirect addressing mode)은 유효 주소의 주소를 오퍼랜드 필드에 명시한다.
  • 직접 주소 지정 방식보다 표현할 수 있는 유효 주소의 범위가 넓어진다.
  • 다만, 두 번의 메모리 접근이 필요하기 때문에 앞서 설명한 주소 지정 방식들보다 일반적으로 느린 방식이다.

 

때때로 연산에 사용할 데이터가 레지스터에 저장된 경우도 있다. 이 경우 레지스터 주소 지정 방식 또는 레지스터 간접 주소 지정 방식을 사용할 수 있다.

[ 레지스터 주소 지정 방식 ]

  • 레지스터 주소 지정 방식(register addressing mode)은 직접 주소 지정 방식과 비슷하게 연산에 사용할 데이터를 저장한 레지스터를 오퍼랜드 필드에 직접 명시하는 방법이다.
  • 일반적으로 CPU 외부에 있는 메모리에 접근하는 것보다 CPU 내부에 있는 레지스터에 접근하는 것이 더 빠르다. 
  • 그러므로 레지스터 주소 지정 방식은 직접 주소 지정 방식보다 빠르게 데이터에 접근할 수 있다.
  • but, 레지스터 주소 지정 방식은 직접 주소 지정 방식과 비슷한 문제를 공유한다. 표현할 수 있는 레지스터 크기에 제한이 생길 수 있다는 점이다.

 

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

  • 레지스터 간접 주소 지정 방식(register indirect addressing mode)은 연산에 사용할 데이터를 메모리에 저장하고, 그 주소(유효 주소)를 저장한 레지스터를 오퍼랜드 필드에 명시하는 방법이다.
  • 유효 주소를 찾는 과정이 간접 주소 지정 방식과 비슷하지만, 메모리에 접근하는 횟수가 한 번으로 줄어든다는 차이이자 장점이 있다.
  • 메모리에 접근하는 것이 레지스터에 접근하는 것보다 더 느리다고 했는데, 그래서 레지스터 간접 주소 지정 방식은 간접 주소 지정 방식보다 빠르다.

 

 

 

정리하면 아래와 같다.

  • 즉시 주소 지정 방식 -> 연산에 사용할 데이터
  • 직접 주소 지정 방식 -> 유효 주소(메모리 주소)
  • 간접 주소 지정 방식 -> 유효 주소의 주소
  • 레지스터 주소 지정 방식 -> 유효 주소(레지스터 이름)
  • 레지스터 간접 주소 지정 방식 -> 유효 주소를 지정한 레지스터

References

혼자 공부하는 컴퓨터 구조 + 운영체제, 강민철 (2022)

반응형