ARM 프로세서에는 특이하게 7개의 모드가 존재한다.
동작모드는 프로세서가 어떤 권한을 가지고 무슨 일을 처리하는지 나타내는 프로세서의 동작상태를 일컷는다.
이 7개의 Mode서, 각 모드가 따로 SP(Stack Point)를 갖고 있는다. 
(User와 System은 갖은 Stack 사용, 즉 6개의 Stack이 사용된다.)

 

Mode

Description

CPSR M[4:0]

User

User Task나 Application을 수행할때의 동작모드로 모든 동작모드중유일하게 비특권 모드이다. User Mode는 메모리, I/O장치와 같은 시스템 자원을 사용하는데 제한을 두어 사용자의 실수를 방지한다. 다른 모드(SVC)로 이동하기 위한 방법으로는 소프트웨어 인터럽트를 발생시킨다.

10000b

FIQ

(Fast IRQ)

2개의 인터럽트 소스중 아주 빠르게 인터럽트를 처리할 수 있도록 구성된 모드. 빠른 처리를 위해서 Exception Vector에서도 최하단에 존재하고 별도의 레지스터를 소유한다.

10001b

IRQ

(Interrupt Request)

일반적으로 사용되는 인터럽트로 외부 장치에서 요청되는 하드웨어적인 IRQ의 발생에 의해 ARM 코어는 IRQ모드로 전환하고 인터럽트를 처리한다.

10010b

SVC

(Superviser)

대부분의 시스템 자원을 자유롭게 관리할 수 있는 동작모드로 주로 커널이나 디바이스 드라이버를 처리할 때(System Call) 동작되는 모드이다.
Reset신호가 입력되거나 SWI가 발생하면 SVC모드로 전환된다.

10011b

Abort

메모리에서 명령을 읽거나 데이터를 읽거나 쓸 때 오류가 발생할 때 Abort Mode로 전환하여 오류를 처리한다.

10111b

Undefined

명령어를 읽어 실행하고자 하나 읽어온 명령이 디코더에 정의되어 있지 않은 명령인 경우 발생되는 오류를 처리하는 모드이다.

11011b

System

User 모드와 동일한 Register를 사용하고 동일한 용도로 사용된다. User 모드와의 차이점은 특권을 갖고 있다는 것이다. 
(ex : OS Kernel)

11111b

 

위 표에서 우측의 5bit짜리 정보는 CPSR이라는 상태 Register내에 저장된 정보이다. CPSR은 현재 모드의 상태를 저장하고 있는데, 그중 우측 0~4bit 총 5bit는 각 모드의 정보를 담고 있다. 32bit중 나머지 bit는 N,Z,C,V로 컨디션 코드 및 기타 정보로 사용된다. (Register 부분에서 언급예정)

인터럽트나 에러 또는 프로그래머에 의하여 모드가 변경될 수 있다.
다음에는 Exception에 대해 알아 볼 것인데 Exception에 의해서 모드가 변경되기 때문에 각 모드의 특징에 대해 알고 있어야 한다.

System/User/SVC Mode가 은근히 헷갈리고 명확히 정의내리기 애매했었다.
System과 User는 같은 Stack을 이용하여 거의 비슷하나, User Mode에서는 Device등의 자원을 사용하는데 제한이 주어진다. SVC Mode는 처음 Reset되었을 때 접근하는 Mode로 시스템 자원을 자유롭게 관리가 가능하며 별도의 Stack을 이용한다.
User Mode는 OS상에 올라가는 Application으로 생각하고,
OS는 기본적으로 System Mode,
OS나 Application이 시스템 자원(I/O등)을 사용할때는 SVC모드로 바뀐후 사용한다고 보면 된다.

이때, SVC모드로 바뀌어서 시스템 자원을 사용할 권한을 획득하는 것을 System Call이라고 지칭한다. (추후 자세히)


※ 이러한 모드가 눈에 보이는 것이 아니라, 개념적이고 추상적인 부분이라 정의를 명확히 해야한다. 현재 모드를 확인하려면 CPSR의 5bit를 출력해 봐야 비로소 눈으로 확인이 가능하다 -.,-; (가끔 헷갈려서 필자도 몇번 레지스터를 출력해보았다^_^)


출 처 : http://shinluckyarchive.tistory.com/270


1. 개요

    이 문서는 GNU 어셈블러 문법에서 지시자로 사용되는 의사 명령어를 간단히 정리 한 문서이다. 
    더 많은 참조를 원한다면 다음 사이트를 방문한다.
    
        http://tigcc.ticalc.org/doc/gnuasm.html#SEC70
    
