Implement resetting DI via HW_RESETS
This commit is contained in:
parent
954f27c5d7
commit
f664cf1903
|
@ -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>());
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue