임베디드/임베디드 레시피

Inline Assembly와 INTLOCK의 구현

twoweeks-within 2024. 11. 20. 17:44

 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


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