2. 데이터 지시자 

    .byte   <byte1> {,<byte2>} ...      : 8  비트 데이터 
    .hword  <short1> {,<short2>} ...    : 16 비트 데이터 
    .word   <word1> {, <word2>} ...     : 32 비트 데이터 
    .ascii  "<string>"                  : 문자열 
    .asciz  "<string>"                  : 문자열 : 마지막에 자동으로 NUL 문자인 0을 추가 한다. 
    .space  size[, fill]                : 주어진 바이트 수를 할당한다. 바이트들은 0 이나 fill 값으로 채워져 있다. 

3. 상수 또는 심볼릭 관련 지시자 

    .equ <symbol name>, <value>                 : 심볼값을 정의 한다. 
    <register_name> .req <register_name>        : 레지스터의 이름을 짓는다. 
    .global <symbol>                            : 심볼을 외부 참조가 가능하게 한다 
    .set <variable_name>, <variable_value>      : 변수 값 설정
    

4. 코드 블럭 관련 지시자 
    
    .code <number_of_bits>    : 명령어 폭을 비트로 설정한다.

                                                Thumb 어셈블리에서는 16을, ARM 어셈블리에서는 32를 사용한다. 
    .section name[, "flags"]  : 새로운 코드 섹션 또는 데이터 섹션을 지정한다. 
                                섹션 명은 링크 디스크립터에 정의 되어 있어야 한다. 일반적으로 다음항목을 사용한다.
                                   .text : 코드 섹션
                                   .data : 초기화된 데이터 섹션
                                   .bss  : 초기화되지 않은 데이터 섹션. 
                                flags 는 ELF 포멧에서 
                                   a     : 섹션 할당
                                   w     : 쓰기 가능한 섹션
                                   x     : 실행 가능한 섹션


    .align alignment[, [fill][, max]]    : 주소를 2^n 바이트 단위로  정렬한다. 
                                           어셈블러는 fill나 적절한 디폴트 값을 추가하여 정렬을 맞춘다. 
                                           만약 max보다 더 많은 fill 바이트가 필요하다면 정렬되지 않는다.
                                                  
     .rept count                         : count 만큼 코드 블록을 반복한다. .endr로 끝난다.
    .endr                                : 반복 루프를 끝낸다. 

5. 매크로 

    .macro name [macargs...]       : name 이름의 어셈블러 매크로를 정의한다. 매크로 정의는 .endm으로 끝난다. 
    .endm                          : 매크로 정의를 끝낸다. 
    .exitm                         : 매크로를 빠져나온다. armasm에서의  유사하다.

6. 조건 컴파일 

    .if absolute_expression        : 컴파일 조건문 시작 .endif를 사용하여 끝낸다. 
    .ifdef symbol                  : symbol 이 정의되어 컴파일 대상에 포함되는 시작 .endif를 사용하여 끝낸다. 
    .else                          : .if와 .endif와 예외 조건이다. 
    .elseif                        : else 와 if 를 합친 것이다. 
    .endif                         : 조건부 컴파일 지정을 끝낸다(.if, .ifdef, .ifndef 참고). 
    .err                           : 에러가 있으면 어셈블리를 정지시킨다.

7. 기타 

    .include "<filename>"          : 지정한 소스 파일을 포함한다

8. GNU ASM 과 ARM ASM 의 대치표

    .ascii      : DCB
    .byte       : DCB
    .hword      : DCW
    .word       : DCD
    .space      : SPACE
    .set        : SETA
    
    .equ        : EQU
    .req        : RN
    
    .code       : CODE16,CODE32
    .sectoin    : AREA
    .balign     : ALAIN
    
    .macro      : MACRO
    .endm       : MEND
    .exitm      : MEXIT
    
    .if         : IF     
    .else       : ELSE
    .endif      : ENDIF
    .include    : INCLUDE


출 처 : http://forum.falinux.com/zbxe/index.php?document_srl=550700&mid=lecture_tip



ABI

application binary interface


EABI

embedded application binary interface


None-EABI

특정 OS에 맞춘 EABI가 아니라는 뜻


ABI는

응용 프로그램 - 운영체제,

응용 프로그램 -  해당 라이브러리,

응용 프로그램의 구성 요소 간에서 사용되는 낮은 수준의 인터페이스이다.,


ABI 와 API 비교

API -> 소스코드

ABI -> 바이너리


결론

난 cross-compile을 할려고 했는데, target platform이 EABI인 arm-none-eabi를 설치했다.

헛 짓을 며칠 하다 위의 내용을 알고 arm-none-gnueabi로 다시 설치.


eabi는 arm-none-gnueabi를 컴파일 할 때 사용 한단다.


arm-none-eabi를 gnu target을 컴파일 하면


/home/kang/다운로드/arm-2013.05/bin/../lib/gcc/arm-none-eabi/4.7.3/../../.. /../arm-none-eabi/bin/ld: warning: cannot find entry symbol _start; defaulting to 00008018

/home/kang/다운로드/arm-2013.05/bin/../lib/gcc /arm-none-eabi/4.7.3/../../../../arm-none-eabi/lib/libc.a(lib_a-sbrkr.o): In function `_sbrk_r':

sbrkr.c:(.text+0x18): undefined reference to `_sbrk'

/home /kang/다운로드/arm-2013.05/bin/../lib/gcc/arm-none-eabi/4.7.3/../../../.. /arm-none-eabi/lib/libc.a(lib_a-writer.o): In function `_write_r':

writer.c:(.text+0x20): undefined reference to `_write'

/home /kang/다운로드/arm-2013.05/bin/../lib/gcc/arm-none-eabi/4.7.3/../../../.. /arm-none-eabi/lib/libc.a(lib_a-closer.o): In function `_close_r':

closer.c:(.text+0x18): undefined reference to `_close'

/home /kang/다운로드/arm-2013.05/bin/../lib/gcc/arm-none-eabi/4.7.3/../../../.. /arm-none-eabi/lib/libc.a(lib_a-fstatr.o): In function `_fstat_r':

fstatr.c:(.text+0x1c): undefined reference to `_fstat'

/home /kang/다운로드/arm-2013.05/bin/../lib/gcc/arm-none-eabi/4.7.3/../../../.. /arm-none-eabi/lib/libc.a(lib_a-isattyr.o): In function `_isatty_r':

isattyr.c:(.text+0x18): undefined reference to `_isatty'

/home /kang/다운로드/arm-2013.05/bin/../lib/gcc/arm-none-eabi/4.7.3/../../../.. /arm-none-eabi/lib/libc.a(lib_a-lseekr.o): In function `_lseek_r':

lseekr.c:(.text+0x20): undefined reference to `_lseek'

/home /kang/다운로드/arm-2013.05/bin/../lib/gcc/arm-none-eabi/4.7.3/../../../.. /arm-none-eabi/lib/libc.a(lib_a-readr.o): In function `_read_r':

readr.c:(.text+0x20): undefined reference to `_read'

collect2: error: ld returned 1 exit status


위와 같은 오류가 나타난다.


target platform 에 대해 더 자세하게는 아랫글을 보도록 하자

EABI/ELF


RTOS systems or "bare metal" systems where no operating system is present. These toolchains should not be used to build Linux kernels or applications.


GNU/Linux


Systems running "full" Linux, i.e., Linux on CPUs with an MMU. Use Sourcery CodeBench to build both the Linux kernel and applications.


출 처 : http://blog.daum.net/wonjin92/37



1) Literal pool
- 영어 의미 그대로 상수(Literal)들이 담겨있는 메모리 특정영역(pool)을 의미한다.
- ARM assemble에서는 ARM 범용레지스터에 '32bit 상수값'을 넣기 위해 사용한다.

2) ARM 범용레지스터에 32bit값 대입하기
LDR{cond}{size} Rd =DATA(32bit)
    ex) LDR        R1,  =0x12345678 ;  데이터 0x12345678을  R1에 대입
