-128 ~ 127

-1 ~ 254

0 ~ 255




0000 0001 = 1

1111 1111 = -1


0000 0010 = 2

1111 1110 = -2




0111 1111 = 127

1000 0001 = -127

1000 0000 = -128

ARM 명령어들은 컴퓨터 내부에서 높고 낮은 전기 신호의 연속으로 저장된다. 때문에 숫자로(2진수) 표현할 수 있다. 모든 ARM 명령어의 길이는 32비트이다. 명령어를 숫자로 표현한 것을 기계어(machine language)라고 하고, 이런 명령어들의 시퀀스를 기계 코드(machine code)라 한다.



ARM 명령어 형식


DP형식: 데이터 처리(data processing, DP) 명령어 형식

DT형식: 데이터 전송(data transfer, DT) 명령어 형식


명령어

형식

 예

비고

필드크기

 

4비트

2비트

1비트

4비트

1비트

4비트

4비트

12비트

모든 ARM 명령어의 길이는 32비트이다.

DP형식

DP

Cond

F

I

Opcode

S

Rn

Rd

Operand2

산술연산 명령어 형식

ADD

DP

14

0

0

4

0

2

1

3

ADD r1,r2,r3

SUB

DP

14

0

0

2

0

2

1

3

SUB r1,r2,r3

DT형식

DT

Cond

F

Opcode

Rn

Rd

Offset12

 데이터 전송 명령어 형식

LDR

DT

14

1

24

2

1

100

LDR r1,100(r2)

STR

DT

14

1

25

2

1

100

STR r1,100(r2)

필드크기

 

4비트

2비트

2비트

24비트


BR형식

BR

Cond

F

Opcode

signed_immed_24

 B와 BL명령어


필드

설명

 Opcode

 명령어가 실행할 연산의 종류로서 연산자(opcode)라고 부른다.

 Rd

 목적지(destination) 피연산자 레지스터. 연산 결과가 기억된다.

 Rn

 첫 번째 근원지 피연산자 레지스터

 Operand2

 두 번째 근원지 피연산자

 I

 Immediate. 

 I=0이면 레지스터에 있다.

 I=1이면 두 번째 근원지 피연산자는 12비트 수치값이다.

 즉, I=0 일때는 Operand2에 레지스터 번호가 들어가고, I=1이면 Operand2에 상수 값이 들어간다.

 S

 Set Condition Code.

 이 필드는 무조건 분기 명령어와 관련이 있다.

 Cond

 Condition.

 이 필드는 조건부 분기 명령어와 관련이 있다.

 F

 Instruction Format.

 이 필드는 필요할 때 ARM이 다른 명령어 형식을 사용할 수 있게 해준다.

 F=0이면 DP형식, F=1이면 DT형식이다.


예를 들어서, ADD, SUB, LDR, STR 네 개의 명령어를 ARM 명령어로 나타내면 아래와 같다.

명령어

형식 

Cond

F

I

Opcode

S

Rn

Rd

Operand2

ADD

DP

14

0

0

4

0

reg

reg

reg

SUB(subtract)

DP

14

0

0

2

0

reg

reg

reg

ADD(immediate)

DP

14

0

1

4

0

reg

reg

constant

LDR(load word)

DT

14

1

사용안함

24

사용안함

reg

reg

address

STR(stroe word)

DT

14

1

사용안함

25

사용안함

reg

reg

address

reg는 0부터 15사이의 레지스터 번호, constant는 12 비트 상수(부호 없는 상수인가?), address는 12비트 주소를 나타낸다.



논리연산 명령어


명령어

형식

 예

비고

필드크기

 

4비트

2비트

1비트

4비트

1비트

4비트

4비트

12비트

모든 ARM 명령어의 길이는 32비트이다.

DP형식

DP

Cond

F

I

Opcode

S

Rn

Rd

Shift_imm

Shift


Rm

 

Rs

0

Shift


Rm

산술연산 명령어 형식

ADD

DP

14

0

0

4

0

1

5

2

0

0

2

ADD r5,r1,r2, LSL #2

MOV

DP

14

0

0

13

0

0

6

4

1

0

5

MOV r6, r5, LSR #4

MOV

DP

14

0

0

13

0

0

6

3

0

1

1

5

MOV r6,r5, LSR r3


위의 12비트를 자세히 보면 아래와 같다.

4비트

1비트

2비트

1비트

4비트

11~8

7

6

5

4

3

2

1

0

shift_imm

Shift

0

Rm

Rs

0

Shift

1

Rm


