MIPS 명령어의 포맷
명령어 하나는 결국 컴퓨터라는 기계가 이해하기 위해 32bit 의 0과 1의 조합으로 나타내야한다.
32bit 의 공간 안에 이 명령어가 어떤 명령어인지, 어떤 데이터를 가지고 명령어를 실행하는지를 기술한다.
이를 위해 32bit 명령어의 어디부터 어디까지 어떤 데이터를 담을지 형식을 정의해두었는데 이를 명령어 포맷이라고 한다.
MIPS 명령어는 R-format, I-format, J-format 이렇게 3가지 포맷이 존재한다.
( MIPS의 설계 원칙 중 좋은 디자인을 위해 타협이 필요하다는 원칙에 입각하여,
편의를 위해 명령어 포맷을 1가지로 통일하기보다 3가지로 구분하는 쪽으로 타협을 본 것이다.)
이번 글에서는 R-formant, I-format 2가지를 살펴보며, 그 중 산술, 논리 연산 명령어들만 정리해보려고 한다.
R - format
R - format은 위와 같은 형식을 가진다.
각각의 영역에 대해 정리하면 아래와 같다.
op ( 6 bit ) : op - code 명령어 코드를 기술한다.
rs ( 5 bit ) : register for source, 첫번째 피 연산자의 레지스터 번호를 기술한다.
rt ( 5 bit ) : register for source2, 두번째 피 연산자의 레지스터 번호를 기술한다. (s 다음이 t 라서 t 로 지었다고 한다.)
rd ( 5 bit ) : register for destination, 연산결과가 저장될 레지스터 번호를 기술한다.
shamt ( 5 bit ) : shift amount, 나중에 bit shift 연산을 수행할 때 얼만큼 shift 할지를 기술한다.
funct ( 6 bit ) : function code, op - code 와 조합하여 명령어를 해석한다.
R - format 은 레지스터 3개가 필요한 포맷으로, 레지스터에 의존적이라 레지스터 포맷 이라고도 부른다.
이 포맷의 작동 방식은 이렇게 나타낼 수 있다.
op, funct 코드는 2-input mux (multiplexer) 에 들어가 어떤 명령어를 실행할 지 선택한 뒤 ALU에 선택 결과를 보낸다.
rs, rt 는 각각 레지스터 파일의 input 포트에 들어간 뒤 해당 번호의 레지스터 데이터를 내보내 ALU에 넣는다.
rd 는 ALU에서 연산한 데이터를 저장할 레지스터의 번호를 나타낸다.
연산 결과는 write data 로서 register file 의 wd 포트로 들어가 값을 쓰게된다.
( ALU 는 Arithmetic and Logical Unit 의 약자로, 산술 논리 연산 장치라는 뜻이다.)
Arithmetic Instructions (register)
add rd, rs, rt => rd <- rs + rt
sub rd, rs, rt => rd <- rs - rt
ex) add $t0, $s1, $s2
s1 레지스터와 s2 레지스터의 값을 더해서 t0 레지스터에 저장한다.
s1, s2 레지스터는 각각 17, 18번째 레지스터이므로 rs, rt 필드에 17, 18에 해당하는 이진수가 들어간다.
t0 은 8번째 레지스터이므로 8에 해당하는 rd 필드에는 이진수값이 들어간다.
shift 명령이 아니라면 shamt 필드에는 0이 채워지고, op, funct 코드는 add 명령어에 맞게 값이 들어간다.
Logical Instructions (register)
and rd, rs, rt => rd <- rs & rt
or rd, rs, rt => rd <- rs | rt
xor rd, rs, rt => rd <- rs ^ rt
nor rd, rs, rt => rd <- not ( rs | rt )
and 연산은 masking bit 와 사용할 때 유용하다.
어떤 임의 bit 에서 일부 비트를 만 추출하고 나머지는 0으로 두고 싶을 때, 추출할 bit를 1, 버릴 bit (마스크)를 0으로 설정한 마스킹 비트와 임의 bit 를 and 연산하면, 임의 bit에서 마스킹한 bit를 제외하고 얻어낼 수 있다.
or 연산은 두 bit 필드를 합칠 때 유용하다.
연산자 중에 32bit 데이터의 상위 x bit 와 하위 32-x bit 를 조합하여 32 bit 의 온전한 데이터를 얻는 연산을 할 때 or 연산자를 활용한다.
nor 연산자는 not 논리 연산을 위해 사용된다.
A or 0 = A 이므로, A nor 0 = not A 가 되는 성질을 이용한다.
Logical Shift & Arithmetic Shift Instructions
sll rd, rt, sa => rd <- rt << sa ( rs 가 아니라 rt 를 사용한다! rs는 0으로 비워둔다.)
srl rd, rt, sa => rd <- rt >> sa
sra rd, rt, sa => rd <- rt >>> sa
sllv rd, rt, rs => rd <- rt << rs
srlv rd, rt, rs => rd <- rt >> rs
srav rd, rt, rs => rd <- rt >>> rs ( rt, rs 의 순서가 다르다! )
sll, srl, sra는 상수 값으로 bit shift 하고 싶을 때 사용한다.
뒤에 v가 붙으면 variable 레지스터 값으로 bit shift 할 때 사용한다.
상수를 사용할 때 I - format 대신 5bit sa 필드를 사용하는 이유는
register가 32bit 라서 32번이 넘는 shift는 의미가 없기 때문이다.
마찬가지로 sllv 처럼 v가 붙더라도, 해당 레지스터에 있는 값 중 하위 5bit 만 참고하여 시프트한다.
sll, srl 은 logical shift 로, unsigned 연산이다.
왼쪽으로 밀거나 오른쪽으로 밀 때, 새로 생기는 공간에 항상 0 을 채운다.
논리적으로 bit를 밀거나, unsigned 값을 대상으로 곱하거나 나눌 때 사용한다.
sra 는 arithmetic shift 로, signed 연산이다. ( srl과 구분하려고 >>> 라고 쓴다.)
오른쪽으로 밀 때, MSB에 새로 생기는 bit 는 기존 sign bit 에 맞춰 채운다.
2로 나눈뒤 floor 연산을 취하는 것과 동일한 효과이다.
floor( 5 / 2 ) = 2.5 보다 작은 정수 중 제일 큰 값인 2가 나온다.
floor( -3 / 2 ) = -1.5 보다 작은 정수 중 제일 큰 값인 -2가 나온다.
(단순히 소수점을 없앤다고 생각하면 안된다!)
high level 코드를 어셈블리로 옮길 때, signed 인지 unsigned 인지 유의해서 shift 하자.
I - format
Immediate format
피연산자 2개중 하나를 상수값 (immediate) 으로 받아들인다.
이름이 immediate 인 이유는 변수에서 다른 과정 없이 즉시 사용할 수 있기 때문이다.
I - format은 다음과 같은 형식을 가진다.
opcode ( 6 bit )
rs ( 5 bit )
rt ( 5 bit ) : 기존 R - format에서 rd의 역할을 수행한다.
immediate ( 16 bit ) : signed 값으로, - 2^15 ~ 2^15 -1 사이의 값을 가질 수 있다.
* 참고 - 2의 보수 (접은 글)
2의 보수 체계
2진수로 수를 나타내는 대표적인 3가지 방법이 있다.
1. unsigned
부호없이 모든 bit를 수를 표현하는데 사용한다. 0 ~ 2^N -1 의 값을 나타낼 수 있다.
2. Sign / Magnitude
첫번째 bit 를 부호를 나타내는데 사용하고, 나머지 bit 는 절댓값을 나타내는데 사용한다.
- 2^(N-1) - 1 ~ 2^(N-1) - 1 까지의 범위를 나타낼 수 있다.
이 방법은 0을 -0, +0 으로 구분하여 표기하므로, 표현범위가 하나 낭비된다는 문제가 있다.
또한 이렇게 수를 표현하면 음수와 양수를 더할 때 불편함이 존재하여 마지막 3번째 방법을 사용한다.
3. 2's complement
2의 보수 체계를 사용하여, 음수를 나타낼 때는 기존 양수값의 모든 bit를 뒤집고, 1을 더하여 음수를 표현한다.
음수는 MSB가 1이고, 양수는 MSB가 0이다.
Arithmetic Instructions (Immediate)
addi rt, rs, imm => rt <- rs + imm
( subi 는 없다. imm 필드에 음수값을 넣으면 되기 때문이다. (기출) )
연산시 레지스터에서 읽어온 값은 32bit 이고, immediate 필드의 값은 16bit 이다.
따라서 연산을 하려면 immediate 필드의 앞 16bit 를 임의로 채워서 32bit로 맞춰줘야 한다.
산술연산은 앞 16 bit 값을 immediate 필드의 sign 부호와 동일하게 채운다.
이를 sign-extension 이라고 한다.
Logical Instructions (Immediate)
andi rt, rs => rt <- rs & imm
ori rt, rs => rt <- rs | imm
xori rt, rs => rt <- rs ^ imm
반면 논리연산의 immediate 필드를 32bit로 맞출 때는 bit by bit 로 보는 것이므로 항상 0 으로 채운다.
이를 zero-extension 이라고 한다.
'CS > 컴퓨터 구조' 카테고리의 다른 글
[컴퓨터 구조] 6. MIPS Branch Instructions (0) | 2024.04.15 |
---|---|
[컴퓨터 구조] 5. MIPS Data Transfer Instructions (2) (0) | 2024.04.14 |
[컴퓨터 구조] 4. MIPS Data Transfer Instructions (1) (0) | 2024.04.06 |
[컴퓨터 구조] 2. Clock & Verilog (0) | 2024.04.06 |
[컴퓨터 구조] 1. MIPS Instruction & Register (0) | 2024.03.12 |