Programming/8051

8051( 중급 3부 - 함수 )

청솔1 2009. 11. 29. 10:43

8051( 중급 3부 - 함수 )

이번 강좌는 8051 C 에서 함수 사용 방법에 대한 내용이다.
일반 PC에서의 C 프로그래밍이라면 그다지 신경을 쓰지 않아도 될 사항들이지만 8비트 마이컴으로 작성하
는 C 프로그래밍에서는 최종적으로 생성될 코드의 크기프로그램 수행 속도를 생각치 않을수 없다.

정수형 두수의 합에 대한 프로그램을 생각해 보기로 하자.
정수형( integer - 발음 주의 )은 2바이트의 변수이다.
먼저 다음과 같은 프로그램이 있다고 하자. ( main1.c )

#include 
#pragma memory = default
/*------------------------------------- 일반 루틴 -------------------------------*/
#pragma function = default
void main( void ) {
    int a, b, c;
    a = 100;
    b = 257;
    c = a + b;
    }


이 프로그램을 컴파일 해보자.
결과는 다음과 같다.

      5          void main( void ) {
   \   0000            main:
      6              int a, b, c;
      7              a = 100;
   \   0000  750000            MOV     $LOCBD main,#0         상위 바이트 	
   \   0003  750064            MOV     $LOCBD main+1,#100     하위 바이트
      8              b = 257;
   \   0006  750001            MOV     $LOCBD main+2,#1       상위 바이트
   \   0009  750001            MOV     $LOCBD main+3,#1       하위 바이트
      9              c = a + b;
   \   000C  E500              MOV     A,$LOCBD main+1        
   \   000E  2500              ADD     A,$LOCBD main+3        하위 바이트끼리 더한다
   \   0010  F500              MOV     $LOCBD main+5,A        하위 바이트의 연산 결과 저장
   \   0012  E500              MOV     A,$LOCBD main
   \   0014  3500              ADDC    A,$LOCBD main+2        상위 바이트끼리 더한다
   \   0016  F500              MOV     $LOCBD main+4,A        상위 바이트의 연산 결과 저장
     10              }
   \   0018  22                RET
   \   0019                    END


그런데 우리의 고민은 모든 프로그램을 main 함수 안에 죄다 넣어둘수 없다는 야그다.
C 프로그램에서는 main 함수는 항상 있어야 하고 단 하나만 존재 해야 한다.
그래서 자주 사용하는 공통되는 기능을 따로 떼어내 하나의 함수로 만든다.
그럼 다음 프로그램을 보기로 하자.

#include 
#pragma memory = default
/*------------------------------------- 일반 루틴 -------------------------------*/
#pragma function = default
/* 사용자 함수명의 첫자리는 대문자 처리해서 일반 함수와 구분을 두라.                */
/* 그래야 한다는 규칙은 없다. 다만 어느 누가 보더라도 이해 하기 쉽게 하라는 야그다. */
int Add( int add1, int add2 ) {	/* 전달 받는 두 변수도 정수형이고 리턴값도 정수형인 함수 */
    return add1 + add2;
    }
void main( void ) {
    int a, b, c;
    a = 100;
    b = 257;
    c = Add( a, b );
    }


위 프로그램을 컴파일 해보자.( main2.c )

      5          int Add( int add1, int add2 ) {
   \   0000            Add:
      6              return add1 + add2;
   \   0000  EF                MOV     A,R7                   변수가 내부 레지스터에 할당( xdata가 아니므로)
   \   0001  2500              ADD     A,$LOCBD Add+3         하위 바이트의 덧셈
   \   0003  FF                MOV     R7,A                   하위 바이트 덧셈 결과 저장
   \   0004  E500              MOV     A,$LOCBD Add+2
   \   0006  3E                ADDC    A,R6                   상위 바이트의 덧셈
   \   0007  FE                MOV     R6,A                   상위 바이트의 덧셈 결과 저장
      7              }
   \   0008  22                RET
      8          void main( void ) {
   \   0009            main:
      9              int a, b, c;
     10              a = 100;
   \   0009  750000            MOV     $LOCBD main,#0
   \   000C  750064            MOV     $LOCBD main+1,#100
     11              b = 257;
   \   000F  750001            MOV     $LOCBD main+2,#1
   \   0012  750001            MOV     $LOCBD main+3,#1
     12
     13              c = Add( a, b );
   \   0015  850000            MOV     $PRMBD Add+2,$LOCBD main+2
   \   0018  850000            MOV     $PRMBD Add+3,$LOCBD main+3
   \   001B  AF00              MOV     R7,$LOCBD main+1
   \   001D  AE00              MOV     R6,$LOCBD main
   \   001F  120000            LCALL   $REFFN Add
   \   0022  8E00              MOV     $LOCBD main+4,R6        Add 함수의 리턴값(상위바이트)
   \   0024  8F00              MOV     $LOCBD main+5,R7        Add 함수의 리턴값(하위바이트)
     14              }
   \   0026  22                RET
     15
   \   0027                    END


