Cortex-M4 Instruction Set : ARM Cortex-M4 프로세서에서 사용되는 명령어 집합. Thumb-2 아키텍쳐 기반으로 코드 크기를 줄이면서도 높은 성능을 제공한다.
Assembly Programming : 저수준 언어로 소프트웨어를 작성하는 방법
| ARM 버전 | 명령어 세트 | 특징 |
|---|---|---|
| ~ ARMv4 | ARM | 32비트 명령어만 존재. 모든 명령어가 32비트 길이이므로 코드 크기가 큼. |
| ARMv4T ~ ARMv6 | ARM + Thumb | ARM: 32비트 명령어 Thumb: 16비트 명령어 Thumb 도입으로 코드 크기 및 메모리 사용 최적화. 두 명령어 세트 전환 가능(전환 시 오버헤드 및 일부 기능 제한 존재). |
| ARMv7 | ARM + Thumb2 | Thumb2: 16비트와 32비트 명령어 모두 포함. 코드 크기와 성능을 동시에 최적화. |
레지스터 : CPU 내부의 빠른 임시 저정소
즉 CPU가 실제로 데이터를 처리할 때는 항상 32비트 단위로 작업한다.
다양한 크기의 데이터를 효율적으로 처리할 수 있도록 설계되었다.
Main MOV r0,#0 ; move 0 int R0
어셈블러에게 특별한 작업을 지시하는 명령
AREA
- 프로그램의 영역 선언
ENTRY
- 프로그램의 시작점 선언
EQU
-상수나 값을 레이블과 연결
-MAX_SIZE EQU 100 : MAX SIZE = 100
END
- 프로그램 소스의 끝을 알림
DCB ( Define Constant Byte )
- 8비트 크기의 상수 데이터를 메모리에 정의, 프로그램 시작 시 메모리에 초기값을 저장할 때 사용
DCW ( Define Constant Word )
- 16비트 크기의 상수 데이터를 메모리에 정의
DCD ( Define Constant Data )
- 32비트 크기의 상수 데이터를 메모리에 정의
상수 데이터를 메모리에 정의한다는 의미 : 프로그램이 실행될 때 변하지 않는 값을 메모리에 특정 위치에 미리 저장하고, 필요할때 쓴다는 뜻. 주로 하드웨어 설정 값 등
어셈블리 코드에서 사용할 수 있는 다양한 데이터
AREA Example, CODE, READONLY ; Example이라는 이름의 읽기 전용 코드 섹션 정의
AREA ARMex, CODE, READONLY ; 코드 섹션 정의 (이름: ARMex, 코드, 읽기 전용)
ENTRY ; 프로그램 진입점 지정
EXPORT ep1 ; ep1 심볼을 외부에 내보냄(링커가 찾을 수 있게)
ep1 ; 레이블(실제 코드 시작 위치)
; code ; 실제 명령어가 들어갈 자리
END ; 소스 코드 끝
ARM/Thumb 명령어의 주요 분류
분기 명령어의 종류와 특징
| 명령어 | 용도 | 분기 주소 지정 방식 | 추가 기능 |
|---|---|---|---|
| B | 단순 분기 | 즉시값(label) | - |
| BL | 분기 + 리턴주소 저장(call) | 즉시값(label) | LR(r14)에 복귀주소 저장 |
| BLX | 분기 + 리턴주소 저장 + ISA 전환 | 즉시값/레지스터 | LR(r14), ISA 전환 |
| BX | 레지스터 값으로 분기 + ISA 전환 | 레지스터 | ISA 전환 |
| BXJ | 레지스터 값으로 분기 + Jazelle 전환 | 레지스터 | Jazelle 실행 |
즉시값 ( Immediate Value) : 명령어에 직접 적혀 있는 값
MOV R0, #10 ; R0에 10을 저장
B Loop ; Loop이라는 위치로 분기(점프)
#10, Loop 처럼 값이나 주소가 명령어에 바로 들어가 있음
레지스터 값 : 레지스터에 저장된 값을 사용
BX R3 ; R3에 들어있는 주소로 분기(점프)
ISA -> insturction set architecture
함수 호출과 복귀가 어떻게 이루어지는가?
void func1(void) {
...
func2();
...
}
어셈블리 흐름
func1 내부에서 BL func2 명령어 실행 -> 리턴 주소를 LR에 저장
func2 내부에서 함수가 끝나면 BX lr 실행 -> func1의 다음 명령어로 복귀
Branch with Link, Branch with Link and Exchange
해당 명령어들은, 오직 레지스터만 오퍼랜드로 사용한다. 즉 메모리 주소를 직접 접근할 수 없다.
만약 메모리 접근이 필요한 경우, 별도의 load, store 명령어를 사용해야 한다.
<Operation>{<cond>}{S} Rd, Rn, Operand2
<Operation>: 명령어 종류(ADD, SUB 등)
<cond>: 조건부 실행 코드(예: EQ, NE 등, 생략 가능)
{S}: S를 붙이면 연산 결과에 따라 플래그(CPSR)를 설정함
Rd: 결과가 저장될 레지스터
Rn: 첫 번째 입력 레지스터
Operand2: 두 번째 입력(레지스터, 즉시값, 시프트 등)
비교명령만 자동으로 플래그를 세팅하고, 나머지 명령은 접미사 S를 사용하여 플래그를 세팅할 수 있다.
; r0 := r1 + r2| 명령어 | 예시 | 설명 |
|---|---|---|
| ADD | r0, r1, r2 | r0 := r1 + r2 |
| ADC | r0, r1, r2 | r0 := r1 + r2 + C |
| SUB | r0, r1, r2 | r0 := r1 - r2 |
| SUBC | r0, r1, r2 | r0 := r1 - r2 + C - 1 |
| RSB | r0, r1, r2 | r0 := r2 - r1 |
| RSC | r0, r1, r2 | r0 := r2 - r1 + C - 1 |
| 명령어 | 예시 | 설명 |
|---|---|---|
| AND | r0, r1, r2 | r0 := r1 and r2 |
| ORR | r0, r1, r2 | r0 := r1 or r2 |
| EOR | r0, r1, r2 | r0 := r1 xor r2 |
| BIC | r0, r1, r2 | r0 := r1 and not r2 |
| 명령어 | 예시 | 설명 |
|---|---|---|
| MOV | r0, r2 | r0 := r2 |
| MOVN | r0, r2 | r0 := not r2 |
| 명령어 | 예시 | 설명 |
|---|---|---|
| CMP | r1, r2 | set cc on r1 - r2 |
| CMN | r1, r2 | set cc on r1 + r2 |
| TST | r1, r2 | set cc on r1 and r2 |
| TEQ | r1, r2 | set cc on r1 xor r2 |
B 명령에 접미어로 사용되어 조건부 분기를 실행 -> BEQ, BGT
IT블록에서는 조건부 실행 명령으로 사용될 수 있따. -> ADDLE, MOVENE
| Suffix(접미어) | 관련된 플래그 | 의미 |
|---|---|---|
| EQ | Z set | Equal |
| NE | Z clear | Not equal |
| CS / HS | C set | Higher or same (unsigned >= ) |
| CC / LO | C clear | Lower (unsigned < ) |
| MI | N set | Negative |
| PL | N clear | Positive or zero |
| VS | V set | Overflow |
| VC | V clear | No overflow |
| HI | C set and Z clear | Higher (unsigned > ) |
| LS | C clear or Z set | Lower or same (unsigned <= ) |
| GE | N and V the same | Signed >= |
| LT | N and V different | Signed < |
| GT | Z clear, N and V the same | Signed > |
| LE | Z set, or N and V different | Signed <= |
| AL | Any | Always (usually omitted) |

