load / store 명령어
ldr
ldr r1, =0x20000148
ldr r0, [r1]
r1에 주소를 저장하고
r0에 해당 주소에 있는 값을 저장
str
str r1, =0x20000154
str r0, [r1]
r1에 주소를 저장하고
r0의 값을 해당 메모리주소에 저장
ldrb
메모리에서 1byte만 읽을 때
ldr r1, =0x20000148
ldrb r0, [r1]
ldrb에서의 b는 byte를 의미함
r1에 주소를 저장하고
해당 주소에서 1byte를 읽어서 r0에 저장
pre-indexed addressing mode
ldr r1, =0x20000140
ldr r0, [r1, #8]
실제 접근하는 주소는 0x20000148이 된다.
post-indexed addressing mode
ldr r1, =0x20000140
ldr r0, [r1, #8]!
r1을 offset만큼 먼저 더하고
해당 주소에서 데이터를 가져옴
!를 auto updator라고 부름 -> C언어에서 ++i와 유사
ldr r1, =0x20000140
ldr r0, [r1], #8
r1이 가리키는 곳에서 데이터를 가져온 후,
r1을 offset만큼 더해서 업데이트 -> C언어에서 i++와 유사
메모리 복사 예제
1word 단위로 복사
.global MEMCPY_4BYTE_PRAC
MEMCPY_4BTYE_PRAC: @ r0=dst, r1=src, r2=size:16
push {lr}
loop2:
ldr r3, [r1], #4 @ 4byte씩 더해가니까 1word만큼 이동시키는 것을 알 수 있음
str r3, [r0], #4
sub r2, #1
cmp r2, #0
bgt loop2
pop {pc}
r3로 복사 시작하려는 주소에 있는 값을 가져온다음 주소에 4를 더한다. (주소를 4씩 증가시켜야 4바이트씩 증가함)
그리고 r3에 있는 값을 주소 r0에 저장시키고 r0주소를 4증가시킨다.
이렇게하면 주소를 4바이트씩 건너뛰게 된다.
16번 반복하면 된다.
이외 생략.
1byte 단위로 복사
.global MEMCPY_SINGLE_PRAC
MEMCPY_SINGLE_PRAC: @ r0=dst, r1=src, r2=size:16
push {lr}
lsl r2, #2 @ word단위를 byte단위로 환산하기 위함: 곱하기4 해야함
loop2:
ldrb r3, [r1], #1
strb r3, [r0], #1
sub r2, #1
cmp r2, #0
bgt loop2
pop {pc}
1word 복사랑 논리는 같지만,
lsl r2, #2가 추가되었다.
위의 1word 복사는 word단위로 읽어오기 때문에 size에서 1씩 빼가며,
총 16번 반복하면 된다.
하지만 1byte 복사는 lsl r2, #2로 size에 4를 곱해줘야한다.
1byte씩 복사보다 1word씩 복사가 더 빠를 것을 예측할 수 있다.
push / pop
.global MEMCPY_4BYTE_PRAC
MEMCPY_4BTYE_PRAC: @ r0=dst, r1=src, r2=size:16
push {lr}
loop2:
ldr r3, [r1], #4
str r3, [r0], #4
sub r2, #1
cmp r2, #0
bgt loop2
pop {pc}
이 코드에서 보면 시작구간에서 push {lr}로 lr레지스터를 스택메모리에 넣고
마지막 구간에서는 pop {pc}로 스택메모리에서 pop한다음 pc(프로그램 카운터)레지스터에 넣는다.
pc는 다음번에 실행할 코드의 주소를 가리키므로 복귀주소가 다음에 실행할 코드의 주소가 된다.
이걸 왜 할까??
- ARM 아키텍처에서 함수가 호출될 때, lr (Link Register)에 복귀할 주소(리턴 주소)가 자동으로 저장
- 하지만 함수 내부에서 다른 함수를 호출하거나, lr을 중간에 사용하면 복귀 주소가 덮어써질 수 있기 때문에, 복귀 주소를 스택에 백업
- 마지막에 pop {pc}를 사용하면, 저장해둔 복귀 주소를 프로그램 카운터(pc)에 넣게 되어, 함수가 끝난 뒤 원래 위치로 정확하게 복귀
bx lr 명령어는 제한된 환경에서만 제대로 동작하므로
그냥 push / pop으로 return을 하자
블록 단위로 복사
ldrmia
ldrmia r0, {r2,r3,r4}
or
ldrmia r0, {r2-r4}
r0주소에서 데이터 읽어서 레지스터 2, 3, 4에 저장
strmia
strmia r1, {r2,r3,r4}
or
strmia r1, {r2-r4}
메모리의 r1주소를 시작으로 레지스터 2, 3, 4의 데이터를 저장
블록 단위 복사 예제
.global MEMCPY_BLOCK_PRAC
MEMCPY_BLOCK_PRAC: @ r0=dst, r1=src, r2=size
push {r4-r6, lr}
loop6:
ldmia r1!, {r3-r6} @ r1에서 읽어와서 레지스터 3,4,5,6에 저장
stmia r0!, {r3-r6} @ 레지스터 3,4,5,6의 값들을 주소 r0부터 저장
sub r2, #4 @ 루프한번에 4word씩 돈다. why? r3 r4 r5 r6이렇게 read/write하니까
cmp r2, #0
bgt loop6
pop {r4-r6, pc}
블록 단위 복사 이점
DRAM은 한 번 메모리 접근할 때 연속된 주소 블록을 한꺼번에 읽거나 쓸 수 있는 burst 모드를 지원.
블록 단위 복사는 연속적인 메모리를 한번에 읽기 때문에, 효율적이고 빠름
'ARM arch > Cortex-M' 카테고리의 다른 글
| NVIC, Exception Vector Table (0) | 2025.07.16 |
|---|---|
| cortex-m3 AMBA와 메모리 맵 (0) | 2025.07.14 |
| 2025.07.07 - ARM assembly 프로그래밍1 (0) | 2025.07.08 |
| 빅 엔디언, 리틀 엔디언 (0) | 2025.07.02 |
| AAPCS (0) | 2025.06.30 |