2011년11월29일_BrickOS mm.c의 MM_BLOCK_FREE 매크로함수 분석



● mm_init()

   1: void mm_init() {
   2:   size_t *current,*next;
   3:   
   4:   current=&mm_start;
   5:  
   6:   // memory layout
   7:   //
   8:   MM_BLOCK_FREE    (&mm_start);   // ram
   9:   
  10:                                   // something at 0xc000 ?
  11:   
  12:   MM_BLOCK_RESERVED(0xef30);      // lcddata
  13:   MM_BLOCK_FREE    (0xef50);      // ram2
  14:   MM_BLOCK_RESERVED(0xf000);      // motor
  15:   MM_BLOCK_FREE    (0xfe00);      // ram4
  16:   MM_BLOCK_RESERVED(0xff00);      // stack, onchip
  17:  
  18:   // expand last block to encompass all available memory
  19:   *current=(int)(((-(int) current)-2)>>1);
  20:   
  21:   mm_update_first_free(&mm_start);
  22: }



● MM_BLOCK_FREE 매크로함수

   1: // Macros for mm_init()
   2: // Always alternate FREE and RESERVED.
   3: //
   4:  
   5: //! memory from addr on can be allocated
   6: /*! Macro for mm_init().
   7:     Always alternate MM_BLOCK_FREE and MM_BLOCK_RESERVED.
   8: */
   9: #define MM_BLOCK_FREE(addr)                     \
  10:     next=(size_t*)(addr);                    \
  11:     *current=((((size_t)next)-(size_t)current)-2)>>1;    \
  12:     *(next++)=MM_FREE;                    \
  13:     current=next;

 

   9: #define MM_BLOCK_FREE(addr)                     \

addr을 인자로 받는다.  역슬래시는 다음 행에도 연달아 쓸 때 이렇게 한다.

  10:     next=(size_t*)(addr);                    \

next가 인자로 받은 주소를 가리키게 한다.

  11:     *current=((((size_t)next)-(size_t)current)-2)>>1;    \

next가 가리키는 주소값에서 current가 가리키는 주소값을 뺀 후 –2Bytes (16bit단위 데이터)한다. 
오른쪽으로 한 번 시프트하면 결과값에 나누기 2를 한 것이다.

   4:   current=&mm_start;
   8:   MM_BLOCK_FREE    (&mm_start);   // ram

mm_init( ) 에서,
current가 mm_start의 주소를 가리키게 하고, MM_BLOCK_FREE( ) 매크로에도 mm_start의 주소값을 인자로 넘겨 주었다.
결국 current와 next는 같은 곳을 가리킨다. 아직 mm_start의 주소는 알 필요없다. (몰라도 매크로함수가 무엇을 하는지는 알 수 있다.)
next와 current의 차이는 0이므로 –2를 오른쪽으로 한 번 시프트 한 값이 current가 가리키는 곳에 들어간다.
-2는 0xFFFE이고 (H8300은 8bit CPU이나 지금은 2Bytes단위로 엑세스한다.) 우로 한 번 시프트하면 0x7FFF가 된다.
이 값이 current가 가리키는 곳에 들어가나, 바로 다음...

  12:     *(next++)=MM_FREE;                    \

next가 가리키는 곳에 MM_FREE(0x0000으로 정의 됨.)이 들어간다. 즉, 최종값은 0x0000이 된다.
++은 후치연산이므로 대입 후에 증가된다.
size_t는 unsigned로 정의되어 있으니 unsigned int 즉, 8bit CPU에선 16bit크기를 갖는다. (이건 AVR과 비슷하다.)

여기서 mm_init( )의 다음 라인들을 해석하지 않고 임의로 next의 값...즉, MM_BLOCK_FREE 매크로함수의 인자를 바꿔서 결과를 보자.
next가 current보다 +2인 경우 next와 current의 차이는 0이 되므로 시프트연산을 해도 0이다. 그러니 두 공간(16bit)이 0x0000된다.
next가 current보다 +4인 경우 next와 current의 차이는 2가 되므로 시프트연산을 하면 1이 된다. 
        낮은 주소 current에는 1이 들어가고 +4주소(next)에는 0x0000이 들어간다.
next가 current보다 +6인 경우 next와 current의 차이는 4가 되므로 시프트연산을 하면 2가 된다.
        낮은 주소 current에는 2가 들어가고 +6주소(next)에는 0x0000이 들어간다.

여기서 우로 한 번 시프트하면 나누기 2라는 뜻이고,
(next – current) - 2는 next와 current의 차이 즉, next와 current 사이의 메모리공간의 크기가 된다.
나누기 2를 하는 이유는 16bit엑세스를 하기 때문에 하나의 공간이 2Bytes이다. 그러므로 2Bytes단위로 계수하겠다는 의미이다.

