정말 오랜만에 강좌를 쓰는군요. 생활에 여유가 좀 있어서 강좌를 시작했는데 예상보다 강좌가 길어지고 더불어 점점 다른 일들이 생겨서 요즘에는 통 시간을 낼 수가 없었습니다. 혹, 강좌를 기다리신 분이 계시다면 죄송하다는 말씀을 드려야겠군요. 아무래도 강좌를 빨리 정리해야겠다는 생각이 듭니다. 자꾸 늘어지니까, 저도 부담스럽고, 읽으시는 분들에게도 폐를 끼치는 것 같아서.
남은 부분에 대해서는 최대한 핵심만 얘기하려 합니다.
사실 명령어 하나하나에 대해 하나하나 설명한다는 것이... 뭐 그다지 의미 있는 일 같지도 않고, 이쯤까지 왔다면 대충 어떤 명령어들이 있는지 파악하시고, 나중에 필요하실 때 데이터시트 찾아보시면 될 것 같아요.
정리를 해보자면, 이제 말씀드릴 명령은
곱하기 명령 (MUL,MLA),
데이터 전송 명령 (LDR, STR),
블록 데이터 전송명령 (LDM, STM),
기타 명령(SWP, SWI)
정도입니다. Coprocessor 명령은 생략하도록 하고, 명령이 끝나면 실전 케이스로 간단한 예제 설명을 하나 드리려고 하고, 마지막으로 StrongARM의 Spec 정도를 간단히 언급하겠습니다.
남은 부분이 앞으로 몇 번에 걸쳐서 매듭지어질지는 해봐야겠지만, 최대한 스피디한 진행을 하려 합니다.
차후에 기회가 되면 MMU 부분 같은 것들을 짧게 다룰 수도 있겠지요.
자, 이제 시작하겠습니다.
● 곱하기 명령(MUL, MLA)
ARM7에서의 곱하기 명령은 크게 두 종류가 있습니다. 하나는 그냥 곱하는 것이고, 다른 하나는 곱해서 더하는 것입니다. ^^ 명료하죠?
1) MUL{cond}{S} Rd,Rm,Rs
cond 부분은 해당 명령의 실행 조건입니다. S플래그는 결과가 플래그 레지스터에 영향을 미칠지의 여부라고 계속 말씀 드렸죠? 다음으로 레지스터 3개를 지정하도록 되어있는데, Rd는 결과가 저장될 레지스터이고, Rm과 Rs는 곱해질 두개의 레지스터입니다. 보셨듯이 이 명령에서는 레지스터만을 사용해야 합니다.
Ex) MUL R1,R2,R3 ; R1:=R2*R3
# 참고로 곱하기의 결과가 32비트를 넘는다면, 하위 32비트만 결과 레지스터에 남습니다.
2) MLA{cond}{S} Rd,Rm,Rs,Rn
위와 비슷합니다만, 이번에는 레지스터가 4개입니다. 단순 명료하게 의미를 말씀드리면,
Rd := Rm * Rs + Rn 의 의미입니다.
● Single Data Transfer (LDR, STR)
해당 명령은 레지스터와 외부 메모리와의 데이터 전송을 담당하는 명령입니다. 무척 사용빈도가 높은 명령입니다. 86에서는 MOV하나로 레지스터간의 데이터 전송과, 외부메모리와의 데이터 전송의 두 가지 목적으로 사용하지만, ARM7 에서는 구분이 되어있습니다.
즉, MOV명령은 레지스터간의 전송명령으로 분류도 데이터 처리명령으로 분류되고, 데이터 전송명령으로는 LDR과 STR이 있습니다.
여기에 사용되는 옵션이 많은데요, 우선, LDR은 로드라는 의미이므로 외부 메모리로부터 레지스터로 데이터를 읽어오는 명령이고, STR은 반대의 기능을 하는 명령입니다.
- 명령 형식
LDR{cond}{B} Rd, address{!} Rd:= contents of address
LDR{cond}{B} Rd, =expression Rd:= expression
STR{cond}{B} Rd, address{!} contents of address := Rd
LDR과 STR은 Single Data 전송 명령입니다. 비슷한 명령으로 LDM, STM은 여러 개의 레지스터 내용을 전송할 수 있는 명령입니다. 이밖에 SWP라는 스왑 명령이 있는데, 해당 5개의 명령만이 외부 메모리와 레지스터간의 전송을 가능하게 하는 명령입니다.
LDR과 STR의 경우 전송 단위를 바이트, 혹은 워드(32Bit)단위로 수행 할 수 있습니다. 바이트 단위 전송의 경우 해당 레지스터의 어떤 부분이 사용될지의 여부는 해당 프로세서의 Endian에 달려 있습니다. 또, 워드 전송 명령을 사용할 경우, 메모리 어드레스는 Word align이 되어야 합니다.
다음 표는 LDR과 STR에서 사용하는 전송 모드를 나타낸 것입니다.
--------------------------------------------------------------------
Mode Effective address Indexing
--------------------------------------------------------------------
[Rn] Rn none
[Rn,+- expression] Rn +- expression Pre-indexed
[Rn,+- Rm] Rn +- Rm Pre-indexed
[Rn,+- Rm, shift cnt] Rn+-(Rm shifted by cnt) Pre-indexed
[Rn],+-expression Rn Post-indexed
[Rn],+-Rm Rn Post-indexed
[Rn],+-Rm,shift cnt Rn Post-indexed
--------------------------------------------------------------------
# Rn : base 주소를 가지고 있는 레지스터
Rm : r15를 제외한 레지스터(Offset), 부호 값 사용
expression : -4095 - +4096 범위의 Immediate 값(12Bit)
shift : LSL, LSR, ASR, ROR, RRX
cnt : 1..31 사이의 값.
상당히 복잡한 내용입니다. 표에서 Mode 부분이 실제 어셈블러에서 사용하는 명령의 형식이라고 보시면 됩니다. 각 모드별로 예제를 들어서 설명하도록 하죠.
1) Pre-Indexed Addressing Mode
이 모드에서는 Rn을 베이스 주소로 사용합니다. 여기에 더해서 Offset을 지정하거나, 혹은 지정하지 않을 수 있습니다. Offset을 사용할 경우에는 해당 Offset을 베이스 주소에서 더하도록 하거나 뺄 수 있는데, 위의 표에서 +, - 기호가 그것을 의미합니다.
Offset은 부호가 있는 12Bit Immediate값을 사용하거나 레지스터를 지정해서 해당 레지스터의 내용으로 사용할 수 있습니다. 또한 레지스터를 Offset으로 사용하는 경우에는 Shift 시켜서 적용시킬 수 있습니다. 관련 내용을 다시 확인하고 싶으신 분들은 데이터 전송명령의 오퍼랜드 2에 대한 설명을 다시 확인하시길 바랍니다.
Ex) LDR r0,[r1] ; r1을 베이스로 사용하고 Offset은 지정하지 않는 형태입니다. r0에 r1을 번지로 하는 워드(4바이트)를 읽어드립니다.
LDR r0,[r1,#132] ; 이번에는 Offset으로 132를 지정한 형태입니다.
STR r0,[r1,r2] ; 베이스는 r1, Offset은 r2를 사용한 형태입니다. 즉
*(r1+r2)=r0 와 같은 의미.
LDR r0,[r1,r2,LSL #2] ; 베이스 r1, Offset은 r2<<2 의 형태.
이번에는 Write-Back 기능에 대해서 알아보겠습니다. LDR명령의 경우 옵션으로 Write-Back을 지정할 수 있는데, 지정할 경우 원래 베이스에 Offset을 더한 값을 다시 베이스 레지스터로 넣는 기능을 합니다. 제 생각에는 이 기능이 C에서 ++와 같은 의미라 생각되는데요. 글쎄, 쓰기 나름이겠지요. 다음은 예제입니다.
Ex)
1: LDR r0,=table_end
2: LDR r1,=table
3:
4: MOV r2,#0
5:loop STR r2,[r0,#-4]!
6:
7: ADD r2,r2,#1
8: CMP r0,r1
9: BNE loop
10:
11: ...
12:
13: ALIGN
14:table % table_length*4
15:table_end
예제가 좀 길군요. 주의해서 보실 부분은 5번 라인입니다. 해당 명령은 STR r2,[r0,#-4]! 이죠. r2를 r0를 베이스로 삼고 Offset으로 -4값을 사용했군요. r0가 가리키는 포인터가 실제 버퍼로 사용할 공간이 끝난 바로 다음을 가리키고 있으므로 -4를 하면 마지막 워드 엔트리 포인트가 됩니다. 그리고 주의하실 점은 !를 사용한 것인데요. 이것이 바로 Write-Back기능입니다. 위의 설명에 나와있듯이 베이스 값(여기서는 r0가 되겠군요.)을 갱신하는 것인데요...
예를 들어 1000번지부터 2개의 entry를 가정한다면, table은 1000이 될 것이고 table_end는 1008 번지가 될 것입니다. 이때 STR명령에서 1008을 베이스로 -4를 Offset으로 설정했으므로 실제로 r2값은 1004번지부터 4바이트에 기록이 될 것입니다. 이후 Write-Back 옵션이 적용되어 해당 명령이 끝나면 r0값은 1004로 (1008-4) 바뀌게 됩니다.
참, 여기서 Pre-Index Mode이기 때문에 실제 주소를 구할 때 Offset을 먼저 빼 주었군요. 아무쪼록 이해 하셨기를 바라며 다음으로 넘어가렵니다.
2) Post-Indexed Addressing Mode
Post인덱스 모드의 경우에는 Pre인덱스 모드에 비해 Effective Address는 항상 Rn, 즉 베이스 어드레스를 나타내는 레지스터의 값입니다. 그리고 한가지 주의하실 점은 Post인덱스 모드의 경우엔 따로 !를 사용 하지 않더라도 디폴트로 Write-Back 모드가 사용됩니다. 그렇겠죠? Post인데 Write-Back이 안된다면, Offset을 사용할 이유가 없군요.
Ex) LDR r0,[r1],r2 ; 대괄호의 사용이 Pre 인덱스의 경우와
다릅니다. 실제 수행되는 것은 r0에다
r1을 주소로 하여 값을 읽어오고 다시
r1에는 r2를 더해주는 일을 합니다.
STR r0,[r1],#20 ; [r1]:=r0, r1:=r1+20
1: LDR r0,=table
2: LDR r1,=table_end
3:
4: MOV r2,#0
5:loop STR r2,[r0], #4
6:
7: ADD r2,r2,#1
8: CMP r0,r1
9: BNE loop
10:
11: ...
12:
13: ALIGN
14:table % table_length*4
15:table_end
마지막 예제는 위에서 나왔던 예제와 비슷하니 잘 분석해 보시길 바랍니다.
3) Relative Addressing Mode
해당 Addressing 모드는 어셈블러가 적절히 지원하여 변환해 주는 모드라고 생각하시면 됩니다.
Ex) LDR r5,ThreeCubed
...
ThreeCubed DCD 27
위와 같은 경우 실제로는 LDR r5,[PC,#constant] 형태의 코드로 번역해 줍니다. 즉 PC를 베이스로 삼아서 코드를 만드는 것이지요. 만약 해당 Symbol이 지정하는 범위가 너무 커서 상수로 지정할 수 없다면 어셈블을 할 때 에러를 내게 됩니다.
또 많이 사용되는 표현으로 다음과 같은 표현이 있습니다.
Ex) LDR r0,=0x12345678
사실은 위의 예에서도 사용했었는데요. 이 경우 어셈블러가 해당 상수 값을 특정 공간에 모아서 삽입 해 주고 (이공간을 Literal Pool이라 하는군요) 역시 마찬가지로 PC를 베이스로 해서 명령코드를 만듭니다.
자.. 오늘은 이만 쓰렵니다. 사실 열두번째 강좌는 쓰기 시작한지 몇 주만에 끝내게 되는군요. 중간에 계속 일이 생겨서. 미뤄두고 있다가, 메일로 강좌를 재촉하시는 분들이 계셔서.
아무튼, 다음강좌에 뵙도록 하겠습니다.