명령어 포멧별 표현 방식

한포도·2024년 3월 21일
0

Compter Architecture

목록 보기
5/8
post-custom-banner

💭메모리 피연산자

우리는 MIPS를 사용해서 메모리에 저장된 명령어와 값을 연산한다.
저번 포스팅에서 loadstore 에 대해서 포스팅했다. 말 그대로 메모리에 있던 값을 load하고 레지스터에서 메모리로 store 하는 명령어다.

👇load word

lw 명령어에서 lw $t0, 32($s3) 과 같이 $s3 에는 기본주소(베이스 레지스터)가 저장되어있다. 주소값을 참조하는것과 같다.

헷갈림 Point!
$s3 에 값을 가져오는거면 $s3에는 상수값이 들어있는거 아닌가요?

반은 맞고 반은 틀리다. 우리가 HLL로 배열을 생성할때 배우는 개념인데, 배열은 Pointer로서의 역할을 한다. 즉 A[0] = 1; 이라고 해보자. 그럼 A[0] = adress 인것이다. 저장된건 주소의 값이고 1이 들어있는게 아니다. 주소값을 참조해서 따라가보니 거기에 1이 저장된 공간으로 가는것이다.
쉽게 말해서 집주소가 적힌 종이가 배열이고 집에 들어있는 내가 1인것이다.

👆store word

sw 명령어에서 sw $t0, 48($s3) 과 같이 $s3에 저장된 베이스 레지스터를 기준으로 48바이트 떨어진 주소의 메모리에 저장한다.

📝레지스터 vs 메모리

  • 레지스터는 당연히 메모리보다 접근이 빠르다. CPU 안에 레지스터가 있기 때문에.
  • 메모리 데이터를 사용하는 것은 (lw, st) 명령을 필요로 한다.
  • 위의 이유 때문에 많은 명령어가 필요로 하게 된다.

떄문에 우리는 많이 쓰이는 데이터는 가능한 많이 레지스터 안에 있어야 하고 자주 쓰이지 않는 데이터는 메모리로 보내야 한다. 그래야 최적화가 될테니까. 우리는 이런 최적화를 Register spilling 이라고 한다.

⚙️수치 피연산자

그럼 메모리에서 데이터를 가져오지 않고 산술연산을 하기 위해서는 상수에 대한 명령어를 사용해야한다. 3이라는 숫자를 쓰기 위해 메모리에서 가져오는것은 불필요한 동작을 추가하는것과 같기 때문이다. 이런 명령을 위해 addi 라는 명령어가 존재한다.

#수치 피연산자
 #상수 연산 명령어에서 사용되는 상수 피연산자
 	addi $s3, $s3, 4
#수치 명령어에서 뺄셈은 없음
 #음수 상수 사용
 	addi $s2, $s1, -1

상수로서의 0️⃣

상수로의 0은 레지스터가 따로 존재한다. MIPS register 0 ($zero) 는 곧 0을 의미한다. 이는 덮어씌울수도 없다. 굳이 0을 추가해서 사용하기 보다 레지스터로서 활용해서 불필요한 동작을 제거한다.

add $t2, $s1, $zero 처럼 어떤 값을 옮길때 ex)$s2 -> $t0 사용할 수 있다.

⛔음수의 표현

우리가 사용한 MIPS는 결국 기계어로서 2진수로 다시 바뀌게 된다. HLL에서 MIPS로 바뀌는 컴파일이 있듯이, MIPS가 기계어로 바뀌는 컴파일이 존재한다. 그럼 MIPS는 어떻게 명령어를 기계어로 바꿀까? 앞서 말했던 명령어 포맷에 따라서 할당되는 비트가 나누어져 있다. R,I포맷은 몇비트, 상수는 몇비트. 그럼 상수를 2진수로 바꿀때 어떻게 음수를 표현할까?

2의 보수표현

여기서 등장하는 개념이 보수표현이다. Complement number인데, 보충하는 수라는 의미이다. 2의 보수를 사용한 상수 표현에서는 32비트의 2진수에서 LSB부터 31번째 비트가 수를 나타낸다. 32번째 비트는 MSB 라고 불리며 0 일때 양수, 1일때 음수를 표현하게 된다.

때문에 부호가 있고 없고에 따라서 범위가 달라진다.

  • 부호가 없는 수의 범위(unsigned) 는 n비트일때 0 to +2^n – 1
  • 부호가 있는 수의 범위(signed) 는 n 비트일때 –2^(n – 1) to + 2^(n – 1) – 1

