강좌 & 팁
글 수 2,412
2012.09.17 09:57:49 (*.52.177.29)
43144
하...
오랫만에 산책로 걷습니다.
마음은 매일 산책하고 싶지만 그렇게 쉽지 않네요
이전에 함수와 시스템 콜에 대한 개념적인 부분을 설명했습니다.
시스템 콜에 소프트웨어 인터럽트를 사용한다고 했습니다.
왜? 시스템 콜에 소프트웨어 인터럽트를 사용할까요?
시스템 콜 개념이 나온 것은 응용 프로그램 코드의 재 활용을 극대화 하기 위한 성격이 강합니다.
응용 프로그램을 작성하는 프로그래머 입장에서는
한번 작성된 프로그램이
아이폰에서도 동작하고
안드로이드 폰에서도 동작하고
PC 에서도 동작하는 것이 좋죠..
하지만 현실은 그렇게 되도록 두지 않습니다.
대부분 각각의 플랫폼에 맞도록 프로그램을 작성해야 합니다.
그나마 다행인 것은 여러분이
C 언어로 프로그램을 구현하고
C 라이브러리를 이용하며
GUI 가 아닌 형태의 프로그램을 작성했다면
소스 재 사용율이 매우 높아질 수 있다는 겁니다.
왜냐하면 대부분이 키보드나 파일 출력에 관련된 부분이 시스템 콜을 사용하는데
이 시스템 콜들은 유닉스나 리눅스나 도스나 위도우즈나 맥이나 비슷 비슷한 형태를 갖기 때문입니다.
예를 들어 printf() 함수나 scanf() 함수 류이고
이 함수들은 대부분 비슷 합니다 .
화일을 다루는 것도 비슷하고요.
또 현존하는 대부분의 언어들이 시스템에 접근하는 함수를 구현하는 경우
그 밑 바닥에는 거의 C 로 되어 있다고 봐도 됩니다.
아. 물론 어셈블러도 조금 사용하는 경우도 있습니다.
결국 C 언어를 사용하게 되면 시스템에 관련된 처리의 함수 및 소스 호환성이 매우 높아 집니다.
자 이제
시스템 함수들은 시스템을 지원하는 운영체제가 지원하면 됩니다.
이전에 정의한대로 한다면
펌웨어에서는 시스템 함수 라이브러리 형태로 지원하게 되고
운영체제가 있다면 시스템 공유 라이브러리 형태로 지원하게 되죠...
이제 화면에 무언가 출력하려면 printf() 함수를 사용하면 됩니다 .
이때 printf() 함수를 호출 표현은 C 언어에서 이렇게 표현될 겁니다.
printf("hello\n" );
이 함수는 라이브러리 내에 존재하게 되고 이 라이브러리의 실제 코드 주소는 링크 과정에서 결정됩니다.
결국 어셈블러로 해석되어 코드로 변환되는데 어셈블러적인 표현은 호출하는 경우 이렇게 되겠죠.
bl printf
만약 led_on() 이라는 시스템에 LED 를 켜는 함수를 운영체제가 제공한다면
led_on();
이라고 C 언어로 표현하고 어셈블러로 본다면
bl led_on
뭐 이런식으로 될겁니다 .
ARM 에서는 bl 는 복귀 주소를 lr 레지스터에 저장하고 지정된 위치로 명령 수행 위치를 변경하는 브랜치 명령이고 led_on은 심볼릭 명이 됩니다.
이 심볼릭 명은 링크 과정을 거치고 실행되는 시점에서는 반드시 해당 코드가 있는 주소 값으로 변환되어야 합니다.
예를 들어 led_on 코드가 0x2001000 번지에 있다면 위 어셈블러 명령은
bl 0x2001000
이 될 겁니다.
물론 bl 라는 어셈블러 명령은 절대 위치를 지정할 수 있는 것이 아니고
현재 수행하는 위치 의 PC 값을 기준으로 +/- 32M 상대 위치로 점프하는 명령입니다
그러므로 위 명령이 0x2000000 에서 수행된다고 하고
led_on 코드가 0x2001000 에 있다면
실제로 만들어지는 어셈블러 명령은
bl 0x1000
이 될 겁니다
즉 브랜치 명령인 bl 로 하면 호출하려고 하는 함수의 주소와 수행되는 코드의 주소간에 관계가 있어야 하는 것입니다.
물론 상대 위치가 아닌 절대 위치로 수행 시점인 PC(Program Counter)를 수정하는 명령이 있죠
bx 명령이 그런 명령입니다. 위 명령은 이런 식으로 수정할 수 있겠죠
ldr r0, =0x2001000
bx r0
어찌되었던 함수를 호출하기 위해서는 해당 함수명의 심볼릭에 주소 값이 필요하죠..
이전에 소개 했던 함수 테이블 방식 이라고 하더라도 함수 테이블이 있는 주소가 필요한 겁니다.
이런 것을 제거 하고 싶기 때문에 소프트웨어 인터럽트를 사용합니다.
ARM 에서 소프트웨어 인터럽트 명령은 SWI 라고 했습니다 .
이 SWI 함수는 ARM 어셈블러에서는
SWI 0xFA0001
이런 식으로 기술 할 수 있습니다.
SWI 의 기계어 코드 형식은 다음과 같습니다.
수행 조건(4비트) + SWI 명령(4비트) + 임의값(24비트)
여기서
수행 조건은 암에서 SWI 코드가 수행되는 조건인데
다음과 같은 값을 가질 수 있습니다.
0000 = EQ - Z set (equal)
0001 = NE - Z clear (not equal)
0010 = HS / CS - C set (unsigned higher or same)
0011 = LO / CC - C clear (unsigned lower)
0100 = MI -N set (negative)
0101 = PL - N clear (positive or zero)
0110 = VS - V set (overflow)
0111 = VC - V clear (no overflow)
1000 = HI - C set and Z clear (unsigned higher)
1001 = LS - C clear or Z (set unsigned lower or same)
1010 = GE - N set and V set, or N clear and V clear (>or =)
1011 = LT - N set and V clear, or N clear and V set (>)
1100 = GT - Z clear, and either N set and V set, or N clear and V set (>)
1101 = LE - Z set, or N set and V clear,or N clear and V set (<, or =)
1110 = AL - always
1111 = NV - reserved
암 명령의 기계어 코드를 보면 이 컨디션 값이 가장 처음 설정되죠..
조건을 따지지 않고 무조건 수행되는 경우라면 0xE 값이 됩니다.
가장 흔하게 볼수 있는 값입니다.
명령 코드의 27 비트 부터 24 비트까지 모두 1 값이면 SWI 명령으로 해석됩니다.
그리고 그 뒤에 24 비트 즉 0 비트 부터 23비트까지는 명령 처리에 아무런 상관이 없습니다.
그래서
SWI 0
명령은 기계어 코드로
11101111000000000000000000000000
입니다. 16진수 표시로 바꾸면
0xEF000000
이 됩니다.
SWI 0xFA0001
이 명령은
0xEFFA00001
이 됩니다.
헉! 퇴근 시간이다.
에혀 퇴근 해야죠...
다음 산책 시간에 뵙겠습니다.
아마도 금주에도 1 번 이상 산책하기 힘들 듯 ㅠㅠ