Preserve formatted NAND boot and Wii Remote pairing, add DMA/SRAM conformance tests and a Wii comparison probe. Keep P1 profiling tools and native update A/B evidence; reject the unhelpful targeted SRAM shortcut.
129 lines
4.3 KiB
C++
129 lines
4.3 KiB
C++
// Copyright 2026 Dolphin Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <iomanip>
|
|
#include <memory>
|
|
#include <optional>
|
|
|
|
#include <gtest/gtest.h>
|
|
|
|
#include "Core/Core.h"
|
|
#include "Core/HW/WII_IPC.h"
|
|
#include "Core/IOS/Starlet/StarletMemory.h"
|
|
#include "Core/System.h"
|
|
|
|
namespace IOS::LLE
|
|
{
|
|
namespace
|
|
{
|
|
// Deliberately branch-based reference, independent of the optimized aperture-bit swap.
|
|
std::optional<u32> MapSRAMByte(u32 address, bool split, bool boot0)
|
|
{
|
|
if (!(address >= 0x0d400000 && address < 0x0d420000) && address < 0xfff00000)
|
|
return std::nullopt;
|
|
const u32 window = address & 0x1ffff;
|
|
if (boot0 && address >= 0xfffe0000 && (split ? window < 0x10000 : window >= 0x10000))
|
|
return std::nullopt;
|
|
if (!split)
|
|
return window < 0x18000 ? std::optional(window) : std::nullopt;
|
|
if (window < 0x8000)
|
|
return 0x10000 + window;
|
|
if (window >= 0x10000)
|
|
return window - 0x10000;
|
|
return std::nullopt;
|
|
}
|
|
|
|
class StarletSRAMWrite : public testing::Test
|
|
{
|
|
protected:
|
|
void SetUp() override
|
|
{
|
|
Core::DeclareAsCPUThread();
|
|
Core::System::GetInstance().GetWiiIPC().Reset();
|
|
}
|
|
static void SetMapping(StarletMemory& memory, bool split, bool boot0)
|
|
{
|
|
memory.Write32(0x0d800060, split ? 0x20 : 0);
|
|
memory.Write32(0x0d80018c, boot0 ? 0 : 0x1000);
|
|
}
|
|
};
|
|
|
|
TEST_F(StarletSRAMWrite, EveryApertureByteMatchesIndependentMapping)
|
|
{
|
|
auto memory = std::make_unique<StarletMemory>(Core::System::GetInstance());
|
|
memory->Reset();
|
|
u8* const sram = memory->GetFastmemSRAMBase();
|
|
for (u32 i = 0; i < StarletMemory::SRAM_SIZE; ++i)
|
|
sram[i] = static_cast<u8>((i * 13) ^ (i >> 8) ^ 0xa5);
|
|
for (const bool split : {false, true})
|
|
{
|
|
for (const bool boot0 : {false, true})
|
|
{
|
|
SetMapping(*memory, split, boot0);
|
|
for (const u32 base : {0x0d400000u, 0xfff00000u, 0xfffe0000u})
|
|
{
|
|
for (u32 i = 0; i < 0x20000; ++i)
|
|
{
|
|
const u32 address = base + i;
|
|
const auto offset = MapSRAMByte(address, split, boot0);
|
|
// Reset leaves the synthetic boot ROM zero-filled. Holes also read zero.
|
|
ASSERT_EQ(memory->Read8(address), offset ? sram[*offset] : 0)
|
|
<< "address=" << std::hex << address << " split=" << split << " boot0=" << boot0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_F(StarletSRAMWrite, WideStoresMatchByteOracleAcrossLiveMappingsAndAllMirrors)
|
|
{
|
|
auto memory = std::make_unique<StarletMemory>(Core::System::GetInstance());
|
|
memory->Reset();
|
|
std::array<u8, StarletMemory::SRAM_SIZE> expected;
|
|
for (u32 i = 0; i < expected.size(); ++i)
|
|
expected[i] = static_cast<u8>((i * 13) ^ (i >> 8) ^ 0xa5);
|
|
std::copy(expected.begin(), expected.end(), memory->GetFastmemSRAMBase());
|
|
// Toggle back, too: no cached host pointer may survive changes to SM/boot0.
|
|
for (const bool split : {false, true, false})
|
|
{
|
|
for (const bool boot0 : {false, true, false})
|
|
{
|
|
SetMapping(*memory, split, boot0);
|
|
for (const u32 base : {0x0d400000u, 0xfff00000u, 0xfff20000u, 0xfff40000u, 0xfff60000u,
|
|
0xfff80000u, 0xfffa0000u, 0xfffc0000u, 0xfffe0000u})
|
|
{
|
|
for (u32 page = 0; page < 32; ++page)
|
|
{
|
|
for (const u32 in_page : {0u, 1u, 2u, 3u, 0xffcu, 0xffdu, 0xffeu, 0xfffu})
|
|
{
|
|
const u32 address = base + page * 0x1000 + in_page;
|
|
for (const u32 width : {2u, 4u})
|
|
{
|
|
if (address > 0xffffffffu - (width - 1))
|
|
continue; // Do not wrap into uninitialized MEM1.
|
|
const u32 value = (address * 0x12345u) ^ (width == 2 ? 0x89abcdefu : 0x76543210u);
|
|
for (u32 byte = 0; byte < width; ++byte)
|
|
{
|
|
if (const auto offset = MapSRAMByte(address + byte, split, boot0))
|
|
expected[*offset] = static_cast<u8>(value >> ((width - 1 - byte) * 8));
|
|
}
|
|
if (width == 2)
|
|
memory->Write16(address, static_cast<u16>(value));
|
|
else
|
|
memory->Write32(address, value);
|
|
ASSERT_TRUE(
|
|
std::equal(expected.begin(), expected.end(), memory->GetFastmemSRAMBase()))
|
|
<< "address=" << std::hex << address << " width=" << width << " split=" << split
|
|
<< " boot0=" << boot0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} // namespace
|
|
} // namespace IOS::LLE
|