N negative : 연산 결과가 음수이면 1
Z zero : 연산 결과가 0이면 1
C carry : carry(자리올림) 이 발생하면 1
V overflow : signed 연산에서 overflow가 발생하면 1
Q status math overflowed : 포화 연산에서 오버플로어가 발생하면 1
ISR Number : interrupt service routine, 예외상황에서 컴퓨터가 해야할 일
- O : Thread 모드 ( 일발 실행 상태 )
T비트
- T = 0 : ARM 상태, Cortex-M에서 해당 상태가 되면 오류
IT/ICI
- If Then 조건 코드 : Thumb-2에서 조건부 실행을 위한 블록 지정
ARM ISA에서는 대부분의 명령어에 조건을 붙여 실행할 수 있다.
CMP r0, #5
BEQ BYPASS
ADD r1, r1, r0
SUB r1, r1, r2
r0이 5와 같으면 바이패스로 점프, 아니면 순차적으로 ADD, SUB 실행
CMP r0, #5 ; r0와 5를 비교 (r0 - 5 결과로 플래그 설정)
ADDEQ r1, r1, r2 ; r0 == 5일 때만 r1 = r1 + r2 실행
SUBNE r1, r1, r3 ; r0 != 5일 때만 r1 = r1 - r3 실행
하나하나 조건문을 붙일 수도 있음.
코드가 더 짧고, 빠르며, 분기 명령어가 줄어들음. 즉 파이프라인 성능이 높아김
Thumb-2 특징
- If Then 블록 안에서만 조건부 명령 사용 가능
기본 문법:
IT{X{Y{Z}}} condition
X, Y, Z의 의미:
조건 스위치:
예시:
ITTE NE
레지스터 <-> 메모리 사이에서 데이터를 주고받는 명령어
한 번에 하나 또는 두개의 메모리 사이에서 이동 가능
LDRD / STRD: Doubleword(64비트) 단위로 이동
LDR / STR: Word(32비트) 단위로 이동
LDRB / STRB: Byte(8비트) 단위로 이동
LDRH / STRH: Halfword(16비트) 단위로 이동
LDRSB: 부호 확장된 바이트(8비트) 로드
LDRSH: 부호 확장된 하프워드(16비트) 로드
LDRB r0, [r1] ; r1이 가리키는 메모리에서 1바이트를 읽어 r0의 하위 8비트에 저장
LDMIA r1,{r0,r2,r5}
Load Multiple Increment After
r1이 가리키는 메모리 주소에서 32비트 단위로 값을 읽어와 차례로 r0,r2,r5에 저장
즉
r0 = mem32r1
r2 = mem32r1 + 4
r5 = mem32r1 + 8
r1은 자동 인덱싱에 사용
STMIA r1,{r0,r2,r5}
Store Mulitiple Increment After 32비트 단위로 저장

