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

728x90

안녕하세요. 해당 포스트는 강민철 저자님의 '혼자 공부하는 컴퓨터 구조+운영체제'를 읽고 정리한 포스트입니다.

 

 

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

모든 소스 코드는 컴퓨터 내부에서 명령어로 변환됨!

 

고급 언어와 저급 언어

고급언어

  • JAVA, Python, C, C++ 같은 프로그래밍 언어
  • 개발자(사람)를 위한 언어

 

저급언어

  • 컴퓨터가 직접 이해하고 실행할 수 있는 언어
    • 기계어 : 0과 1의 명령어 비트로 이루어진 언어(16진수로 표현하기도 함)
    • 어셈블리어 : 0과 1로 표현된 명령어(기계어)를 읽기 편한 형태로 번역한 언어

 

 

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

고급 언어가 저급 언어로 변환되는 방식

  • 컴파일 방식
  • 인터프리터 방식

 

컴파일 언어

  • 컴파일 방식으로 작동하는 프로그래밍 언어
  • 컴파일 언어로 작성된 소스 코드는 컴파일러에 의해 저급 언어로 변환(컴파일)되고, 컴파일의 결과로 저급 언어인 목적 코드가 생성됨
  • 컴파일러가 소스 코드 내에서 오류를 하나라도 발견하면 해당 소스 코드는 컴파일에 실패함. 소스 코드를 통째로 컴파일함
  • 대표적으로 C가 있음

 

인터프리터 언어

  • 인터프리트 방식으로 작동하는 프로그래밍 언어
  • 소스 코드를 한 줄씩 저급 언어로 변환(인터프리터)하여 실행함
  • 한 줄씩 실행하기 때문에 소스 코드 전체를 저급 언어로 변환하는 시간을 기다릴 필요가 없음

 

✔️ 컴파일을 통해 나온 목적 코드는 컴퓨터가 이해하고 실행할 수 있는 저급 언어인 반면, 인터프리터 언어는 소스 코드 마지막에 이를 때까지 한 줄씩 저급 언어로 해석하며 실행하기 때문에 일반적으로 인터프리터 언어가 컴파일 언어보다 느림!

 

C언어의 컴파일 과정

전처리기(Preprocessor)

  • test.c → test.i
  • 컴파일 전 처리할 작업들
  • 외부에 선언된 다양한 소스 코드, 라이브러리 포함 (e.g. #include)
  • 프로그래밍의 편의를 위해 작성된 매크로 변환 (e.g. #define)
  • 컴파일할 영역 명시 (e.g. #if, #indef)

 

컴파일러(Compiler)

  • test.i → test.s
  • 전처리가 완료되어도 여전히 소스 코드임!
  • 전처리가 완료된 소스 코드를 저급 언어(어셈블리 언어)로 변환

 

어셈블러(Assembler)

  • test.s → test.o
  • 어셈블리어를 기계어로 변환
  • 목적 코드를 포함하는 목적 파일이 됨

 

링커(Linker)

  • test.o → test.exe
  • 각기 다른 목적 코드를 하나의 실행 파일로 묶어주는 작업 -> 링킹(linking)

 

 

 

 

03-2. 명령어의 구조

연산 코드와 오퍼랜드

명령어는 연산 코드(operation code)와 오퍼랜드(operand)로 구성되어 있음

연산 코드가 담기는 영역을 연산 코드 필드, 오퍼랜드가 담기는 영역을 오퍼랜드 필드라고 함

 

연산 코드(=연산자)

  • 명령어가 수행할 연산
  • 연산 코드의 종류와 생김새는 CPU마다 다름
  • 연산 코드 종류
    • 데이터 전송
      • 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 : 입출력 장치의 상태를 확인하라

 

오퍼랜드(=피연산자)

  • 연산에 사용할 데이터 또는 연산에 사용할 데이터가 저장된 위치(이게 훨씬 더 많이 저장함!!)
  • 주소 필드라고 부르기도 함
  • 오퍼랜드가 하나도 없는 명령어(0-주소 명령어)가 있을 수도 있고 오퍼랜드가 n개인 명령어(n-주소 명령어)가 있을 수도 있음

 

주소 지정 방식

  • 명령어의 오퍼랜드에 값을 직접 넣지 않고 메모리나 레지스터의 주소를 담는 이유? 명령어의 길이 때문!
  • 유효 주소 : 연산 코드에 사용할 데이터가 저장된 위치

 

즉시 주소 지정 방식

  • 연산에 사용할 데이터를 오퍼랜드 필드에 직접 명시하는 방식
  • 데이터의 크기가 작아지는 단점이 있음
  • 연산에 사용할 데이터를 메모리나 레지스터로부터 찾는 과정이 없기 때문에 빠름

 

직접 주소 지정 방식

  • 오퍼랜드 필드에 유효 주소를 직접적으로 명시하는 방식
  • 표현할 수 있는 오퍼랜드 필드의 길이가 연산 코드의 길이만큼 짧아져 표현할 수 있는 유효 주소에 제한이 생길 수 있음

 

간접 주소 지정 방식

  • 유효 주소의 주소를 오퍼랜드 필드에 명시
  • 직접 주소 지정 방식보다 표현할 수 있는 유효 주소의 범위가 넓어졌지만 두 번의 메모리 접근이 필요하기 때문에 느린 방식임 (CPU가 메모리 접근을 최소화하는 것이 속도면에서는 무조건 좋음!)

 

레지스터 주소 지정 방식

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

 

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

  • 연산에 사용할 데이터를 메모리에 저장하고 그 주소(유효 주소)를 지정한 레지스터를 오퍼랜드 필드에 명시하는 방법

 

스택과 큐

스택(stack)

  • 한쪽 끝이 막혀 있는 통과 같은 저장 공간
  • 메모리에 위치해있음
  • 나중에 저장한 데이터를 가장 먼저 빼내는 데이터 관리 방식(LIFO, Last In First Out, '리포')

 

큐(queue)

  • 양쪽이 뚫려 있는 통과 같은 저장 공간
  • 선입선출(FIFO, First In First Out, '피포')

 

728x90