-
ARM CPU의 기본 구성
-
R0~R14총 15개의 범용 레지스터를 가지고 있습니다. -
범용 레지스터
R13는 특수 레지스터SP로 사용됩니다.SP는 C언어 사용시 스택의 주소를 저장하는 레지스터입니다.
-
범용 레지스터
R14는 특수 레지스터LR로 사용됩니다.LR은 함수 호출 시 되돌아갈 함수의 주소가 저장되는 레지스터입니다.
-
범용 레지스터
R15는 특수 레지스터PC로 사용됩니다.PC는 다음 실행할 프로그램의 주소를 가지고 있는 레지스터입니다.
-
CPSR이라는 상태 레지스터를 가지고 있습니다.

-
Rs,Rd는 반드시 레지스터(R0~R15)이어야 합니다.OP1은 항상 레지스터 입니다.
-
OP2는 레지스터 일수도 있고, 레지스터가 아닐수도 있습니다.
-
Load, Store
MOV 명령
- ARM에서 레지스터의 데이터 이동은
MOV명령을 사용합니다.- 예)
MOV R0, R1:R1의 내용을R0으로 복사 - 예)
MOV R0, #1: 상수1을R0으로 복사
- 예)
- 메모리→레지스터 :
LDR(Load to Register) - 레지스터→메모리 :
STR(Store to Memory)
상수 값을 레지스터에 저장
LDR명령 사용 방법LDR Rn, =ValueLDR을 사용하여 상수의 값을 레지스터에 저장할때=Value에서=를 빼먹지 않게 주의합니다.- 다른 명령어에서는 상수 입력에
#이 들어가지만LDR만=이 들어가니 주의합시다.
- 다른 명령어에서는 상수 입력에
- 레지스터간 데이터 복사는
MOV를 사용합니다. Rn:R0~R15(대소문자 구분 없음)=Value: 상수 값 (최대 4바이트)- 10진수 :
- 예)
41,-27
- 예)
- 16진수 :
- 예)
0x1234,-0x1EF
- 예)
- 예)
LDR R0, =100,LDR R13, =0x1234
- 10진수 :
상수 표현
OP2의 상수는 8비트의 값고 짝수 비트 ROR(Rotate Right)로 표현되어야 합니다.- 32비트 명령 안에서 상수 값을 함께 저장하다보니 범위 제한이 발생하기 때문입니다.
0~255범위의 상수는 무조건 사용이 가능합니다.
사용된 상수가 MOV 명령에서 사용 가능한지 불가능한지 판단해보자
-
MOV R0, #0x7F00: 가능
-
MOV R0, #0x30C0: 가능
-
MOV R3, #0x14E: 불가능
-
LDR을 사용하면, 범위 제한 없는 32비트 값으로 표현 가능- 실제 상수 값은 메모리에 존재하며 레지스터 참조로 변환됩니다.
MOV보다LDR이 느리지만,LDR은 32비트 데이터를 사용할 수 있습니다.
레지스터 간접참조 LDR, STR
- 레지스터 간접 참조 :
LDR Rd, [Rs]Rs레지스터 값을 메모리 주소로 하여Rd에 저장Rs,Rd:R0~R15
STR Rs, [Rd]Rd레지스터 값을 메모리 주소로 하여Rs를 저장Rs,Rd:R0~R15
예)
LDR R0, =0x1000 @ R0에 상수 0x1000 저장
LDR R1, =80 @ R1에 상수 80 저장
STR R1, [R0] @ R0의 값인 0x1000를 메모리 주소로 참조하여
@ 0x1000에 상수 80을 저장
LDR, STR 쉽게 이해하는 방법

