이제는 사용하지 않는 공부방/Computer architecture

[컴퓨터구조] instruction: language of the computer

환상상상속상 2020. 9. 19. 17:00

0.

소프트웨어와 하드웨어의 인터페이스를 ISA라고 한다.

ISA는 프로세서가 인식해서 실행할 수 있는 명령어 집합이다. - 컴퓨터 구조의 일부이다.

 

따라서, 서로 다른 컴퓨터는 서로 다른 instruction set을 갖는다. - 하지만, 비슷하다.

 

예전에는 컴퓨터들이 비교적 단순한 instruction set을 가졌다. - risc vs cisc , risc가 단순해서 구현도 간단해진다는 장점을 갖는다.

즉, 하드웨어도 간단해지고 더 싸지고, 덜 복잡한 덜 실수하고 더 좋을 수 있다.

 

최근 컴퓨터는 대부분 간단한 instruction sets을 갖는다 - 99%가 RISC를 따른다.

 

1.

컴퓨터 디자이너의 목표

"하드웨어와 컴파일러를 간단히 만든다, 기능은 최대화시키고 가격,에너지는 최소화하면서...그러한 기계언어(language)를 찾는다."

= 좋은 instruction set을 만드는 게 목표다!

-요즘은 에너지에 더 집중한다 왜냐면 폰처럼 배터리가 중요하기 때문이다.

-여기서 언어(기계어)는 instruction set이다. (based on)

performance, cost, energy, easy to build hardware , complier

simple instruction set(machine language) = simple hardware,complier = less cost, less mistake

 

2.

MIPS instruction set로 공부를 할 것이야!

1980...typical of modern ISA

 

3.

overview

많은 instruction가 있는데 types와 formats에 따라서 나뉜다.

이것들에 대해서 하나하나 알아보자.

 

4.

첫번째, arithmetic operation in mips

add, subtract, 3개의 피연산자사용, 

add a,b,c  -> a = b + c

 

all arithmetic operations have this form 

 

a = b + c + d

(compile) -> add a,b,c

                     add a,a,d

 

(보통 2개를 더하지 3개를 더하지느 않기때문에 이렇게함)

 

설계규칙. 단순성은 규칙성을 선호한다.

-regularity가 구현을 간단히만든다.

-simplicity가 낮은 가격에 좋은 성능을 가능하게 만든다.

 

ex. f = (g + h) - (i + j)도 컴파일 해보자.

 

근데 이것들은 진짜 명령어는 아니다 그럼 진짜는 뭔데? 아래부터 알아보자

 

 

5.

두번째, register operations

operands는 register이여야한다!

레지스터리는 작은 메모리 공간으로 프로세서안에 있다. 매우 빠른 논리 처리 장치다.

 

mips는 32bit computer이고, 32개의 32bit 레지스터(0 ~ 31 레지스터 ex. $0,$1,$2...$31)를 가지고 있다.

레지스터는 보통 데이터 접근 자주 쓰인다. 

 

((여기서잠깐팁))

기계어와 어셈블리어의 차이? 같지만, 기계어는 bit이고 어셈블리어는 사람이 읽을 수 있게 표현되어있다.

 

레지스터도 사람이 읽을 수 있게 레지스터에 이름을 준다.

assembler names

$t for temporary values -> intermediate caculation을 위해서 쓰인다.

$s for saved variables

 

이렇게 $s, $t가 쓰여야지 진짜 다.!!

 

 

그럼 $0,$1....$31 여기서 뭐가 뭔지 어떻게 구분하지? 이름을 어떻게 알 수 있지?
책의 첫페이지에 모든 내용이 나와있어(ISA, register)...

예를 들어서 표를 보자면, 16 - 23번 레지스터는 $s0 ~ $s7이다.

(인터넷에 mips green card 검색해봅시당)

 

참고: 32bit data = 1word = 4bytes

ex. size알기

char - 1 byte (takes up 1byte)

int - 4 --> it depends on what? int is usually 4bytes( 16bit computer - 2bytes, 32bit - 4bytes, 64bit - ....4bytes)

8바이트도 가능하지만 4바이트만 해도 충분하다 이거지!! 낭비하고 싶지않다는 뜻이야 근데 항상 4바이트는 아니고 컴퓨터에 따라서 다르단ㅁ라이야  int32_t, int64_t 처럼 stdint.h로 바이트 수를 바꿀 수 있다.

