용's

[Chap 3] ARM Assembly Language Programming 본문

Computer Science/Embedded System

[Chap 3] ARM Assembly Language Programming

TaeYOng's 2014. 10. 17. 16:43

3.1 Data processing instructions

- 데이터 처리 명령이란, 레지스터에 저장되어 있는 값을 이용하여 산술 및 논리연산, 레지스터 간 데이터 이동, 비교 및 비트 단위 연산을 수행하고 그 결과를 다시 레지스터에 저장하는 명령.

- ARM의 명령어들은 3 address format 이며 그 구조는 다음 그림과 같다.

           

 Cond[31:28]

00[27:26] 

#[25]

OpCode[24:21] 

S[20] 

Rn[19:16]

Rd[15:12]

Operand2[11:0] 

<표1>

 

명령의 구분은 비트 [24:21]에 4비트로 구성되어, 어떤 명령인지를 나타낸다. Rd는 연산 결과가 저장되는 레지스터, Rn은 연산에 사용되는 첫 번째 오퍼랜드, Operand2는 두 번째 오퍼랜드를 나타낸다.


- 산술연산 명령은 다음과 같다

0100 

ADD

더하기 

Rd:=Rn+Op2 

0101 

ADC 

캐리와 함께 더하기 

Rd:=Rn+Op2+Carry 

0010 

SUB 

빼기 

Rd:=Rn-Op2 

0110 

SBC 

캐리와 함께 빼기 

Rd:=Rn-Op2-1+Carry 

0011 

RSB 

역으로 빼기 

Rd:=Op2-Rn 

0111 

RSC 

캐리와 함께 역으로 빼기 

Rd:=Op2-Rn-1+Carry 


- 논리연산은 AND, ORR, EOR, BIC가 있다. BIC는 Bit Clear로 Rd:=Rn AND ~Op2과 같고 해당 비트를 0으로 셋(Clear)한다. 


- 데이터 이동은 MOV, MVN가 있다. MVN은 Negative를 취하고 이동시킨다.


- 비교 연산은 CMP(비교), CMN(Negative를 취해 검사), TEQ(비트 단위로 EOR 검사), TST(비트 단위 검사)가 있다. 비교연산은 다른 연산과 달리 항상 CPSR 플래그를 설정하기 위한 명령으로 {S}가 없어도 항상 플래그를 설정.(다른 명령에 {S}를 추가하면 CPSR를 설정할 수 있다.)

예) TSTEQ    R2, #5;        EQ조건이면 R2와 #5를 비교연산한 후 그 결과로 CPSR 플래그 설정.


- 데이터 처리 명령과 내부 버스를 보면 다음 그림과 같다. 

=> 데이터 처리 명령의 소스 레지스터인 Rn은 레지스터 뱅크에서 A버스를 통해서 ALU에 전달되고, 오퍼랜드 2는 B버스와 배럴 시프터를 통해 ALU로 전달됨. 따라서 오퍼랜드 2는 쉬프트 동작과 함께 사용될 수 있다는 점을 알자!

=> 연산의 결과는 ALU 버스를 통해서 레지스터 뱅크의 대상 레지스터(Rd)에 저장된다. 


1) 오퍼랜드 2와 배럴 쉬프터(Barrel Shifter)

- 데이터 처리 명령의 오퍼랜드 2는 레지스터 또는 이미디어트(immediate) 상수 2가지 종류를 가질 수 있음. 이 두 가지에 대한 사용 여부는 표1에서 비트 [25]에 의해 결정(0: 레지스터로 사용, 1: 이미디어트 상수로 사용)

- 오퍼랜드 2가 레지스터로 사용된다면, 다음과 같은 5가지의 쉬프트 동작 지원

=> LSL(Logical Shift Left): 2의 지수승으로 곱하기, 비트[6:5] 00

=> LSR(Logical Shift Right): 2의 지수승으로 나누기, 비트[6:5] 01

=> ASR(Arithmetic Shift Right): 부호가 있는 2의 지수승으로 나누기, 비트[6:5] 10

