Make reset button (Emulation -> Reset) work correctly for Wii games.

Fixes issue 8328.

As far as I know, this works the same way as console. Games will generally
react to the reset button the same way as Home->Reset, so this is
only marginally useful, but possibly nice to have in certain situations.

Note that if you try to use Reset, and you're running a WAD which isn't
installed, it will likely crash because WADs respond to the reset button
by launching themselves with ES_LAUNCH.  It might be a good idea to add some
sort of hack to make this work as expected.

It would be easy to extend this to support the power button, but it's
unclear how exactly that should be exposed in the UI. See also issue 8979.

Needs to be rebased once PR #3811 is merged.
This commit is contained in:
magumagu 2016-05-07 14:22:31 -07:00
parent 10682dbf58
commit e01a33f69b
2 changed files with 54 additions and 33 deletions

View File

@ -10,6 +10,8 @@
#include "Core/CoreTiming.h"
#include "Core/HW/MMIO.h"
#include "Core/HW/ProcessorInterface.h"
#include "Core/IPC_HLE/WII_IPC_HLE.h"
#include "Core/IPC_HLE/WII_IPC_HLE_Device_stm.h"
#include "Core/PowerPC/PowerPC.h"
namespace ProcessorInterface
@ -31,6 +33,9 @@ static u32 m_Unknown;
static int toggleResetButton;
static void ToggleResetButtonCallback(u64 userdata, s64 cyclesLate);
static int iosNotifyResetButton;
static void IOSNotifyResetButtonCallback(u64 userdata, s64 cyclesLate);
// Let the PPC know that an external exception is set/cleared
void UpdateException();
@ -67,6 +72,8 @@ void Init()
m_InterruptCause = INT_CAUSE_RST_BUTTON | INT_CAUSE_VI;
toggleResetButton = CoreTiming::RegisterEvent("ToggleResetButton", ToggleResetButtonCallback);
iosNotifyResetButton =
CoreTiming::RegisterEvent("IOSNotifyResetButton", IOSNotifyResetButtonCallback);
}
void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
@ -195,9 +202,21 @@ static void ToggleResetButtonCallback(u64 userdata, s64 cyclesLate)
SetResetButton(!!userdata);
}
static void IOSNotifyResetButtonCallback(u64 userdata, s64 cyclesLate)
{
if (SConfig::GetInstance().bWii)
{
std::shared_ptr<IWII_IPC_HLE_Device> stm =
WII_IPC_HLE_Interface::GetDeviceByName("/dev/stm/eventhook");
if (stm)
std::static_pointer_cast<CWII_IPC_HLE_Device_stm_eventhook>(stm)->ResetButton();
}
}
void ResetButton_Tap()
{
CoreTiming::ScheduleEvent_AnyThread(0, toggleResetButton, true);
CoreTiming::ScheduleEvent_AnyThread(0, iosNotifyResetButton, 0);
CoreTiming::ScheduleEvent_AnyThread(243000000, toggleResetButton, false);
}

View File

@ -25,6 +25,12 @@ enum
IOCTL_STM_READDDRREG2 = 0x4002,
};
enum
{
STM_EVENT_RESET = 0x00020000,
STM_EVENT_POWER = 0x00000800
};
// The /dev/stm/immediate
class CWII_IPC_HLE_Device_stm_immediate : public IWII_IPC_HLE_Device
{
@ -141,42 +147,38 @@ public:
IPCCommandResult IOCtl(u32 _CommandAddress) override
{
u32 Parameter = Memory::Read_U32(_CommandAddress + 0x0C);
u32 BufferIn = Memory::Read_U32(_CommandAddress + 0x10);
u32 BufferInSize = Memory::Read_U32(_CommandAddress + 0x14);
u32 BufferOut = Memory::Read_U32(_CommandAddress + 0x18);
u32 BufferOutSize = Memory::Read_U32(_CommandAddress + 0x1C);
// Prepare the out buffer(s) with zeros as a safety precaution
// to avoid returning bad values
Memory::Memset(BufferOut, 0, BufferOutSize);
u32 ReturnValue = 0;
// write return value
switch (Parameter)
if (Parameter != IOCTL_STM_EVENTHOOK)
{
case IOCTL_STM_EVENTHOOK:
{
m_EventHookAddress = _CommandAddress;
INFO_LOG(WII_IPC_STM, "%s registers event hook:", GetDeviceName().c_str());
DEBUG_LOG(WII_IPC_STM, "%x - IOCTL_STM_EVENTHOOK", Parameter);
DEBUG_LOG(WII_IPC_STM, "BufferIn: 0x%08x", BufferIn);
DEBUG_LOG(WII_IPC_STM, "BufferInSize: 0x%08x", BufferInSize);
DEBUG_LOG(WII_IPC_STM, "BufferOut: 0x%08x", BufferOut);
DEBUG_LOG(WII_IPC_STM, "BufferOutSize: 0x%08x", BufferOutSize);
DumpCommands(BufferIn, BufferInSize / 4, LogTypes::WII_IPC_STM);
}
break;
default:
_dbg_assert_msg_(WII_IPC_STM, 0, "unknown %s ioctl %x", GetDeviceName().c_str(), Parameter);
break;
ERROR_LOG(WII_IPC_STM, "Bad IOCtl in CWII_IPC_HLE_Device_stm_eventhook");
Memory::Write_U32(FS_EINVAL, _CommandAddress + 4);
return GetDefaultReply();
}
// Write return value to the IPC call, 0 means success
Memory::Write_U32(ReturnValue, _CommandAddress + 0x4);
return GetDefaultReply();
// IOCTL_STM_EVENTHOOK waits until the reset button or power button
// is pressed.
m_EventHookAddress = _CommandAddress;
return GetNoReply();
}
void ResetButton()
{
if (!m_Active || m_EventHookAddress == 0)
{
// If the device isn't open, ignore the button press.
return;
}
// The reset button returns STM_EVENT_RESET.
u32 BufferOut = Memory::Read_U32(m_EventHookAddress + 0x18);
Memory::Write_U32(STM_EVENT_RESET, BufferOut);
// Fill in command buffer.
Memory::Write_U32(FS_SUCCESS, m_EventHookAddress + 4);
Memory::Write_U32(IPC_REP_ASYNC, m_EventHookAddress);
Memory::Write_U32(IPC_CMD_IOCTL, m_EventHookAddress + 8);
// Generate a reply to the IPC command.
WII_IPC_HLE_Interface::EnqueueReply_Immediate(m_EventHookAddress);
}
// STATE_TO_SAVE