Skip to content

M3 DS Real

lifehackerhansol edited this page Apr 13, 2023 · 5 revisions

M3 DS Real

This page documents a subset of extra card commands supported by the M3 DS Real (herein M3). This page was written by lifehackerhansol based on previous documentation by Taiju Yamada (nee Xenon++).

Commands: what are they, what do they do (or at least some of them)

M3's original DLDI uses several different commands for reading and writing. But we will be looking at a different set of commands; these align with the implemenation from the Original R4. These are a little slower, but easier to implement.

At some point the faster read/write commands will be documented...

Below are commands documented by Taiju Yamada in u64, as follows:

XXXXXXXX XXXXXXXX

Each two digits, from left to right, is REG_CARD_COMMAND[0] to REG_CARD_COMMAND[7] as defined in libnds.
Lowercase x will imply that this is unused. Any others are explained.

B0xxxxxx xxxxxxxx

  • Initialization / Get jumper pins
  • This is identical to the original R4's implementation.

BDaaaaaa aaxxxxxx

  • aaaaaaaa: sector address (Don't need to x512)
  • preparation to read from SD/TF sector. wait until reading zero.

BAaaaaaa aaxxxxxx

  • aaaaaaaa: sector address (Don't need to x512)
  • read from SD/TF sector

BEaaaaaa aaxxxxxx

  • aaaaaaaa: sector address (Don't need to x512)
  • write to SD/TF sector

BCaaaaaa aaxxxxxx

  • aaaaaaaa: sector address (Don't need to x512)
  • completion to write SD/TF sector. wait until reading zero.

CDxxxxxx xxxxxxxx

  • Get cart region
  • M3 has JP/EU/US regions; the only difference appears to be that the encryption key for the boot file are different.

Basic FAT pseudocode(?)

Below describe basic SD card read/write routines, just enough to get a FAT file system up and running.

Here, we also use cardPolledTransfer from libnds. See here for reference: https://github.com/devkitPro/libnds/blob/master/source/common/card.c

Read

The following card command only supports single-sector reads of size 0x200 bytes. Unlike the original R4, sector addresses need not be << 9.

Read 1 sector:

CARD_COMMAND = BDaaaaaa aaxxxxxx
aaaaaaaa = sector address (highest bit to lowest bit)
REG_ROMCTRL = 0xa7586000
read REG_CARD_DATA_RD once
// the output isn't important but we need to flush it before doing anything else
// note, devkitPro's cardPolledTransfer supports NULL destination, you can use that

CARD_COMMAND = BAaaaaaa aaxxxxxx
aaaaaaaa = sector address (highest bit to lowest bit) // unsure if required
REG_ROMCTRL = 0xa1586000
With the above control flag and command, do cardPolledTransfer to 0x200 byte buffer

To read more than 0x200 bytes, simply loop the above.

NOTE: devkitPro's cardPolledTransfer does not support unaligned buffers. Write a version of cardPolledTransfer yourself that handles it.

Write

Writing has the exact same restriction as read under these commands.

Writing 1 sector:

CARD_COMMAND = BEaaaaaa aaxxxxxx
aaaaaaaa = sector address (highest bit to lowest bit)

REG_ROMCTRL = 0xe1586000;
// from here, we need a reverse of cardPolledTransfer that will write u32 chunks to REG_CARD_DATA_RD
// make sure it handles unaligned buffers!
// loop until 0x200 bytes (128 u32s) are written

// instruct cart that data end
CARD_COMMAND = BCaaaaaa aaxxxxxx
aaaaaaaa = u32 sector (highest bit to lowest bit) // it is unknown whether this is needed
REG_ROMCTRL = 0xa7586000

To write more than 0x200 bytes, simply loop the above.

Clone this wiki locally