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