rewrite (lowish level) wii ipc to be more like wiibrew (http://wiibrew.org/wiki/Hardware/IPC)

git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@5189 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
Shawn Hoffman 2010-03-11 17:22:13 +00:00
parent 5bf33f778f
commit b2ab31681a
5 changed files with 199 additions and 230 deletions

View File

@ -238,12 +238,12 @@ void SetInterrupt(u32 _causemask, bool _bSet)
if (_bSet && !(m_InterruptCause & _causemask))
{
DEBUG_LOG(PROCESSORINTERFACE, "Setting Interrupt %s (%s)", Debug_GetInterruptName(_causemask), "set");
DEBUG_LOG(PROCESSORINTERFACE, "Setting Interrupt %s (set)", Debug_GetInterruptName(_causemask));
}
if (!_bSet && (m_InterruptCause & _causemask))
{
DEBUG_LOG(PROCESSORINTERFACE, "Setting Interrupt %s (%s)", Debug_GetInterruptName(_causemask), "clear");
DEBUG_LOG(PROCESSORINTERFACE, "Setting Interrupt %s (clear)", Debug_GetInterruptName(_causemask));
}
if (_bSet)

View File

@ -27,109 +27,133 @@
#include "../IPC_HLE/WII_IPC_HLE.h"
#include "WII_IPC.h"
// This is the intercommunication between ARM and PPC. Currently only PPC actually uses it, because of the IOS HLE
// How IOS uses IPC:
// X1 Execute command: a new pointer is available in HW_IPC_PPCCTRL
// 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*
namespace WII_IPCInterface
{
enum
{
IPC_COMMAND_REGISTER = 0x00,
IPC_CONTROL_REGISTER = 0x04,
IPC_REPLY_REGISTER = 0x08,
IPC_STATUS_REGISTER = 0x30,
IPC_CONFIG_REGISTER = 0x34,
IPC_SENSOR_BAR_POWER_REGISTER = 0xC0
IPC_PPCMSG = 0x00,
IPC_PPCCTRL = 0x04,
IPC_ARMMSG = 0x08,
IPC_ARMCTRL = 0x0c,
PPC_IRQFLAG = 0x30,
PPC_IRQMASK = 0x34,
ARM_IRQFLAG = 0x38,
ARM_IRQMASK = 0x3c,
GPIOB_OUT = 0xc0 // sensor bar power flag??
};
union UIPC_Control
struct CtrlRegister
{
u32 Hex;
struct
{
unsigned ExecuteCmd : 1;
unsigned AckReady : 1;
unsigned ReplyReady : 1;
unsigned Relaunch : 1;
unsigned unk5 : 1;
unsigned unk6 : 1;
unsigned pad : 26;
};
UIPC_Control(u32 _Hex = 0) {Hex = _Hex;}
};
u8 X1 : 1;
u8 X2 : 1;
u8 Y1 : 1;
u8 Y2 : 1;
u8 IX1 : 1;
u8 IX2 : 1;
u8 IY1 : 1;
u8 IY2 : 1;
union UIPC_Status
{
u32 Hex;
struct
{
unsigned : 30;
unsigned INTERRUPT : 1; // 0x40000000
unsigned : 1;
};
UIPC_Status(u32 _Hex = 0) {Hex = _Hex;}
};
CtrlRegister() { X1 = X2 = Y1 = Y2 = IX1 = IX2 = IY1 = IY2 = 0; }
union UIPC_Config
{
u32 Hex;
struct
{
unsigned : 30;
unsigned INT_MASK : 1; // 0x40000000
unsigned : 1;
};
UIPC_Config(u32 _Hex = 0)
{
Hex = _Hex;
INT_MASK = 1;
}
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
bool g_ExeCmd = false;
u32 g_Address = NULL;
u32 g_Reply = NULL;
u32 g_ReplyHead = NULL;
u32 g_ReplyTail = NULL;
u32 g_ReplyNum = NULL;
u32 g_ReplyFifo[REPLY_FIFO_DEPTH] = {0};
u32 g_SensorBarPower = NULL;
UIPC_Status g_IPC_Status(NULL);
UIPC_Config g_IPC_Config(NULL);
UIPC_Control g_IPC_Control(NULL);
u32 ppc_msg;
u32 arm_msg;
CtrlRegister ctrl;
u32 ppc_irq_flags;
u32 ppc_irq_masks;
u32 arm_irq_flags;
u32 arm_irq_masks;
u32 sensorbar_power; // do we need to care about this?
// not actual registers:
bool cmd_active;
u32 g_ReplyHead;
u32 g_ReplyTail;
u32 g_ReplyNum;
u32 g_ReplyFifo[REPLY_FIFO_DEPTH];
void DoState(PointerWrap &p)
{
p.Do(g_ExeCmd);
p.Do(g_Address);
p.Do(g_Reply);
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(sensorbar_power);
p.Do(cmd_active);
p.Do(g_ReplyHead);
p.Do(g_ReplyTail);
p.Do(g_ReplyNum);
p.DoArray(g_ReplyFifo, REPLY_FIFO_DEPTH);
p.Do(g_SensorBarPower);
p.Do(g_IPC_Status);
p.Do(g_IPC_Config);
p.Do(g_IPC_Control);
}
// Init
void Init()
{
g_ExeCmd = false;
g_Address = NULL;
g_Reply = NULL;
g_ReplyHead = NULL;
g_ReplyTail = NULL;
g_ReplyNum = NULL;
g_SensorBarPower = NULL;
g_IPC_Status = UIPC_Status(NULL);
g_IPC_Config = UIPC_Config(NULL);
g_IPC_Control = UIPC_Control(NULL);
cmd_active = false;
ctrl = CtrlRegister();
ppc_msg =
arm_msg =
ppc_irq_flags =
ppc_irq_masks =
arm_irq_flags =
arm_irq_masks =
sensorbar_power =
g_ReplyHead =
g_ReplyTail =
g_ReplyNum = 0;
memset(g_ReplyFifo, 0, REPLY_FIFO_DEPTH);
ppc_irq_masks |= INT_CAUSE_IPC_BROADWAY;
}
void Reset()
{
INFO_LOG(WII_IPC, "Resetting ...");
Init();
WII_IPC_HLE_Interface::Reset();
}
void Shutdown()
@ -140,26 +164,26 @@ void Read32(u32& _rReturnValue, const u32 _Address)
{
switch(_Address & 0xFFFF)
{
case IPC_CONTROL_REGISTER:
_rReturnValue = g_IPC_Control.Hex;
INFO_LOG(WII_IPC, "IOP: Read32, IPC_CONTROL_REGISTER(0x04) = 0x%08x [R:%i A:%i E:%i]",
_rReturnValue, (_rReturnValue>>2)&1, (_rReturnValue>>1)&1, _rReturnValue&1);
case IPC_PPCCTRL:
_rReturnValue = ctrl.ppc();
INFO_LOG(WII_IPC, "r32 IPC_PPCCTRL %03x [R:%i A:%i E:%i]",
ctrl.ppc(), ctrl.Y1, ctrl.Y2, ctrl.X1);
// if ((REASON_REG & 0x14) == 0x14) CALL IPCReplayHanlder
// if ((REASON_REG & 0x22) != 0x22) Jumps to the end
// if ((REASON_REG & 0x14) == 0x14) CALL IPCReplayHandler
// if ((REASON_REG & 0x22) != 0x22) Jumps to the end
break;
case IPC_REPLY_REGISTER: // looks a little bit like a callback function
_rReturnValue = g_Reply;
INFO_LOG(WII_IPC, "IOP: Read32, IPC_REPLY_REGISTER(0x08) = 0x%08x ", _rReturnValue);
break;
case IPC_ARMMSG: // looks a little bit like a callback function
_rReturnValue = arm_msg;
INFO_LOG(WII_IPC, "r32 IPC_ARMMSG %08x ", _rReturnValue);
break;
case IPC_SENSOR_BAR_POWER_REGISTER:
_rReturnValue = g_SensorBarPower;
case GPIOB_OUT:
_rReturnValue = sensorbar_power;
break;
default:
_dbg_assert_msg_(WII_IPC, 0, "IOP: Read32 from 0x%08x", _Address);
_dbg_assert_msg_(WII_IPC, 0, "r32 from %08x", _Address);
break;
}
}
@ -168,117 +192,94 @@ void Write32(const u32 _Value, const u32 _Address)
{
switch(_Address & 0xFFFF)
{
// write only ??
case IPC_COMMAND_REGISTER: // __ios_Ipc2 ... a value from __responses is loaded
{
g_Address = _Value;
INFO_LOG(WII_IPC, "IOP: Write32, IPC_ADDRESS_REGISTER(0x00) = 0x%08x", g_Address);
}
case IPC_PPCMSG: // __ios_Ipc2 ... a value from __responses is loaded
{
ppc_msg = _Value;
INFO_LOG(WII_IPC, "IPC_PPCMSG = %08x", ppc_msg);
}
break;
case IPC_CONTROL_REGISTER:
{
INFO_LOG(WII_IPC, "IOP: Write32, IPC_CONTROL_REGISTER(0x04) = 0x%08x [R:%i A:%i E:%i] (old: 0x%08x) ",
_Value, (_Value>>2)&1, (_Value>>1)&1, _Value&1, g_IPC_Control.Hex);
UIPC_Control TempControl(_Value);
_dbg_assert_msg_(WII_IPC, TempControl.pad == 0, "IOP: Write to UIPC_Control.pad", _Address);
if (TempControl.AckReady) { g_IPC_Control.AckReady = 0; }
if (TempControl.ReplyReady) { g_IPC_Control.ReplyReady = 0; }
// Ayuanx: What is this Relaunch bit used for ???
// I have done considerable amount of tests that show no use of it at all
// So I'm commenting this out
//
//if (TempControl.Relaunch) { g_IPC_Control.Relaunch = 0; }
g_IPC_Control.Relaunch = TempControl.Relaunch;
g_IPC_Control.unk5 = TempControl.unk5;
g_IPC_Control.unk6 = TempControl.unk6;
g_IPC_Control.pad = TempControl.pad;
if (TempControl.ExecuteCmd)
{
g_ExeCmd = true;
}
}
case IPC_PPCCTRL:
{
ctrl.ppc(_Value);
INFO_LOG(WII_IPC, "w32 %08x IPC_PPCCTRL = %03x [R:%i A:%i E:%i]",
_Value, ctrl.ppc(), ctrl.Y1, ctrl.Y2, ctrl.X1);
if (ctrl.X1) // seems like we should just be able to use X1 directly, but it doesn't work...why?!
cmd_active = true;
}
break;
case IPC_STATUS_REGISTER: // ACR REGISTER IT IS CALLED IN DEBUG
{
UIPC_Status NewStatus(_Value);
if (NewStatus.INTERRUPT) g_IPC_Status.INTERRUPT = 0; // clear interrupt
INFO_LOG(WII_IPC, "IOP: Write32, IPC_STATUS_REGISTER(0x30) = 0x%08x", _Value);
}
break;
case IPC_CONFIG_REGISTER: // __OSInterruptInit (0x40000000)
{
INFO_LOG(WII_IPC, "IOP: Write32, IPC_CONFIG_REGISTER(0x33) = 0x%08x", _Value);
g_IPC_Config.Hex = _Value;
if (_Value&0x40000000)
{
INFO_LOG(WII_IPC, "Reset triggered, Resetting ...");
Reset();
WII_IPC_HLE_Interface::Reset();
}
}
case PPC_IRQFLAG: // ACR REGISTER IT IS CALLED IN DEBUG
{
ppc_irq_flags &= ~_Value;
INFO_LOG(WII_IPC, "w32 PPC_IRQFLAG %08x (%08x)", _Value, ppc_irq_flags);
}
break;
case IPC_SENSOR_BAR_POWER_REGISTER:
g_SensorBarPower = _Value;
case PPC_IRQMASK: // __OSInterruptInit (0x40000000)
{
ppc_irq_masks = _Value;
if (ppc_irq_masks & INT_CAUSE_IPC_BROADWAY) // wtf?
Reset();
INFO_LOG(WII_IPC, "w32 PPC_IRQMASK %08x", ppc_irq_masks);
}
break;
case GPIOB_OUT:
sensorbar_power = _Value;
break;
default:
{
_dbg_assert_msg_(WII_IPC, 0, "IOP: Write32 to 0x%08x", _Address);
}
_dbg_assert_msg_(WII_IPC, 0, "w32 %08x @ %08x", _Value, _Address);
break;
}
// update the interrupts
UpdateInterrupts();
}
void UpdateInterrupts()
{
if ((ctrl.Y1 & ctrl.IY1) || (ctrl.Y2 & ctrl.IY2))
{
ppc_irq_flags |= INT_CAUSE_IPC_BROADWAY;
}
if ((ctrl.X1 & ctrl.IX1) || (ctrl.X2 & ctrl.IX2))
{
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
ProcessorInterface::SetInterrupt(ProcessorInterface::INT_CAUSE_WII_IPC, !!(ppc_irq_flags & ppc_irq_masks));
}
// The rest is for IOS HLE
bool IsReady()
{
return ((ctrl.Y1 == 0) && (ctrl.Y2 == 0) && ((ppc_irq_flags & INT_CAUSE_IPC_BROADWAY) == 0));
}
u32 GetAddress()
{
return ((g_ExeCmd) ? g_Address : NULL);
return (cmd_active ? ppc_msg : 0);
}
void GenerateAck()
{
g_ExeCmd = false;
g_IPC_Control.AckReady = 1;
UpdateInterrupts();
cmd_active = false;
ctrl.Y2 = 1;
UpdateInterrupts();
}
void GenerateReply(u32 _Address)
{
g_Reply = _Address;
g_IPC_Control.ReplyReady = 1;
arm_msg = _Address;
ctrl.Y1 = 1;
UpdateInterrupts();
}
void EnqReply(u32 _Address)
{
/*
// AyuanX: Replies are stored in a FIFO (depth 2), like ping-pong, and 2 is fairly enough
// Simple structure of fixed length will do good for DoState
//
if (g_ReplyHead == NULL)
{
g_ReplyHead = g_ReplyTail;
g_ReplyTail = _Address;
}
else
{
ERROR_LOG(WII_IPC, "Reply FIFO is full, something must be wrong!");
PanicAlert("WII_IPC: Reply FIFO is full, something must be wrong!");
}
*/
if (g_ReplyNum < REPLY_FIFO_DEPTH)
{
g_ReplyFifo[g_ReplyTail++] = _Address;
@ -294,16 +295,6 @@ void EnqReply(u32 _Address)
u32 DeqReply()
{
/*
u32 _Address = (g_ReplyHead) ? g_ReplyHead : g_ReplyTail;
if (g_ReplyHead)
g_ReplyHead = NULL;
else
g_ReplyTail = NULL;
return _Address;
*/
u32 _Address;
if (g_ReplyNum)
@ -320,30 +311,4 @@ u32 DeqReply()
return _Address;
}
void UpdateInterrupts()
{
if ((g_IPC_Control.AckReady == 1) ||
(g_IPC_Control.ReplyReady == 1))
{
g_IPC_Status.INTERRUPT = 1;
}
// check if we have to generate an interrupt
if (g_IPC_Config.INT_MASK & g_IPC_Status.INTERRUPT)
{
ProcessorInterface::SetInterrupt(ProcessorInterface::INT_CAUSE_WII_IPC, true);
}
else
{
ProcessorInterface::SetInterrupt(ProcessorInterface::INT_CAUSE_WII_IPC, false);
}
}
bool IsReady()
{
return ((g_IPC_Control.ReplyReady == 0) && (g_IPC_Control.AckReady == 0) && (g_IPC_Status.INTERRUPT == 0));
}
} // end of namespace IPC

View File

@ -23,7 +23,28 @@ class PointerWrap;
namespace WII_IPCInterface
{
#define REPLY_FIFO_DEPTH (8)
enum StarletInterruptCause
{
INT_CAUSE_TIMER = 0x1,
INT_CAUSE_NAND = 0x2,
INT_CAUSE_AES = 0x4,
INT_CAUSE_SHA1 = 0x8,
INT_CAUSE_EHCI = 0x10,
INT_CAUSE_OHCI0 = 0x20,
INT_CAUSE_OHCI1 = 0x40,
INT_CAUSE_SD = 0x80,
INT_CAUSE_WIFI = 0x100,
INT_CAUSE_GPIO_BROADWAY = 0x400,
INT_CAUSE_GPIO_STARLET = 0x800,
INT_CAUSE_RST_BUTTON = 0x40000,
INT_CAUSE_IPC_BROADWAY = 0x40000000,
INT_CAUSE_IPC_STARLET = 0x80000000
};
#define REPLY_FIFO_DEPTH (unsigned)8
#define REPLY_FIFO_MASK (REPLY_FIFO_DEPTH - 1)
void Init();
@ -44,8 +65,6 @@ u32 DeqReply();
void UpdateInterrupts();
bool IsReady();
} // end of namespace AudioInterface
}
#endif

View File

@ -454,7 +454,7 @@ void Update()
// if we have a reply to send
u32 _Reply = WII_IPCInterface::DeqReply();
if (_Reply != 0)
if (_Reply)
{
WII_IPCInterface::GenerateReply(_Reply);
INFO_LOG(WII_IPC_HLE, "<<-- Reply to Command Address: 0x%08x", _Reply);
@ -463,32 +463,17 @@ void Update()
// If there is a a new command
u32 _Address = WII_IPCInterface::GetAddress();
if (_Address != 0)
if (_Address)
{
WII_IPCInterface::GenerateAck();
INFO_LOG(WII_IPC_HLE, "||-- Acknowledge Command Address: 0x%08x", _Address);
ExecuteCommand(_Address);
/*
// AyuanX: Since current HLE time slot is empty, we can piggyback a reply
// Besides, this trick makes a Ping-Pong Reply FIFO never get full
// I don't know whether original hardware supports this feature or not
// but it works here and we gain 1/3 extra bandwidth
//
u32 _Reply = WII_IPCInterface::DeqReply();
if (_Reply != NULL)
{
WII_IPCInterface::GenerateReply(_Reply);
INFO_LOG(WII_IPC_HLE, "<<-- Reply to Command Address: 0x%08x", _Reply);
}
*/
#if MAX_LOG_LEVEL >= DEBUG_LEVEL
Debugger::PrintCallstack(LogTypes::WII_IPC_HLE, LogTypes::LDEBUG);
#endif
return;
}
}
void UpdateDevices()

View File

@ -489,7 +489,7 @@ u32 CWII_IPC_HLE_Device_usb_oh1_57e_305::Update()
}
// Link channels when connected
if (m_ACLBuffer.m_address && !m_LastCmd && !WII_IPCInterface::GetAddress())
if (m_ACLBuffer.m_address && !m_LastCmd)
{
for (size_t i = 0; i < m_WiiMotes.size(); i++)
{
@ -519,7 +519,7 @@ u32 CWII_IPC_HLE_Device_usb_oh1_57e_305::Update()
// or CPU will disconnect WiiMote automatically
// but don't send too many or it will jam the bus and cost extra CPU time
// TODO: Figure out the correct frequency to send this thing
if (m_HCIBuffer.m_address && !WII_IPCInterface::GetAddress())
if (m_HCIBuffer.m_address)
{
if (++m_FreqDividerSync > 500)
m_FreqDividerSync = 0;