Programming/8051

8051( 중급 4부 - define )

청솔1 2009. 11. 29. 11:09

8051( 중급 4부 - define )

어셈블리에서는 EQU라는 pseudo command가 있다.
그와 비슷한 기능을 하는 것이 C 언어에서는 define이다.
4부에서도 실제 target 보드에 직접 시험을 하는것은 아니고 컴파일만 해서 그 결과를 살펴 보기로 한다.
다음 프로그램을 보자.

#include 
#define OUT_74HC574 0x00      /* MOVX @R0, A 명령어를 사용할 OUTPUT address 어셈블리의 EQU 기능과 동일 */
#define IN_74HC244  0x00      /* MOVX A, @R0 명령어를 사용할 INPUT address  */
#define write_byte_io( address, value ) (*( char *)( 0x030000 + address ) = value )
#define read_byte_io( address ) (*( char *)( 0x030000 + address ))
void main( void ) {
    char value;
    value = 0x0f;
    write_byte_io( OUT_74HC574, value );
    value = read_byte_io( IN_74HC244 );
    write_io( 0x1000, value );
    value = read_io( 0x1000 );
    }


위 프로그램에서 특이한 부분은 write_byte_io(), read_byte_io(), write_io(), read_io() 이다.
이 부분을 알기 위해서는 컴파일시 include되는 io320.h 화일을 볼 필요가 있다.

io320.h에 정의된 내용
#define write_io( address, value ) ((( char *)0x010000 )[ address ] = value )
#define read_io(  address )        ((( char *)0x010000 )[ address ] )
#define read_code( address )       ((( char *)0x020000 )[ address ] )


위의 내용은 define 함수라고 한다. 즉 실제 C 함수는 아니고 어셈블러에서의 macro 기능과 유사한것이다.
이와 같이 간단한 기능을 갖는 함수를 사용할 경우 define 함수를 이용하면 일반 함수 사용보다 훨씬 코드도 줄어 들고 powerful한 기능을 수행할수 있다.

여기에서 ICC8051 컴파일러에 정해진 부분을 유념해야 한다.
즉 code를 다룰때는 0x02 segment를,

xdata를 다룰때는 0x01 segment를,

R0를 이용한 인덱스 i/o를 다룰때는 0x03 segment를 사용

토록 정해져 있다.

8 bit CPU에서는 2 byte의 address( 0x0000 ~ 0xFFFF )로 표현이 되기 때문에 어셈블러상에서는 segment, offset 개념이 필요치 않으며

16 bit CPU에서는 64 Kbytes의 offset과 상위 1 byte를 표현하는 segment 개념을 필요로 한다.

일례로 들면 8086 CPU에서 segment = 0x1000, offset = 0xFF00 이라 한다면 실제적인 address는 0x10FF00이 된다.

하지만 8051 CPU는 8 bit CPU 이므로 segment 개념을 MOVC, MOVX 명령을 활용하는 방법으로 사용이 된다.

그래서 MOVC 명령( code memory를 읽어 들이는 명령 )에서의 segment는 0x02이며

DPTR을 사용하는 MOVX 명령에서는 0x01 segment를, R0를 사용하는 MOVX 명령에서는 0x03 segment를 사용하도록 되어 있다.

위의 프로그램을 컴파일 해 보자.

