컴퓨터는 '명령어를 처리하는 기계' 라고 표현할 수 있다. 이러한 명령어를 만드는 프로그래밍 언어, 즉 소스코드가 컴퓨터 내부에서 명령어로 변환되는 과정을 알아보자.
컴퓨터는 C, C++, JAVA와 같은 프로그래밍 언어를 직접 이해할 수 없다.
프로그래밍 언어는 컴퓨터가 아닌 사람이 이해하고 작성하기 쉽게 만들어진 언어이다. 이렇게 사람을 위한 언어를 고급언어, 반대로 컴퓨터가 직접 이해하고 실행할 수 있는 언어를 저급언어 라고 한다.
기계어
0과 1의 명령어 비트로 이루어진 언어이다. 오로지 컴퓨터만을 위해 만들어진 언어이기 때문에 사람이 읽으면 그 의미를 이해하기 어렵다.
어셈블리어
기계어를 이해하기 어렵기 때문에 등장한 언어이다. 하지만 이것만으로 복잡한 프로그램을 만들기 쉽지 않다.
하지만 하드웨어와 밀접하게 맞닿아 있는 프로그램을 개발하는 임베디드 개발자, 게임 개발자, 정보 보안분야 등의 개발자는 어셈블리어를 많이 이용한다.
고급 언어를 저급 언어로 변환하는 방법으로 컴파일과 인터프리터 방식이 있다.
컴파일 언어
대표적인 언어로는 C, JAVA가 있다. 컴파일을 수생해 주는 도구를 컴파일러 라고 하는데 컴파일러는 소스 코드 전체를 쭉 훑어보며 소스 코드에 문법적인 오류가 없는지, 실행 가능한지 처음부터 끝까지 저급 언어로 컴파일 한다. 이때 오류를 하나라도 발견하면 컴파일에 실패한다.
컴파일이 성공적으로 수행되면 저급언어로 변환된다. 이렇게 컴파일러를 통해 저급언어로 변환된 코드를 목적코드 라고 한다.
인터프리터 언어
대표적인 언어로는 JavaScript, Python이 있다. 인터프리터 언어는 인터프리터에 의해 소스코드가 한 줄씩 실행되는 고급 언어이다.
컴파일 언어와 달리 오류가 있는 소스코드라도 실행이 되고, 오류를 만나야 실행이 멈춘다.
목적 파일과 실행 파일
컴파일러를 통해 저급언어로 변환된 코드를 목적코드 라고 했다. 그럼 실행 파일은 무엇인가?
여러 목적 파일( 목적 코드로 이루어진 파일 )을 링킹( 외부 기능들을 연결 짓는 작업 ) 작업까지 거치면 하나의 실팽파일(.exe)이 만들어 진다.
명령어의 구조와 주소 방식을 학습하며 작동원리를 이해해보자.
명령어는 연산코드와 오퍼랜드로 구성되어있다.
데이터 전송
산술/논리 연산
제어 흐름 변경
입출력 제어
위와 같은 네가지로 나눌 수 있고 이에 대한 자세한 내용은 담지 않겠다.
기계어와 어셈블리어 또한 명령어이기 때문에 연산 코드와 오퍼랜드로 구성되어 있다.
아래 작성한 어셈블리어에서 왼쪽에 글씨가 연산코드, 오른쪽 글씨가 오퍼랜드이다.push rbp mov rbp, rsp mov DWORD PTR [rbp-4],1
오퍼랜드 필드에 데이터가 저장된 위치를 명시 할 때 연산에 사용할 데이터 위치를 찾는 방법을 주소 지정 방식 이라고 한다. 다시 말해 유효 주소를 찾는 방법이다.
즉시 주소 지정 방식
연산에 사용할 데이터를 오퍼랜드 필드에 직접 명시하는 방식이다.
표현할 수 있는 데이터의 크기는 작지만 빠르다.
직접 주소 지정 방식
오퍼랜드 필드에 유효 주소(메모리의 주소)를 직접적으로 명시하는 방식이다.
즉시 주소 지정 방식보다는 표현 가능할 수 있는 범위가 커졌지만 여전히 연산 코드 비트 수만큼 유효 주소를 표현할 수 있는 범위가 줄어든다.
간접 주소 지정 방식
유효 주소의 주소를 오퍼랜드 필드에 명시한다. 이렇게 하면 유효 주소의 범위가 넓어졌지만 두 번의 메모리 접근이 필요하기 때문에 위 두 방식보다 느리다.
레지스터 주소 지정 방식
연산에 사용할 데이터를 저장한 레지스터를 오퍼랜드 필드에 직접 명시하는 방법이다.
레지스터에 직접 접근하기 때문에 다른 방식들 보다 빠르나, 직접 주소 지정 방식과 비슷한 문제를 공유한다.
레지스터
프로그램을 실행하는 데 필요한 값을 빠르게 가져오기 위해 있는
CPU 내부의 작은 임시 저장 장치이다.
레지스터 간접 주소 지정 방식
연산에 사용할 데이터를 저장한 레지스터를 오퍼랜드 필드에 직접 명시하는 부분은 레지스터 주소 지정 방식과 같으나 레지스터에는 연산에 사용할 데이터가 저장되어 있는 메모리의 유효 주소가 저장되어 있다.