=> ROR(Rotate Right): 비트[6:5] 11

=> RRX(Rotate RIght Extend)

    

예) ADD    R0, R1, R2                ; R0:=R1+R2

     ADD    R0, R1, R2, LSL #7    ; R0:=R1+(R2 << 7)

     ADD    R0, R1, LSL R3          ; R0:=R1+(R2 << R3)     


- ARM의 곱셈 동작은 곰셈기(Multiplier)를 사용하는데 이 명령이 사용되면 여러 개의 내부 사이클이 별도로 소요. 하지만 이 쉬프트 동작을 이용하면 1 사이클 내에 곰셈 및 나눗셈 연산이 가능하므로, 사이클으로 인한 속도 개선 가능.


- 표1 을 보면 Operand 2는 [11:0]으로 12비트임. 하지만 여기에 immediate 상수를 표현하기에는 모든 명령어을 32비트로 구성된 ARM에서는 12비트를 가지고 32비트 상수를 표현할 수 없음. 

=> 비트[7:0]의 8비트 상수가 비트[11:8]의 4비트 로데이트 값에 2를 곱한만큼 ROR(로테이트 라이트)하여 나온 값을 사용하면 32비트로 확장 가능.


2) 곱셈 연산

- ARM의 곱셈 명령은 레지스터에 저장된 두 개의 32비트 레지스터를 곱하는 연산을 수행하여 64비트의 결과를 만듬

=> 32비트 곱셈 명령에서는 하위 32비트만 대상 레지스터에 저장.   예) MUL, MLA

=> 64비트 곱셈 명령(Long multiply)에서는 2개의 대상 레지스터에 32비트씩 결과를 나누어 저장. 예) MULL, MLAL


3.2 Data transfer instructions

- ARM의 모든 ALU 동작은 레지스터 안에 있는 값이나 명령어 내에 포함된 이미디어트 상수가 사용되며, 메모리와 데이터는 Load/Store 명령을 통해 전송됨.

- ARM에서 지원하는 Load/Store 명령은 크게 3가지가 있음

=> 단일 레지스터를 사용한 데이터 전송 명령(LDR/STR)

=> 여러 개의 레지스터를 사용하여 하나의 명령으로 여러 워드의 데이터를 전송할 수 있는 명령(LDM/STM)

=> 하나의 명령으로 메모리 내용과 레지스터 내용을 교환하는 명령(SWP)


1) 단일 레지스터를 사용한 데이터 전송 명령
- 단일 32비트 레지스터를 사용하면, 32bit-word/16bit-half-word/byte 3종류의 단위로 전송 가능

- LDR/STR 명령에서 데이터의 위치 정보를 표현하기 위해서는 직접주소방식이 아닌, 상대 주소방식(Indirect addressing)을 사용. 

=> 상대 주소지정 방식은 Base register 값과 offset 값을 이용하여 주소를 계산. 

=> 베이스 레지스터는 A버스를 통해 전달되며, 오프셋은 B버스를 통하여 전달. 그리곤 ALU로 주소 계산.

- 베이스 레지스터와 오프셋 값을 어떻게 계산하느냐예 따라 pre-indexed 와 post-indexed 주소지정 방식이 나뉨

=> pre-indexed addressing mode: access하는 주소(address) 계산을 베이스 레지스터와 오프셋 값을 가지고 access하기 전에 계산되어 사용함. 또한 pre-indexed의 경우 일반적으로 계산된 주소로 데이터 전송이 완료되더라도 베이스 레지스터 값은 변경되지 않지만,사용자 지정에 의해 변경할 수 도 있음. 이를 auto-index 라 함. 

예) LDR r0, [r1, #4]            ;r0:=mem32[r1+4]

예) LDR r0, [r1, #4]!;          ;r0:=mem32[r1+4]  이 방법은 pre-indexed addressing with auto-indexing. (r1 값이 update 됨)


=> post-indexed addressing mode: 베이스 레지스터가 가르키는 어드레스로 액세스한 후에 베이스 레지스터 값과 오프셋을 이용하여 베이스 레지스터 값을 변경하는 방법

예) LDR r0, [r1], #4            ;r0:=mem32[r1]

    ;r1:=r1+4

