Data Processing Instructions

Comparisons ( no results - just set condition codes )

 

'비교'를 '산술 연산' 다음에서야 다루는 이유는 '비교'에서 절대 빠질 수 없는 CPSR에 대한 개념을 '산술 연산'에서 한번 다루고 그때 배운 CPSR 개념을 사용해서 '비교'를 더 제대로 배우기 위함이다. '산술 연산'에서 CPSR 을 사용할때 어떤 방법을 사용하였는지 기억하는가? 

 

접미사 (Postfix) 'S' 를 산술 연산에 관련된 Instruction 에 붙여서 '산술 연산'을 진행시킨다음 그 결과로 나온 값에 대한 여러 특징들을 살펴보고, 각각 특정 조건을 갖는가?(1) 갖지 않는가?(0) 등의 조건에 대한 Flag 들을 세팅하고 이렇게 세팅된 Flag 값들은 다음 라인부터 사용 가능했다. 이 정도는 기억할 수 있을꺼라 믿는다!

 

   1. No postfix 'S' is needed

그런데 Comparison 은 Postfix 'S' 가 필요없다. 그냥 '비교' Instruction 자체가 'S' 를 포함하고 있다고 생각하면 된다. [ CMP = 'CMPS 이렇게 S를 포함하여 쓴거하고 똑같다고 보면 된다.' ] 아래 Operation 들을 보면 '비교'를 진행시킴과 동시에 그 결과로 나온 값을 이용하여, 여러 특징들을 갖는가? 갖지않는가? 등의 조건에 대한 Flag 들을 세팅하고 이렇게 세팅된 Flag 값들은 다음 라인부터 사용 가능하다는 것을 알 수 있다. ( postfix 'S'를 쓰지 않았지만, CPSR 의 조건 Flag 사용법과 완전 똑같다. )

 

   2. No destination register is required

그리고 우리가 rd 로 사용해 왔던 Destination register 가 이 '비교'에서는 필요가 없다. CMP에 대한 결과값을 Destination register (연산의 결과가 들어갈 목적 레지스터) 에 저장하는것이 아니라, CPSR의 Flag bits 자리에 넣기 때문이다. 그렇기 때문에 이 Instruction 을 사용할 때는 간단하게 비교할 두 대상만 써주면 된다. ( XXX r1, r2 / r1 : 비교될 대상 1, r2 : 비교될 대상 2 )

 

시스템 프로그래밍 - 4 (3) : Data Processing Instructions - Comparisons 

 

 

 - Operations

 

-    CMP    r0, r1         @ r0 - r1 to set flags

     r0 과 r1 을 비교한다 (비교 방법은 r0에서 r1 값을 뺀다) 그리고 그 결과에 대한 Flag 들을 설정한다.

 

-    CMN    r0, #9        @ r0 + 9 to set flags

     뒤엣 #9 (또는 레지스터 값이 들어올 수 있다)를 NOT 시킨 결과를 r0 에서 빼준다.

결국엔 #9 를 r0 에서 더해주는 연산과 다를바가 없다. 그리고 그 결과에 대한 Flag 들을 설정한다.

 

-    TST     r0, #4         @ r0 & 0100(2진수) to set flags

     r0과 #4 (또는 레지스터 값이 들어올 수 있다)를 2 진수 연산인 AND 를 시킨다.

그리고 그 결과에 대한 Flag 들을 설정한다.

 

-    TEQ     r0, r1         @ r0 ^ r1 to set flags

r0과 r1를 2 진수 연산인 OR 를 시킨다. 그리고 그 결과에 대한 Flag 들을 설정한다.

 

 

 

 - Branch Instructions

 

C 언어에서는 프로그램을 작성할 때 여러 함수들을 main 함수와 분리시켜 놓고, main 함수에서 특정한 기능들이 필요할 때 마다 우리가 만들어 놓았던 여러 함수들을 호출하면, 컴퓨터는 C 언어를 위에서 아래로 쭉 읽어나가다가 호출된 함수로 이동해서 해당 함수의 작업들을 끝내고, 다시 원래 읽어나가고 있던 자리로 돌아온다.

 

우리가 배우고 있는 이 ARM 프로그래밍 (시스템 프로그래밍)은 알다시피 '어셈블리어'이다. C 언어와 같이 우리가 고수준 언어로 여러 함수들을 호출, 리턴하는 작업들을 간단하게 작성했던것과 달리 우리는 컴퓨터의 작동 원리를 그대로 따라서 (컴퓨터처럼 생각해야한다.) 하나하나 일일이 다 작업해주어야한다.

 

  - 특정 함수를 호출하면 어느 Line에 그 함수가 있는지 알아내어 그 Line 으로 가라고 명령해야하고,

 

  - 리턴할때는 어떤 Line 으로 가야지 월래 컴퓨터가 실행하던 라인으로 다시 돌아가는지 알려주어야한다.

 

마치, 'A칸부터 D칸까지 순서대로 있는 책장'을 가리키며 갑이 을에게 '책장 C칸에 있는 'Hello'이라는 제목을 가진 책좀 가져다 줄래?' 라는 말을 '책장을 보면 오른쪽부터 왼쪽까지 A, B, C, D 칸이 있어, 거기서 오른쪽에서 순서대로 세 칸을 이동하면 거기에 C 칸이 존재하는데, 그 C 칸에서 오른쪽부터 123번째에 있는 책이 'Hello'이라는 제목을 가지고 있을거야. 그 책을 나에게 가져다 줘' 라고 귀찮게 일일이 다 말해주어야하는것과 같다.

 

Branch는 이 귀찮은 작업들을 조금 더 유용하게 해준다.

Branch Instruction 을 사용하기 위해서는 'Label' 과 'Branch'. 이 두 개를 이용한다.

 

 

 

 - Label

 

'책장을 보면 오른쪽부터 왼쪽까지 A, B, C, D 칸이 있어.'

'C칸에서 오른쪽부터 123번째에 있는 책이 'Hello'이라는 제목을 가지고 있을꺼야.'

 

Label 은 함수가 존재하는 Line 을 '2388 번째 Line' 이렇게 일일이 숫자를 붙혀 표현하면 힘들기 때문에 해당 2388번 Line에 말 그대로 Label (이름표)을 달아주는 것이다. 123 번째에는 Hello 라는 이름표를 붙여주어, 나중에 Hello 라는 책을 찾을때 몇 번째에 있는지 일일이 다 세어보지 않고서도 Label (이름표)를 찾기만 하면 된다.

 

 

 

 

 - Branch (B, BL)

 

Branch 의 언어적인 뜻은 '나뭇가지'이다. 어셈블리어에서 branch는 C 언어에서의 goto 와 비슷한 것으로 '위에서 아래로'라는 일반적인 컴퓨터의 실행 흐름에서 특정한 장소로 실행 위치를 이동하기 위함이다.

 

C 언어에서는 권장하지 않는 goto 문의 기능을 왜 어셈블리어에서는 굉장히 중요한 명령어로 취급할까? 그 이유는 바로 어셈블리어의 특징에 있다. 컴퓨터가 읽기 쉬운 형태로 만들어놓은 어셈블리어에는 자동적으로 실행 위치를 바꾸어주는(for while switch 등등) 복잡한 고급언어에 존재하는 명령어들이 없다.

 

for 이든 while 이든 switch 이든 실행라인 위치를 자동으로 변환시켜주는 이러한 고급언어의 명령어들은 실제로 어셈블리어로 어셈블리 되면, Branch 라는 (goto 와 유사) 명령어를 통해 일일이 '실행라인 이동 명령'들의 집합으로 구성되게 된다. Branch을 필요로 하는 상황들은 아래와 같다.

 

 

1. 컴퓨터의 일반적인 실행 방향인 '위에서 아래로'에서 특정한 조건에 따라 실행하기 싫은 Instruction 이 중간에 있다면, 그 Instruction을 뛰어 넘어야 할때 (예를 들어, 어떤 값이 음수이면 어떤 명령어는 실행하지 말아야할 때)

 

2. 특정한 위치에 내가 사용하고 싶은 특정한 Instruction 을 만들어 놓고, 그 Instruction 을 이용하고 싶을때 마다 그 Instruction의 위치로 이동하여 그것을 실행하고 싶을 때 (마치, C언어에서 함수를 호출하는 방법과 유사하다)

 

3. 혹은, 위에서 아래로 순서대로 읽어서 실행하는 어셈블리어에 있어서, 반복문 (Loop 문)을 넣고싶을 때, 한번 그 반복문을 돌고나서 반복문의 끝부분에서 다시 반복문의 처음부분으로 넘어가야할 상황. ( C 언어에서는 간단하게 for 나 while 문을 이용해서 { } 로 필드를 구성해주면 알아서 그 안에서 반복하게 되지만, 이 고급언어가 어셈블리어로 변환되면 컴퓨터가 읽기 위한 어셈블리어에는 이렇게 { } 필드 구성이 없게된다. 그래서 반복문의 끝 부분에 Branch를 넣어서 '반복문의 첫 부분으로 이동하라!' 라는 명령을 넣어야지만이 처음으로 돌아가 반복문을 반복 수행하게 된다. )

 

우리는 이 Branch에 (2)와 (3)의 첫 부분인 Comparison 와 Arithmetics operation 에서 배운 CPSR의 Condition Flag 값들을 이용해 Branch 라인의 전 라인의 Instruction 에서의 연산(혹은 비교) 결과 값이 음수인지 아닌지? (N Flag), 0인지 0이 아닌 값인지? (Z Flag)... 등등의 특징들을 조건으로 부가해줄수 있다. 

 

LOOP:   CMP     r0, #10            @ CMP 의 결과(r0 - #10)에 대한 Condition Flag 가 세팅된다.

BEQ     LOOP              @ r0 - #10 의 결과가 '0' 이라면 같다는 뜻이다 ( EQ 의 의미 )

    @ '0' 이면 (같으면) 'Z Flag = 1'로 세팅, EQ 는 'Z Flag = 1'이면 실행한다

    @ Z Flag = 1 (비교 결과 같다) > EQ 만족 > LOOP 의 Label로 이동하라

    @                                                   (이것이 Branch 의 의미)

 

