명령어 집합과 프로그램 실행
ISA와 구현을 구분하고 LOAD·ADD·STORE·분기 교육용 CPU를 실행하며 종료와 경계를 검사합니다.
같은 프로그램이 다른 CPU 구현에서 돌아갈 수 있는 이유는 명령의 의미를 정한 약속이 있기 때문입니다. ISA(Instruction Set Architecture)는 소프트웨어가 보는 명령·레지스터·데이터 형식 등의 약속입니다. 내부 파이프라인 길이·캐시 구성은 구현의 선택일 수 있습니다.
명령의 역할과 형식
명령은 보통 연산을 고르는 opcode와 값을 지정하는 피연산자 정보로 나뉩니다. 피연산자는 레지스터 번호·즉시값·메모리 주소·분기 목적지 등입니다. 데이터 이동, 산술·논리, 제어 이동, 시스템 제어는 서로 다른 목적을 갖습니다.
3주소형 ADD Rd,Rs1,Rs2는 입력 둘과 결과 하나를 명시합니다. 2주소형은 결과 자리를 입력과 겸하고, 1주소형 누산기 모델은 일부 피연산자를 암묵적으로 사용합니다. 0주소형 스택 모델은 스택 꼭대기 값을 사용합니다. 표기만 짧다고 실행에 필요한 정보가 없어진 것은 아닙니다.
RISC는 단순한 형식·레지스터 중심 계산 같은 설계 원칙으로, CISC는 풍부한 명령·주소 형식과 역사적 호환 요구로 설명할 수 있습니다. “RISC가 항상 빠르고 CISC가 느리다”는 결론은 나오지 않습니다. 현대 구현에는 명령 해독·내부 연산·파이프라인 등 다른 요소도 있습니다.
실제 예로 RISC-V RV32I 공식 규격은 32비트 정수 레지스터·PC·명령 형식을 정의합니다. 아래 모델은 규격 구현이 아니라 개념 학습용입니다. 특정 실제 CPU의 명령이나 예외 동작으로 오해하지 마세요.
작은 CPU 실행기
Python의 딕셔너리 {주소: 값}는 메모리, 목록은 명령, 튜플은 명령의 필드로 사용합니다. 이 모델은 정수 값을 폭 제한 없이 다루며, 주소가 없으면 오류로 처리합니다. JNZ는 R이 0이 아니면 지정 명령으로 분기합니다. 최대 단계 제한은 끝나지 않는 프로그램을 감지합니다.
def run(program, memory, limit=100):
memory = dict(memory)
pc, r = 0, 0
trace = []
for _ in range(limit):
if not 0 <= pc < len(program):
raise ValueError("명령 범위 밖 PC")
op, arg = program[pc]
current = pc
pc += 1
if op == "LOAD":
r = memory[arg]
elif op == "ADDI":
r += arg
elif op == "STORE":
memory[arg] = r
elif op == "JNZ":
if r != 0:
pc = arg
elif op == "HALT":
trace.append((current, op, r))
return memory, trace
else:
raise ValueError("알 수 없는 명령")
trace.append((current, op, r))
raise RuntimeError("단계 제한: 무한 반복 가능성")
program = [("LOAD", 20), ("ADDI", -1), ("JNZ", 1), ("STORE", 21), ("HALT", None)]
result, trace = run(program, {20: 3})
print([row[2] for row in trace])
assert result[20] == 3 and result[21] == 0
assert len(trace) == 9출력은 [3, 2, 2, 1, 1, 0, 0, 0, 0]입니다. JNZ는 R을 수정하지 않으므로 같은 값이 연속해서 보입니다. 최초 메모리 값 3을 읽고 레지스터만 감소시키며, 마지막 STORE로 결과 0을 다른 주소에 기록합니다.
독립 문제: 종료되지 않는 입력
위 프로그램에서 M[20]=0으로 시작하면 어떻게 될까요? 감소 후 R을 보고 분기한다는 순서를 이용해 설명하고, 처음 입력이 0일 때도 즉시 끝나게 할 제어 흐름을 글로 설계하세요. 테스트에는 정상 양수·0·잘못된 주소·끝나지 않는 명령열을 포함하세요.
힌트
조건 검사가 감소 이전인지 이후인지가 다릅니다. 현재 모델에는 0일 때 분기하는 명령이 없으므로 하나를 추가하거나 프로그램 시작 검사를 별도로 설계할 수 있습니다.
풀이와 오개념
0을 읽은 뒤 -1로 감소하고 계속 -2·-3으로 내려가므로 이 무제한 정수 모델에서는 0으로 돌아오지 않습니다. 단계 제한 오류가 납니다. JZ를 추가해 LOAD 다음에 0이면 STORE로 분기하고, 양수일 때만 감소 루프에 들어가는 구성이 가능합니다. 음수 입력은 별도 거부 조건이 필요합니다.
코드의 실행 여부만 확인하지 말고 종료·주소·값의 범위를 명시하세요. 다음 주소지정·호출에서 명령이 값을 찾는 여러 방법을 비교합니다.