- 어셈블러가 '=DATA' 부분을 [PC, #<offset>] 형태로 바꾸고 PC+offset 위치에 있는 'Literal pool'에서
  size만큼 데이터를 읽어 Rd에 저장한다.
- LDR instruction의 디코드표를 보면 offset은 12bit가 할당되어 있으므로, 최대 '4kB' 까지 나타낼 수 있다.
  따라서 Literal pool은 pc값 주소의 4kB안에 있어야 한다.

출 처 :  
http://supsupi.tistory.com/entry/1-Literal-pool%B0%FA-32bit-%C1%D6%BC%D2-%B0%AA-%B7%B9%C1%F6%BD%BA%C5%CD%BF%A1-%BA%D2%B7%AF%BF%C0%B1%E2

각 프로세서별로 별도의 캐시 메모리를 가지고 있는 공유 메모리 멀티프로세서에서, 한 명령어 피연산자의 복제본이 주기억장치에 하나, 그리고 각 캐시 메모리에 하나씩, 여러 개 존재할 수 있다. 이러한 상황에서 피연산자가 변경되면, 나머지 복제본 역시 변경되어야만 한다. 캐시 일관성은 공유된 피연산자의 값이 변경되면, 그 사실이 전체 시스템에 즉시 전파되는 것을 보장하는 하나의 질서이다.

캐시 일관성에는 다음 세 가지의 명확한 단계가 있다.

  1. 모든 쓰기 연산은 즉시 발생하는 것처럼 보인다.
  2. 모든 프로세스들은 독립되어 있는 각 피연산자들의 값이 정확히 같은 순서대로 변경되는 것을 본다.
  3. 다른 프로세스들은, 값들의 순서가 다른 것을 사실이라고 생각하는 피연산자를 볼 수 있을지도 모른다 (이것은 일관성이 없는 행동으로 간주된다).

2 단계의 행동과 3 단계의 행동 둘 모두에서, 프로그램은 틀린 데이터를 볼 수 있다. 컴퓨터 설계자들은 최근, 2 단계 행동을 다루는데 필요한 프로그래밍 규율이, 3 단계 행동을 다루는데 충분하다는 것을 깨닫게 되었다. 그러므로, 어떤 경우에는 같은 컴퓨터에서는 오직 1 단계와 3 단계 행동만이 보일 것이다.

출 처 : http://www.terms.co.kr/cachecoherence.htm

 SPI(Serial Peripheral Interface)는 Motorola사에 의하여 개발되었고 지금은 표준화 되어 전세계적으로 널리 사용하는 근거리용 직렬통신 규격으로서, 마스터(master)와 슬레이브(slave) 사이에서 MOSI, MOSO, SCK, SS 등 4개의 통신선을 사용하는 고속 동기식 직렬통신 방식이다.
 SPI 직렬통신은 반드시 1개의 마스터와 1개의 슬레이브 사이에서만 수행된다. 마스터는 송신 데이터와 함께 클럭 신호를 동시에 슬레이브에게 보낸다. 그러면 슬레이브는 이 클럭을 사용하여 데이터를 수신하면서 동시에 자신의 데이터를 마스터에게 보낸다.
 이와 같이 SPI 직렬통신에서는 마스터가 통신의 제어를 주도하며, 항상 송신 동작과 수신 동작이 동시에 수행되어 전이중 통신이 된다. 따라서, 만약 마스터가 슬레이브에게 데이터를 보내는 것이 목적이라면 마스터가 받은 수신 데이터는 무시하면 되며, 반대로 마스터가 슬레이브로부터 데이터를 가져오는 것이 목적이라면 슬레이브에게 더미 데이터를 송신하면서 수신된 데이터를 읽으면 된다.
 SPI 직렬통신 포트에서는 각각의 통신 채널마다 4개씩의 신호가 사용되는데, MOSI(Master-Out Slave-In) 신호는 마스터에서 서 출력되고 슬레이브로 입력되는 신호이고, MISO(Master-In Slave-Out) 신호는 마스터에서 출력되고 슬레이브로 입력되는 신호이며, SCK(Serial Clock) 신호는 마스터에 의하여 구동되는 직렬전송 클럭 신호이다. 또한 SS(Slave Select) 신호는 마스터가 여러개의 슬레이브 디바이스 중에서 마스터와 서로 데이터를 전송할 1개의 소자를 선택하는 칩선택 신호이다.

도무지 이 내용들이 이해가 잘 안가서 모두 AXD로 돌려보고 올립니다.

이해 안가시면 한번 실제로 해보면 좋을 듯합니다.

그럼 이만^^ 퍼가실때 출처 남겨주시길 바랍니다.

작성자 : realdani

***********************************************************************

Decrement Before : 현제 가리키로 있는 곳에서 상위주소(0번지쪽으로) 가리킨후 실행(STMFD)

LDMDB, LDMEA, STMDB, STMFD 는 모두 r13!,{r2-r3}였을 때

만약 r13에 !가 안붙으면 r13의 값은 변하지 않음

LD일 경우,

처음 r13이 #&14일때 마지막은 #&0C를 가리키고,

r2에 Ox000000C번지의 word, r3에는 Ox0000010번지의 word가 들어감


ST일 경우,

처음 r13이 #&14일때 마지막은 #&0C를 가리키고,

r2의 내용이 Ox0000000C번지에, r3의 내용이 Ox00000010번지에 저장됨

**********************************************************************

Decrease Before

LDMDA,LDMFA,STMDA,STMED 모두 r13!,{r2-r3}였을 때

만약 r13에 !가 안붙으면 r13의 값은 변하지 않음

LD인 경우,

처음 r13이 #&14일때 마지막은 #&0C를 가리키고,

r2에는 0x00000010번지의 내용이 들어가고,

r3에는 0x00000014번지의 내용이 들어감,


ST의 경우,

처음 r13이 #&14일때 마지막은 #&0C를 가리키고

r2의 내용이 0x00000010번지에 저장

r3의 내용이 0x00000014번지에 저장


**********************************************************************

Increment Before

STMIB,STMFA,LDMIB,LDMED 모두 r13!,{r2-r3}였을 때

만약 r13에 !가 안붙으면 r13의 값은 변하지 않음

LD일 경우,

처음 r13이 #&14일때 마지막은 #&1C를 가리키고,

r2에 0x00000018번지의 word내용이저장, r3에 0x0000001c의 (내용)word가 저장


ST일 경우,

처음 r13이 #&14일때 마지막은 #&1C를 가리키고,

r2의 내용이 0x00000018번지에, r3의 내용이 0x0000001c에 저장됨



**********************************************************************

Increment After

STMIA,STMEA,LDMIA,LDMFD 모두 r13!,{r2-r3}였을 때

만약 r13에 !가 안붙으면 r13의 값은 변하지 않음

LD일 경우

r13은 0x00000014 -> 0x0000001C로 변함

r2에 Ox00000014번지 내용이 저장

r3에 Ox00000018번지 내용이 저장


ST일 경우

r13은 0x00000014 -> 0x0000001C로 변함

Ox00000014번지에 r2내용이 저장

Ox00000018번지에 r3내용이 저장


출처 : http://blog.naver.com/realdani?Redirect=Log&logNo=100007759984

 Most chips basded on the ARM architecture are comlex on-chip systems, in which most of the hardware modules, whose working statuses are set by softwares, are configurable. Right after the system starts up, before running applications, a block of codes has to be executed to initialize the system. Generally speaking, the initialization should consist of the following steps:
* Interrupt vector table
* Initializing storage system
* Initializing stack
* Initializing the ports and devices that have special requirements
* Initializing excution environment for  user's applications
* Changing the processor mode
* Running main applications

Interrupt vector table
     The interrupt vector table must be located in the space that starts from address “0” and extends for 8X4 byte.
     Every time when an interrupt occurs, the ARM processor forcibly sets the PC pointer to be the address value of the related interrupt type in the vector table. Since each interrupt only takes up the storage space of one character in the vector table, an ARM instruction must be placed to make the program jump to other spaces in the storage, and then the interrupt can be executed.

     The source program of interrupt vector table is as below

AREA Boot ,CODE, READONLY 
ENTRY 
B  ResetHandler 
B  UndefHandler 
B  SWIHandler 
B  PreAbortHandler 
B  DataAbortHandler 

B  IRQHandler 
B  FIQHandler 

     In these codes, the keyword “ENTRY” is used so that these codes might not be regarded as redunancy and therefore optimized by the compiler.

     Make sure to link this code to the address “0” and make it to be the entry point of the whole program.


Initializing storage system
    Storage types and timing configuration:

    Generally, Flash and SRAM are both static storage devices, they can share the same storage interface. While DRAM, due to its specific characters such as dynamic refreshing and address line multiplexing, must be equipped with dedicated storage interface.

 ARM의 의사(Pseudo) 명령은 실제로 ARM의 명령어 디코더에 정의되어 있는 명령이 아니고, 어셈블러에서 지원하는 명령이다.

 

종류

ADR, ADRL, NOP, ALIGN, DCx

 

ADR : 어드레스를 읽어오는 명령

ADR{cond} Rd, <expression>

{cond}는 조건부 실행을 위한 조건을 나타낸다.

Rd는 ARM의 범용 레지스터로 어드레스를 읽어서 저장될 레지스터이다.

<expression>은 읽어올 어드레스의 위치를 나타내며, 라벨 형태로 표현된다.

<expression>에 표현될 수 있는 라벨의 어드레스 범위는, word-aligned로 되어 있는 경우 PC 값을 기준으로 ±1020바이트이다.

 

ADR 명령은 <expression>의 주소 값을 Rd에 저장한다. 실제 동작될 때는 주소 값을 읽어오기 위해 하나의 ADD나 SUB 같은 명령을 사용하여 주소를 계산한다.

예)

