Published on

명령어 집합(instruction set)

Authors
  • avatar
    Name
    유사공대생
    Twitter

명령어

명령어는 컴퓨터가 PC를 통해 메모리에서 찾는 내용을 말한다. 어떤 종류의 명령어를 CPU에서 찾을 수 있는지 보고, 명령어에 어떤 비트 패턴을 선택해야 하는지 알기 위해 16비트 크기의 명령어를 사용하는 컴퓨터를 가정한다.

3주소 명령어

image

이렇게 필드를 나누는 게 좋은 아이디어 같긴 하지만 실제로는 잘 작동하지 않는다. 결과와 피연산자 주소에 4비트밖에 쓸 수 없기 떄문이다. 사용할 수 있는 주소가 16개밖에 없으면 쓸모 있는 크기의 메모리를 가리킬 수 없을 것이다. 명령어를 더 크게 만들수도 있지만 64비트 크기의 명령어를 사용해도 주소로 쓸 수 있는 크기는 20비트뿐이다. 20비트로는 몇 메가바이트밖에 처리 할 수 없기 때문에 좋은 방법은 아니다.

또 다른 접근 방법은 DRAM 주소 지정 기법을 활용하는 것이다. 주소 확장 레지스터(address extention register) 를 두고 별도의 명령어를 사용해 상위주소를 지정한다.

물론 주소 확장 레지스터에 갑을 설정하려면 시간이 더 들고, 이런 접근 방식을 사용해 메모리 영역을 모두 지정하려면 여러 번 레지스터를 설정해야 한다는 단점이 있다.

이런 3주소 명령어(three-address)가 잘 작동하지 않는 더 중요한 이유가 있다. 3주소 명령어는 세가지 메모리 위치를 한꺼번에 지정하는(물리적으로 존재하지 않는) 메모리 주소 접근을 사용한다.

image

이 경우 피연산자 A의 내용과 피연산자 B의 내용을 저장하는 레지스터를 추가해 명령어를 처리할 수 있다. 하드웨어는 다음과 같은 절차를 거쳐 명령어를 처리한다.

  1. 프로그램 카운터에 들어있는 주소를 사용해 메모리에서 명령어를 읽어온다.
  2. 명령어의 피연산자 A 부분에 있는 주소를 사용해 메모리에서 데이터를 읽어서 피연산자 A 레지스터에 저장한다.
  3. 명령어의 피연산자 B 부분에 있는 주소를 사용해 메모리에서 데이터를 읽어서 피연산자 B 레지스터에 저장한다.
  4. 연산을 수행한 결과를 명령어를 결과 부분에 있는 주소에 해당하는 메모리에 저장한다.

이런 명령을 수행하는 회로는 아주 복잡해진다. 각 단계를 한 클록에 수행한다면 명령어를 처리하기 위해 네 클록이 필요하다. 한 번에 한 메모리 위치에만 접근할 수 있다는 사실에 맞춰 명령어 집합을 적절히 설계해야 한다는 사실을 깨달아야 한다.

1주소 명령어

image

누산기(accumulator)라는 레지스터를 추가한다면 1주소 명령어로 바꿀 수 있다. 누산기는 ALU가 계산한 결과를 저장한다. 물론 누산기에 있는 값을 메모리에 저장하기 위해 저장(store) 명령어를 추가해야 한다. 그렇게 하면 명령어 구성을 위 그림처럼 바꿀 수 있다.

위 그림처럼 하면 더 많은 주소 비트를 사용할 수 있지만, 같은 일을 할 떄 더 많은 명령어가 필요하다.

3주소 명령어는 C = A + B를 사용하여 계산하였다. 하지만 이제는 명령어가 3개가 필요하다.

누산기 = A

누산기 = 누산기 + B

C = 누산기

한 명령어를 세 명령어로 바꾸어서 결과적으로 명령어를 더 크게 만근 셈이다. 그래서 명령어를 개선한다는 목적에는 어긋나 보인다. 하지만 일반적으로는 이 말이 성립하지 않는다.

만약 다음 식을 계산한다고 해보자.

D = A + B + C

네가지 주소가 연관되어서 3주소 명령어를 사용하더라도 이 식을 한 명령어로 처리할 수 없다. 이 식을 3주소 명령어로 처리하려면 다음과 같은 과정이 필요하다.

중간값 = A + B

D = 중간 값 + C

12비트 주소를 사용하면 세 가지 주소와 명령 코드를 저장하기 위해 40비트 명령어를 써야 한다. 그리고 이런 명령어가 2개 필요하므로 D를 계산하기 위해 80비트의 명령어가 필요하다.

하지만 1주소 명령어를 사용하면 4개의 명령어를 사용하고 64비트가 필요하다.

누산기 = A

누산기 = 누산기 + B

누산기 = 누산기 + C

D = 누산기

주소 지정 모드

image

누산기를 사용하면 12비트를 주소 지정레 쓸 수 있고, 4096가지 주소를 가리킬 수 있으면 16가지 주소를 가리킬 수 있는 것보다는 훨씬 낫지만, 여전히 사용 가능한 주소가 충분하지 않다. 이런 방식으로 주소를 가리키는 경우를 직접 주소 지정(direct addressing) 이라고 부른다.

