본문 바로가기

문과 코린이의, [컴퓨터 구조] 기록/컴퓨터구조 2강

[문과 코린이의 IT기록장] 컴퓨터 구조 - 2.(1) ~ 2.(3) [서론, 하드웨어 연산, 피연산자]

반응형

 

< 2강. 명령어 : 컴퓨터 언어 >

[문과 코린이의 IT기록장] 컴퓨터 구조 - 2.(1) ~ 2.(3) [서론, 하드웨어 연산, 피연산자]



 0. 들어가기에 앞서 (진수 변환하기, 연산자 간단 이해하기)

문제 )

(1) 32476 : 2진수 변환, 8진수변환, 16진수 변환, 음수 변환하기

 

(2) 48765 : 2진수 변환, 8진수변환, 16진수 변환, 음수 변환하기


 (3) (1), (2) : & / | / ^ 계산하기


(4) 3.7647 : 이진수로 변환하기

 


 1. 서론

- 컴퓨터 하드웨어에게 일을 시키려면 하드웨어가 알아들을 수 있는 언어로 말을 해야함.

- 컴퓨터 언어에서 단어(어셈블리어)를 명령어라 하고, 그 어휘명령어 집합구조라 함.

- 이 장의 명령어 설명은 하향식(top-down)으로 이루어져 있음.

 

 

 

- 내장 프로그램 개념 * 컴퓨터의 기초 근간 개념

: 여러 종류의 데이터와 명령어메모리에 숫자로 저장할 수 있다는 개념.

 

cf )  기존 : 프로그램이 외장이엇음. (입력값을 기억하는 방식이 아니다.), 현재 : 기억장치 발전되어서 내장으로 변경


 

 2. 하드웨어 연산

- 컴퓨터는 기본적으로 산술연산을 할 수 있어야 함.

- MIPS 어셈블리 언어 산술 명령어반드시 한 종류의 연산만 지시하며, 항상 변수 세 개를 갖는 형식을 엄격히 지킨다.

 * 하드웨어를 단순하게 하자는 원칙과 부합함.

 * 이는 하드웨어 설계의 3대 원칙 중 첫 번째를 도출 가능

 

[ 설계원칙 1 ) 간단하게 하기 위해서는 규칙적인 것이 좋다. ]

ex) 네 변수의 합을 구하는 명령어 // 변수를 가변적으로 두지 않는다.

add a, b, c // b+c를 a에 넣는다.

add a, a, d // (b+c) + d를 a에 넣는다.

add a, a, e // ((b+c) + d) + e를 a에 넣는다.


< MIPS Operands >

32 registers

$s0-$s7, $t0-%t9, $zero, $a0-a3, $v0-$v1, $gp, $fp, $sp, $ra, $at

2^30 memeory words

Memort[0], Memory[4]...., Memory[4294967292]

< MIPS assembly language >

category

instruction

example

meaning

Arithmetic

add

add $s1, $s2, $s3

$s1 = $s2 + $s3

subtract

sub $s1, $s2, $s3

$s1 = $s2 - $s3

add immediate

addi $s1, $s2, 20

$s1 = $s2 + 20

Data transfer

load word

lw $s1, 20($s2)

$s1

=Memory[$s2 + 20]

store word

sw $s1, 20($s2)

Memory[$s2+20]= $s1

load half

lh $s1, 20($s2)

$s1 = Memory[$s2 +20]

load half unsigned

lhu $s1, 20($s2)

$s1 = Memory[$s2 +20]

store half

sh $s1, 20($s2)

Memory[$s2+20] = $s1

load byte

lb $s1, 20($s2)

$s1 = Memory[$s2 +20]

load byte unsigned

lbu $s1, 20($s2)

$s1 = Memory[$s2 +20]

store byte

sb $s1, 20($s2)

Memory[$s2+20] = $s1

load linked word

ll $s1, 20($s2)

$s1 = Memory[$s2 +20]

store condition.word

sc $s1, 20($s2)

Memory[$s2+20]

= $s1;$s1 = 0 or 1

load upper immed.

lui $s1, 20($s2)

$s1 = 20 * 2^16

Logical

and

and $s1, $s2, $s3

$s1 = $s2 & $s3

or

or $s1, $s2, $s3

$s1 = $s2 | $s3

nor

nor $s1, $s2, $s3

$s1 = ~($s2|$s3)

and immediate

andi $s1, $s2, 20

$s1 = $s2 & 20

or immediate

ori $s1, $s2, 20

$s1 = $s2 | 20

shift left logical

sll $s1, $s2, 10

$s1 = $s2 << 10

shift right logical

srl $s1, $s2, 10

$s1 = $s2 >> 10

Conditional branch

branch on equal

beq $s1, $s2, 25

if($s1 == $s2) go to

PC+4+100

branch on not equal

bne $s1, $s2, 25

if($s1!=$s2) go to

PC+4+100

set on less than

slt $s1, $s2, $s3

if($s2<$s3) $s1 = 1;

else $s1 = 0

set on less than

unsigned

sltu $s1, $s2, $s3

if($s2<$s3) $s1 = 1;

else $s1 = 0

set less than

immediate

slti $s1, $s2, 20

if($s2<20) $s1 = 1;

else $s1 = 0

set less than

immediate unsigned

sltiu $s1, $s2, 20

if($s2<20) $s1 = 1;

else $s1 = 0

Unconditional jump

jump

j 2500

go to 10000

jump register

jr $ra

go to $ra

jump and link

jal 2500

$ra = PC +4; go to 10000


(1) C 치환문 두 개의 번역

a = b + c

d = a - e

 

-> C컴파일러가 생성하는 MIPS 코드

add a, b, c

sub d, a, e

 

 

(2) 복잡한 C 치환문의 번역

- 변수가 f,g,h,i,j인 조금 더 복잡한 다음 C문장에 대한 컴파일러 출력은 무엇인가?

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

 

첫 MIPS 명령어는 g와 h의 합을 구하고 결과치를 어디인가에 저장해야 한다.

컴파일러는 t0라는 임시 변수를 생성한다.

add t0, g, h

add t1, I, j

sub f, t0, t1

 

[ C++과 JAVA의 비교 ]

JAVA : 인터프리터 사용 ( 더 느림 )

- 한문장씩 번역해서 컴파일함. (코드의 오류를 잡는데 더 편함)

- 이식성이 좋음 (기기에 맞게 기계어 번역)

- 자바 가상머신 프로그램으로 웹상에서도 적용

- 이 인터프리터의 명령어 집합을 JAVA 바이트코드라 함.

- JAVA컴파일러를 JIT(Just In Time)컴파일러라고 부르기도 함.


 

  3. 피연산자

- 산술 명령어의 피연산자에는 제약이 있다.

: 레지스터라고 하는 하드웨어로 직접 구현된 특수 위치 몇 곳에 있는 것만을 사용할 수 있다.

 

- MIPS 구조에서 레지스터의 크기는 32bit이며 이를 워드(word)라고 함 * 32bit = 1 word = 4byte

 

- 프로그래밍 언어에서 사용되는 변수와 하드웨어 레지스터의 큰 차이점 하나는, 레지스터는 개수가 한정되어 있다는 점이다.

 

- 현대 컴퓨터에는 MIPS처럼 보통 32개의 레지스터가 있다

 * 따라서 산술 명령어의 각 피연산자는 32개의 32비트 레지스터 중 하나여야 한다.

[ cf ) 레지스터 개수를 32개로 제한하는 이유는? ]
 (1)