start     MOV   r0,   #10

ADR     r4, start            ; start 라벨의 주소를 r4에 저장한다. 실제 동작은 SUB r4, PC, #0xC가 된다.

 

 

 

ADRL : 어드레스를 읽어오는 명령으로 ADR과 유사하지만 보다 광범위한 어드레스 범위를 읽어올 수 있다.

ADR{cond}L Rd, <expression>

{cond}는 조건부 실행을 위한 조건을 나타낸다.

Rd는 ARM의 범용 레지스터로 어드레스를 읽어서 저장될 레지스터이다.

<expression>은 읽어올 어드레스의 위치를 나타내며, 라벨 형태로 표현된다.

<expression>에 표현될 수 있는 라벨의 어드레스 범위는, word-aligned로 되어 있는 경우 PC 값을 기준으로 ±256K바이트이다.

ADR 명령은 <expression>의 주소 값을 Rd에 저장한다. 실제 동작될 때는 주소 값을 읽어오기 위해 하나의 ADD나 SUB 같은 명령을 사용하여 주소를 계산한다.

예)

start     MOV   r0,   #10

ADRL   r4,   start   ; start 라벨의 주소를 r4에 저장한다. 실제 동작은 ADD r4, PC, #0xE800

                            ADD  r4, r4, #0x254 와 같이 사용되어 계산된 주소가 r4에 저장한다.

 

 

 

NOP : NOP 명령이 사용되면 프로세서에 아무런 영향 없이 한 프로세서 사이클을 소비한다.