간점 주소 지정(indirect addressing) 을 추가하면 더 많은 메모리를 사용할 수 있다. 간접 주소 지정 방식에서 CPU는 명령어에 들어 있는 값을 피연산자 주소로 해석하지 않고, 피연산자 주소를 얻을 수 있는 메모리 위치를 가리키는 주소로 사용한다.

이 두가지 주소 지정 모드(addressing mode)만으로도 메모리를 다루기에는 충분하다. 하지만 때로는 상수를 지정해야 할 때도 있다. 이를 위해 즉시 주소 지정 모드(immediate addressing mode) 라는 또 다른 주소 지정 모드를 추가할 수 있다. 이 경우에는 주소에 해당하는 비트를 그냥 값으로 간주한다.

분명히 직접 주소 지정은 즉시 주소 지정보다 느리다. 메모리에 두 번 접근(한 번은 명령어, 한 번은 메모리에서 데이터를 읽음)해야 하기 때문이다. 간접 주소 지정은 메모리에 세 번 접근해야 하기 때문에 더더욱 느리다.

조건 코드 명령어

조건 코드 명령어(Conditional Code Instruction)는 플래그 레지스터나 상태 레지스터와 같은 특정 레지스터의 값에 따라 명령어 실행 여부를 결정한다.

조건 코드 명령어는 기본적으로 다음과 같은 형태를 가지고 있다.

    OPCODE [OPERAND], [OPERAND], [OPERAND] ; CONDITION

여기서 OPCODE는 명령어 연산자를 나타내고, OPERAND는 명령어에 대한 피연산자이다. 마지막으로 CONDITION은 명령어 실행 여부를 결정하기 위한 조건 코드이다.

조건 코드는 여러 가지가 있으며, 아키텍처에 따라 다르게 구현될 수 있다. 대표적인 조건 코드로는 다음과 같은 것이 있다.

  • Zero flag (ZF): 연산 결과가 0인 경우
  • Sign flag (SF): 연산 결과가 음수인 경우
  • Carry flag (CF): 덧셈에서 자리 올림 발생한 경우 또는 뺄셈에서 자리 빌림 발생한 경우
  • Overflow flag (OF): 연산 결과가 부호 비트를 벗어난 경우

조건 코드 명령어는 프로그래밍에서 분기문과 반복문과 같은 조건 분기를 구현하는 데 자주 사용된다. 이를 통해 프로그램의 흐름을 제어하고 더욱 유연하게 프로그래밍할 수 있다.

분기 명령어

이제는 다양한 일을 할 수 있는 명령어가 있지만, 지금은 명령어를 처음부터 끝까지 순서대로 수행할 수밖에 없다. 이는 그다지 유용하지 않다. 의사결정을 내리고 코드 중 일부를 선택적으로 실행할 수 있는 프로그램이 있으면 정말 좋을 것이다. 이를 위해서는 프로그램 카운터(PC)의 값을 변경할 수 있는 명령어가 필요하다. 이런 명령어를 분기(branch) 명령어라고 부른다.

분기 명령어만으로는 순서대로 일련의 명령어를 실행할 수 있는 것에 비해 그다지 더 유용하지는 않다. 하지만 모든 분기 명령어가 항상 무조건 다른 주소로 분기(프로그램 카운터의 값을 바꿔서 다른 곳의 프로그램을 실행함)하는 명령어인 것은 아니다. 조건 코드를 살펴보고 어떤 조건을 만족할 때만 분기하는 명령어도 있다. 조건을 만족하지 않으면 프로그램 카운터가 평소와 마찬가지로 중가해서 분기 명령어 다음에 위치한 명령어가 실행된다.

다음은 분기 명령어의 조건이다.

image

때로는 프로그램 카운터의 내용을 명시적으로 바꿀 필요가 있다. 이를 위한 두 가지 특별한 명령어가 있다. pca는 현재 프로그램 카운터 값을 누산기에 복사하고, apc는 누산기의 값을 프로그램 카운터에 복사한다.

최종 명령어 집합 구성

앞서 말한 모든 기능을 명령어 집합에 넣으면 다음과 같다.

image

주소 지정 모드가 3가지 있다. 따라서 주소 지정 모드 선택을 위해 2비트가 필요하다. 앞에서 다룬 세 가지 주소 지정 모드 외의 네 번째 모드는 메모리와 관계없는 연산을 표현한다.

image

주소 지정 모드와 명령 코드를 디코딩하면 위와 같은 명령어를 얻을 수 있다. 분기 명령어를 명령 코드에 추가했고, 주소 지정 모드 3의 명령코드는 누산기와만 관련된 명령어를 표현하기 위해 사용된다. 다만, 이렇게 만든 완전한 구현은 앞서 본 ALU의 내용과 정확히 일치하지는 않는다.

image

왼쪽 시프트와 오른쪽 시프트는 다른 경우에 사용하지 않을 비트들을 사용해 시프트할 비트 수를 지정한다.