책/컴퓨터 구조

컴퓨터 구조 Ch02_"Language of the Computer-2"

RyoTTa 2020. 11. 19. 23:00
반응형

2.6 논리연산 명령어

 워드 전체가 아닌 개개 비트에 대한 연산을 논리연산이라 칭한다.

 

Shift 연산

  자리이동 연산, 더블워드 내의 모든 비트를 왼쪽 또는 오른쪽으로 이동시키고, 이동후 빈자리는 0으로 채운다.

  Shift left      <<      LSL

  Shift right     >>     LSR

  Bit AND        &     AND, ANDI

   ''  OR          \      OR, ORI

   '' NOT         ~      EOR,EORI

 

 ex) 000.... 00001001 (2) -> LSL 4번 >000..... 10010000(2)

 

 R-type 명령어에서 shamt 의 뜻이 shift mount를 나타낸다. 

 ex)LSL X11, X19 #4  // reg X11 = reg X9 << 4 bits

 

 LSL = 2^i를 곱하는 것과 같은 연산

 AND = 비트 마스크로 사용

 EOR = Exclusive OR(두 비트가 같을때 0), 따라서 NOT = EOR 111....111x

 

2.7 판단을 위한 명령어

 일반적인 고급언어에서의 if문장으로 판단을 내린다.

 

 조건부 분기

 CBZ register, L1

  register의 값이 0라면 L1에 해당하는 문장으로 간다.

 CBNZ register, L1

  register의 값이 0이 아니라면 L1에 해당하는 문장으로 간다.

 

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

  SUB X9, X22, X23 // i와 j의 차의 값 확인

  CBNZ X9, Else // 0이 아니라면 Else로 이동

  ADD X19, X20, X21 // 위의 CBNZ에서 Else로 가지 않았다면(if 참) 실행할 코드

  B Exit // 무조건 분기 LEGv8 에서 조건부 분기와 구분하기위해 B(Branch)라는 이름을 붙이고 사용한다

  Else : SUB X19, X20, X21 // 조건부 분기에서 이동하는 Else 부분

  Exit: // 무조건 분기에서 이동하는 Exit 부분

 

 순환문

 일반적인 고급언어에서 while문장으로 반복을 한다.

 

 ex) while(save[i] == k)

           i += 1

  Loop: LSL X10, X22, #3) // save[i]를 레지스터로 적재하기위해 해당 값에 8을 곱한다 (<< 3), Temp reg X10 = i  * 8

  ADD X10, X10, X25 // X10값에 X25를 더하여 save[i]에 해당하는 주소값을 계산한다.

  LDUR X9, [X10, #0] // X10주소에 대한 값을 X9에 저장한다.

  SUB X11, X9, X24 // save[i]에서 k를 빼고 그 차이를 X11에 넣는다.

  CBNZ X11, Exit // save[i] != k 라면 Exit로 조건부 분기 이동

  ADDI X22, X22, #1 // i에 1을 더한다.

  B Loop // while의 끝부분 무조건 분기에서 이동하는 LOOP 부분(while 조건 검사부분으로 이동)

  Exit: // while문 검사부분에서 참일경우 무조건 분기에서 이동하는 Exit부분

 

 예를들어 for문장 같은 경우는 두 수의 대소 비교가 필요하다.

 컴퓨터 설계자들은 명령어 실행 중에 일어났던 일을 기록하는 비트 4개만 추가로 사용해 처리했다.

 이를위해 4비트를 추가해 컨디션 코드 및 Flag라 칭한다.

 음수(N) - 연산 결과의 MSB 비트가 1이면 1이된다.

 제로(Z) -  연산 결과가 0이면 1이 된다.

 오버플로(V) - 연산 결과에 오버플로가 발생하면 1이 된다.

 올림수(C) - 연산 결과의 MSB에서 발생한 캐리가 1이거나 MSB로 버로우가 발생하면 1이 된다.

 

 Case/Switch 문장

 연속적인 If-then-else문으로의 변환보다, 여러 코드의 시작 주소를 표로 만들면 더 효율적으로 구현할 수있다.

 이때 프로그램은 분기 주소 테이블, 분기 테이블의 인덱스만 계산해서 해당 루틴으로 분기할 수있다.

 분기 테이블은 프로그램상의 레이블에 해당하는 주소를 저장하고 있는 배열이다. 즉 분기테이블의 적당한 주소를 

  레지스터에 적재한 후 레지스터의 주소를 사용하여 분기한다. 

 이를 위해 LEGv8 에는 이런 상황을 다루기 위해 BR이라는 명령어를 가지고있다.

 

2.8 하드웨어의 프로시저 지원

 프로시저나 함수는 이해하기 쉽고 재사용이 쉽게 하도록 프로그램을 구조화하는 방법 중 하나이다.

 인수(Parameter)는 프로시저에 값을 보내고 결과를 받아온다. 

 

 프로시저의 여섯단계

  1. 프로시저가 접근할 수 있는 곳에 인수를 넣는다.

  2. 프로시저로 제어를 넘긴다.

  3. 프로시저가 필요로 하는 메모리 자원을 획득한다.

  4. 필요한 작업을 수행한다.

  5. 호출한 프로그램이 접근할 수 있는 장소에 결과값을 넣는다.

  6. 프로시저는 프로그램 내의 여러 곳에서 호출될 수 있으므로 원래 위치로 제어를 돌려준다.

  

 보통 X0-X7 레지스터는 파라미터를 위한 인수 레지스터 8개

 LR(X30) 레지스터는 호출한 곳으로 되돌아가기 위한 복귀 주소를 가지고 있다.

 BL(Branch-and-link) 명령어 : 지정된 주소로 분기하면서 동시에 다음 명령어 주소(복귀 주소)를 LR 레지스터에 저장

 따라서 최종적으로 복귀할때에는 BR LR 형식으로 복귀를 하게된다.

 

 호출 프로그램은 X0-X7에 전달한 인수값을 넣은 후 BL X를 통해 프로시저 X(피호출 프로그램)로 분기한다.

 피호출 프로그램은 계산을 끝낸 후 결과를 같은 인수 레지스터에 넣은후 BR LR 명령을 실행하여 복귀한다.

 

 더많은 레지스터의 사용

 인수 레지스터 8개만으로 부족, 호출 프로그램이 사용하는 모든 레지스터는 복귀하기전에 프로시저 호출전으로 복귀

 2.3절 에서 논의한 레지스터 스필링이 필요한 예이다.

 

 스택

 스택은 나중에 들어간것이 먼저 나오는 큐이다. (LIFO)

 스택에는 다음 프로시저가 사용한 레지스터를 저장할 장소나 레지스터의 예전값을 저장된 장소를 표시하기위해

  스택 포인터를 가진다.  (저장되거나 복귀될때마다 더블워드씩 조정된다).

 

 임시 레지스터를 사용하지 않았지만 원상 복구하는 일을 방지하기 위해 레지스터를 두종류로 나눈다.

  X9 - X17 프로시저 호출시, 피호출 프로그램이 값을 보존해 주지 않는 임시 레지스터

  X19 - X28 프로시저 호출 전과 후의 값이 같게 유지되어야 하는 변수 레지스터 10개(복구해야한다)

 

 중첩된 프로시저

  다른 프로시저를 호출하지 않는 프로시저 말단(leaf)프로시저라 한다.

  하지만 이러한 프로시저만 존재하는 프로그램은 적으며, 피호출 프로시저가 다른 프로시저를 호출할 수 있다.

  따라서 레지스터 충돌이 예기치 않게 일어나게 되며 이를 방지하기위한 방법중 하나는 값을 보존할 모든 레지스터를       스택에 넣는것이다.

   호출 프로그램은 인수 레지스터(X0-X7) 임시 레지스터(X9-X17) 중 프로시저 호출 후에도 계속사용하는 것.

   피호출 프로그램은 복귀 주소 레지스터 LR과 저장 레지스터(X19-X25)중 피호출 프로그램이 사용하는 레지스터. 저장

 

 새 데이터를 위한 스택 공간의 할당

  레지스터에서 들어가지 못할 만큼 큰 배열이나 구조체 같은 지역변수를 저장하는데에도 스택을 사용한다.

  프로시저의 저장된 레지스터와 지역변수를 가지고 있는 스택 영역을 프로시저 프레임 또는 액티베이션 레코드라 한다.

  프레임포인터가 프로시저 프레임의 첫 번째 더블워드를 가르키도록 한다. 

  SP는 프로그램이 실행되면서 변경될 수 있으므로, 변수 참조를 위해서는 FP를 사용하는 것이 좋다.

  FP(Frame Pointer) = 호출이 완료된 프로시저를 종료하는 시점에 SP를 어디까지 이동시켜야하는지 정하는 곳

 

  1. fct2 함수가 호출되기 직전에 sp 레지스터에는 주소값 20이 들어가 있다. 현재 스택 주소를 가리키는 것이다.

  2. fct2 함수가 호출되기 직전에 fp 레지스터에는 주소값 8이 들어가 있다. main 함수나  fct1 함수의 경계에 해당하는     주소 정보이다.

  3. fct2 함수가 호출되면서, fp 레지스터에 저장된 값 (주소값 8)을 현재 sp 레지스터가 가리키는 위치 20번지에 먼저       저 장한다. 그 다음 fp 레지스터 값 20을 저장한다. 이 값은 fct1 스택과 fct2 스택의 경계가 된다.

  4. 이후 fct2 함수 호출이 완료되어 반환하고자 한다면, fp 레지스터에 저장된 값을 참조해서 sp 레지스터 값을 20으로     변경한다. 이는 fct2 함수의 스택 프레임을 날리는 효과를 가져온다.

  5. 현재 sp 레지스터가 가리키는 위치(주소 20번지) 에 저장되어 있는 값을 fp 레지스터에 옮겨다 놓는다. 이로써 fct1       함수 호출이 완료되는 상황에서 sp의 위치를 8번지에 가져다 놓을 수 있게 되었다. 이는 main 스택과 fct1 스택의

   경계가 된다.

  

 새 데이터를 위한 힙 공간의 할당

  정적변수와 동적 자료구조를 위한 메모리 공간 할당

  스택은 최상위 주소에서부터 시작해서 아래쪽으로 늘어난다.

  최하위 주소부분은 이미 사용되고 있으며 Text Segment라 한다. Text Segment위에는 정적 데이터 세그먼트라는 부분     이 있는데 상수와 기타 정적 변수들이 여기에 들어간다(ex 고정된 크기 배열).

  크기가 유동적인 링크드 리스트 같은경우 Heap Segment에 저장된다. 

 

  

반응형