ARM System Developer's Guide
- Designing and Optimizaing System Software
#####
Data Processing instructions
Branch Instructions
Load-Store Instructions
Software Interrupt Instructions
Program Status Register Instructions
Loading Constants
ARMv5E Extensions
Conditional Execution
> ARM은 버전에 따라 지원하는 명령어들이 다르지만 하위 호환성을 보장
> 어떤 명령어들은 기능 확장으로 mnemonic이 변경되기도 함
> ARM 명령어는 register안에 들어있는 데이터를 처리
> load-store 명령어만 가지고 메모리 액세스
> 2개 or 3개의 오퍼랜드를 가짐
> 명령어는 4가지로 분류
- Data Processing 명령어
- branch 명령어
- load-store 명령어
- Software interrupt 명령어
### 3.1. Data Processing Instructions
- 레지스터 안에서 데이터를 조작하는데 사용
- 데이터 이동 명령어, 산술 명령어, 논리 명령어, 비교 명령어, 곱셈 명령어
- 대부분의 데이터 처리 명령어에서 오퍼랜드 중 하나를 barrel shifter를 사용하여 처리가 가능
- "S" 접미사 : 명령어 처리 후 결과를 가지고 cpsr 플래그들을 업데이트
- 데이터 이동 연산과 논리연산 후 상태 플래그 업데이트
C : carry. barrel shifter의 결과, 시프트되어 나온 마지막 비트값으로 설정
N : nagative. 결과값의 31번째 비트 값으로 설정
Z : zero. 결과가 0인경우 설정
## 3.1.1. 데이터 이동 명령어
- N 값을 결과 레지스터인 Rd에 복사
* N : 레지스터 값 혹은 상수 값
- 초기값 설정이나 레지스터간에 데이터를 이동하기 위해 사용
## 3.1.2. Barrel Shifter
: 데이터 처리 명령어는 ALU에서 처리되는데, 데이터 처리의 효율을 목적으로 ALU로 입력되기 전 배럴 시프터에 의해 2진 단위의 좌우 시프트가 가능하게 함
-> 많은 데이터 처리 명령어의 강력함가 유연성이 증진
> N : 일반 상수값 or 레지스터값 or 데이터 처리 명령어에 의해 사용기 전 배럴 시프터로 미리 시프트 처리를 한 레지스터 Rm
> barrel shifter를 사용할 수 없는 데이터 처리 명령어
: MUL(multiply), CLZ(Count Leadming Zero), QADD(signed saturated 32-bit add)
- 선처리나 시프트 작업은 한 명령어의 실행 사이클 내에서 처리
-> 상수 값을 레지스터로 로드하여 2의 거듭자리 만큼 곱하거나 나누기를 할 때 유용함
* LSL : C언어에서의 시프트 연산자 (<<)
# Barrel Shifter에서 사용할 수 있는 5가지 시프트연산
* carry flag : 1
예제.
* shift 해서 계산 후, 명령어에 S가 붙어있으므로 상태플래그의 캐리비트 활성화
## 3.1.3. Arithmetic Instructions
: 산술명령어. 32-bit signed/unsigned 값의 덧셈/뺄셈을 구현하기 위해 사용
* ?????, 예제 3.6에서 상태플래그 중 Carry는 왜 1로 설정?
## 3.1.4. 산술 연산에서의 배럴 시프터 사용
* 산술 연산이나 논리 연산에서 두 번째 오퍼랜드의 시프트가 가능하는 점은 ARM 명령어 세트에서 매우 강력한 특징
## 3.1.5. Logical Instructions
> 논리 명령어는 2개의 소스 레지스터에 비트단위로 논리 연산을 수행
* ?????, BIC 예제 잘 이해 안됨. 물어보기.
## 3.1.6. Comparison Instructions
: 32-bit 값을 가진 레지스터를 비교하고 테스트 하기위해 사용
- cpsr 플래그 비트를 업데이트 하며, 다른 레지스터에는 영향을 미지치 않음
- 해당 비트들을 세트한 후, 조건부 실행을 사용해 프로그램의 흐름을 변겨하는데 사용
- 접미사 "S"가 필요하지 않음
* 비교 명령어는 비교되는 레지스터에는 영향을 끼치지 않음.
오직 cpsr의 상태 비트만 업데이트.
## 3.1.7. Multiply Instructions
: 곱셈명령어. 두 레지스터 안의 내용을 곱하는 명령어
- 명령어에 따라 곱한 결과에 또 다른 값을 더하기도 함
- 62-bit 곱셈명령어(long multiply)는 64비트를 표현하는 2개의 레지스터를 이용하여 곱셈
결과는 하나의 결과레지스터에 저장 or 2개의 레지스터를 이용해 저장
- 곱셈 명령어를 실행하는 데 걸리는 사이클 수는 프로세서마다 다름
- 어떤 프로세서에서는 실행 사이클이 Rs 안에 저장되어 있는 값에 따라 다름
> 64-bit 곱셈 명령어(SMAL, SMULL, UMLAL, UMULL)를 수행하면 64-bit의 결과가 나옴
but, 결과는 32-bit 레지스터에 저장해야 하는데 크기때문에 문제가 됨
=> RdLo, RdHi라는 레이블이 붙은 2개에 레지스터에 각각 결과중 하위 32bits, 상위 32bits가 저장
### 3.2. Branch Instructions
: 실행의 흐름을 변경하거나 어떤 루틴을 호출하는 데 사용
- subroutines, if-then-else 구조, loops 사용이 가능하도록 해줌
- PC가 새로운 주소를 가리키도록 함으로써 실행의 흐름을 바꿈
# ARMv5E 명령어 세트의 4가지 분기 명령어
- label이 가리킼는 주소는 "signed PC - 상대 offset"의 형태로 명령어에 저장
- 분기 명령어의 약 32MB 이내의 주소여야 함
- T : cpsr의 Thumb bit
- BL : 링크값을 갖는 분기로써, LR로 복귀할 주소를 저장하며 서브루틴 호출을 수행하는데 사용
- BX : Thumb 코드로 분기하기 위해 레지스터 Rm에 있는 절대 주소값을 사용
- BLX : BX와 유사하며, 최하위 비트값으로 cpsr의 T 비트를 업데이트. 추가로 LR를 복귀할 주소를 저장
### 3.3. Load-Store Instructions
: 메모리와 프로세서 레지스터 사이에서 데이터를 전송
- Single-Register Transfer
- Single-Register Load-Store Addressing Modes
- Multiple-Register Transfer
## 3.3.2. 단일 레지스터 전송 명령어
- Single-Register Transfer
- 메모리->레지스터, 레지스터->메모리, 하나의 데이터를 전송하는데 사용
- 지원되는 데이터형 : signed/unsigned words(32-bit), halfwords(16-bit), bytes(8-bit)
* addressing1, addressing2 : 표 3.5, 표 3.6 참고
- LDR과 STR 명령어는 load되거나 store되는 데이터형 크기와 똑같은 배열로 데이터를 load/store 할 수 있음
(ex. LDR은 0, 4, 8 등으로 메모리 주소상에 32비트 워드를 load할 수 있음)
*예제 3.15.
; 레지스터 r1이 가리키는 메모리 주소의 내옹을 r0에 로드
LDR r0, [r1] ; LDR r0, [r1, #0]
; 레지스터 r0이 가리키는 메모리 주소의 내용을 r1이 가리키는 메모리 주소에 저장
STR r0, [r1] ; STR r0, [r1, #0]
> #0 : offset, r1 : base address register
## 3.3.2. 단일-레지스터 로드-스토어 주소지정방식
- Single-Register Load-Store Addressing Modes
- ARM 명령어 세트는 메모리 주소의 계산을 위해 다양한 모드를 제공하며,
이 모드는 preindex with writeback, preindex, postindex이라는 indexing methonds 중 하나를 포함하고 있음
* !는 명령어가 계산된 주소를 베이스 주소 레지스터에 저장한다는 의미
* 자동인덱스, 프리인덱스, 포스트인덱스로 변역되어 있음
# 자동인덱스
- auto index
- preindex with writeback
- 베이스 레지스터로부터의 주소에서, 주소 offset만큼을 계산한 후 새로운 주소값으로 베이스 레지스터를 업데이트
- 배열을 검색하는데 사용
# 프리인덱스
- 베이스 레지스터로부터의 주소에서, 주소 offset만큼을 계산한 후 주소 베이스 레지스터를 업데이트 하지는 않음
- 배열을 검색하는데 사용
# 포스트인덱스
- 주소가 사용된 후 주소 베이스 레지스터를 단지 업데이트만 수행 함
- 데이터 구조 안의 요소들을 액세스 하기위해 사용
> Example 3.16 <
pre r0 = 0x00000000
r1 = 0x00009000
mem32[0x00009000] = 0x01010101
mem32[0x00009004] = 0x02020202
- LDR r0, [r1, #4]!
Preindexing with writeback :
POST[1] r0 = 0x02020202
r1 = 00009004
- LDR r0, [r1, #4]
Preindexing :
POST[2] r0= = 0x02020202
r1 = 0x00009000
- LDR r0, [r1], #4
Postindexing :
POST[3] r0 = 0x01010101
r1 = 0x00009004
# 32-bit 워드 또는 unsigned 바이트의 load-store를 위해 사용할 수 있는 주소지정방식
- 특별한 load-store 명령어를 사용할 수 있는 주소지정방식은 명령어군에 따라 달라짐
- signed offset or register는 +/- : base address register Rn으로부터의 음/양수 offset
- immediate : base address register + offset(12-bit)을 통해 계산된 주소
- scaled : base address resister + barrel shift를 통해 계산된 주소
# 각각 다르 주소지정방식을 사용한 LDR 명령어의 예
# 단일 레지스터 로드-스토어 주소지정방식, 하프워드, signed 하프워드, signed 바이트, 더블워드
: 16-bit halfword나 signed byte data를 사용하여 load-store 명령어 상에서 사용할 수 있는 주소지정방식
* Table 3.6, Table 3.7의 동작들은 barrel shifter를 사용할 수 없음
- STRH는 signed/unsigned halfword를 둘 다 저장할 수 있어, STRSB나 STRSH가 존재하지 않음
* ????, STRH면 haftwork를 다루는건데, 왜 STRSB같은 바이트를 다루는 명령어를 여기서 이야기 할까
STRB에서 거론해야하는거 아닌가... ?
# 다양한 STRH 명령어들
## 3.3.3. 다중-레지스터 전송 명령어
- Multiple-Register Transfer
- 명령어 하나로 메모리와 프로세서 사이에서 여러개의 레지스터를 전송
- 전송은 메모리를 가리키는 base address register에서 발생
- 인터럽트 발생 시, 다중-레지스터 전송명령어가 완료 될 때가지 인터럽트가 지연되므로 인터럽트 지연시간을 증대시킬 수 있음
* armcc와 같은 컴파일러는 전송할 레지스터의 최대수를 제어하여, 최대 인터럽트 지연시간을 제한
# 다중 로드-스토어 명령어를 위한 여러가지 주소지정 방식 (*N : 레지스터 리스트 안에 포함된 레지스터의 수)
: 메모리 배열의 앞쪽, 뒤쪽을 액세스가 가능하게 됨 (Stack의 push, pop)
> Example 3.17 <
"LDMIA r0!, {r1-r3}"
- "r0" : base register, Rn
- "!" : 명령어 실행 후 Rn에 update
- "-" : 레지스터 범위. 물론 열거해도 됨
- "{}" : 괄호안에 레지스터 각각을 열거해도 됨
- r0은 전 상태에서 메모리 주소 0x80010을 가리킴
- 메모리 주소 0x80010, 0x80014, 0x80018은 각각 1,2,3의 값을 가짐
> LDM명령이 수행된 후 각 r1, r2, r3는 1,2,3 값을 가지게 됨
- base register가 같는 주소는 업데이트 되어 0x8001c를 가짐
> "LDMIB는 Increment Before"
: base address register의 값을 미리 증가를 시킨 후 명령을 수행 ?
> DA, DB는 "decrement"
- 시작 주소를 감소시킨 후 그위의 메모리 영역에 저장
- 메모리 주소의 감소가 아니라 레지스터 리스트가 반대 순서로 액세스 함
# base address가 업데이트 될 때 사용되는 LDM-STM의 쌍
- 레지스터의 값을 임시로 저장하고 복구할 때 유용
> Example 3.18 <
----------------------------------------
PRE r0 = 0x00009000
r1 = 0x00000009
r2 = 0x00000008
r3 = 0x00000007
STMIB r0!, {r1-r3}
MOV r1, #1
MOV r2, #2
MOV r3, #3
----------------------------------------
- "IB" , "Rn!"이므로
r0의 주소값은 Rn + 4 * N = r0(0x00009000 + 4 * 3) = 0x0000900c 로 없데이트
- r1, r2, r3의 값인 9,8,7을 메모리에 저장
- MOV 명령어로 각각 1,2,3으로 업데이트
----------------------------------------
PRE2 r0 = 0x0000900c
r1 = 0x00000001
r2 = 0x00000002
r3 = 0x00000003
LDMDA r0!, {r1-r3}
----------------------------------------
- 레지스터 리스트가 반대 순서로 액세스하며 원래 값을 다시 읽음
- DA로 시작주소를 감소
r0 = Rn - 4 * 3 = 0x0000900c - 12 = 0x00009000
- "!"이 있으므로 r0값을 업데이트
----------------------------------------
POST r0 = 0x00009000
r1 = 0x00000009
r2 = 0x00000008
r3 = 0x00000007
----------------------------------------
> Example 3.19 <
: 블록메모리 복사를 위한 다중-레지스터 전송 명령어의 사용. 32bytes 복사하기
; r9 - 소스데이터의 시작 위치
; r10 - 결과 데이터의 시작 위치
; r11 - 쓰의 끝 위치
Loop
; 소스의 위치부터 r0-r7(4bytes * 8 = 32bytes)를 로드하여 r9을 업데이트 (복사할 다음 블럭의 위치를 저장해야하므로 "!")
LDMIA r9!, {r0-r7}
; 결과데이터를 32bytes 저장하고 r10 업데이트 (결과를 저장할 다음 목적지 영역을 저장해야하므로 "!")
STMIA r10!, {r0-r7}
; 소스의 끝인지 체크
CMP r9, r11 ; r9, r11을 비교
BNE loop ; Branch Not Equal. CMP 명령어가 상태 플래그를 not equal로 설정하면 분기 명령어 실행
; 업데이트 된 상태명령어가 not equal이 아니면 루틴이 끝남
# 3.3.3.1. Stack Operations
- 다중-레지스터 전송 명령어를 사용
- Pop : LDM, Push : STM
- ascending 스택 : 스택을 메모리 상위 주소 뱡향으로 증가
descending stack :스택을 메모리 하위 주소 방향으로 증가
- full stack(F) : sp에 마지막으로 사용된 위치 저장(마지막 아이템)
empty stack(E) : sp에 처음 사용되지 않은 위치를 저장(마지막 아이템 다음의 빈공간)
- stack 동작을 지원하기 위한 다양한 다중-레지스터 전송 주소지정방식이 있음
- ARM에서는 루틴의 호출과 레지스터들의 할당을 ATPCS에 정의하고 있음
* ATPCS : ARM-Thumb Procedure Call
- ATPCS에서 stack을 full descending stack으로 정의
- pop, push를 LDMFD, STMFD 명령어로 구현
> 스택 동작을 위한 어드레싱 방법
> STMFD 명령어 : full stack push 동작과 empty stack push 동작
> Stack 처리를 위해 보존해야하는 3가지 인자
- Stack Base : 메모리 상에서 스택의 시작 위치
- Stack Pointer : 초기에는 스택 베이스를 가리키지만 데이터가 스택으로 들어가면 메모리 하단으로 이동하며 스택의 가장 맨 위를 계속 가리킴
- Stack Limit : 스택 포인터가 스택 리미트를 넘어가면 Stack Overflow가 발생
* Stack Overflow의 체크
SUB sp, sp, #size
CMP sp, r10
BLLO_stack_overflow ; 조건
- ATPCS에서는 r10 레지스터를 "sl(stack limit)"으로 정의
- BLLO : 분기 명령어 "BL" + 조건인자 "LO"
=> sp가 r10보다 작으면 overflow 발생
=> sp가 stack base보다 작아질 때도 오버플로우 발생
## 3.3.4. Swap Instruction
: 메모리의 내용과 레지스터의 내용을 바꾸어주는 명령어
- 단일 수행 명령어
: 명령어의 처리가 끝날 떄까지 다른 명령어가 그 영역을 read/write 하지 못하도록 같은 버스를 사용해 어떤 영영을 read/write 함
=> "버스를 잡고있다."라고 표현
- OS안에서 세마포어나 뮤텍스 구현에 유용함
- 표기법에서 인자B(바이트 단위를 나타냄)를 사용할 수 있기때문에, 워드나 바이트 단위의 데이터 교환에 유용