Label
START: MOV R0, #10 @ R0 레지스터에 상수 10을 저장
STR R0, [R1] @ R1에 저장되어 있는 주소에 R0 값을 저장
LDR PC, =START @ START의 주소를 PC(Program counter)에 저장
- Label은 모두 주소 값(해당 Label 위치의 주소)입니다.
- 위의 코드는
START의 주소 값을 PC(Program counter)에 저장하여 무한 루프를 하게 됩니다.
LDR, STR Post Indexing
Post Indexing 사용 방법:
LDR R0, [R1], #4 @ R1 주소에 있는 값을 R0에 넣고, R1에 4를 더합니다.
STR R0, [R2], #-4 @ R0의 값을 R2의 주소에 넣고, R2에 4를 뺍니다.
LDR R0, [R1], R3 @ R1 주소에 있는 값을 R0에 넣고, R1에 R3의 값을 더합니다.
STR R0, [R2], -R3 @ R0의 값을 R2의 주소에 넣고, R2에 R3의 값을 뺍니다.
LDR R0, [R1], R3, LSL #2 @ R1 주소에 있는 값을 R0에 넣고,
@ R1에 R3, LSL #2한 값을 더합니다.
STR R0, [R2], -R3, ASR #2 @ R2 주소에 있는 값을 R0에 넣고,
@ R2에 R3, ASR #2한 값을 뺍니다.
예)
LDR R1, 0x1000 @ R1에 상수 0x1000을 넣습니다.
@ R1 : 0x1000
LDR R0, [R1], #4 @ R0에 0x1000를 참조한 값(R1의 메모리 참조주소)을 넣고,
@ R1에 4를 더합니다.
@ R1 : 0x1004
LDR R2, [R1], #4 @ R2에 0x1004를 참조한 값(R1의 메모리 참조주소)을 넣고,
@ R1에 4를 더합니다.
@ R1 : 0x1008
LDR, STR Pre Indexing
Pre Indexing 사용 방법:
LDR R0, [R1], #4 @ R1에 4를 더한것을 참조하여 R0에 넣습니다.
STR R0, [R2], #-4 @ R0의 값을 R2에 4를 뺀 주소에 넣습니다.
LDR R0, [R1], R3 @ R1에 R3의 값을 더한것을 참조하여 R0에 넣습니다.
STR R0, [R2], -R3 @ R0의 값을 R2에 R3의 값을 뺀 주소에 넣습니다.
LDR R0, [R1], R3, LSL #2 @ R1에 R3, LSL #2한 값을 더한뒤,
@ 주소에 있는 값을 R0에 넣습니다.
STR R0, [R2], -R3, ASR #2 @ R2에 R3, ASR #2한 값을 뺀뒤,
@ R0의 값을 변경된 주소로 넣습니다.
예)
LDR R1, 0x1000 @ R1에 상수 0x1000을 넣습니다.
@ R1 : 0x1000
LDR R0, [R1, #4] @ R1에 4를 더한 주소의 참조한 값(0x1004)을 R0에 넣습니다.
@ R1 : 0x1000
LDR R2, [R1, #4] @ R1에 4를 더한 주소의 참조한 값(0x1004)을 R2에 넣습니다.
@ R1 : 0x1000
Pre Indexing에서는 값이 업데이트 되지 않습니다! 만약 값을 업데이트하고 싶다면 아래의
!(Auto Update) 옵션을 사용해야합니다.
‘!(Auto Update) suffix’ 사용 예시)
LDR R1, 0x1000 @ R1에 상수 0x1000을 넣습니다.
@ R1 : 0x1000
LDR R0, [R1, #4]! @ R1에 4를 더한 주소의 참조한 값(0x1004)을 R0에 넣습니다.
@ ! suffix가 있으므로, 자동으로 값이 업데이트 됩니다.
@ R1 : 0x1004
LDR R2, [R1, #4] @ R1에 4를 더한 주소의 참조한 값(0x1008)을 R2에 넣습니다.
@ ! suffix가 없으므로, 자동으로 값이 업데이트 되지 않습니다.
@ R1 : 0x1004
LSL, LSR Shift
LSL #n: 최후에 밀려난 비트가CPSR(상태레지스터)의 C(Carry) flag에 저장- Logical Shift Left
LSL:signed/unsigned곱하기 2
LSR #n: 최후에 밀려난 비트가CPSR(상태레지스터)의 C(Carry) flag에 저장- Logical Shift Right
LSR:unsigned나누기 2
ASR Shift
ASR #n: MSB(부호)를 유지하고, 밀려난 비트가CPSR(상태레지스터)의 C(Carry) flag에 저장- 레지스터를 우측으로 지정한 비트 수 만큼 부호를 유지하며 Shift합니다.
ASR은signed int의 나누기 2 동작을 수행합니다.
비트제어 명령어
-
ORR: 원하는 비트만 1로 설정합니다.- 예)
ORR R0, R0, #0x1F,ORR R0, R1, R2, LSL #2
- 예)
-
EOR: 원하는 비트만 반전 시킵니다.- 예)
EOR R0, R0, #0x1F,EOR R0, R1, R2, LSL #2
- 예)
-
AND: 원하는 비트만 0으로 설정합니다.- 0인 비트에 0으로 설정합니다.
- 예)
AND R0, R0, #0x1F,AND R0, R1, R2, LSL #2
-
BIC: 원하는 비트만 0으로 설정합니다.- 1인 비트에 0으로 설정합니다.
AND보다 편리한 장점이 있습니다.- 예)
BIC R0, R0, #0x1F,BIC R0, R1, R2, LSL #2
다양한 비트연산
- [문제]
0x1000의 4~7번 비트를1010으로 변경하는 어셈블리 코드를 작성하세요.

