summaryrefslogtreecommitdiff
path: root/Source/Core
diff options
context:
space:
mode:
authorPokechu22 <Pokechu022@gmail.com>2021-06-27 13:34:44 -0700
committerPokechu22 <Pokechu022@gmail.com>2021-07-06 11:05:08 -0700
commitf664cf1903ff8403fd66fcbb8cf8b4092048c1ce (patch)
tree8361afc56d30dd71fdb2acea8565182ea99e267a /Source/Core
parent954f27c5d7289b2b806d0fb94dd105edaa1bb4be (diff)
Implement resetting DI via HW_RESETS
Diffstat (limited to 'Source/Core')
-rw-r--r--Source/Core/Core/HW/WII_IPC.cpp76
-rw-r--r--Source/Core/Core/IOS/DI/DI.cpp30
2 files changed, 100 insertions, 6 deletions
diff --git a/Source/Core/Core/HW/WII_IPC.cpp b/Source/Core/Core/HW/WII_IPC.cpp
index 5fd98e91ba..e388de87fe 100644
--- a/Source/Core/Core/HW/WII_IPC.cpp
+++ b/Source/Core/Core/HW/WII_IPC.cpp
@@ -43,6 +43,12 @@ enum
GPIOB_DIR = 0xc4,
GPIOB_IN = 0xc8,
+ GPIO_OUT = 0xe0,
+ GPIO_DIR = 0xe4,
+ GPIO_IN = 0xe8,
+
+ HW_RESETS = 0x194,
+
UNK_180 = 0x180,
UNK_1CC = 0x1cc,
UNK_1D0 = 0x1d0,
@@ -103,6 +109,8 @@ static constexpr Common::Flags<GPIO> gpio_owner = {GPIO::SLOT_LED, GPIO::SLOT_IN
static Common::Flags<GPIO> gpio_dir;
Common::Flags<GPIO> g_gpio_out;
+static u32 resets;
+
static CoreTiming::EventType* updateInterrupts;
static void UpdateInterrupts(u64 = 0, s64 cyclesLate = 0);
@@ -129,10 +137,20 @@ static void InitState()
arm_irq_flags = 0;
arm_irq_masks = 0;
- // The only input broadway has is SLOT_IN; all the others it has access to are outputs
- gpio_dir = {GPIO::SLOT_LED, GPIO::SENSOR_BAR, GPIO::DO_EJECT, GPIO::AVE_SCL, GPIO::AVE_SDA};
+ // The only inputs are POWER, EJECT_BTN, SLOT_IN, and EEP_MISO; Broadway only has access to
+ // SLOT_IN
+ gpio_dir = {
+ GPIO::POWER, GPIO::SHUTDOWN, GPIO::FAN, GPIO::DC_DC, GPIO::DI_SPIN, GPIO::SLOT_LED,
+ GPIO::SENSOR_BAR, GPIO::DO_EJECT, GPIO::EEP_CS, GPIO::EEP_CLK, GPIO::EEP_MOSI, GPIO::AVE_SCL,
+ GPIO::AVE_SDA, GPIO::DEBUG0, GPIO::DEBUG1, GPIO::DEBUG2, GPIO::DEBUG3, GPIO::DEBUG4,
+ GPIO::DEBUG5, GPIO::DEBUG6, GPIO::DEBUG7,
+ };
g_gpio_out = {};
+ // A cleared bit indicates the device is reset/off, so set everything to 1 (this may not exactly
+ // match hardware)
+ resets = 0xffffffff;
+
ppc_irq_masks |= INT_CAUSE_IPC_BROADWAY;
}
@@ -189,7 +207,8 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
mmio->Register(base | GPIOB_OUT, MMIO::DirectRead<u32>(&g_gpio_out.m_hex),
MMIO::ComplexWrite<u32>([](u32, u32 val) {
- g_gpio_out.m_hex = val & gpio_owner.m_hex;
+ g_gpio_out.m_hex =
+ (val & gpio_owner.m_hex) | (g_gpio_out.m_hex & ~gpio_owner.m_hex);
if (g_gpio_out[GPIO::DO_EJECT])
{
INFO_LOG_FMT(WII_IPC, "Ejecting disc due to GPIO write");
@@ -199,13 +218,62 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
// TODO: AVE, SLOT_LED
}));
mmio->Register(base | GPIOB_DIR, MMIO::DirectRead<u32>(&gpio_dir.m_hex),
- MMIO::DirectWrite<u32>(&gpio_dir.m_hex));
+ MMIO::ComplexWrite<u32>([](u32, u32 val) {
+ gpio_dir.m_hex = (val & gpio_owner.m_hex) | (gpio_dir.m_hex & ~gpio_owner.m_hex);
+ }));
mmio->Register(base | GPIOB_IN, MMIO::ComplexRead<u32>([](u32) {
Common::Flags<GPIO> gpio_in;
gpio_in[GPIO::SLOT_IN] = DVDInterface::IsDiscInside();
return gpio_in.m_hex;
}),
MMIO::Nop<u32>());
+ // Starlet GPIO registers, not normally accessible by PPC (but they can be depending on how
+ // AHBPROT is set up). We just always allow access, since some homebrew uses them.
+
+ // Note from WiiBrew: When switching owners, copying of the data is not necessary. For example, if
+ // pin 0 has certain configuration in the HW_GPIO registers, and that bit is then set in the
+ // HW_GPIO_OWNER register, those settings will immediately be visible in the HW_GPIOB registers.
+ // There is only one set of data registers, and the HW_GPIO_OWNER register just controls the
+ // access that the HW_GPIOB registers have to that data.
+ // Also: The HW_GPIO registers always have read access to all pins, but any writes (changes) must
+ // go through the HW_GPIOB registers if the corresponding bit is set in the HW_GPIO_OWNER
+ // register.
+ mmio->Register(base | GPIO_OUT, MMIO::DirectRead<u32>(&g_gpio_out.m_hex),
+ MMIO::ComplexWrite<u32>([](u32, u32 val) {
+ g_gpio_out.m_hex =
+ (g_gpio_out.m_hex & gpio_owner.m_hex) | (val & ~gpio_owner.m_hex);
+ if (g_gpio_out[GPIO::DO_EJECT])
+ {
+ INFO_LOG_FMT(WII_IPC, "Ejecting disc due to GPIO write");
+ DVDInterface::EjectDisc(DVDInterface::EjectCause::Software);
+ }
+ // SENSOR_BAR is checked by WiimoteEmu::CameraLogic
+ // TODO: AVE, SLOT_LED
+ }));
+ mmio->Register(base | GPIO_DIR, MMIO::DirectRead<u32>(&gpio_dir.m_hex),
+ MMIO::ComplexWrite<u32>([](u32, u32 val) {
+ gpio_dir.m_hex = (gpio_dir.m_hex & gpio_owner.m_hex) | (val & ~gpio_owner.m_hex);
+ }));
+ mmio->Register(base | GPIO_IN, MMIO::ComplexRead<u32>([](u32) {
+ Common::Flags<GPIO> gpio_in;
+ gpio_in[GPIO::SLOT_IN] = DVDInterface::IsDiscInside();
+ return gpio_in.m_hex;
+ }),
+ MMIO::Nop<u32>());
+
+ mmio->Register(base | HW_RESETS, MMIO::DirectRead<u32>(&resets),
+ MMIO::ComplexWrite<u32>([](u32, u32 val) {
+ // A reset occurs when the corresponding bit is cleared
+ const bool di_reset_triggered = (resets & 0x400) && !(val & 0x400);
+ resets = val;
+ if (di_reset_triggered)
+ {
+ // The GPIO *disables* spinning up the drive
+ const bool spinup = !g_gpio_out[GPIO::DI_SPIN];
+ INFO_LOG_FMT(WII_IPC, "Resetting DI {} spinup", spinup ? "with" : "without");
+ DVDInterface::ResetDrive(spinup);
+ }
+ }));
// Register some stubbed/unknown MMIOs required to make Wii games work.
mmio->Register(base | PPCSPEED, MMIO::InvalidRead<u32>(), MMIO::Nop<u32>());
diff --git a/Source/Core/Core/IOS/DI/DI.cpp b/Source/Core/Core/IOS/DI/DI.cpp
index 7ca6a6fdc2..567e1ecc3a 100644
--- a/Source/Core/Core/IOS/DI/DI.cpp
+++ b/Source/Core/Core/IOS/DI/DI.cpp
@@ -17,6 +17,7 @@
#include "Core/HW/DVD/DVDThread.h"
#include "Core/HW/MMIO.h"
#include "Core/HW/Memmap.h"
+#include "Core/HW/WII_IPC.h"
#include "Core/IOS/ES/ES.h"
#include "Core/IOS/ES/Formats.h"
#include "DiscIO/Volume.h"
@@ -38,6 +39,9 @@ static RegisterWrapper<0x0D806018> DILENGTH;
static RegisterWrapper<0x0D80601C> DICR;
static RegisterWrapper<0x0D806020> DIIMMBUF;
+static RegisterWrapper<0x0D8000E0> HW_GPIO_OUT;
+static RegisterWrapper<0x0D800194> HW_RESETS;
+
namespace IOS::HLE
{
CoreTiming::EventType* DIDevice::s_finish_executing_di_command;
@@ -266,8 +270,30 @@ std::optional<DIDevice::DIResult> DIDevice::StartIOCtl(const IOCtlRequest& reque
case DIIoctl::DVDLowReset:
{
const bool spinup = Memory::Read_U32(request.buffer_in + 4);
- INFO_LOG_FMT(IOS_DI, "DVDLowReset {} spinup", spinup ? "with" : "without");
- DVDInterface::ResetDrive(spinup);
+
+ // The GPIO *disables* spinning up the drive. Normally handled via syscall 0x4e.
+ const u32 old_gpio = HW_GPIO_OUT;
+ if (spinup)
+ HW_GPIO_OUT = old_gpio & ~static_cast<u32>(GPIO::DI_SPIN);
+ else
+ HW_GPIO_OUT = old_gpio | static_cast<u32>(GPIO::DI_SPIN);
+
+ // Syscall 0x46 check_di_reset
+ const bool was_resetting = (HW_RESETS & (1 << 10)) == 0;
+ if (was_resetting)
+ {
+ // This route will not generally be taken in Dolphin but is included for completeness
+ // Syscall 0x45 deassert_di_reset
+ HW_RESETS = HW_RESETS | (1 << 10);
+ }
+ else
+ {
+ // Syscall 0x44 assert_di_reset
+ HW_RESETS = HW_RESETS & ~(1 << 10);
+ // Normally IOS sleeps for 12 microseconds here, but we can't easily emulate that
+ // Syscall 0x45 deassert_di_reset
+ HW_RESETS = HW_RESETS | (1 << 10);
+ }
ResetDIRegisters();
return DIResult::Success;
}