본문 바로가기

혼자 공부하는 컴구, 운체

[1주차] Chapter 03. 소스 코드, 명령어, 명령어 구조

Chapter 03 명령어


03-1 소스 코드와 명령어

1. 고급 언어와 저급언어

고급 언어(high-level programming language)

 -사람을 위한 언어 (사람이 읽고 쓰기 편하며, 더 나은 가독성, 편리한 문법 제공 덕분에 복잡한 프로그램을 구현할 수 있다)

 -대부분의 프로그래밍 언어가 고급 언어에 속한다.

 -고급 언어로 작성된 소스 코드는 반드시 저급 언어(명령어)로 변환되어야 실행이 가능하다.

 

저급 언어(low-level programming language)

 -컴퓨터가 직접 이해하고 실행할 수 있는 언어 (프로그래머에게 작성의 대상이자 관찰의 대상, 프로그램의 작동 절차를 추적하고 관찰할 수 있다)

 -명령어로 이루어져 있다.

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

 

②-1 기계어(machine code)

 -0과 1의 명령어 비트로 이루어진 언어

 -이진수의 나열, 혹은 가독성을 위하여 십육진수로 표현하기도 한다.

 

②-2 어셈블리어(low-level programming language)

 -기계어를 읽기 편한 형태로 번역한 언어

더보기

롤러코스터 타이쿤이라는 게임이 어셈블리어로 프로그래밍 된 게임이라는 얘기를 들은 것 같다.

 

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

① 컴파일 언어

 -컴파일 방식으로 작동하는 프로그래밍 언어

ex) C

 -컴파일(compile) : 컴파일러에 의해 소스 코드 전체가 저급 언어로 변환되는 과정. 컴파일러가 소스 코드 내에서 오류를 하나라도 발견할 시 해당 소스 코드는 컴파일에 실패한다.

 -컴파일러(compiler) : 컴파일을 수행해 주는 도구. 소스 코드의 문법적 오류의 존재 여부, 실행 가능성, 실행 시 불필요한 코드가 없는지 확인하며 컴파일을 진행한다. 

 -목적 코드(object code) : 컴파일러를 통한 컴파일이 성공했을 때 저급 언어로 변환된 코드

 

 -컴파일 언어의 장점 : 소스 코드를 목적 코드로 만들어서 컴퓨터가 이해하고 실행하기 때문에 빠르다. 

ex) 다른 언어의 연설문을 미리 번역하여 빠르게 읽어내리는 것과 같다.

 

 -컴파일 언어의 단점

: 소스 코드 전체를 저급 언어로 변환하는 데에 시간이 든다. 

: 소스 코드에 오류가 단 하나라도 있을 시 실행이 불가능하다.

: 소스 코드가 바뀌면 다시 컴파일을 해야한다.

 

② 인터프리터 언어

 -인터프리트 방식으로 작동하는 프로그래밍 언어

ex) Python

 -인터프리트(interpret) : 인터프리터에 의해 소스 코드가 한 줄씩 실행되는 과정

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

 

 -인터프리터 언어의 장점

: 소스 코드 전체를 저급 언어로 변환하는 시간을 기다릴 필요가 없다.

: 소스 코드에 오류가 있더라도 오류가 있는 줄의 전 줄까지는 올바르게 수행할 수 있다. 

: 소스 코드가 바뀌어도 실행 지점이 바뀐 부분까지 도달하지 않았다면 다시 실행할 필요가 없다. 

 

 -인터프리터 언어의 단점 : 소스 코드를 한 줄씩 저급 언어로 변환하기 때문에 느리다.

ex) 다른 언어의 연설문을 한 줄 씩 번역하며 읽어내리는 것과 같다.

 

+컴파일 언어와 인터프리터 언어의 구분에 대하여

 -하나의 프로그래밍 언어가 반드시 둘 중 하나의 방식만으로 작동하는 것은 아니다. 

ex) Python도 컴파일을 하기는 하며, Java는 컴파일과 인터프리트를 동시에 수행한다.

≫'고급 언어를 저급 언어로 변환하는 방식에 컴파일 방식과 인터프리트 방식이 있다' 정도로만 이해하자.

 

3. 목적 파일 vs 실행 파일

① 목적 파일

 -목적 코드로 이루어진 파일

 -링킹 : 목적 코드를 실행 파일로 만드는 과정에서 목적 코드 간 기능을 연결 짓는 과정

소스 코드 helper.c : 'HELPER_더하기'라는 기능이 구현되어 있다.
main.c : helper.c에 구현된 'HELPER_더하기'와 프로그래밍 언어가 기본으로 제공하는 '화면_출력'이라는 기능을 사용
컴파일 helper.c를 컴파일 하여 helper.o라는 목적 파일을 생성
main.c를 컴파일 하여 main.o라는 목적 파일을 생성
링킹 main.o에는 'HELPER_더하기'와 '화면_출력'을 실행하는 방법이 없다.
>main.o에 없는 외부 기능을 main.o와 연결 짓는 작업이 필요
>>링킹 실행
실행 파일 링킹 작업이 완료 되면 실행 파일을 생성

