Programming/Computer Science

[혼공학습단 9기] 혼.공.컴.운. - 2. 데이터, 3. 명령어

리버김 2023. 1. 9.

02-1 0과 1로 숫자를 표현하는 방법

정보 단위

비트(bit): 0과 1을 나타내는 가장 작은 정보 단위. n비트는 2^n가지 정보를 표현할 수 있다.

 바이트(byte): 비트 X 8

  킬로바이트(KB): 바이트 X 1000

   메가바이트(MB): 킬로바이트 X 1000

    기가바이트(GB): 메가바이트 X 1000

     테라바이트(TB): 기가바이트 X 1000

 

이보다 큰 단위도 존재하지만, 우리가 다룰 데이터의 크기는 최대 테라바이트까지인 경우가 많다.

이진법

수학에서 0과 1만으로 모든 숫자를 표현하는 방법을 이진법이라고 한다. 숫자가 1을 넘어가는 시점에 자리 올림을 하여 0과 1. 두 개의 숫자만으로 모든 수를 표현한다. 10과 같이 십진수와 혼동되는 경우를 막기 위해 이진수 끝에 아래첨자 (2)를 붙이거나 앞에 0b를 붙인다. 전자는 이진수를 수학적으로 표기할 때, 후자는 코드 상에서 표기할 때 사용된다.

이진수의 음수 표현

컴퓨터는 0과 1만 이해할 수 있기 때문에 십진수처럼 마이너스 부호로 음수를 표현하는 것이 불가능하다.

널리 사용되는 방법은 '2의 보수'다. 어떤 수를 그보다 큰 2^n에서 뺀 값을 의미한다.

쉽게 표현하는 방법은 모든 0과 1을 뒤집고 마지막으로 1을 더하는 것이다. 즉, 1011(2)의 음수는 0101(2)이 된다.

 

[음수인지는 어떻게 알 수 있을까?]

플래그: 컴퓨터 내부에서 어떤 값을 다룰 떄 부가 정보가 필요한 경우 플래그를 사용한다.(04장에서 자세히 다룸)

 

십육진법

십육진법은 수가 15를 넘어가는 시점에 자리 올림을 하는 숫자 표현 방식이다. 그리고 십진수 10, 11, 12, 13, 14, 15를 십육진법 체계에서는 각각 A, B, C, D, E, F로 표기한다.

 

십육진수도 이진수와 마찬가지로 아래첨자 (16)이나 앞에 0x라는 별도의 문자를 붙여 표기해 십육진수임을 알게 한다.

 

십육진수를 사용하는 주된 이유 중 하나는 이진수를 십육진수로, 십육진수를 이진수로 변환하기 쉽기 때문이다.

 

십육진수를 이진수로 변환하기

십육진수를 이루는 숫자 하나를 이진수로 표현하려면 4비트가 필요하다. 십육진수를 이루고 있는 각 글자를 4개의 숫자로 구성된 이진수로 변환하고, 그것들을 그대로 이어 붙이면 십육진수의 이진수 변환이 끝난다.

 

예를 들어 1A2B(16)이라는 십육진수는 이진수로 0001101000101011(2)다.

 

이진수를 십육진수로 변환하기

반대로 이진수를 십육진수로 변환할 때는 이진수 숫자를 네 개씩 끊고, 끊어 준 네 개의 숫자를 하나의 십육진수로 변환한 뒤 그대로 이어 붙이면 된다. 이진수를 십진수로 변환할 때는 이렇게 간단하지 않기에 이진수를 십육진수로 묶어 표현하는 것이다.

 

02-2 0과 1로 문자를 표현하는 방법

문자 집합과 인코딩

문자 집합: 컴퓨터가 인식하고 표현할 수 있는 문자의 모음

문자 인코딩: 문자 집합에 속한 문자들을 0과 1로 변환한 결과값. 같은 문자 집합에 대해서도 다양한 인코딩 방법이 있을 수 있다.