NOP

NOP는 'MOV r0, r0'로 코드가 생성되어 사용된다.

 

 

ALIGN : 메모리 내의 코드 또는 데이터의 배치를 워드 단위 경계로 정렬한다.

 

DCx : 데이터를 선언한다.

 

DCB : 바이트 데이터(byte data)
DCW : 하프워드데이터(half word data)
DCD : 워드데이터(word data)
DCS : 255바이트까지의 문자열 데이터
DCF : Floating Point Data

예)

DCB    0x12

DCD   0x12345678

DCS   "hello"


출처 : http://blog.naver.com/luis8282?Redirect=Log&logNo=20077512659

[출처] ARM의 의사 명령어|작성자 piao

1. cstartup.s
- Exception Vector Table
- reset_handler / irq_handler 구현
- lowlevel_init으로 분기 : 클럭 설정, 각 인터럽트 소스에 디폴트 핸들러 등록, Watchdog Disable (분석이 더 필요)
- Remap SRAM
- Stack 설정 : Abort, IRQ, SVC 모드일 때 각각 스택 영역 설정
- Data 영역을 RAM에 적재
- Bss 영역을 0으로 초기화

2. gnu 어셈블리 지시어
- .global <symbol> : 심볼을 외부 링크로 제공
- .align {<expression>, {<offset>}} : 다음 명령어의 주소를 <expression> + <offset> 형태로 정렬

ARM은 기본적으로 거의 모든 명령이 4 byte로 구성되어 진다. 4의 배수로 실행 코드를 쪼개서 기계어 하나하나를 해석해 낸다고 보면 된다. 그런데 asm 코드들 중간에 address나 변수 등을 정의할 때 1 byte 2 byte로 정의를 해버리면 4의 배수가 않되는 경우가 발생한다. 이때 오류가 발생할 수 있다. 4의 배수를 맞추기 위하여 0으로 초기화되는 byte들을 추가로 패딩해주는 것이 필요한데 이것을 해주는 directive이다.

- .section <section_name> {, "<flags>"} : 새로운 코드 색션 또는 데이터 색션을 시작한다. 보통 코드 색션 .text, 초기화 된 데이터 색션 .data, 초기화 되지 않은 데이터 색션 .bss를 호출해야 한다. 이것들은 디폴트 플래그를 가지고 있으며 링커는 이 디폴트 이름들을 이해하고 있다.
- .word <word1> {,<word2>}... : ARM asm에서의 DCD와 같이 데이터로 32비트 워드값 목록들을 어셈블리에 삽입한다.

3. cstartup.S 코드
#include "project.h"

#define TOP_OF_MEMORY    (AT91C_ISRAM + AT91C_ISRAM_SIZE)
#define ABT_STACK_SIZE   8*3*4
#define IRQ_STACK_SIZE   8*3*4

#define ARM_MODE_ABT     0x17
#define ARM_MODE_FIQ     0x11
#define ARM_MODE_IRQ     0x12
#define ARM_MODE_SVC     0x13

#define I_BIT            0x80
#define F_BIT            0x40


/* Application startup entry point */
        .globl reset_handler    // reset_handler 심볼을 외부에 보이도록 한다.
        .align 4          // 4 바이트 단위로 정렬하겠다는 의미

.section .vectors    /* .vectors 색션 시작 */
.arm
        
/// 1. start ////////////////////////////////////////////////////////////////////////
/* Exception vectors (should be a branch to be detected as a valid code by the rom */
/* exception vector table */
_exception_vectors:
reset_vector:
        ldr    pc, =reset_handler  /* Reset vector : pc := reselt_handler  0x00000000 */
                  /* reset_handler로 분기됨 */
undef_vector:
        b    undef_vector    /* Undefined Instruction : 미구현  0x00000004 */
swi_vector:
        b    swi_vector      /* Software Interrupt : 미구현     0x00000008 */
pabt_vector:
        b    pabt_vector     /* Prefetch Abort : 미구현       0x0000000C */
dabt_vector:
        b    dabt_vector     /* Data Abort : 미구현         0x00000010 */
rsvd_vector:
        b    rsvd_vector     /* reserved : 예약되어 있는 공간   0x00000014 */
irq_vector:
        b    irq_handler     /* IRQ : read the AIC         0x00000018 */
fiq_vector:
/*------------------------------------------------------------------------------
 *- Function             : fiq_handler
 *- Treatments           : FIQ Interrupt Handler.
 *- Called Functions     : 
 *------------------------------------------------------------------------------*/

fiq_handler:
    b    fiq_handler    /* FIQ : 미구현           0x0000001C */
