The journey to becoming a developer

My future is created by what I do today, not tomorrow.

Computer Science/Crash Course

Instructions & Programs: Crash Course Computer Science #8

Millie 2021. 10. 10. 17:48

 

이번 Crash Course 시간에는 명령어와 프로그램에 대해서 배워 본다.

한 번 보고 전부 완벽하게 이해하긴 어려운 내용이기 때문에 이 역시 소화하려면 차근차근 봐야 한다.

 


 

Last episode

ALU + control unit + memory + clock = CPU

이 네 가지를 결합해서 기초적이지만 기능적으로 동작하는 CPU를 지난 시간에 만들어보았다.

CPU는 컴퓨터의 심장과도 같은 것이다.

지금까지 이러한 부품(component)들을 전자 회로(electronic circuit)를 통해 만들어 왔다.

 

Today's episode

CPU를 만들었으니, 이제 CPU에게 실제로 처리할 명령어를 주자.

CPU가 강력한 이유는 프로그래밍이 가능하기 때문이다. 명령어의 순서를 바꿔서 쓰면, CPU는 그에 따라 다른 일을 수행한다.

CPU는 '변경하기 쉬운 소프트웨어'로 제어되는 '하드웨어 조각'이라고 할 수 있다.

The thing that makes a CPU powerful is the fact that it is programmable.
So the CPU is a piece of hardware which is controlled by easy-to-modify software!

 

 

지난 시간에 배웠던 것을 가져와보자.

 

컴퓨터 메모리는 위와 같이 생겼고, 각 주소에는 8비트 데이터가 저장되어 있다.

위의 가상의 CPU에서, 첫 4비트는 operation code(opcode)를 나타낸다. 나머지 4비트는 메모리 주소나 레지스터를 나타낸다.

 

메모리 주소 0에는 0010 1110이 있다.

여기서 0010은 opcode에 해당하며, "LOAD_A" 명령어와 대응된다. 이 명령어는 다음 4비트에 있는 메모리 주소(1110, 십진수로는 14)에서 데이터를 읽어 와서 Register A에 저장한다.

 

앞으로 이것을 LOAD_A 14 라고 생각해 보자.

 

우리의 프로그램에는 명령어가 단 4개뿐이다.

메모리에 하단에는 3과 14라는 숫자를 넣었다.

이 상태에서 프로그램을 구동시켜보자.

 

LOAD_A 14메모리의 14번지에 저장된 숫자 3을 Register A에 저장한다.

LOAD_B 15메모리의 15번지에 저장된 숫자 14를 Register B에 저장한다.

 

LOAD A, LOAD B는 이런 식인데, ADD instruction은 어떨까?

이 명령어는 프로세서가 ALU를 사용해서 2개의 register를 서로 더하라는 것이다. 이 경우에는 ADD B A라고 썼으니, B와 A를 더하는 것이다.

순서가 중요한데, 왜냐면 더하기의 결과가 두 번째로 적혀 있는 레지스터에 저장되기 때문이다. 그렇기 때문에 이 경우 더하기의 결과가 Register A에 저장된다.

 

마지막 명령어 STORE_A 13의 뜻도 알아보자.

이것은 CPU가 Register A의 값을 메모리 13번지 위치에 쓰라고 명령하는 것이다. 

 

이렇게 우리의 프로그램은 두 숫자를 더했다. 명령어 4개를 가지고 말이다!

 

Instruction Table

 

명령어를 좀 더 추가해 보자.

 

SUB : ADD와 비슷하게 두 레지스터를 연산한다.

JUMP : 프로그램이 새로운 위치로 "점프"하도록 한다.

  • 명령어 처리 순서를 바꾸거나, 명령어 몇 개를 건너뛰고 싶을 때 유용
  • JUMP 0은 프로그램을 처음으로 되돌아가게 할 수 있음
  • low level에서 본다면, 이것은 명령어의 뒤 4비트가 가리키는 값을 명령어 주소 레지스터의 현재 값에 덮어쓰는 것이다.

JUMP_NEG : 이것은 JUMP의 스페셜 버전인데, ALU의 음수 플래그가 true로 설정되었을 때만 점프한다.

 

에피소드 5에서, 산술 연산 결과가 음수가 되면 음수 플래그가 설정된다는 것을 배웠다. 즉 연산 결과가 0이거나 양수이면 음수 플래그는 설정되지 않는다. 이 때는 JUMP_NEGATIVE에 의한 점프가 일어나지 않고, CPU 바로 다음 명령어를 처리하게 된다.

 