LDR R0, =0x1000 @ R0에 상수 0x1000을 넣습니다.
LDR R1, [R0] @ R1에 R0을 참조한 값을 넣습니다.
BIC R1, R1, 0xF << 4 @ '1111'을 4번 Left Shift하면 '11110000'가 됩니다.
@ BIC로 R1의 4~7 비트를 0으로 설정합니다.
ORR R1, R1, 0xA << 4 @ '1010'을 4번 Left Shift하면 '10100000'가 됩니다.
@ ORR로 R1의 5, 7번 비트를 1로 설정합니다.
STR R1, [R0] @ R1의 데이터를 R0에 저장되어있는 메모리주소에 저장합니다.
분기 명령어
B명령 사용 예시:B add: Global LabelB 1f: Local LabelB .: 무한 루프 (.은 제자리를 말합니다.)
B R0와 같이B명령은 분기 주소로 레지스터를 쓸수 없습니다.
비교 연산 명령어
CMP:CMP Rs, OP2Rn - OP2연산을 하여CPSR(상태레지스터)의 Flag만 변경합니다.
CMN:CMN Rs, OP2Rn + OP2연산을 하여CPSR(상태레지스터)의 Flag만 변경합니다.- 컴파일러단에서 주로 사용되니, 되도록이면
CMP를 사용하자.
TST:TST Rs, OP2Rn & OP2연산을 하여CPSR(상태레지스터)의 Flag만 변경합니다.- 특정비트를 확인하기 위해 사용됩니다.
- 비교 비트가 1이면, Z clear, 0이면 Z set
TEQ:TEQ Rs, OP2Rn ^ OP2연산을 하여CPSR(상태레지스터)의 Flag만 변경합니다.- 두 데이터의 비트가 완전 똑같은지 확인하기 위해 사용됩니다
- 비트 패턴이 동일하면, Zero set
비교연산은 레지스터에 변화가 없으며, Flag만 변합니다.
-
[문제]
R0의 7번 비트가1이면R1레지스터에 2를 기록하고,0이면R1레지스터에 3을 기록하는 프로그램을 작성하시오. -
AND를 사용하여 해결:
AND R0, R0, #1 << 7
CMP R0, #0
MOVEQ R1, #3
MOVNE R1, #2
TST를 사용하여 해결:
TST R0, #1 << 7
MOVEQ R1, #3
MOVNE R1, #2
CPSR(상태레지스터)과 Flag