@ EQ 는 오직 'Z Flag' 가 1인지 0인지만을 판단하는 조건이다.

 

N, Z, C, V 이 조건 Flag 들의 조합에 따라 다양한 '조건'들을 만들어 낼 수 있다.

N = 0, Z = 1, C = 1, V = 0        ( = 0110 라고 CPSR의 최상위 비트(MSB)의 4bits 자리에 새겨진다.)

이런 방식으로 0111, 0100, 1010 ... 많은 조건들을 단순하게 인간이 이해할 수 있는 표현언어로 만들었다.

이렇게 만들어진 표현언어는 <cond> (=Condition, 조건) 자리에 '조건'으로 자리잡게된다.

 

Branch 사용법, 표현법 :   

B<cond> LABEL

@ B = Branch

 

예시:

LOOP:     CMP      r0, #10

BEQ       LOOP

 

 

<cond>에 들어갈 Condition Field - 모든 조건들 모음 :

 

After CMP, CMN ...

 

EQ : equal                                                    NE : not equal

 

HS : unsigned higher or same                            GE : signed greater or equal

HI : unsigned higher                                       GT : signed greater

 

LS : unsigned lower or same                              LE : signed less or equal

LO : unsigned lower                                        LT : signed less

 

 

After TST, TEQ ... or Numeric Operation with S postfix

 

EQ : zero                                                     NE : non-zero

 

CS : carry-bit set                                            VS : overflow

CC : carry-bit clear                                          VC : no overflow

 

MI : negative                                                PL : positive or zero

 

[ 조건 조합에 관한 블로그 : http://blog.naver.com/yosiba?Redirect=Log&logNo=60058803504 ]

 

 

Branch 확장 - Procedure Call (프로시져 콜) Basic :

BL<cond> LABEL

@ BL = Boy's Love (xD) = Branch with Link

...

...

MOV pc, lr

@ 돌아갈때 lr 에 저장해두었던 LABEL 다음 Line 을 pc에다 넣는다.

그냥 Branch 는 B 뒤에 LABEL 로 이동하는 작업'만' 한다. 그런데 BL 은 뜻에서도 알 수 있듯이 B 뒤에 LABEL 로 이동함과 '동시에' BL 명령어 다음 Line 을 저장한다. 예를 들어 BL 이 Line 30 번에 있으면 Line 31번 을 lr 에 복사해 넣고, 이동한 LABEL ( Line 55라고 가정, LABEL의 끝은 Line 60 이라고 가정 )에서 모든 작업이 끝나면 lr 에 저장되었던 Line 31를 바로 pc에 넣는다. pc 값은 'Line 60' 이었다가 'Line 31'로 바뀌어서 다시 BL<cond> LABEL 다음 Instruction 으로 이동하게 된다.

 

예시:

                    BL         FUNC

                 ...

FUNC:         ...

                 ...

         MOV      pc, lr

 

 

 

 

 - Pointer Initialization

 

Branch 말고도 LABEL 을 이용하는 Instruction 이 하나 또 있다. Pointer Initialization 이라고 하며, 프로그램의 끝 부분에 여러 문자들이나 문장들을 써넣으면 그 문장을 특정 메모리 주소에 저장하고 포인터를 갖게 되는데 그 포인터(주소)를 우리가 원하는 레지스터(예를 들어, r1 과 같은)에 저장하는 것이다.

 

Pointer Initialization 사용법, 표현법 :

ADR      rd, LABEL

 

예시:

ADR           r0, HELLO

......

HELLO:                                         

.asciz "Hello, world! \n"

 

ADR 은 실제로는 ARM Instruction 가 아니라 ADD 와 같은 다른 Instruction 들의 조합으로 만들어(emulated) pseudo Instruction (수도, 가짜 명령어) 이다.

 

지금까지 (1), (2), (3) 에 걸쳐서 Data Processing Instruction 들에 대하여 알아보았다. 이 4장을 통해 우리는 이제서야 제대로 된 ARM 프로그래밍을 할 수 있다. 지금까지 배운 명령어들과 1, 2, 3에서 배운 기초적이면서도 매우 basic한 지식들을 기반으로 ARM Assembly Language 를 이용해서 몇 간단한 프로그램을 작성할것이다.

 

Hello, World 출력 프로그램과  Hello, World 구문의 길이(String Length)를 계산하는 프로그램.

 

4장을 시작할때 ARM Instruction 은 매우 방대한데, Data Processing Instruction 이 전체 ARM Instruction 의 대부분을 차지한다고 설명하였었다. 다음 장은 4장에서 배운 Data Processing 관련 명령어가 아닌 기타 다른 Basic ARM Instruction 에 대해 알아보고 우리가 작성한 위 2개의 간단한 프로그램보다 조금더 복잡한 (추가로 배울 Basic ARM Instruction 들을 더 추가로 사용하여 작성한) 프로그램들을 살펴볼것이다.

 

 

 

 

 

 

Posted by 하늘_