HW: stabilize original BootMii interaction

This commit is contained in:
2026-08-25 23:26:29 +02:00
parent 5387658e0c
commit b85667435c
5 changed files with 57 additions and 13 deletions
+5 -1
View File
@@ -77,7 +77,11 @@ void ProcessorInterfaceManager::RegisterMMIO(MMIO::Mapping* mmio, u32 base)
mmio->Register(base | PI_INTERRUPT_CAUSE, MMIO::DirectRead<u32>(&m_interrupt_cause), mmio->Register(base | PI_INTERRUPT_CAUSE, MMIO::DirectRead<u32>(&m_interrupt_cause),
MMIO::ComplexWrite<u32>([](Core::System& system, u32, u32 val) { MMIO::ComplexWrite<u32>([](Core::System& system, u32, u32 val) {
auto& processor_interface = system.GetProcessorInterface(); auto& processor_interface = system.GetProcessorInterface();
processor_interface.m_interrupt_cause &= ~val; // The reset-button bit is a live, active-low input state rather than an
// interrupt latch. Software can acknowledge the other causes, but must not be
// able to turn an unpressed reset button into a permanently pressed one.
processor_interface.m_interrupt_cause =
AcknowledgeInterruptCauses(processor_interface.m_interrupt_cause, val);
processor_interface.UpdateException(); processor_interface.UpdateException();
})); }));
+5
View File
@@ -78,6 +78,11 @@ enum ErrorCause : u32
Reserved = 7, Reserved = 7,
}; };
constexpr u32 AcknowledgeInterruptCauses(u32 current, u32 acknowledged)
{
return current & ~(acknowledged & ~static_cast<u32>(INT_CAUSE_RST_BUTTON));
}
class ProcessorInterfaceManager class ProcessorInterfaceManager
{ {
public: public:
+23 -9
View File
@@ -320,9 +320,12 @@ void VideoInterfaceManager::RegisterMMIO(MMIO::Mapping* mmio, u32 base)
return 1 + (vi.m_half_line_count) / 2; return 1 + (vi.m_half_line_count) / 2;
}), }),
MMIO::ComplexWrite<u16>([](Core::System& system, u32, u16 val) { MMIO::ComplexWrite<u16>([](Core::System& system, u32, u16 val) {
WARN_LOG_FMT( auto& vi = system.GetVideoInterface();
VIDEOINTERFACE, const u32 total_half_lines = vi.GetHalfLinesPerEvenField() + vi.GetHalfLinesPerOddField();
"Changing vertical beam position to {:#06x} - not documented or implemented yet", val); const u32 line = val == 0 ? 0 : static_cast<u32>(val - 1);
const u32 target_half_line = line * 2 + (vi.m_half_line_count & 1);
vi.m_half_line_count =
total_half_lines == 0 ? target_half_line : target_half_line % total_half_lines;
})); }));
mmio->Register( mmio->Register(
base | VI_HORIZONTAL_BEAM_POSITION, MMIO::ComplexRead<u16>([](Core::System& system, u32) { base | VI_HORIZONTAL_BEAM_POSITION, MMIO::ComplexRead<u16>([](Core::System& system, u32) {
@@ -334,10 +337,20 @@ void VideoInterfaceManager::RegisterMMIO(MMIO::Mapping* mmio, u32 base)
return std::clamp<u16>(value, 1, vi.m_h_timing_0.HLW * 2); return std::clamp<u16>(value, 1, vi.m_h_timing_0.HLW * 2);
}), }),
MMIO::ComplexWrite<u16>([](Core::System& system, u32, u16 val) { MMIO::ComplexWrite<u16>([](Core::System& system, u32, u16 val) {
WARN_LOG_FMT( auto& vi = system.GetVideoInterface();
VIDEOINTERFACE, const u32 half_line_width = vi.m_h_timing_0.HLW;
"Changing horizontal beam position to {:#06x} - not documented or implemented yet", if (half_line_width == 0)
val); return;
const u32 horizontal_position =
std::clamp<u32>(val, 1, static_cast<u32>(half_line_width) * 2);
const u32 target_half_line = horizontal_position > half_line_width ? 1 : 0;
vi.m_half_line_count = (vi.m_half_line_count & ~1u) | target_half_line;
const u64 ticks_into_line =
static_cast<u64>(horizontal_position - 1) * vi.GetTicksPerHalfLine() / half_line_width;
const u64 ticks = system.GetCoreTiming().GetTicks();
vi.m_ticks_last_line_start = ticks >= ticks_into_line ? ticks - ticks_into_line : 0;
})); }));
// The following MMIOs are interrupts related and update interrupt status // The following MMIOs are interrupts related and update interrupt status
@@ -972,9 +985,10 @@ void VideoInterfaceManager::Update(u64 ticks)
// the beginning of a new full-line, update the timer // the beginning of a new full-line, update the timer
++m_half_line_count; ++m_half_line_count;
if (m_half_line_count == GetHalfLinesPerEvenField() + GetHalfLinesPerOddField()) const u32 total_half_lines = GetHalfLinesPerEvenField() + GetHalfLinesPerOddField();
if (total_half_lines != 0 && m_half_line_count >= total_half_lines)
{ {
m_half_line_count = 0; m_half_line_count %= total_half_lines;
} }
if (!(m_half_line_count & 1)) if (!(m_half_line_count & 1))
+9
View File
@@ -9,6 +9,7 @@
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Core/HW/GPFifo.h" #include "Core/HW/GPFifo.h"
#include "Core/HW/MMIO.h" #include "Core/HW/MMIO.h"
#include "Core/HW/ProcessorInterface.h"
#include "Core/System.h" #include "Core/System.h"
// Tests that the UniqueID function returns a "unique enough" identifier // Tests that the UniqueID function returns a "unique enough" identifier
@@ -53,6 +54,14 @@ TEST(IsMMIOAddress, SpecialAddresses)
EXPECT_TRUE(MMIO::IsMMIOAddress(0x0D800F10, is_wii)); // Mirror of Wii MMIOs EXPECT_TRUE(MMIO::IsMMIOAddress(0x0D800F10, is_wii)); // Mirror of Wii MMIOs
} }
TEST(ProcessorInterface, ResetButtonStateCannotBeAcknowledged)
{
constexpr u32 reset = ProcessorInterface::INT_CAUSE_RST_BUTTON;
EXPECT_EQ(reset, ProcessorInterface::AcknowledgeInterruptCauses(
reset | ProcessorInterface::INT_CAUSE_VI, 0xffffffffu));
EXPECT_EQ(0u, ProcessorInterface::AcknowledgeInterruptCauses(0, 0xffffffffu));
}
class MappingTest : public testing::Test class MappingTest : public testing::Test
{ {
protected: protected:
+15 -3
View File
@@ -295,10 +295,21 @@ An isolated boot probe using the local, mutually matching dumps has executed thi
EXI paths, published its EXI Broadway boot vector, released Broadway, and ran the original EXI paths, published its EXI Broadway boot vector, released Broadway, and ran the original
`ppcboot.elf`. The interactive four-icon BootMii menu rendered without a host-side firmware `ppcboot.elf`. The interactive four-icon BootMii menu rendered without a host-side firmware
jump, a patched BootMii binary, or a synthesized SI reply. jump, a patched BootMii binary, or a synthesized SI reply.
21. BootMii exposed two remaining Broadway hardware differences. Its VI setup writes the vertical
and horizontal beam-position registers; ignoring those writes let Dolphin's half-line counter
escape the active field and prevented the next VI interrupt. The beam writes now reposition the
emulated raster and field wrapping tolerates an out-of-range position. BootMii also clears the
PI interrupt-cause register before polling the front-panel controls. The reset-button bit is a
live active-low input, not an acknowledgeable interrupt latch, so PI acknowledgements now
preserve it. This removed the false held-RESET state that selected the Wii icon and issued
`IPC_BOOT2_RUN(1, 2)` immediately. Cold-boot BootMii and HBC -> IOS254 -> MINI -> BootMii both
remain in the original interactive menu, and keyboard-backed GameCube navigation was validated
without modifying `armboot.bin`, `ppcboot.elf`, or `bootmii.ini`.
The probe never prints ROM, NAND, key, or firmware instruction bytes. The committed unit suite The probe never prints ROM, NAND, key, or firmware instruction bytes. The committed unit suite
covers ARM-to-Thumb loads into PC, high Starlet exception vectors, privileged `LDM ... ^` user-bank covers ARM-to-Thumb loads into PC, high Starlet exception vectors, privileged `LDM ... ^` user-bank
transfers, latched bidirectional IPC control bits, empty-slot SDHCI reset/clock/status behavior, transfers, latched bidirectional IPC control bits, the PI reset-button state surviving interrupt
acknowledgements, empty-slot SDHCI reset/clock/status behavior,
timer equal/future comparator matches with independent IRQ write-one-to-clear acknowledgement, timer equal/future comparator matches with independent IRQ write-one-to-clear acknowledgement,
OHCI power-good/root-hub state, and a complete three-TD USB device-descriptor transaction. Targeted OHCI power-good/root-hub state, and a complete three-TD USB device-descriptor transaction. Targeted
development probes additionally exercised NAND read/program/erase and program-time ECC, SEEPROM development probes additionally exercised NAND read/program/erase and program-time ECC, SEEPROM
@@ -317,8 +328,8 @@ the three pre-existing bad-ECC pages in the source dump, a valid EXI reset vecto
The current end-to-end boundary is a rendered, controller-connected and post-health-screen System The current end-to-end boundary is a rendered, controller-connected and post-health-screen System
Menu with its populated channel grid, followed by a successful original IOS80-to-IOS58 reload, a Menu with its populated channel grid, followed by a successful original IOS80-to-IOS58 reload, a
rendered Homebrew Channel, LetterBomb reaching the interactive HackMii Installer menu, and the rendered Homebrew Channel, LetterBomb reaching the interactive HackMii Installer menu, and both
dump's BootMii-as-boot2 installation reaching its original interactive UI through MINI and cold-boot and IOS254 launches reaching a stable, navigable original BootMii UI through MINI and
`ppcboot.elf`. The path sustains PPC-to-original-IOS filesystem, DI, Bluetooth HID, SDIO/Wi-Fi and `ppcboot.elf`. The path sustains PPC-to-original-IOS filesystem, DI, Bluetooth HID, SDIO/Wi-Fi and
network-service traffic. The stabilized Menu has been measured at 59.91 FPS and HBC at 59.94 FPS. network-service traffic. The stabilized Menu has been measured at 59.91 FPS and HBC at 59.94 FPS.
It proves the emulated first Wii Remote's pairing, L2CAP setup, command exchange and input-report It proves the emulated first Wii Remote's pairing, L2CAP setup, command exchange and input-report
@@ -387,6 +398,7 @@ than a drop-in replacement for Dolphin's mature IOS HLE mode.
| Address space and devices | `Core/IOS/Starlet/StarletMemory.{h,cpp}` | | Address space and devices | `Core/IOS/Starlet/StarletMemory.{h,cpp}` |
| Bluetooth pairing and Wii Remote HID | `Core/IOS/USB/Bluetooth/{BTBase,WiimoteDevice}.{h,cpp}` | | Bluetooth pairing and Wii Remote HID | `Core/IOS/USB/Bluetooth/{BTBase,WiimoteDevice}.{h,cpp}` |
| IPC and Broadway reset | `Core/HW/WII_IPC.{h,cpp}` | | IPC and Broadway reset | `Core/HW/WII_IPC.{h,cpp}` |
| Broadway VI and front-panel state | `Core/HW/VideoInterface.cpp`, `Core/HW/ProcessorInterface.{h,cpp}` |
| Mode selection/lifetime | `Core/HW/HW.cpp`, `Core/System.{h,cpp}` | | Mode selection/lifetime | `Core/HW/HW.cpp`, `Core/System.{h,cpp}` |
| Boot-path separation | `Core/Boot/Boot.cpp`, `Core/ConfigManager.cpp`, `Core/Core.cpp` | | Boot-path separation | `Core/Boot/Boot.cpp`, `Core/ConfigManager.cpp`, `Core/Core.cpp` |
| Configuration | `Core/Config/MainSettings.{h,cpp}` | | Configuration | `Core/Config/MainSettings.{h,cpp}` |