Assembly로 코딩해야할때
1. Low Level을 직접 다룰 때 : Coprocessor를 다뤄야 할 때 ( C로는 매우 난감)
2. ARM을 직접 다룰 때 : PSR을 다뤄야 할 때. Interrupt Lock을 걸고 싶을 때
3. Register를 직접 다룰 때 : Register를 직접 다루고 싶을 때는 어떻게 해야 할지.. R0, R1을 내 맘대로 다루고 싶을때
Assembly (.s) > Assembler로 compil > 기존 C 의 .o link
__asm
> C compiler가 이 다음부터는 Assembly구나~ 하고 넘어가줌
Inline Assembly
간단한 함수 하나 정도만 Assembly로 할때 간단 방법
{} 사이에 원하는 Assembly를 끼워 넣으면됨
ex) stack pointer를 가져올 수 있게 하는 함수 ( c 에서는 하기 어려움)
getsp.c ----------------------------------------------------------------
typedef unsigned long word;
word *get_StackPointer(word *stackPointer)
{
__asm
{
mov stackPointer, sp;
}
return;
}
------------------------------------------------------------------------
get_StackPointer() 함수
전달 받는 인자가 1개 R0로 전달 될 것이고, 그 이외에는 stack을 쓸 일이 없음,
get_StackPointer()함수를 부르는 시점에서 Stack Pointer가 변할 일이 없으니, 정확히 현재 시점의 Stack pointer를 가져올 수 있음
아래처럼 Assembly로 get_StackPointer()를 컴파일해 보면. (getsp.c에 위의 함수만 하나 덜렁 넣어놓고 컴파일 해 보면)
tcc -o getsp.s -S getsp.c
//-S: C 소스 파일을 어셈블리 코드로 변환하는 옵션
getsp.s ----------------------------------------------------------------------------------------
; generated by Thumb C Compiler, ADS1.2 [Build 805]
; commandline [-O2 -S -IC:\apps\ADS12\INCLUDE]
CODE16 ; 16bit Thumb code
AREA ||.text||, CODE, READONLY ; RO, .text 속성이구~
get_StackPointer PROC ; 함수이름 get_StackPointer
MOV r0,sp ; return값이 들어갈 r0에 sp를 넣고서
BX lr ; Linked Register의 값으로 돌아가
ENDP ; 함수의 끝
EXPORT get_StackPointer ; get_StackPointer()함수는 어디선가 가져다 쓸꺼니까 참고
------------------------------------------------------------------------
Inline assembly에서도 local 변수, 즉 stackPointer직접참조 가능.
.xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office"
// "o" 접두사를 사용> Microsoft Office 관련 XML 스키마를 사용하고자 할 때 사용
CPSR을만져서 Interrupt가발생하지못하도록 하는 함수( Interrupt_lock )
FIQ와 IRQ를모두 disable
Privileged Mode에서만 PSR을만질수음, CPSR을만질수있는건ARM mode에서만가능
> Compiler는 tcc가아닌 armcc를사용.
CPSR의 interrupt 관련 bit를 1로 set하기위한코드
intlock.c --------------------------------------------------------------------------
#define PSR_Irq_Mask 0x80 // CPSR에서 IRQ mask
#define PSR_Fiq_Mask 0x40 // FIQ mas
#define PSR_INT_Mask 0xC0 // OR 계산
void Interrupt_lock(void)
{
__asm
{
mrs a1, CPSR
orr a2, a1, #PSR_INT_Mask
msr CPSR_c, a2
}
return;
}
----------------------------------------------------------------------------------
Register Convention을 r0, r1대신 a1, a2 (이렇게 해도됨)
a1에 CPSR 값을 가져 온후, a1과 interrupt bit field를 OR해서 a2에 집어 넣은 후, a2값을 MSR을 이용해서 CPSR에 넣어줌
armcc -o intlock.s -S intlock.c
------------------------------------------------------------------------------
; generated by ARM C Compiler, ADS1.2 [Build 805]
; commandline [-O2 -S -IC:\apps\ADS12\INCLUDE]
CODE32 ; 여기서 부터32bit ARM code
AREA ||.text||, CODE, READONLY ; Symbol 단위 특성은 CODE야.
Interrupt_lock PROC ; 함수명 Interrupt_lock()
MRS r0,CPSR ; CPSR값을 r0에 loading해
ORR r1,r0,#0xC0 ; loading한 r0값과 0xC0을 OR해서 r1에 넣고
MSR CPSR_c,r1 ; r1을 CPSR에 넣고
MOV pc,lr ; 돌아가자
ENDP
EXPORT Interrupt_lock
------------------------------------------------------------------------------
다음 코드에서 return 값은?
a = 0, b = 1
------------------------------------------------------------------------------
int add (int a, int b)
{
__asm
{
add r0, r0, #1
}
return a+b;
}
------------------------------------------------------------------------------
return =2 > X
a와 b를 다른 register에 넣고, inline 안에서 만지는 레지스터들은 inline 내부에서만,
함수의 argument로 넘어온 값들과는 inline 내부에서는 완전 별개로 관리할 수 있도록 backup
inline assembly에서의 r0, r1등의 register의 사용법은 > inline에 진입하기 직전에 backup >APCS와 상관없음
inline된 register는 컴파일러가 자동으로 stack에 save > restore or 다른 register에 할당
add() : 실제로 inline assembly 내부의 r0가 따로 쓰일 일이 없음 > 컴파일러가 알아서 그 내용을 최적화 > 없앰
------------------------------------------------------------------------------
CODE16
AREA ||.text||, CODE, READONLY
add PROC
ADD r0,r0,r1 ; a + b
BX lr ; a: return값, lr로 돌아감
ENDP
------------------------------------------------------------------------------
r0 에 a 를 넣고, r1 에 b를 넣음
r0 = r0 + r1 = 1
return a+b ;
return 1, return 2 > X
r0 에 1을 저장했을뿐이지 a,b 의 값에는 변화 x
>> 값을 가져와 레지스터에 따로 저장해서 따로 계산함
가져온 값에는 변화 xx
inline으로 넣은 코드 : 무소용
In-line Assembly restriction
ⓐ 의사 명령어, LDR Register,= Expression과 ADR의 형식을 사용할 수 없음
ⓑ Label 불가
ⓒ BL. BLX등의 branch 명령어는 허용이 안됩니다.( inline에서 다른 데로 jump해 버리면.. 안되니)
ⓓ Processor Mode나 Coprocessor 의 상태를 바꿀 수 있으나, compiler는 변화감지x >사용자 책임
ⓔ Thumb mode에서 inline 된 Assembly는 r0~r7까지만 Access 가능
ⓕ 중요) inline된 Register는 컴파일러가 Stack에 저장 > Restore하는 걸 자동으로 만들어줌
이런 제한이 있어도 Low Level의 Control에는 편리
CPSR macros, CPSR_c
32bit짜리 register > 8bit씩 4개
> 그때 그때 mask 로 값 넣기 귀찮
> CPSR_c 라는 Macro를 미리 Compiler와 약속,
아래 control 즉 0~7 bit 까지의 영역에만 값을 써주는 역할
CPSR_c : 0~7 bit 즉 Mode, ARM/Thumb, IRQ/FIQ Enable/Disable (Control Field)
CPSR_x : 8~15 bit Unused (Extension Field)
CPSR_s : 16_23 bit Unused (Status Field)
CPSR_f : 24~31 bit NZCV flag와 unused (Flags Field)
CPSR_cx, CPSR_sf 처럼 붙여서 사용가능, 결국 CPSR_cxsf = CPSR ( CPSR_cf 자주 사용 )
+SPSR 도 동일하게 사용
Inline : 코드자체 ( Inline 컴파일 된 함수)가 함수(호출하는) 에 삽입 , Inline 함수로 분기 > X
'임베디드 > 임베디드 레시피' 카테고리의 다른 글
Reset Handler에서 main까지 (Entry Point) (0) | 2024.12.01 |
---|---|
Coprocessor Assembly (0) | 2024.11.27 |
SWI 의 진실 (0) | 2024.11.26 |
vector table의 구현과 실제 (0) | 2024.11.26 |
Pipe line과 Exception 관계 그리고 ^접미사 (0) | 2024.11.21 |