설계원칙 2 : 작은 것이 더 빠르다와 관련 O
* 레지스터가 아주 많아지면 전기 신호가 더 멀리까지 전달되어야 하므로 클럭 사이클 시간이 길어짐.
* 작은공간을 한정적으로 써야, 효율적으로 사용하고 채울 수 있음.
* 그렇지만, 이는 절대적인 것은 아님. ex) 레지스터를 31개로 한다고 해서 32개보다 빨라지지는 않음.

 (2) 명령어 형식에서 레지스터가 사용하는 비트 수와 관련이 있음. 

 

- MIPS의 관례는 달러 기호 뒤에 두 글자가 따라 나오는 이름을 사용함.

 * 변수에 해당하는 레지스터를 위해서는 $s0, $s1 등을 사용

 * 프로그램을 MIPS 명령어로 컴파일하기 위해 필요한 임시 레지스터를 위해서는$t0, $t1사용


 

(1) 레지스터를 사용하여 C 치환문을 번역

ex) f = (g + h) - (i +j); 에서 컴파일러가 변수 f,g,h,i,j를 레지스터 $s0,$s1,$s2,$s3,$s4에 각각 할당했다고 하자. 컴파일된 MIPS 코드를 보여라.

add $t0, $s1, $s2

add $t1, $s3, $s4

sub $s0, $t0, $t1


(2) 메모리 피연산자

- 프로세서소량의 데이터만을 레지스터에 저장할 수 있으므로, 구조체나 배열과 같은 복잡한 자료구조는 메모리에 저장됨.

 

- 따라서 메모리와 레지스터 간에 데이터를 주고받는 명령어가 있어야 함. = 데이터 전송 명령어

 

- 메모리에 기억된 데이터 워드에 접근 하려면 명령어가 메모리 주소를 지정해야 함

 

- 정렬 제약

: 메모리 내에서 데이터는 자연스러운 경계를 지켜서 정렬되어야 한다는 요구 조건.

: MIPS에서 워드의 시작 주소는 항상 4의 배수여야 한다.

: 데이터 전송이 빨라짐.

- MIPS는 제일 왼쪽 즉 최상위 바이트 주소를 워드 주소로 사용하는 빅엔디안(big-endian)계열에 속함.


a. 적재(load)

: 메모리에서 레지스터로 데이터를 복사해 오는 데이터 전송 명령

* 적재 명령은 연산자 이름메모리에서 읽어온 값을 저장할 레지스터, 메모리 접근에 사용할 상수레지스터로 구성됨.

* 메모리 주소는 명령어의 상수 부분두 번째 레지스터 값의 합으로 구해진다.

[ 메모리 피연산자를 사용하는 치환문의 번역 ]

ex) A는 100워드 배열이고, 변수 g,h는 레지스터 $s1, $s2에 할당되었다고 가정한다. 또 배열 A의 시작주소가 $s3에 기억되어 있다고 할 때, 다음 C문장을 컴파일하라.

g = h + A[8]


풀이 )

피연산자 중 하나가 메모리에 있으므로 먼저 A[8]을 레지스터로 옮긴 후 연산을 시작해야 한다.

이 데이터는 다음 명령어가 사용할 수 있도록 임시 레지스터에 넣어야 한다.


1. lw $t0, 8($s3)

- 데이터 전송 명령어의 상수 부분 (8)변위(offset)이라 하고, 주소 계산을 위해 여기에 더해지는 레지스터($s3)베이스 레지스터라고 한다.  * 베이스 레지스터 = 인덱스 레지스터

 

2. 이제 필요한 값(즉 A[8])을 레지스터 $t0에 넣었으므로 덧셈을 수행할 수 있다.

- add $s1, $s2, $t0

cf )
- 원래 데이터 전송 명령은 레지스터가 변위를 배열의 인덱스로 저장하여 배열의 시작 주소를 갖도록 설계되었다.
- 따라서 베이스 레지스터 인덱스 레지스터라고 한다.
- 오늘날에는 메모리가 매우 크고 데이터 할당을 위한 소프트웨어 모델이 훨씬 복잡하다.
- 따라서 배열의 시작 주소가 변위 부분에 다 들어가지 않는 경우가 많으므로 레지스터에 넣는 것이 보통이다.
* MIPS는 음의 상수도 지원하기 때문에 수치 피연산자를 갖는 뺄셈을 정의할 필요가 없다.

 

