- [CS] 명령어의 구조와 명령어 주소 지정 방식2022년 12월 27일 23시 41분 05초에 업로드 된 글입니다.작성자: nickhealthy
프로그램을 개발할 때 우리는 '소스코드' 를 작성하고, 컴파일러를 통해 컴파일 되어 컴퓨터가 이해할 수 있는 '저급 언어'로 변환되어 실행된다. 저급 언어는 명령어들로 이루어져 있는데, 그럼 명령어 하나하나는 어떻게 생겼을까?
*참고: 저급언어는 기계어, 어셈블리어로 구성되어 있다. 둘 다 명령어의 집합임
명령어의 구조
사람이 누군가에게 명령을 한다면 아마 이런식으로 명령을 할 것이다. "학생들, 다음 주까지 과제를 제출하세요." 컴퓨터의 명령어 구조도 이와 마찬가지다. 컴퓨터는 '무엇을 대상으로, 무엇을 수행하라' 식의 명령어 구조를 가지고 있다.
컴퓨터는 아래의 사진과 같이 명령어 구조를 이루고 있다.
왼쪽에는 수행할 연산, 오른쪽은 연산에 사용될 데이터 혹은 연산에 사용될 데이터가 저장된 위치를 담고 있다.
즉, 명령어는 연산 코드와 오퍼랜드(아래 사진에서 오른쪽 흰색 부분)로 구성된다.
아래의 사진에서 왼쪽(빨강)은 연산 코드, 오른쪽(검정)은 오퍼랜드에 해당한다.
출처: 혼자 공부하는 컴퓨터 구조 💡오퍼랜드란?
연산에 사용될 데이터 혹은 연산에 사용될 데이터가 저장된 위치
'연산에 사용될 데이터가 저장된 위치'가 더욱 많이 저장되므로 '주소필드'라고도 불린다.
오퍼랜드의 개수는 여러 개가 될 수도 있고, 하나도 없을 수도 있다. 위의 사진의 오퍼랜드에 해당하는 부분에서 오퍼랜드가 두 개 이상인 부분은 콤마로 구분되어 있다.
대표적인 연산 코드의 종류
*참고: 연산 코드의 종류 & 생김새는 CPU 마다 다르다.
데이터 전송
- MOVE: 데이터를 옮겨라
- STORE: 메모리에 저장하라
- LOAD(FETCH): 메모리에서 CPU로 데이터를 가져와라
- PUSH: 스택에 데이터를 저장하라
- POP: 스택의 최상단 데이터를 가져와라
산술/논리 연산
- ADD / SUBTRACT / MULTIPLY / DIVIDE: 덧셈 / 뺄셈 / 곱셈 / 나눗셈을 수행하라
- INCREMENT / DECREMENT: 오퍼랜드에 1을 더하라 / 오퍼랜드에 1을 빼라
- AND / OR / NOT: AND / OR / NOT: 연산을 수행하라
- COMPARE: 두 개의 숫자 또는 TRUE / FALSE 값을 비교하라
제어 흐름 변경
*특정 메모리 주소로 실행의 순서를 옮기는 것
- JUMP: 특정 주소로 실행 순서를 옮겨라
- CONDITIONAL JUMP: 조건에 부합할 때 특정 주소로 실행 순서를 옮겨라
- HALT: 프로그램의 실행을 멈춰라
- CALL: 되돌아올 주소(리턴 주소)를 저장한 채 특정 주소로 실행 순서를 옮겨라
- RETURN: CALL을 호출할 때 저장했던 주소로 돌아가라
입출력 제어
- READ(INPUT): 특정 입출력 장치로부터 데이터를 읽어라
- WRITE(OUTPUT): 특정 입출력 장치로 데이터를 써라
- START IO: 입출력 장치를 시작하라
- TEST IO: 입출력 장치의 상태를 확인하라
명령어 주소 지정 방식
아래의 설명을 요약하자면 아래와 같다.
- 연산에 사용할 데이터의 저장된 위치를 찾는 방법
- 유효 주소를 찾는 방법
- 다양한 명령어 주소 지정 방식들이 있다.
위에서 오퍼랜드에는 연산에 사용될 데이터 보다 '연산에 사용될 데이터가 저장된 위치'가 더욱 많이 사용된다고 하여 '주소 필드'라고도 불린다고 설명하였다. 근데 왜 바로 연산에 사용될 데이터를 입력하여 연산을 수행하면 되지, 왜 데이터가 있는 주소를 넣어서 데이터를 한번 더 찾아가는 작업을 할까?
결론부터 말하자면 명령어 내에서 데이터를 표현할 수 있는 크기가 제한되어 있기 때문이다. 예를 들어 아래의 사진과 같이 명령어의 크기는 16비트라고 해보자. 이때 연산 코드에 해당하는 부분은 4비트를 차지하고 있다. 그리고 2-주소 명령어(오퍼랜드가 2개)라고 하였을 때, 각 오퍼랜드는 12비트를 나눠서 6비트씩의 공간을 사용할 수 있다. 그럼 하나의 오퍼랜드로 데이터를 표현할 수 있는 범위는 2^6(64) 이다.
명령어 주소 지정 방식(2-주소 명령어) 또 다른 예시를 살펴보자. 똑같이 명령어의 크기가 16비트라고 가정한다. 그리고 3-주소 명령어라고 가정한다. 그럼 아래의 이미지와 같이 표현될 것이다.
명령어 주소 지정 방식(3-주소 명령어) 데이터를 표현할 수 있는 범위가 2^4(16) 으로 더욱 범위가 좁아졌다. 즉, 명령어 내에서 데이터를 표현할 수 있는 크기가 너무 제한적이다. 이제 데이터가 있는 주소를(메모리) 넣어서 데이터를 찾아가보자. (아래 이미지 참고)
참고로 아래 사진에서는 메모리 한 주소마다 16비트를 표현할 수 있다고 가정되어 있다.
오퍼랜드에 데이터가 있는 주소(메모리)를 넣음 위의 사진에서 알 수 있듯이 10번지의 메모리 주소를 오퍼랜드에 넣을 시, 그 주소는 메모리 주소로서 우리가 표현할 수 있는 데이터의 범위가 훨씬 넓어졌다.(오퍼랜드에 연산할 데이터를 넣었으면 2^4 표현, 데이터가 있는 주소를 넣었을 땐 2^16까지 표현)
참고로 아래의 사진과 같이 데이터가 있는 주소를 오퍼랜드에 넣을 때, 메모리 주소 뿐 아니라 CPU의 레지스터 주소를 넣을 수도 있다.
오퍼랜드에 데이터가 있는 주소(레지스터)를 넣음 명령어 주소 지정 방식 디테일
이제 어느정도 명령어의 구조가 어떻게 생겼는지 감이 잡힌 것 같다. 그럼 조금 더 디테일하게 들어가보자!
들어가기 전에 앞서 설명한 '연산에 사용할 데이터가 저장된 주소 또는 위치' 는 조금 더 고급지게 말해서 유효주소라고 한다. 또한 위에서 설명한 대로 오퍼랜드에 담길 수 있는 유효주소는 메모리 주소, 레지스터 주소, 또는 연산에 사용할 데이터가 직접적으로 담길 수 있었다. 근데 CPU 입장에서는 유효주소에 어떤 값이 들어있든 연산에 사용될 데이터를 곧바로 찾아 수행할 수 있어야한다. 그 방식을 명령어 주소 지정 방식이라고 한다. 명령어 주소 지정 방식에는 여러가지 방법이 있는데 이는 아래와 같다.
즉시 주소 지정 방식(immediate addressing mode)
- 연산에 사용할 데이터를 오퍼랜드 필드에 직접 명시
- 가장 간단한 형태의 주소 지정 방식
- 연산에 사용할 데이터의 크기가 작아질 수 있지만 빠름
- 위에서 설명한 주소 지정 방식 중 하나이다.(사진 참고)
직접 주소 지정 방식(direct addressing mode, feat. 메모리)
- 오퍼랜드 필드에 유효주소를 직접적으로 명시(메모리, 레지스터 등 위치가 저장된 곳을 명시하는 것)
- 유효 주소를 표현할 수 있는 크기가 연산 코드만큼 줄어듦
- 위에서 설명한 주소 지정 방식 중 하나이다.(사진 참고)
간접 주소 지정 방식(indirect addressing mode, feat. 메모리)
- 오퍼랜드 필드에 유효 주소의 주소를 명시
- 앞선 주소 지정 방식들에 비해 속도가 느리다.(메모리를 타고 타고 들어가야 하기 때문에, CPU는 최대한 메모리를 덜 타야 속도가 빠르다.)
레지스터 주소 지정 방식(register addressing mode, feat. 레지스터)
- 연산에 사용할 데이터가 저장된 레지스터 명시
- 메모리에 접근하는 속도보다 레지스터에 접근하는 것이 빠름(당연함)
- 위에서 설명한 주소 지정 방식 중 하나이다.(사진 참고)
레지스터 간접 주소 지정 방식(register indirect addressing mode, feat. 레지스터+메모리)
- 연산에 사용할 데이터를 메모리에 저장
- 그 주소를 저장한 레지스터를 오퍼랜드 필드에 명시
이외 특정 레지스터를 이용해 사용할 수 있는 주소 지정 방식도 존재한다.(중요함)
이건 다음에 시간이 될 때 정리해보자!
Ref
https://www.youtube.com/watch?v=bWPHUi6BPxo&list=PLVsNizTWUw7FCS83JhC1vflK8OcLRG0Hl&index=9
다음글이 없습니다.이전글이 없습니다.댓글