MIPS의 산술 연산 과정 2 - 명령어 형식, 논리 연산과 조건부 연산

 

MIPS 명령어의 인코딩

  • MIPS 명령어는 32비트의 길이로 인코딩되며, 이는 프로세서가 해석하고 실행하는 기계어로 변환된다.
  • 각 명령어는 몇 개의 필드로 나뉘며, 이러한 필드는 명령어의 동작, 사용되는 레지스터, 상수, 주소 등을 지정한다.

2. 명령어 형식

  • MIPS 명령어는 다양한 형식으로 나뉜다.
    • R-형식 (Register Format): 레지스터 간의 연산을 처리하는 명령어.
    • I-형식 (Immediate Format): 즉시 값을 포함하는 명령어.
    • J-형식 (Jump Format): 점프 명령어.

3. 레지스터 번호

  • MIPS의 각 레지스터는 고유한 번호를 가진다.
    • $t0-$t7: 레지스터 8-15.
    • $s0-$s7: 레지스터 16-23.
    • $t8-$t9: 레지스터 24-25.

 

 

 

 

1. R-형식 명령어 (Register Format)

R-형식 명령어 레지스터 간의 연산을 처리하는 명령어이다. MIPS의 산술 연산(add, sub)이나 논리 연산(and, or) 등에서 사용된다.

 

 

R-형식 명령어의 구조 (필드):

R-형식 명령어는 6개의 필드로 나뉜다:

 

 

필드 이름크기(비트)설명

op (opcode) 6비트 명령어의 종류를 나타내는 연산 코드. R-형식에서는 대부분 0으로 설정됩니다.
rs (source register) 5비트 첫 번째 소스 레지스터의 번호를 나타냅니다.
rt (target register) 5비트 두 번째 소스 레지스터의 번호를 나타냅니다.
rd (destination register) 5비트 연산 결과가 저장될 목적지 레지스터의 번호를 나타냅니다.
shamt (shift amount) 5비트 쉬프트 연산에서 사용되는 비트 수. 일반 산술 연산에서는 0으로 설정됩니다.
funct (function code) 6비트 명령어의 구체적인 기능을 정의합니다. R-형식에서는 이 필드가 실제 연산을 지정합니다.

 

 

R-형식 명령어 예시:

add $s1, $s2, $s3  # $s1 = $s2 + $s3
  • op: 000000 (R-형식 명령어이므로 고정값)
  • rs: $s2 (첫 번째 소스 레지스터, 번호 18)
  • rt: $s3 (두 번째 소스 레지스터, 번호 19)
  • rd: $s1 (결과가 저장될 목적지 레지스터, 번호 17)
  • shamt: 00000 (add 명령어에서는 쉬프트 없음)
  • funct: 100000 (add 연산을 지정하는 기능 코드)

이 명령어는 레지스터 $s2와 $s3에 있는 값을 더한 후, 그 결과를 $s1에 저장하는 작업을 수행한다.

 

 


 

 

 

 

2. I-형식 명령어 (Immediate Format)

I-형식 명령어는 즉시 값(상수)을 포함한 연산을 처리하거나, 메모리 주소를 참조하는 명령어다. 메모리 접근(load, store) 명령어와 비교 및 분기 명령어(branch) 등이 I-형식으로 표현된다.

 

 

I-형식 명령어의 구조 (필드):

I-형식 명령어는 4개의 필드로 나뉜다:

 

 

필드 이름크기(비트)설명

op (opcode) 6비트 명령어의 종류를 나타내는 연산 코드
rs (source register) 5비트 소스 레지스터의 번호를 나타냅니다.
rt (target register) 5비트 연산 결과나 데이터가 저장될 레지스터의 번호를 나타냅니다.
immediate 16비트 명령어가 사용하는 즉시 값(상수) 또는 오프셋(메모리 주소)

 

 

I-형식 명령어 예시:

addi $t0, $t1, 10  # $t0 = $t1 + 10
  • op: 001000 (addi 명령어의 opcode)
  • rs: $t1 (소스 레지스터, 번호 9)
  • rt: $t0 (목적지 레지스터, 번호 8)
  • immediate: 0000 0000 0000 1010 (즉시 값 10)