Shift필드가 0이면 왼쪽 논리 자리이동, 1이면 오른쪽 논리 자리이동 연산이다.

Rm은 자리이동 할 레지스터 번호

4번비트가 0이면, Rm을 Shift_imm만큼 자리이동, 4번비트가 1이면, Rm을 레지스터 Rs의 값 만큼 자리이동.



조건부 분기


CMP r0,r1 ; compare r0, r1

BNE L1 ; branch if not equal

BEQ L2 ; branch if equal


BEQ와 BNE의 두 명령어를 조건부 분기라 부른다. 경우에 따라서는 두 변수 간의 대소비교가 필요할 때도 있다.

LT, LE, GT, GE는 비교 결과가 작으면, 작거나 같으면, 크면, 크거나 같으면 분기한다.


비교 명령은 부호있는 수와 부호없는 수 사이의 이분법도 다루어야 한다. 어떤 때는 MSB가 1인 수가 음수를 나타내며, 이 때는 당연히 MSB가 0인 어떤 양수보다도 작다. 하지만, 부호없는 정수의 경우에는 MSB가 1인 수가 MSB가 0인 어떤 수보다도 더 크다.


ARM은 이 두 가지 경우를 처리할 수 있도록 조건부 분기 명령을 더 많이 제공하고 있다. 부호없는 비교로 작다는 LO(lower), 작거나 같다는 LS(lower or same), 크다는 HI(higher), 크거나 같다는 HS(higher or same)로 부른다.



조건부 분기는 조건 플래그(condition flag) 또는 조건 코드(condition code)를 이용한다. CMP 명령이 조건 플래그의 값을 결정하면 분기는 이 값을 검사한다. 조건 플래그는 비교 명령 다음에는 아무 때나 조건부 분기 명령으로 검사할 수 있는 특수 목적 레지스터이다. 분기 명령이 꼭 비교 명령 바로 다음에 나올 필요는 없다.


조건 플래그 값을 설정할 수 있는 명령어는 비교 명령뿐이 아니고 그 외에도 많이 있다. 단, 이 경우는 연산 결과가 0과 비교된다. DP형식 명령어의 S비트는 데이터 처리 명령의 일부로 조건 플래그 값을 설정할 것인지를 결정한다.

어셈블리 프로그래머는 명령어 이름 끝에 S를 붙여서 조건 플래그 설정을 지시할 수 있다. 그러므로 SUB는 조건 플래그를 바꾸지 않지만, SUBS는 뺄셈의 결과에 따라 조건 플래그를 바꾼다.


ARM의 분기 명령어 인코딩


프로그램 카운터(program counter) : 현재 실행중인 명령어의 주소를 가지고 있는 레지스터, ARM구조에서는 줄여서 pc라고 한다. (r15)

따라서 r15를 목적지 레지스터로 하는 LDR 명령은 무조건 분기를 의미한다.


4비트

4비트

24비트

Cond

12

주소


Cond 필드의 값


의미

의미

0

 EQ(EQual)

8

 HI(unsigned HIgher)

1

 NE(Not Equal)

9

 LS(unsigned Lower of Same)

2

 HS(unsigned Higher of Same)

10

 GE(signed Greater than or Equal)

3

 LO(ungined LOwer)

11

 LT(signed Less Than)

4

 MI(MInus, <0)

12

 GT(sigend Greater Than)

5

 PL - (PLus, >=0)

13

 LE(signed Less Than or Equal)

6

 VS(oVerflow Set, overflow)

14

 AL(ALways)

7

 VC(oVerflow Clear, no overflow)

15

 NV(reserved)


조건부 실행


ARM의 또 다른 독특한 특징은 분기 명령뿐만 아니라 거의 대부분의 명령을 조건부로 실행할 수 있다는 것이다. 이 때문에 대부분의 ARM 명령어 형식에 4비트 Cond 필드가 포함되어 있는 것이다. 어셈블리 프로그래머는 명령어 이름 뒤에 원하는 조건만 덧붙이면 된다. 컴퓨터는 해당 조건 플래그가 참일때만 이 명령어를 실행한다.


ADDEQ는 플래그 값을 결정한 가장 최근의 연산에서 피연산자들이 같았다고 조건 플래그에 표시되어 있을 때만 덧셈을 한다. 


CMP r3, r4 ; r3과 r4를 비교해서 조건 플래그 값을 결정한다.

ADDEQ r0, r1, r2 ; 조건 플래그에 피연산자들이 같았다고 표시되어 있을 때, r0 = r1 + r2

