Implement resetting DI via HW_RESETS
This commit is contained in:
parent
954f27c5d7
commit
f664cf1903
|
@ -43,6 +43,12 @@ enum
|
||||||
GPIOB_DIR = 0xc4,
|
GPIOB_DIR = 0xc4,
|
||||||
GPIOB_IN = 0xc8,
|
GPIOB_IN = 0xc8,
|
||||||
|
|
||||||
|
GPIO_OUT = 0xe0,
|
||||||
|
GPIO_DIR = 0xe4,
|
||||||
|
GPIO_IN = 0xe8,
|
||||||
|
|
||||||
|
HW_RESETS = 0x194,
|
||||||
|
|
||||||
UNK_180 = 0x180,
|
UNK_180 = 0x180,
|
||||||
UNK_1CC = 0x1cc,
|
UNK_1CC = 0x1cc,
|
||||||
UNK_1D0 = 0x1d0,
|
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;
|
static Common::Flags<GPIO> gpio_dir;
|
||||||
Common::Flags<GPIO> g_gpio_out;
|
Common::Flags<GPIO> g_gpio_out;
|
||||||
|
|
||||||
|
static u32 resets;
|
||||||
|
|
||||||
static CoreTiming::EventType* updateInterrupts;
|
static CoreTiming::EventType* updateInterrupts;
|
||||||
static void UpdateInterrupts(u64 = 0, s64 cyclesLate = 0);
|
static void UpdateInterrupts(u64 = 0, s64 cyclesLate = 0);
|
||||||
|
|
||||||
|
@ -129,10 +137,20 @@ static void InitState()
|
||||||
arm_irq_flags = 0;
|
arm_irq_flags = 0;
|
||||||
arm_irq_masks = 0;
|
arm_irq_masks = 0;
|
||||||
|
|
||||||
// The only input broadway has is SLOT_IN; all the others it has access to are outputs
|
// The only inputs are POWER, EJECT_BTN, SLOT_IN, and EEP_MISO; Broadway only has access to
|
||||||
gpio_dir = {GPIO::SLOT_LED, GPIO::SENSOR_BAR, GPIO::DO_EJECT, GPIO::AVE_SCL, GPIO::AVE_SDA};
|
// 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 = {};
|
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;
|
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->Register(base | GPIOB_OUT, MMIO::DirectRead<u32>(&g_gpio_out.m_hex),
|
||||||
MMIO::ComplexWrite<u32>([](u32, u32 val) {
|
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])
|
if (g_gpio_out[GPIO::DO_EJECT])
|
||||||
{
|
{
|
||||||
INFO_LOG_FMT(WII_IPC, "Ejecting disc due to GPIO write");
|
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
|
// TODO: AVE, SLOT_LED
|
||||||
}));
|
}));
|
||||||
mmio->Register(base | GPIOB_DIR, MMIO::DirectRead<u32>(&gpio_dir.m_hex),
|
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) {
|
mmio->Register(base | GPIOB_IN, MMIO::ComplexRead<u32>([](u32) {
|
||||||
Common::Flags<GPIO> gpio_in;
|
Common::Flags<GPIO> gpio_in;
|
||||||
gpio_in[GPIO::SLOT_IN] = DVDInterface::IsDiscInside();
|
gpio_in[GPIO::SLOT_IN] = DVDInterface::IsDiscInside();
|
||||||
return gpio_in.m_hex;
|
return gpio_in.m_hex;
|
||||||
}),
|
}),
|
||||||
MMIO::Nop<u32>());
|
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.
|
// Register some stubbed/unknown MMIOs required to make Wii games work.
|
||||||
mmio->Register(base | PPCSPEED, MMIO::InvalidRead<u32>(), MMIO::Nop<u32>());
|
mmio->Register(base | PPCSPEED, MMIO::InvalidRead<u32>(), MMIO::Nop<u32>());
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include "Core/HW/DVD/DVDThread.h"
|
#include "Core/HW/DVD/DVDThread.h"
|
||||||
#include "Core/HW/MMIO.h"
|
#include "Core/HW/MMIO.h"
|
||||||
#include "Core/HW/Memmap.h"
|
#include "Core/HW/Memmap.h"
|
||||||
|
#include "Core/HW/WII_IPC.h"
|
||||||
#include "Core/IOS/ES/ES.h"
|
#include "Core/IOS/ES/ES.h"
|
||||||
#include "Core/IOS/ES/Formats.h"
|
#include "Core/IOS/ES/Formats.h"
|
||||||
#include "DiscIO/Volume.h"
|
#include "DiscIO/Volume.h"
|
||||||
|
@ -38,6 +39,9 @@ static RegisterWrapper<0x0D806018> DILENGTH;
|
||||||
static RegisterWrapper<0x0D80601C> DICR;
|
static RegisterWrapper<0x0D80601C> DICR;
|
||||||
static RegisterWrapper<0x0D806020> DIIMMBUF;
|
static RegisterWrapper<0x0D806020> DIIMMBUF;
|
||||||
|
|
||||||
|
static RegisterWrapper<0x0D8000E0> HW_GPIO_OUT;
|
||||||
|
static RegisterWrapper<0x0D800194> HW_RESETS;
|
||||||
|
|
||||||
namespace IOS::HLE
|
namespace IOS::HLE
|
||||||
{
|
{
|
||||||
CoreTiming::EventType* DIDevice::s_finish_executing_di_command;
|
CoreTiming::EventType* DIDevice::s_finish_executing_di_command;
|
||||||
|
@ -266,8 +270,30 @@ std::optional<DIDevice::DIResult> DIDevice::StartIOCtl(const IOCtlRequest& reque
|
||||||
case DIIoctl::DVDLowReset:
|
case DIIoctl::DVDLowReset:
|
||||||
{
|
{
|
||||||
const bool spinup = Memory::Read_U32(request.buffer_in + 4);
|
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();
|
ResetDIRegisters();
|
||||||
return DIResult::Success;
|
return DIResult::Success;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue