본문 바로가기
컴퓨터공학/기초

명령어와 프로그램

by 하이방가루 2022. 7. 31.
728x90
반응형

이 글은 Crash Course의 Computer Science를 보고 정리한 글입니다.

CPU를 강력하게 만드는 점은 programmable 하다는 점이다. 명령어의 순서를 바꾸면, CPU는 다른 일을 수행한다. 그래서 CPU는 변경하기 쉬운 소프트웨어로 제어가 가능한 하드웨어 조각이다. 

 

이전 명령어에서 몇 가지 명령어를 추가해보겠다.

SUB는 두 개의 레지스터 값을 빼기 연산하는 기능이다.

JUMP는 프로그램을 새 위치로 점프시키는 것이다. 이것은 명령어 처리 순서를 바꾸거나, 명령어 몇 개를 건너뛰고 싶을 때 유용하게 사용된다. 예를 들어 "JUMP 0"는 프로그램을 처음으로 되돌아가게 할 수 있다. 자세하게 설명하자면 명령어의 뒤 4비트가 가리키는 값을 명령어 주소 (instruction address) 레지스터의 현재 값에 덮어쓰는 것이다.

JUMP_NEGATIVE는 JUMP의 스페셜 버전이다. 이 명령어는 ALU의 음수(negative) 플래그가 true로 설정될 때만 점프하는 것이다.

마지막은 컴퓨터가 처리를 언제 끝낼지 알려주는 HALT(중단) 명령어이다. 명령어와 데이터는 모두 같은 메모리에 저장되어 있다. 명령어와 데이터는 모두 이진수로 같기 때문에 명령어와 데이터를 구분해 줄 수 있는 HALT명령어가 진짜 중요하다.

JUMP를 이용하여 이전 프로그램을 바꿔보았다. CPU가 하듯이 프로그램 수행을 단계별로 알아보자.

제일 먼저, "LOAD_A 14"는 Register A에 값 1을 옮긴다. 다음 "LOAD_B 15"는 Register B에 값 1을 옮기고, 다음에 Register B와 A의 값을 합치고, 결과는 Register A로 가게 된다. 1+1=2이기 때문에, Register A의 값을 2가 된다. 그러고 나서, STORE 명령어로 메모리 13번지에 저장을 한다.

이제 "JUMP 2" 명령어가 나왔다. 이 명령어는 프로세서가 명령어 주소 레지스터 (instruction address register)의 값을 덮어쓰게 만든다. 현재 명령어 주소 레지스터의 값 4가 새로운 값 2로 덮어써진다. 그러면 프로세서는 다음 fetch 사이클에 HALT를 가져오지 않고 메모리 2번지에 있는 "ADD B A"를 가져온다. Register A의 값은 2이고, Register B는 1이기 때문에, 1+2=3, 즉 Register A는 3이라는 값을 갖게 된다. 이것을 메모리 13번지에 저장하고, 다시 "JUMP"를 만나서 "ADD B A"로 돌아간다. 이렇게 계속 1씩 증가하는 프로그램이다. 하지만, 빠져나갈 방법이 없다. 항상 JUMP를 만나기 때문에 절대로 HALT 명령어까지 갈 수가 없다. 이런 것을 무한 루프(infinite loop)라고 한다. 프로그램이 영원히 작동하는 것이다.

 

반복 루프를 빠져나오려면, 조건부 점프가 필요하다. 특정 상태가 되어야만 점프가 일어나는 것을 얘기한다.

JUMP_NEGATIVE 명령어가 조건부 점프의 한 예이다. 물론 JUMP_IF_EQUAL과 JUMP_IF_GREATER 등과 같은 다른 타입들도 있다.

 

좀 더 나은 프로그램을 만들어보자.

이 프로그램도 이전처럼 메모리에서 Register A와 B에 값을 옮기는 것으로 시작한다. Register A에 숫자 11을 Register B에 숫자 5를 옮긴다. 이제 Register A에서 Register B를 뺀다. 11-5=6이다. 따라서 Register A에 6이 저장된다.

이제 "JUMP_NEGATIVE"가 나온다. 이전 ALU 결과는 6이었다. 양수이기 때문에 음수(negative) 플래그는 false이다. 즉, 프로세서는 점프하지 않는다는 뜻이다. 그래서 다음 명령어를 계속 진행한다. "JUMP 2"는 조건이 없기 때문에 바로 명령어 2번으로 점프한다. Register A에서 Register B를 빼는 것으로 돌아가서 6-1=1, Register A에 1이 저장된다. 다음 명령어인 JUMP NEGATIVE로 돌아왔지만, 마찬가지로 음수 플래그가 false이기 때문에 점프하지 않는다. CPU는 "JUMP 2"를 실행하고, 다시 루프를 거슬러 올라가 "SUB B A" 명령어가 반복된다. 이번에는 1-5=-4로 처음으로 ALU가 음수(negative) 플래그를 true로 설정한다. 이제 다음 명령어 "JUMP_NEGATIVE 5"를 진행할 때, CPU는 "메모리 5번지로 점프"를 수행하게 된다. 루프에서 빠져나왔다. 이제 "ADD B A"를 실행해서 -4+5=1을 Register A에 저장한다. STORE명령어로 Register A의 값을 메모리 13번지로 저장한다. 마지막으로 "HALT"명령어를 만나 컴퓨터는 휴식을 취하게 된다.

 

이 프로그램은 7개의 명령어로 이루어졌지만, CPU는 총 13개의 명령어를 실행했다. 내부적으로 2번 루프를 돌았기 때문이다. 

 

이 코드는 11을 5로 나누었을 때 나머지를 계산하는 것이다. 결과는 1이다. 코드에 몇 줄 더 추가해서 루프를 몇 번 돌았는지 셀 수 있다. 즉, 5가 11에 몇 번 들어가는가? 루프를 2번 돌았으니 5는 11에 2번 들어가 있다. 11을 5로 나누면 몫이 2라는 것이다.

 

이 코드는 메모리의 저장한 수를 바꾸면 어떤 수라도 연산이 가능하다. 7과 81, 18과 53 어떤 것이든지 연산이 가능하다. 이것이 소프트웨어(software)의 파워이다. 

 

소프트웨어는 우리 하드웨어가 할 수 없는 일도 할 수 있게 해 준다. ALU는 나누기를 하는 기능이 없다. 프로그램이 이 기능을 할 수 있게 만든 것이다. 그리고 다른 프로그램은 이 나누기 프로그램을 사용해서 더 멋있는 일도 할 수 있을 것이다. 

 

우리의 CPU는 매우 기초적인 것으로 모든 명령어는 8비트이고, OPCODE는 처음 4비트만 차지하므로, CPU는 최대 16개의 명령어만 지원할 수 있다. 그것보다도 우리 명령어는 메모리 위치를 나타나는데 하위 4비트만 사용한다. 다시 4비트는 16개 값만 표현할 수 있으므로, 최대 16개의 메모리 위치만 가리킬 수 있다는 뜻이다. 예를 들어 16번지로 JUMP 할 수 없다. 16을 4비트로 표현할 수 없기 때문이다.

 

이런 이유로 진짜 현대 CPU들은 2가지 전략은 쓴다. 첫 번째, 직접적인 방법은 명령어 길이를 늘리는 것이다. 32비트나 64비트로 말이다. 이것을 명령어 길이(Instruction length)라고 한다. 두 번째 방법은 명령어 길이를 가변하는 것(Variable length instructions)이다. 예를 들어, CPU가 8비트 OPCODE를 사용한다고 해보자. 추가 정보가 필요없는 HALT 같은 명령어를 만나면, CPU는 즉시 실행할 수 있다. 하지만 JUMP와 같은 명령어를 만나면 CPU는 점프할 주소를 또 읽어와야 한다. 그래서 메모리에서 JUMP 명령어 뒤에 저장된 값을 즉시 읽어온다. 이것을 "Immediate Value"라고 한다.

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

 

이제 실제 CPU의 설계를 살펴보자. 1971년에 Intel은 4004 프로세서를 출시했다. 이것은 모든 기능을 단일 칩에 넣은 최초의 CPU이고 오늘날 우리가 알고 있는 Intel 프로세서의 기반을 닦은 칩이다.

Intel 4004 instruction set

위와 같이 46개의 명령어를 지원하고, 이것은 컴퓨터가 하는 모든 일들을 만드는데 충분하다. 지금까지 설명한 JUMP, ADD, SUBTRACT, LOAD 같은 명령어가 많이 사용된다. 이 CPU는 많은 메모리 주소를 가리킬 수 있도록 JUMP 같은 명령어에 8비트 Immediate value를 사용한다.

 

Intel Core i7과 같은 현대 컴퓨터 프로세서는 수천 개의 서로 다른 명령어와 명령어의 변형형태를 갖고 있다. 길이의 범위도 1바이트에서 15바이트에 이른다. 예를 들어 ADD의 변형형태로 10여 개 이상의 서로 다른 OPCODE를 갖고 있다. 이렇게 명령어 집합 크기가 거대하게 커지면서 많은 부가 부속품들이 사용된다. 이로 인해서 프로세서를 설계하는데 시간이 오래 걸리게 됐다.

728x90
반응형

'컴퓨터공학 > 기초' 카테고리의 다른 글

초기의 프로그래밍  (0) 2022.08.03
고급 CPU 설계  (0) 2022.08.02
중앙 처리 장치 (CPU)  (0) 2022.07.30
레지스터와 RAM  (0) 2022.07.30
컴퓨터는 어떻게 계산할까?-ALU편  (0) 2022.07.28

댓글