소스 코드가 어떻게 컴퓨터를 동작시킬 수 있는 명령어로 변환될까?
개발자가 작성한 소스 코드를 컴퓨터는 바로 직독직해 후 이해를 할 수가 없다.
사람이 편하게 사용할 수 있는 언어는 고급 언어이다. 바로 고급 언어로 명령어를 실행 시킬 수 있는 것이 아닌 컴퓨터가 이해하는 저급 언어로 변환을 시켜야 명령어를 실행 시킬 수 있다.
고급 언어와 저급 언어
- 고급언어: 개발을 할 때 사용하는 언어
- 저급언어: 소스코드가 내부에서 실행될 때 사용하는 언어 = (컴퓨터가 이해하고 실행하기 위한 명령어)
* 저급언어
- 기계어: 0과 1로 이루어진 명령어로 구성됐다. 2진수로 표현도 하지만 16진수로도 표현된 기계어도 있다.
- 어셈블리어: 0과 1로 이루어진 기계어를 읽기 편한 형태로 번역한 형태
어셈블리어는 소스코드에 직접적으로 명시하면서 개발 시 사용되기 한다.
* 고급언어
c, c++, paython, java... 개발을 할 때 사용하는 용어
- 컴파일 언어: 소스코드 -> 컴파일러 -> 목적 코드
- 저급언어로 변환시켜주는 컴파일러로 컴파일 시킨 후 0과 1(명령어)로 이루어진 언어 인 목적 코드가 생성된다. ( 각 언어별로 전처리, 어셈블, 컴파일, 실행코드 등등 다양하고 자세한 단계가 있다. )
- 인터프리터 언어 : 인터프리터에 의해 한 줄씩 실행
- 소스 코드 전체가 저급 언어로 변환되기까지 기다릴 필요가 없다.
1) 컴파일 언어는 컴파일러에 의해서 일반적으로 한줄 씩이 아닌 처음부터 끝까지 전부다 훑어 본 후 오류와 최적화 등을 체크 한 후 전체를 통째로 컴파일 시킨다.
반대로 인터프리터 언어는 한줄 씩 해당 부분들을 체크하고 넘어간다.
2) 컴파일 언어는 소스 코드 컴파일 중 오류가 발생하면 코드 전체가 실행되지 않는다.
반대로 인터프리터 언어는 소스 코드 중간에 오류가 있더라도 해당 코드 직전까지의 코드는 실행한다.
ex) 컴파일 방식은 독일어를 모르는 친구한테 독일어로 이루어진 책을 통째로 한국어로 번역(목적코드)해 주는 방식이다.
인터프리터 언어는 한줄 씩 곁에서 번역해주는 형태
테스트 해보기
좌측에 있는 화면은 고급 언어 중 컴파일 언어이다.
우측에 있는 화면은 저급 언어로 변환된 모습이다.(어셈블리어 형태의 모습으로 변환됐다.)
컴퓨터가 알아듣고 실행할 수 있는 목적 코드 형태로 변경된 모습이다.
컴파일 언어와 인터프리터 언어는 모호한 경계를 갖고 있다.
모든 언어가 흑과 백처럼 양분된 개념이 아니기에 각 방식에 대한 고급 언어가 저급언어로 변환되는 대표적인 방식으로만 기억해 두는 것이 좋다.
명령어의 구조
명령어 하나하나는 어떻게 생겼고 어떻게 동작할까?
보통 사람이 명령을 할 때 구조 그대로 따르고 있다.
"망고야, 공 갖고와!"
- 컴퓨터를 동작시키는 명령어의 형태
명령어의 구조는 연산 코드와 오퍼랜드로 구성되어 있다.
무엇을 대상으로, 무엇을 수행하라 라는 형식으로 명령어가 구성되어져 있다.
- 연산 코드: 수행할 연산 즉, 명령어를 통해서 어떤 것을 하고 싶은지
- 오퍼랜드: 무엇을 대상으로 해당 연산을 수행할 지 연산에 사용될 데이터 혹은 연산에 사용될 데이터가 저장된 위치 형태를 갖고 있다.
[오퍼랜드]
* 오퍼랜드가 저장되는 위치 (오퍼랜드 필드)는 연산에 사용할 값이 담기기도 하지만, 연산에 사용될 저장된 값(주소) 가 담기는 부분이 훨씬 더 자주 사용된다.
오퍼랜드 필드를 주소 필드라고도 부른다.
기계어, 어셈블리어도 명령어이기에 명령어와 오퍼랜드로 구성되어 져 있다.
[연산 코드]
* 연산 코드의 종류 & 생김새는 CPU마다 다르다.
공통적인 연산 코드의 종류
1. 데이터 전송
- MOVE: 데이터를 옮겨라
- STORE: 메모리에 저장하라
- LOAD: 메모리에서 CPU로 데이터를 가져와라
- PUSH: 스택에 데이터를 저장하라
- POP: 스택의 최상단 데이터를 가져와라
2. 산술/논리 연산
- ADD/ SUBSTRACT/ MULTIPLY/ DIVIDE: 사칙연산을 수행해라
- INCREMENT/ DECREMENT: 오퍼랜드에 1을 더해라 / 오퍼랜드에 1을 빼라
- AND/ OR/ NOT: 연산을 수행해라
- COMPARE: 불린 값을 비교해라
3. 제어 흐름 변경
- JUMP: 특정 주소로 실행 순서를 옮겨라
- CONDITIONAL JUMP: 조건에 부합할 때 특정 주소로 실행 순서를 옮겨라
- HALT: 프로그램의 실행을 멈춰라
- CALL: 되돌아올 주소를 저장한 채 특정 주소를 실행 순서를 옮겨라
- RETURN: CALL을 호출할 때 저장했던 주소로 돌아가라
* CALL과 RETURN : 리턴 주소를 저장한 채 특정 주소를 실행
특정 함수를 실행하면 해당 함수가 저장되어 있는 메모리 주소에 있는 명령어를 실행하고
함수가 저장되어져 있는 공간의 명령어를 다 실행 했다면, 다시 함수를 호출했던 해당 부분으로 돌아온다.
4. 입출력 제어
- READ(INPUT): 특정 입출력 장치로부터 데이터를 읽어라
- WRITE(OUTPUT): 특정 입출력 장치로 데이터를 써라
- START IO: 입출력 장치를 시작하라
- TEST IO: 입출력 장치의 상태를 확인하라
명령어 주소 지정 방식
오퍼랜드 필드를 주소 필드라고도 부른다.
왜 굳이 저장된 위치를 사용할 까?
특정 위치의 값을 명령어에 저장하는 이유가 뭘까?
명령어 내에서 표현할 수 있는 데이터의 크기가 제한되기 때문이다.
즉 명령어의 크기를 최대한으로 사용할 수 있게 하기 위해서는 명령어 주소 자체를 오퍼랜드에 넣을 경우 표현할 수 있는 명령어의 크기가 커질 수있다.
표현 받을 수 있는 정보의 크기가 제한 받지 않도록 하기 위해서 오퍼랜드 필드에 연산에 사용되는 저장된 위치 값을 명시해서 사용한다.
오퍼랜드 필드에는 레지스터가 담길 수도 있고, 메모리 주소 값이 담길 수도 있고 또는 직접적으로 명시해줄 수도 있다.
* 유효주소(effective address) 연산에 사용할 데이터가 저장 될 위치
* 명령어 주소 지정 방식(addressing modes)
- 연산에 사용할 데이터가 저장된 위치를 찾는 방법
- 유효 주소를 찾는 방법
- 다양한 명령어 주소 지정 방식들
[메모리에 지정]
1. 즉시 주소 지정 방식(immediate addressing mode)
- 연산에 사용할 데이터를 오퍼랜드 필드에 직접적으로 명시하는 가장 간단한 형태의 주소 지정 방식이다.
- 연산에 사용 될 데이터의 크기가 작아질 수 있다. 하지만 속도는 빠르다.
2. 직접 주소 지정 방식(direct addressing mode)
- 오퍼랜드 필드에 유효 주소를 직접적으로 명시한 방식이다.
- 유효 주소를 표현할 수 있는 크기가 연산 코드만큼 줄어든다.
3. 간접 주소 지정 방식(indirect addressing mode)
- 오퍼랜드 필드에 유효 주소의 주소를 명시한다.
- 앞선 주소 지정 방식들에 비해 속도가 느리다.
- 메모리를 여러번 뒤적거려야 하기 때문에 시간이 걸린다.
- 유효 주소를 충분히 크게 가져올 수 있는 방식이다.
[레지스터에 지정]
1. 레지스터 주소 지정 방식(register addressing mode)
- 연산에 사용할 데이터가 저장된 레지스터를 명시한다.
- 메모리에 접근하는 속도보다 레지스터에 접근하는 것이 빠르다.
* 메모리는 cpu 밖에 있고 레지스터는 안에 있기에 접근 속도가 레지스터가 훨씬 더 빠르다.
2. 레지스터 간접 주소 지정 방식(register indirect addressing mode)
- 연산에 사용할 데이터를 메모리에 저장한다.
- 그 주소를 저장한 레지스터를 오퍼랜드 필드에 명시한다.
'CS > 컴퓨터구조와운영체제' 카테고리의 다른 글
장치 컨트롤러와 장치 드라이버 (0) | 2023.07.01 |
---|---|
RAM의 특성과 종류 (0) | 2023.06.06 |
컴퓨터구조_빠른 CPU 설계 기법 (0) | 2023.05.20 |
CPU 내부 구성 (0) | 2023.05.17 |
컴퓨터 구조 (0) | 2023.05.13 |
댓글