From a8dbbab62de0ec429d9d887c39b9669c75f51c21 Mon Sep 17 00:00:00 2001 From: Yaya48 Date: Wed, 23 Sep 2026 14:31:21 +0200 Subject: [PATCH] IOS LLE: emulate SD CMD6 and document Wii conformance run --- .../Core/Core/IOS/Starlet/StarletMemory.cpp | 45 +++++++++++++++++++ Source/Core/Core/IOS/Starlet/StarletMemory.h | 1 + Tools/WiiConformanceProbe/README.md | 23 +++++++++- 3 files changed, 68 insertions(+), 1 deletion(-) diff --git a/Source/Core/Core/IOS/Starlet/StarletMemory.cpp b/Source/Core/Core/IOS/Starlet/StarletMemory.cpp index 2b5ed865a3..9a36d9dc4a 100644 --- a/Source/Core/Core/IOS/Starlet/StarletMemory.cpp +++ b/Source/Core/Core/IOS/Starlet/StarletMemory.cpp @@ -274,6 +274,7 @@ constexpr u16 SDHC_ERROR_DATA_TIMEOUT = 1U << 4; constexpr u32 SD_CARD_GO_IDLE = 0; constexpr u32 SD_CARD_ALL_SEND_CID = 2; constexpr u32 SD_CARD_SEND_RELATIVE_ADDRESS = 3; +constexpr u32 SD_CARD_SWITCH_FUNCTION = 6; constexpr u32 SD_CARD_SELECT = 7; constexpr u32 SD_CARD_SEND_IF_CONDITION = 8; constexpr u32 SD_CARD_SEND_CSD = 9; @@ -608,6 +609,7 @@ void StarletMemory::InitSDCard() m_sd_card.Close(); m_sd_card_inserted = false; m_sd_card_high_capacity = false; + m_sd_high_speed = false; m_sd_block_length = 512; if (!Config::Get(Config::MAIN_WII_SD_CARD)) return; @@ -673,6 +675,7 @@ void StarletMemory::Reset() m_seeprom_write_all = false; m_sd_app_command = false; m_sd_card_selected = false; + m_sd_high_speed = false; m_sd_block_length = 512; m_sdhc_status_before_write = 0; m_wifi_sdio_status_before_write = 0; @@ -748,6 +751,7 @@ void StarletMemory::DoState(PointerWrap& p) p.Do(m_sd_card_high_capacity); p.Do(m_sd_app_command); p.Do(m_sd_card_selected); + p.Do(m_sd_high_speed); p.Do(m_sd_block_length); p.Do(m_sdhc_status_before_write); p.Do(m_wifi_sdio_status_before_write); @@ -3204,8 +3208,49 @@ void StarletMemory::ExecuteSDHCCommand(u32 command_and_mode) { case SD_CARD_GO_IDLE: m_sd_card_selected = false; + m_sd_high_speed = false; m_sd_block_length = 512; break; + case SD_CARD_SWITCH_FUNCTION: + { + // CMD6 returns a 512-bit status block, distinct from ACMD6 (bus width). + // Offer default and high-speed access modes in function group 1. The + // other groups have only their mandatory default and no-change functions. + if (!data_command || (transfer_mode & SDHC_TRANSFER_DMA_ENABLE) == 0 || block_size < 64) + { + succeeded = false; + break; + } + + std::array status{}; + status[1] = 64; // Maximum current consumption: 64 mA. + bool function_error = false; + for (u32 group = 0; group < 6; ++group) + { + const u16 supported = group == 0 ? 0x8003 : 0x8001; + status[2 + (5 - group) * 2] = static_cast(supported >> 8); + status[3 + (5 - group) * 2] = static_cast(supported); + + const u32 requested = (argument >> (group * 4)) & 0xf; + u8 selected = group == 0 && m_sd_high_speed ? 1 : 0; + if (requested != 0xf) + selected = requested == 0 || (group == 0 && requested == 1) ? + static_cast(requested) : + 0xf; + function_error |= selected == 0xf; + const u32 status_byte = 16 - group / 2; + status[status_byte] |= static_cast(selected << ((group & 1) * 4)); + } + status[17] = 1; // Busy-status fields are defined and all functions are ready. + for (u32 i = 0; i < status.size(); ++i) + WriteDMA8(dma_address + i, status[i]); + WriteRegister(SDHC_DMA_ADDRESS, dma_address + static_cast(status.size())); + WriteRegister(SDHC_RESPONSE, 0x900); + transfer_complete = true; + if ((argument & 0x80000000) != 0 && !function_error && (argument & 0xf) <= 1) + m_sd_high_speed = (argument & 0xf) == 1; + break; + } case SD_CARD_ALL_SEND_CID: case SD_CARD_SEND_CID: { diff --git a/Source/Core/Core/IOS/Starlet/StarletMemory.h b/Source/Core/Core/IOS/Starlet/StarletMemory.h index eeda10fd5c..9e007a2e4c 100644 --- a/Source/Core/Core/IOS/Starlet/StarletMemory.h +++ b/Source/Core/Core/IOS/Starlet/StarletMemory.h @@ -250,6 +250,7 @@ private: bool m_sd_card_high_capacity = false; bool m_sd_app_command = false; bool m_sd_card_selected = false; + bool m_sd_high_speed = false; u32 m_sd_block_length = 512; u32 m_sdhc_status_before_write = 0; u32 m_wifi_sdio_status_before_write = 0; diff --git a/Tools/WiiConformanceProbe/README.md b/Tools/WiiConformanceProbe/README.md index 2995339990..5e8c0691ef 100644 --- a/Tools/WiiConformanceProbe/README.md +++ b/Tools/WiiConformanceProbe/README.md @@ -21,7 +21,7 @@ successfully from this directory in PowerShell: ``` The ready-to-copy Homebrew Channel folder is `apps/wii-conformance/`, containing -`boot.dol` and `meta.xml`. This build has not yet been run on a physical Wii. +`boot.dol` and `meta.xml`. ## Capture @@ -57,3 +57,24 @@ shared-memory cache visibility, DMA, IRQ ordering, and individual devices. PPC homebrew cannot inspect every Starlet register while Nintendo IOS is running. A separate Starlet-side probe under MINI/BootMii can test raw hardware registers, but its results must be labeled as MINI-mode tests. + +## 2026-09-23 IOS58 comparison + +The same DOL (SHA-256 +`28EB2F51CB12B302D736518232CAB59715877A81CD0AABC1A938EF16CF0ADFC4`) +ran on a physical Wii and in this fork's Starlet LLE mode under IOS58 revision +6176. Both captures contain all 1,860 observations. The SD tests in LLE first +failed during card initialization because Starlet SDHC did not implement CMD6 +(Switch Function). After adding its 64-byte status response, all SD operations +completed and the CSV closed successfully. + +The comparison finds 64 differences, all repeats of `es_title_count`: 111 on +the Wii versus 101 in the Dolphin profile. A read-only walk of the Dolphin raw +NAND and its journal found exactly 101 installed title TMDs. The remaining +1,796 observations match. This result covers the tested IOS58 paths only; it +cannot establish an overall emulator accuracy percentage. + +Local captures are `captures/wii-ios58-r6176.csv` (SHA-256 +`C08E49B0ADCF1F0EF30FFE16775E3A354D269923028C9B775BBB189E7E2FF3E6`) +and `captures/dolphin-lle-ios58-r6176.csv` (SHA-256 +`64D2E00EAE67370578BE2A15FC0951E7051E3468252B0D2533F5B968A0E14AD8`).