2) Multiple register data transfers(여러개의 레지스터로 여러 워드의 데이터 전송 명령)

- LDM/STM은 하나의 명령으로 메모리와 프로세서 레지스터 사이에 여러개의 데이터를 옮기는 명령이다. 명령은 메모리 복사, 메모리 이동(move) 등의 대용량의 데이터를 옴길 때 사용된다.

예) LDMIA r1, {r0, r2, r5} ; r0:=mem32[r1]

  r1:=mem32[r1+4]

  r5:=mem32[r1+8]

- ARM에서는 PUSH나 POP같은 스택동작이 따로 정의되어 있지 않으므로, LDM/STM을 사용하여 스택동작을 대신.

=> Stack addressing은 4가지 주소방식이 있음

Full ascending(pre-increment): 먼저 어드레스를 증가시키고, 데이터를 읽거나 저장하는 방식

예) STMFA r0, {r1, r2, r3}        ; r1,,r2, r3의 데이터를 r0의 위치에서부터 차례로 저장한다. 동작 완료 후 

  ; r0 값은 변함이 없다. (만약 STMFA r0! 였다면 r0값은 +12만큼 더해짐)

Full descending: Full인 방식은 같으나 데이터가 채워지는 flow가 아래 방향이다.

Empty ascending(post-increment): 데이터를 먼저 읽거나 저장한 뒤, 어드레스를 증가시키는 방식

Empty descending: Empty인 방식은 같으나 데이터가 채워지는 flow가 아래 방향이다.

예)

   

=> 위의 그림에서 STMIA는 STMEA 방식이며, STMIB는 STMFA, STMDA는 STMED,STMDB는 STMFD

(IA나 IB, DB, DA는 데이터 전송을 위한 어셈블리어에서 사용되는 주소지정 방식이며, FA나 FD, EA, ED는 데이터 전송과 동작은 동일하지만, 스택의 타입에 따라 별도의 키워드 제공)

=> 다음과 같이 연속적으로도 가능

예) LDMIA r0!, {r2-r9}; 


3.3 Control flow instructions

- 분기 명령은 명령어의 실행 순서를 임의로 변경하는 명령으로 Branch(B)와 Branch with Link(BL) 명령이 있다. BL 명령의 경우 함수를 호출할 때 사용되는데 되돌아 올 주소를 링크 레지스터(LR)에 저장하는 기능을 함.

=> 명령어 비트[24]에서 비트가 0이면 그냥 Branch, 1이면 Branch with Link

 예) BL    here        ; 무조건 here로 분기, LR에 돌아올 주소 저장

B      there       ; 무조건 there로 분기


3.4 기타 명령어들

1) 소프트웨어 인터럽트(SWI) 명령어

- SWI는 프로그램 제어 목적으로 프로세서가 Supervisor 모드로 전환되어 정해진 처리를 할 수 있도록 하는 명령.

- SWI 명령의 호출에 의해 프로세서는 예외처리(input / output) 절차에 따라 CPSR을 SPSR_svc에 저장하고 PC를 0x08로 분기하며, 0x08번지에는 SWI 핸들러로 분기하는 위한 명령이 있다. 

예) SWI SWI_WriteC    ;r0의 비트[7:0] 출력

     SWI SWI_Exit        ;모니터로 return


2) 의사 명령(Pseudo Instructions)

- ADR: Address를 읽어오는 명령이다. ADR Rd, <expression> 이면 <expression>의 주소 값을 Rd에 저장

- DCx: 데이터를 선언(DCB는 바이트 데이터, DCD는 워드 데이터, DCW 하프 워드 데이터)






※Reference:     Arm system-on-chip Architecture(Addison Wesley)

     ARM으로 배우는 임베디드 시스템(안효복)




Comments