1 // Copyright 2008 Dolphin Emulator Project
2 // Licensed under GPLv2+
3 // Refer to the license.txt file included.
4 
5 #include "Core/HW/ProcessorInterface.h"
6 
7 #include <cstdio>
8 #include <memory>
9 
10 #include "Common/ChunkFile.h"
11 #include "Common/CommonTypes.h"
12 #include "Core/Core.h"
13 #include "Core/CoreTiming.h"
14 #include "Core/HW/DVD/DVDInterface.h"
15 #include "Core/HW/MMIO.h"
16 #include "Core/HW/SystemTimers.h"
17 #include "Core/IOS/IOS.h"
18 #include "Core/IOS/STM/STM.h"
19 #include "Core/PowerPC/PowerPC.h"
20 
21 namespace ProcessorInterface
22 {
23 // STATE_TO_SAVE
24 u32 m_InterruptCause;
25 u32 m_InterruptMask;
26 // addresses for CPU fifo accesses
27 u32 Fifo_CPUBase;
28 u32 Fifo_CPUEnd;
29 u32 Fifo_CPUWritePointer;
30 
31 static u32 m_Fifo_Reset;
32 static u32 m_ResetCode;
33 static u32 m_FlipperRev;
34 static u32 m_Unknown;
35 
36 // ID and callback for scheduling reset button presses/releases
37 static CoreTiming::EventType* toggleResetButton;
38 static void ToggleResetButtonCallback(u64 userdata, s64 cyclesLate);
39 
40 static CoreTiming::EventType* iosNotifyResetButton;
41 static void IOSNotifyResetButtonCallback(u64 userdata, s64 cyclesLate);
42 
43 static CoreTiming::EventType* iosNotifyPowerButton;
44 static void IOSNotifyPowerButtonCallback(u64 userdata, s64 cyclesLate);
45 
46 // Let the PPC know that an external exception is set/cleared
47 void UpdateException();
48 
DoState(PointerWrap & p)49 void DoState(PointerWrap& p)
50 {
51   p.Do(m_InterruptMask);
52   p.Do(m_InterruptCause);
53   p.Do(Fifo_CPUBase);
54   p.Do(Fifo_CPUEnd);
55   p.Do(Fifo_CPUWritePointer);
56   p.Do(m_Fifo_Reset);
57   p.Do(m_ResetCode);
58   p.Do(m_FlipperRev);
59   p.Do(m_Unknown);
60 }
61 
Init()62 void Init()
63 {
64   m_InterruptMask = 0;
65   m_InterruptCause = 0;
66 
67   Fifo_CPUBase = 0;
68   Fifo_CPUEnd = 0;
69   Fifo_CPUWritePointer = 0;
70   /*
71   Previous Flipper IDs:
72   0x046500B0 = A
73   0x146500B1 = B
74   */
75   m_FlipperRev = 0x246500B1;  // revision C
76   m_Unknown = 0;
77 
78   m_ResetCode = 0;  // Cold reset
79   m_InterruptCause = INT_CAUSE_RST_BUTTON | INT_CAUSE_VI;
80 
81   toggleResetButton = CoreTiming::RegisterEvent("ToggleResetButton", ToggleResetButtonCallback);
82   iosNotifyResetButton =
83       CoreTiming::RegisterEvent("IOSNotifyResetButton", IOSNotifyResetButtonCallback);
84   iosNotifyPowerButton =
85       CoreTiming::RegisterEvent("IOSNotifyPowerButton", IOSNotifyPowerButtonCallback);
86 }
87 
RegisterMMIO(MMIO::Mapping * mmio,u32 base)88 void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
89 {
90   mmio->Register(base | PI_INTERRUPT_CAUSE, MMIO::DirectRead<u32>(&m_InterruptCause),
91                  MMIO::ComplexWrite<u32>([](u32, u32 val) {
92                    m_InterruptCause &= ~val;
93                    UpdateException();
94                  }));
95 
96   mmio->Register(base | PI_INTERRUPT_MASK, MMIO::DirectRead<u32>(&m_InterruptMask),
97                  MMIO::ComplexWrite<u32>([](u32, u32 val) {
98                    m_InterruptMask = val;
99                    UpdateException();
100                  }));
101 
102   mmio->Register(base | PI_FIFO_BASE, MMIO::DirectRead<u32>(&Fifo_CPUBase),
103                  MMIO::DirectWrite<u32>(&Fifo_CPUBase, 0xFFFFFFE0));
104 
105   mmio->Register(base | PI_FIFO_END, MMIO::DirectRead<u32>(&Fifo_CPUEnd),
106                  MMIO::DirectWrite<u32>(&Fifo_CPUEnd, 0xFFFFFFE0));
107 
108   mmio->Register(base | PI_FIFO_WPTR, MMIO::DirectRead<u32>(&Fifo_CPUWritePointer),
109                  MMIO::DirectWrite<u32>(&Fifo_CPUWritePointer, 0xFFFFFFE0));
110 
111   mmio->Register(base | PI_FIFO_RESET, MMIO::InvalidRead<u32>(),
112                  MMIO::ComplexWrite<u32>(
113                      [](u32, u32 val) { WARN_LOG(PROCESSORINTERFACE, "Fifo reset (%08x)", val); }));
114 
115   mmio->Register(base | PI_RESET_CODE, MMIO::ComplexRead<u32>([](u32) {
116                    DEBUG_LOG(PROCESSORINTERFACE, "Read PI_RESET_CODE: %08x", m_ResetCode);
117                    return m_ResetCode;
118                  }),
119                  MMIO::ComplexWrite<u32>([](u32, u32 val) {
120                    m_ResetCode = val;
121                    INFO_LOG(PROCESSORINTERFACE, "Wrote PI_RESET_CODE: %08x", m_ResetCode);
122                    if (!SConfig::GetInstance().bWii && ~m_ResetCode & 0x4)
123                    {
124                      DVDInterface::ResetDrive(true);
125                    }
126                  }));
127 
128   mmio->Register(base | PI_FLIPPER_REV, MMIO::DirectRead<u32>(&m_FlipperRev),
129                  MMIO::InvalidWrite<u32>());
130 
131   // 16 bit reads are based on 32 bit reads.
132   for (int i = 0; i < 0x1000; i += 4)
133   {
134     mmio->Register(base | i, MMIO::ReadToLarger<u16>(mmio, base | i, 16),
135                    MMIO::InvalidWrite<u16>());
136     mmio->Register(base | (i + 2), MMIO::ReadToLarger<u16>(mmio, base | i, 0),
137                    MMIO::InvalidWrite<u16>());
138   }
139 }
140 
UpdateException()141 void UpdateException()
142 {
143   if ((m_InterruptCause & m_InterruptMask) != 0)
144     PowerPC::ppcState.Exceptions |= EXCEPTION_EXTERNAL_INT;
145   else
146     PowerPC::ppcState.Exceptions &= ~EXCEPTION_EXTERNAL_INT;
147 }
148 
Debug_GetInterruptName(u32 cause_mask)149 static const char* Debug_GetInterruptName(u32 cause_mask)
150 {
151   switch (cause_mask)
152   {
153   case INT_CAUSE_PI:
154     return "INT_CAUSE_PI";
155   case INT_CAUSE_DI:
156     return "INT_CAUSE_DI";
157   case INT_CAUSE_RSW:
158     return "INT_CAUSE_RSW";
159   case INT_CAUSE_SI:
160     return "INT_CAUSE_SI";
161   case INT_CAUSE_EXI:
162     return "INT_CAUSE_EXI";
163   case INT_CAUSE_AI:
164     return "INT_CAUSE_AI";
165   case INT_CAUSE_DSP:
166     return "INT_CAUSE_DSP";
167   case INT_CAUSE_MEMORY:
168     return "INT_CAUSE_MEMORY";
169   case INT_CAUSE_VI:
170     return "INT_CAUSE_VI";
171   case INT_CAUSE_PE_TOKEN:
172     return "INT_CAUSE_PE_TOKEN";
173   case INT_CAUSE_PE_FINISH:
174     return "INT_CAUSE_PE_FINISH";
175   case INT_CAUSE_CP:
176     return "INT_CAUSE_CP";
177   case INT_CAUSE_DEBUG:
178     return "INT_CAUSE_DEBUG";
179   case INT_CAUSE_WII_IPC:
180     return "INT_CAUSE_WII_IPC";
181   case INT_CAUSE_HSP:
182     return "INT_CAUSE_HSP";
183   case INT_CAUSE_RST_BUTTON:
184     return "INT_CAUSE_RST_BUTTON";
185   default:
186     return "!!! ERROR-unknown Interrupt !!!";
187   }
188 }
189 
SetInterrupt(u32 cause_mask,bool set)190 void SetInterrupt(u32 cause_mask, bool set)
191 {
192   DEBUG_ASSERT_MSG(POWERPC, Core::IsCPUThread(), "SetInterrupt from wrong thread");
193 
194   if (set && !(m_InterruptCause & cause_mask))
195   {
196     DEBUG_LOG(PROCESSORINTERFACE, "Setting Interrupt %s (set)", Debug_GetInterruptName(cause_mask));
197   }
198 
199   if (!set && (m_InterruptCause & cause_mask))
200   {
201     DEBUG_LOG(PROCESSORINTERFACE, "Setting Interrupt %s (clear)",
202               Debug_GetInterruptName(cause_mask));
203   }
204 
205   if (set)
206     m_InterruptCause |= cause_mask;
207   else
208     m_InterruptCause &= ~cause_mask;  // is there any reason to have this possibility?
209   // F|RES: i think the hw devices reset the interrupt in the PI to 0
210   // if the interrupt cause is eliminated. that isn't done by software (afaik)
211   UpdateException();
212 }
213 
SetResetButton(bool set)214 static void SetResetButton(bool set)
215 {
216   SetInterrupt(INT_CAUSE_RST_BUTTON, !set);
217 }
218 
ToggleResetButtonCallback(u64 userdata,s64 cyclesLate)219 static void ToggleResetButtonCallback(u64 userdata, s64 cyclesLate)
220 {
221   SetResetButton(!!userdata);
222 }
223 
IOSNotifyResetButtonCallback(u64 userdata,s64 cyclesLate)224 static void IOSNotifyResetButtonCallback(u64 userdata, s64 cyclesLate)
225 {
226   const auto ios = IOS::HLE::GetIOS();
227   if (!ios)
228     return;
229 
230   auto stm = ios->GetDeviceByName("/dev/stm/eventhook");
231   if (stm)
232     std::static_pointer_cast<IOS::HLE::Device::STMEventHook>(stm)->ResetButton();
233 }
234 
IOSNotifyPowerButtonCallback(u64 userdata,s64 cyclesLate)235 static void IOSNotifyPowerButtonCallback(u64 userdata, s64 cyclesLate)
236 {
237   const auto ios = IOS::HLE::GetIOS();
238   if (!ios)
239     return;
240 
241   auto stm = ios->GetDeviceByName("/dev/stm/eventhook");
242   if (stm)
243     std::static_pointer_cast<IOS::HLE::Device::STMEventHook>(stm)->PowerButton();
244 }
245 
ResetButton_Tap()246 void ResetButton_Tap()
247 {
248   if (!Core::IsRunning())
249     return;
250   CoreTiming::ScheduleEvent(0, toggleResetButton, true, CoreTiming::FromThread::ANY);
251   CoreTiming::ScheduleEvent(0, iosNotifyResetButton, 0, CoreTiming::FromThread::ANY);
252   CoreTiming::ScheduleEvent(SystemTimers::GetTicksPerSecond() / 2, toggleResetButton, false,
253                             CoreTiming::FromThread::ANY);
254 }
255 
PowerButton_Tap()256 void PowerButton_Tap()
257 {
258   if (!Core::IsRunning())
259     return;
260   CoreTiming::ScheduleEvent(0, iosNotifyPowerButton, 0, CoreTiming::FromThread::ANY);
261 }
262 
263 }  // namespace ProcessorInterface
264