문자 디코딩: 0과 1로 이루어진 문자 코드를 사람이 이해할 수 있는 문자로 변환하는 과정

 

아스키 코드

아스키(ASCII: American Standard Code for Information Interchange): 초창기 문자 집합 중 하나로, 영어 알파벳과 아라비아 숫자, 그리고 일부 특수 문자를 포함한다. 아스키 문자 집합에 속한 문자들은 각각 7비트로 표현되는데, 7비트로 표현할 수 있는 정보의 가짓수는 2^7개로, 총 128개의 문자를 표현할 수 있다. 

 

* 실제로는 하나의 아스키 문자를 나타내기 위해 1비트의 패리티 비트를 추가하여 8비트를 사용한다.

 

https://commons.wikimedia.org/wiki/File:ASCII-Table-wide.svg

 

File:ASCII-Table-wide.svg - Wikimedia Commons

-->

commons.wikimedia.org

위의 아스키 코드표를 보면 알 수 있듯, 아스키 문자들은 0부터 127까지 총 128개의 숫자 중 하나의 고유한 수에 일대일로 대응된다. 아스키 문자에 대응된 고유한 수를 아스키 코드라고 한다. 예를 들어 'A'는 십진수 65로 인코딩된다.

 

이렇듯 매우 간단하게 인코딩된다는 장점이 있지만, 단점도 있다. 한글을 표현할 수 있다는 것이다. 아스키 코드에 1비트를 추가한 확장 아스키가 등장하기도 했지만, 그럼에도 표현 가능한 문자의 수는 256개로 매우 부족했다.

 

그래서 EUC-KR이라는 한글 인코딩 방식이 등장했다.

 

EUC-KR

한글 인코딩에는 완성형과 조합형이 있다.

 

완성형 인코딩

  • 초성, 중성, 종성의 조합으로 이루어진 완성된 하나의 글자에 고유한 코드를 부여

조합형 인코딩

  • 초성, 중성, 종성 각각을 위한 비트열을 할당하여 그것들의 조합으로 글자 코드를 완성

EUC-KR은 대표적인 완성형 인코딩 방식. 초성, 중성, 종성이 모두 결합된 한글 단어에 2바이트 크기의 코드를 부여한다. 따라서 네 자리의 십육진수로 표현할 수 있다.

 

EUC-KR 인코딩 방식으로 2,350개 정도의 한글 단어를 표현할 수 있다. 그러나 이는 모든 한글 조합을 표현할 수 있는 양은 아니다.('쀍' 등)

 

이 문제를 해결하기 위해 등장한 것이 마이크로소프트의 CP949로, EUC-KR의 확장 버전이다. 그러나 이마저도 충분하지는 않다.

 

유니코드와 UTF-8

유니코드: EUC-KR보다 훨씬 다양한 한글을 포함하며 대부분 나라의 문자, 특수문자, 화살표, 이모티콘까지도 코드로 표현할 수 있는 통일된 문자 집합

 

UTF-8: 가장 대중적인 유니코드 문자에 부여된 값의 인코딩 방식


03-1 소스코드와 명령어

프로그래밍 언어가 어떻게 명령어가 되어 실행되는지 알아보자.

고급 언어와 저급 언어

고급 언어: 사람을 위한 언어

저급 언어: 컴퓨터가 직접 실행할 수 있는 언어

  • 기계어: 0과 1의 명령어 비트로 이루어진 언어
  • 어셈블리어: 기계어를 사람이 읽기 편한 형태로 번역한 언어

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

고급 언어가 저급 언어로 변환되는 방식에는 컴파일 방식과 인터프리트 방식이 있다.

컴파일 언어

컴파일러에 의해 소스 코드 전체가 저급 언어로 변환되어 실행되는 고급 언어(C 등)

 

컴파일: 소스 코드 전체가 저급 언어로 변환되는 과정

컴파일러: 컴파일을 수행 주는 도구