main1.c 보다는 훨씬 복잡해졌다. 다만 계속되는 프로그램내에서 공통될수 있는 기능을 함수로 독립 시킨것이다.

함수를 사용할때는 서로 전달하고 전달 받아야 되는것들 때문에 프로그램 길이도 길어지고 그 만큼 수행 속도도 저하 된다.
그러면 어떻게 해야 최종으로 코딩된 프로그램 용량도 줄이고 수행 속도도 향상 시킬것인가?
그것이 마이컴 프로그래머의 고민거리이다. 한정된 메모리 크기에 죄다 집어 넣어야 하는데다

실시간에 시간 낭비 없이 프로그램이 돌아줘야 하는 상황도 봉착하게 되기 때문이다.

프로그램에는 정답이 없다. 달리 말하면 정답은 무수히 많다. 결과가 맞으면 되기 때문이다.
하지만 프로그램다운 프로그램이 되려면 다음과 같은 사항을 명심하라. 이것은 산적이 주장하는 거다.

1. 누가 보아도 쉽게 이해 할수 있게 프로그램을 작성하라.

한눈에 보아 쉽게 이해 될수 있어야 남들도 이해하기 쉽고 자신도 자신이 파놓은 함정에서 헤어나기 쉽다.
프로그램 길이만 길지 너저분하게 줄래 줄래 빨래 널듯이 기능을 널어 놓은 프로그램은 프로그램이 아니다.
보다 함축적으로 함수를 정리 하고 체계적인 방법들을 이용하라.

2. 함수에서 또다른 함수 호출을 하는 경우 세단계 이상을 넘지 않도록 하라.

위 프로그램( main2.c )를 보면 main 함수에서 Add 함수를 호출 했다.
기능을 구현하기 위해서는 다시 Add 함수에서 다른 함수를 호출 할수 있다.
보다 프로그램을 복잡하지 않게 하기 위해서는 가급적 그 이상의 함수 호출은 하지 말라는 야그다.

3. 전역 변수나 사용자 함수명은 첫문자를 대문자 처리 하라.

지역 변수와 전역 변수의 차이를 금방 알아 볼수 있고 사용자 함수와

라이브러리 함수와의 구분을 쉽게 할수 있어서 혼동을 피할수 있다.

4. 함수에 전달하는 인수나 리턴값을 가급적 줄여라.

위의 main2.c에서 보면 함수에 전달하는 인수를 위해 a 변수는 R6, R7에, b 변수는 다른 영역에 넣고
함수를 호출하고 그 결과 값을 다시 R6, R7로 되돌려 받는 방식을 취하고 있다.
main1.c 보다 복잡해졌다는것을 알수 있다. 그렇다고 함수 사용을 전혀 안할수는 없다.
그러므로 두개의 함수 이상에서 사용되는 변수는 전역 변수 처리해서 프로그램을 작성하면 보다 단순
해지고 그만큼 speed up을 시킬수 있다.( main3.c를 보면 알수 있다. )

위와 같은 내용은 C++ 이나 JAVA에서 주장하는 객체 지향과는 배치되는 내용이 많다.
하지만 상대적으로 낮은 speed와 메모리 한계를 느끼며 프로그램해야 하는 마이컴 땜쟁이들에게 권장하는 바이다.

그럼 위에 정한 규칙에 의해 다시 프로그램을 정리해 보기로 하자.( main3.c )

#include 
#pragma memory = default
int Add1, Add2, Result;
/*------------------------------------- 일반 루틴 -------------------------------*/
#pragma function = default
void Add( void ) {  /* void - 전달 받을 인수도, 리턴값도 없다는것을 명시적으로 선언 */
    Result = Add1 + Add2;
    }
void main( void ) {
    Add1 = 100;
    Add2 = 257;
    Add();
    }


위 프로그램을 컴파일 해보라. 눈으로만 공부하려고 달라들면 자신에게 남는게 없다. 직접 해보라.
항상 주장하는 말이지만 똥인지 된장인지 직접 맛을 보고 확인하라. 그게 엔지니어의 철칙이다.
똥을 하도 많이 찍어 먹어 봐서 입안이 구리면 산적처럼 막걸리나 사묵어라. 산적은 사줄돈 음따.
컴파일하고서 main1.c, main2.c, main3.c간의 차이를 비교해보라.

 

출처 : 민이의 여행

 << 전자기기 개발 --- 건강기기, 통신기기,각종 전자회로 개발, PCB ARTWORK >>

 

'Programming > 8051' 카테고리의 다른 글

8051( 중급 6부 - RS485 )  (0) 2009.11.29
8051( 중급 5부 - pre-processor )  (0) 2009.11.29
8051( 중급 4부 - define )  (0) 2009.11.29
8051( 중급 2부 - C 프로그램 맛보기 )  (0) 2009.11.29
8051( 중급 - 1부 )   (0) 2009.11.28