####################################################################################################
#                                                                                                  #
#     Micro Series 8051 C-Compiler V4.20C/DXT                              22/Mar/102  08:05:11     #
#                                                                                                  #
#           Memory model  =  tiny                                                                  #
#           Source file   =  pdata.c                                                               #
#           List file     =  pdata.lst                                                             #
#           Object file   =  pdata.r03                                                             #
#           ASM file      =  pdata.s03                                                             #
#           Command line  =  -mt -L -q -P -a PDATA PDATA                                           #
#                                                                                                  #
#                                                                   (c) Copyright IAR Systems 1991 #
####################################################################################################
   \   0000                    NAME    pdata(16)
   \   0000                    RSEG    CODE(0)
   \   0000                    PUBLIC  main
   \   0000                    $DEFFN  main(1,0,0,0,0,0,0,0)
   \   0000                    EXTERN  ?CL8051T_4_20_L17
   \   0000                    RSEG    CODE
      1          #include 
      2          #define OUT_74HC574 0x00
      3          #define IN_74HC244 0x00
      4          #define write_byte_io( address, value ) (*( char *)( 0x030000 + address ) = value )
      5          #define read_byte_io( address ) (*( char *)( 0x030000 + address ))
      6          #pragma memory = default
      7          /*------------------------------------- 일반 루틴 -------------------------------*/
      8          #pragma function = default
      9          void main( void ) {
   \   0000            main:
     10              char value;
     11              value = 0x0f;
   \   0000  75000F            MOV     $LOCBD main,#15
     12              write_byte_io( OUT_74HC574, value );
   \   0003  E500              MOV     A,$LOCBD main
   \   0005  7800              MOV     R0,#0
   \   0007  F2                MOVX    @R0,A
     13              value = read_byte_io( IN_74HC244 );
   \   0008  7800              MOV     R0,#0
   \   000A  E2                MOVX    A,@R0
   \   000B  F500              MOV     $LOCBD main,A
     14              write_io( 0x1000, value );
   \   000D  E500              MOV     A,$LOCBD main
   \   000F  901000            MOV     DPTR,#4096
   \   0012  F0                MOVX    @DPTR,A
     15              value = read_io( 0x1000 );
   \   0013  E0                MOVX    A,@DPTR
   \   0014  F500              MOV     $LOCBD main,A
     16              }
   \   0016  22                RET
     17
   \   0017                    END
Errors: none
Warnings: none
Code size: 23
Constant size: 0
Static variable size: Data(0) Idata(0) Bit(0) Xdata(0)


위의 경우는 외부 I/O나 code memory를 사용키 위한 define 함수이다.
그러면 일반적으로 사용자가 만들어서 쓰는 define 함수를 보기로 하자.

#include 
#define COM1_ready()     ( Tail  == Head ) ? 0 : -1
#pragma memory = default
int Head, Tail;
char COM1_rdy( void ) {
    if( Tail == Head ) return -1;
    else               return 0;
    }
void main( void ) {
    COM1_ready();
    COM1_rdy();
    }


위의 COM1_ready() 라는 define 함수와 COM1_rdy() 라는 사용자 함수는 100% 동일한 기능이다.
다음은 컴파일 결과 이다.

      6          char COM1_rdy( void ) {
   \   0000            COM1_rdy:
      7              if( Tail == Head ) return -1;
   \   0000  E503              MOV     A,Tail+1
   \   0002  6501              XRL     A,Head+1
   \   0004  7004              JNZ     ?0006
   \   0006  E502              MOV     A,Tail
   \   0008  6500              XRL     A,Head
   \   000A            ?0006:
   \   000A  7003              JNZ     ?0001
   \   000C            ?0000:
   \   000C  7FFF              MOV     R7,#255
      8              else               return 0;
   \   000E  22                RET
   \   000F            ?0001:
   \   000F  7F00              MOV     R7,#0
      9              }
   \   0011            ?0002:
   \   0011  22                RET
     10          void main( void ) {
   \   0012            main:
     11              COM1_ready();
   \   0012  E503              MOV     A,Tail+1
   \   0014  6501              XRL     A,Head+1
   \   0016  7004              JNZ     ?0007
   \   0018  E502              MOV     A,Tail
   \   001A  6500              XRL     A,Head
   \   001C            ?0007:
   \   001C  7004              JNZ     ?0004
   \   001E            ?0003:
   \   001E  7F00              MOV     R7,#0
   \   0020  8002              SJMP    ?0005
   \   0022            ?0004:
   \   0022  7FFF              MOV     R7,#255
   \   0024            ?0005:
     12              COM1_rdy();
   \   0024  120000            LCALL   $REFFN COM1_rdy
     13              }


사용자 함수와 define 함수의 컴파일 결과 차이점은 define 함수쪽에는 LCALL 과 RET 명령이 불필요하다는 점이다.

그래서 stack 도 사용이 되지 않게 된다.
큰 차이점이 없다 하더라도 자주 조회하는 함수 인 경우라면 define 함수쪽이 스피드 면에서유리한건 사실이다.
매번 호출하는 곳마다 어셈블리의 MACRO 기능처럼 나열이 되기 때문에 CODE가 커지게 된다.

 

 

 

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

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