목적 코드: 컴파일러를 통해 저급 언어로 변환된 코드

인터프리터 언어

인터프리터에 의해 소스 코드가 한 줄씩 실행되는 고급 언어(Python 등)

소스코드 N번째 줄에 문법 오류가 있더라도 N-1번째 줄까지는 올바르게 수행

일반적으로 컴파일 언어보다 느림

 

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

 

*현대의 많은 프로그래밍 언어는 컴파일 언어와 인터프리터 언어 간의 경계가 모호하다.

 

03-2 명령어의 구조

연산 코드, 오퍼랜드, 주소 지정 방식이라는 개념 학습

연산 코드와 오퍼랜드

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

 

연산 코드: 명령어가 수행할 연산(연산자)

오퍼랜드: 연산에 사용할 데이터 또는 데이터가 저장된 위치(피연산자)

 

연산 코드 필드: 연산 코드가 담기는 영역

오퍼랜드 필드: 오퍼랜드가 담기는 영역

오퍼랜드

오퍼랜드 필드에는 숫자와 문자 등을 나타내는 데이터 또는 메모리나 레지스터 주소가 올 수 있다. 그래서 주소 필드라고도 한다. 주로 데이터보다는 데이터가 저장된 위치가 명시된다.

 

오퍼랜드는 명령어에 하나도 없을 수도 있고, 세 개 등 여러 개 있을 수도 있다. 갯수에 따라 0-주소 명령어, 1-주소 명령어, 2-주소 명령어, 3-주소 명령어가 있다. 

 

연산 코드

연산 코드는 네 가지 유형으로 나눌 수 있다. 연산 코드의 종류와 생김새는 CPU마다 다르기 때문에, 대부분의 CPU가 공통으로 이해하는 대표적인 연산 코드의 종류만 조금 알아보자.

  1. 데이터 전송
  2. 산술/논리 연산
  3. 제어 흐름 변경
  4. 입출력 제어 

 

[데이터 전송]

  • 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: 입출력 장치의 상태를 확인하라

 

주소 지정 방식

굳이 오퍼랜드 필드라는 걸 따로 두어 메모리나 레지스터의 주소를 담는 이유는 직접 데이터를 담는 것보다, 해당 메모리 주소를 명시하는 것이 저장 효율이 더 크기 때문이다.

 

유효주소: 연산 코드에 사용할 데이터가 저장된 위치

주소 지정 방식: 오퍼랜드 필드에 데이터가 저장된 위치를 명시할 때 연산에 사용할 데이터 위치를 찾는 방법, 즉, 유효 주소를 찾는 방법

 

현대 CPU는 다양한 주소 지정 방식을 사용한다.

 

즉시 주소 지정 방식

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

 

직접 주소 지정 방식

오퍼랜드 필드에 유효 주소를 직접적으로 명시하는 방식. 여전히 유효 주소를 표현할 수 있는 범위가 연산 코드의 비트 수만큼 줄어든다.

 

간접 주소 지정 방식

유효 주소의 주소를 오퍼랜드 필드에 명시한다. 유효 주소의 범위는 넓어지지만, 두 번의 메모리 접근이 필요하기 때문에 일반적으로 더 느린 방식이다.

 

레지스터 주소 지정 방식

연산에 사용할 데이터를 저장한 레지스터를 오퍼랜드 필드에 직접 명시하는 방법. 일반적으로 CPU 외부에 있는 메모리에 접근하는 것보다 CPU 내부의 레지스터에 접근하는 것이 더 빠르다. 다만, 표현할 수 있는 레지스터 크기에 제한이 생길 수 있다는 문제가 있다.

 

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

연산에 사용할 데이터를 메모리에 저장하고, 그 주소(유효 주소)를 저장한 레지스터를 오퍼랜드 필드에 명시하는 방법. 간접 주소 지정 방식과 비슷하지만, 메모리에 접근하는 횟수가 한 번으로 줄어든다. 

 

끝.

반응형

댓글