1-1
차례에선 이 2진법에 대해 자세하게 다룰 예정이며, 다음 절에선 명령어의 정의와 이에 대한 기본적인 것을 설명하고, 글 전체에 걸쳐 자세히 설명할 예정이다.
컴퓨터 구조론 기초에 들어가기 앞서 가장 중요한, 기본적인 요소라 하면 컴퓨터는 2진법, 즉 0
과 1
만 이해할 수 있다는 것이다.
먼저 2진법이라는 단어가 생소할 수 있다. 우리가 일반적으로 쓰이는 숫자 체계는 무엇인가?
체계라 하면 이해가 안될 수 도 있으니 쉽게 생각해보자. 우리가 쓰는 숫자(아라비아 숫자)는 총 10개의 숫자가 있다. (1 2 3 4 5 6 7 8 9 0
)
9
를 넘어가면 10
이 된다는건 당연시 되어왔을 건데, 10
이란 수는 1
과 0
의 조합인 수다. 이처럼 9
를 넘어가면 기존의 숫자인 1
과 0
의 조합이 된다.
즉 한 자리에서 수가 10
이 될때마다 자리올림이 발생하고, 다음 자리로 1
을 올려주는 과정이라 할 수 있다.
10
은 1 x 10 + 0 x 1
라는 식으로 표현할 수 도 있다. 일의 자리에서 10
으로 올려졌으므로 십의 자리로 1
이 올라갔다.
즉 n진법
이라 하면, n
을 기준으로 하여 자리올림하는 수의 표현법이라 할 수 있다.
[!NOTE]
이제부터 여러 진법의 수가 나올텐데, 그냥 쓰면 헷갈릴 수 있다. 이때 아래첨자를 사용하여 이를 구분하는데, 이 글에선 괄호 속 수를 통하여 여러 진법을 구분할 것이다.
예를 들어
- 2진법의
1011
=>1011₂
또는1011b
- 8진법의
144
=>144₈
또는144o
- 10진법의
104
=>104₁₀
또는104d
(생략)- 16진법의
3F2
=>3F2₁₆
또는0x3F2
라는 식으로 표현할 예정이다. 다만 10진법의 경우 일반적으로 사용하는 진법이라 접미사는 생략할 예정이다.
설명이 길었다. 이제 이 체계를 그대로 적용하는데, 숫자가 0
과 1
밖에 없는것이 2진법이다.
0₂
다음 1₂
이 되고, 그 다음(1₂ + 1₂
)은 자리올림이 발생하여 10₂
이 된다. 여기서 다음은 11₂
, 그 다음은 100₂
이 되는 식이다. (각각 0
1
2
3
4
에 해당한다)
이는 원리이고, 이를 쉽게 계산하는 방법이 크게 두가지가 존재한다. 하나는 나눗셈을 이용하는 방법, 다른 하나는 각 자리에 2의 n제곱
을 적용하여 계산하는 방법이다.
이 글에서 설명한 방법은 후자를 택하여 계산해볼 예정이다. 만약 1011
이라는 2진법을 10진수로 변환해보자.
1 0 1 1 b
8 4 2 1 d
=> 2³ x 1
+ 2² x 0
+ 2¹ x 1
+ 2⁰ x 1
이 과정을 반대로 하면 10진수를 2진수로 변환할 수 있다. 이 방법 말고도 다른 방법이 있으나, 다른 글을 참고하길 바란다.
이 외에도 8진법이나 16진법 등이 있으나, 16진법의 경우 컴퓨터를 다루다보면 자주 등장하는 진법이니 간략히 설명하겠다.
16진법은 이름에서도 알 수 있듯이 16개의 수를 사용하는데, 여기선 0-9
의 수와 A-F
의 문자로 총 16개를 사용한다.
10진수 10
은 16진수 A₁₆
로 표현될 수 있고, 10진수 15
은 F₁₆
라 표현되며, 그 다음은 16
은 10₁₆
로 표현된다. 20
은 14₁₆
가 된다.
16진수의 변환법, 8진수 등은 다른 글을 참조해보길 바란다.
컴퓨터에선 문자 또한 0과 1로 표현된다. 이를 설명하기 전에 문자 집합과 인코딩/디코딩에 대해 알아보자.
컴퓨터는 문자를 인코딩/디코딩할때 문자 집합을 참조하여 변환하기 때문에, 어떠한 문자가 해당 문자 집합에 속하지 않는다면 해당 문자를 표현할 수 없다.
만약 문자 집합이 영어 알파벳(a-zA-Z)만 존재한다면, 2와 같은 숫자의 문자는 표현할 수 없다는 의미다.
문자 집합엔 여러가지가 있으나, 이 글에서 알아볼 문자 집합은 아래의 ASCII와 유니코드다.
초기에 개발된 문자 집합 중 하나로, 영어 알파벳, 아라비아 숫자(0-9), 일부 특수문자 및 제어 문자로 구성되었다.
예를 들어 ASCII A
는 65
로 인코딩된다. 반대로 97
이라는 값이 있다면, 이는 ASCII a
로 디코딩된다. 이처럼 각 문자 집합의 문자엔 하나의 할당된(부여된) 값이 존재하는데, 이를 코드 포인트라고 한다.
ASCII는 문자 하나당 8비트로 구성되는데, 표현 가능한 문자는 128개 뿐이다.
그럼 7비트 아니냐? (2⁷), ASCII는 7비트를 문자로 할당하고, 나머지 하나는 오류 검출을 위한 패러티 비트인데, 이는 추후에 설명할 예정이다.
하지만 이러한 ASCII만으론 한글, 한자 등의 다양한 문자를 표현하기엔 턱없이 부족하다. 이러한 문제를 해결하기 위해 Extended ASCII 등의 확장된 문자 집합도 등장하였으나, 여전히 부족하다.
그래서 초장기엔 여러 언어에 해당하는 문자 집합을 개발하게 되었으나, 각 언어마다 매번 문자 집합을 다르게 해줘야 하며 여러 언어를 혼용할 수 없기 때문에 유니코드라는 문자 집합이 등장한다.
유니코드를 설명하기 전에 한글의 표현에 대해 설명하겠다. 먼저 한글은 영어와는 다르게 초성, 중성, 종성의 조합으로 문자가 만들어진다.
그렇기에 한글 문자를 하나하나 등록해둔 완성형 인코딩 방식과 조합형 인코딩 방식으로 나눠지게 된다.
말 그대로 문자 하나하나를 등록해두고, 이에 대한 코드 포인트를 부여한다. 당연히 ASCII에 비해 코드 포인트의 크기가 커질 것이고, 이는 조합형 인코딩 방식에서도 마찬가지다.
'가'
, '나'
, '다'
… '힣'
등의 거의 모든 글자를 등록한다.
여기서 ‘거의’라는 의미는 존재하지 않는 글자 또한 존재한다는 의미고, 이는 곧 완성형 인코딩의 단점이 된다.
완성형과는 다르게 글자 하나를 조합하는 방식이며, 예를 들어 간
이라는 글자가 있다면 ㄱ + ㅏ + ㄴ
으로 조합된다.
KS X 1001, KS X 1003 문자 집합 기반으로, 완성형 인코딩 방식인 EUC-KR이 등장한다. 글자 하나 당 2바이트 크기의 코드 포인트가 부여된다.
EUC-KR은 약 2000개 이상의 한글을 표현할 수 있으나, 그럼에도 복잡한 글자는 표현할 수 없다. 이러한 이유로 유니코드가 자주 사용된다.
유니코드는 전세계의 모든 문자를 일괄되게 표현할 수 있게 하는 표준 문자 집합이다. 유니코드의 코드 포인트를 표현할땐 U+[16진수]
형식으로 표현된다.
유니코드엔 UTF-8
, UTF-16
, UTF-32
와 같이 다양한 문자 집합이 있다.
앞서 열심히 2진법에 설명한 이유는 알겠지만 컴퓨터는 2진수로만 처리할 수 있기 때문이다. 즉 지금 보고있는 글자도, 사진도, 유튜브의 동영상도 모두 2진수로 되어있다는 의미이다.
여기서 0
과 1
, 그 최소한의 단위를 비트(bit)
라고 하며, 8비트라 하면 8자리의 이진수를 뜻한다. (예: 10011100₂
)
허나 비트로만 표현하면 단위로 쓰기엔 너무 클때가 있고, 추후에 설명하겠지만 컴퓨터에서 처리할 수 있는 최소 단위는 바이트(Byte)다.
1바이트는 8비트이며, 추후에 설명할 ASCII 또한 문자 하나당 1바이트로 할당된다.
그 이상의 단위는 다음과 같다:
단위 | 크기 |
---|---|
1바이트(byte) | 8비트(bit) |
1킬로바이트(kB) | 1000바이트 |
1메가바이트(MB) | 1000킬로바이트 |
1기가바이트(GB) | 1000메가바이트 |
1테라바이트(TB) | 1000기가바이트 |
[!NOTE]
단위의 크기를 나타낼 때 1024 라는 수가 많이 나오기도 하는데, 들의 단위는 kiB, MiB 등의 단위로 나타낸다. 1000단위로 나타내는것을 SI 단위계, 1024씩 나타내는 단위를 이진 단위라 한다.
마지막으로 워드(Word)라는 단위가 자주 등장할 예정인데, 이는 CPU가 한번에 처리할 수 있는 데이터의 크기를 의미한다.
만약 CPU가 64비트 CPU라면 1 word = 64
이며, 아래와 같은 워드 단위가 존재한다.
[!NOTE]
목차 상 명령어를 먼저 설명하나, 원활한 이해를 위해 후반의 컴퓨터 구조(메모리, 레지스터 등)를 먼저 보고 오거나, 오고가며 병행하는걸 추천한다.
우리가 사용하는 모든 컴퓨터의 프로그램은 명령어의 집합이다.
[!INFO]
프로그램, program
- (명사) 진행 계획이나 순서. 또는, 그 목록. 순화어는
계획(표)',
차례(표)’. “∼을 짜다”- (컴퓨터) 어떤 문제를 해결하기 위해 컴퓨터에게 주어지는 처리 방법과 순서를 기술한 일련의 명령문의 집합체.
정의 출처: Oxford Languages
위 사전과 같이 프로그램은 어떠한 명령어들이 순서대로 짜여져있는것을 의미하며, 컴퓨터 또한 이 순서로 프로그램의 명령어를 실행하게 된다.
우리가 누군가에게 명령을 할때, 일반적으로 ~을 대상으로 ~무엇을 해라
식으로 말한다. 이처럼 컴퓨터의 명령어 또한 ‘무엇’에 해당하는 수행할 연산과 ‘대상’엔 연산에 사용될 데이터(또는 주소)가 해당된다.
예를 들어, '1과 2를 더해라'
라는 명령문은 컴퓨터에선 명령어 add
와 연산에 사용될 1
과 2
로 나뉠 수 있다.
컴퓨터에선 무엇(명령)
을 연산 코드(opcode, operation code), 연산에 사용될 데이터(또는 주소) 대상을 피연산자(operand)라 칭한다.
즉 컴퓨터의 명령어는 opcode와 operand의 조합인것이다.
또한 이러한 명령어는 instruction이라고 하고, 이러한 명령어의 집합을 instruction set이라고 한다.
[!NOTE]
해당 글에선 명칭의 가독성을 위해 opcode와 operand로 통일한다.
또한, 아래에서 설명하는 명령어는 이해를 돕기 위한 가상의 명령어이며, CPU 아키텍쳐2, 종류에 따라 재각각 다르다.
opcode엔 다양한 역할의 opcode가 존재하는데, 크게 아래와 같은 종류의 opcode가 존재한다.
각 종류에 대해 아래와 같은 명령어들이 존재하며, 여러 CPU 아키텍쳐에 따라 다르나 대부분 비슷한 계열의 명령어들이 존재한다.
데이터 전송엔 데이터를 옮기는 MOVE
, 메모리에 저장하는 STORE
, 메모리에서 꺼내오는 LOAD
, 이 밖에 스택과 관련된 PUSH
, POP
과 같은 명령어가 존재한다.
데이터 연산엔 ALU가 담당하는 연산들의 명령어가 존재하며, ADD
(덧셈), SUB
(뺄셈), MUL
(곱셈), DIV
(나눗셈) 등과 같은 기본적인 사칙연산 명령어들과 논리 연산자(AND
, OR
등)와 같은 명령어가 존재한다.
흐름 제어에 대해 배우기 전에 프로그램 카운터(PC)에 대해 알면 좋으나, 이는 추후에 설명하고 지금은 ‘프로그램의 명령어들은 프로그램 카운터(PC)에 따라 실행된다’ 라고 생각하자. 여기서 흐름 제어는 이러한 프로그램 카운터를 제어하여 명령어들의 순서 등을 옮기는 명령어다.
여기엔 특정 주소로 순서(PC)를 옮기는 JUMP
, 프로시저와 관련된 CALL
, RETURN
등의 명령어가 존재한다. 여기서 HALT
와 같은 프로그램을 종료하는 명령어도 흐름 제어에 속한다.
입출력 제어엔 추후에 설명하겠지만, 외부 장치와 소통하는것을 일반적으로 칭한다. 즉 외부 장치와 소통하기 위한 인터럽트와 관련된 기능과 같은 명령어들이 존재한다.
operand(피연산자)엔 데이터가 직접 들어가거나 주소(또는 레지스터)가 들어간다. 주소에 대해선 추후 ‘메모리’ 절에서 자세히 설명하겠지만(미리 보고와도 좋다), 간단하게 설명하면 메모리엔 데이터(프로그램 명령어 또한 메모리에 저장된다)가 저장되는 공간엔 ‘주소’가 부여 된다.
즉, 해당 주소에 접근한다는건 해당 주소 공간에 있는 데이터에 접근한다는 의미이다.
여기서 몇개의 주소를 넣을것인가에 따라 n주소 명령어라고 한다. 만약 PUSH와 같이 operand가 없는 경우 0주소 명령이라고 한다.
다시 돌아와서 피연산자에 주소가 들어갈 수 있다고 했는데, ‘그냥 데이터를 직접 넣으면 되지 않나’라고 생각할 수 있다. 만약 32비트의 명령어에 opcode가 1바이트(8비트)라 한다면 operand의 크기는 3바이트(24비트)가 된다. 하지만 만약 데이터가 32비트 이상이라면 명령어에 직접 넣기엔 공간이 부족하다.
이 처럼 32비트의 데이터를 메모리에 저장하고, 그 메모리 주소를 명령어에 넣음으로써 32비트 데이터를 사용할 수 있게 된다. 연산에 사용되는 데이터가 저장된 메모리 주소 위치를 유효 주소(effective address)라고 한다.
명령어에 주소를 넣을땐 다양한 방식이 있는데, 이러한 방식을 메모리 지정 방식이라고 한다.
[!NOTE]
주소 지정 방식엔 설명하는 글 또는 아키텍쳐마다 이름이 다를 수 있다. 다만 그 방식에 대해선 대부분 같은 역할이므로 방식에 대해서 이해만 해도 좋다.
메모리 주소 지정 방식(Addressing Mode)엔 크게 아래와 같은 방식이 존재한다.
연산에 사용할 유효 주소를 명시하는 방식으로, 데이터를 직접 넣는 방식에 비해 데이터의 크기가 커지나 메모리에 접근해야되기 때문에 속도가 느려진다.
또한 32비트 명령어에서 opcode가 8비트라면 operand의 크기는 24비트, 유효 주소의 크기 또한 24비트로 제한된다. 즉 주소가 32비트라면 명시가 불가하다.
레지스터 주소 지정(Register Addressing)
레지스터에 접근하는것이 메모리에 접근하는 것 보다 빠르므로 직접 주소 지정 방식보다 빠르다.
이 밖에도 주소를 어떠한 값(레지스터 등)과 계산하여 구하는 방식인 계산에 의한 주소 지정(Calculated Addressing) (또는 변위 주소 지정, Displacement Addressing)이 존재한다.
변위 주소 지정 방식은 명령어에 두개 이상의 operand가 있고, 이들을 사용하여 계산한다.
이 밖에도 아키텍쳐에 따라 다양한 주소 지정 방식이 존재한다. 허나 위에서 설명한 주소 지정 방식은 대부분 존재하므로 알아두는것이 좋다.
추가적으로 주소 지정 방식의 속도를 빠른 순으로 정리하면 다음과 같다.
묵시적 > 즉시 > 레지스터 > 직접 > 레지스터 간접 > 간접 > 변위