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 |
#include "PrecompiledHeader.h" |
17 |
#include "Common.h" |
18 |
|
19 |
#include "SPR.h" |
20 |
#include "VUmicro.h" |
21 |
|
22 |
extern void mfifoGIFtransfer(int); |
23 |
|
24 |
static bool spr0finished = false; |
25 |
static bool spr1finished = false; |
26 |
|
27 |
static u32 mfifotransferred = 0; |
28 |
|
29 |
void sprInit() |
30 |
{ |
31 |
} |
32 |
|
33 |
static void TestClearVUs(u32 madr, u32 size) |
34 |
{ |
35 |
if (madr >= 0x11000000) |
36 |
{ |
37 |
if (madr < 0x11004000) |
38 |
{ |
39 |
DbgCon.Warning("scratch pad clearing vu0"); |
40 |
CpuVU0->Clear(madr&0xfff, size); |
41 |
} |
42 |
else if (madr >= 0x11008000 && madr < 0x1100c000) |
43 |
{ |
44 |
DbgCon.Warning("scratch pad clearing vu1"); |
45 |
CpuVU1->Clear(madr&0x3fff, size); |
46 |
} |
47 |
} |
48 |
} |
49 |
|
50 |
int _SPR0chain() |
51 |
{ |
52 |
tDMA_TAG *pMem; |
53 |
|
54 |
if (spr0->qwc == 0) return 0; |
55 |
pMem = SPRdmaGetAddr(spr0->madr, true); |
56 |
if (pMem == NULL) return -1; |
57 |
|
58 |
switch (dmacRegs->ctrl.MFD) |
59 |
{ |
60 |
case MFD_VIF1: |
61 |
case MFD_GIF: |
62 |
if ((spr0->madr & ~dmacRegs->rbsr.RMSK) != dmacRegs->rbor.ADDR) |
63 |
Console.WriteLn("SPR MFIFO Write outside MFIFO area"); |
64 |
else |
65 |
mfifotransferred += spr0->qwc; |
66 |
|
67 |
hwMFIFOWrite(spr0->madr, &psSu8(spr0->sadr), spr0->qwc << 4); |
68 |
spr0->madr += spr0->qwc << 4; |
69 |
spr0->madr = dmacRegs->rbor.ADDR + (spr0->madr & dmacRegs->rbsr.RMSK); |
70 |
break; |
71 |
|
72 |
case NO_MFD: |
73 |
case MFD_RESERVED: |
74 |
memcpy_fast((u8*)pMem, &psSu8(spr0->sadr), spr0->qwc << 4); |
75 |
|
76 |
// clear VU mem also! |
77 |
TestClearVUs(spr0->madr, spr0->qwc << 2); // Wtf is going on here? AFAIK, only VIF should affect VU micromem (cottonvibes) |
78 |
spr0->madr += spr0->qwc << 4; |
79 |
break; |
80 |
} |
81 |
|
82 |
spr0->sadr += spr0->qwc << 4; |
83 |
|
84 |
return (spr0->qwc) * BIAS; // bus is 1/2 the ee speed |
85 |
} |
86 |
|
87 |
__forceinline void SPR0chain() |
88 |
{ |
89 |
_SPR0chain(); |
90 |
spr0->qwc = 0; |
91 |
} |
92 |
|
93 |
void _SPR0interleave() |
94 |
{ |
95 |
int qwc = spr0->qwc; |
96 |
int sqwc = dmacRegs->sqwc.SQWC; |
97 |
int tqwc = dmacRegs->sqwc.TQWC; |
98 |
tDMA_TAG *pMem; |
99 |
|
100 |
if (tqwc == 0) tqwc = qwc; |
101 |
//Console.WriteLn("dmaSPR0 interleave"); |
102 |
SPR_LOG("SPR0 interleave size=%d, tqwc=%d, sqwc=%d, addr=%lx sadr=%lx", |
103 |
spr0->qwc, tqwc, sqwc, spr0->madr, spr0->sadr); |
104 |
|
105 |
while (qwc > 0) |
106 |
{ |
107 |
spr0->qwc = std::min(tqwc, qwc); |
108 |
qwc -= spr0->qwc; |
109 |
pMem = SPRdmaGetAddr(spr0->madr, true); |
110 |
|
111 |
switch (dmacRegs->ctrl.MFD) |
112 |
{ |
113 |
case MFD_VIF1: |
114 |
case MFD_GIF: |
115 |
hwMFIFOWrite(spr0->madr, &psSu8(spr0->sadr), spr0->qwc << 4); |
116 |
mfifotransferred += spr0->qwc; |
117 |
break; |
118 |
|
119 |
case NO_MFD: |
120 |
case MFD_RESERVED: |
121 |
// clear VU mem also! |
122 |
TestClearVUs(spr0->madr, spr0->qwc << 2); |
123 |
memcpy_fast((u8*)pMem, &psSu8(spr0->sadr), spr0->qwc << 4); |
124 |
break; |
125 |
} |
126 |
spr0->sadr += spr0->qwc * 16; |
127 |
spr0->madr += (sqwc + spr0->qwc) * 16; |
128 |
} |
129 |
|
130 |
spr0->qwc = 0; |
131 |
spr0finished = true; |
132 |
} |
133 |
|
134 |
static __forceinline void _dmaSPR0() |
135 |
{ |
136 |
if (dmacRegs->ctrl.STS == STS_fromSPR) |
137 |
{ |
138 |
Console.WriteLn("SPR0 stall %d", dmacRegs->ctrl.STS); |
139 |
} |
140 |
|
141 |
// Transfer Dn_QWC from SPR to Dn_MADR |
142 |
switch(spr0->chcr.MOD) |
143 |
{ |
144 |
case NORMAL_MODE: |
145 |
{ |
146 |
SPR0chain(); |
147 |
spr0finished = true; |
148 |
return; |
149 |
} |
150 |
case CHAIN_MODE: |
151 |
{ |
152 |
tDMA_TAG *ptag; |
153 |
bool done = FALSE; |
154 |
|
155 |
if (spr0->qwc > 0) |
156 |
{ |
157 |
SPR0chain(); |
158 |
spr0finished = true; |
159 |
return; |
160 |
} |
161 |
// Destination Chain Mode |
162 |
ptag = (tDMA_TAG*)&psSu32(spr0->sadr); |
163 |
spr0->sadr += 16; |
164 |
|
165 |
spr0->unsafeTransfer(ptag); |
166 |
|
167 |
spr0->madr = ptag[1]._u32; //MADR = ADDR field + SPR |
168 |
|
169 |
SPR_LOG("spr0 dmaChain %8.8x_%8.8x size=%d, id=%d, addr=%lx spr=%lx", |
170 |
ptag[1]._u32, ptag[0]._u32, spr0->qwc, ptag->ID, spr0->madr, spr0->sadr); |
171 |
|
172 |
if (dmacRegs->ctrl.STS == STS_fromSPR) // STS == fromSPR |
173 |
{ |
174 |
Console.WriteLn("SPR stall control"); |
175 |
} |
176 |
|
177 |
switch (ptag->ID) |
178 |
{ |
179 |
case TAG_CNTS: // CNTS - Transfer QWC following the tag (Stall Control) |
180 |
if (dmacRegs->ctrl.STS == STS_fromSPR) dmacRegs->stadr.ADDR = spr0->madr + (spr0->qwc * 16); //Copy MADR to DMAC_STADR stall addr register |
181 |
break; |
182 |
|
183 |
case TAG_CNT: // CNT - Transfer QWC following the tag. |
184 |
done = false; |
185 |
break; |
186 |
|
187 |
case TAG_END: // End - Transfer QWC following the tag |
188 |
done = true; |
189 |
break; |
190 |
} |
191 |
|
192 |
SPR0chain(); |
193 |
|
194 |
if (spr0->chcr.TIE && ptag->IRQ) //Check TIE bit of CHCR and IRQ bit of tag |
195 |
{ |
196 |
//Console.WriteLn("SPR0 TIE"); |
197 |
done = true; |
198 |
} |
199 |
|
200 |
spr0finished = done; |
201 |
|
202 |
if (!done) |
203 |
{ |
204 |
ptag = (tDMA_TAG*)&psSu32(spr0->sadr); //Set memory pointer to SADR |
205 |
CPU_INT(DMAC_FROM_SPR, /*ptag[0].QWC / BIAS*/ 4 ); // the lower 16bits of the tag / BIAS); |
206 |
return; |
207 |
} |
208 |
SPR_LOG("spr0 dmaChain complete %8.8x_%8.8x size=%d, id=%d, addr=%lx spr=%lx", |
209 |
ptag[1]._u32, ptag[0]._u32, spr0->qwc, ptag->ID, spr0->madr); |
210 |
break; |
211 |
} |
212 |
//case INTERLEAVE_MODE: |
213 |
default: |
214 |
{ |
215 |
_SPR0interleave(); |
216 |
break; |
217 |
} |
218 |
} |
219 |
} |
220 |
|
221 |
void SPRFROMinterrupt() |
222 |
{ |
223 |
_dmaSPR0(); |
224 |
|
225 |
if(mfifotransferred != 0) |
226 |
{ |
227 |
switch (dmacRegs->ctrl.MFD) |
228 |
{ |
229 |
case MFD_VIF1: // Most common case. |
230 |
{ |
231 |
if ((spr0->madr & ~dmacRegs->rbsr.RMSK) != dmacRegs->rbor.ADDR) Console.WriteLn("VIF MFIFO Write outside MFIFO area"); |
232 |
spr0->madr = dmacRegs->rbor.ADDR + (spr0->madr & dmacRegs->rbsr.RMSK); |
233 |
//Console.WriteLn("mfifoVIF1transfer %x madr %x, tadr %x", vif1ch->chcr._u32, vif1ch->madr, vif1ch->tadr); |
234 |
mfifoVIF1transfer(mfifotransferred); |
235 |
mfifotransferred = 0; |
236 |
if (vif1ch->chcr.STR) return; |
237 |
break; |
238 |
} |
239 |
case MFD_GIF: |
240 |
{ |
241 |
if ((spr0->madr & ~dmacRegs->rbsr.RMSK) != dmacRegs->rbor.ADDR) Console.WriteLn("GIF MFIFO Write outside MFIFO area"); |
242 |
spr0->madr = dmacRegs->rbor.ADDR + (spr0->madr & dmacRegs->rbsr.RMSK); |
243 |
//Console.WriteLn("mfifoGIFtransfer %x madr %x, tadr %x", gif->chcr._u32, gif->madr, gif->tadr); |
244 |
mfifoGIFtransfer(mfifotransferred); |
245 |
mfifotransferred = 0; |
246 |
if (gif->chcr.STR) return; |
247 |
break; |
248 |
} |
249 |
default: |
250 |
break; |
251 |
} |
252 |
} |
253 |
if (!spr0finished) return; |
254 |
|
255 |
|
256 |
spr0->chcr.STR = false; |
257 |
hwDmacIrq(DMAC_FROM_SPR); |
258 |
} |
259 |
|
260 |
void dmaSPR0() // fromSPR |
261 |
{ |
262 |
SPR_LOG("dmaSPR0 chcr = %lx, madr = %lx, qwc = %lx, sadr = %lx", |
263 |
spr0->chcr._u32, spr0->madr, spr0->qwc, spr0->sadr); |
264 |
|
265 |
if ((spr0->chcr.MOD == CHAIN_MODE) && spr0->qwc == 0) |
266 |
{ |
267 |
tDMA_TAG *ptag; |
268 |
ptag = (tDMA_TAG*)&psSu32(spr0->sadr); //Set memory pointer to SADR |
269 |
CPU_INT(DMAC_FROM_SPR, /*ptag[0].QWC / BIAS*/ 4 ); |
270 |
return; |
271 |
} |
272 |
if(spr0->chcr.MOD == CHAIN_MODE && spr0->qwc > 0) DevCon.Warning(L"SPR0 QWC on Chain " + spr0->chcr.desc()); |
273 |
// COMPLETE HACK!!! For now at least.. FFX Videos dont rely on interrupts or reading DMA values |
274 |
// It merely assumes that the last one has finished then starts another one (broke with the DMA fix) |
275 |
// This "shouldn't" cause any problems as SPR is generally faster than the other DMAS anyway. (Refraction) |
276 |
CPU_INT(DMAC_FROM_SPR, /*spr0->qwc / BIAS*/ 4 ); |
277 |
} |
278 |
|
279 |
__forceinline static void SPR1transfer(u32 *data, int size) |
280 |
{ |
281 |
memcpy_fast(&psSu8(spr1->sadr), (u8*)data, size << 2); |
282 |
|
283 |
spr1->sadr += size << 2; |
284 |
} |
285 |
|
286 |
int _SPR1chain() |
287 |
{ |
288 |
tDMA_TAG *pMem; |
289 |
|
290 |
if (spr1->qwc == 0) return 0; |
291 |
|
292 |
pMem = SPRdmaGetAddr(spr1->madr, false); |
293 |
if (pMem == NULL) return -1; |
294 |
|
295 |
SPR1transfer((u32*)pMem, spr1->qwc << 2); |
296 |
spr1->madr += spr1->qwc << 4; |
297 |
|
298 |
return (spr1->qwc) * BIAS; |
299 |
} |
300 |
|
301 |
__forceinline void SPR1chain() |
302 |
{ |
303 |
_SPR1chain(); |
304 |
spr1->qwc = 0; |
305 |
} |
306 |
|
307 |
void _SPR1interleave() |
308 |
{ |
309 |
int qwc = spr1->qwc; |
310 |
int sqwc = dmacRegs->sqwc.SQWC; |
311 |
int tqwc = dmacRegs->sqwc.TQWC; |
312 |
tDMA_TAG *pMem; |
313 |
|
314 |
if (tqwc == 0) tqwc = qwc; |
315 |
SPR_LOG("SPR1 interleave size=%d, tqwc=%d, sqwc=%d, addr=%lx sadr=%lx", |
316 |
spr1->qwc, tqwc, sqwc, spr1->madr, spr1->sadr); |
317 |
|
318 |
while (qwc > 0) |
319 |
{ |
320 |
spr1->qwc = std::min(tqwc, qwc); |
321 |
qwc -= spr1->qwc; |
322 |
pMem = SPRdmaGetAddr(spr1->madr, false); |
323 |
memcpy_fast(&psSu8(spr1->sadr), (u8*)pMem, spr1->qwc << 4); |
324 |
spr1->sadr += spr1->qwc * 16; |
325 |
spr1->madr += (sqwc + spr1->qwc) * 16; |
326 |
} |
327 |
|
328 |
spr1->qwc = 0; |
329 |
spr1finished = true; |
330 |
} |
331 |
|
332 |
void _dmaSPR1() // toSPR work function |
333 |
{ |
334 |
switch(spr1->chcr.MOD) |
335 |
{ |
336 |
case NORMAL_MODE: |
337 |
{ |
338 |
//int cycles = 0; |
339 |
// Transfer Dn_QWC from Dn_MADR to SPR1 |
340 |
SPR1chain(); |
341 |
spr1finished = true; |
342 |
return; |
343 |
} |
344 |
case CHAIN_MODE: |
345 |
{ |
346 |
tDMA_TAG *ptag; |
347 |
bool done = false; |
348 |
|
349 |
if (spr1->qwc > 0) |
350 |
{ |
351 |
// Transfer Dn_QWC from Dn_MADR to SPR1 |
352 |
SPR1chain(); |
353 |
spr1finished = true; |
354 |
return; |
355 |
} |
356 |
// Chain Mode |
357 |
|
358 |
ptag = SPRdmaGetAddr(spr1->tadr, false); //Set memory pointer to TADR |
359 |
|
360 |
if (!spr1->transfer("SPR1 Tag", ptag)) |
361 |
{ |
362 |
done = true; |
363 |
spr1finished = done; |
364 |
} |
365 |
|
366 |
spr1->madr = ptag[1]._u32; //MADR = ADDR field + SPR |
367 |
|
368 |
// Transfer dma tag if tte is set |
369 |
if (spr1->chcr.TTE) |
370 |
{ |
371 |
SPR_LOG("SPR TTE: %x_%x\n", ptag[3]._u32, ptag[2]._u32); |
372 |
SPR1transfer((u32*)ptag, 4); //Transfer Tag |
373 |
} |
374 |
|
375 |
SPR_LOG("spr1 dmaChain %8.8x_%8.8x size=%d, id=%d, addr=%lx", |
376 |
ptag[1]._u32, ptag[0]._u32, spr1->qwc, ptag->ID, spr1->madr); |
377 |
|
378 |
done = (hwDmacSrcChain(spr1, ptag->ID)); |
379 |
SPR1chain(); //Transfers the data set by the switch |
380 |
|
381 |
if (spr1->chcr.TIE && ptag->IRQ) //Check TIE bit of CHCR and IRQ bit of tag |
382 |
{ |
383 |
SPR_LOG("dmaIrq Set"); |
384 |
|
385 |
//Console.WriteLn("SPR1 TIE"); |
386 |
done = true; |
387 |
} |
388 |
|
389 |
spr1finished = done; |
390 |
if (!done) |
391 |
{ |
392 |
ptag = SPRdmaGetAddr(spr1->tadr, false); //Set memory pointer to TADR |
393 |
CPU_INT(DMAC_TO_SPR, /*(ptag[0].QWC / BIAS)*/ 4 );// the lower 16 bits of the tag / BIAS); |
394 |
} |
395 |
break; |
396 |
} |
397 |
//case INTERLEAVE_MODE: |
398 |
default: |
399 |
{ |
400 |
_SPR1interleave(); |
401 |
break; |
402 |
} |
403 |
} |
404 |
} |
405 |
|
406 |
void dmaSPR1() // toSPR |
407 |
{ |
408 |
SPR_LOG("dmaSPR1 chcr = 0x%x, madr = 0x%x, qwc = 0x%x\n" |
409 |
" tadr = 0x%x, sadr = 0x%x", |
410 |
spr1->chcr._u32, spr1->madr, spr1->qwc, |
411 |
spr1->tadr, spr1->sadr); |
412 |
|
413 |
if ((spr1->chcr.MOD == CHAIN_MODE) && (spr1->qwc == 0)) |
414 |
{ |
415 |
tDMA_TAG *ptag; |
416 |
ptag = SPRdmaGetAddr(spr1->tadr, false); //Set memory pointer to TADR |
417 |
CPU_INT(DMAC_TO_SPR, /*ptag[0].QWC / BIAS*/ 4 ); |
418 |
return; |
419 |
} |
420 |
if(spr1->chcr.MOD == CHAIN_MODE && spr1->qwc > 0) DevCon.Warning(L"SPR1 QWC on Chain " + spr1->chcr.desc()); |
421 |
// COMPLETE HACK!!! For now at least.. FFX Videos dont rely on interrupts or reading DMA values |
422 |
// It merely assumes that the last one has finished then starts another one (broke with the DMA fix) |
423 |
// This "shouldn't" cause any problems as SPR is generally faster than the other DMAS anyway. (Refraction) |
424 |
CPU_INT(DMAC_TO_SPR, /*spr1->qwc / BIAS*/ 4 ); |
425 |
} |
426 |
|
427 |
void SPRTOinterrupt() |
428 |
{ |
429 |
_dmaSPR1(); |
430 |
if (!spr1finished) return; |
431 |
|
432 |
spr1->chcr.STR = false; |
433 |
hwDmacIrq(DMAC_TO_SPR); |
434 |
} |
435 |
|
436 |
void SaveStateBase::sprFreeze() |
437 |
{ |
438 |
FreezeTag("SPRdma"); |
439 |
|
440 |
Freeze(spr0finished); |
441 |
Freeze(spr1finished); |
442 |
Freeze(mfifotransferred); |
443 |
} |