이 명령어는 레지스터 $t1에 있는 값에 10을 더한 후 그 결과를 $t0에 저장합니다.

 

 

또 다른 예로 메모리 접근 명령어 lw (load word) 명령어를 살펴보면:

lw $t0, 4($t1)  # $t0에 $t1이 가리키는 메모리 주소에서 4만큼 떨어진 위치의 값을 로드
  • op: 100011 (lw 명령어의 opcode)
  • rs: $t1 (베이스 주소를 가진 레지스터, 번호 9)
  • rt: $t0 (로드한 값을 저장할 목적지 레지스터, 번호 8)
  • immediate: 0000 0000 0000 0100 (메모리 주소 오프셋 4)

이 명령어는 $t1 레지스터에 저장된 주소에서 오프셋 4 만큼 떨어진 메모리 위치에서 32비트 데이터를 읽어와 $t0에 저장한다.

 

 

 


 

 

 

 

3. J-형식 명령어 (Jump Format)

J-형식 명령어 프로그램의 실행 흐름을 변경하는 명령어다. 주로 프로그램의 특정 위치로 점프(jump)할 때 사용된다.

 

 

J-형식 명령어의 구조 (필드):

J-형식 명령어는 2개의 필드로 나뉩니다:

 

 

필드 이름크기(비트)설명

op (opcode) 6비트 명령어의 종류를 나타내는 연산 코드
address 26비트 프로그램 내에서 점프할 주소를 지정하는 필드

 

 

J-형식 명령어 예시:

j 0x00400000  # 0x00400000 주소로 점프
  • op: 000010 (jump 명령어의 opcode)
  • address: 0000 0000 0100 0000 0000 0000 00 (점프할 대상 주소)

이 명령어는 프로그램 실행을 0x00400000 주소로 이동시킨다.

 

 

 


 

 

 

각 필드의 역할 요약

 

  1. Opcode (연산 코드):
    • 명령어의 종류를 지정한다. 예를 들어, 덧셈, 뺄셈, 메모리 접근, 분기, 점프 등 명령어가 어떤 작업을 수행할지를 결정한다.
  2. rs (Source Register):
    • 연산에 필요한 첫 번째 입력 값이 저장된 레지스터를 지정한다. 예를 들어, addi $t0, $t1, 10에서는 $t1이 rs에 해당된다.
  3. rt (Target Register):
    • 연산에 필요한 두 번째 입력 값이 저장된 레지스터이거나, 연산 결과가 저장될 목적지 레지스터로 사용된다. 예를 들어, addi $t0, $t1, 10에서 $t0가 rt에 해당된다.
  4. rd (Destination Register):
    • R-형식에서 연산 결과를 저장할 목적지 레지스터를 지정한다. 예를 들어, add $s1, $s2, $s3에서 $s1이 rd에 해당된다.
  5. shamt (Shift Amount):
    • 쉬프트 연산에서 오른쪽 또는 왼쪽으로 얼마나 비트를 이동할지를 지정한다. 일반적인 산술 연산에서는 이 값이 0으로 설정된다.
  6. funct (Function Code):
    • R-형식 명령어에서 구체적인 연산을 지정한다.

 

 

 

 

 


 

 

 

 

 

Logical Operations (논리 연산)

 

저번 Arithmetic Instructions과 Memory (Data Transfer) Instructions 에 이어서 MIPS의 논리연산이다.

 

 

 

AND, OR, XOR, NOR와 같은 연산이 어떻게 동작하는지, 그래서 어떻게 비트 조작을 하는지에 대해 알아보자.

 

 

 

 

shift 연산은 이와같은 bit 공간을 차지한다.

shamt는 얼마나 많은 position이 shift됐는지를 저장한다.

 

 

 

shift 연산 예시

 

sll $t0, $t1, 4  # $t0 = $t1를 왼쪽으로 4비트 시프트

sll (shift left logical): 비트를 왼쪽으로 이동시키고, 오른쪽에 0을 채운다.

 

 

 

srl $t0, $t1, 4  # $t0 = $t1를 오른쪽으로 4비트 시프트

