/[pcsx2_0.9.7]/trunk/pcsx2/IPU/IPU.cpp
ViewVC logotype

Annotation of /trunk/pcsx2/IPU/IPU.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 31 - (hide annotations) (download)
Tue Sep 7 03:24:11 2010 UTC (9 years, 10 months ago) by william
File size: 42261 byte(s)
committing r3113 initial commit again...
1 william 31 /* 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     // PCH Warning! This file, when compiled with PCH + Optimizations, fails in very curious
18     // and unexpected ways (most obvious is a freeze in the middle of the New Game video of
19     // Final Fantasy XII). So make sure to force-disable PCH for this file at ALL times.
20     // ----------------------------------------------------------------------------
21    
22     #include "PrecompiledHeader.h"
23     #include "Common.h"
24    
25     #include "IPU.h"
26     #include "yuv2rgb.h"
27     #include "Vif.h"
28     #include "Gif.h"
29     #include "Vif_Dma.h"
30    
31     // Zero cycle IRQ schedules aren't really good, but the IPU uses them.
32     // Better to throw the IRQ inline:
33    
34     #define IPU_INT0_FROM() ipu0Interrupt()
35     //#define IPU_INT0_FROM() CPU_INT( DMAC_FROM_IPU, 0 )
36    
37     // IPU Inline'd IRQs : Calls the IPU interrupt handlers directly instead of
38     // feeding them through the EE's branch test. (see IPU.h for details)
39    
40    
41    
42     static tIPU_DMA g_nDMATransfer(0);
43     static tIPU_cmd ipu_cmd;
44     static IPUStatus IPU1Status;
45    
46     // FIXME - g_nIPU0Data and Pointer are not saved in the savestate, which breaks savestates for some
47     // FMVs at random (if they get saved during the half frame of a 30fps rate). The fix is complicated
48     // since coroutine is such a pita. (air)
49     int g_nIPU0Data = 0; // data left to transfer
50     u8* g_pIPU0Pointer = NULL;
51    
52     void ReorderBitstream();
53    
54     // the BP doesn't advance and returns -1 if there is no data to be read
55     tIPU_BP g_BP;
56     static coroutine_t s_routine; // used for executing BDEC/IDEC
57     static int s_RoutineDone = 0;
58     static u32 s_tempstack[0x4000]; // 64k
59    
60     void IPUWorker();
61    
62     // Color conversion stuff, the memory layout is a total hack
63     // convert_data_buffer is a pointer to the internal rgb struct (the first param in convert_init_t)
64     //char convert_data_buffer[sizeof(convert_rgb_t)];
65     char convert_data_buffer[0x1C];
66    
67     // Quantization matrix
68     static u8 niq[64]; //non-intraquant matrix
69     static u8 iq[64]; //intraquant matrix
70     u16 vqclut[16]; //clut conversion table
71     static u8 s_thresh[2]; //thresholds for color conversions
72     int coded_block_pattern = 0;
73     __aligned16 macroblock_8 mb8;
74     __aligned16 macroblock_16 mb16;
75     __aligned16 macroblock_rgb32 rgb32;
76     __aligned16 macroblock_rgb16 rgb16;
77    
78     u8 indx4[16*16/2];
79     bool mpeg2_inited = false; //mpeg2_idct_init() must be called only once
80     u8 PCT[] = {'r', 'I', 'P', 'B', 'D', '-', '-', '-'};
81     decoder_t g_decoder; //static, only to place it in bss
82     decoder_t tempdec;
83    
84     extern "C"
85     {
86     extern u8 mpeg2_scan_norm[64];
87     extern u8 mpeg2_scan_alt[64];
88     }
89    
90     __aligned16 u8 _readbits[80]; //local buffer (ring buffer)
91     u8* readbits = _readbits; // always can decrement by one 1qw
92    
93     __forceinline void IPUProcessInterrupt()
94     {
95     if (ipuRegs->ctrl.BUSY && g_BP.IFC) IPUWorker();
96     }
97    
98     void init_g_decoder()
99     {
100     //other stuff
101     g_decoder.intra_quantizer_matrix = (u8*)iq;
102     g_decoder.non_intra_quantizer_matrix = (u8*)niq;
103     g_decoder.picture_structure = FRAME_PICTURE; //default: progressive...my guess:P
104     g_decoder.mb8 = &mb8;
105     g_decoder.mb16 = &mb16;
106     g_decoder.rgb32 = &rgb32;
107     g_decoder.rgb16 = &rgb16;
108     g_decoder.stride = 16;
109     }
110    
111     void mpeg2_init()
112     {
113     if (!mpeg2_inited)
114     {
115     mpeg2_idct_init();
116     yuv2rgb_init();
117     memzero(mb8.Y);
118     memzero(mb8.Cb);
119     memzero(mb8.Cr);
120     memzero(mb16.Y);
121     memzero(mb16.Cb);
122     memzero(mb16.Cr);
123     mpeg2_inited = true;
124     }
125     }
126    
127     /////////////////////////////////////////////////////////
128     // Register accesses (run on EE thread)
129     int ipuInit()
130     {
131     memzero(*ipuRegs);
132     memzero(g_BP);
133     init_g_decoder();
134     g_nDMATransfer.reset();
135     IPU1Status.InProgress = false;
136     IPU1Status.DMAMode = DMA_MODE_NORMAL;
137     IPU1Status.DMAFinished = true;
138     ipu_fifo.init();
139     ipu_cmd.clear();
140     return 0;
141     }
142    
143     void ipuReset()
144     {
145     ipuInit();
146     }
147    
148     void ipuShutdown()
149     {
150     }
151    
152     void ReportIPU()
153     {
154     Console.WriteLn(g_nDMATransfer.desc());
155     Console.WriteLn(ipu_fifo.in.desc());
156     Console.WriteLn(ipu_fifo.out.desc());
157     Console.WriteLn(g_BP.desc());
158     Console.WriteLn("niq = 0x%x, iq = 0x%x.", niq, iq);
159     Console.WriteLn("vqclut = 0x%x.", vqclut);
160     Console.WriteLn("s_thresh = 0x%x.", s_thresh);
161     Console.WriteLn("coded_block_pattern = 0x%x.", coded_block_pattern);
162     Console.WriteLn("g_decoder = 0x%x.", g_decoder);
163     Console.WriteLn("mpeg2: scan_norm = 0x%x, alt = 0x%x.", mpeg2_scan_norm, mpeg2_scan_alt);
164     Console.WriteLn(ipu_cmd.desc());
165     Console.WriteLn("_readbits = 0x%x. readbits - _readbits, which is also frozen, is 0x%x.",
166     _readbits, readbits - _readbits);
167     Console.Newline();
168     }
169     // fixme - ipuFreeze looks fairly broken. Should probably take a closer look at some point.
170    
171     void SaveStateBase::ipuFreeze()
172     {
173     // Get a report of the status of the ipu variables when saving and loading savestates.
174     //ReportIPU();
175     FreezeTag("IPU");
176    
177     // old versions saved the IPU regs, but they're already saved as part of HW!
178     //FreezeMem(ipuRegs, sizeof(IPUregisters));
179    
180     Freeze(g_nDMATransfer);
181     Freeze(ipu_fifo);
182    
183     Freeze(g_BP);
184     Freeze(niq);
185     Freeze(iq);
186     Freeze(vqclut);
187     Freeze(s_thresh);
188     Freeze(coded_block_pattern);
189     Freeze(g_decoder);
190     Freeze(mpeg2_scan_norm);
191     Freeze(mpeg2_scan_alt);
192    
193     Freeze(ipu_cmd);
194    
195     Freeze(_readbits);
196    
197     int temp = readbits - _readbits;
198     Freeze(temp);
199    
200     if (IsLoading())
201     {
202     readbits = _readbits;
203     init_g_decoder();
204     mpeg2_init();
205     }
206     }
207    
208     bool ipuCanFreeze()
209     {
210     return (ipu_cmd.current == -1);
211     }
212    
213     __forceinline u32 ipuRead32(u32 mem)
214     {
215     // Note: It's assumed that mem's input value is always in the 0x10002000 page
216     // of memory (if not, it's probably bad code).
217    
218     pxAssert((mem & ~0xff) == 0x10002000);
219     mem &= 0xff; // ipu repeats every 0x100
220    
221     //IPUProcessInterrupt();
222    
223     switch (mem)
224     {
225     ipucase(IPU_CTRL): // IPU_CTRL
226     ipuRegs->ctrl.IFC = g_BP.IFC;
227     ipuRegs->ctrl.CBP = coded_block_pattern;
228    
229     if (!ipuRegs->ctrl.BUSY)
230     IPU_LOG("Ipu read32: IPU_CTRL=0x%08X %x", ipuRegs->ctrl._u32, cpuRegs.pc);
231    
232     return ipuRegs->ctrl._u32;
233    
234     ipucase(IPU_BP): // IPU_BP
235     ipuRegs->ipubp = g_BP.BP & 0x7f;
236     ipuRegs->ipubp |= g_BP.IFC << 8;
237     ipuRegs->ipubp |= (g_BP.FP /*+ g_BP.bufferhasnew*/) << 16;
238    
239     IPU_LOG("Ipu read32: IPU_BP=0x%08X", ipuRegs->ipubp);
240     return ipuRegs->ipubp;
241     default:
242     IPU_LOG("Ipu read32: Addr=0x%x Value = 0x%08X", mem, *(u32*)(((u8*)ipuRegs) + mem));
243     }
244    
245     return *(u32*)(((u8*)ipuRegs) + mem);
246     }
247    
248     __forceinline u64 ipuRead64(u32 mem)
249     {
250     // Note: It's assumed that mem's input value is always in the 0x10002000 page
251     // of memory (if not, it's probably bad code).
252    
253     pxAssert((mem & ~0xff) == 0x10002000);
254     mem &= 0xff; // ipu repeats every 0x100
255    
256     //IPUProcessInterrupt();
257    
258     switch (mem)
259     {
260     ipucase(IPU_CMD): // IPU_CMD
261     if (ipuRegs->cmd.DATA & 0xffffff)
262     IPU_LOG("Ipu read64: IPU_CMD=BUSY=%x, DATA=%08X", ipuRegs->cmd.BUSY ? 1 : 0, ipuRegs->cmd.DATA);
263     break;
264    
265     ipucase(IPU_CTRL):
266     DevCon.Warning("reading 64bit IPU ctrl");
267     break;
268    
269     ipucase(IPU_BP):
270     DevCon.Warning("reading 64bit IPU top");
271     break;
272    
273     ipucase(IPU_TOP): // IPU_TOP
274     IPU_LOG("Ipu read64: IPU_TOP=%x, bp = %d", ipuRegs->top, g_BP.BP);
275     break;
276    
277     default:
278     IPU_LOG("Ipu read64: Unknown=%x", mem);
279     break;
280     }
281     return *(u64*)(((u8*)ipuRegs) + mem);
282     }
283    
284     void ipuSoftReset()
285     {
286     mpeg2_init();
287     ipu_fifo.clear();
288    
289     coded_block_pattern = 0;
290    
291     ipuRegs->ctrl.reset();
292     ipuRegs->top = 0;
293     ipu_cmd.clear();
294    
295     g_BP.BP = 0;
296     g_BP.FP = 0;
297     //g_BP.bufferhasnew = 0;
298     }
299    
300     __forceinline void ipuWrite32(u32 mem, u32 value)
301     {
302     // Note: It's assumed that mem's input value is always in the 0x10002000 page
303     // of memory (if not, it's probably bad code).
304    
305     pxAssert((mem & ~0xfff) == 0x10002000);
306     mem &= 0xfff;
307    
308     IPUProcessInterrupt();
309    
310     switch (mem)
311     {
312     ipucase(IPU_CMD): // IPU_CMD
313     IPU_LOG("Ipu write32: IPU_CMD=0x%08X", value);
314     IPUCMD_WRITE(value);
315     break;
316    
317     ipucase(IPU_CTRL): // IPU_CTRL
318     // CTRL = the first 16 bits of ctrl [0x8000ffff], + value for the next 16 bits,
319     // minus the reserved bits. (18-19; 27-29) [0x47f30000]
320     ipuRegs->ctrl.write(value);
321     if (ipuRegs->ctrl.IDP == 3)
322     {
323     Console.WriteLn("IPU Invalid Intra DC Precision, switching to 9 bits");
324     ipuRegs->ctrl.IDP = 1;
325     }
326    
327     if (ipuRegs->ctrl.RST) ipuSoftReset(); // RESET
328    
329     IPU_LOG("Ipu write32: IPU_CTRL=0x%08X", value);
330     break;
331    
332     default:
333     IPU_LOG("Ipu write32: Unknown=%x", mem);
334     *(u32*)((u8*)ipuRegs + mem) = value;
335     break;
336     }
337     }
338    
339     __forceinline void ipuWrite64(u32 mem, u64 value)
340     {
341     // Note: It's assumed that mem's input value is always in the 0x10002000 page
342     // of memory (if not, it's probably bad code).
343    
344     pxAssert((mem & ~0xfff) == 0x10002000);
345     mem &= 0xfff;
346    
347     IPUProcessInterrupt();
348    
349     switch (mem)
350     {
351     ipucase(IPU_CMD):
352     IPU_LOG("Ipu write64: IPU_CMD=0x%08X", value);
353     IPUCMD_WRITE((u32)value);
354     break;
355    
356     default:
357     IPU_LOG("Ipu write64: Unknown=%x", mem);
358     *(u64*)((u8*)ipuRegs + mem) = value;
359     break;
360     }
361     }
362    
363    
364     //////////////////////////////////////////////////////
365     // IPU Commands (exec on worker thread only)
366    
367     static void ipuBCLR(u32 val)
368     {
369     ipu_fifo.in.clear();
370    
371     g_BP.BP = val & 0x7F;
372     g_BP.FP = 0;
373     //g_BP.bufferhasnew = 0;
374     ipuRegs->ctrl.BUSY = 0;
375     ipuRegs->cmd.BUSY = 0;
376     memzero_ptr<80>(readbits);
377     IPU_LOG("Clear IPU input FIFO. Set Bit offset=0x%X", g_BP.BP);
378     }
379    
380     static BOOL ipuIDEC(u32 val)
381     {
382     tIPU_CMD_IDEC idec(val);
383    
384     idec.log();
385     g_BP.BP += idec.FB;//skip FB bits
386     //from IPU_CTRL
387     ipuRegs->ctrl.PCT = I_TYPE; //Intra DECoding;)
388     g_decoder.coding_type = ipuRegs->ctrl.PCT;
389     g_decoder.mpeg1 = ipuRegs->ctrl.MP1;
390     g_decoder.q_scale_type = ipuRegs->ctrl.QST;
391     g_decoder.intra_vlc_format = ipuRegs->ctrl.IVF;
392     g_decoder.scan = ipuRegs->ctrl.AS ? mpeg2_scan_alt : mpeg2_scan_norm;
393     g_decoder.intra_dc_precision = ipuRegs->ctrl.IDP;
394    
395     //from IDEC value
396     g_decoder.quantizer_scale = idec.QSC;
397     g_decoder.frame_pred_frame_dct = !idec.DTD;
398     g_decoder.sgn = idec.SGN;
399     g_decoder.dte = idec.DTE;
400     g_decoder.ofm = idec.OFM;
401    
402     //other stuff
403     g_decoder.dcr = 1; // resets DC prediction value
404    
405     s_routine = so_create(mpeg2sliceIDEC, &s_RoutineDone, s_tempstack, sizeof(s_tempstack));
406     pxAssert(s_routine != NULL);
407     so_call(s_routine);
408     if (s_RoutineDone) s_routine = NULL;
409    
410     return s_RoutineDone;
411     }
412    
413     static int s_bdec = 0;
414    
415     static __forceinline BOOL ipuBDEC(u32 val)
416     {
417     tIPU_CMD_BDEC bdec(val);
418    
419     bdec.log(s_bdec);
420     if (IsDebugBuild) s_bdec++;
421    
422     g_BP.BP += bdec.FB;//skip FB bits
423     g_decoder.coding_type = I_TYPE;
424     g_decoder.mpeg1 = ipuRegs->ctrl.MP1;
425     g_decoder.q_scale_type = ipuRegs->ctrl.QST;
426     g_decoder.intra_vlc_format = ipuRegs->ctrl.IVF;
427     g_decoder.scan = ipuRegs->ctrl.AS ? mpeg2_scan_alt : mpeg2_scan_norm;
428     g_decoder.intra_dc_precision = ipuRegs->ctrl.IDP;
429    
430     //from BDEC value
431     /* JayteeMaster: the quantizer (linear/non linear) depends on the q_scale_type */
432     g_decoder.quantizer_scale = g_decoder.q_scale_type ? non_linear_quantizer_scale [bdec.QSC] : bdec.QSC << 1;
433     g_decoder.macroblock_modes = bdec.DT ? DCT_TYPE_INTERLACED : 0;
434     g_decoder.dcr = bdec.DCR;
435     g_decoder.macroblock_modes |= bdec.MBI ? MACROBLOCK_INTRA : MACROBLOCK_PATTERN;
436    
437     memzero(mb8);
438     memzero(mb16);
439    
440     s_routine = so_create(mpeg2_slice, &s_RoutineDone, s_tempstack, sizeof(s_tempstack));
441     pxAssert(s_routine != NULL);
442     so_call(s_routine);
443    
444     if (s_RoutineDone) s_routine = NULL;
445     return s_RoutineDone;
446     }
447    
448     static BOOL __fastcall ipuVDEC(u32 val)
449     {
450     switch (ipu_cmd.pos[0])
451     {
452     case 0:
453     ipuRegs->cmd.DATA = 0;
454     if (!getBits32((u8*)&g_decoder.bitstream_buf, 0)) return FALSE;
455    
456     g_decoder.bitstream_bits = -16;
457     BigEndian(g_decoder.bitstream_buf, g_decoder.bitstream_buf);
458    
459     switch ((val >> 26) & 3)
460     {
461     case 0://Macroblock Address Increment
462     g_decoder.mpeg1 = ipuRegs->ctrl.MP1;
463     ipuRegs->cmd.DATA = get_macroblock_address_increment(&g_decoder);
464     break;
465    
466     case 1://Macroblock Type //known issues: no error detected
467     g_decoder.frame_pred_frame_dct = 1;//prevent DCT_TYPE_INTERLACED
468     g_decoder.coding_type = ipuRegs->ctrl.PCT;
469     ipuRegs->cmd.DATA = get_macroblock_modes(&g_decoder);
470     break;
471    
472     case 2://Motion Code //known issues: no error detected
473     ipuRegs->cmd.DATA = get_motion_delta(&g_decoder, 0);
474     break;
475    
476     case 3://DMVector
477     ipuRegs->cmd.DATA = get_dmv(&g_decoder);
478     break;
479     }
480    
481     g_BP.BP += (g_decoder.bitstream_bits + 16);
482    
483     if ((int)g_BP.BP < 0)
484     {
485     g_BP.BP += 128;
486     ReorderBitstream();
487     }
488    
489     FillInternalBuffer(&g_BP.BP, 1, 0);
490    
491     ipuRegs->cmd.DATA = (ipuRegs->cmd.DATA & 0xFFFF) | ((g_decoder.bitstream_bits + 16) << 16);
492     ipuRegs->ctrl.ECD = (ipuRegs->cmd.DATA == 0);
493    
494     case 1:
495     if (!getBits32((u8*)&ipuRegs->top, 0))
496     {
497     ipu_cmd.pos[0] = 1;
498     return FALSE;
499     }
500    
501     BigEndian(ipuRegs->top, ipuRegs->top);
502    
503     IPU_LOG("IPU VDEC command data 0x%x(0x%x). Skip 0x%X bits/Table=%d (%s), pct %d",
504     ipuRegs->cmd.DATA, ipuRegs->cmd.DATA >> 16, val & 0x3f, (val >> 26) & 3, (val >> 26) & 1 ?
505     ((val >> 26) & 2 ? "DMV" : "MBT") : (((val >> 26) & 2 ? "MC" : "MBAI")), ipuRegs->ctrl.PCT);
506     return TRUE;
507    
508     jNO_DEFAULT
509     }
510    
511     return FALSE;
512     }
513    
514     static __forceinline BOOL ipuFDEC(u32 val)
515     {
516     if (!getBits32((u8*)&ipuRegs->cmd.DATA, 0)) return FALSE;
517    
518     BigEndian(ipuRegs->cmd.DATA, ipuRegs->cmd.DATA);
519     ipuRegs->top = ipuRegs->cmd.DATA;
520    
521     IPU_LOG("FDEC read: 0x%8.8x", ipuRegs->top);
522    
523     return TRUE;
524     }
525    
526     static BOOL ipuSETIQ(u32 val)
527     {
528     int i;
529    
530     if ((val >> 27) & 1)
531     {
532     ipu_cmd.pos[0] += getBits((u8*)niq + ipu_cmd.pos[0], 512 - 8 * ipu_cmd.pos[0], 1); // 8*8*8
533    
534     IPU_LOG("Read non-intra quantization matrix from IPU FIFO.");
535     for (i = 0; i < 8; i++)
536     {
537     IPU_LOG("%02X %02X %02X %02X %02X %02X %02X %02X",
538     niq[i * 8 + 0], niq[i * 8 + 1], niq[i * 8 + 2], niq[i * 8 + 3],
539     niq[i * 8 + 4], niq[i * 8 + 5], niq[i * 8 + 6], niq[i * 8 + 7]);
540     }
541     }
542     else
543     {
544     ipu_cmd.pos[0] += getBits((u8*)iq + 8 * ipu_cmd.pos[0], 512 - 8 * ipu_cmd.pos[0], 1);
545    
546     IPU_LOG("Read intra quantization matrix from IPU FIFO.");
547     for (i = 0; i < 8; i++)
548     {
549     IPU_LOG("%02X %02X %02X %02X %02X %02X %02X %02X",
550     iq[i * 8 + 0], iq[i * 8 + 1], iq[i * 8 + 2], iq[i *8 + 3],
551     iq[i * 8 + 4], iq[i * 8 + 5], iq[i * 8 + 6], iq[i *8 + 7]);
552     }
553     }
554    
555     return ipu_cmd.pos[0] == 64;
556     }
557    
558     static BOOL ipuSETVQ(u32 val)
559     {
560     ipu_cmd.pos[0] += getBits((u8*)vqclut + ipu_cmd.pos[0], 256 - 8 * ipu_cmd.pos[0], 1); // 16*2*8
561    
562     if (ipu_cmd.pos[0] == 32)
563     {
564     IPU_LOG("IPU SETVQ command.\nRead VQCLUT table from IPU FIFO.");
565     IPU_LOG(
566     "%02d:%02d:%02d %02d:%02d:%02d %02d:%02d:%02d %02d:%02d:%02d "
567     "%02d:%02d:%02d %02d:%02d:%02d %02d:%02d:%02d %02d:%02d:%02d"
568     "%02d:%02d:%02d %02d:%02d:%02d %02d:%02d:%02d %02d:%02d:%02d "
569     "%02d:%02d:%02d %02d:%02d:%02d %02d:%02d:%02d %02d:%02d:%02d",
570     vqclut[0] >> 10, (vqclut[0] >> 5) & 0x1F, vqclut[0] & 0x1F,
571     vqclut[1] >> 10, (vqclut[1] >> 5) & 0x1F, vqclut[1] & 0x1F,
572     vqclut[2] >> 10, (vqclut[2] >> 5) & 0x1F, vqclut[2] & 0x1F,
573     vqclut[3] >> 10, (vqclut[3] >> 5) & 0x1F, vqclut[3] & 0x1F,
574     vqclut[4] >> 10, (vqclut[4] >> 5) & 0x1F, vqclut[4] & 0x1F,
575     vqclut[5] >> 10, (vqclut[5] >> 5) & 0x1F, vqclut[5] & 0x1F,
576     vqclut[6] >> 10, (vqclut[6] >> 5) & 0x1F, vqclut[6] & 0x1F,
577     vqclut[7] >> 10, (vqclut[7] >> 5) & 0x1F, vqclut[7] & 0x1F,
578     vqclut[8] >> 10, (vqclut[8] >> 5) & 0x1F, vqclut[8] & 0x1F,
579     vqclut[9] >> 10, (vqclut[9] >> 5) & 0x1F, vqclut[9] & 0x1F,
580     vqclut[10] >> 10, (vqclut[10] >> 5) & 0x1F, vqclut[10] & 0x1F,
581     vqclut[11] >> 10, (vqclut[11] >> 5) & 0x1F, vqclut[11] & 0x1F,
582     vqclut[12] >> 10, (vqclut[12] >> 5) & 0x1F, vqclut[12] & 0x1F,
583     vqclut[13] >> 10, (vqclut[13] >> 5) & 0x1F, vqclut[13] & 0x1F,
584     vqclut[14] >> 10, (vqclut[14] >> 5) & 0x1F, vqclut[14] & 0x1F,
585     vqclut[15] >> 10, (vqclut[15] >> 5) & 0x1F, vqclut[15] & 0x1F);
586     }
587    
588     return ipu_cmd.pos[0] == 32;
589     }
590    
591     // IPU Transfers are split into 8Qwords so we need to send ALL the data
592     static BOOL __fastcall ipuCSC(u32 val)
593     {
594     tIPU_CMD_CSC csc(val);
595     csc.log_from_YCbCr();
596    
597     for (;ipu_cmd.index < (int)csc.MBC; ipu_cmd.index++)
598     {
599    
600     if (ipu_cmd.pos[0] < 3072 / 8)
601     {
602     ipu_cmd.pos[0] += getBits((u8*) & mb8 + ipu_cmd.pos[0], 3072 - 8 * ipu_cmd.pos[0], 1);
603    
604     if (ipu_cmd.pos[0] < 3072 / 8) return FALSE;
605    
606     ipu_csc(&mb8, &rgb32, 0);
607     if (csc.OFM) ipu_dither(&rgb32, &rgb16, csc.DTE);
608     }
609    
610     if (csc.OFM)
611     {
612     while (ipu_cmd.pos[1] < 32)
613     {
614     ipu_cmd.pos[1] += ipu_fifo.out.write(((u32*) & rgb16) + 4 * ipu_cmd.pos[1], 32 - ipu_cmd.pos[1]);
615    
616     if (ipu_cmd.pos[1] <= 0) return FALSE;
617     }
618     }
619     else
620     {
621     while (ipu_cmd.pos[1] < 64)
622     {
623     ipu_cmd.pos[1] += ipu_fifo.out.write(((u32*) & rgb32) + 4 * ipu_cmd.pos[1], 64 - ipu_cmd.pos[1]);
624    
625     if (ipu_cmd.pos[1] <= 0) return FALSE;
626     }
627     }
628    
629     ipu_cmd.pos[0] = 0;
630     ipu_cmd.pos[1] = 0;
631     }
632    
633     return TRUE;
634     }
635    
636     // Todo - Need to add the same stop and start code as CSC
637     static BOOL ipuPACK(u32 val)
638     {
639     tIPU_CMD_CSC csc(val);
640     csc.log_from_RGB32();
641    
642     for (;ipu_cmd.index < (int)csc.MBC; ipu_cmd.index++)
643     {
644     if (ipu_cmd.pos[0] < 512)
645     {
646     ipu_cmd.pos[0] += getBits((u8*) & mb8 + ipu_cmd.pos[0], 512 - 8 * ipu_cmd.pos[0], 1);
647    
648     if (ipu_cmd.pos[0] < 64) return FALSE;
649    
650     ipu_csc(&mb8, &rgb32, 0);
651     ipu_dither(&rgb32, &rgb16, csc.DTE);
652    
653     if (csc.OFM) ipu_vq(&rgb16, indx4);
654     }
655    
656     if (csc.OFM)
657     {
658     ipu_cmd.pos[1] += ipu_fifo.out.write(((u32*) & rgb16) + 4 * ipu_cmd.pos[1], 32 - ipu_cmd.pos[1]);
659    
660     if (ipu_cmd.pos[1] < 32) return FALSE;
661     }
662     else
663     {
664     ipu_cmd.pos[1] += ipu_fifo.out.write(((u32*)indx4) + 4 * ipu_cmd.pos[1], 8 - ipu_cmd.pos[1]);
665    
666     if (ipu_cmd.pos[1] < 8) return FALSE;
667     }
668    
669     ipu_cmd.pos[0] = 0;
670     ipu_cmd.pos[1] = 0;
671     }
672    
673     return TRUE;
674     }
675    
676     static void ipuSETTH(u32 val)
677     {
678     s_thresh[0] = (val & 0xff);
679     s_thresh[1] = ((val >> 16) & 0xff);
680     IPU_LOG("IPU SETTH (Set threshold value)command %x.", val&0xff00ff);
681     }
682    
683     ///////////////////////
684     // IPU Worker Thread //
685     ///////////////////////
686     __forceinline void IPU_INTERRUPT() //dma
687     {
688     hwIntcIrq(INTC_IPU);
689     }
690    
691     void IPUCMD_WRITE(u32 val)
692     {
693     // don't process anything if currently busy
694     if (ipuRegs->ctrl.BUSY) Console.WriteLn("IPU BUSY!"); // wait for thread
695    
696     ipuRegs->ctrl.ECD = 0;
697     ipuRegs->ctrl.SCD = 0; //clear ECD/SCD
698     ipuRegs->cmd.DATA = val;
699     ipu_cmd.pos[0] = 0;
700    
701     switch (ipuRegs->cmd.CMD)
702     {
703     case SCE_IPU_BCLR:
704     ipuBCLR(val);
705     IPU_INTERRUPT(); //DMAC_TO_IPU
706     return;
707    
708     case SCE_IPU_VDEC:
709    
710     g_BP.BP += val & 0x3F;
711    
712     // check if enough data in queue
713     if (ipuVDEC(val)) return;
714    
715     ipuRegs->cmd.BUSY = 0x80000000;
716     ipuRegs->topbusy = 0x80000000;
717     break;
718    
719     case SCE_IPU_FDEC:
720     IPU_LOG("IPU FDEC command. Skip 0x%X bits, FIFO 0x%X qwords, BP 0x%X, FP %d, CHCR 0x%x, %x",
721     val & 0x3f, g_BP.IFC, (int)g_BP.BP, g_BP.FP, ipu1dma->chcr._u32, cpuRegs.pc);
722     g_BP.BP += val & 0x3F;
723     if (ipuFDEC(val)) return;
724     ipuRegs->cmd.BUSY = 0x80000000;
725     ipuRegs->topbusy = 0x80000000;
726     break;
727    
728     case SCE_IPU_SETTH:
729     ipuSETTH(val);
730     hwIntcIrq(INTC_IPU);
731     return;
732    
733     case SCE_IPU_SETIQ:
734     IPU_LOG("IPU SETIQ command.");
735     if (val & 0x3f) IPU_LOG("Skip %d bits.", val & 0x3f);
736     g_BP.BP += val & 0x3F;
737     if (ipuSETIQ(ipuRegs->cmd.DATA)) return;
738     break;
739    
740     case SCE_IPU_SETVQ:
741     if (ipuSETVQ(ipuRegs->cmd.DATA)) return;
742     break;
743    
744     case SCE_IPU_CSC:
745     ipu_cmd.pos[1] = 0;
746     ipu_cmd.index = 0;
747    
748     if (ipuCSC(ipuRegs->cmd.DATA))
749     {
750     if (ipu0dma->qwc > 0 && ipu0dma->chcr.STR) IPU_INT0_FROM();
751     return;
752     }
753     break;
754    
755     case SCE_IPU_PACK:
756     ipu_cmd.pos[1] = 0;
757     ipu_cmd.index = 0;
758     if (ipuPACK(ipuRegs->cmd.DATA)) return;
759     break;
760    
761     case SCE_IPU_IDEC:
762     if (ipuIDEC(val))
763     {
764     // idec done, ipu0 done too
765     if (ipu0dma->qwc > 0 && ipu0dma->chcr.STR) IPU_INT0_FROM();
766     return;
767     }
768     ipuRegs->topbusy = 0x80000000;
769     // have to resort to the thread
770     ipu_cmd.current = val >> 28;
771     ipuRegs->ctrl.BUSY = 1;
772     return;
773    
774     case SCE_IPU_BDEC:
775     if (ipuBDEC(val))
776     {
777     if (ipu0dma->qwc > 0 && ipu0dma->chcr.STR) IPU_INT0_FROM();
778     if (ipuRegs->ctrl.SCD || ipuRegs->ctrl.ECD) hwIntcIrq(INTC_IPU);
779     return;
780     }
781     ipuRegs->topbusy = 0x80000000;
782     ipu_cmd.current = val >> 28;
783     ipuRegs->ctrl.BUSY = 1;
784     return;
785     }
786    
787     // have to resort to the thread
788     ipu_cmd.current = val >> 28;
789     ipuRegs->ctrl.BUSY = 1;
790     if(ipu1dma->chcr.STR == false) hwIntcIrq(INTC_IPU);
791     }
792    
793     void IPUWorker()
794     {
795     pxAssert(ipuRegs->ctrl.BUSY);
796    
797     switch (ipu_cmd.current)
798     {
799     case SCE_IPU_VDEC:
800     if (!ipuVDEC(ipuRegs->cmd.DATA))
801     {
802     if(ipu1dma->chcr.STR == false) hwIntcIrq(INTC_IPU);
803     return;
804     }
805     ipuRegs->cmd.BUSY = 0;
806     ipuRegs->topbusy = 0;
807     break;
808    
809     case SCE_IPU_FDEC:
810     if (!ipuFDEC(ipuRegs->cmd.DATA))
811     {
812     if(ipu1dma->chcr.STR == false) hwIntcIrq(INTC_IPU);
813     return;
814     }
815     ipuRegs->cmd.BUSY = 0;
816     ipuRegs->topbusy = 0;
817     break;
818    
819     case SCE_IPU_SETIQ:
820     if (!ipuSETIQ(ipuRegs->cmd.DATA))
821     {
822     if(ipu1dma->chcr.STR == false) hwIntcIrq(INTC_IPU);
823     return;
824     }
825     break;
826    
827     case SCE_IPU_SETVQ:
828     if (!ipuSETVQ(ipuRegs->cmd.DATA))
829     {
830     if(ipu1dma->chcr.STR == false) hwIntcIrq(INTC_IPU);
831     return;
832     }
833     break;
834    
835     case SCE_IPU_CSC:
836     if (!ipuCSC(ipuRegs->cmd.DATA))
837     {
838     if(ipu1dma->chcr.STR == false) hwIntcIrq(INTC_IPU);
839     return;
840     }
841     if (ipu0dma->qwc > 0 && ipu0dma->chcr.STR) IPU_INT0_FROM();
842     break;
843    
844     case SCE_IPU_PACK:
845     if (!ipuPACK(ipuRegs->cmd.DATA))
846     {
847     if(ipu1dma->chcr.STR == false) hwIntcIrq(INTC_IPU);
848     return;
849     }
850     break;
851    
852     case SCE_IPU_IDEC:
853     so_call(s_routine);
854     if (!s_RoutineDone)
855     {
856     if(ipu1dma->chcr.STR == false) hwIntcIrq(INTC_IPU);
857     return;
858     }
859    
860     ipuRegs->ctrl.OFC = 0;
861     ipuRegs->ctrl.BUSY = 0;
862     ipuRegs->topbusy = 0;
863     ipuRegs->cmd.BUSY = 0;
864     ipu_cmd.current = 0xffffffff;
865    
866     // CHECK!: IPU0dma remains when IDEC is done, so we need to clear it
867     if (ipu0dma->qwc > 0 && ipu0dma->chcr.STR) IPU_INT0_FROM();
868     s_routine = NULL;
869     break;
870    
871     case SCE_IPU_BDEC:
872     so_call(s_routine);
873     if (!s_RoutineDone)
874     {
875     if(ipu1dma->chcr.STR == false) hwIntcIrq(INTC_IPU);
876     return;
877     }
878    
879     ipuRegs->ctrl.BUSY = 0;
880     ipuRegs->topbusy = 0;
881     ipuRegs->cmd.BUSY = 0;
882     ipu_cmd.current = 0xffffffff;
883    
884     if (ipu0dma->qwc > 0 && ipu0dma->chcr.STR) IPU_INT0_FROM();
885     s_routine = NULL;
886     if (ipuRegs->ctrl.SCD || ipuRegs->ctrl.ECD) hwIntcIrq(INTC_IPU);
887     return;
888    
889     default:
890     Console.WriteLn("Unknown IPU command: %x", ipuRegs->cmd.CMD);
891     break;
892     }
893    
894     // success
895     ipuRegs->ctrl.BUSY = 0;
896     ipu_cmd.current = 0xffffffff;
897     }
898    
899     /////////////////
900     // Buffer reader
901    
902     // move the readbits queue
903     __forceinline void inc_readbits()
904     {
905     readbits += 16;
906     if (readbits >= _readbits + 64)
907     {
908     // move back
909     *(u64*)(_readbits) = *(u64*)(_readbits + 64);
910     *(u64*)(_readbits + 8) = *(u64*)(_readbits + 72);
911     readbits = _readbits;
912     }
913     }
914    
915     // returns the pointer of readbits moved by 1 qword
916     __forceinline u8* next_readbits()
917     {
918     return readbits + 16;
919     }
920    
921     // returns the pointer of readbits moved by 1 qword
922     u8* prev_readbits()
923     {
924     if (readbits < _readbits + 16) return _readbits + 48 - (readbits - _readbits);
925    
926     return readbits - 16;
927     }
928    
929     void ReorderBitstream()
930     {
931     readbits = prev_readbits();
932     g_BP.FP = 2;
933     }
934    
935     // IPU has a 2qword internal buffer whose status is pointed by FP.
936     // If FP is 1, there's 1 qword in buffer. Second qword is only loaded
937     // incase there are less than 32bits available in the first qword.
938     // \return Number of bits available (clamps at 16 bits)
939     u16 __fastcall FillInternalBuffer(u32 * pointer, u32 advance, u32 size)
940     {
941     if (g_BP.FP == 0)
942     {
943     if (ipu_fifo.in.read(next_readbits()) == 0) return 0;
944    
945     inc_readbits();
946     g_BP.FP = 1;
947     }
948    
949     if ((g_BP.FP < 2) && (*(int*)pointer + size) >= 128)
950     {
951     if (ipu_fifo.in.read(next_readbits())) g_BP.FP += 1;
952     }
953    
954     if (*(int*)pointer >= 128)
955     {
956     pxAssert(g_BP.FP >= 1);
957    
958     if (g_BP.FP > 1) inc_readbits();
959    
960     if (advance)
961     {
962     g_BP.FP--;
963     *pointer &= 127;
964     }
965     }
966    
967     return (g_BP.FP >= 1) ? g_BP.FP * 128 - (*(int*)pointer) : 0;
968     }
969    
970     // whenever reading fractions of bytes. The low bits always come from the next byte
971     // while the high bits come from the current byte
972     u8 __fastcall getBits32(u8 *address, u32 advance)
973     {
974     register u32 mask, shift = 0;
975     u8* readpos;
976    
977     // Check if the current BP has exceeded or reached the limit of 128
978     if (FillInternalBuffer(&g_BP.BP, 1, 32) < 32) return 0;
979    
980     readpos = readbits + (int)g_BP.BP / 8;
981    
982     if (g_BP.BP & 7)
983     {
984     shift = g_BP.BP & 7;
985     mask = (0xff >> shift);
986     mask = mask | (mask << 8) | (mask << 16) | (mask << 24);
987    
988     *(u32*)address = ((~mask & *(u32*)(readpos + 1)) >> (8 - shift)) | (((mask) & *(u32*)readpos) << shift);
989     }
990     else
991     {
992     *(u32*)address = *(u32*)readpos;
993     }
994    
995     if (advance) g_BP.BP += 32;
996    
997     return 1;
998     }
999    
1000     __forceinline u8 __fastcall getBits16(u8 *address, u32 advance)
1001     {
1002     register u32 mask, shift = 0;
1003     u8* readpos;
1004    
1005     // Check if the current BP has exceeded or reached the limit of 128
1006     if (FillInternalBuffer(&g_BP.BP, 1, 16) < 16) return 0;
1007    
1008     readpos = readbits + (int)g_BP.BP / 8;
1009    
1010     if (g_BP.BP & 7)
1011     {
1012     shift = g_BP.BP & 7;
1013     mask = (0xff >> shift);
1014     mask = mask | (mask << 8);
1015    
1016     *(u16*)address = ((~mask & *(u16*)(readpos + 1)) >> (8 - shift)) | (((mask) & *(u16*)readpos) << shift);
1017     }
1018     else
1019     {
1020     *(u16*)address = *(u16*)readpos;
1021     }
1022    
1023     if (advance) g_BP.BP += 16;
1024    
1025     return 1;
1026     }
1027    
1028     u8 __fastcall getBits8(u8 *address, u32 advance)
1029     {
1030     register u32 mask, shift = 0;
1031     u8* readpos;
1032    
1033     // Check if the current BP has exceeded or reached the limit of 128
1034     if (FillInternalBuffer(&g_BP.BP, 1, 8) < 8)
1035     return 0;
1036    
1037     readpos = readbits + (int)g_BP.BP / 8;
1038    
1039     if (g_BP.BP & 7)
1040     {
1041     shift = g_BP.BP & 7;
1042     mask = (0xff >> shift);
1043    
1044     *(u8*)address = (((~mask) & readpos[1]) >> (8 - shift)) | (((mask) & *readpos) << shift);
1045     }
1046     else
1047     {
1048     *(u8*)address = *(u8*)readpos;
1049     }
1050    
1051     if (advance) g_BP.BP += 8;
1052    
1053     return 1;
1054     }
1055    
1056     int __fastcall getBits(u8 *address, u32 size, u32 advance)
1057     {
1058     register u32 mask = 0, shift = 0, howmuch;
1059     u8* oldbits, *oldaddr = address;
1060     u32 pointer = 0, temp;
1061    
1062     // Check if the current BP has exceeded or reached the limit of 128
1063     if (FillInternalBuffer(&g_BP.BP, 1, 8) < 8) return 0;
1064    
1065     oldbits = readbits;
1066     // Backup the current BP in case of VDEC/FDEC
1067     pointer = g_BP.BP;
1068    
1069     if (pointer & 7)
1070     {
1071     address--;
1072     while (size)
1073     {
1074     if (shift == 0)
1075     {
1076     *++address = 0;
1077     shift = 8;
1078     }
1079    
1080     temp = shift; // Lets not pass a register to min.
1081     howmuch = min(min(8 - (pointer & 7), 128 - pointer), min(size, temp));
1082    
1083     if (FillInternalBuffer(&pointer, advance, 8) < 8)
1084     {
1085     if (advance) g_BP.BP = pointer;
1086     return address - oldaddr;
1087     }
1088    
1089     mask = ((0xFF >> (pointer & 7)) << (8 - howmuch - (pointer & 7))) & 0xFF;
1090     mask &= readbits[((pointer) >> 3)];
1091     mask >>= 8 - howmuch - (pointer & 7);
1092     pointer += howmuch;
1093     size -= howmuch;
1094     shift -= howmuch;
1095     *address |= mask << shift;
1096     }
1097     ++address;
1098     }
1099     else
1100     {
1101     u8* readmem;
1102     while (size)
1103     {
1104     if (FillInternalBuffer(&pointer, advance, 8) < 8)
1105     {
1106     if (advance) g_BP.BP = pointer;
1107     return address -oldaddr;
1108     }
1109    
1110     howmuch = min(128 - pointer, size);
1111     size -= howmuch;
1112    
1113     readmem = readbits + (pointer >> 3);
1114     pointer += howmuch;
1115     howmuch >>= 3;
1116    
1117     while (howmuch >= 4)
1118     {
1119     *(u32*)address = *(u32*)readmem;
1120     howmuch -= 4;
1121     address += 4;
1122     readmem += 4;
1123     }
1124    
1125     switch (howmuch)
1126     {
1127     case 3:
1128     address[2] = readmem[2];
1129     case 2:
1130     address[1] = readmem[1];
1131     case 1:
1132     address[0] = readmem[0];
1133     case 0:
1134     break;
1135    
1136     jNO_DEFAULT
1137     }
1138    
1139     address += howmuch;
1140     }
1141     }
1142    
1143     // If not advance then reset the Reading buffer value
1144     if (advance)
1145     g_BP.BP = pointer;
1146     else
1147     readbits = oldbits; // restore the last pointer
1148    
1149     return address - oldaddr;
1150     }
1151    
1152     ///////////////////// CORE FUNCTIONS /////////////////
1153     void Skl_YUV_To_RGB32_MMX(u8 *RGB, const int Dst_BpS, const u8 *Y, const u8 *U, const u8 *V,
1154     const int Src_BpS, const int Width, const int Height);
1155    
1156     void __fastcall ipu_csc(macroblock_8 *mb8, macroblock_rgb32 *rgb32, int sgn)
1157     {
1158     int i;
1159     u8* p = (u8*)rgb32;
1160    
1161     yuv2rgb_sse2();
1162    
1163     if (s_thresh[0] > 0)
1164     {
1165     for (i = 0; i < 16*16; i++, p += 4)
1166     {
1167     if ((p[0] < s_thresh[0]) && (p[1] < s_thresh[0]) && (p[2] < s_thresh[0]))
1168     *(u32*)p = 0;
1169     else if ((p[0] < s_thresh[1]) && (p[1] < s_thresh[1]) && (p[2] < s_thresh[1]))
1170     p[3] = 0x40;
1171     }
1172     }
1173     else if (s_thresh[1] > 0)
1174     {
1175     for (i = 0; i < 16*16; i++, p += 4)
1176     {
1177     if ((p[0] < s_thresh[1]) && (p[1] < s_thresh[1]) && (p[2] < s_thresh[1]))
1178     p[3] = 0x40;
1179     }
1180     }
1181     if (sgn)
1182     {
1183     for (i = 0; i < 16*16; i++, p += 4)
1184     {
1185     *(u32*)p ^= 0x808080;
1186     }
1187     }
1188     }
1189    
1190     void __fastcall ipu_dither(const macroblock_rgb32* rgb32, macroblock_rgb16 *rgb16, int dte)
1191     {
1192     int i, j;
1193     for (i = 0; i < 16; ++i)
1194     {
1195     for (j = 0; j < 16; ++j)
1196     {
1197     rgb16->c[i][j].r = rgb32->c[i][j].r >> 3;
1198     rgb16->c[i][j].g = rgb32->c[i][j].g >> 3;
1199     rgb16->c[i][j].b = rgb32->c[i][j].b >> 3;
1200     rgb16->c[i][j].a = rgb32->c[i][j].a == 0x40;
1201     }
1202     }
1203     }
1204    
1205     void __fastcall ipu_vq(macroblock_rgb16 *rgb16, u8* indx4)
1206     {
1207     Console.Error("IPU: VQ not implemented");
1208     }
1209    
1210     void __fastcall ipu_copy(const macroblock_8 *mb8, macroblock_16 *mb16)
1211     {
1212     const u8 *s = (const u8*)mb8;
1213     s16 *d = (s16*)mb16;
1214     int i;
1215     for (i = 0; i < 256; i++) *d++ = *s++; //Y bias - 16
1216     for (i = 0; i < 64; i++) *d++ = *s++; //Cr bias - 128
1217     for (i = 0; i < 64; i++) *d++ = *s++; //Cb bias - 128
1218     }
1219    
1220    
1221    
1222     static __forceinline bool ipuDmacPartialChain(tDMA_TAG tag)
1223     {
1224     switch (tag.ID)
1225     {
1226     case TAG_REFE: // refe
1227     ipu1dma->tadr += 16;
1228     return true;
1229    
1230     case TAG_END: // end
1231     ipu1dma->tadr = ipu1dma->madr;
1232     return true;
1233     }
1234     return false;
1235     }
1236    
1237     extern void gsInterrupt();
1238     extern void vif1Interrupt();
1239    
1240     static __forceinline void ipuDmacSrcChain()
1241     {
1242    
1243     switch (IPU1Status.ChainMode)
1244     {
1245     case TAG_REFE: // refe
1246     //if(IPU1Status.InProgress == false) ipu1dma->tadr += 16;
1247     if(IPU1Status.DMAFinished == false) IPU1Status.DMAFinished = true;
1248     break;
1249     case TAG_CNT: // cnt
1250     // Set the taddr to the next tag
1251     ipu1dma->tadr = ipu1dma->madr;
1252     //if(IPU1Status.DMAFinished == false) IPU1Status.DMAFinished = false;
1253     break;
1254    
1255     case TAG_NEXT: // next
1256     ipu1dma->tadr = IPU1Status.NextMem;
1257     //if(IPU1Status.DMAFinished == false) IPU1Status.DMAFinished = false;
1258     break;
1259    
1260     case TAG_REF: // ref
1261     //if(IPU1Status.InProgress == false)ipu1dma->tadr += 16;
1262     //if(IPU1Status.DMAFinished == false) IPU1Status.DMAFinished = false;
1263     break;
1264    
1265     case TAG_END: // end
1266     ipu1dma->tadr = ipu1dma->madr;
1267     if(IPU1Status.DMAFinished == false) IPU1Status.DMAFinished = true;
1268     break;
1269     }
1270     }
1271    
1272     static __forceinline bool WaitGSPaths()
1273     {
1274     if(CHECK_IPUWAITHACK)
1275     {
1276     if(GSTransferStatus.PTH3 < STOPPED_MODE)
1277     {
1278     //GIF_LOG("Flushing gif chcr %x tadr %x madr %x qwc %x", gif->chcr._u32, gif->tadr, gif->madr, gif->qwc);
1279     //DevCon.WriteLn("Waiting for GIF");
1280     return false;
1281     }
1282    
1283     if(GSTransferStatus.PTH2 != STOPPED_MODE)
1284     {
1285     //DevCon.WriteLn("Waiting for VIF");
1286     return false;
1287     }
1288     if(GSTransferStatus.PTH1 != STOPPED_MODE)
1289     {
1290     //DevCon.WriteLn("Waiting for VU");
1291     return false;
1292     }
1293     }
1294     return true;
1295     }
1296    
1297     static __forceinline int IPU1chain() {
1298    
1299     int totalqwc = 0;
1300    
1301     if (ipu1dma->qwc > 0 && IPU1Status.InProgress == true)
1302     {
1303    
1304     int qwc = ipu1dma->qwc;
1305     u32 *pMem;
1306    
1307     pMem = (u32*)dmaGetAddr(ipu1dma->madr, false);
1308    
1309     if (pMem == NULL)
1310     {
1311     Console.Error("ipu1dma NULL!"); return totalqwc;
1312     }
1313    
1314     //Write our data to the fifo
1315     qwc = ipu_fifo.in.write(pMem, qwc);
1316     ipu1dma->madr += qwc << 4;
1317     ipu1dma->qwc -= qwc;
1318     totalqwc += qwc;
1319     }
1320     if( ipu1dma->qwc == 0)
1321     {
1322     //Update TADR etc
1323     if(IPU1Status.DMAMode == DMA_MODE_CHAIN) ipuDmacSrcChain();
1324     //If the transfer has finished or we have room in the FIFO, schedule to the interrupt code.
1325    
1326     //No data left
1327     IPU1Status.InProgress = false;
1328     } //If we still have data the commands should pull this across when need be.
1329    
1330     return totalqwc;
1331     }
1332    
1333     //static __forceinline bool WaitGSPaths()
1334     //{
1335     // //Wait for all GS paths to be clear
1336     // if (GSTransferStatus._u32 != 0x2a)
1337     // {
1338     // if(GSTransferStatus.PTH3 != STOPPED_MODE && vif1Regs->mskpath3) return true;
1339     // IPU_LOG("Waiting for GS transfers to finish %x", GSTransferStatus._u32);
1340     // IPU_INT_TO(4);
1341     // return false;
1342     // }
1343     // return true;
1344     //}
1345    
1346    
1347    
1348     int IPU1dma()
1349     {
1350     int ipu1cycles = 0;
1351     int totalqwc = 0;
1352    
1353     //We need to make sure GIF has flushed before sending IPU data, it seems to REALLY screw FFX videos
1354     //if(!WaitGSPaths()) return totalqwc;
1355    
1356     if(ipu1dma->chcr.STR == false || IPU1Status.DMAMode == 2)
1357     {
1358     //We MUST stop the IPU from trying to fill the FIFO with more data if the DMA has been suspended
1359     //if we don't, we risk causing the data to go out of sync with the fifo and we end up losing some!
1360     //This is true for Dragons Quest 8 and probably others which suspend the DMA.
1361     DevCon.Warning("IPU1 running when IPU1 DMA disabled! CHCR %x QWC %x", ipu1dma->chcr._u32, ipu1dma->qwc);
1362     return 0;
1363     }
1364    
1365     IPU_LOG("IPU1 DMA Called QWC %x Finished %d In Progress %d tadr %x", ipu1dma->qwc, IPU1Status.DMAFinished, IPU1Status.InProgress, ipu1dma->tadr);
1366    
1367     switch(IPU1Status.DMAMode)
1368     {
1369     case DMA_MODE_NORMAL:
1370     {
1371     if(!WaitGSPaths())
1372     { // legacy WaitGSPaths() for now
1373     IPU_INT_TO(4); //Give it a short wait.
1374     return totalqwc;
1375     }
1376     IPU_LOG("Processing Normal QWC left %x Finished %d In Progress %d", ipu1dma->qwc, IPU1Status.DMAFinished, IPU1Status.InProgress);
1377     if(IPU1Status.InProgress == true) totalqwc += IPU1chain();
1378     }
1379     break;
1380    
1381     case DMA_MODE_CHAIN:
1382     {
1383     if(IPU1Status.InProgress == true) //No transfer is ready to go so we need to set one up
1384     {
1385     if(!WaitGSPaths())
1386     { // legacy WaitGSPaths() for now
1387     IPU_INT_TO(4); //Give it a short wait.
1388     return totalqwc;
1389     }
1390     IPU_LOG("Processing Chain QWC left %x Finished %d In Progress %d", ipu1dma->qwc, IPU1Status.DMAFinished, IPU1Status.InProgress);
1391     totalqwc += IPU1chain();
1392     //Set the TADR forward
1393     }
1394    
1395    
1396     if(IPU1Status.InProgress == false && IPU1Status.DMAFinished == false) //No transfer is ready to go so we need to set one up
1397     {
1398     tDMA_TAG* ptag = dmaGetAddr(ipu1dma->tadr, false); //Set memory pointer to TADR
1399    
1400     if (!ipu1dma->transfer("IPU1", ptag))
1401     {
1402     return totalqwc;
1403     }
1404    
1405     ipu1cycles += 1; // Add 1 cycles from the QW read for the tag
1406     IPU1Status.ChainMode = ptag->ID;
1407    
1408     if(ipu1dma->chcr.TTE) DevCon.Warning("TTE?");
1409    
1410     switch (IPU1Status.ChainMode)
1411     {
1412     case TAG_REFE: // refe
1413     // do not change tadr
1414     //ipu1dma->tadr += 16;
1415     ipu1dma->tadr += 16;
1416     ipu1dma->madr = ptag[1]._u32;
1417     IPU_LOG("Tag should end on %x", ipu1dma->tadr);
1418    
1419     break;
1420    
1421     case TAG_CNT: // cnt
1422     ipu1dma->madr = ipu1dma->tadr + 16;
1423     IPU_LOG("Tag should end on %x", ipu1dma->madr + ipu1dma->qwc * 16);
1424     //ipu1dma->tadr = ipu1dma->madr + (ipu1dma->qwc * 16);
1425     // Set the taddr to the next tag
1426     //IPU1Status.DMAFinished = false;
1427     break;
1428    
1429     case TAG_NEXT: // next
1430     ipu1dma->madr = ipu1dma->tadr + 16;
1431     IPU1Status.NextMem = ptag[1]._u32;
1432     IPU_LOG("Tag should end on %x", IPU1Status.NextMem);
1433     //IPU1Status.DMAFinished = false;
1434     break;
1435    
1436     case TAG_REF: // ref
1437     ipu1dma->madr = ptag[1]._u32;
1438     ipu1dma->tadr += 16;
1439     IPU_LOG("Tag should end on %x", ipu1dma->tadr);
1440     //IPU1Status.DMAFinished = false;
1441     break;
1442    
1443     case TAG_END: // end
1444     // do not change tadr
1445     ipu1dma->madr = ipu1dma->tadr + 16;
1446     ipu1dma->tadr += 16;
1447     IPU_LOG("Tag should end on %x", ipu1dma->madr + ipu1dma->qwc * 16);
1448    
1449     break;
1450    
1451     default:
1452     Console.Error("IPU ERROR: different transfer mode!, Please report to PCSX2 Team");
1453     break;
1454     }
1455    
1456     //if(ipu1dma->qwc == 0) Console.Warning("Blank QWC!");
1457     if(ipu1dma->qwc > 0) IPU1Status.InProgress = true;
1458     IPU_LOG("dmaIPU1 dmaChain %8.8x_%8.8x size=%d, addr=%lx, fifosize=%x",
1459     ptag[1]._u32, ptag[0]._u32, ipu1dma->qwc, ipu1dma->madr, 8 - g_BP.IFC);
1460    
1461     if (ipu1dma->chcr.TIE && ptag->IRQ) //Tag Interrupt is set, so schedule the end/interrupt
1462     IPU1Status.DMAFinished = true;
1463    
1464    
1465     if(!WaitGSPaths() && ipu1dma->qwc > 0)
1466     { // legacy WaitGSPaths() for now
1467     IPU_INT_TO(4); //Give it a short wait.
1468     return totalqwc;
1469     }
1470     IPU_LOG("Processing Start Chain QWC left %x Finished %d In Progress %d", ipu1dma->qwc, IPU1Status.DMAFinished, IPU1Status.InProgress);
1471     totalqwc += IPU1chain();
1472     //Set the TADR forward
1473     }
1474    
1475     }
1476     break;
1477     }
1478    
1479     //Do this here to prevent double settings on Chain DMA's
1480     if(totalqwc > 0 || ipu1dma->qwc == 0)
1481     {
1482     IPU_INT_TO(totalqwc * BIAS);
1483     if(ipuRegs->ctrl.BUSY && g_BP.IFC) IPUWorker();
1484     }
1485     else
1486     {
1487     IPU_LOG("Here");
1488     cpuRegs.eCycle[4] = 0x9999;//IPU_INT_TO(2048);
1489     }
1490    
1491     IPU_LOG("Completed Call IPU1 DMA QWC Remaining %x Finished %d In Progress %d tadr %x", ipu1dma->qwc, IPU1Status.DMAFinished, IPU1Status.InProgress, ipu1dma->tadr);
1492     return totalqwc;
1493     }
1494    
1495     int IPU0dma()
1496     {
1497     int readsize;
1498     static int totalsize = 0;
1499     tDMA_TAG* pMem;
1500    
1501     if ((!(ipu0dma->chcr.STR) || (cpuRegs.interrupt & (1 << DMAC_FROM_IPU))) || (ipu0dma->qwc == 0))
1502     return 0;
1503    
1504     pxAssert(!(ipu0dma->chcr.TTE));
1505    
1506     IPU_LOG("dmaIPU0 chcr = %lx, madr = %lx, qwc = %lx",
1507     ipu0dma->chcr._u32, ipu0dma->madr, ipu0dma->qwc);
1508    
1509     pxAssert(ipu0dma->chcr.MOD == NORMAL_MODE);
1510    
1511     pMem = dmaGetAddr(ipu0dma->madr, true);
1512    
1513     readsize = min(ipu0dma->qwc, (u16)ipuRegs->ctrl.OFC);
1514     totalsize+=readsize;
1515     ipu_fifo.out.read(pMem, readsize);
1516    
1517     ipu0dma->madr += readsize << 4;
1518     ipu0dma->qwc -= readsize; // note: qwc is u16
1519    
1520     if (ipu0dma->qwc == 0)
1521     {
1522     if (dmacRegs->ctrl.STS == STS_fromIPU) // STS == fromIPU
1523     {
1524     dmacRegs->stadr.ADDR = ipu0dma->madr;
1525     switch (dmacRegs->ctrl.STD)
1526     {
1527     case NO_STD:
1528     break;
1529     case STD_GIF: // GIF
1530     Console.Warning("GIFSTALL");
1531     g_nDMATransfer.GIFSTALL = true;
1532     break;
1533     case STD_VIF1: // VIF
1534     Console.Warning("VIFSTALL");
1535     g_nDMATransfer.VIFSTALL = true;
1536     break;
1537     case STD_SIF1:
1538     Console.Warning("SIFSTALL");
1539     g_nDMATransfer.SIFSTALL = true;
1540     break;
1541     }
1542     }
1543     //Fixme ( voodoocycles ):
1544     //This was IPU_INT_FROM(readsize*BIAS );
1545     //This broke vids in Digital Devil Saga
1546     //Note that interrupting based on totalsize is just guessing..
1547     IPU_INT_FROM( readsize * BIAS );
1548     totalsize = 0;
1549     }
1550    
1551     return readsize;
1552     }
1553    
1554     __forceinline void dmaIPU0() // fromIPU
1555     {
1556     if (ipu0dma->pad != 0)
1557     {
1558     // Note: pad is the padding right above qwc, so we're testing whether qwc
1559     // has overflowed into pad.
1560     DevCon.Warning(L"IPU0dma's upper 16 bits set to %x\n", ipu0dma->pad);
1561     ipu0dma->qwc = ipu0dma->pad = 0;
1562     //If we are going to clear down IPU0, we should end it too. Going to test this scenario on the PS2 mind - Refraction
1563     ipu0dma->chcr.STR = false;
1564     hwDmacIrq(DMAC_FROM_IPU);
1565     }
1566     if (ipuRegs->ctrl.BUSY) IPUWorker();
1567     }
1568    
1569     __forceinline void dmaIPU1() // toIPU
1570     {
1571     IPU_LOG("IPU1DMAStart QWC %x, MADR %x, CHCR %x, TADR %x", ipu1dma->qwc, ipu1dma->madr, ipu1dma->chcr._u32, ipu1dma->tadr);
1572    
1573     if (ipu1dma->pad != 0)
1574     {
1575     // Note: pad is the padding right above qwc, so we're testing whether qwc
1576     // has overflowed into pad.
1577     DevCon.Warning(L"IPU1dma's upper 16 bits set to %x\n", ipu1dma->pad);
1578     ipu1dma->qwc = ipu1dma->pad = 0;
1579     // If we are going to clear down IPU1, we should end it too.
1580     // Going to test this scenario on the PS2 mind - Refraction
1581     ipu1dma->chcr.STR = false;
1582     hwDmacIrq(DMAC_TO_IPU);
1583     }
1584    
1585     if (ipu1dma->chcr.MOD == CHAIN_MODE) //Chain Mode
1586     {
1587     IPU_LOG("Setting up IPU1 Chain mode");
1588     if(ipu1dma->qwc == 0)
1589     {
1590     IPU1Status.InProgress = false;
1591     IPU1Status.DMAFinished = false;
1592     }
1593     else
1594     { //Attempting to continue a previous chain
1595     IPU_LOG("Resuming DMA TAG %x", (ipu1dma->chcr.TAG >> 12));
1596     //We MUST check the CHCR for the tag it last knew, it can be manipulated!
1597     IPU1Status.ChainMode = (ipu1dma->chcr.TAG >> 12) & 0x7;
1598     IPU1Status.InProgress = true;
1599     IPU1Status.DMAFinished = ((ipu1dma->chcr.TAG >> 15) && ipu1dma->chcr.TIE) ? true : false;
1600     }
1601    
1602     IPU1Status.DMAMode = DMA_MODE_CHAIN;
1603     IPU1dma();
1604     //if (ipuRegs->ctrl.BUSY) IPUWorker();
1605     }
1606     else //Normal Mode
1607     {
1608     if(ipu1dma->qwc == 0)
1609     {
1610     ipu1dma->chcr.STR = false;
1611     // Hack to force stop IPU
1612     ipuRegs->cmd.BUSY = 0;
1613     ipuRegs->ctrl.BUSY = 0;
1614     ipuRegs->topbusy = 0;
1615     //
1616     hwDmacIrq(DMAC_TO_IPU);
1617     Console.Warning("IPU1 Normal error!");
1618     }
1619     else
1620     {
1621     IPU_LOG("Setting up IPU1 Normal mode");
1622     IPU1Status.InProgress = true;
1623     IPU1Status.DMAFinished = true;
1624     IPU1Status.DMAMode = DMA_MODE_NORMAL;
1625     IPU1dma();
1626     //if (ipuRegs->ctrl.BUSY) IPUWorker();
1627     }
1628     }
1629     }
1630    
1631     extern void GIFdma();
1632    
1633     void ipu0Interrupt()
1634     {
1635     IPU_LOG("ipu0Interrupt: %x", cpuRegs.cycle);
1636    
1637     if (g_nDMATransfer.FIREINT0)
1638     {
1639     g_nDMATransfer.FIREINT0 = false;
1640     hwIntcIrq(INTC_IPU);
1641     }
1642    
1643     if (g_nDMATransfer.GIFSTALL)
1644     {
1645     // gif
1646     Console.Warning("IPU GIF Stall");
1647     g_nDMATransfer.GIFSTALL = false;
1648     //if (gif->chcr.STR) GIFdma();
1649     }
1650    
1651     if (g_nDMATransfer.VIFSTALL)
1652     {
1653     // vif
1654     Console.Warning("IPU VIF Stall");
1655     g_nDMATransfer.VIFSTALL = false;
1656     //if (vif1ch->chcr.STR) dmaVIF1();
1657     }
1658    
1659     if (g_nDMATransfer.SIFSTALL)
1660     {
1661     // sif
1662     Console.Warning("IPU SIF Stall");
1663     g_nDMATransfer.SIFSTALL = false;
1664    
1665     // Not totally sure whether this needs to be done or not, so I'm
1666     // leaving it commented out for the moment.
1667     //if (sif1dma->chcr.STR) SIF1Dma();
1668     }
1669    
1670     if (g_nDMATransfer.TIE0)
1671     {
1672     g_nDMATransfer.TIE0 = false;
1673     }
1674    
1675     ipu0dma->chcr.STR = false;
1676     hwDmacIrq(DMAC_FROM_IPU);
1677     }
1678    
1679     IPU_FORCEINLINE void ipu1Interrupt()
1680     {
1681     IPU_LOG("ipu1Interrupt %x:", cpuRegs.cycle);
1682    
1683     if(IPU1Status.DMAFinished == false || IPU1Status.InProgress == true) //Sanity Check
1684     {
1685     IPU1dma();
1686     return;
1687     }
1688    
1689     IPU_LOG("ipu1 finish %x:", cpuRegs.cycle);
1690     ipu1dma->chcr.STR = false;
1691     IPU1Status.DMAMode = 2;
1692     hwDmacIrq(DMAC_TO_IPU);
1693     }

  ViewVC Help
Powered by ViewVC 1.1.22