이번 문서의 목표: 이 파일을 다 읽으면 프로세서 내부 구성·명령어 형식·명령 사이클·RISC와 CISC의 핵심을 한 번에 복습하고, 확장 오퍼레이션 코드(expanding opcode) 방식으로 명령어 비트를 배분하는 계산과 같은 연산을 CISC·RISC 방식으로 각각 처리할 때 필요한 명령어 개수 비교를 직접 풀 수 있다.
이 편에서 다시 짚어야 할 것과 새로 풀어야 할 것
프로세서 내부 구조, 명령어 형식, 명령 사이클, RISC와 CISC는 이미 독학사 2단계 컴퓨터구조에서 각각 한 편씩 깊게 다뤘다. 독학사 4단계는 이 정의들을 다시 처음부터 설명하는 대신, 정의를 알고 있다는 전제 위에서 여러 개념을 한 문제 안에 엮은 계산·비교 문제를 낸다. 그래서 이 편은 정의는 표로 짧게 요약만 하고, 나머지 대부분을 4단계 수준의 새 계산 문제 두 가지 — 확장 오퍼레이션 코드 비트 계산과 RISC·CISC 명령어 개수 비교 — 에 쓴다.
요약 복습: 프로세서 내부 구성
| 구성요소 | 역할 | 자세히 다루는 편 |
|---|---|---|
| 레지스터(register) | CPU 내부의 초고속 임시 저장 공간(PC, IR, MAR, MBR, AC, 상태 레지스터 등) | 2단계 컴퓨터구조 11편 |
| ALU(Arithmetic Logic Unit) | 산술·논리 연산을 수행하는 계산 부품 | 2단계 컴퓨터구조 11편 |
| 제어장치(Control Unit) | 레지스터·ALU에 언제 무엇을 하라고 지시하는 제어신호 발생기 | 2단계 컴퓨터구조 11편, 14편 |
요약 복습: 명령어 형식·주소지정 방식·명령 사이클
명령어는 오퍼레이션 코드(연산 종류)와 오퍼랜드(피연산자)로 나뉘고, 오퍼랜드를 해석하는 방식(즉시·직접·간접·레지스터·레지스터 간접 등 주소지정 방식)에 따라 유효주소(Effective Address, EA)와 메모리 참조 횟수가 달라진다. 자세한 유효주소 계산 예시는 2단계 컴퓨터구조 13편을 참고한다. 명령어 하나가 인출(fetch)–간접(indirect)–실행(execute)–인터럽트(interrupt) 사이클로 진행되는 과정은 12편에서 마이크로오퍼레이션 단위로 이미 다뤘다.
통합형 문항에서 자주 나오는 연결고리: 주소지정 방식이 다르면 명령어 하나를 실행하는 데 필요한 메모리 참조 횟수가 달라진다. 즉시 주소지정은 0회, 직접 주소지정은 1회, 간접 주소지정은 2회(간접 사이클 1회 + 실행 사이클 1회) 메모리를 참조한다. 이 참조 횟수는 뒤에서 다룰 캐시(09–10편)의 적중률 계산과 곧바로 이어진다 — 메모리 참조가 많을수록 캐시 적중·실패가 성능에 미치는 영향도 커진다.
계산 문제 1: 확장 오퍼레이션 코드로 명령어 비트 배분하기
쉽게 말하면: 명령어 길이는 정해져 있는데, 주소를 3개 쓰는 명령어와 주소를 1개도 안 쓰는 명령어가 같은 오퍼코드 비트 수를 나눠 가지면 낭비가 심하다. 확장 오퍼레이션 코드는 “주소를 많이 쓰는 명령어는 오퍼코드를 짧게, 주소를 적게 쓰는 명령어는 남는 자리를 오퍼코드로 더 길게 쓰자”는 절충 기법이다.
왜 이 계산이 필요한가
명령어 하나의 길이는 하드웨어 설계 단계에서 고정된다(예: 16비트). 그런데 실제 프로그램에는 레지스터 3개를 쓰는 연산(R1 = R2 + R3처럼 3-주소 명령어)도 있고, 아무 주소도 필요 없는 연산(스택에서 값을 그냥 꺼내는 것과 같은 0-주소 명령어)도 있다. 만약 모든 명령어 형식에 똑같은 오퍼코드 비트 수를 배정하면, 주소를 적게 쓰는 명령어일수록 남는 비트가 그냥 버려진다. 확장 오퍼레이션 코드(expanding opcode)는 주소 필드 개수가 다른 형식마다 오퍼코드 비트 수를 다르게 두어, 주소를 적게 쓰는 형식일수록 더 많은 오퍼코드 종류를 지원하도록 비트를 재활용하는 기법이다.
정의와 계산 규칙
- 명령어 전체 길이를 , 레지스터 주소 필드 하나의 길이를 비트라 하자.
- -주소 명령어(주소 필드를 개 쓰는 명령어)가 쓸 수 있는 오퍼코드 비트 수는 이다.
- 한 형식(예: 3-주소)에서 실제로 쓰지 않고 “예약해 둔” 오퍼코드 패턴은, 그 다음으로 주소가 하나 적은 형식(2-주소)의 오퍼코드 앞부분(접두어, prefix)으로 재사용된다. 예약된 패턴 하나마다 다음 단계에서 개의 새로운 오퍼코드 자리가 생긴다 — 그 다음 형식은 주소 필드가 하나 줄어드는 대신 그 자리(비트)만큼 오퍼코드가 늘어나기 때문이다.
예제: 16비트 명령어, 4비트 레지스터 필드(레지스터 16개)
- 명령어 전체 길이 비트
- 레지스터 필드 길이 비트(레지스터 0번부터 15번까지, 개)
- 3-주소 명령어를 3개 지원하고, 2-주소 명령어를 100개 지원하려 한다. 이때 1-주소 명령어는 최대 몇 개까지 지원할 수 있는가(0-주소 명령어는 고려하지 않는다)?
1단계: 3-주소 형식에서 쓸 수 있는 전체 오퍼코드 자리 수를 구한다. 3-주소 명령어는 레지스터 필드 3개(비트)를 쓰므로, 남는 오퍼코드 비트는 비트다.
2단계: 3-주소 명령어 3개를 배정하고 남은 자리를 구한다. 16개의 자리 중 3개를 3-주소 명령어에 배정했으므로, 나머지 개의 자리가 남는다. 이 13개의 자리는 이제 “3-주소로는 안 쓰지만, 2-주소 형식의 접두어로는 쓸 수 있는” 자리다.
3단계: 이 13개의 접두어가 2-주소 형식에서 만들어내는 전체 자리 수를 구한다. 2-주소 형식은 레지스터 필드가 2개(비트)만 필요하므로, 3-주소 단계에서 남긴 접두어 하나마다 남는 4비트(비트만큼 3-주소보다 여유가 더 생긴 것이 아니라, 정확히는 2-주소 형식 전체 오퍼코드 공간 비트 중 접두어 4비트를 뺀 나머지 4비트, 즉 가지)가 추가로 생긴다.
4단계: 2-주소 명령어 100개를 배정하고 남은 자리를 구한다. 208개의 자리 중 100개를 2-주소 명령어에 배정했으므로, 나머지 개의 자리가 1-주소 형식의 접두어로 넘어간다.
5단계: 이 108개의 접두어가 1-주소 형식에서 만들어내는 전체 자리 수, 즉 1-주소 명령어의 최댓값을 구한다. 1-주소 형식은 레지스터 필드가 1개(비트)만 필요하므로, 접두어 하나마다 다시 가지가 생긴다.
결과 해석: 이 16비트 명령어 체계에서 3-주소 명령어 3개, 2-주소 명령어 100개를 이미 배정했다면, 1-주소 명령어는 최대 1,728개까지 만들 수 있다. 이 1,728개를 전부 1-주소 명령어에 쓴다면 0-주소 명령어를 위한 자리는 하나도 남지 않는다.
| 단계 | 형식 | 오퍼코드 비트 | 전체 자리 수 | 배정한 개수 | 다음 단계로 넘어가는 접두어 수 |
|---|---|---|---|---|---|
| 1 | 3-주소 | 4비트 | 16 | 3 | 13 |
| 2 | 2-주소 | 8비트(접두어 기준 확장) | 13 × 16 = 208 | 100 | 108 |
| 3 | 1-주소 | 12비트(접두어 기준 확장) | 108 × 16 = 1728 | 최대 1728 | 0 |
시험 함정: “오퍼코드 비트 수가 형식마다 다르면 하드웨어가 명령어를 해독할 수 없다”는 진술은 틀렸다. 확장 오퍼레이션 코드는 오퍼코드의 앞부분(접두어) 패턴만 보면 “이 명령어가 몇 주소 형식인지”를 알 수 있도록 설계되므로, 제어장치는 접두어를 먼저 해독한 뒤 나머지 비트를 그 형식에 맞게 해석한다. 비트 수가 가변적인 것과 해독이 불가능한 것은 별개의 문제다.
계산 문제 2: 같은 연산을 CISC와 RISC로 처리할 때 명령어 개수 비교
쉽게 말하면: CISC는 복잡한 연산을 명령어 하나로 표현하려 하고, RISC는 단순한 명령어 여러 개를 조합해 같은 연산을 표현한다. 실제로 몇 개가 필요한지 세어 보면 그 철학 차이가 숫자로 드러난다.
RISC와 CISC의 설계 철학 비교표는 2단계 컴퓨터구조 14편에서 이미 정리했다. 이 편에서는 그 차이를 하나의 구체적인 연산에 직접 적용해 명령어 개수를 세어 본다.
예제 연산: C = A + B (A, B, C는 모두 주기억장치에 저장된 변수)
CISC 방식(메모리-메모리 연산 허용): CISC는 메모리에 있는 두 값을 ALU로 바로 가져와 연산하고, 결과를 다시 메모리에 저장하는 것까지 명령어 하나로 처리할 수 있다.
ADD C, A, B ; 메모리 A와 B를 더해 메모리 C에 저장 (명령어 1개)RISC 방식(적재/저장(Load/Store) 구조): RISC는 산술 연산을 반드시 레지스터끼리만 수행하므로, 메모리에 있는 값을 먼저 레지스터로 가져오고(load), 레지스터끼리 더한 뒤, 결과를 다시 메모리로 내보내야(store) 한다.
LOAD R1, A ; 메모리 A를 R1로 적재
LOAD R2, B ; 메모리 B를 R2로 적재
ADD R3, R1, R2 ; 레지스터끼리 덧셈
STORE C, R3 ; 결과를 메모리 C에 저장| 구분 | 필요한 명령어 개수 | 메모리 접근 횟수 |
|---|---|---|
| CISC(메모리-메모리 연산) | 1개 | 3회(A 읽기, B 읽기, C 쓰기) |
| RISC(Load/Store 구조) | 4개 | 3회(A 읽기, B 읽기, C 쓰기) — 단, 각 접근이 독립된 명령어로 분리됨 |
결과 해석: 이 예제 하나만 보면 CISC가 명령어 개수 면에서 유리해 보인다. 그러나 CISC의 ADD C, A, B 한 명령어는 내부적으로 메모리 접근 세 번을 포함한 여러 클록 주기가 필요한 복잡한 마이크로오퍼레이션 묶음으로 실행되는 반면, RISC의 각 명령어는 07편에서 다룰 파이프라인 방식으로 한 클록에 가깝게 처리하도록 설계된다. 즉 명령어 개수만으로 어느 쪽이 더 빠른지 단정할 수 없다 — 실제 실행 시간은 “명령어 개수 × 명령어당 평균 클록 수(CPI) × 클록 주기”로 정해지며, 이 계산은 07편에서 정면으로 다룬다.
시험 함정: “RISC는 명령어 개수가 적다”는 진술은 위 예제가 보여주듯 틀렸다. RISC는 명령어 “종류”가 적을 뿐, 같은 작업을 처리하는 데 필요한 명령어 “개수”는 오히려 CISC보다 많은 경우가 대부분이다. 이 함정은 14편에서도 다뤘지만, 통합형 문항에서는 이렇게 구체적인 코드 예제로 다시 물어보는 경우가 많다.
자주 틀리는 점
- 확장 오퍼레이션 코드 계산에서 “남은 자리 수”와 “다음 단계 전체 자리 수”를 혼동해, 곱하기(×16)를 빼먹고 그냥 뺄셈만으로 답을 낸다.
- CISC의 명령어 1개를 RISC의 명령어 1개와 실행 시간이 같다고 착각한다. 명령어 개수 비교와 실행 시간 비교는 서로 다른 질문이다.
- 주소지정 방식별 메모리 참조 횟수(즉시 0회, 직접 1회, 간접 2회)를 명령어 형식(몇-주소 명령어인지)과 혼동한다 — 전자는 오퍼랜드 하나를 찾는 데 드는 참조 횟수이고, 후자는 명령어 안에 오퍼랜드가 몇 개 있는지의 문제다.
핵심 정리
- 프로세서 구성(레지스터·ALU·제어장치), 명령어 형식·주소지정 방식, 명령 사이클, RISC·CISC의 정의는 2단계 컴퓨터구조에서 이미 다뤘으므로 이 편은 요약과 링크로 대신한다.
- 확장 오퍼레이션 코드는 주소 필드가 적은 명령어 형식일수록 남는 비트를 오퍼코드로 재활용해 더 많은 종류의 명령어를 지원하는 기법이며, 각 단계의 남은 자리 수에 을 곱해 다음 단계의 전체 자리 수를 구한다.
- 같은 연산도 CISC는 적은 개수의 복잡한 명령어로, RISC는 많은 개수의 단순한 명령어로 처리하며, 명령어 개수만으로 속도를 단정할 수 없다.
- 주소지정 방식에 따른 메모리 참조 횟수 차이는 이후 캐시 적중률 계산(09–10편)과 직접 연결된다.