스택의 운영 방식에 따라
Increment I : 증가하는 스택, SP 증가
Decrement D : 감소하는 스택
After A : 데이터를 푸쉬 한 후 SP를 감소or증가시킴. 즉 현재 SP가 가리키는 위치에 먼저 데이터를 저장하고 SP 이동
Before B : SP를 스택 방향에 따라 먼저 증or감 하고 그리고 데이터 저장
Full F : 스택 포인터가 현재 쌓여있는 데이터의 위치를 가르킴
Empty E : 스택 포이터가 다음에 데이터를 저장할 빈 공간을 가르킴
를 접미사에 붙여준다.

STMIA : Store Multiple Increment After
여러 레지스터의 값을 메모리에 한 번에 저장 -> 저장할 때마다 메모리 주소가 4씩 증가
즉, r0, r1, r5 값을 r9가 가르키는 메모리로부터(Before) 4바이트씩 차례로 저장하고, 저장이 끝나면 r9값이 0x100C로 자동 증가
A : 데이터를 저장하기 전에 SP를 먼저 증가/감소시킨다.
A는 F랑 엮어짐.

STMIB : 여러 레지스터의 값을 메모리에 저장
Increase Before: 즉 저장 전에 SP를 증가시킴
용어 정리
• A(After): 저장/로드 “후”에 포인터(SP)가 증가/감소
• B(Before): 저장/로드 “전”에 포인터가 증가/감소
• F(Full): SP가 마지막으로 저장된 데이터(채워진 위치)를 가리킴
• E(Empty): SP가 다음에 저장될 빈 공간(아직 데이터가 없는 위치)을 가리킴

첫번째 그림 : STMFD sp!, {r0, r1, r3-r5}
걍 F랑 B랑 같다고 생각하믄 됨. 그럼면 STMDB. 즉 데이터를 저장하기 전에 SP를 먼저 감소시킨다.
STM Full Decrement
감소하는 스택 방향, 다 끝나고 스택 포인터는 현재 쌓여있는 데이터의 위치를 가르킴.

STMFA sp!. {r0,r1,r3-r5}
데이터를 저장해라. 이때 현재 sp는 Full이다. 그리고 마지막은 Ascending
즉 한칸 증가하고 채워넣기
FA -> Full Ascending
Full : SP가 항상 마지막에 데이터가 채워진 위치를 가르킴
== STMIB -> Increment Before
데이터가 채워져 있으므로, SP를 ++ or -- 하고 Load, Store 진행
A에는 Acsending과 After 두 가지 의미가 있다. 따라서
FA -> Full Acsending
IA -> Incresement After
두 가지 의미는 완전히 반대다. 유의하도록 하자.
LDMIA LDM Incresement After
즉 현재 r0가 가르키는 주소에서 r2-r9 값을 불러옴
각 값을 불러올 때마다 r0를 4바이트씩 증가
그리고 마지막에는 SP 증가
STMIA
STM Incresment After
r1부터 r2-r9값을 저장함.
이때 r1은 초기화하지 않음, 즉 r1이 가리키는 주소는 처음과 같다. !의 유무
Full Decsent . FULL : FULL ? r13이 마지막으로 저장된 데이터를 가르킴. 따라서 SP를 먼저 4만큼 감소시킨다.
그리고 데이터 저장 한 후 반복
Increse After .
즉 현재 r0 위치에서부터 데이터를 load함. 같은 말로는 LDMED
Program status register의 값을 일반 레지스터로 복사하는 명령어이다. CPU의 현재 상태 (플래그, 모드, 인터럽트 상태) 를 소프트웨어가 직접 읽어서 사용할 수 있게 해준다.
MRS : Move Register from Status. 즉 PSR의 값을 일반 레지스터 ( r0-r15 ) 로 복사함.
MSR : Move status from Register. 일반 레지스터의 값을 PSR에 넣기
MRS R0, CPSR ; CPSR(현재 상태 레지스터)의 값을 R0에 복사
Mpsr는 다음 중 하나일 수 있음:
참고

ITTEE EQ
IT THEN THEN ELSE ELS어
조건은 EQ. 즉 Z플래그가 1.
Z=1 이면 1,2번째 명령어 EQ
Z=0 이면 3,4번째 명령어 NE
suffix 명령어 실행 결과를 CPU의 플래그 레지스터에 기록하게 하줌.
S=1 ? AND 명령어에서 비트0이 1이면 True, AND연산이 1이 되는 상황을 의미
ANDNE R3, R3, #1 실행
