1. 메모리 내의 명령어
명령어는 컴퓨터의 언어로, 컴퓨터가 이해하는 명령 체계를 의미한다. 명령어는 어휘와 공식 문법들을 따라줘야 한다. 같은 명령어 집합 구조(ISA) family는 같은 명령어를 지원하는데, 예를 들면 Intel x86 family나 ARM, MIPS, DEC Alpha 등 이 있다. 이들은 하휘 호환성을 지원한다.
※하위 호환성이란? 새 제품이 이전 제품을 염두에 두고 만들어진 제품에서 별도의 수정 없이 그대로 쓰일 수 있는 것을 의미.
다른 컴퓨터에서는 다른 명령어 집합을 가지지만 이들 사이에서도 동일한 성질들이 존재한다. 초기의 컴퓨터들은 굉장히 간단한 명령어 집합들을 가졌다. 그 이래로 ISA는 점점 복잡해지는 경향이 있었고, 이를 CISC(Complex Instruction Set Comuper)이라고 부른다. 반대로 많은 현대의 컴퓨터들은 단순한 명령어 집합들을 가지고 있다. RISC(Reduced Instruction Set Computer)이라고 부른다.
CISC
- 복잡함.
- 하나의 명령어가 여러 동작을 수행할 수 있음.
- 고성능이지만 전력 소모가 큼.
- 예) Intel Computer
RISC
- 정형화, 단순화됨
- 각 명령어의 성능은 CISC보다 떨어짐.
- 각 명령어끼리 비슷한 성능을 가짐 (효율이 높고, 파워 소모가 적다)
- 예) ARM, MIPS 등
명령어들은 숫자로 표현되어지고 그렇기 떄문에 데이터랑 구별하기 어렵다. 프로그램들은 데이터와 마찬가지로 변경 가능한 (읽거나 쓸 수 있는)메모리에 저장된다.
- 저장되는 프로그램 개념프로그램들은 이진 수로 된 파일로써 전달되어 질 수 있다. -> 이진 호환성
컴퓨터는 기존의 ISA와 호환이 된다면 기성 소프트웨어를 물려받을 수 있다.
2. Basic MIPS Instruction
C code : a = b + c
어셈블리어 : (human-friendly machine instructions)
add a, b, c #a = 목적지 / b, c = source
머신코드 : (hardware-friendly machine instructions)
00000010001100100100000000100000
예제 : 다음 C 코드를 어셈블리 코드로 바꾸면?
a = b + c + d + e;
add a, b, c
add a, a, d
add a, a, e
혹은
add a, b, c
add f, d, e
add a, a, f
명령어는 고정된 수의 피연산자를 가진다는 점에서 C와 다르게 단순하다.
한 줄의 C 언어 코드는 여러 줄의 어셈블리어로 전환될 수 있다.
어떤 순서는 다른 것보다 나은 경우가 있다. 두 번째 답은 f라는 일시적 변수를 하나 더 필요로 한다.
뺄셈 예제 :
C 언어 f = (g + h) - (i + j);
add t0, g, h
add t1, i, j
sub f, t0, t1
혹은
add f, g, h
sub f, f, i
sub f, f, j
위의 서로 다른 두 경우는 다른 결과를 가져올 수 있다. floating-point 연산이 반드시 연관되고 상호 호환적인 것은 아니기 때문이다. 이 부분은 나중에 다시 다룰 예정이다.
3. 피연산자
C 언어에서는 각 "변수"는 메모리에 위치해 있다.
하드웨어에서는(CPU입장에서는) 각 메모리에 접근하는 비용이 비싸고 어렵다. 만약 a라는 변수가 반복해서 접근된다면 변수를 on-chip scratchpad로 가져오고 scratchpad(register)에서 연산하도록 돕는다.
※scratchpad 란? 스크래치 패드, 스크래치 패드 RAM 또는 컴퓨터 용어로 로컬 저장소라고도 하는 스크래치 패드 메모리는 계산, 데이터 및 진행중인 기타 작업의 임시 저장에 사용되는 고속 내부 메모리이다.
명령어를 간단하게하기 위해서는 각 명령어(add, sub)가 레지스터에서만 연산하도록 요구한다.
C에서의 피연산자(변수)의 수는 매우 크지만 어셈블리어에서의 피연산자의 수는 고정되어 있다. 그 만큼의 스크래치패드 레지스터만이 존재할 수 있다.
4. 레지스터
- MIPS ISA는 32개의 레지스터가 있다. (인텔의 x86은 8개) 왜 32개보다 많이 하거나 적게 하지 않았을까? 많이 하게 되면 비용이 올라가기 때문에 적절한 효율성을 고려하여 32개로 설정한 것이다.
- 각 레지스터는 32비트 크기이다. (현대의 64비트 구조에서는 64비트 크기의 레지스터들을 가지고 있다.)
- 32비트(4바이트) 짜리를 하나의 워드로 칭한다. 워드는 해당 컴퓨터가 처리하는 연산의 단위이다.
- 코드를 더 읽기 쉽게 만들기 위해서 레지스터들은 $s0 - $s7 (C나 자바의 변수들), $t0 - $t9(일시적 변수들)으로 나뉘어져 있다. (해당 표기법은 MIPS에서 사용하는 표기법임)
5. 메모리 피연산자
- 명령어(add나 sub)가 연산하기 전에 값들은 메모리로부터 가져와져야 한다.
- load word : lw $t0, memory-address
- store word : sw $t0, memory-address
6. 메모리 주소
- 컴파일러는 메모리 내의 데이터를 정리한다. 컴파일러는 테이블에 저장된 모든 변수의 위치를 알고 있으며 load-store 명령어에 대한 적절한 메모리 주소를 채워넣을 수 있다.
7. Immediate Operands
- 명령어는 상수를 입력으로 요구할 수 있다.
- immediate 명령어는 레지스터 피연산자 대신에 상수를 입력으로 사용하는 명령어를 의미한다.
addi $s0, $zero, 1000
//프로그램은 base address가 있다.
//1000은 $s0에 저장되어 있음
//$zero는 항상 0을 의미하는 레지스터임
addi $s1, $s0, 0 //이건 변수 a의 주소
addi $s2, $s0, 4 //이건 변수 b의 주소
addi $s3, $s0, 8 //이건 변수 c의 주소
addi $s4, $s0, 12 //이건 변수 d[0]의 주소
8. 메모리 명령어 형식
- load 명령어의 형식:
- store 명령어의 형식:
예 : 다음 C 코드를 어셈블리어로 바꿔라.
C 코드 : d[3] = d[2] + a;
addi $s0, $zero, 1000
addi $s1, $s0, 0
addi $s2, $s0, 4
addi $s3, $s0, 8
addi $s4, $s0, 12
lw $t0, 8($s4) //d[2]가 $t0로 불러와짐
lw $t1, 0($s1) //a가 $t1으로 불러와짐
add $t0, $t0, $t1 //d[2] + a의 값을 $t0에 저장
sw $t0, 12($s4) //$t0 값을 d[3]로 내보냄
'TIL > CS 기본' 카테고리의 다른 글
2021.9.3 TIL : [네트워크] 스위칭 패킷 교환과 회선 교환 (0) | 2021.09.03 |
---|
댓글