어떤 n진수를 2의 보수로 바꾸는 방법은 따로 포스팅 하겠다. 간단하게는 2진수로 변환하고 1의 보수를 취한뒤 1을 더해주면 된다.

⏭부호 확장

더 많은 비트 수로 표현하는 것, 동일한 수를 16비트->32비트로 표현해야 한다.

부호 확장 방법

간단하다! 부호 비트를 왼쪽으로 복제하면 된다.

  • 부호없는 수 : 앞을 0으로 확장
  • 2의 보수로 음수일 경우 : 앞을 1로 전부 채우면 된다

예시: 8-bit to 16-bit

+2: 0000 0010 => 0000 0000 0000 0010

–2: 1111 1110 => 1111 1111 1111 1110

📢명령어 표현

명령어는 2진수로

기계는 0과 1밖에 모르는 바보다. 우리가 기계어에 가까운 MIPS를 사용했다 하더라도 이건 2진수가 아니다. 때문에 우리가 사용한, 명령어,상수,레지스터 이름을 다시 2진수로 바꿔주는 작업이 필요하다. 또한 비트를 나눈 위치별로 기계가 알아듣기 때문에 어떤 위치에 비트로 쪼개지는지 알아야 할것이다.

그럼 명령어가 워드(4바이트)단위 였으니까 32비트로 나눠야겠네?

맞다! 초창기에 이렇다더라~ 하고 넘어간 3개의 형식에 따라서 할당하는 비트가 다르다. 먼저 R 포맷 명령어가 어떻게 32비트로 바뀌는지 알아보자.

MIPS R-format instruction


각 비트는 위와 같이 분배된다. 여기서 사용되는 op,rs는 명령어 필드(Instruction fields)라고 한다. 이 필드들의 의미는 다음과 같다.

  • op = 연산의 종류 (R-format, I-format.. etc)
  • rs = 첫번째 소스 피연산자
  • rt = 두번째 소스 피연산자
  • rd = 목적지 레지스터
  • shamt = bit shift의 양
  • funct = 기능코드로서 op가 연산의 종류라면 fucnt는 좀더 구체적인 연산을 나타낸다.

그래서 어떻게 써먹는데..?

예시로 알아보자!

만약에 add $t0 $s1 $s2 라는 명령어가 있다고 하자. 그럼 우리는 $s1 + $s2 의 결과를 $t0 에 저장하도록 명령을 작성했다. 그럼 addfunct 가 된다.
또한 $s1 이 첫번째로 사용되는 피 연산자, 즉 첫번째 소스 이므로 rs가 되며,
$s2 가 두번째 소스로서 rt , $t0 가 저장될(목적지) 레지스터 이므로 rd 가 된다.

참고로 저 값을 나타내면 32비트인
000000100011001001000000001000002 가 되는데... 너무 기니 16진수로 변환하자.
0x02324020 가 된다. (16진수는 한개의 숫자에 4비트가 할당된다.)

MIPS I-format instruction

I-format 의 명령어는 비교적 간단하다! 왜냐하면 명령에 사용되는 레지스터가 2개기 때문이다. 또한 우리는 메모리 주소에 관여해야 하므로 주소를 표현하기 위한 비트 할당이 많은게 특징이다.

  • op = 연산의 종류
  • rs = 베이스 레지스터
  • rt = 저장할 레지스터
  • constant or adress = 오프셋이거나 상수

- 주의 -
_R포맷에서는 rd가 목적지 레지스터 였지만 I포맷에서는 rt가 목적지 레지스터값이니 주의하자. rd는 없다.

요것도 예시로 알아보자.

ex) load word

lw $s1 100($s2) 라는 명령어를 작성했다. $s2 의 주소에서 100바이트 뒤 주소를 참조, 그 메모리 주소의 값을 $s1 으로 불러오는 명령어다.

그럼 op = lw , rs = $s2 , rd = $s1 , adress = 100 가 되는것이다!!!

ex) addi

addi 는 상수 피연산자를 이용할때 쓰는 명령어다. 다시 기억하고 예시를 보자.

addi $s1 $s2 100 라는 명령어를 작성했다. $s2 + 100$s1 레지스터에 저장한다는 뜻이다. 그럼 rs$s2가 되며 rt$s1 이 된다. 앞에서는 adress 로 할당된 16비트가 여기선 상수 100을 표현하는데 할당된다. 여기서 상수의 표현범위는 MSB가 부호 표현으로 쓰이므로 -2^15~2^15-1 이 된다.

profile
응애 개발맨
post-custom-banner

0개의 댓글