/* exception vector table end */
/* x86의 경우엔 해당 벡터에 address를 넣어 두면, 인터럽트 발생시에 해당 주소를 가져다가
   PC(Program Counter)에 넣어 주는 일이 발생하지만, ARM7의 경우엔 그냥 해당 벡터로 점프한다.
   예를 들어 IRQ가 발생했다면 다음 순간의 PC값은 0x00000018이 된다. 따라서 해당 벡터 번지에는
   단순히 번지가 들어가는 것이 아니라 점프 명령 같은 것이 들어간다.  */

/// 1. end //////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
    
/*------------------------------------------------------------------------------
 *- Function             : irq_handler
 *- Treatments           : IRQ Controller Interrupt Handler.
 *- Called Functions     : AIC_IVR[interrupt]
 *------------------------------------------------------------------------------*/

irq_handler:
/*- Manage Exception Entry */
/*- Adjust and save LR_irq in IRQ stack */
        sub      lr, lr, #4      /* lr := lr - 4 / 돌아갈 주소 교정 */
        stmfd    sp!, {lr}      /* *sp := lr / 스택에 lr 저장 */
/*- Save r0 and SPSR in IRQ stack */
        mrs      r14, SPSR      /* r14 := SPSR */
        stmfd    sp!, {r0,r14}    /* *sp <- r0, r14 */

