23 Soft Reset 분석

원문 :  http://kkamagui.springnote.com/pages/547779

 

들어가기 전에...

 

0.시작하면서...

 홈브루 개발자라면 한번쯤은 자신이 개발한 홈브루도 소프트 리셋(전원 버튼을 누르지 않고 재부팅 시키는 방법)에 대해서 고민해 봤을 것이다. 문쉘(MoonShell)을 보면 플러그인 기능으로 Reset.mse를 통해 소프트 리셋(Soft Reset) 기능을 지원한다. 하지만 사실 문쉘 소스에 있는 Reset.mse는 모든 카드를 지원하지 않는다. 아무래도 제작자가 다 테스트 해보기엔 무리였을지도...

 그런데 얼마전에 3 in 1 Expansion Pack 을 쓰면서 우연히 Rudolph(루돌프인가.. ㅡ_ㅡa..)라는 분이 이 문제를 해결했다는 것을 알았다. 이분 덕택에 문쉘 소프트 리셋 기능이 잘 동작하게 된듯....

 이제 본격적으로 한번 분석해 보자. 

 

1.ARM9 코드 분석

  1. //---------------------------------------------------------------------------------
    int main(void) {
    //---------------------------------------------------------------------------------
      MSEINFO_Readed=MSE_GetMSEINFO(&MSEINFO);
     
      REG_IME=0;
     
      POWER_CR = POWER_ALL_2D;
    //  POWER_CR &= ~POWER_SWAP_LCDS;
      POWER_CR |= POWER_SWAP_LCDS;
     
      SetARM9_REG_WaitCR();
     
      irqInit();
     
      {
        void InitVRAM(void);
        InitVRAM();
        void ShowMSEINFO(void);
        ShowMSEINFO();
        void SoftReset(void);
        SoftReset();
      }
     
      while(1);

 Soft Reset 함수를 부르는거 말고는 별로 하는 것이 없는 듯 하다.

 

  1. void SoftReset(void)

    {

    const char *pname=MSEINFO.AdapterName;

     

    _consolePrintf("go to farmware menu. [%s]\n",pname);

     

    ERESET RESET=RESET_NULL;

     

    if(strcmp(pname,"M3CF")==0){

    cartSetMenuMode_M3CFSD();

    RESET=RESET_MENU_M3CF;

    }

    if(strcmp(pname,"M3SD")==0){

    cartSetMenuMode_M3CFSD();

    RESET=RESET_MENU_M3SD;

    }

    if(strcmp(pname,"MPCF")==0){

    cartSetMenuMode_MPCF();

    RESET=RESET_MENU_MPCF;

    }

    if(strcmp(pname,"SCCF")==0){

    cartSetMenuMode_SCCFSD();

    RESET=RESET_MENU_SCCF;

    }

    if(strcmp(pname,"SCSD")==0){

    cartSetMenuMode_SCCFSD();

    RESET=RESET_MENU_SCSD;

    }

    if(strcmp(pname,"SCLT")==0){

    cartSetMenuMode_SCCFSD();

    RESET=RESET_MENU_SCSD;

    }

    if(strcmp(pname,"EZSD")==0){

    cartSetMenuMode_EZSD();

    RESET=RESET_MENU_EZSD;

    }

    if(strcmp(pname,"DLMS")==0){

    RESET=RESET_MENU_DSLink;

    IPCEX->RESET=RESET;

    LinkReset_ARM9();

    while(1);

    }

     

    //====== R4TF was added.

    if(strcmp(pname,"R4TF")==0){

    if(FAT_InitFiles() == false) {

    _consolePrintf("Can not initialized FAT.\n");

    while(1);

    }

    FAT_FILE *r4 = FAT_fopen("/_DS_MENU.DAT", "rb");

    (*(vu32*)0x027FFE18) = r4->dirEntSector*512+r4->dirEntOffset*32; <== 이부분 주의

    FAT_fclose(r4);

    _consolePrintf("_DS_MENU.DAT = %08X\n", *(vu32*)0x027FFE18);

     

    _boot_VRAM_clear();

     

    RESET=RESET_MENU_R4TF;

    IPCEX->RESET=RESET;

     

    REG_IME = 0;

    REG_IE = 0;

    REG_IF = REG_IF;

     

    WAIT_CR = 0xE880;

    REG_IPC_SYNC = 0;

    DMA0_CR = 0;

    DMA1_CR = 0;

    DMA2_CR = 0;

     

    ret_menu9_R4();

    while(1);

    }

     

    //====== EZ5S was added.

    if(strcmp(pname,"EZ5S")==0){

    RESET=RESET_MENU_EZ5S;

    IPCEX->RESET=RESET;

    ret_menu9_EZ5();

    while(1);

    }

    //====== by Rudolph (2007/05/25)

     

     

    if(RESET==RESET_NULL){

    _consolePrintf("not support adapter type.\n");

    while(1);

    }

     

    *(vu16*)(0x04000208) = 0; //REG_IME = IME_DISABLE;

    *(vu16*)(0x04000204) |= 0x0880; //sysSetBusOwners(false, false);

    *((vu32*)0x027FFFFC) = 0;

    *((vu32*)0x027FFE04) = (u32)0xE59FF018;

    *((vu32*)0x027FFE24) = (u32)0x027FFE04;

     

    IPCEX->RESET=RESET;

     

    asm("swi 0x00"); //swiSoftReset();

    asm("bx lr");

     

    while(1);

    }

 (*(vu32*)0x027FFE18) = r4->dirEntSector*512+r4->dirEntOffset*32; 코드에서 실제로 카드 내에 FAT 영역의 주소를 ARM7에 넘겨줘서 ARM7이 카드 명령을 통해 데이터를 읽게 만드는 부분이 중요한 부분인것 같다. ARM7에서도 카드에 접근해서 데이터를 읽을 수 있음을 보여주는 예이다.

 이부분에 대한 자세한 내용은 아래의 1.1 참고 부분을 참조하자. 

 

 아래는 위에서 호출하는 _boot_VRAM_clear() 함수와 ret_menu9_R4() 함수이다.

  1. //====== R4TF was added.
    static void _boot_VRAM_clear()
    {
    /*********
     u16 _VRAM_C_CR=VRAM_C_CR;
     u16 _VRAM_D_CR=VRAM_D_CR;
  2.  VRAM_C_CR = VRAM_ENABLE | (1 | 0);
     VRAM_D_CR = VRAM_ENABLE | (1 | (1<<3));
  3.  memset((char*)0x06000000, 0x00, 0x40000);
  4.  VRAM_C_CR=_VRAM_C_CR;
     VRAM_D_CR=_VRAM_D_CR;
    *********/
  5. /* 덇렄밒궸?렑궠귢귡궴궞귣궻귒긏깏귺 */
     u16 *vr;
     int i;
  6.  u16 _VRAM_C_CR=VRAM_C_CR;
  7.  VRAM_C_CR = VRAM_ENABLE | VRAM_C_MAIN_BG_0x6000000;
  8.  vr = (u16*)0x06010000;
     for(i = 0; i < 0x10000/2; i++) {
      *vr = 0x0000;
      vr++;
     }
  9.  VRAM_C_CR=_VRAM_C_CR;
  10. }
    //====== by Rudolph (2007/05/25) 

 

  1.  .ALIGN
     .GLOBAL ret_menu9_R4
     .CODE 32
     .ARM
  2. ret_menu9_R4:
  3.  mov r0, #0x2000
     orr r0, r0, #0x78
     mov r1, #0x00
     mcr 15, 0, r0, cr1, cr0, 0
     mcr 15, 0, r1, cr7, cr5, 0
     mcr 15, 0, r1, cr7, cr6, 0
     mcr 15, 0, r1, cr7, cr10, 4
     orr r0, r0, #0x50000
     mcr 15, 0, r0, cr1, cr0, 0
  4.       ldr r0, =0x027FFDF8
          ldr r1, =0xE51FF004
          str r1, [r0, #0x0]   @ (027ffdf8)=E51FF004:ldr r15,[r15, #-0x4]
          str r0, [r0, #0x4]   @ (027ffdfC)=027FFDF8
  5.  bx r0    @ JUMP 027FFDF8
  6.  .END 

 위의 ret_menu9_R4까지 호출하면 ARM9은 처리가 완전히 끝난다. 

 

1.1 참고 (*(vu32*)0x027FFE18) = r4->dirEntSector*512+r4->dirEntOffset*32 코드 분석

 2007/07/26 테스트 결과  r4->dirEntSector 값은 실제 _DS_MENU.DAT가 존재하는 Directory Entry의 물리 섹터 번호를 의미하고 r4->dirEntOffset*32 값은 Directory Entry 내의 Offset을 의미했다. 위의 코드를 아래와 같이 고쳐서 테스트 해본 결과 0x2E0 섹터의 2번째 Offset에 _DS_MENU.DAT 파일이 존재하는 것으로 나왔는데, 실제 확인 결과 그러했다.

  1. //====== R4TF was added.
      if(strcmp(pname,"R4TF")==0){
     if(FAT_InitFiles() == false) {
      _consolePrintf("Can not initialized FAT.\n");
      while(1);
     }
     FAT_FILE *r4 = FAT_fopen("/_DS_MENU.DAT", "rb");
     (*(vu32*)0x027FFE18) = r4->dirEntSector*512+r4->dirEntOffset*32;
     _consolePrintf("_DS_MENU.DAT = %08X\n", *(vu32*)0x027FFE18);
     _consolePrintf("Entry Sector = %08X, Entry Offset = %08X\n", r4->dirEntSector, r4->dirEntOffset );
     FAT_fclose(r4);
     while( 1 );
     _boot_VRAM_clear();
  2.  RESET=RESET_MENU_R4TF;
     IPCEX->RESET=RESET;
  3.  REG_IME = 0;
     REG_IE = 0;
     REG_IF = REG_IF;
  4.  WAIT_CR = 0xE880;
     REG_IPC_SYNC = 0;
     DMA0_CR = 0;
     DMA1_CR = 0;
     DMA2_CR = 0;
  5.  ret_menu9_R4();
     while(1);
      } 

R4.PNG

<실제 분석한 화면>

 이것으로 보아 R4에는 디렉토리 엔트리 정보를 이용해서 실제 파일을 읽어들일 수 있음을 알 수 있었다. 

 또한 rebootlib 소스를 분석하면서 저 _DS_MENU.DAT 파일이 특수한 형태로 암호화된 nds 파일이라는 것을 알 수 있었고, ARM7 소스에서 왜 카드 명령을 통해 다시 읽어오는가도 짐작할 수 있었다. 리버싱을 통해 R4에게 명령을 내리고 R4 카드가 디코딩을 하면 그것을 다시 메모리에 복사하면 굳이 압축 해제 알고리즘을 몰라도 처리 가능하기 때문이었다.

 

 

2.ARM7 코드 분석

 아래는 ARM7에서 사용하는 함수 메인이다.

  1. __attribute__((noinline)) static void main_Proc_Reset(ERESET RESET)
    {
      switch(RESET){
        case RESET_NULL: return; break;
        case RESET_VRAM: {
          REG_IME = IME_DISABLE; // Disable interrupts
          REG_IF = REG_IF; // Acknowledge interrupt
          *((vu32*)0x027FFE34) = (u32)0x06000000; // Bootloader start address for VRAM
          swiSoftReset(); // Jump to boot loader
        } break;
        case RESET_GBAMP: boot_GBAMP(); break;
        case RESET_GBAROM: boot_GBAROM(); break;
        case RESET_MENU_DSLink: LinkReset_ARM7(); break;
        case RESET_MENU_MPCF: break;
        case RESET_MENU_M3CF: break;
        case RESET_MENU_M3SD: break;
        case RESET_MENU_SCCF: break;
        case RESET_MENU_SCSD: break;
        case RESET_MENU_EZSD: break;
    //====== R4TF was added.
        case RESET_MENU_R4TF: ret_menu7_R4(); break;
    //====== EZ5S was added.
        case RESET_MENU_EZ5S: ret_menu7_EZ5(); break;
    //====== by Rudolph (2007/05/25)
      }
  2.   *(vu16*)(0x04000208) = 0;       //REG_IME = IME_DISABLE;
      *((vu32*)0x027FFE34) = *((vu32*)0x027FFFF8);
      asm("swi 0x00");                //swiSoftReset();
      asm("bx lr");

 

  1. void ret_menu7_R4()
    {
     char *adr;
     u32 blk, siz;
     u32 i;
     u32 *mem;
  2.  REG_IME = 0;
     REG_IE = 0;
     REG_IF = REG_IF;
  3.  REG_IPC_SYNC = 0;
     DMA0_CR = 0;
     DMA1_CR = 0;
     DMA2_CR = 0;
     DMA3_CR = 0;
  4.  while((*(vu32*)0x027FFDFC) != 0x027FFDF8); // Timing adjustment with ARM9
  5.  mem = (u32*)0x02000000;
     for(i = 0; i < 0x3FF800/4; i++) {
      *mem = 0x00000000;
      mem++;
     }
    // memset((u8*)0x2000000, 0x00, 0x3FF800);
  6.  while(_set_r4menu());
  7.  adr = (char*)0x027FFE00;
     _read_r4menu(adr, 0);   // Header

  8.  blk = (*(vu32*)0x027FFE20) / 512;
     adr = (char*)(*(vu32*)0x027FFE28);
     siz = (*(vu32*)0x027FFE2C);
     for(i = 0; i < siz; i += 512) {  // ARM9
      _read_r4menu(adr, blk);
      blk++;
      adr += 512;
     }
  9.  blk = (*(vu32*)0x027FFE30) / 512;
     adr = (char*)(*(vu32*)0x027FFE38);
     siz = (*(vu32*)0x027FFE3C);
     for(i = 0; i < siz; i += 512) {  // ARM7
      _read_r4menu(adr, blk);
      blk++;
      adr += 512;
     }
  10.  *(vu32*)0x027FFDFC = *(vu32*)0x027FFE24;
     asm("swi 0x00");   // JUMP 0x027FFE34
  11.  while(1);

 

  1. static int _set_r4menu()
    {
     u32 add;
  2.  add = (*(vu32*)0x027FFE18); <== 이 부분이 _DS_MENU.DAT에 대한 FAT 정보를 읽는 부분이다.
  3.  while(CARD_CR2 & CARD_BUSY);
  4.  CARD_CR1H = 0xC0;
     CARD_COMMAND[0] = 0xB4;
     CARD_COMMAND[1] = (add >> 24) & 0xFF;
     CARD_COMMAND[2] = (add >> 16) & 0xFF;
     CARD_COMMAND[3] = (add >> 8) & 0xFF;
     CARD_COMMAND[4] = add & 0xFF;
    // CARD_COMMAND[5] = 0x00;
    // CARD_COMMAND[6] = 0x00;
    // CARD_COMMAND[7] = 0x00;
  5.  CARD_CR2 = 0xA7586000;
     while(!(CARD_CR2 & CARD_DATA_READY));
  6.  return(CARD_DATA_RD);

 

  1. static int _read_r4menu(char *buf, u32 blk)
    {
     int s = 0;
     u32 *buf32;
  2.  buf32 = (u32*)buf;
     blk *= 2;

  3.  do {
      while(CARD_CR2 & CARD_BUSY);
      CARD_CR1H = 0xC0;
      CARD_COMMAND[0] = 0xB6;
      CARD_COMMAND[1] = (blk >> 16) & 0xFF;
      CARD_COMMAND[2] = (blk >> 8) & 0xFF;
      CARD_COMMAND[3] = blk & 0xFF;
      CARD_COMMAND[4] = 0x00;
    //  CARD_COMMAND[5] = 0x00;
    //  CARD_COMMAND[6] = 0x00;
    //  CARD_COMMAND[7] = 0x00;
      CARD_CR2 = 0xA7586000;
      while(!(CARD_CR2 & CARD_DATA_READY));
     } while(CARD_DATA_RD);
  4.  while(CARD_CR2 & CARD_BUSY);

  5.  CARD_CR1H = 0xC0;
     CARD_COMMAND[0] = 0xBF;
     CARD_COMMAND[1] = (blk >> 16) & 0xFF;
     CARD_COMMAND[2] = (blk >> 8) & 0xFF;
     CARD_COMMAND[3] = blk & 0xFF;
     CARD_COMMAND[4] = 0x00;
    // CARD_COMMAND[5] = 0x00;
    // CARD_COMMAND[6] = 0x00;
    // CARD_COMMAND[7] = 0x00;
     CARD_CR2 = 0xA1586000;
  6.  do {
      while(!(CARD_CR2 & CARD_DATA_READY));
      *buf32 = CARD_DATA_RD;
      buf32++;
      s += 4;
     } while(CARD_CR2 & CARD_BUSY);
  7.  return(s);
    }

 

 위에서 사용된 CARD 관련 명령들은 libnds.h에 정의되어있으며 아래와 같다. 

  1. // Card bus
    #define CARD_CR1       (*(vuint16*)0x040001A0)
    #define CARD_CR1H      (*(vuint8*)0x040001A1)
    #define CARD_EEPDATA   (*(vuint8*)0x040001A2)
    #define CARD_CR2       (*(vuint32*)0x040001A4)
    #define CARD_COMMAND   ((vuint8*)0x040001A8)
  2. #define CARD_DATA_RD   (*(vuint32*)0x04100010)
  3. #define CARD_1B0       (*(vuint32*)0x040001B0)
    #define CARD_1B4       (*(vuint32*)0x040001B4)
    #define CARD_1B8       (*(vuint16*)0x040001B8)
    #define CARD_1BA       (*(vuint16*)0x040001BA)

  4. #define CARD_CR1_ENABLE  0x80  // in byte 1, i.e. 0x8000
    #define CARD_CR1_IRQ     0x40  // in byte 1, i.e. 0x4000

  5. // CARD_CR2 register:
  6. #define CARD_ACTIVATE   (1<<31)  // when writing, get the ball rolling
    // 1<<30
    #define CARD_nRESET     (1<<29)  // value on the /reset pin (1 = high out, not a reset state, 0 = low out = in reset)
    #define CARD_28         (1<<28)  // when writing
    #define CARD_27         (1<<27)  // when writing
    #define CARD_26         (1<<26)
    #define CARD_22         (1<<22)
    #define CARD_19         (1<<19)
    #define CARD_ENCRYPTED  (1<<14)  // when writing, this command should be encrypted
    #define CARD_13         (1<<13)  // when writing
    #define CARD_4          (1<<4)   // when writing
  7. // 3 bits in b10..b8 indicate something
    // read bits
    #define CARD_BUSY       (1<<31)  // when reading, still expecting incomming data?
    #define CARD_DATA_READY (1<<23)  // when reading, CARD_DATA_RD or CARD_DATA has another word of data and is good to go

 CARD에 직접 명령을 내려서 이것을 처리할려면 R4에 대해서 자세히 안다는 전제가 필요한데... 정말 대단한 사람들이 아닐 수 없다. 이걸 다 어떻게 분석한거지... ㅡ_ㅡ;;;

 Card에 대한 내용은 GBATEK(http://nocash.emubase.de/gbatek.htm#dsmemorycontrolcartridgesandmainram)에서 찾을 수 있다.

 

 

3.libfat 코드 분석 

 구버전의 GBA FS는 File ID에 FILE_STRUCTURE 포인터를 그대로 넘기도록 되어있어서 File ID를 이용하면 FAT 관련 정보를 모두 얻어낼 수 있었다. 하지만 libfat로 업그레이드 되면서 POSIX 표준 함수(fopen, fwrite, fread, fclose 등)을 지원하게 되었고 구조가 조금 달라지게 되었다. 물론 FILE* 값을 이용해서 fileno() 함수로 File ID를 얻어올 수 있지만 테스트 결과 GBA FS 처럼 FILE_STRUCTURE로 캐스팅해서 값을 정상적으로 얻을 수 없었다(물론 잠결에 테스트 했기때문에, 좀더 테스트를 진행해 봐야 한다.. ㅡ_ㅡa..)

 다행이도 libfat의 Partition 관련 변수가 export 되어있으므로 그 변수를 이용해서 FAT 관련 기본정보를 얻을 수 있고, diropen() 과 같은 함수를 사용하면 Directry 구조체를 얻을 수 있는데 이것을 이용하면 Directory Entry에 대한 정보를 얻을 수 있으므로 GBA FS 수준의 정보를 얻을 수 있다. 

3.1 dir.h 분석

  1. /* Directory iterator for mantaining state between dir* calls */
    typedef struct {
        int device;
        void *dirStruct;
    } DIR_ITER;
  2. DIR_ITER* diropen (const char *path);
    int dirreset (DIR_ITER *dirState);
    int dirnext (DIR_ITER *dirState, char *filename, struct stat *filestat);
    int dirclose (DIR_ITER *dirState); 

 위의 코드를 보면 diropen() 함수는 DIR_ITER* 를 리턴한다. 구조체의 dirStruct 필드가 심상치 않은데, 예상대로 아래에서 보듯 DIR_STATE_STRUCT 구조체를 얻을 수 있다. 

 

3.2 fatdir.c 

  1. int _FAT_dirnext_r (struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat) {
     DIR_STATE_STRUCT* state = (DIR_STATE_STRUCT*) (dirState->dirStruct);
  2.  // Make sure we are still using this entry
     if (!state->inUse) {
      r->_errno = EBADF;
      return -1;
     } 

 DIR_STATE_STRUCT는 아래에서 볼 수 있다.

 

3.3 fatdir.h 

  1. typedef struct {
     PARTITION* partition;
     DIR_ENTRY currentEntry;
     u32 startCluster;
     bool inUse;
     bool validEntry;
    } DIR_STATE_STRUCT;

 만세, PARTITION 정보와 DIR_ENTRY 구조체 정보를 얻을 수 있다.

 

3.4 patition.h 

  1. #ifdef NDS
    typedef enum {PI_DEFAULT, PI_SLOT_1, PI_SLOT_2, PI_CUSTOM} PARTITION_INTERFACE;
    #else
    typedef enum {PI_CART_SLOT} PARTITION_INTERFACE;
    #endif
  2. typedef struct {
     u32 fatStart;
     u32 sectorsPerFat;
     u32 lastCluster;
     u32 firstFree;
    } FAT;
  3. typedef struct {
     const IO_INTERFACE* disc;
     CACHE* cache;
     // Info about the partition
     bool readOnly;  // If this is set, then do not try writing to the disc
     FS_TYPE filesysType;
     u32 totalSize;
     u32 rootDirStart; <== 실제 Root Directory가 시작되는 물리 섹터 번호. 이것으로 접근하면 바로 Root Directory를 찾을 수 있다.
     u32 rootDirCluster;
     u32 numberOfSectors;
     u32 dataStart;
     u32 bytesPerSector;
     u32 sectorsPerCluster;
     u32 bytesPerCluster;
     FAT fat;
     // Values that may change after construction
     u32 cwdCluster;   // Current working directory cluser
     u32 openFileCount;
    } PARTITION;

 FAT에 관련된 거의 대부분의 정보는 PARTITION 구조체에서 얻을 수 있다. IO_INTERFACE 구조체를 이용하면 현재 사용중인 Device의 타입을 알 수 있다.

 

  1. extern PARTITION* _FAT_partitions[];

 위와 같이 사용하면 실제 libfat에 있는 파티션 정보를 얻어올 수 있고, 인덱스로 PI_DEFAULT( 0 ), PI_SLOT_1( 1 ), PI_SLOT_2( 2 ), PI_CUSTOM( 3 )과 같은 값을 넘겨주면 해당 Partition 정보에 접근할 수 있다. 보통 Default로 설정하여 libfat를 사용하므로 PI_DEFAULT or 0을 넣으면 Partition 정보를 얻을 수 있다.

 

3.5 disc_io.h

  1. #define FEATURE_MEDIUM_CANREAD  0x00000001
    #define FEATURE_MEDIUM_CANWRITE  0x00000002
    #define FEATURE_SLOT_GBA   0x00000010
    #define FEATURE_SLOT_NDS   0x00000020
  2. typedef bool (* FN_MEDIUM_STARTUP)(void) ;
    typedef bool (* FN_MEDIUM_ISINSERTED)(void) ;
    typedef bool (* FN_MEDIUM_READSECTORS)(u32 sector, u8 numSecs, void* buffer) ;
    typedef bool (* FN_MEDIUM_WRITESECTORS)(u32 sector, u8 numSecs, void* buffer) ;
    typedef bool (* FN_MEDIUM_CLEARSTATUS)(void) ;
    typedef bool (* FN_MEDIUM_SHUTDOWN)(void) ;

  3. typedef struct {
     unsigned long   ul_ioType ;
     unsigned long   ul_Features ;
     FN_MEDIUM_STARTUP  fn_StartUp ;
     FN_MEDIUM_ISINSERTED fn_IsInserted ;
     FN_MEDIUM_READSECTORS fn_ReadSectors ;
     FN_MEDIUM_WRITESECTORS fn_WriteSectors ;
     FN_MEDIUM_CLEARSTATUS fn_ClearStatus ;
     FN_MEDIUM_SHUTDOWN  fn_Shutdown ;
    } IO_INTERFACE, *LPIO_INTERFACE ;

 ul_ioType을 자세히 보면 4Byte의 Charactor ASCII 값으로 되어있고 0Byte ~ 3Byte의 순서로 "M3SD", "M3CF", "R4TF" 와 같은 문자들이 들어있다. 이것을 이용하면 현재 사용중인 Device의 정보도 알 수 있다.

 

3.6 Directory Navigation으로 정보 뽑기 예제

 첨부에 있는 msev10_reset_R4EZ5_kkamagui.zip의 ARM9 코드를 조금 수정하여 GBA FS를 사용하지 않고 libfat를 이용해서 정보를 뽑아낸 예제이다.

  1. //====== R4TF was added.
      if(strcmp(pname,"R4TF")==0)
      {
        PARTITION* pstPartition;
        DIR_ITER* pstCur;
        DIR_STATE_STRUCT* pstDirState;
        int iOffset;
        int iRootDirSector;
       
        char vcBuffer[ 256 ];
       
        fatInitDefault();
       
        iOffset = -1;
        pstCur = diropen( "/" );
        if( pstCur == NULL )
        {
      _consolePrintf("Can Not Open /_DS_MENU.DAT File.\n");
            while( 1 );
        }
        while( dirnext( pstCur, vcBuffer, NULL ) == 0 )
        {
            pstDirState = ( DIR_STATE_STRUCT* ) pstCur->dirStruct; <== 여기가 포인트~ pstDirState를 이용하면 FAT 관련 섹터정보를 얻을 수 있음 자세한건 위의 구조체 참조
            _consolePrintf( "%s %08X\n",
                vcBuffer, pstDirState->currentEntry.dataStart.offset );
            if( strcmp( vcBuffer, "_DS_MENU.DAT" ) == 0 )
            {
                // Offset 자체가 1부터 카운팅 되기 때문에 실제 인덱스를 알려면
                // 1을 빼야한다.
                iOffset = pstDirState->currentEntry.dataStart.offset - 1;
               
                _consolePrintf( "DirEntry = %08X\n", pstDirState->currentEntry.dataStart.offset );
                break;
            }
        }
        dirclose( pstCur );
       
        pstPartition = _FAT_partitions[ 0 ];
        _consolePrintf("partition = %08X %08X\n", 
            pstPartition->rootDirStart, pstPartition->bytesPerSector );
        iRootDirSector = pstPartition->rootDirStart;
  2.     // R4 CART에 넘겨줄 _DS_MENU.DAT의 정보가 담긴 Directory Offset 값
        (*(vu32*)0x027FFE18) = iRootDirSector * 512 + iOffset * 32;
        
        while( 1 ); 

Directory_테스트.PNG

<실행 결과>

 

4.마치면서... 

 간단히 NDS를 소프트웨어적으로 리셋하는 코드에 대해서 살펴보았다. 조금씩 Device 마다 차이는 있지만 결국 NDS의 상태를 초기상태와 비슷하게 만들거나 또는 Firmware를 강제로 메모리에 올린다음 그것을 실행하게 방식으로 처리하는 것을 알 수 있었다.

 더 궁금한 점은 첨부에 올려놓은 소스를 보면 될 것 같고, 다음에는 이것을 이용하여 만든 Reset Library를 소개하고 사용법에 대해서 알아보겠다.

 

5.첨부 

 

 

이 글은 스프링노트에서 작성되었습니다.

+ Recent posts