1 |
/* PCSX2 - PS2 Emulator for PCs |
2 |
* Copyright (C) 2002-2010 PCSX2 Dev Team |
3 |
* |
4 |
* PCSX2 is free software: you can redistribute it and/or modify it under the terms |
5 |
* of the GNU Lesser General Public License as published by the Free Software Found- |
6 |
* ation, either version 3 of the License, or (at your option) any later version. |
7 |
* |
8 |
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; |
9 |
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR |
10 |
* PURPOSE. See the GNU General Public License for more details. |
11 |
* |
12 |
* You should have received a copy of the GNU General Public License along with PCSX2. |
13 |
* If not, see <http://www.gnu.org/licenses/>. |
14 |
*/ |
15 |
|
16 |
|
17 |
#include "PrecompiledHeader.h" |
18 |
#include "Common.h" |
19 |
#include "Hardware.h" |
20 |
|
21 |
#include "IPU/IPUdma.h" |
22 |
#include "ps2/HwInternal.h" |
23 |
|
24 |
bool DMACh::transfer(const char *s, tDMA_TAG* ptag) |
25 |
{ |
26 |
if (ptag == NULL) // Is ptag empty? |
27 |
{ |
28 |
throwBusError(s); |
29 |
return false; |
30 |
} |
31 |
chcrTransfer(ptag); |
32 |
|
33 |
qwcTransfer(ptag); |
34 |
return true; |
35 |
} |
36 |
|
37 |
void DMACh::unsafeTransfer(tDMA_TAG* ptag) |
38 |
{ |
39 |
chcrTransfer(ptag); |
40 |
qwcTransfer(ptag); |
41 |
} |
42 |
|
43 |
tDMA_TAG *DMACh::getAddr(u32 addr, u32 num, bool write) |
44 |
{ |
45 |
tDMA_TAG *ptr = dmaGetAddr(addr, write); |
46 |
if (ptr == NULL) |
47 |
{ |
48 |
throwBusError("dmaGetAddr"); |
49 |
setDmacStat(num); |
50 |
chcr.STR = false; |
51 |
} |
52 |
|
53 |
return ptr; |
54 |
} |
55 |
|
56 |
tDMA_TAG *DMACh::DMAtransfer(u32 addr, u32 num) |
57 |
{ |
58 |
tDMA_TAG *tag = getAddr(addr, num, false); |
59 |
|
60 |
if (tag == NULL) return NULL; |
61 |
|
62 |
chcrTransfer(tag); |
63 |
qwcTransfer(tag); |
64 |
return tag; |
65 |
} |
66 |
|
67 |
tDMA_TAG DMACh::dma_tag() |
68 |
{ |
69 |
return chcr.tag(); |
70 |
} |
71 |
|
72 |
wxString DMACh::cmq_to_str() const |
73 |
{ |
74 |
return wxsFormat(L"chcr = %lx, madr = %lx, qwc = %lx", chcr._u32, madr, qwc); |
75 |
} |
76 |
|
77 |
wxString DMACh::cmqt_to_str() const |
78 |
{ |
79 |
return wxsFormat(L"chcr = %lx, madr = %lx, qwc = %lx, tadr = %1x", chcr._u32, madr, qwc, tadr); |
80 |
} |
81 |
|
82 |
__fi void throwBusError(const char *s) |
83 |
{ |
84 |
Console.Error("%s BUSERR", s); |
85 |
dmacRegs.stat.BEIS = true; |
86 |
} |
87 |
|
88 |
__fi void setDmacStat(u32 num) |
89 |
{ |
90 |
dmacRegs.stat.set_flags(1 << num); |
91 |
} |
92 |
|
93 |
// Note: Dma addresses are guaranteed to be aligned to 16 bytes (128 bits) |
94 |
__fi tDMA_TAG *SPRdmaGetAddr(u32 addr, bool write) |
95 |
{ |
96 |
// if (addr & 0xf) { DMA_LOG("*PCSX2*: DMA address not 128bit aligned: %8.8x", addr); } |
97 |
|
98 |
//For some reason Getaway references SPR Memory from itself using SPR0, oh well, let it i guess... |
99 |
if((addr & 0x70000000) == 0x70000000) |
100 |
{ |
101 |
return (tDMA_TAG*)&eeMem->Scratch[addr & 0x3ff0]; |
102 |
} |
103 |
|
104 |
// FIXME: Why??? DMA uses physical addresses |
105 |
addr &= 0x1ffffff0; |
106 |
|
107 |
if (addr < Ps2MemSize::Base) |
108 |
{ |
109 |
return (tDMA_TAG*)&eeMem->Main[addr]; |
110 |
} |
111 |
else if (addr < 0x10000000) |
112 |
{ |
113 |
return (tDMA_TAG*)(write ? eeMem->ZeroWrite : eeMem->ZeroRead); |
114 |
} |
115 |
else if ((addr >= 0x11004000) && (addr < 0x11010000)) |
116 |
{ |
117 |
//Access for VU Memory |
118 |
return (tDMA_TAG*)vtlb_GetPhyPtr(addr & 0x1FFFFFF0); |
119 |
} |
120 |
else |
121 |
{ |
122 |
Console.Error( "*PCSX2*: DMA error: %8.8x", addr); |
123 |
return NULL; |
124 |
} |
125 |
} |
126 |
|
127 |
// Note: Dma addresses are guaranteed to be aligned to 16 bytes (128 bits) |
128 |
__ri tDMA_TAG *dmaGetAddr(u32 addr, bool write) |
129 |
{ |
130 |
// if (addr & 0xf) { DMA_LOG("*PCSX2*: DMA address not 128bit aligned: %8.8x", addr); } |
131 |
if (DMA_TAG(addr).SPR) return (tDMA_TAG*)&eeMem->Scratch[addr & 0x3ff0]; |
132 |
|
133 |
// FIXME: Why??? DMA uses physical addresses |
134 |
addr &= 0x1ffffff0; |
135 |
|
136 |
if (addr < Ps2MemSize::Base) |
137 |
{ |
138 |
return (tDMA_TAG*)&eeMem->Main[addr]; |
139 |
} |
140 |
else if (addr < 0x10000000) |
141 |
{ |
142 |
return (tDMA_TAG*)(write ? eeMem->ZeroWrite : eeMem->ZeroRead); |
143 |
} |
144 |
else if (addr < 0x10004000) |
145 |
{ |
146 |
// Secret scratchpad address for DMA = end of maximum main memory? |
147 |
//Console.Warning("Writing to the scratchpad without the SPR flag set!"); |
148 |
return (tDMA_TAG*)&eeMem->Scratch[addr & 0x3ff0]; |
149 |
} |
150 |
else |
151 |
{ |
152 |
Console.Error( "*PCSX2*: DMA error: %8.8x", addr); |
153 |
return NULL; |
154 |
} |
155 |
} |
156 |
|
157 |
|
158 |
// Returns true if the DMA is enabled and executed successfully. Returns false if execution |
159 |
// was blocked (DMAE or master DMA enabler). |
160 |
static bool QuickDmaExec( void (*func)(), u32 mem) |
161 |
{ |
162 |
bool ret = false; |
163 |
DMACh& reg = (DMACh&)psHu32(mem); |
164 |
|
165 |
if (reg.chcr.STR && dmacRegs.ctrl.DMAE && !psHu8(DMAC_ENABLER+2)) |
166 |
{ |
167 |
func(); |
168 |
ret = true; |
169 |
} |
170 |
|
171 |
return ret; |
172 |
} |
173 |
|
174 |
|
175 |
static tDMAC_QUEUE QueuedDMA(0); |
176 |
static u32 oldvalue = 0; |
177 |
|
178 |
static void StartQueuedDMA() |
179 |
{ |
180 |
if (QueuedDMA.VIF0) { DMA_LOG("Resuming DMA for VIF0"); QueuedDMA.VIF0 = !QuickDmaExec(dmaVIF0, D0_CHCR); } |
181 |
if (QueuedDMA.VIF1) { DMA_LOG("Resuming DMA for VIF1"); QueuedDMA.VIF1 = !QuickDmaExec(dmaVIF1, D1_CHCR); } |
182 |
if (QueuedDMA.GIF ) { DMA_LOG("Resuming DMA for GIF" ); QueuedDMA.GIF = !QuickDmaExec(dmaGIF , D2_CHCR); } |
183 |
if (QueuedDMA.IPU0) { DMA_LOG("Resuming DMA for IPU0"); QueuedDMA.IPU0 = !QuickDmaExec(dmaIPU0, D3_CHCR); } |
184 |
if (QueuedDMA.IPU1) { DMA_LOG("Resuming DMA for IPU1"); QueuedDMA.IPU1 = !QuickDmaExec(dmaIPU1, D4_CHCR); } |
185 |
if (QueuedDMA.SIF0) { DMA_LOG("Resuming DMA for SIF0"); QueuedDMA.SIF0 = !QuickDmaExec(dmaSIF0, D5_CHCR); } |
186 |
if (QueuedDMA.SIF1) { DMA_LOG("Resuming DMA for SIF1"); QueuedDMA.SIF1 = !QuickDmaExec(dmaSIF1, D6_CHCR); } |
187 |
if (QueuedDMA.SIF2) { DMA_LOG("Resuming DMA for SIF2"); QueuedDMA.SIF2 = !QuickDmaExec(dmaSIF2, D7_CHCR); } |
188 |
if (QueuedDMA.SPR0) { DMA_LOG("Resuming DMA for SPR0"); QueuedDMA.SPR0 = !QuickDmaExec(dmaSPR0, D8_CHCR); } |
189 |
if (QueuedDMA.SPR1) { DMA_LOG("Resuming DMA for SPR1"); QueuedDMA.SPR1 = !QuickDmaExec(dmaSPR1, D9_CHCR); } |
190 |
} |
191 |
|
192 |
static __ri void DmaExec( void (*func)(), u32 mem, u32 value ) |
193 |
{ |
194 |
DMACh& reg = (DMACh&)psHu32(mem); |
195 |
tDMA_CHCR chcr(value); |
196 |
|
197 |
//It's invalid for the hardware to write a DMA while it is active, not without Suspending the DMAC |
198 |
if (reg.chcr.STR) |
199 |
{ |
200 |
const uint channel = ChannelNumber(mem); |
201 |
|
202 |
if(psHu8(DMAC_ENABLER+2) == 1) //DMA is suspended so we can allow writes to anything |
203 |
{ |
204 |
//If it stops the DMA, we need to clear any pending interrupts so the DMA doesnt continue. |
205 |
if(chcr.STR == 0) |
206 |
{ |
207 |
//DevCon.Warning(L"32bit %s DMA Stopped on Suspend", ChcrName(mem)); |
208 |
if(channel == 1) |
209 |
{ |
210 |
cpuClearInt( 10 ); |
211 |
QueuedDMA._u16 &= ~(1 << 10); //Clear any queued DMA requests for this channel |
212 |
} |
213 |
else if(channel == 2) |
214 |
{ |
215 |
cpuClearInt( 11 ); |
216 |
QueuedDMA._u16 &= ~(1 << 11); //Clear any queued DMA requests for this channel |
217 |
} |
218 |
|
219 |
cpuClearInt( channel ); |
220 |
QueuedDMA._u16 &= ~(1 << channel); //Clear any queued DMA requests for this channel |
221 |
} |
222 |
//Sanity Check for possible future bug fix0rs ;p |
223 |
//Spams on Persona 4 opening. |
224 |
//if(reg.chcr.TAG != chcr.TAG) DevCon.Warning(L"32bit CHCR Tag on %s changed to %x from %x QWC = %x Channel Active", ChcrName(mem), chcr.TAG, reg.chcr.TAG, reg.qwc); |
225 |
//Here we update the LOWER CHCR, if a chain is stopped half way through, it can be manipulated in to a different mode |
226 |
//But we need to preserve the existing tag for now |
227 |
reg.chcr.set((reg.chcr.TAG << 16) | chcr.lower()); |
228 |
return; |
229 |
} |
230 |
else //Else the DMA is running (Not Suspended), so we cant touch it! |
231 |
{ |
232 |
//As the manual states "Fields other than STR can only be written to when the DMA is stopped" |
233 |
//Also "The DMA may not stop properly just by writing 0 to STR" |
234 |
//So the presumption is that STR can be written to (ala force stop the DMA) but nothing else |
235 |
|
236 |
if(chcr.STR == 0) |
237 |
{ |
238 |
//DevCon.Warning(L"32bit Force Stopping %s (Current CHCR %x) while DMA active", ChcrName(mem), reg.chcr._u32, chcr._u32); |
239 |
reg.chcr.STR = 0; |
240 |
//We need to clear any existing DMA loops that are in progress else they will continue! |
241 |
|
242 |
if(channel == 1) |
243 |
{ |
244 |
cpuClearInt( 10 ); |
245 |
QueuedDMA._u16 &= ~(1 << 10); //Clear any queued DMA requests for this channel |
246 |
} |
247 |
else if(channel == 2) |
248 |
{ |
249 |
cpuClearInt( 11 ); |
250 |
QueuedDMA._u16 &= ~(1 << 11); //Clear any queued DMA requests for this channel |
251 |
} |
252 |
|
253 |
cpuClearInt( channel ); |
254 |
QueuedDMA._u16 &= ~(1 << channel); //Clear any queued DMA requests for this channel |
255 |
} |
256 |
//else DevCon.Warning(L"32bit Attempted to change %s CHCR (Currently %x) with %x while DMA active, ignoring QWC = %x", ChcrName(mem), reg.chcr._u32, chcr._u32, reg.qwc); |
257 |
return; |
258 |
} |
259 |
|
260 |
} |
261 |
|
262 |
//if(reg.chcr.TAG != chcr.TAG && chcr.MOD == CHAIN_MODE) DevCon.Warning(L"32bit CHCR Tag on %s changed to %x from %x QWC = %x Channel Not Active", ChcrName(mem), chcr.TAG, reg.chcr.TAG, reg.qwc); |
263 |
|
264 |
reg.chcr.set(value); |
265 |
|
266 |
if (reg.chcr.STR && dmacRegs.ctrl.DMAE && !psHu8(DMAC_ENABLER+2)) |
267 |
{ |
268 |
func(); |
269 |
} |
270 |
else if(reg.chcr.STR) |
271 |
{ |
272 |
//DevCon.Warning(L"32bit %s DMA Start while DMAC Disabled\n", ChcrName(mem)); |
273 |
QueuedDMA._u16 |= (1 << ChannelNumber(mem)); //Queue the DMA up to be started then the DMA's are Enabled and or the Suspend is lifted |
274 |
} //else QueuedDMA._u16 &~= (1 << ChannelNumber(mem)); // |
275 |
} |
276 |
|
277 |
template< uint page > |
278 |
__fi u32 dmacRead32( u32 mem ) |
279 |
{ |
280 |
// Fixme: OPH hack. Toggle the flag on each GIF_STAT access. (rama) |
281 |
if (IsPageFor(mem) && (mem == GIF_STAT) && CHECK_OPHFLAGHACK) |
282 |
{ |
283 |
gifRegs.stat.OPH = !gifRegs.stat.OPH; |
284 |
} |
285 |
|
286 |
return psHu32(mem); |
287 |
} |
288 |
|
289 |
// Returns TRUE if the caller should do writeback of the register to eeHw; false if the |
290 |
// register has no writeback, or if the writeback is handled internally. |
291 |
template< uint page > |
292 |
__fi bool dmacWrite32( u32 mem, mem32_t& value ) |
293 |
{ |
294 |
iswitch(mem) { |
295 |
icase(D0_CHCR) // dma0 - vif0 |
296 |
{ |
297 |
DMA_LOG("VIF0dma EXECUTE, value=0x%x", value); |
298 |
DmaExec(dmaVIF0, mem, value); |
299 |
return false; |
300 |
} |
301 |
|
302 |
icase(D1_CHCR) // dma1 - vif1 - chcr |
303 |
{ |
304 |
DMA_LOG("VIF1dma EXECUTE, value=0x%x", value); |
305 |
DmaExec(dmaVIF1, mem, value); |
306 |
return false; |
307 |
} |
308 |
|
309 |
icase(D2_CHCR) // dma2 - gif |
310 |
{ |
311 |
DMA_LOG("GIFdma EXECUTE, value=0x%x", value); |
312 |
DmaExec(dmaGIF, mem, value); |
313 |
return false; |
314 |
} |
315 |
|
316 |
icase(D3_CHCR) // dma3 - fromIPU |
317 |
{ |
318 |
DMA_LOG("IPU0dma EXECUTE, value=0x%x\n", value); |
319 |
DmaExec(dmaIPU0, mem, value); |
320 |
return false; |
321 |
} |
322 |
|
323 |
icase(D4_CHCR) // dma4 - toIPU |
324 |
{ |
325 |
DMA_LOG("IPU1dma EXECUTE, value=0x%x\n", value); |
326 |
DmaExec(dmaIPU1, mem, value); |
327 |
return false; |
328 |
} |
329 |
|
330 |
icase(D5_CHCR) // dma5 - sif0 |
331 |
{ |
332 |
DMA_LOG("SIF0dma EXECUTE, value=0x%x", value); |
333 |
DmaExec(dmaSIF0, mem, value); |
334 |
return false; |
335 |
} |
336 |
|
337 |
icase(D6_CHCR) // dma6 - sif1 |
338 |
{ |
339 |
DMA_LOG("SIF1dma EXECUTE, value=0x%x", value); |
340 |
DmaExec(dmaSIF1, mem, value); |
341 |
return false; |
342 |
} |
343 |
|
344 |
icase(D7_CHCR) // dma7 - sif2 |
345 |
{ |
346 |
DMA_LOG("SIF2dma EXECUTE, value=0x%x", value); |
347 |
DmaExec(dmaSIF2, mem, value); |
348 |
return false; |
349 |
} |
350 |
|
351 |
icase(D8_CHCR) // dma8 - fromSPR |
352 |
{ |
353 |
DMA_LOG("SPR0dma EXECUTE (fromSPR), value=0x%x", value); |
354 |
DmaExec(dmaSPR0, mem, value); |
355 |
return false; |
356 |
} |
357 |
|
358 |
icase(D9_CHCR) // dma9 - toSPR |
359 |
{ |
360 |
DMA_LOG("SPR1dma EXECUTE (toSPR), value=0x%x", value); |
361 |
DmaExec(dmaSPR1, mem, value); |
362 |
return false; |
363 |
} |
364 |
|
365 |
icase(DMAC_CTRL) |
366 |
{ |
367 |
u32 oldvalue = psHu32(mem); |
368 |
|
369 |
HW_LOG("DMAC_CTRL Write 32bit %x", value); |
370 |
|
371 |
psHu32(mem) = value; |
372 |
//Check for DMAS that were started while the DMAC was disabled |
373 |
if (((oldvalue & 0x1) == 0) && ((value & 0x1) == 1)) |
374 |
{ |
375 |
if (!QueuedDMA.empty()) StartQueuedDMA(); |
376 |
} |
377 |
if ((oldvalue & 0x30) != (value & 0x30)) |
378 |
{ |
379 |
DevCon.Warning("32bit Stall Source Changed to %x", (value & 0x30) >> 4); |
380 |
} |
381 |
if ((oldvalue & 0xC0) != (value & 0xC0)) |
382 |
{ |
383 |
DevCon.Warning("32bit Stall Destination Changed to %x", (value & 0xC0) >> 4); |
384 |
} |
385 |
return false; |
386 |
} |
387 |
|
388 |
icase(DMAC_STAT) |
389 |
{ |
390 |
HW_LOG("DMAC_STAT Write 32bit %x", value); |
391 |
|
392 |
// lower 16 bits: clear on 1 |
393 |
// upper 16 bits: reverse on 1 |
394 |
|
395 |
psHu16(0xe010) &= ~(value & 0xffff); |
396 |
psHu16(0xe012) ^= (u16)(value >> 16); |
397 |
|
398 |
cpuTestDMACInts(); |
399 |
return false; |
400 |
} |
401 |
|
402 |
icase(DMAC_ENABLEW) |
403 |
{ |
404 |
HW_LOG("DMAC_ENABLEW Write 32bit %lx", value); |
405 |
oldvalue = psHu8(DMAC_ENABLEW + 2); |
406 |
psHu32(DMAC_ENABLEW) = value; |
407 |
psHu32(DMAC_ENABLER) = value; |
408 |
if (((oldvalue & 0x1) == 1) && (((value >> 16) & 0x1) == 0)) |
409 |
{ |
410 |
if (!QueuedDMA.empty()) StartQueuedDMA(); |
411 |
} |
412 |
return false; |
413 |
} |
414 |
} |
415 |
|
416 |
// fall-through: use the default writeback provided by caller. |
417 |
return true; |
418 |
} |
419 |
|
420 |
template u32 dmacRead32<0x03>( u32 mem ); |
421 |
|
422 |
template bool dmacWrite32<0x00>( u32 mem, mem32_t& value ); |
423 |
template bool dmacWrite32<0x01>( u32 mem, mem32_t& value ); |
424 |
template bool dmacWrite32<0x02>( u32 mem, mem32_t& value ); |
425 |
template bool dmacWrite32<0x03>( u32 mem, mem32_t& value ); |
426 |
template bool dmacWrite32<0x04>( u32 mem, mem32_t& value ); |
427 |
template bool dmacWrite32<0x05>( u32 mem, mem32_t& value ); |
428 |
template bool dmacWrite32<0x06>( u32 mem, mem32_t& value ); |
429 |
template bool dmacWrite32<0x07>( u32 mem, mem32_t& value ); |
430 |
template bool dmacWrite32<0x08>( u32 mem, mem32_t& value ); |
431 |
template bool dmacWrite32<0x09>( u32 mem, mem32_t& value ); |
432 |
template bool dmacWrite32<0x0a>( u32 mem, mem32_t& value ); |
433 |
template bool dmacWrite32<0x0b>( u32 mem, mem32_t& value ); |
434 |
template bool dmacWrite32<0x0c>( u32 mem, mem32_t& value ); |
435 |
template bool dmacWrite32<0x0d>( u32 mem, mem32_t& value ); |
436 |
template bool dmacWrite32<0x0e>( u32 mem, mem32_t& value ); |
437 |
template bool dmacWrite32<0x0f>( u32 mem, mem32_t& value ); |