mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2024-11-14 21:37:52 -07:00
Merge pull request #12494 from AdmiralCurtiss/globals-wiiipc
Core/IOS/WiiIPC: Refactor to class, move to System.
This commit is contained in:
commit
6725c25600
@ -56,7 +56,7 @@ void Init(Core::System& system, const Sram* override_sram)
|
||||
|
||||
if (SConfig::GetInstance().bWii)
|
||||
{
|
||||
IOS::Init();
|
||||
system.GetWiiIPC().Init();
|
||||
IOS::HLE::Init(system); // Depends on Memory
|
||||
}
|
||||
}
|
||||
@ -65,7 +65,7 @@ void Shutdown(Core::System& system)
|
||||
{
|
||||
// IOS should always be shut down regardless of bWii because it can be running in GC mode (MIOS).
|
||||
IOS::HLE::Shutdown(); // Depends on Memory
|
||||
IOS::Shutdown();
|
||||
system.GetWiiIPC().Shutdown();
|
||||
|
||||
system.GetSystemTimers().Shutdown();
|
||||
system.GetCPU().Shutdown();
|
||||
@ -110,7 +110,7 @@ void DoState(Core::System& system, PointerWrap& p)
|
||||
|
||||
if (SConfig::GetInstance().bWii)
|
||||
{
|
||||
IOS::DoState(p);
|
||||
system.GetWiiIPC().DoState(p);
|
||||
p.DoMarker("IOS");
|
||||
IOS::HLE::GetIOS()->DoState(p);
|
||||
p.DoMarker("IOS::HLE");
|
||||
|
@ -63,7 +63,7 @@ void MemoryManager::InitMMIO(bool is_wii)
|
||||
m_system.GetAudioInterface().RegisterMMIO(m_mmio_mapping.get(), 0x0C006C00);
|
||||
if (is_wii)
|
||||
{
|
||||
IOS::RegisterMMIO(m_mmio_mapping.get(), 0x0D000000);
|
||||
m_system.GetWiiIPC().RegisterMMIO(m_mmio_mapping.get(), 0x0D000000);
|
||||
m_system.GetDVDInterface().RegisterMMIO(m_mmio_mapping.get(), 0x0D006000, true);
|
||||
m_system.GetSerialInterface().RegisterMMIO(m_mmio_mapping.get(), 0x0D006400);
|
||||
m_system.GetExpansionInterface().RegisterMMIO(m_mmio_mapping.get(), 0x0D006800);
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "Core/HW/MMIO.h"
|
||||
#include "Core/HW/ProcessorInterface.h"
|
||||
#include "Core/IOS/IOS.h"
|
||||
#include "Core/System.h"
|
||||
|
||||
// This is the intercommunication between ARM and PPC. Currently only PPC actually uses it, because
|
||||
// of the IOS HLE
|
||||
@ -19,8 +20,8 @@
|
||||
// X2 Reload (a new IOS is being loaded, old one doesn't need to reply anymore)
|
||||
// Y1 Command executed and reply available in HW_IPC_ARMMSG
|
||||
// Y2 Command acknowledge
|
||||
// ppc_msg is a pointer to 0x40byte command structure
|
||||
// arm_msg is, similarly, starlet's response buffer*
|
||||
// m_ppc_msg is a pointer to 0x40byte command structure
|
||||
// m_arm_msg is, similarly, starlet's response buffer*
|
||||
|
||||
namespace IOS
|
||||
{
|
||||
@ -54,164 +55,125 @@ enum
|
||||
UNK_1D0 = 0x1d0,
|
||||
};
|
||||
|
||||
struct CtrlRegister
|
||||
{
|
||||
u8 X1 : 1;
|
||||
u8 X2 : 1;
|
||||
u8 Y1 : 1;
|
||||
u8 Y2 : 1;
|
||||
u8 IX1 : 1;
|
||||
u8 IX2 : 1;
|
||||
u8 IY1 : 1;
|
||||
u8 IY2 : 1;
|
||||
|
||||
CtrlRegister() { X1 = X2 = Y1 = Y2 = IX1 = IX2 = IY1 = IY2 = 0; }
|
||||
inline u8 ppc() { return (IY2 << 5) | (IY1 << 4) | (X2 << 3) | (Y1 << 2) | (Y2 << 1) | X1; }
|
||||
inline u8 arm() { return (IX2 << 5) | (IX1 << 4) | (Y2 << 3) | (X1 << 2) | (X2 << 1) | Y1; }
|
||||
inline void ppc(u32 v)
|
||||
{
|
||||
X1 = v & 1;
|
||||
X2 = (v >> 3) & 1;
|
||||
if ((v >> 2) & 1)
|
||||
Y1 = 0;
|
||||
if ((v >> 1) & 1)
|
||||
Y2 = 0;
|
||||
IY1 = (v >> 4) & 1;
|
||||
IY2 = (v >> 5) & 1;
|
||||
}
|
||||
|
||||
inline void arm(u32 v)
|
||||
{
|
||||
Y1 = v & 1;
|
||||
Y2 = (v >> 3) & 1;
|
||||
if ((v >> 2) & 1)
|
||||
X1 = 0;
|
||||
if ((v >> 1) & 1)
|
||||
X2 = 0;
|
||||
IX1 = (v >> 4) & 1;
|
||||
IX2 = (v >> 5) & 1;
|
||||
}
|
||||
};
|
||||
|
||||
// STATE_TO_SAVE
|
||||
static u32 ppc_msg;
|
||||
static u32 arm_msg;
|
||||
static CtrlRegister ctrl;
|
||||
|
||||
static u32 ppc_irq_flags;
|
||||
static u32 ppc_irq_masks;
|
||||
static u32 arm_irq_flags;
|
||||
static u32 arm_irq_masks;
|
||||
|
||||
// Indicates which pins are accessible by broadway. Writable by starlet only.
|
||||
static constexpr Common::Flags<GPIO> gpio_owner = {GPIO::SLOT_LED, GPIO::SLOT_IN, GPIO::SENSOR_BAR,
|
||||
GPIO::DO_EJECT, GPIO::AVE_SCL, GPIO::AVE_SDA};
|
||||
static Common::Flags<GPIO> gpio_dir;
|
||||
Common::Flags<GPIO> g_gpio_out;
|
||||
|
||||
static u32 resets;
|
||||
|
||||
static CoreTiming::EventType* updateInterrupts;
|
||||
static void UpdateInterrupts(Core::System& system, u64 userdata, s64 cyclesLate);
|
||||
|
||||
void DoState(PointerWrap& p)
|
||||
WiiIPC::WiiIPC(Core::System& system) : m_system(system)
|
||||
{
|
||||
p.Do(ppc_msg);
|
||||
p.Do(arm_msg);
|
||||
p.Do(ctrl);
|
||||
p.Do(ppc_irq_flags);
|
||||
p.Do(ppc_irq_masks);
|
||||
p.Do(arm_irq_flags);
|
||||
p.Do(arm_irq_masks);
|
||||
p.Do(g_gpio_out);
|
||||
}
|
||||
|
||||
static void InitState()
|
||||
{
|
||||
ctrl = CtrlRegister();
|
||||
ppc_msg = 0;
|
||||
arm_msg = 0;
|
||||
WiiIPC::~WiiIPC() = default;
|
||||
|
||||
ppc_irq_flags = 0;
|
||||
ppc_irq_masks = 0;
|
||||
arm_irq_flags = 0;
|
||||
arm_irq_masks = 0;
|
||||
void WiiIPC::DoState(PointerWrap& p)
|
||||
{
|
||||
p.Do(m_ppc_msg);
|
||||
p.Do(m_arm_msg);
|
||||
p.Do(m_ctrl);
|
||||
p.Do(m_ppc_irq_flags);
|
||||
p.Do(m_ppc_irq_masks);
|
||||
p.Do(m_arm_irq_flags);
|
||||
p.Do(m_arm_irq_masks);
|
||||
p.Do(m_gpio_dir);
|
||||
p.Do(m_gpio_out);
|
||||
p.Do(m_resets);
|
||||
}
|
||||
|
||||
void WiiIPC::InitState()
|
||||
{
|
||||
m_ctrl = CtrlRegister();
|
||||
m_ppc_msg = 0;
|
||||
m_arm_msg = 0;
|
||||
|
||||
m_ppc_irq_flags = 0;
|
||||
m_ppc_irq_masks = 0;
|
||||
m_arm_irq_flags = 0;
|
||||
m_arm_irq_masks = 0;
|
||||
|
||||
// The only inputs are POWER, EJECT_BTN, SLOT_IN, and EEP_MISO; Broadway only has access to
|
||||
// SLOT_IN
|
||||
gpio_dir = {
|
||||
m_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 = {};
|
||||
m_gpio_out = {};
|
||||
|
||||
// A cleared bit indicates the device is reset/off, so set everything to 1 (this may not exactly
|
||||
// match hardware)
|
||||
resets = 0xffffffff;
|
||||
m_resets = 0xffffffff;
|
||||
|
||||
ppc_irq_masks |= INT_CAUSE_IPC_BROADWAY;
|
||||
m_ppc_irq_masks |= INT_CAUSE_IPC_BROADWAY;
|
||||
}
|
||||
|
||||
void Init()
|
||||
void WiiIPC::Init()
|
||||
{
|
||||
InitState();
|
||||
updateInterrupts =
|
||||
Core::System::GetInstance().GetCoreTiming().RegisterEvent("IPCInterrupt", UpdateInterrupts);
|
||||
m_event_type_update_interrupts =
|
||||
m_system.GetCoreTiming().RegisterEvent("IPCInterrupt", UpdateInterruptsCallback);
|
||||
}
|
||||
|
||||
void Reset()
|
||||
void WiiIPC::Reset()
|
||||
{
|
||||
INFO_LOG_FMT(WII_IPC, "Resetting ...");
|
||||
InitState();
|
||||
}
|
||||
|
||||
void Shutdown()
|
||||
void WiiIPC::Shutdown()
|
||||
{
|
||||
}
|
||||
|
||||
void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
||||
void WiiIPC::RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
||||
{
|
||||
mmio->Register(base | IPC_PPCMSG, MMIO::InvalidRead<u32>(), MMIO::DirectWrite<u32>(&ppc_msg));
|
||||
mmio->Register(base | IPC_PPCMSG, MMIO::InvalidRead<u32>(), MMIO::DirectWrite<u32>(&m_ppc_msg));
|
||||
|
||||
mmio->Register(base | IPC_PPCCTRL,
|
||||
MMIO::ComplexRead<u32>([](Core::System&, u32) { return ctrl.ppc(); }),
|
||||
mmio->Register(base | IPC_PPCCTRL, MMIO::ComplexRead<u32>([](Core::System& system, u32) {
|
||||
auto& wii_ipc = system.GetWiiIPC();
|
||||
return wii_ipc.m_ctrl.ppc();
|
||||
}),
|
||||
MMIO::ComplexWrite<u32>([](Core::System& system, u32, u32 val) {
|
||||
ctrl.ppc(val);
|
||||
auto& wii_ipc = system.GetWiiIPC();
|
||||
wii_ipc.m_ctrl.ppc(val);
|
||||
// The IPC interrupt is triggered when IY1/IY2 is set and
|
||||
// Y1/Y2 is written to -- even when this results in clearing the bit.
|
||||
if ((val >> 2 & 1 && ctrl.IY1) || (val >> 1 & 1 && ctrl.IY2))
|
||||
ppc_irq_flags |= INT_CAUSE_IPC_BROADWAY;
|
||||
if (ctrl.X1)
|
||||
HLE::GetIOS()->EnqueueIPCRequest(ppc_msg);
|
||||
if ((val >> 2 & 1 && wii_ipc.m_ctrl.IY1) || (val >> 1 & 1 && wii_ipc.m_ctrl.IY2))
|
||||
wii_ipc.m_ppc_irq_flags |= INT_CAUSE_IPC_BROADWAY;
|
||||
if (wii_ipc.m_ctrl.X1)
|
||||
HLE::GetIOS()->EnqueueIPCRequest(wii_ipc.m_ppc_msg);
|
||||
HLE::GetIOS()->UpdateIPC();
|
||||
system.GetCoreTiming().ScheduleEvent(0, updateInterrupts, 0);
|
||||
system.GetCoreTiming().ScheduleEvent(0, wii_ipc.m_event_type_update_interrupts,
|
||||
0);
|
||||
}));
|
||||
|
||||
mmio->Register(base | IPC_ARMMSG, MMIO::DirectRead<u32>(&arm_msg), MMIO::InvalidWrite<u32>());
|
||||
mmio->Register(base | IPC_ARMMSG, MMIO::DirectRead<u32>(&m_arm_msg), MMIO::InvalidWrite<u32>());
|
||||
|
||||
mmio->Register(base | PPC_IRQFLAG, MMIO::InvalidRead<u32>(),
|
||||
MMIO::ComplexWrite<u32>([](Core::System& system, u32, u32 val) {
|
||||
ppc_irq_flags &= ~val;
|
||||
auto& wii_ipc = system.GetWiiIPC();
|
||||
wii_ipc.m_ppc_irq_flags &= ~val;
|
||||
HLE::GetIOS()->UpdateIPC();
|
||||
system.GetCoreTiming().ScheduleEvent(0, updateInterrupts, 0);
|
||||
system.GetCoreTiming().ScheduleEvent(0, wii_ipc.m_event_type_update_interrupts,
|
||||
0);
|
||||
}));
|
||||
|
||||
mmio->Register(base | PPC_IRQMASK, MMIO::InvalidRead<u32>(),
|
||||
MMIO::ComplexWrite<u32>([](Core::System& system, u32, u32 val) {
|
||||
ppc_irq_masks = val;
|
||||
if (ppc_irq_masks & INT_CAUSE_IPC_BROADWAY) // wtf?
|
||||
Reset();
|
||||
auto& wii_ipc = system.GetWiiIPC();
|
||||
wii_ipc.m_ppc_irq_masks = val;
|
||||
if (wii_ipc.m_ppc_irq_masks & INT_CAUSE_IPC_BROADWAY) // wtf?
|
||||
wii_ipc.Reset();
|
||||
HLE::GetIOS()->UpdateIPC();
|
||||
system.GetCoreTiming().ScheduleEvent(0, updateInterrupts, 0);
|
||||
system.GetCoreTiming().ScheduleEvent(0, wii_ipc.m_event_type_update_interrupts,
|
||||
0);
|
||||
}));
|
||||
|
||||
mmio->Register(base | GPIOB_OUT, MMIO::DirectRead<u32>(&g_gpio_out.m_hex),
|
||||
mmio->Register(base | GPIOB_OUT, MMIO::DirectRead<u32>(&m_gpio_out.m_hex),
|
||||
MMIO::ComplexWrite<u32>([](Core::System& system, u32, u32 val) {
|
||||
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])
|
||||
auto& wii_ipc = system.GetWiiIPC();
|
||||
wii_ipc.m_gpio_out.m_hex =
|
||||
(val & gpio_owner.m_hex) | (wii_ipc.m_gpio_out.m_hex & ~gpio_owner.m_hex);
|
||||
if (wii_ipc.m_gpio_out[GPIO::DO_EJECT])
|
||||
{
|
||||
INFO_LOG_FMT(WII_IPC, "Ejecting disc due to GPIO write");
|
||||
system.GetDVDInterface().EjectDisc(DVD::EjectCause::Software);
|
||||
@ -219,9 +181,11 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
||||
// SENSOR_BAR is checked by WiimoteEmu::CameraLogic
|
||||
// TODO: AVE, SLOT_LED
|
||||
}));
|
||||
mmio->Register(base | GPIOB_DIR, MMIO::DirectRead<u32>(&gpio_dir.m_hex),
|
||||
MMIO::ComplexWrite<u32>([](Core::System&, u32, u32 val) {
|
||||
gpio_dir.m_hex = (val & gpio_owner.m_hex) | (gpio_dir.m_hex & ~gpio_owner.m_hex);
|
||||
mmio->Register(base | GPIOB_DIR, MMIO::DirectRead<u32>(&m_gpio_dir.m_hex),
|
||||
MMIO::ComplexWrite<u32>([](Core::System& system, u32, u32 val) {
|
||||
auto& wii_ipc = system.GetWiiIPC();
|
||||
wii_ipc.m_gpio_dir.m_hex =
|
||||
(val & gpio_owner.m_hex) | (wii_ipc.m_gpio_dir.m_hex & ~gpio_owner.m_hex);
|
||||
}));
|
||||
mmio->Register(base | GPIOB_IN, MMIO::ComplexRead<u32>([](Core::System& system, u32) {
|
||||
Common::Flags<GPIO> gpio_in;
|
||||
@ -240,11 +204,12 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
||||
// 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->Register(base | GPIO_OUT, MMIO::DirectRead<u32>(&m_gpio_out.m_hex),
|
||||
MMIO::ComplexWrite<u32>([](Core::System& system, 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])
|
||||
auto& wii_ipc = system.GetWiiIPC();
|
||||
wii_ipc.m_gpio_out.m_hex =
|
||||
(wii_ipc.m_gpio_out.m_hex & gpio_owner.m_hex) | (val & ~gpio_owner.m_hex);
|
||||
if (wii_ipc.m_gpio_out[GPIO::DO_EJECT])
|
||||
{
|
||||
INFO_LOG_FMT(WII_IPC, "Ejecting disc due to GPIO write");
|
||||
system.GetDVDInterface().EjectDisc(DVD::EjectCause::Software);
|
||||
@ -252,9 +217,11 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
||||
// 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>([](Core::System&, u32, u32 val) {
|
||||
gpio_dir.m_hex = (gpio_dir.m_hex & gpio_owner.m_hex) | (val & ~gpio_owner.m_hex);
|
||||
mmio->Register(base | GPIO_DIR, MMIO::DirectRead<u32>(&m_gpio_dir.m_hex),
|
||||
MMIO::ComplexWrite<u32>([](Core::System& system, u32, u32 val) {
|
||||
auto& wii_ipc = system.GetWiiIPC();
|
||||
wii_ipc.m_gpio_dir.m_hex =
|
||||
(wii_ipc.m_gpio_dir.m_hex & gpio_owner.m_hex) | (val & ~gpio_owner.m_hex);
|
||||
}));
|
||||
mmio->Register(base | GPIO_IN, MMIO::ComplexRead<u32>([](Core::System& system, u32) {
|
||||
Common::Flags<GPIO> gpio_in;
|
||||
@ -263,15 +230,16 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
||||
}),
|
||||
MMIO::Nop<u32>());
|
||||
|
||||
mmio->Register(base | HW_RESETS, MMIO::DirectRead<u32>(&resets),
|
||||
mmio->Register(base | HW_RESETS, MMIO::DirectRead<u32>(&m_resets),
|
||||
MMIO::ComplexWrite<u32>([](Core::System& system, u32, u32 val) {
|
||||
// A reset occurs when the corresponding bit is cleared
|
||||
const bool di_reset_triggered = (resets & 0x400) && !(val & 0x400);
|
||||
resets = val;
|
||||
auto& wii_ipc = system.GetWiiIPC();
|
||||
const bool di_reset_triggered = (wii_ipc.m_resets & 0x400) && !(val & 0x400);
|
||||
wii_ipc.m_resets = val;
|
||||
if (di_reset_triggered)
|
||||
{
|
||||
// The GPIO *disables* spinning up the drive
|
||||
const bool spinup = !g_gpio_out[GPIO::DI_SPIN];
|
||||
const bool spinup = !wii_ipc.m_gpio_out[GPIO::DI_SPIN];
|
||||
INFO_LOG_FMT(WII_IPC, "Resetting DI {} spinup", spinup ? "with" : "without");
|
||||
system.GetDVDInterface().ResetDrive(spinup);
|
||||
}
|
||||
@ -285,53 +253,59 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
||||
mmio->Register(base | UNK_1D0, MMIO::Constant<u32>(0), MMIO::Nop<u32>());
|
||||
}
|
||||
|
||||
static void UpdateInterrupts(Core::System& system, u64 userdata, s64 cyclesLate)
|
||||
void WiiIPC::UpdateInterruptsCallback(Core::System& system, u64 userdata, s64 cycles_late)
|
||||
{
|
||||
if ((ctrl.Y1 & ctrl.IY1) || (ctrl.Y2 & ctrl.IY2))
|
||||
system.GetWiiIPC().UpdateInterrupts();
|
||||
}
|
||||
|
||||
void WiiIPC::UpdateInterrupts()
|
||||
{
|
||||
if ((m_ctrl.Y1 & m_ctrl.IY1) || (m_ctrl.Y2 & m_ctrl.IY2))
|
||||
{
|
||||
ppc_irq_flags |= INT_CAUSE_IPC_BROADWAY;
|
||||
m_ppc_irq_flags |= INT_CAUSE_IPC_BROADWAY;
|
||||
}
|
||||
|
||||
if ((ctrl.X1 & ctrl.IX1) || (ctrl.X2 & ctrl.IX2))
|
||||
if ((m_ctrl.X1 & m_ctrl.IX1) || (m_ctrl.X2 & m_ctrl.IX2))
|
||||
{
|
||||
ppc_irq_flags |= INT_CAUSE_IPC_STARLET;
|
||||
m_ppc_irq_flags |= INT_CAUSE_IPC_STARLET;
|
||||
}
|
||||
|
||||
// Generate interrupt on PI if any of the devices behind starlet have an interrupt and mask is set
|
||||
system.GetProcessorInterface().SetInterrupt(ProcessorInterface::INT_CAUSE_WII_IPC,
|
||||
!!(ppc_irq_flags & ppc_irq_masks));
|
||||
m_system.GetProcessorInterface().SetInterrupt(ProcessorInterface::INT_CAUSE_WII_IPC,
|
||||
!!(m_ppc_irq_flags & m_ppc_irq_masks));
|
||||
}
|
||||
|
||||
void ClearX1()
|
||||
void WiiIPC::ClearX1()
|
||||
{
|
||||
ctrl.X1 = 0;
|
||||
m_ctrl.X1 = 0;
|
||||
}
|
||||
|
||||
void GenerateAck(u32 address)
|
||||
void WiiIPC::GenerateAck(u32 address)
|
||||
{
|
||||
ctrl.Y2 = 1;
|
||||
DEBUG_LOG_FMT(WII_IPC, "GenerateAck: {:08x} | {:08x} [R:{} A:{} E:{}]", ppc_msg, address, ctrl.Y1,
|
||||
ctrl.Y2, ctrl.X1);
|
||||
m_ctrl.Y2 = 1;
|
||||
DEBUG_LOG_FMT(WII_IPC, "GenerateAck: {:08x} | {:08x} [R:{} A:{} E:{}]", m_ppc_msg, address,
|
||||
m_ctrl.Y1, m_ctrl.Y2, m_ctrl.X1);
|
||||
// Based on a hardware test, the IPC interrupt takes approximately 100 TB ticks to fire
|
||||
// after Y2 is seen in the control register.
|
||||
Core::System::GetInstance().GetCoreTiming().ScheduleEvent(100 * SystemTimers::TIMER_RATIO,
|
||||
updateInterrupts);
|
||||
m_system.GetCoreTiming().ScheduleEvent(100 * SystemTimers::TIMER_RATIO,
|
||||
m_event_type_update_interrupts);
|
||||
}
|
||||
|
||||
void GenerateReply(u32 address)
|
||||
void WiiIPC::GenerateReply(u32 address)
|
||||
{
|
||||
arm_msg = address;
|
||||
ctrl.Y1 = 1;
|
||||
DEBUG_LOG_FMT(WII_IPC, "GenerateReply: {:08x} | {:08x} [R:{} A:{} E:{}]", ppc_msg, address,
|
||||
ctrl.Y1, ctrl.Y2, ctrl.X1);
|
||||
m_arm_msg = address;
|
||||
m_ctrl.Y1 = 1;
|
||||
DEBUG_LOG_FMT(WII_IPC, "GenerateReply: {:08x} | {:08x} [R:{} A:{} E:{}]", m_ppc_msg, address,
|
||||
m_ctrl.Y1, m_ctrl.Y2, m_ctrl.X1);
|
||||
// Based on a hardware test, the IPC interrupt takes approximately 100 TB ticks to fire
|
||||
// after Y1 is seen in the control register.
|
||||
Core::System::GetInstance().GetCoreTiming().ScheduleEvent(100 * SystemTimers::TIMER_RATIO,
|
||||
updateInterrupts);
|
||||
m_system.GetCoreTiming().ScheduleEvent(100 * SystemTimers::TIMER_RATIO,
|
||||
m_event_type_update_interrupts);
|
||||
}
|
||||
|
||||
bool IsReady()
|
||||
bool WiiIPC::IsReady() const
|
||||
{
|
||||
return ((ctrl.Y1 == 0) && (ctrl.Y2 == 0) && ((ppc_irq_flags & INT_CAUSE_IPC_BROADWAY) == 0));
|
||||
return ((m_ctrl.Y1 == 0) && (m_ctrl.Y2 == 0) &&
|
||||
((m_ppc_irq_flags & INT_CAUSE_IPC_BROADWAY) == 0));
|
||||
}
|
||||
} // namespace IOS
|
||||
|
@ -7,6 +7,14 @@
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
class PointerWrap;
|
||||
namespace Core
|
||||
{
|
||||
class System;
|
||||
}
|
||||
namespace CoreTiming
|
||||
{
|
||||
struct EventType;
|
||||
}
|
||||
namespace MMIO
|
||||
{
|
||||
class Mapping;
|
||||
@ -63,18 +71,92 @@ enum class GPIO : u32
|
||||
DEBUG7 = 0x800000,
|
||||
};
|
||||
|
||||
extern Common::Flags<GPIO> g_gpio_out;
|
||||
struct CtrlRegister
|
||||
{
|
||||
u8 X1 : 1;
|
||||
u8 X2 : 1;
|
||||
u8 Y1 : 1;
|
||||
u8 Y2 : 1;
|
||||
u8 IX1 : 1;
|
||||
u8 IX2 : 1;
|
||||
u8 IY1 : 1;
|
||||
u8 IY2 : 1;
|
||||
|
||||
void Init();
|
||||
void Reset();
|
||||
void Shutdown();
|
||||
void DoState(PointerWrap& p);
|
||||
CtrlRegister() { X1 = X2 = Y1 = Y2 = IX1 = IX2 = IY1 = IY2 = 0; }
|
||||
inline u8 ppc() { return (IY2 << 5) | (IY1 << 4) | (X2 << 3) | (Y1 << 2) | (Y2 << 1) | X1; }
|
||||
inline u8 arm() { return (IX2 << 5) | (IX1 << 4) | (Y2 << 3) | (X1 << 2) | (X2 << 1) | Y1; }
|
||||
inline void ppc(u32 v)
|
||||
{
|
||||
X1 = v & 1;
|
||||
X2 = (v >> 3) & 1;
|
||||
if ((v >> 2) & 1)
|
||||
Y1 = 0;
|
||||
if ((v >> 1) & 1)
|
||||
Y2 = 0;
|
||||
IY1 = (v >> 4) & 1;
|
||||
IY2 = (v >> 5) & 1;
|
||||
}
|
||||
|
||||
void RegisterMMIO(MMIO::Mapping* mmio, u32 base);
|
||||
inline void arm(u32 v)
|
||||
{
|
||||
Y1 = v & 1;
|
||||
Y2 = (v >> 3) & 1;
|
||||
if ((v >> 2) & 1)
|
||||
X1 = 0;
|
||||
if ((v >> 1) & 1)
|
||||
X2 = 0;
|
||||
IX1 = (v >> 4) & 1;
|
||||
IX2 = (v >> 5) & 1;
|
||||
}
|
||||
};
|
||||
|
||||
void ClearX1();
|
||||
void GenerateAck(u32 address);
|
||||
void GenerateReply(u32 address);
|
||||
class WiiIPC
|
||||
{
|
||||
public:
|
||||
explicit WiiIPC(Core::System& system);
|
||||
WiiIPC(const WiiIPC&) = delete;
|
||||
WiiIPC(WiiIPC&&) = delete;
|
||||
WiiIPC& operator=(const WiiIPC&) = delete;
|
||||
WiiIPC& operator=(WiiIPC&&) = delete;
|
||||
~WiiIPC();
|
||||
|
||||
bool IsReady();
|
||||
void Init();
|
||||
void Reset();
|
||||
void Shutdown();
|
||||
void DoState(PointerWrap& p);
|
||||
|
||||
void RegisterMMIO(MMIO::Mapping* mmio, u32 base);
|
||||
|
||||
void ClearX1();
|
||||
void GenerateAck(u32 address);
|
||||
void GenerateReply(u32 address);
|
||||
|
||||
bool IsReady() const;
|
||||
|
||||
Common::Flags<GPIO> GetGPIOOutFlags() const { return m_gpio_out; }
|
||||
|
||||
private:
|
||||
void InitState();
|
||||
|
||||
static void UpdateInterruptsCallback(Core::System& system, u64 userdata, s64 cycles_late);
|
||||
void UpdateInterrupts();
|
||||
|
||||
u32 m_ppc_msg = 0;
|
||||
u32 m_arm_msg = 0;
|
||||
CtrlRegister m_ctrl{};
|
||||
|
||||
u32 m_ppc_irq_flags = 0;
|
||||
u32 m_ppc_irq_masks = 0;
|
||||
u32 m_arm_irq_flags = 0;
|
||||
u32 m_arm_irq_masks = 0;
|
||||
|
||||
Common::Flags<GPIO> m_gpio_dir{};
|
||||
Common::Flags<GPIO> m_gpio_out{};
|
||||
|
||||
u32 m_resets = 0;
|
||||
|
||||
CoreTiming::EventType* m_event_type_update_interrupts = nullptr;
|
||||
|
||||
Core::System& m_system;
|
||||
};
|
||||
} // namespace IOS
|
||||
|
@ -38,8 +38,15 @@ public:
|
||||
virtual u8 GetWiimoteDeviceIndex() const = 0;
|
||||
virtual void SetWiimoteDeviceIndex(u8 index) = 0;
|
||||
|
||||
enum class SensorBarState : bool
|
||||
{
|
||||
Disabled,
|
||||
Enabled
|
||||
};
|
||||
|
||||
// Called every ~200hz after HID channels are established.
|
||||
virtual void PrepareInput(WiimoteEmu::DesiredWiimoteState* target_state) = 0;
|
||||
virtual void PrepareInput(WiimoteEmu::DesiredWiimoteState* target_state,
|
||||
SensorBarState sensor_bar_state) = 0;
|
||||
virtual void Update(const WiimoteEmu::DesiredWiimoteState& target_state) = 0;
|
||||
|
||||
void SetInterruptCallback(InterruptCallbackType callback) { m_callback = std::move(callback); }
|
||||
|
@ -11,7 +11,6 @@
|
||||
#include "Common/MathUtil.h"
|
||||
#include "Common/Matrix.h"
|
||||
|
||||
#include "Core/HW/WII_IPC.h"
|
||||
#include "Core/HW/WiimoteCommon/WiimoteReport.h"
|
||||
|
||||
namespace WiimoteEmu
|
||||
@ -111,10 +110,6 @@ void CameraLogic::Update(const std::array<CameraPoint, NUM_POINTS>& camera_point
|
||||
if (m_reg_data.enable_object_tracking != OBJECT_TRACKING_ENABLE)
|
||||
return;
|
||||
|
||||
// If the sensor bar is off the camera will see no LEDs and return 0xFFs.
|
||||
if (!IOS::g_gpio_out[IOS::GPIO::SENSOR_BAR])
|
||||
return;
|
||||
|
||||
switch (m_reg_data.mode)
|
||||
{
|
||||
case IR_MODE_BASIC:
|
||||
|
@ -446,7 +446,8 @@ void Wiimote::UpdateButtonsStatus(const DesiredWiimoteState& target_state)
|
||||
m_status.buttons.hex = target_state.buttons.hex & ButtonData::BUTTON_MASK;
|
||||
}
|
||||
|
||||
void Wiimote::BuildDesiredWiimoteState(DesiredWiimoteState* target_state)
|
||||
void Wiimote::BuildDesiredWiimoteState(DesiredWiimoteState* target_state,
|
||||
SensorBarState sensor_bar_state)
|
||||
{
|
||||
// Hotkey / settings modifier
|
||||
// Data is later accessed in IsSideways and IsUpright
|
||||
@ -468,10 +469,18 @@ void Wiimote::BuildDesiredWiimoteState(DesiredWiimoteState* target_state)
|
||||
ConvertAccelData(GetTotalAcceleration(), ACCEL_ZERO_G << 2, ACCEL_ONE_G << 2);
|
||||
|
||||
// Calculate IR camera state.
|
||||
target_state->camera_points = CameraLogic::GetCameraPoints(
|
||||
GetTotalTransformation(),
|
||||
Common::Vec2(m_fov_x_setting.GetValue(), m_fov_y_setting.GetValue()) / 360 *
|
||||
float(MathUtil::TAU));
|
||||
if (sensor_bar_state == SensorBarState::Enabled)
|
||||
{
|
||||
target_state->camera_points = CameraLogic::GetCameraPoints(
|
||||
GetTotalTransformation(),
|
||||
Common::Vec2(m_fov_x_setting.GetValue(), m_fov_y_setting.GetValue()) / 360 *
|
||||
float(MathUtil::TAU));
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the sensor bar is off the camera will see no LEDs and return 0xFFs.
|
||||
target_state->camera_points = DesiredWiimoteState::DEFAULT_CAMERA;
|
||||
}
|
||||
|
||||
// Calculate MotionPlus state.
|
||||
if (m_motion_plus_setting.GetValue())
|
||||
@ -498,10 +507,11 @@ void Wiimote::SetWiimoteDeviceIndex(u8 index)
|
||||
}
|
||||
|
||||
// This is called every ::Wiimote::UPDATE_FREQ (200hz)
|
||||
void Wiimote::PrepareInput(WiimoteEmu::DesiredWiimoteState* target_state)
|
||||
void Wiimote::PrepareInput(WiimoteEmu::DesiredWiimoteState* target_state,
|
||||
SensorBarState sensor_bar_state)
|
||||
{
|
||||
const auto lock = GetStateLock();
|
||||
BuildDesiredWiimoteState(target_state);
|
||||
BuildDesiredWiimoteState(target_state, sensor_bar_state);
|
||||
}
|
||||
|
||||
void Wiimote::Update(const WiimoteEmu::DesiredWiimoteState& target_state)
|
||||
|
@ -156,7 +156,8 @@ public:
|
||||
u8 GetWiimoteDeviceIndex() const override;
|
||||
void SetWiimoteDeviceIndex(u8 index) override;
|
||||
|
||||
void PrepareInput(WiimoteEmu::DesiredWiimoteState* target_state) override;
|
||||
void PrepareInput(WiimoteEmu::DesiredWiimoteState* target_state,
|
||||
SensorBarState sensor_bar_state) override;
|
||||
void Update(const WiimoteEmu::DesiredWiimoteState& target_state) override;
|
||||
void EventLinked() override;
|
||||
void EventUnlinked() override;
|
||||
@ -187,7 +188,7 @@ private:
|
||||
|
||||
void StepDynamics();
|
||||
void UpdateButtonsStatus(const DesiredWiimoteState& target_state);
|
||||
void BuildDesiredWiimoteState(DesiredWiimoteState* target_state);
|
||||
void BuildDesiredWiimoteState(DesiredWiimoteState* target_state, SensorBarState sensor_bar_state);
|
||||
|
||||
// Returns simulated accelerometer data in m/s^2.
|
||||
Common::Vec3 GetAcceleration(Common::Vec3 extra_acceleration) const;
|
||||
|
@ -465,7 +465,8 @@ void Wiimote::SetWiimoteDeviceIndex(u8 index)
|
||||
m_bt_device_index = index;
|
||||
}
|
||||
|
||||
void Wiimote::PrepareInput(WiimoteEmu::DesiredWiimoteState* target_state)
|
||||
void Wiimote::PrepareInput(WiimoteEmu::DesiredWiimoteState* target_state,
|
||||
SensorBarState sensor_bar_state)
|
||||
{
|
||||
// Nothing to do here on real Wiimotes.
|
||||
}
|
||||
|
@ -67,7 +67,8 @@ public:
|
||||
u8 GetWiimoteDeviceIndex() const override;
|
||||
void SetWiimoteDeviceIndex(u8 index) override;
|
||||
|
||||
void PrepareInput(WiimoteEmu::DesiredWiimoteState* target_state) override;
|
||||
void PrepareInput(WiimoteEmu::DesiredWiimoteState* target_state,
|
||||
SensorBarState sensor_bar_state) override;
|
||||
void Update(const WiimoteEmu::DesiredWiimoteState& target_state) override;
|
||||
void EventLinked() override;
|
||||
void EventUnlinked() override;
|
||||
|
@ -234,7 +234,6 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
EmulationKernel& GetEmulationKernel() const { return static_cast<EmulationKernel&>(m_ios); }
|
||||
|
||||
Core::System& GetSystem() const { return GetEmulationKernel().GetSystem(); }
|
||||
|
@ -539,7 +539,7 @@ void EmulationKernel::InitIPC()
|
||||
return;
|
||||
|
||||
INFO_LOG_FMT(IOS, "IPC initialised.");
|
||||
GenerateAck(0);
|
||||
m_system.GetWiiIPC().GenerateAck(0);
|
||||
}
|
||||
|
||||
void EmulationKernel::AddDevice(std::unique_ptr<Device> device)
|
||||
@ -816,13 +816,14 @@ void EmulationKernel::HandleIPCEvent(u64 userdata)
|
||||
|
||||
void EmulationKernel::UpdateIPC()
|
||||
{
|
||||
if (m_ipc_paused || !IsReady())
|
||||
auto& wii_ipc = m_system.GetWiiIPC();
|
||||
if (m_ipc_paused || !wii_ipc.IsReady())
|
||||
return;
|
||||
|
||||
if (!m_request_queue.empty())
|
||||
{
|
||||
ClearX1();
|
||||
GenerateAck(m_request_queue.front());
|
||||
wii_ipc.ClearX1();
|
||||
wii_ipc.GenerateAck(m_request_queue.front());
|
||||
u32 command = m_request_queue.front();
|
||||
m_request_queue.pop_front();
|
||||
ExecuteIPCCommand(command);
|
||||
@ -831,7 +832,7 @@ void EmulationKernel::UpdateIPC()
|
||||
|
||||
if (!m_reply_queue.empty())
|
||||
{
|
||||
GenerateReply(m_reply_queue.front());
|
||||
wii_ipc.GenerateReply(m_reply_queue.front());
|
||||
DEBUG_LOG_FMT(IOS, "<<-- Reply to IPC Request @ {:#010x}", m_reply_queue.front());
|
||||
m_reply_queue.pop_front();
|
||||
return;
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "Common/StringUtil.h"
|
||||
#include "Common/Swap.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/HW/WII_IPC.h"
|
||||
#include "Core/HW/Wiimote.h"
|
||||
#include "Core/HW/WiimoteCommon/WiimoteConstants.h"
|
||||
#include "Core/HW/WiimoteCommon/WiimoteHid.h"
|
||||
@ -26,6 +27,7 @@
|
||||
#include "Core/IOS/USB/Bluetooth/BTEmu.h"
|
||||
#include "Core/IOS/USB/Bluetooth/WiimoteHIDAttr.h"
|
||||
#include "Core/IOS/USB/Bluetooth/l2cap.h"
|
||||
#include "Core/System.h"
|
||||
|
||||
namespace IOS::HLE
|
||||
{
|
||||
@ -367,7 +369,11 @@ WiimoteDevice::PrepareInput(WiimoteEmu::DesiredWiimoteState* wiimote_state)
|
||||
const auto* channel = FindChannelWithPSM(L2CAP_PSM_HID_INTR);
|
||||
if (channel && channel->IsComplete())
|
||||
{
|
||||
m_hid_source->PrepareInput(wiimote_state);
|
||||
auto gpio_out = m_host->GetSystem().GetWiiIPC().GetGPIOOutFlags();
|
||||
m_hid_source->PrepareInput(wiimote_state,
|
||||
gpio_out[IOS::GPIO::SENSOR_BAR] ?
|
||||
WiimoteCommon::HIDWiimote::SensorBarState::Enabled :
|
||||
WiimoteCommon::HIDWiimote::SensorBarState::Disabled);
|
||||
return NextUpdateInputCall::Update;
|
||||
}
|
||||
return NextUpdateInputCall::None;
|
||||
|
@ -95,7 +95,7 @@ static size_t s_state_writes_in_queue;
|
||||
static std::condition_variable s_state_write_queue_is_empty;
|
||||
|
||||
// Don't forget to increase this after doing changes on the savestate system
|
||||
constexpr u32 STATE_VERSION = 166; // Last changed in PR 12487
|
||||
constexpr u32 STATE_VERSION = 167; // Last changed in PR 12494
|
||||
|
||||
// Increase this if the StateExtendedHeader definition changes
|
||||
constexpr u32 EXTENDED_HEADER_VERSION = 1; // Last changed in PR 12217
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "Core/HW/Sram.h"
|
||||
#include "Core/HW/SystemTimers.h"
|
||||
#include "Core/HW/VideoInterface.h"
|
||||
#include "Core/HW/WII_IPC.h"
|
||||
#include "Core/PowerPC/Interpreter/Interpreter.h"
|
||||
#include "Core/PowerPC/JitInterface.h"
|
||||
#include "Core/PowerPC/PowerPC.h"
|
||||
@ -45,7 +46,7 @@ struct System::Impl
|
||||
explicit Impl(System& system)
|
||||
: m_audio_interface(system), m_core_timing(system), m_command_processor{system},
|
||||
m_cpu(system), m_dsp(system), m_dvd_interface(system), m_dvd_thread(system),
|
||||
m_expansion_interface(system), m_fifo{system}, m_gp_fifo(system),
|
||||
m_expansion_interface(system), m_fifo{system}, m_gp_fifo(system), m_wii_ipc(system),
|
||||
m_memory(system), m_pixel_engine{system}, m_power_pc(system),
|
||||
m_mmu(system, m_memory, m_power_pc), m_processor_interface(system),
|
||||
m_serial_interface(system), m_system_timers(system), m_video_interface(system),
|
||||
@ -72,6 +73,7 @@ struct System::Impl
|
||||
HSP::HSPManager m_hsp;
|
||||
IOS::HLE::USB::InfinityBase m_infinity_base;
|
||||
IOS::HLE::USB::SkylanderPortal m_skylander_portal;
|
||||
IOS::WiiIPC m_wii_ipc;
|
||||
Memory::MemoryManager m_memory;
|
||||
MemoryInterface::MemoryInterfaceManager m_memory_interface;
|
||||
PixelEngine::PixelEngineManager m_pixel_engine;
|
||||
@ -219,6 +221,11 @@ IOS::HLE::USB::InfinityBase& System::GetInfinityBase() const
|
||||
return m_impl->m_infinity_base;
|
||||
}
|
||||
|
||||
IOS::WiiIPC& System::GetWiiIPC() const
|
||||
{
|
||||
return m_impl->m_wii_ipc;
|
||||
}
|
||||
|
||||
Memory::MemoryManager& System::GetMemory() const
|
||||
{
|
||||
return m_impl->m_memory;
|
||||
|
@ -56,6 +56,10 @@ namespace HSP
|
||||
{
|
||||
class HSPManager;
|
||||
}
|
||||
namespace IOS
|
||||
{
|
||||
class WiiIPC;
|
||||
}
|
||||
namespace IOS::HLE::USB
|
||||
{
|
||||
class SkylanderPortal;
|
||||
@ -151,6 +155,7 @@ public:
|
||||
JitInterface& GetJitInterface() const;
|
||||
IOS::HLE::USB::SkylanderPortal& GetSkylanderPortal() const;
|
||||
IOS::HLE::USB::InfinityBase& GetInfinityBase() const;
|
||||
IOS::WiiIPC& GetWiiIPC() const;
|
||||
Memory::MemoryManager& GetMemory() const;
|
||||
MemoryInterface::MemoryInterfaceManager& GetMemoryInterface() const;
|
||||
PowerPC::MMU& GetMMU() const;
|
||||
|
Loading…
Reference in New Issue
Block a user