gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

DS development > DS Memory Expansion Pak

#116624 - chishm - Sat Jan 27, 2007 2:18 pm

I got the DS (Lite) Browser today, so naturally I played with the memory pak a bit. Since I haven't seen any similar topics, I thought I'd create one about the Memory Expansion Pak. Some of this information was obtained from MoonLight's ImageView application, although I'm not sure he is the original author. If you are the original author, post here to acknowledge this.

The Pak contains 8 MiB of usable RAM.
There is a header at 0x080000B0 consisting of:
Code:
FF FF 00 00 00 24 24 24 FF FF FF FF FF FF FF 7F

To use the memory, you have to unlock it. The unlock register is at 0x08240000. Write 0x0001 there to unlock the RAM, 0x0000 to lock it.
The RAM starts at 0x09000000 and ends at 0x09800000, and is 8 MiB in size.
The memory from 0x08000000 to 0x09000000 is not writable. It is all 0xFFFF except for the header and the unlock register.
You need to write at least 16 bits at a time (bytes will be mirrored across a 16 bit address).
To access the cart from the ARM9, you'll need to give the ARM9 access to Slot-2.
_________________
http://chishm.drunkencoders.com
http://dldi.drunkencoders.com

#116625 - simonjhall - Sat Jan 27, 2007 2:36 pm

Wow, that sounds pretty easy! Well done!

I'm kinda disappointed that it's slot-2 though - I'd hoped that I'd be slot-1, allowing people with RAM-less cards (like the gbamp) get access to extra memory. Plus I'd imagine there would be better performance from slot 1...

Now whatever happened to having that library which'd wrap all up all these 'extra RAM' solutions?
_________________
Big thanks to everyone who donated for Quake2

#116668 - chishm - Sun Jan 28, 2007 2:03 am

Well as I said, some of the info came from MoonLight's Image Viewer application, which contains a basic RAM expansion library.

Slot-1 is impossible to use for directly addressed memory. The slot-2 expansion is good for use with slot-1 media adapters. Being an official Nintendo product, it is a lot easier to buy too.
_________________
http://chishm.drunkencoders.com
http://dldi.drunkencoders.com

#116669 - tepples - Sun Jan 28, 2007 2:26 am

chishm wrote:
The slot-2 expansion is good for use with slot-1 media adapters. Being an official Nintendo product, it is a lot easier to buy too.

Only in Japan, Europe, and Australia. In the United States and Canada, both SuperCard and Nintendo DS Browser are import products.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#116673 - Firon - Sun Jan 28, 2007 2:43 am

It's pretty easy to find online, though.

#116687 - chishm - Sun Jan 28, 2007 11:22 am

tepples wrote:
In the United States and Canada, both SuperCard and Nintendo DS Browser are import products.

That's because North America is such a minor market that products are only released there as an afterthought. :p
_________________
http://chishm.drunkencoders.com
http://dldi.drunkencoders.com

#116689 - Lick - Sun Jan 28, 2007 11:57 am

Oh don't be silly, think about all the PS3's sold in Europe!


None.
_________________
http://licklick.wordpress.com

#116877 - silent_code - Tue Jan 30, 2007 4:54 pm

Lick wrote:
Oh don't be silly, think about all the PS3's sold in Europe! None.


because the ps3 wasn't released here, yet. ;P launch is on 27-3-2007 in eu and au.

#116913 - Lynx - Tue Jan 30, 2007 11:16 pm

That's his point.. talk about a minor market.. :)
_________________
NDS Homebrew Roms & Reviews

#116988 - Lazy1 - Wed Jan 31, 2007 4:03 pm

Thanks for the information, now those who have this cart can use it to get 4MB of ram when using Mini vMac :)

#116993 - 0xtob - Wed Jan 31, 2007 5:34 pm

Lazy1:
How have you eliminated 8 bit writes? Did you manually wrap each 8bit write to a read 16bit, modify, write 16bit operation?
_________________
http://blog.dev-scene.com/0xtob | http://nitrotracker.tobw.net | http://dsmi.tobw.net

#116995 - Lazy1 - Wed Jan 31, 2007 5:47 pm

Well, the Mini vMac sources for the most part wrapped all the memory reads/writes except for a few places.
To find the remaining areas I modified desmume to log 8bit writes to the gba slot and exit.

I replaced the 8bit write wrapper with code provided by sgstair to do the read/modify/write stuff, then went around with addr2line and the desmume output fixing the areas where 8bit writes still existed.

#117096 - Dood77 - Thu Feb 01, 2007 7:02 am

Does anyone know how fast the access to this memory is in comparison to the supercard/m3 ram?

#117661 - OOPMan - Tue Feb 06, 2007 1:12 pm