이를 보기 좋게 그림으로 표현하면 아래와 같다.

image
image

current와 next가 같은 경우엔 0x0000이 되고 메모리관리 영역의 시작을 0으로 만든다는 뜻이다.
NULL값을 표현할 때 (void *)0으로 하는데 이것과 연관지어 생각해 보면 메모리 관리영역의 최초영역은 0으로 채워져 있다는게 아닐까?
(이건 본문하고 관계없으니 생략하고)
next가 current보다 2Bytes 큰 경우엔 둘다 0x0000이라는 값을 갖는다. 이 의미는 메모리 구역을 지정했는데 사용할 수 있는 공간이 없다는 뜻이고, next가 current보다 4Bytes, 6Bytes 큰 경우를 보면 current가 가리키는 곳에는 빈 공간의 갯수가 들어가고,
next가 가리키는 곳에는 그 영역의 상태가 들어가는 것 같다. MM_RESERVED는 0xFFFF로 정의 되어 있으며 예약이라는 의미로 할당 받은 공간은 예약되어 있으니 자유롭게 사용할 수 없다는 뜻이 아닌가 한다.

이제 mm_init( )의 아래 라인들을 분석해 보자.

  12:   MM_BLOCK_RESERVED(0xef30);      // lcddata
  13:   MM_BLOCK_FREE    (0xef50);      // ram2
  14:   MM_BLOCK_RESERVED(0xf000);      // motor
  15:   MM_BLOCK_FREE    (0xfe00);      // ram4
  16:   MM_BLOCK_RESERVED(0xff00);      // stack, onchip

12행에서 0xEF30번지를 lcddata영역으로 예약한다. next는 계속 다음 번지를 가리킨다. (구역지정이 끝난 다음 주소)
          MM_BLOCK_RESERVED 매크로함수는 채우는 값만 다를 뿐 같은 기능을 하는 함수이다.
13행에서 0xEF50번지를 ram2영역으로 분류한다. 이 영역은 나중에 자유롭게 사용할 수 있는 영역일 것이다.
         운영체제가 사용하든 어플리케이션이 사용하든 잘 모르겠지만 어쨋든…
         0xEF32 ~ 0xEF4E까지 lcddata영역이다. (위 아래로 4Bytes는 사용이 불가능)
         H8300.rcx파일에서 메모리맵을 확인해보면 같다는 것을 알 수 있다.
14행에서 0xF000번지를 motor영역으로 분류한다. 
           0xEF52 ~ 0xEFFE까지 ram2영역이 될 것이다.
15행에서 0xFE00번지를 ram4영역으로 분류한다.
           0xF002 ~ 0xFDFE까지 motor영역이 될 것이다.

이하 생략하고 이렇게 보니 mm_start가 궁금해졌다. H8300.rcx파일의 13행부터 시작되는 메모리맵을 보면…

   1: MEMORY {
   2:   rom (r)  : o = 0x0000, l = 0x8000
   3:  
   4:   ram      : o = 0x8000, l = 0x6f30
   5:   lcddata  : o = 0xef30, l = 0x0020   /* display memory */
   6:   ram2     : o = 0xef50, l = 0x00b0
   7:   ram3     : o = 0xf002, l = 0x0b7e
   8:   romdata  : o = 0xfd80, l = 0x0040   /* port shadows, interrupt vectors */
   9:   ram4     : o = 0xfe00, l = 0x0100
  10:  
  11:   eight    : o = 0xff00, l = 0x0100
  12: }

ROM영역이 0x0000 ~ 0x8000까지  32KBytes고, 당연 이 영역은 메모리 관리를 하지 않을 것이다.
RAM영역이 0x8000부터 시작하는데 이 부분부터 메모리 관리를 시작하지 않나 생각된다.        




● 결론

MM_BLOCK_FREE 매크로함수는 RAM영역을 자유롭게 사용할 수 있는 빈 공간으로 구역을 정하는 함수이다.
각 영역의 최상단에는 그 블록의 Bytes수 * 2 즉, 워드의 갯수가 기록이 되어 있으니 다른 메모리 관리 함수가 블록을 접근할 때,
최상단의 워드개수를 확인하고 그 영역의 크기를 짐작하고 워드개수를 지나 최하단의 영역(블록의 최하단)에는 0x0000또는 0xFFFF가 기록되어 있으니 0x0000인 경우엔 자유롭게 사용할 수 있도록 내버려 두고 0xFFFF인 경우엔 I/O영역이니 특별히 관리하겠다는 뜻이다.