(네트워크 프로그램, 임베디드 프로그램, 저수준 프로그래밍에서는 매우매우 확실하게 bits를 정해줘야해서 이런 자료형을 쓰면 매우 좋다_)

short - 2

long - 8

float - 4

double - 8

 

**두 컴퓨터의 차이점에서 가장 쭝요한 것.

32bit computer? 메모리에 얼마나 많은 메모리주소에 쓰는 가 = size of pointer(memory address)

64bit computer? 포인터의 크기가 8바이트이다.

 

**차이점(모든 자료형의 포인터의 사이즈는 같지만, 증가시켰을 때 이동하는 단위가 다르다.)

int *ptr : ptr + 1 = 4bytes after ptr (size of int)

unsigned char *ptr = ptr + 1 = 1byte after ptr (size of char)

 

6.

세번째, 메모리 operands (메인메모리 = 램)

composite(복합) data를 위해 쓰인다.

ex. array, dynamic data, structues

 

모든 변수들은 레지스터에 담길 수 없다.

- mips has only 32registers so, 다 담을 수 없다. 그리고 크기도 32비트 제한이잖어

 

그래서 보통 메인메모리에 데이터를 넣어두고, 그리고 나서 레지스터로 가져오는 방식을 택한다.

레지스터에서 계산을 하고 다시 메모리로 넣는다.

= data transfer instructions!!!!!!!!!!!!!!! 즉, arithmetic operation을 위해서는 아래 두 명령어가 필요하다.

1. load from memory to registers

2. store from registers to memory

 

근데, 메모리에 접근하기 위해서는 메모리 주소가 필요하다.

너무 너무 중요한 부분이야 설명해줄게 밑에 봐바

 

예를 들어서 32비트 컴퓨터면 32비트 메모리 주소를 갖는다.

설명을 쉽게하기 위해서 호텔이 있다고 생각해봐바... 근데 방번호가 2자리수(2digit)라고 해보자

얼마나 많은 호텔방을 가질 수 있을까? 00 ~ 99 = 100개

 

자, 그럼 얼마나 많은 메모리를 가졌느냐는 메모리 주소에 달렸다는 것이야.

 

근데 메모리는 byte addressed(8비트 즉, 1바이트에의해 주소화된다)지 즉, every byte has address

호텔에서 모든 방이 방번호가 있는 것처럼 말이야 (그림봐바)

메인메모리에서 모든 메모리에 메모리 주소가 있다. (원바이트마다..)

 

(extra note)

메모리로 생각해보자  32bit memory address!!!

32비트 컴퓨터 기준이지

 

그럼 메인메모리에서.. 첫번째 바이트의 주소는 0이지 두번째는 1 세번째는 2......n번째 바이트 주소는 n + 1

자 그럼 마지막은 ? 그림봐바 

각 공간은 1byte로 구성되어있는데 그 공간을 나타내는 주소가 2^32개가 있어

0000 0000... 0000 ~ 1111 1111... 1111

 

그럼 당연히 max size of memory는 2^32 bytes겠찌.

2^2 * 2^10 * 2^10 * 2^10  = 4GB   (1024 = 2^10)

          K          M        G

 

즉, 가장 큰 메모리가 4기가바이트다!

 

정리해보면, 32비트 메모리는 메모리의 크기가 4기가보다 작다