SUBNE r0, r1, r2 ; 조건 플래그에 피연산자들이 다르다고 표시되어 있을 때, r0 = r1 - r2


Cond 필드의 값은 분기 명령에만 사용되는 것이 아니라 모든 명령의 조건부 실행에 사용된다 .14는 항상 실행을 뜻한다. 따라서 Cond 필드 값이 14이면 항상 실행되라는 명령어란 뜻이다.




32비트 수치 피연산자


ARM 명령어의 길이를 32비트로 고정한 덕택에 하드웨어가 간단해지기는 했지만, 32비트 상수나 32비트 주소를 사용할 수 없어서 불편한 경우도 있다. 


프로그램에서 사용하는 상수는 대체로 크기가 작다. 그러므로 대부분 작은 필드이면 충분하지만 때에 따라서는 더 큰 상수 값이 필요한 경우도 있다. ARM 설계자는 어떤 32비트 상수들은 다른 것보다 자주 사용된다고 생각하여, 중요해 보이는 일부 상수를 ARM이 지정할 수 있게 하는 트릭을 포함시켰다.


DP형식의 12비트 Operand2 필드는 오른편의 상수 필드 8비트와 오른쪽 회전 필드 4비트로 나누어진다. 8비트 상수는 회전 필드 값의 두 배만큼 오른쪽으로 회전된다. 이 방법을 사용하면 다음 값을 갖는 부호없는 정수는 어느 것이라도 표현할 수 있다.




여기서 X는 0과 255사이의 값이고, i는 0과 15사이의 값이다. 또한 회전된 8비트 상수가 MSB와 LSB 모두에 영향을 미치는 경우에 몇 가지 패턴을 더 나타낼 수 있다.






레지스터 r0에 다음 32비트 상수를 채우는 ARM 기계어 코드는 다음과 같다.


0000 0000 1101 1001 0000 0000 0000 0000


0이 아닌 비트 8개를 찾는다. 비트 패턴 1101 1001을 찾았고 이는 십진수 217에 해당한다. 따라서 오른편 8비트의 상수필드(mm_8)에 넣는다. 이 패턴을 최하위 8비트에서 원래 위치인 비트16과 23사이로 옮기려면 오른쪽으로 16비트만큼 회전해야 한다. ARM은 회전 필드의 값에 2를 곱하기 때문에(2*i) 회전 필드에는 8을 넣는다. 그러므로 r4에 이 32비트 상수를 채우는 기계어 MOV명령어(opcode=13)는 다음과 같다.


Cond

F

I

Opcode

S

Rn

Rd

rotate-imm

mm_8

14

0

1

13

0

0

4

8

217

4비트

2비트

1비트

4비트

1비트

4비트

4비트

4비트

8비트


만약 숫자가 위 패턴들에 들어맞지 않으면, 어셈블러는 이 32비트 상수를 메모리에 저장하고 나중에 레지스터에 적재해야 한다.



1. ARM은 바이트 주소를 사용한다.


2. ARM에서 워드의 시작주소는 항상 4의 배수이어야 한다. 이러한 요구사항을 정렬 제약(alignment restriction)이라 하며, 많은 컴퓨터에서 이 방법을 사용한다. (정렬을 사용하면 데이터 전송이 빨라진다.)


3. 컴퓨터는 가장 왼쪽, 즉 "최상위(big end)" 바이트 주소를 워드 주소로 사용하는 것과 가장 오른쪽, 즉 "최하위(little end)" 바이트 주소를 워드 주소로 사용하는 것의 두 종류로 나뉘어진다. ARM은 최하위 주소를 사용하는 little-endian 계열에 속한다.


4. 자주 사용하지 않는(또는 한참 후에 사용할) 변수를 메모리에 넣는 일을 레지스터를 스필링(spilling register)한다고 말한다.


5. 수치 피연산자 => #3 같이 피연산자가 상수를 나타내는 것, 메모리에서 상수를 가져오는 것 대신에 바로 상수를 사용할 수 있다.


6. ARM 하드웨어는 자리이동과 덧셈을 같이 해도 그냥 덧셈만 하는 것보다 느려지지 않게 설계되었다. 또한 자리이동시 상수자리 만큼의 자리이동도 가능하고, 레지스터 값만큼 자리이동하는 것도 가능하다.


7. ARM에는 LSL과 LSR외에도 더 많은 자리이동 연산이 있다. ASR은 자리이동을 하면서 부호비트를 복제한다.

ROR은 자리이동할 때 밀려나는 비트를 버리는 대신 왼쪽의 빈 자리를 채우는 데 다시 사용한다.






