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

Contents of /trunk/pcsx2/IPU/IPU.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 31 - (show 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 /* 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