② 실행 파일

 -실행 코드로 이루어진 파일

ex) .exe 확장자를 가진 파일


확인 문제

1. ③, ④

2. ②


03-2 명령어의 구조

1. 연산 코드와 오퍼랜드

명령어

 -연산 코드와 오퍼랜드로 구성되어 있다.

 -연산 코드 필드에는 연산 코드가, 오퍼랜드 필드(주소 필드)에는 오퍼랜드(숫자, 문자 등의 데이터/(대부분의 경우) 메모리 주소나 레지스터 이름)가 담긴다. 

 

①-1 연산코드(operation code, 연산자) 

 -명령어가 수행할 연산

 -기본적으로 데이터 전송, 산술/논리 연산, 제어 흐름 변경, 입출력 제어의 네 가지로 나뉜다.

데이터 전송 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 입출력 장치의 상태를 확인하라

 

①-2 오퍼랜드(operand, 피연산자)

 -연산에 사용할 데이터, 혹은 연산에 사용할 데이터가 저장된 위치

 -오퍼랜드의 개수에 따른 명령어

오퍼랜드의 개수 이름
없다(0) 0-주소 명령어
1개 1-주소 명령어
2개 2-주소 명령어
3개 3-주소 명령어

 

 

2. 주소 지정 방식

① 주소 지정이 필요한 이유

 -명령어의 길이는 한정적이기 때문이다. 명령어가 n비트이며 1-주소 명령어이고, 연산 코드 필드가 m비트일 때, 오퍼랜드 필드는 n-m비트이다.

 -오퍼랜드 필드 안에 데이터 자체가 아닌 메모리 주소(혹은 레지스터 이름)가 담길 때, 표현할 수 있는 데이터의 크기는 하나의 메모리 주소에 저장할 수 있는 공간만큼 커진다.

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

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

 

즉시 주소 지정 방식(immediate addressing mode)

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

 -장점 : 연산에 사용할 데이터를 메모리나 레지스터에서 찾는 과정이 없어서 다른 주소 지정 방식들보다 빠르다.

 -단점 : 표현할 수 있는 데이터의 크기가 작아진다.

 

직접 주소 지정 방식(direct addressing mode)

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

 -장점 : 표현할 수 있는 데이터의 크기가 즉시 주소 지정방식보다 크다.

 -단점 : 오퍼랜드 필드의 길이가 연산 코드의 길이만큼 짧아졌기 때문에 표현할 수 있는 유효 주소의 길이에 제한이 생길 수 있다.

 

간접 주소 지정 방식(indirect addressing mode)

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

 -장점 : 직접 주소 지정 방식보다 표현할 수 있는 유효 주소의 범위가 더 넓다

 -단점 : 두 번의 메모리 접근이 필요하여 위에 설명한 방식들보다 일반적으로 느리다.


데이터가 레지스터에 저장되어 있을 때 사용할 수 있는 방식들

 

레지스터 주소 지정 방식(register addressing mode)

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

 -장점 : CPU 외부의 메모리보다 CPU 내부의 레지스터에 접근할 수 있기에 직접 주소 지정 방식보다 빠르게 데이터에 접근할 수 있다.

 -단점 : 표현할 수 있는 레지스터 크기에 제한이 생길 수 있다.

 

레지스터 간접 지정 방식(register indirect addressing mode)

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

 -장점 : 메모리 접근 횟수가 1회로 줄어서 간접 주소 지정 방식보다 빠르다.

 

3. 스택과 큐

스택(stack)

 -제일 마지막으로 삽입된 데이터가 제일 먼저 삭제되는 형식의 저장 공간(LIFO;Last-In-First-Out, 후입 선출)

 -PUSH : 스택에 새로운 데이터를 저장하는 명령어

 -POP : 스택에 마지막으로 삽입된 데이터를 꺼내는 명령어

 

(queue)

 -제일 처음에 삽입된 데이터가 제일 먼저 삭제되는 형식의 저장공간(FIFO;First-In-First-Out, 선입 선출)

 -새로운 원소가 삽입되는 끝을 rear, 원소가 삭제되는 끝을 front라고 한다.

 -Enqueue (PUSH, insertion, addition, put 등을 사용하기도 한다) :  큐의 rear에 새로운 데이터를 저장하는 명령어

 -Dequeue (POP, deletion, removal 등을 사용하기도 한다) : 큐의 front에서 데이터를 꺼내는 명령어

 -간혹 스택과 동일한 PUSH, POP 명령어를 쓰기도 한다.


확인 문제

1. ②

2. ① 6, ② 200