simonjhall wrote:

Now whatever happened to having that library which'd wrap all up all these 'extra RAM' solutions?


Hehehe, we discussed it a while back. Unfortunately, no one actually got around to doing said wrapping up :-)

Somewhere in the world there might be a saying along the lines of:

"Man who start thread should implement solution"

Happily, I don't live in that part of the world, and hence I happily start threads on topics such as RAM libs, OSes and more without intending to do the implementing myself ;-)
_________________
"My boot, your face..." - Attributed to OOPMan, Emperor of Eroticon VI

You can find my NDS homebrew projects here...

#117711 - HyperHacker - Tue Feb 06, 2007 9:16 pm

I think the problem was nobody was quite sure how extended memory should be handled, since different programs need different things. Some need one big 36MB block while others can live with two, and some would need to be able to malloc() chunks of the extended memory while others (emulators, for example) would be better off with direct access.
_________________
I'm a PSP hacker now, but I still <3 DS.

#117713 - Lazy1 - Tue Feb 06, 2007 9:28 pm

The cart detection and lock/unlock functions I use in Mini vMac DS could be used as the base of a library I guess.
It would require some work though since the M3 codes don't seem to work on all carts and I don't have the hardware to debug/fix it and my email to the company hasn't been replied to yet.

Code:

#ifdef UseGBARam
   #define MakeLong( a, b, c, d ) ( ( d << 24 ) + ( c << 16 ) + ( b << 8 ) + a )

   extern void setCache( unsigned int Flags );

   typedef struct CartAPI_S {
      void ( *SetCart_RAM ) ( void );
      void ( *SetCart_Disk ) ( void );

      si5b ( *GetCart_Base ) ( void );
      si5b ( *GetCart_Size ) ( void );
   } CartAPI_T;

   /**************************************************************************************/
   #define SC_UNLOCK *( ( vu16* ) 0x09FFFFFE )

   LOCALPROC Supercard_Set_RAM( void ) {
      SC_UNLOCK = 0xA55A;
      SC_UNLOCK = 0xA55A;
      SC_UNLOCK = 0x0005;
      SC_UNLOCK = 0x0005;

      DC_FlushAll( );
      setCache( 0x4A );
   }

   LOCALPROC Supercard_Set_Disk( void ) {
      SC_UNLOCK = 0xA55A;
      SC_UNLOCK = 0xA55A;
      SC_UNLOCK = 0x0003;
      SC_UNLOCK = 0x0003;

      DC_FlushAll( );
      setCache( 0x42 );
   }

   LOCALFUNC si5b Supercard_Base( void ) {
      return ( si5b ) 0x08000000;
   }

   LOCALFUNC si5b Supercard_Size( void ) {
      return ( si5b ) ( 32 * 1024 * 1024 );
   }
   /**************************************************************************************/
   #define OPERA_UNLOCK *( ( vu16* ) 0x08240000 )

   LOCALPROC Opera_Set_RAM( void ) {
      OPERA_UNLOCK = 0x0001;

      DC_FlushAll( );
      setCache( 0x4A );
   }

   LOCALPROC Opera_Set_Disk( void ) {
      OPERA_UNLOCK = 0x0000;

      DC_FlushAll( );
      setCache( 0x42 );
   }

   LOCALFUNC si5b Opera_Base( void ) {
      return ( si5b ) 0x09000000;
   }

   LOCALFUNC si5b Opera_Size( void ) {
      return ( si5b ) ( 8 * 1024 * 1024 );
   }

   LOCALFUNC blnr Opera_Detect( void ) {
      Opera_Set_RAM( );
      *( ( vu16* ) 0x09000000 ) = 0xF00D;

      if ( *( ( vu16* ) 0x09000000 ) == 0xF00D ) {
         Opera_Set_Disk( );
         return trueblnr;
      }

      return falseblnr;
   }
   /**************************************************************************************/
   #define M3_MODE_ROM 0x00400004
   #define M3_MODE_MEDIA 0x00400003
   #define M3_MODE_RAM_R 0x00400002
   #define M3_MODE_RAM_RW 0x00400006 // read-write access

   static u16 M3_readHalfword( u32 addr ) {
         return *( ( vu16* ) addr );
   }

   void M3_changeMode( u32 mode ) {
      M3_readHalfword( 0x08e00002 );
      M3_readHalfword( 0x0800000e );
      M3_readHalfword( 0x08801ffc );
      M3_readHalfword( 0x0800104a );
      M3_readHalfword( 0x08800612 );
      M3_readHalfword( 0x08000000 );
      M3_readHalfword( 0x08801b66 );
      M3_readHalfword( 0x08000000 + ( mode << 1 ) );
      M3_readHalfword( 0x0800080e );
      M3_readHalfword( 0x08000000 );

      if ( ( mode & 0x0f ) == 4 ) {   // unlock ROM addr >= 0x200
         M3_readHalfword( 0x080001e4 );
         M3_readHalfword( 0x080001e4 );
         M3_readHalfword( 0x08000188 );
         M3_readHalfword( 0x08000188 );
      } else {
         M3_readHalfword( 0x09000000 );
      }
   }

   LOCALPROC M3_Set_RAM( void ) {
      M3_changeMode( M3_MODE_RAM_RW );

      DC_FlushAll( );
      setCache( 0x4A );
   }

   LOCALPROC M3_Set_Disk( void ) {
      M3_changeMode( M3_MODE_MEDIA );

      DC_FlushAll( );
      setCache( 0x42 );
   }

   LOCALFUNC si5b M3_Base( void ) {
      return ( si5b ) 0x08000000;
   }

   LOCALFUNC si5b M3_Size( void ) {
      return ( 32 * 1024 * 1024 );
   }
   /**************************************************************************************/

   CartAPI_T GBACart;

   LOCALFUNC blnr InitGBACart( void ) {
      struct stat st;

      if ( stat( "/.", &st ) == 0 ) {
         if ( st.st_dev == MakeLong( 'S', 'C', 'C', 'F' ) || st.st_dev == MakeLong( 'S', 'C', 'S', 'D' ) ) {
            GBACart.SetCart_RAM = Supercard_Set_RAM;
            GBACart.SetCart_Disk = Supercard_Set_Disk;
            GBACart.GetCart_Base = Supercard_Base;
            GBACart.GetCart_Size = Supercard_Size;

            iprintf( "Found Supercard CF or Supercard SD\n" );
            return trueblnr;
         }

         if ( st.st_dev == MakeLong( 'M', '3', 'C', 'F' ) || st.st_dev == MakeLong( 'M', '3', 'S', 'D' ) ) {
            GBACart.SetCart_RAM = M3_Set_RAM;
            GBACart.SetCart_Disk = M3_Set_Disk;
            GBACart.GetCart_Base = M3_Base;
            GBACart.GetCart_Size = M3_Size;

            iprintf( "Found M3 CF or M3 SD\n" );
            return trueblnr;
         }

         if ( Opera_Detect( ) == trueblnr ) {
            GBACart.SetCart_RAM = Opera_Set_RAM;
            GBACart.SetCart_Disk = Opera_Set_Disk;
            GBACart.GetCart_Base = Opera_Base;
            GBACart.GetCart_Size = Opera_Size;

            iprintf( "Found Opera memory expansion\n" );
            return trueblnr;
         }
      }

      return falseblnr;
   }
