====== R4 (NDS) programming ====== Original research provided mostly [[https://github.com/DS-Homebrew/DLDI/wiki/Original-R4-and-M3-Simply|by lifehackerhansol]]. ===== Control flags ===== * KEY1 gap1 length: 0 * KEY1 gap2 length: 24 * KEY2 encryption: Enabled for data and commands * Transfer clock rate: 6.7 MHz * Data block size: 4 bytes (512 bytes for SD commands) ===== Commands ===== ==== Get card status (B0 00 00 00 00 00 00 00) ==== 31 bit 0 ???? ???? ???? ???? ???? ???L LLml lsss | |||| |||| | |||| |+++- Initialization status: | |||| | 0 - (in progress) | |||| | 1 - SD/TF card not found | |||| | 2 - SD/TF card read error | |||| | 3 - could not find _DS_MENU.DAT | |||| | 4 - success | |||+-+---- Language: | ||| 1 - Japanese | ||| 2 - English | ||| 3 - Chinese | ||+------- Model: | || 0 - M3DS Simply | || 1 - R4 Revolution for DS +-++-------- Sub-language: 2:2 - French 2:4 - Korean 3:0? - Simplified Chinese 3:1? - Traditional Chinese ==== Save read request (B2 sd sc sb sa 00 00 00) ==== SD read request, but using the "save" FAT entry table. ==== Save read data (B3 00 00 00 00 00 00 00) ==== SD read data, but using the "save" FAT entry table. ==== Set FAT table entry (B4 sd sc sb sa 00 00 00) ==== * (MSB) sd sc sb sa (LSB) - as follows: 31 bit 0 ssss ssss ssss ssss ssss ssss sss? ???t |||| |||| |||| |||| |||| |||| ||| | |||| |||| |||| |||| |||| |||| ||| +- Type: |||| |||| |||| |||| |||| |||| ||| 0 - ROM |||| |||| |||| |||| |||| |||| ||| 1 - Save ++++-++++-++++-++++-++++-++++-+++------- Directory table address (aligned to 0x20 per FAT spec) ==== ROM read request (B6 sd sc sb sa 00 00 00) ==== SD read request, but using the "ROM" FAT entry table. Points to _DS_MENU.DAT on initialization. ==== ROM read data (B7 00 00 00 00 00 00 00) ==== SD read data, but using the "ROM" FAT entry table. ==== Get card ID (B8 00 00 00 00 00 00 00) ==== [[https://github.com/DS-Homebrew/DLDI/wiki/Original-R4-and-M3-Simply#original-r4--m3-ds-simply|Quoting lifehackerhansol]]: > Card ID commands are not patched and use the timings from the retail game ROM header, but without L1 latency (that's what the SDK does). Those can be as fast as a single cycle of L2 latency, depending on the game. ==== SD read request (B9 sd sc sb sa 00 00 00) ==== * (MSB) sd sc sb sa (LSB) - sector address, in bytes Returns ''0x00000000'' when data is ready to access, ''0x00000001'' if ongoing. ==== SD read data (BA 00 00 00 00 00 00 00) ==== Returns the 512 bytes read via the "SD read start" command. ==== SD write start (BB sd sc sb sa 00 00 00) ==== * (MSB) sd sc sb sa (LSB) - sector address, in bytes Followed by 512 bytes of SD card data. ==== SD write status (BC sd sc sb sa 00 00 00) ==== * (MSB) sd sc sb sa (LSB) - sector address, in bytes Returns ''0x00000000'' when data is ready to access, ''0x00000001'' if ongoing. ==== Save write start (BD sd sc sb sa 00 00 00) ==== SD write start, but using the "save" FAT entry table. ==== Save write status (BE sd sc sb sa 00 00 00) ==== SD write status, but using the "save" FAT entry table. ==== ROM read decrypted data (BF 00 00 00 00 00 00 00) ==== Same as SD read data, but applying a XOR-based obfuscation algorithm to the buffer's contents. This is used by the stage 1 loader to decrypt _DS_MENU.DAT. The algorithm was initially discovered by Yasu in 2007, and example source code is provided as follows, where ''key'' is a 16-bit constant: uint8_t[512] in; uint8_t[512] out; uint16_t key1 = key ^ currentSector; for (int i = 0; i < 512; i++) { // Derive key2 from key1. uint8_t key2 = ((key1 >> 7) & 0x80) | ((key1 >> 6) & 0x60) | ((key1 >> 5) & 0x10) | ((key1 >> 4) & 0x0C) | (key1 & 0x03); // Decrypt byte. dest[i] = src[i] ^ key2; // Derive next key1 from key2. uint16_t tmp = ((src[i] << 8) ^ key1); uint16_t tmpXor = 0; for (int ii = 0; ii < 16; ii++) tmpXor ^= (tmp >> ii); uint16_t newKey1 = 0; newKey1 |= ((tmpXor & 0x80) | (tmp & 0x7C)) << 8; newKey1 |= ((tmp ^ (tmpXor >> 14)) << 8) & 0x0300; newKey1 |= (((tmp >> 1) ^ tmp) >> 6) & 0xFC; newKey1 |= ((tmp ^ (tmpXor >> 1)) >> 8) & 0x03; key1 = newKey1; }