/*- Write in the IVR to support Protect Mode */
/*- No effect in Normal Mode */
/*- De-assert the NIRQ and clear the source in Protect Mode */
        ldr      r14, =AT91C_BASE_AIC  /* r14 := AT91C_BASE_AIC */
        ldr      r0 , [r14, #AIC_IVR]  /* r0 := *(r14 + AIC_IVR) */
        str      r14, [r14, #AIC_IVR]  /* *(r14 + AIC_IVR) := r14 */

/*- Enable Interrupt and Switch in Supervisor Mode */
        msr      CPSR_c, #ARM_MODE_SVC

/*- Save scratch/used registers and LR in User Stack */
        stmfd    sp!, {r1-r3, r12, r14}

/*- Branch to the routine pointed by the AIC_IVR */
        mov      r14, pc
        bx       r0

/*- Restore scratch/used registers and LR from User Stack */
        ldmia    sp!, {r1-r3, r12, r14}

/*- Disable Interrupt and switch back in IRQ mode */
        msr      CPSR_c, #ARM_MODE_IRQ | I_BIT

/*- Mark the End of Interrupt on the AIC */
        ldr      r14, =AT91C_BASE_AIC
        str      r14, [r14, #AIC_EOICR]

/*- Restore SPSR_irq and r0 from IRQ stack */
        ldmia    sp!, {r0,r14}
        msr      SPSR_cxsf, r14

/*- Restore adjusted  LR_irq from IRQ stack directly in the PC */
        ldmia    sp!, {pc}^


/// 2. start ////////////////////////////////////////////////////////////////////////
/*------------------------------------------------------------------------------
 *- Function             : reset_handler
 *- Treatments           : Reset Interrupt Handler.
 *- Called Functions     : lowlevel_init
 *                         main
 *------------------------------------------------------------------------------*/

.section .text    /* text 색션 시작 */
reset_handler:
  ldr     pc, =_low_level_init  /* pc := _low_level_init */
                  /* _low_level_init 으로 분기됨 */

/*------------------------------------------------------------------------------
 *- Low level Init is performed in a C function: lowlevel_init
 *- Init Stack Pointer to a valid memory area before calling lowlevel_init
 *------------------------------------------------------------------------------*/

/*- Temporary stack in internal RAM for Low Level Init execution */
_low_level_init:
  ldr      r2, =_lp_ll_init    /* r2 := _lp_ll_init */
        ldmia    r2, {r0, r1}    /* r0 := lowlevel_init, r1 := TOP_OF_MEMORY */
        mov      sp, r1        /* sp := r1    sp := TOP_OF_MEMORY / 스택 위치 설정 */
        mov      lr, pc        /* lr := pc    return address를 저장 */
        bx       r0                 /* Branch on C function (interworking) */
                  /* lowlevel_init으로 분기됨, ARM상태 */
/* #define TOP_OF_MEMORY    ((char *)   0x00200000 + (0x00010000)) */
/// 2. end //////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////

/// 3. start ////////////////////////////////////////////////////////////////////////
/*------------------------------------------------------------------------------
 *- Remap SRAM at 0x0
 *------------------------------------------------------------------------------*/

/* AT91C_MC_RCR : (MC) MC Remap Control Register */
_remap:
        ldr r2, _lp_remap    /* r2 := _lp_remap / r2 := AT91C_MC_RCR */
        mov r0, #AT91C_MC_RCB  /* r0 := AT91C_MC_RCB / AT91C_MC_RCB:1 */
        str r0, [r2]      /* *r2 := r0 / MC_RCR 레지스터에 1을 세팅 */
/* SRAM 영역이 0x00000000으로 리맵핑 된다 */

/*------------------------------------------------------------------------------
 *- Setup the stack for each mode
 *------------------------------------------------------------------------------*/

_stack_init:
  ldr      r2, =_lp_stack_init  /* r2 := _lp_stack_init */
        ldmia    r2, {r0, r1, r2}  /* r0 := TOP_OF_MEMORY / r1 := ABT_STACK_SIZE / r2 := IRQ_STACK_SIZE */
/* #define ABT_STACK_SIZE   8*3*4
   #define IRQ_STACK_SIZE   8*3*4 */


/*- Set up Abort Mode and set ABT Mode Stack */
        msr      CPSR_c, #ARM_MODE_ABT | I_BIT | F_BIT    /* Abort mode로 변경, IRQ/FIQ disable */
        mov      sp, r0      /* sp := r0 / sp := TOP_OF_MEMORY : Abort mode 스택 포인터 설정 */
        sub      r0, r0, r1    /* r0 := r0 - r1 */
/*- Set up Interrupt Mode and set IRQ Mode Stack */
        msr      CPSR_c, #ARM_MODE_IRQ | I_BIT | F_BIT    /* IRQ mode로 변경, IRQ/FIQ disable */
        mov      sp, r0      /* sp := r0 / sp = TOP_OF_MEMORY - 8*3*4 : IRQ mode 스택 포인터 설정 */
        sub      r0, r0, r2    /* r0 := r0 - r2 */

/*- Enable interrupt & Set up Supervisor Mode and set Supervisor Mode Stack */
        msr      CPSR_c, #ARM_MODE_SVC | F_BIT    /* SVC mode로 변경, FIQ disable */
        mov      sp, r0      /* sp := r0 / sp = TOP_OF_MEMORY - 8*3*4 - 8*3*4 : SVC mode 스택 포인터 설정 */
/* Abort, IRQ, SVC mode들의 스택 포인터들을 설정 */
/* 각 모드 마다 스택 포인터가 각각 독립적으로 존재함으로 각 모드의 스택 영역을 설정한 것이다 */

/*------------------------------------------------------------------------------
 *- Segments initialization
 *------------------------------------------------------------------------------*/

/* Copy the data section in RAM at 0x0000 */
_init_data:
  ldr      r2, =_lp_data      /* r2 := _lp_data */
        ldmia    r2, {r1, r3, r4}  /* r1 := _etext / r3 := _sdata / r4 := _edata */ 
1:
        cmp      r3, r4        /* CPSR flag := r3 - r4 */
        ldrcc    r2, [r1], #4    /* C flag clear 이면 r2 := *r1 / r1 := r1 + 4 */
        strcc    r2, [r3], #4    /* C flag clear 이면 *r3 := r2 / r3 := r3 + 4 */
        bcc      1b          /* C flag clear 이면 1로 분기 */
/* _etext : 코드 영역의 끝을 가리킨다 */
/* _sdata : 데이터 영역의 시작을 가리킨다 */
/* _edata : 데이터 영역의 끝을 가리킨다 */

/* Clear the bss segment */
_init_bss:
  ldr      r2, =_lp_bss      /* r2 := _lp_bss */
        ldmia    r2, {r3, r4}    /* r3 := _sbss / r3 := _ebss */
        mov      r2, #0        /* r2 := 0 */
1:
        cmp      r3, r4        /* CPSR flag := r3 - r4 */
        strcc    r2, [r3], #4    /* *r3 := r2 / r3 := r3 + 4 */
        bcc      1b          /* C flag clear 이면 1로 분기 */
/* bss 영역을 0으로 초기화 */
/* _sbss : bss 영역의 시작을 가리킨다 */
/* _ebss : bss 영역의 끝을 가리킨다 */

/*------------------------------------------------------------------------------
 *- Branch to the main
 *------------------------------------------------------------------------------*/

_branch_main:
        ldr      r0, =main    /* r0 := main */
        mov      lr, pc      /* lr := pc / return address 저장 */
        bx       r0        /* main으로 분기 / ARM 상태 */
/// 3. end //////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////

/*------------------------------------------------------------------------------
 *- Litteral pools
 *------------------------------------------------------------------------------*/

_lp_ll_init:
        .word    lowlevel_init
        .word    TOP_OF_MEMORY              /* Default SVC stack after power up */ 

_lp_remap:
        .word    AT91C_MC_RCR

_lp_stack_init:
        .word    TOP_OF_MEMORY             /* Top of the stack */
        .word    ABT_STACK_SIZE            /* ABT stack size */
        .word    IRQ_STACK_SIZE            /* IRQ stack size */

_lp_bss:
        .word    _sbss
        .word    _ebss

_lp_data:
        .word    _etext
        .word    _sdata
        .word    _edata
















+ Recent posts