- 배열이나 구조체와같은 자료구조를 메모리에 할당하는 것컴파일러의 임무임.

- 그 이후 컴파일러는 자료구조의 시작 주소를 데이터 전송 명령에 넣을 수 있음.


 b. 저장(Store)

: 적재와 반대로 레지스터에서 메모리로 데이터를 보내는 명령

* 적재, 저장명령어에서의 주소가 이진수이기 때문에, DRAM 메모리 주소가 이진수 단위로 표시됨을 알 수 있다. 즉, 기가바이트(10^9) 또는 테라바이트(10^12)가 아닌, 게비바이트(2^30), 테비바이트(2^40)으로 표시된다.

[ 적재와 저장을 사용한 번역 ]

ex) 변수 h가 레지스터 $s2에 할당되어 있으며, 배열 A의 시작 주소는 $s3에 들어 있다고 가정하자. 다음 C문장을 MIPS 어셈블리 프로그램으로 바꾸어라

A[12] = h + A[8]

 

풀이 ) 

lw $t0,32($s3)

add $t0, $s2, $t0

sw $t0, 48($s3) // 48(4*12)를 변위로, $s3를 베이스 레지스터로 사용하여 합을 A[12]에 저장한다는 것

- 컴퓨터가 갖고 있는 레지스터보다, 프로그램에서 사용하는 변수가 더 많은 경우가 있다. 그러므로 컴파일러는, 자주 사용되는 변수를 가능한 많이 레지스터에 넣고 나머지 변수는 메모리에 저장했다가 필요할 때 꺼내서 레지스터에 넣는다.

 * 레지스터 스필링 : 자주 사용하지 않는(또는 한참 후에 사용할) 변수를 메모리에 넣는 것

 

- 레지스터에 저장된 데이터메모리 데이터보다 빠르고, 사용하기도 편리함

* MIPS의 산술연산 명령은 레지스터 두 개를 읽어서 연산한 다음 결과를 레지스터에 쓴다.

* 데이터 전송 명령은 피연산자 하나를 읽거나, 쓰는 일만 할 뿐 데이터에 대한 연산은 X.

 

- 레지스터는 메모리보다 접근시간이 짧고, 처리량도 많으므로 효율적으로 사용할 필요가 있음.

 



(3) 상수 또는 수치 피연산자

- 프로그램의 연산에서 상수를 사용하는 경우는 많이 존재.

 

- 이때까지 배운 명령어만으로 상수를 사용하려면 메모리에서 상수를 읽어와야한다.

ex) 레지스터 $s3에 상수 4를 더하는 코드

lw $t0, AddrConstant4($s1) # $t0 = constant 4

add $s3, $s3, $t0 #$s3= $s3 + $t0($t0 == 4)

 

- 적재명령을 사용하지 않는 방법은 피연산자중 하나가 상수인 산술연산 명령을 제공하는 것. 이 상수를 수치 피연산자라고 함. (연산이 더 빠르고 쉽고, 에너지 덜 소모하게 됨)

ex) addi $s3, $s3, 4

 

- 상수 0 : 복사의 역할을 함

ex) $zero

* 빈도가 높으면 상수를 명령어에 포함시키도록 하는 것이 자주 생기는 일을 빠르게 하라는 좋은 아이디어의 또 다른 예가 됨


* 유의사항
- 아직 공부하고 있는 문과생 코린이가, 정리해서 남겨놓은 정리 및 필기노트입니다.
- 정확하지 않거나, 틀린 점이 있을 수 있으니, 유의해서 봐주시면 감사하겠습니다.
- 혹시 잘못된 점을 발견하셨다면, 댓글로 친절하게 남겨주시면 감사하겠습니다 :)
반응형