(장난: 컴퓨터를 좋게하려면? 메모리를 증가시킨다 8기가로..?? 실수를 한거야 ㅋㅋㅋ 32비트 cpu랑 64비트 cpu가 있다고 해보자. 32비트에 32비트 운영체제가 있을거고 64비트 운영체제가 있겠지 32비트 컴퓨터는 4기가까지밖에 안되는데  64비트 컴퓨터는 8기가까지도 되지. 그럼 32비트 cpu, 64비트 운영체제는? 불가능하지. 근데 64비트cpu,32비트운영체제는 가능해 근데 4기가 ram 이상 사용불가능하다

이유: 하드웨어에 플러그인은 가능하지만 운영체제가 사용하질 못해 메모리 주소가 32비트로 고정되어있기때문이야 한계가 있다는 말이지.

즉, how much memory you can use on your computer 메모리는 주소를 통해 접근이 가능하기 때문이지. 아무튼 좋게쓰려면 64비트를 사야지 램을 늘릴 수 있다는 말이야.

 

그리고 다음 줄보면 words are aligned in memory ( 1word = 4bytes mips에서는 기본적으로 대부분 4바이트를 따른다. )

메모리 주소는 is usually used as a multiple of 4 -> 4바이트로 사용하는 것이 간편하다. (메모리 주소는 보통 4의 배수로 사용된다. 23분)

 

예시로 실제로 보여주는데...char a, char b, int c 구조체의 사이즈는 6인데 8이 나온다.. 왜 그렇지??

이유: int,int,int는 12바이트지. 근데, 위와 같은 경우에는 그냥 4개씩 필요하다고 보면 된다. 타입조정하면 6로 만들 수 는 있다.( pack option) 

((이해를 돕기 위해: 바이트패딩을 참고하자.)  supercoding.tistory.com/37

 

바이트 패딩(Byte Padding)

바이트 패딩(Byte Padding) 바이트 패딩(Byte Padding)이란 클래스(구조체)에 바이트를 추가해 CPU 접근에 부하를 덜어주는 기법입니다. 먼저 패딩(Padding) 이라는 말에는 이런뜻이 있습니다. padding 1. (푹��

supercoding.tistory.com

(이해를 위한 설명: mips에서 워드의 시작 주소는 항상 4의 배수여야한다. 이러한 요구 사항을 alignment restriction이라고 한다.

메모리의 기본 단위를 word라고 하며, 1바이트다.)

 

메모리 instructions 예시: 

lw, sw insturction의 사용방법에 대해서 알아보는 것이다.

4개가 필요하다. operands, des register, offset, base address register

$s3 = base register

(여기서 4*8은 A배열이 word array라고 가정.)

 

lw 명령어의 경우!!! offset(32 = 4*8) + $3 -> go to memory address put it back to $t0, 여기서 계속 계산하고 메모리에 갔다가 레지스터로 돌아오지. 즉, 메로리의 내용을 레지스터로 load한 것이다.

sw 명령어의 경우!!! &A[12] = 48 + $s3를 통해서 메모리에 접근한다. 그리고 나서 $t0레지스터의 내용을 그 메모리에 할당한다.

즉, 지금 레지스터의 내용을 메모리에 store한 것이다.

 

(예시2에서 $s3를 계속쓰는 이유는 A의 베이스주소이기 때문이야 여기에 offset만 더한다면 A[12]랑 A[8] 사용가능.)

 

또 다시 언급할 것 mips는 big endian!

multi 바이트(int 4바이트 short 2바이트...) 데이터를 메모리에 어떻게 넣을 것인가?

예시를 들어줄게.. 

 

int a = 10;

0000 0000 ..... 0000 1010 = 10 -> 인간이 적는 방식이지, 오른쪽에 의미를 주지

most siginificant bit, least significant bit

컴퓨터는 바이트 단위로 한다. one byte is basic unit 

여기에 most siginificant byte, least siginificant byte

((이해를 돕기 위해서: 이 때, 이 2진수의 여러 비트들 중 가장 큰 값에 해당하는 비트, 즉 가장 왼쪽의 비트를 MSB(Most Significant Bit)라고 부릅니다. 반면, 가장 작은 값이자 가장 오른쪽의 비트는 LSB(Least Significant Bit)라고 부릅니다. "중요한"이라는 의미의 Significant 단어가 사용되는 이유는 해당 비트가 숫자 값의 크기에 얼마나 큰 영향을 미치는지를 나타내기 때문입니다.))

참고: www.hackerschool.org/Sub_Html/HS_University/HardwareHacking/24.html

 

https://www.hackerschool.org/Sub_Html/HS_University/HardwareHacking/24.html

 

www.hackerschool.org

 

그럼 다시 처음으로 돌아가서 어떻게 메모리에 넣을래?

그림을 보자. ( 0000은 0으로 요약 )

인간의 경우에는, 0 0 0 10 (big endian)또는 10 0 0 0 (little endian)인 두가지 방식이 있다. 

무엇이 자연스러운가? byte ordering에 관한 문제다.

 

빅엔디안은 higher address에 숫자를 넣는다.

워드에서 가장 중요한 바이트(msb)가 가장 낮은 주소(at lower side address)에 있다.

여기서 least와 low는 같은 의미로 쓰인다.

 

즉, 빅엔디안은 최상위바이트(msb)를 차례로 저장하는 방식, 리틀엔디안은 최하위비트(lsb)를 차례로 저장하는 방식이다 그니깐 이 컴퓨터는 lsb부터 나오므로 리틀엔디안이다.

참고: genesis8.tistory.com/37

 

리틀 엔디안 VS 빅 엔디안

먼저 둘을 비교하기에 앞서 엔디언이란 무엇인가? 엔디언(Endianness)은 컴퓨터의 메모리와 같은 1차원의 공간에 여러 개의 연속된 대상을 배열하는 방법을 뜻하며, 바이트를 배열하는 방법을 특히

genesis8.tistory.com

 

pa[0] = 10, pa[1] = 0, pa[2] = 0 ... 이것은 빅일까 리틀일까?  리틀엔디안이다 lsb가 먼저 나오니깐

 

정리하자면, 어떤 엔디안인지에 따라서 메모리에 데이터를 넣는 방식이 달라진다.

빅엔디안은 주로 사람이 사용하는 방식이다. 네트워킹 등을 한다면 이것을 반드시 알아야한다. (byte ordering on the memory)

예를 들어서 중요한 것을 먼저 강조하고 싶을때 리틀엔디안 방식을 택한다 왜냐하면 중요한 것을 먼저 강조하기 때문이다.

 

근데 컴퓨터끼리 네트워크하는 예시에서... a는 리틀, b는 빅이면 10 0 0 0을 넘겨주면 값이 다르게 나오겠지.. 뭐 10을 보냈는데 10*2^24로 알아들을테니깐말이야 메모리를 읽는 방식이 다르니깐 생기는 일이야. - networking problem

그래서 ! network byte order = how to sent mult-byte data over network == big endian order 문제를 해결하기 위해서 이렇게 규약을 맺었다. 리틀인 경우에는 convert하여 네트워킹을 한다.

보라색 convertion: order을 바꾸어 빅도 알아듣게만든다.

하늘색 convertion: network api을 사용한다(?) what the fuck (ex. htos, ntohs...) api들이 convertion을 해준다.

 

network api만 쓰면, 컴파일러가 알아서 처리해준다

(이해를 돕기 위해: 내 컴퓨터가 빅엔디안 방식을 쓴다 하더라도 반드시 변환 함수를 사용해 네트워크 바이트 순서로

   

변환하는 과정을 거쳐야 합니다. 실질적으로 바뀌지는 않지만 이식성을 좋게 하기 위해서죠.

   

내가 만든 프로그램이 빅엔디안 방식을 사용하는 컴퓨터와 리틀엔디안을 사용하는 컴퓨터중

   

어느 컴퓨터에서 실행될지 모르기 때문입니다.)

 

참고: mintnlatte.tistory.com/52

 

네트워크 바이트 순서 (Byte Ordering)

■ 시스템이 내부적으로 데이터를 표현하는 방법 (1) 바이트 순서 : 바이트 순서는 데이터가 바이트 단위로 메모리에 저장되는 순서를 의미한다. - 크게 Big Endian과 Little Endian 방식이 존재하며, ��

mintnlatte.tistory.com

마지막으로 register vs memory

32개의 레지스터리밖에없어서 메모리도 써야한다.

사진 참고 걍 정리하는 거임.

more instructions to be exected -> 57분 메모리에 갔따가 레지서틸에 가갔다가 계산학 등등 3개의 명령이 필여핟.

컴파일러는 변수를 위해서 레지스터를 사용해야한다! 왜? 빠르니깐

많은 변수가 있을때 자주쓰는것을 레지스터에 자주 안쓰면 메모리에 넣어서 빠르게 사용한다.

-> 컴파일러가 결정함.(register optimization)

 

3주차 2강 끝

 

3주차 3강 시작

 

7.

immediate operands

addi =  i가 immediate를 의미한다. ( no subi  -> just addi -4 그냥 음수를 붙여서 더한다. 더 간단하게 만들기 위해서!, add로 sub를 대신할 수 있어서 설계하기 더 쉽다. )

4 = immediate value

small constants(1,2,3,-1,-100 etc...) 자주 사용하기때문에 빠르게 사용하도록 해준다.

immediate operand는 메모리에 넣는 것을 피한다.

 

정리. instruction을 만들지 않는다 다른 Instruction으로 대체 가능하다면. 예시 빼기,

매우 자주 쓴다면 instrucion을 만들어서 더 빠르게 만든다.

 

mips register 0 = $0 = 0(value) 매우 자주 사용하기때문에 가지고 있다.

예를 들어서, 레지스터끼리 옮기기 위해서는 move instruction을 만들지 않고 0과 옮길 숫자를 add instruction을 거친다.

 

8.

Numbers(unsigned binary integers)

8bit unsigned int ( 8 digits )

00001010 = 10

 

공식은 우리가 알고 있는 그 방법과 같다. 1 -> 2 -> 4 -> 8 ....(0 to 2^n - 1)

 

가장 작은 것은 ... 0000 0000 ... 0000 = 0

가장 큰 것은 1111 ... 1111 = 2^n - 1

 

8비트의 경우 2^8 - 1

32비트의 경우 2^32 - 1

16비트의 경우 0 - 65535(=2^16 - 1) (외어라!)

2^7 = 128, 2^15 = 32768(외어라!)

 

Q. uint8_t a = 255;

a = a + 1;

 

what is the value of a now? a = 0;

 

 

그럼 a = 250; a= a + 10; a = ? a = 4다.

a = 200, a = a + 100? a = 44

왜??  내 생각 오버플로우가 발생하므로 다시 돌아가는거야

 

교수님의 설명! integer wrapping을 알아야함

unsignde 8bit integer : 0 - 255

 

예시 , a = 0; while(1) a = a+ 1; 그러면 a가 계속 증가할거야 255까지

근데 만약에 거기서 1을 더 더하면? 한계에서 하나 더 더하면!!!? 

-> a = 255; a = 11111111(2진수) 여기에 1을 더하는 것과 같잖아.

즉 1 0000 0000 그런데 8비트니깐 맨 위의 1이 사라지는 거지.

즉, 0000 0000만 남게 되는거지 그래서 0가 된다.

 

만약 더하면 다시 올라가겠지 옆의 그래프처럼 ㅇㅋ? ㅇㅋ

 

또 다른 예시를 보자 a = 0 while(1) a = a - 1이면...

a = 0000 0000 에서 1을 빼면 ~ (1) 1111 1111 이렇게 되니깐 다시 255로 돌아가게 되는거지

그니깐 비트연산을 통해서 이동한다고 보면 된다. 그래프를 참고해보면 계속 반복된다.

 

그래서 a = 200; a = a+ 100을 하면 옆의 그림의 녹색부분처럼 되는 거지! 300 - 256(0 - 255) = 44

a = 100, a = a + 500 then 그림으 노란색 부분처럼 되는 거지! 600 - k*256 = 88

 

그래서 uint8_t 인 경우 옆의 그림으로 wrap된다.

한계가 있어서 더 크거나 작은 번호를 나타낼 수가 없기 때문이야

 

그럼 8 bit unsigned int는 옆의 그림처럼 될텐데...

만약에 16bit unsigned int가 된다면..? 노란색 그림처럼 뙬거야 한계가 더 커지겠지 그리고 더 높아질거야 하지만 패턴은 그대로 유지된다.65525

uint16_t b = 65530 and + 10? then (65530 + 10) - k*65536(0 - 65535) = 4

 

이것을 integer wrapping라고 한다.

 

negative도 생각해보자

8bit signed int의 경우를 살펴봅시다.

 

결국 같다 단지 음의 영역으로 가게 된다.

 

16bit signed int도 그림을 보자.

 

9.

그럼 이제 부터 negative number을 표현하는 지 알아보자@

지금까지 unsigned만 봤잖아!!

 

3가지 방법이 있어.

1.sign & magnitude

2.1's complement

3.2's complement

 

예시로 8비트 variable을 생각해보자

1번?

첫번째 부호비트, 나머지는 크기비트( 0 - 2^7 - 1) 127 즉 -127 - 127

하지만..........

사용하지 않아!

 

왜??????????????????????????????????

1000 0000 = -0 이건 쓸 필요가 없자나...

-5 =  10000101

5 = 00000101

둘을 더해보면 0이나와야하는데 10001010  = -10이 나오는 대참사발생

그래서 나온 게 1의 보수

 

2번?

flip all bits for negation(음수)

 

5 = 0000 1010

-5 = 1111 0101

하지만 이것도 1111 1111 = -0 문제가 된다

그리고 또 더해도 0이 안된단 말이야... -0가 돼!

 

그래서 나온게

 

3번

2의 보수법! 알쥐? 알쥐

 

0000 0101 = 5

1111 1101 = -5

 

더하면 (1) 0000 0000 carry는 사라진다.

= 0 fuck we got what we want!

 

1111 11111 + 1 = 0000 0000 ->>>>>>  -0 = 0

 

두 문제를 해결했다

3번 방법을 이용하여 음수를 표현한다.

 

그래서 빨강색 네모친 부분이 unsigned number을 표현했다. 

 

지금은 2's complement signed integers는 물결표부분이다.

(n-1)부분은 unsigned int와 같다...same rule! 

다른 점은 첫부분이다!!!

 

예시, 8비트 부호화 정수는 옆 그림처럼 첫부분만 달리진다.

-2^7 2^6 2^5 2^4 2^3 2^2 2^1 2^0->( -128 ~ 127 )

 

모든 n bit signed bit에 적용된다.

 

32비트를 예시로 든 것이 옆 그림이다.

-2^7보다 큰 것이 없어서 top bit가 1이되면 무조건 음수다

-2^(n-1)  ~   +(2^n - 1)

양수라면, top bit = 0이고 나머지는 unsigned Rule과 같다.

 

 

범위는 외어야한다.

 

16비트인 경우.. -32768 ~ 32767 = -2^15 ~ 2^15 - 1

8비트 ,32비트도 옆의 그림을 보고 외운다.

 

3주차 3강 끝

 

3주차 4강 시작

10.

2의 보수

 

all zero = 0

all ones = -1

most negative = 1000 ...0000

most positive = 0111...1111

 

당연하지 top bit가 -2^(n-1)이니깐!!!!

 

signed negation = 1's complement and add 1

 

negate + 10

0000 ...0 0000 1010 -> 2의보수 -> 1111 ... 1 1111 0110 = -10

        24개        총 32개

 

 그림 보고 공식보셈

 

11.

sign extension

숫자를 더 많은 비트를 사용하여 표현하자( 숫자의 값을 유지한다)

 

예시, char a = -5; -> 8bit signed integer => 1111 1011

int b = a; -> 32bit signed integer 

 

b에서 무슨 일이 생기지?? 메모리에는 "확장"이 일어난다.

 

1111 1111 1111 11111 1111 1111 [ 1111 1011 ]

 

2도 -2도 마찬가지다 옆에 그림을 보자

이것이 preserve the same numeric value = sign extension

 

addi, lb,beq는 나중에 배울것이다.

위의 내용을 이용한다. 예를 들어서 16비트 숫자를 가지고 32비트 operation을 하려면 sign extension이 필요하다

 

12.

hexadecimal

4 bits per hex digit

 

why? 다 적기가 너무 귀찮아서 4개를 1개로 줄여서 compact representation을 만는거야

 

예시 eca8 6420 교과서봐라

 

0xeca86420 표현법은 3가지 있따 옆에 그림봐라

 

 

코드보면서 봐보자

127 -> -128이 된다. integer wrapping

 

200에 200더한거랑 500뺀거

내 예상은 .. char면 8비트 최대가 -128 - 127이니깐 244가 되고

기함수기때문에 -(300 - k*256) x축 양을 기준으로 구하고 음수를 붙인다 그니깐 -44

 

진짜 결과는 -56 -112 -44이다. 그림을 그려서 해결하는 편이 낫겠다.

그냥 암기 (내가원하는수) - k * (2^n)

 

8비트라면 200 - K*2^8 = 200 - 1*256 = -56

음의 경우에는 빼주고 음수취해주기 원점으로 옮긴다고 생각하자.

 

%d, %u 같은 숫자를 signed , unsigned로 하냐에 따라서 다르게나왔다.

비트를 니가 어떻게 읽느냐에 따라서 값이 달라진다. 니가 어떻게 쓸거냐에 따라서 달리진다.

 

lsb - little

 

msb - big

2의보수법을 사용하여 빅엔디안을 사용한방법이다.

 

 

3주차 끄읏-   ~_~