HALT : 컴퓨터가 처리를 언제 끝낼지 알려주기 위해 중단 명령어가 필요하다. 

  • 사실 우리가 만든 프로그램에도 HALT가 있었어야 했다. 이게 없다면 CPU는 STORE 명령 이후에도 계속 0을 읽어서 수행한다. 그런데 opcode가 0인 명령어는 없기 때문에, 컴퓨터는 crash 되었을 것이다.
  • 명령어와 데이터를 모두 같은 메모리 안에 저장한다는 사실을 유념하자. 명령어와 데이터는 모두 이진수이기 때문에, 근본적으로는 차이가 없다. 그렇기 때문에 HALT 명령어가 정말 중요하다. 명령어와 데이터를 구분해주기 때문이다. 

 

Infinite Loop

 

JUMP 명령어를 추가해서 프로그램이 좀 더 역동적으로 되었다.

그런데 이 프로그램은 절대 HALT까지 갈 수가 없다. 항상 JUMP를 만나기 때문에 HALT 명령어까지 갈 수가 없게 된다. 

 

이것을 빠져나오려면, Conditional JUMP가 필요하다. 이것은 특정 조건이 충족되어야만 점프하는 것이다.

아까 설명했던 JUMP_NEGATIVE가 바로 조건부 점프의 한 예이다.

 

이것을 사용하면, 나누기 프로그램를 구현할 수 있다. 결과로는 나눗셈의 나머지를 도출해낸다.

코드를 좀 더 추가한다면 루프를 몇 번 돌았는지도 구할 수 있다.

 

New levels of abstraction

소프트웨어는 하드웨어가 할 수 없는 일도 할 수 있게 해 준다.

ALU에는 나누기를 하는 기능은 없었다. 프로그램이 이 기능을 할 수 있게 만든 것이다.

다른 프로그램은 이 나누기 프로그램을 이용해 더 멋있는 일을 할 수 있다.

이것이 바로 새로운 추상화 레벨이다.

 

Two strategies for modern CPUs

limitations

우리가 만든 CPU는 매우 기초적이다. 명령어는 8비트이고, opcode는 처음 4비트만 차지한다.

즉 이 말은 4비트의 모든 조합을 사용하더라도, CPU는 최대 16개의 명령어를 지원할 수 있다는 말이다.

게다가 우리의 명령어는 메모리 위치를 나타내기 위해서 끝 4비트만 사용한다. 이건 16개의 메모리 위치만 나타낼 수 있다는 것인데, 이걸로는 많은 일을 할 수가 없다.

이것을 해결하기 위해 모던 CPU는 두 가지 전략을 쓴다.

 

1. To have bigger instructions

첫 번째 방법은 매우 직관적으로, 32비트나 64비트처럼 명령어의 길이 자체를 늘리는 것이다.

 

2. To use variable length instructions

두 번째 방법은 길이가 변화할 수 있는 명령어를 사용하는 것이다.

예를 들어 CPU가 8비트의 opcode를 쓴다고 생각해 보자.

추가 정보가 필요 없는 HALT 같은 명령어를 만나면, CPU는 바로 실행할 수 있다.

그러나 JUMP같은 명령어를 만나면, CPU는 점프할 주소를 또 가져와야 한다는 것을 안다. 그래서 메모리에서 JUMP 명령어 뒤에 저장된 값을 즉시 읽어온다. 이것을 Immediate Value라고 한다.

이러한 프로세서를 설계하면, 명령어의 길이는 몇 바이트라도 만들 수 있지만 대신 CPU의 fetch cycle이 좀 더 복잡해진다.

 

 

A real CPU example

우리의 예제 CPU와 명령어의 집합은 가상이긴 하지만, 중요한 기본 원칙은 설명할 수 있도록 설계되었다.

이번엔 실제 예시를 보자.

 

1971년에 Intel은 4004 processor를 출시했다.

이것은 모든 기능을 하나의 칩에 넣은 최초의 CPU이고, 오늘날 Intel processor의 기반을 닦았다.

46개의 명령어를 지원하는데, 이것은 컴퓨터가 하는 모든 일들을 빌드하는 데 충분하다. 위에서 설명했던 JUMP, ADD, SUBTRACT, LOAD 같은 명령어가 많이 사용된다.

이 CPU는 많은 메모리의 주소를 가리킬 수 있도록 JUMP 명령어에 8비트 immediate value를 사용한다.

 

 

CPU는 1971년 이후 긴 여정을 거쳐왔고, 이젠 Intel Core i7같은 현대 컴퓨터 프로세서가 출시되었다.

이것은 수천 개의 명령어와 instruction varient들을 가지고 있다. 명령어의 길이 역시 1바이트에서 15바이트에 이른다.

 

예를 들어, ADD의 변형 형태로 10여 개 이상의 opcode를 가지고 있다.

이렇게 명령어의 집합 크기가 거대해지면서 많은 부속품들이 사용되게 되었다.

따라서 프로세서를 설계하는 데 걸리는 시간도 증가했는데, 이것은 다음 시간에 다뤄본다.