비교 연산을 할때는 NZCV만 사용한다
- N (Negative) : 연산결과가 음수인 경우
- Z (Zero) : 연산결과가 0인 경우
- C (Carry) : 덧셈 Carry에 Set, 뺄셈 Borrow에 Clear, Rotate시 밀린 비트 저장
- V (oVerflow) :
signed덧셈, 뺄셈 연산 결과로 값의 초과가 발생한 경우
Carry는 덧셈을 했을때 범위를 초과해서 자리 올림이 발생했을때를 말하는것입니다. Overflow와는 다릅니다.
상태 플래그와 조건
CMP R0, R1
B□□ LOOP
□ 부분에 아래의 16가지 조건식이 들어가게 됩니다.
| 약어 | 뜻 | Flag 상태 |
|---|---|---|
EQ | Equal / Equals zero (False) | Z set |
NE | Not Equal (True) | Z clear |
CS/HS | Carry Set / Unsigned higher or same | C set |
CC/LO | Carry Clear / Unsigned Lower | C clear |
MI | Minus / Negative | N set |
PL | Plus / Positive or zero | N clear |
VS | Overflow | V set |
VC | No Overflow | V clear |
HI | Unsigned higher | C set and Z clear |
LS | Unsigned lower or same | C clear and Z set |
GE | Signed greater then or equal | N equals V |
LT | Signed less than | N is not equals V |
GT | Signed greater than | Z clear and N equals V |
LE | Signed less than or equal | Z set or N is not equal to V |
AL | Always | Any state |
NV | Never | None |
- 주소 연산을 할때는
HI,LO,HS,LS와 같은Unsigned비교를 사용합니다.
산술 명령어
-
ADD :
ADD Rd, Rs, OP2Rd := Rs + OP2
-
ADC :
ADC Rd, Rs, OP2Rd := Rs + OP2 + Carry
-
SUB :
SUB Rd, Rs, OP2Rd := Rs - OP2
-
SBC :
SBC Rd, Rs, OP2Rd := Rs - OP2 - !Carry
-
RSB :
RSB Rd, Rs, OP2Rd := OP2 - Rs
-
RSC :
RSC Rd, Rs, OP2Rd := OP2 - Rs - !Carry
-
RSB,RSC에 있는 R은 Reverse라고 생각합시다.
일정 횟수를 반복하는 프로그램
for(i=0; i<10; i++)방식
MOV R0, #0
1:
@ 이곳에 코드 작성
ADD R0, R0, #1
CMP R0, #10
BLT 1b
for(i=10; i>0; i--)방식
MOV R0, #10
1:
@ 이곳에 코드 작성
SUB R0, R0, #1
CMP R0, #0
BGT 1b
위의 두 방법도 좋지만, Down Count 방식에 S-Suffix를 사용하는게 더 효율적입니다.
MOV R0, #10
1:
@ 이곳에 코드 작성
SUBS R0, R0, #1
BGT 1b
서브루틴 호출
BL label: Branch and LinkBL: 서브루틴 호출 명령label: 서브루틴의 상대주소 값B와 달리 복귀한 주소(따음 명령 주소) 값을R14에 저장합니다.

BL은 분기 주소로 레지스터를 사용하지 못합니다.
서브루틴 호출과 복귀
-
서브루틴 호출 (
BL label):- 복귀할 주소를
LR(R14)에 저장합니다. - 서브루틴의 주소를
PC(R15)에 저장합니다.
- 복귀할 주소를
-
서브루틴 복귀 (
BX LR):LR(R14)의 값을PC(R15)에 저장하여 복귀합니다.
-
왜 함수라고 하지 않고 서브루틴이라고 부를까?
- 함수는 다른 값들에 영향을 주지 않지만, 서브루틴은 다른 값들에 영향을 줄수 있습니다.
특수용도 레지스터
| 레지스터 | 설명 | 용도 |
|---|---|---|
SP / R13 | Stack Pointer | C언어 사용시 스택의 주소를 저장 |
LR / R14 | Link Register | 함수 호출 시 되돌아갈 함수의 주소가 저장 |
PC / R15 | Program Counter | 프로그램 수행 시 읽어오는 명령의 주소를 저장 |
| CPSR | Current Program Status Register | 연산결과, IRQ, FIQ금지, 동작모드 등을 저장 |