srl (shift right logical): 비트를 오른쪽으로 이동시키고, 왼쪽에 0을 채운다.

 

 

 

sra $t0, $t1, 4  # $t0 = $t1를 오른쪽으로 4비트 시프트, 부호 유지

sra (shift right arithmetic): 비트를 오른쪽으로 이동시키고, 왼쪽에 부호 비트를 유지한다.

 

 

 

시프트 연산은 주로 곱셈과 나눗셈을 빠르게 수행하기 위해 사용된다.

  • 왼쪽 시프트는 값을 2의 거듭제곱으로 곱하는 것과 같고,
  • 오른쪽 시프트는 값을 2의 거듭제곱으로 나누는 것과 같다.

 

이외에 비트를 masking하는 용도로도 사용된다.

 

 

 

 

 

 

Conditional Operations (조건부 연산)

 

조건부 연산은 주어진 조건이 참일 때만 분기하거나 특정 연산을 수행하는 명령어이다.

 

 

 

beq (branch if equal): 두 레지스터의 값이 같을 때 분기

beq $t0, $t1, label  # $t0 == $t1이면 label로 분기

 

 

 

bne (branch if not equal): 두 레지스터의 값이 같지 않을 때 분기

bne $t0, $t1, label  # $t0 != $t1이면 label로 분기

 

 

 

slt (set on less than): 첫 번째 값이 두 번째 값보다 작으면 1을 저장하고, 그렇지 않으면 0을 저장

slt $t0, $t1, $t2  # $t1 < $t2면 $t0에 1 저장, 아니면 0 저장

 

 

 

 

 

예제1) if-else 분기문

 

if (i == j) 
    f = g + h;
else 
    f = g - h;

 

i, j, f, g, h는 각각 $s0, $s1, $s2, $s3, $s4에 저장된다고 가정하자.

이와 같은 c코드를 어셈블리 코드로 변환한다면

 

 

 

beq $s0, $s1, if_true  # $s0(i) == $s1(j)면 if_true로 점프

beq 명령어로 i와 j가 같은지 비교한다.

만약 같다면 f = g + h로 분기하고, 그렇지 않으면 else 블록으로 이동한다.

 

 

 

sub $s2, $s3, $s4  # f = g - h (f는 $s2, g는 $s3, h는 $s4)
j exit  # if-else 블록을 빠져나가기 위해 exit로 점프

i와 j가 같지 않으면 f = g - h를 실행하도록 else 블록을 처리한다.

 

 

 

if_true:
add $s2, $s3, $s4  # f = g + h

i와 j가 같을 때 f = g + h를 실행한다.

 

 

마지막으로 if-else 블록을 빠져나가도록 exit: 명령어 처리를 해주면

beq $s0, $s1, if_true  # i == j면 if_true로 점프
sub $s2, $s3, $s4  # f = g - h
j exit  # 블록 종료 후 exit로 점프
if_true:
add $s2, $s3, $s4  # f = g + h
exit:

 

이렇게 MIPS 어셈블리 코드로 분기문을 처리할 수 있다.

 

 

 

 

 

예시 2) while 루프 컴파일

 

while (save[i] == k) 
    i += 1;

 

이와 같은 c 코드를 어셈블리 코드로 변환해보자.

i는 $s3, k는 $s5, 그리고 배열 save의 베이스 주소$s6에 저장된다고 가정한다.

 

 

 

 

loop_start:
lw $t0, 0($s6)  # save[i] 값 로드 (베이스 주소 $s6 + i 오프셋)
bne $t0, $s5, loop_exit  # save[i] != k면 loop_exit으로 점프 (종료)
addi $s3, $s3, 1  # i += 1
j loop_start  # 다시 루프 시작점으로 돌아감
loop_exit:

 

루프의 시작점을 loop_start로 설정하고,

배열 save[i]의 값을 로드(load)한다. i는 $s3에 저장되어 있고, 배열의 베이스 주소는 $s6에 있으므로, lw 명령어로 save[i]를 가져온다.

save[i] == k를 bne 명령어로 비교한다. 같지 않으면 루프를 종료하고 아니면 루프를 다시 시작한다.