#endif


I'm not sure about DC_FlushAll() but I'm pretty sure you won't be needing setCache().

#117715 - HyperHacker - Tue Feb 06, 2007 9:40 pm

You test for the Opera expansion by writing to it and reading it back? Why not just look for the header?
_________________
I'm a PSP hacker now, but I still <3 DS.

#117718 - Lazy1 - Tue Feb 06, 2007 9:57 pm

I guess I could have, but I didn't think of that at the time.
Thanks for the suggestion though, that will probably be more reliable.

#117734 - Lick - Wed Feb 07, 2007 12:34 am

That's a nice piece of pasting you have there! Thanks Lazy1!

*bookmarks*
_________________
http://licklick.wordpress.com

#122178 - Genoil - Sat Mar 17, 2007 5:53 pm

Thanks, I've been using this code to err.. see if it actually works! And it does, but I've changed the detection mechanism (simplified the code aswell):

Code:

int Mempak_Detect( void ) {
  MEMPAK_UNLOCK = 0x0001;

  int i = 0;
  int * header = (int *)0x080000B0;
  int check[]  = {0x0000FFFF, 0x24242400, 0xFFFFFFFF, 0x7FFFFFFF};
 
  while(*(header++) == *(check+i) && i < 4) i++;   

  return i==4;
}


Now that this worked, I wanted to test some actual writing and reading, so I used the code in your (lazy1) detection function to do a write, read it back and test if it is still the same. But somehow it doesn't seem to matter what address I use for writing/reading. If I use 0x08000000 instead of 0x09000000, it will still pass. Or 0x9900000 for that matter.

Now I have to admit that I've never actually been doing any DS development before, and I read C better than I write it, but what am I doing wrong here? How would I actually be able to find the writable parts of the available address space like chishm did?

[edit]

Well I'm still not 100% sure about what went wrong here, but I created something with visual feedback and in there it appears that indeed only the area between 0x09000000 and 0x09800000 is writable.

[edit]

Well now I'm 100% sure I put some code in the wrong place because I tried again and now it works as expected...