[System Programming] CPU Instruction Cycle 예시로 이해하기

Geon·2023년 5월 19일
0

CS

목록 보기
7/9
post-thumbnail

중간고사 기간을 지나고 기말고사 기간에 접어들었다. 현재 이미 함수 호출, 반복문 등 복잡한 어셈블리 코드를 설계하고 이것을 번역하는 컴파일러, 컴파일러가 번역한 기계어를 실행하는 CPU와 메모리까지 설계하고 실제 코드로 구현까지 마쳤지만 (또 2등이다 만년 2등..1등 언제 해보냐😂) CPU의 동작 방식을 처음 접했던 당시에는 이 과정이 잘 이해되지 않았어서 개인적으로 흐름을 이해하기 위해 그림을 그려가며 공부했는데, 이를 포스팅해보려 한다.

지난 포스팅에서 CPU가 명령어를 어떻게 해석하고 실행하는지에 대한 원리를 살펴봤다면, 이번에는 내가 임의로 만든 코드를 어떻게 해석하는지 알아보겠다. 우선 아래와 같은 임의의 명령어 셋을 정의했다.

Instruction Set


이번 예시에 사용할 명령어들
LDA x : AC에 x주소에 저장되어있는 값을 적재
STA x : 현재 AC 값을 x주소에 저장
ADD x : 현재 AC 값 = 현재 AC값 + x주소에 저장되어있는 값
JMP c : PC값을 c로 변경

이번 예시에 사용할 명령코드
Int y = 16;
Int x = 8;
0: LDA x;
1: ADD y;
2: STA y;
3: JMP 2;

아래는 CPU가 이 프로그램을 메모리에 올리고 실행하여 0번 라인부터 읽어가는 상황이다.

LDA x;

왼쪽 영역의 회색 박스가 CPU, 오른쪽 영역의 노란색, 파란색 박스가 Memory이다. 빨간색으로 강조한 부분들을 중점으로 살펴보면 이해가 쉽다!

Fetch

우선 Int y = 16;, Int x = 8; 는 각각 8001, 8002 번지에 미리 저장되어 있다고 가정하고 0번 라인 (LDA x)부터 코드를 읽어들이면서 CPU가 실행하는 상황으로 가정한다(본래 이런 전역변수들은 프로그램 실행 전 Data Segment에 주소값으로 변환되어 저장되는데, 이번 시간엔 흐름을 파악하는것이 우선이니 생략)

PC값은 0부터 시작, 코드 세그먼트가 4000번지 부터 시작한다고 치면 실제로 MAR에 담을 0번 라인의 주소는 PC(0) + CS(4000) = 4000이 된다. '즉 CPU는 지금 0번 라인(메모리에서 4000번지)을 읽겠다'가 되는것이다.

--------------------------------------------------------


현재 MAR은 0x4000 주소를 가르키고 있고, MBR에는 MAR이 가르키고 있는 주소의 값을 적재해야 한다.
'LDA x' 라는 instruction에서
LDA는 16진수로 0x01, x는 상대주소가 0x02라고 하면(실제 절대주소는 Data Segment가 8000번지부터 실행한다고 가정하면 상대주소(2)+Data Segment 주소(8000) = 8002), 이 insturction을 컴파일러가 기계어로 변역하면 0x0102가 될 것이다. 따라서 0x0102를 MBR에 적재
--------------------------------------------------------


MAR 주소에 있는 instruction을 MBR에 옮겼으면 이를 명령을 해석하는 IR에 옮긴다. 여기까지가 Fetch 단계이다.

Decode

앞에 두자리는 명령 정보를 담고있는 Opcode, 뒤의 두자리는 명령의 대상이 되는(연산자) Operand or Address이다. CPU는 IR에서 이 두개를 분리시켜 어떤 명령을 어떻게 수행해야 할지 판단한다. 해석 결과는 Opcode는 0x01이니 LDA, Operand는 0x02니까 절대주소는 0x8002 즉 '8002번지의 값을 AC에 적재해라'라는 명령임을 알게 된다.

Execute

Decode 단계에서 8002번지의 값을 AC에 적재하라는 명령임을 파악했으니 우선 8002번지에 저장되어있는 값을 가져와야 할 것이다. 그렇게 하기 위해 주소를 담는 MAR에 0x8002를 올린다.

--------------------------------------------------------

MAR이 0x8002라는 주소를 가르키고 있으니 MBR에는 그 주소에 해당하는 값(8)을 가져온다. 즉 x에 8이 저장되어 있는 상황에 LDA x를 해서 x를 AC에 로드하라는 명령이었으니 8을 AC에 로드하는 과정을 진행하고 있는것이다.

--------------------------------------------------------

마침내 MBR의 값을 AC에 로드하면서 이 instruction의 수행은 완료된다.

--------------------------------------------------------

해당 insturction의 수행이 완료되었으면 PC값을 1증가시킨다. 그럼 다음번에 CPU가 읽어들일 코드 라인은 PC값(1)+Code Segment 주소(8000) = 8001이 되겠지? 이렇게 한번의 Instruction Cycle이 완료되는 것이다. PC값을 Fetch단계에서 올리고 시작하는 방식도 있고, 위의 예시처럼 Fetch-Decode-Execute까지 완료하고나서 올리는 방식도 있으니 참고하자.

ADD y;

이전에 실행한 LDA x instruction에 의해 현재 AC값은 x주소에 저장된 값인 8이 올라와있는 상태이다. ADD y명령은 AC값에 y주소에 저장된 값인 16을 더하여 AC값을 업데이트 하는것이니 이 명령의 결과로 AC값은 24가 되겠지? 흐름은 비슷하니 지금부터는 웬만하면 그림으로만 설명하겠다.

Fetch


Decode

Execute



STA y;

이전에 실행한 ADD y instruction에 의해 현재 AC값은 24가 저장된 상태고, STA y는 현재 AC의 값 24를 y주소에 저장하는것이다. 즉 이 명령을 실행하고 나면 y = 24가 될 것이다.

Fetch


Decode

Execute


y의 주소인 8001에 AC의 값을 저장한다.

명령 수행 완료 후 PC = PC +1

JMP 0;

PC값을 0으로 세팅하라는 명령이다.

Fetch


Decode

Opcode = 0x05 → JMP → 'PC값을 바꾸는 명령이구나'
Operand = 0x00 → '0으로 바꾸면 되는구나'

Execute

PC = 0

이렇게 PC값이 0이되면 CPU는 다음번 실행할 instruction을 다시 0번 라인인 LDA x부터 읽어들이게 되고, 이 흐름이 계속 반복되어서 y값은 24+8이 되어 32가 되고 다시 JMP 0을 해서 32+8...이런식으로 y값이 계속해서 증가하는 무한 반복문이 작동할 것이다. 이러한 반복을 빠져나가기위해 어셈블리어에선 label이나, BZ(AC의 값이 0보다 작으면 jump),EQZ(AC의 값이 0이면 jump)같은 명령어들로 흐름을 제어하기도 하는데, 이번 시간에서는 여기까지 알아보도록 하겠다.

profile
별에 별 지식 저장해놓고 꺼내먹기📚

0개의 댓글