바이트 전송 명령어


LDRB : 메모리에서 한 바이트를 읽어서 레지스터의 오른쪽 8 비트에 채우는 명령어

STRB : 레지스터의 오른쪽 8비트를 메모리로 보내는 명령어

l

LDRSB : 바이트를 부호있는 수로 취급하여 레지스터의 왼쪽 24비트를 부호확장하여 채우는 명령어


ARM 소프트웨어는 다음의 프로시져 호출 관례에 따라서 레지스터 16개를 할당한다.


r0-r3 : 전달할 인수를 가지고 있는 인수 레지스터 4개

lr(r14) : 호출한 곳으로 되돌아가기 위한 복귀 주소를 가지고 있는 링크 레지스터 한 개



관례

r0-r3, r12 : 프로시져 호출 시, 피호출 프로그램이 값을 보존해 주지 않는 인수 또는 스크래치 레지스터

r4-r11 : 프로시져 호출 전과 후의 값이 같게 유지되어야 하는 변수 레지스터 8개(피호출 프로그램이 이 레지스터를 사용하면 원래 값을 저장했다가 원상 복구한다.)




2.7 판단을 위한 명령어



CMP register1, register2

BEQ L1


register1과 register2가 같으면 L1으로 분기





CMP register1, register2

BNE L1


register1과 register2가 다르면 L1으로 분기



BEQ와 BNE 두 명령어를 조건부 분기라 부른다.



예제

f,g,h,i,j 가 r0부터 r4에 해당한다고 하면


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

else f = g - h;



CMP r3, r4

BEQ, if


SUB r0, r1, r2

B Exit


if: ADD r0, r1, r2

B Exit


Exit:


-------------------

CMP r3, r4

BEQ, Else


ADD r0, r1, r2

B Exit


Else: SUB r0, r1, r2

Exit:


의문사항

1. 레지스터 스필링이란 무엇인가?

자주 사용하지 않는 변수들을 메모리에 넣는 일


2. ARM에서 스택은 어디에 존재하나? 스택을 위한 메모리는 어디에 존재하나?


3. 바이트 주소지정방식과 워드 주소지정방식의 차이는?

ARM은 바이트 주소지정방식을 사용한다.

바이트 주소지정방식은 주소 한 개가 하나의 바이트를 나타내는 것, 워드 주소지정방식은 주소 한 개가 하나의 워드를 나타내는 것.

따라서 바이트 주소지정방식에서는 워드 배열의 각 원소의 주소를 구할 때 인덱스 * 4를 해야한다.


워드 주소를 나타낼 때, 빅 엔디안과 리틀 엔디안 방식이 있다.

빅 엔디안 : 최상위 바이트 주소를 워드 주소로 사용하는 것

리틀 엔디안 : 최하위 바이트 주소를 워드 주소로 사용하는 것

예를 들어, 0x12345678의 값이 있을 때 빅 엔디안일 경우 0x12가 워드의 주소를 나타내고, 리틀 엔디안일 경우 0x78이 워드의 주소를 나타낸다.


컴퓨터 구조 및 설계 4판


page 77


변위와 베이스 레지스터를 사용하는 ARM 주소지정방식은 베이스 레지스터가 시작 위치를 가리키고 변위로 원하는 원소를 선택할 수 있으므로 배열뿐만 아니라 구조체 접근에도 딱 들어맞는다. 

데이터 전송 명령에서 주소 계산에 사용하는 베이스 레지스터를 인덱스 레지스터(index register)라고 부르기도 한다. 원래 배열의 시작주소는 변위에 들어가고 배열의 인덱스가 레지스터에 들어가도록 되어 있었기 때문이다. 하지만, 오늘날에는 메모리가 매우 크고 데이터 할당을 위한 소프트웨어 모델이 훨씬 복잡하다. 따라서 배열의 시작주소가 변위 부분에 다 들어가지 않는 경우가 많으므로 레지스터에 넣는 것이 보통이다.



즉, 변위에 들어갈 수 있는 값의 범위가 32비트보다 작다는 소리인가? 레지스터는 32bit범위.




빅 엔디안 : 최상위 바이트 주소를 워드 주소로 사용하는 것

리틀 엔디안 : 최하위 바이트 주소를 워드 주소로 사용하는 것


1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
 
int main()
{
    int a = 0x12345678;
 
    char *= (char*)&a;
    printf("%x\n"*p);
    printf("%x\n"*(p + 1));
    printf("%x\n"*(p + 2));
    printf("%x\n"*(p + 3));
 
    return 0;
}
cs




+ Recent posts