1 /* Mednafen - Multi-system Emulator
2  *
3  * This program is free software; you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License as published by
5  * the Free Software Foundation; either version 2 of the License, or
6  * (at your option) any later version.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16  */
17 
18 /*
19  MDEC_READ_FIFO(tfr) vs InCounter vs MDEC_DMACanRead() is a bit fragile right now.  Actually, the entire horrible state machine monstrosity is fragile.
20 
21  TODO: OutFIFOReady, so <16bpp works right.
22 
23  TODO CODE:
24 
25   bool InFIFOReady;
26 
27   if(InFIFO.CanWrite())
28   {
29    InFIFO.Write(V);
30 
31    if(InCommand)
32    {
33     if(InCounter != 0xFFFF)
34     {
35      InCounter--;
36 
37      // This condition when InFIFO.CanWrite() != 0 is a bit buggy on real hardware, decoding loop *seems* to be reading too
38      // much and pulling garbage from the FIFO.
39      if(InCounter == 0xFFFF)
40       InFIFOReady = true;
41     }
42 
43     if(InFIFO.CanWrite() == 0)
44      InFIFOReady = true;
45    }
46   }
47 */
48 
49 // Good test-case games:
50 //	Dragon Knight 4(bad disc?)
51 //	Final Fantasy 7 intro movie.
52 //	GameShark Version 4.0 intro movie; (clever) abuse of DMA channel 0.
53 //	SimCity 2000 startup.
54 
55 
56 #include "psx.h"
57 #include "mdec.h"
58 
59 #include "../masmem.h"
60 #include "../math_ops.h"
61 #include "../state_helpers.h"
62 
63 #include "FastFIFO.h"
64 #include <math.h>
65 
66 #if defined(__SSE2__)
67 #include <xmmintrin.h>
68 #include <emmintrin.h>
69 #endif
70 
71 #if defined(ARCH_POWERPC_ALTIVEC) && defined(HAVE_ALTIVEC_H)
72  #include <altivec.h>
73 #endif
74 
75 static int32 ClockCounter;
76 static unsigned MDRPhase;
77 static FastFIFO<uint32, 0x20> InFIFO;
78 static FastFIFO<uint32, 0x20> OutFIFO;
79 
80 static int8 block_y[8][8];
81 static int8 block_cb[8][8];	// [y >> 1][x >> 1]
82 static int8 block_cr[8][8];	// [y >> 1][x >> 1]
83 
84 static uint32 Control;
85 static uint32 Command;
86 static bool InCommand;
87 
88 static uint8 QMatrix[2][64];
89 static uint32 QMIndex;
90 
91 MDFN_ALIGN(16) static int16 IDCTMatrix[64];
92 static uint32 IDCTMIndex;
93 
94 static uint8 QScale;
95 
96 MDFN_ALIGN(16) static int16 Coeff[64];
97 static uint32 CoeffIndex;
98 static uint32 DecodeWB;
99 
100 static union
101 {
102  uint32 pix32[48];
103  uint16 pix16[96];
104  uint8   pix8[192];
105 } PixelBuffer;
106 static uint32 PixelBufferReadOffset;
107 static uint32 PixelBufferCount32;
108 
109 static uint16 InCounter;
110 
111 static uint8 RAMOffsetY;
112 static uint8 RAMOffsetCounter;
113 static uint8 RAMOffsetWWS;
114 
115 static const uint8 ZigZag[64] =
116 {
117  0x00, 0x08, 0x01, 0x02, 0x09, 0x10, 0x18, 0x11,
118  0x0a, 0x03, 0x04, 0x0b, 0x12, 0x19, 0x20, 0x28,
119  0x21, 0x1a, 0x13, 0x0c, 0x05, 0x06, 0x0d, 0x14,
120  0x1b, 0x22, 0x29, 0x30, 0x38, 0x31, 0x2a, 0x23,
121  0x1c, 0x15, 0x0e, 0x07, 0x0f, 0x16, 0x1d, 0x24,
122  0x2b, 0x32, 0x39, 0x3a, 0x33, 0x2c, 0x25, 0x1e,
123  0x17, 0x1f, 0x26, 0x2d, 0x34, 0x3b, 0x3c, 0x35,
124  0x2e, 0x27, 0x2f, 0x36, 0x3d, 0x3e, 0x37, 0x3f,
125 };
126 
MDEC_Power(void)127 void MDEC_Power(void)
128 {
129    ClockCounter = 0;
130    MDRPhase = 0;
131 
132    InFIFO.Flush();
133    OutFIFO.Flush();
134 
135    memset(block_y, 0, sizeof(block_y));
136    memset(block_cb, 0, sizeof(block_cb));
137    memset(block_cr, 0, sizeof(block_cr));
138 
139    Control = 0;
140    Command = 0;
141    InCommand = false;
142 
143    memset(QMatrix, 0, sizeof(QMatrix));
144    QMIndex = 0;
145 
146    memset(IDCTMatrix, 0, sizeof(IDCTMatrix));
147    IDCTMIndex = 0;
148 
149    QScale = 0;
150 
151    memset(Coeff, 0, sizeof(Coeff));
152    CoeffIndex = 0;
153    DecodeWB = 0;
154 
155    memset(PixelBuffer.pix32, 0, sizeof(PixelBuffer.pix32));
156    PixelBufferReadOffset = 0;
157    PixelBufferCount32 = 0;
158 
159    InCounter = 0;
160 
161    RAMOffsetY = 0;
162    RAMOffsetCounter = 0;
163    RAMOffsetWWS = 0;
164 }
165 
MDEC_StateAction(StateMem * sm,int load,int data_only)166 int MDEC_StateAction(StateMem *sm, int load, int data_only)
167 {
168    SFORMAT StateRegs[] =
169    {
170       SFVAR(ClockCounter),
171       SFVAR(MDRPhase),
172 
173 #define SFFIFO32(fifoobj)  SFARRAY32(&fifoobj.data[0], sizeof(fifoobj.data) / sizeof(fifoobj.data[0])),	\
174       SFVAR(fifoobj.read_pos),				\
175       SFVAR(fifoobj.write_pos),				\
176       SFVAR(fifoobj.in_count)
177 
178       SFFIFO32(InFIFO),
179       SFFIFO32(OutFIFO),
180 #undef SFFIFO
181 
182       SFARRAY(&block_y[0][0], sizeof(block_y) / sizeof(block_y[0][0])),
183       SFARRAY(&block_cb[0][0], sizeof(block_cb) / sizeof(block_cb[0][0])),
184       SFARRAY(&block_cr[0][0], sizeof(block_cr) / sizeof(block_cr[0][0])),
185 
186       SFVAR(Control),
187       SFVAR(Command),
188       SFVAR(InCommand),
189 
190       SFARRAY(&QMatrix[0][0], sizeof(QMatrix) / sizeof(QMatrix[0][0])),
191       SFVAR(QMIndex),
192 
193       SFARRAY16(&IDCTMatrix[0], sizeof(IDCTMatrix) / sizeof(IDCTMatrix[0])),
194       SFVAR(IDCTMIndex),
195 
196       SFVAR(QScale),
197 
198       SFARRAY16(&Coeff[0], sizeof(Coeff) / sizeof(Coeff[0])),
199       SFVAR(CoeffIndex),
200       SFVAR(DecodeWB),
201 
202       SFARRAY32(&PixelBuffer.pix32[0], sizeof(PixelBuffer.pix32) / sizeof(PixelBuffer.pix32[0])),
203       SFVAR(PixelBufferReadOffset),
204       SFVAR(PixelBufferCount32),
205 
206       SFVAR(InCounter),
207 
208       SFVAR(RAMOffsetY),
209       SFVAR(RAMOffsetCounter),
210       SFVAR(RAMOffsetWWS),
211 
212       SFEND
213    };
214 
215    int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, "MDEC");
216 
217    if(load)
218    {
219       InFIFO.SaveStatePostLoad();
220       OutFIFO.SaveStatePostLoad();
221 
222       PixelBufferCount32 %= (sizeof(PixelBuffer.pix32) / sizeof(PixelBuffer.pix32[0])) + 1;
223    }
224 
225    return(ret);
226 }
227 
Mask9ClampS8(int32 v)228 static INLINE int8 Mask9ClampS8(int32 v)
229 {
230    v = sign_x_to_s32(9, v);
231 
232    if(v < -128)
233       v = -128;
234 
235    if(v > 127)
236       v = 127;
237 
238    return v;
239 }
240 
241 template<typename T>
IDCT_1D_Multi(int16 * in_coeff,T * out_coeff)242 static void IDCT_1D_Multi(int16 *in_coeff, T *out_coeff)
243 {
244    unsigned col, x;
245 
246    for(col = 0; col < 8; col++)
247    {
248 #if defined(__SSE2__)
249       __m128i c =  _mm_load_si128((__m128i *)&in_coeff[(col * 8)]);
250 #endif
251 
252       for( x = 0; x < 8; x++)
253       {
254 #ifdef __SSE2__
255          MDFN_ALIGN(16) int32 tmp[4];
256          __m128i m   = _mm_load_si128((__m128i *)&IDCTMatrix[(x * 8)]);
257          __m128i sum = _mm_madd_epi16(m, c);
258          sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, (3 << 0) | (2 << 2) | (1 << 4) | (0 << 6)));
259          sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, (1 << 0) | (0 << 2)));
260 
261          _mm_store_si128((__m128i*)tmp, sum);
262 
263          if(sizeof(T) == 1)
264             out_coeff[(col * 8) + x] = Mask9ClampS8((tmp[0] + 0x4000) >> 15);
265          else
266             out_coeff[(x * 8) + col] = (tmp[0] + 0x4000) >> 15;
267 #else
268          int32 sum = 0;
269          unsigned u;
270 
271          for(u = 0; u < 8; u++)
272             sum += (in_coeff[(col * 8) + u] * IDCTMatrix[(x * 8) + u]);
273 
274          if(sizeof(T) == 1)
275             out_coeff[(col * 8) + x] = Mask9ClampS8((sum + 0x4000) >> 15);
276          else
277             out_coeff[(x * 8) + col] = (sum + 0x4000) >> 15;
278 #endif
279       }
280    }
281 }
282 
IDCT(int16 * in_coeff,int8 * out_coeff)283 static void IDCT(int16 *in_coeff, int8 *out_coeff)
284 {
285    MDFN_ALIGN(16) int16 tmpbuf[64];
286 
287    IDCT_1D_Multi<int16>(in_coeff, tmpbuf);
288    IDCT_1D_Multi<int8>(tmpbuf, out_coeff);
289 }
290 
YCbCr_to_RGB(const int8 y,const int8 cb,const int8 cr,int & r,int & g,int & b)291 static INLINE void YCbCr_to_RGB(const int8 y, const int8 cb, const int8 cr, int &r, int &g, int &b)
292 {
293    // The formula for green is still a bit off(precision/rounding issues when both cb and cr are non-zero).
294    r = Mask9ClampS8(y + (((359 * cr) + 0x80) >> 8));
295    //g = Mask9ClampS8(y + (((-88 * cb) + (-183 * cr) + 0x80) >> 8));
296    g = Mask9ClampS8(y + ((((-88 * cb) &~ 0x1F) + ((-183 * cr) &~ 0x07) + 0x80) >> 8));
297    b = Mask9ClampS8(y + (((454 * cb) + 0x80) >> 8));
298 
299    r ^= 0x80;
300    g ^= 0x80;
301    b ^= 0x80;
302 }
303 
RGB_to_RGB555(uint8 r,uint8 g,uint8 b)304 static INLINE uint16 RGB_to_RGB555(uint8 r, uint8 g, uint8 b)
305 {
306    r = (r + 4) >> 3;
307    g = (g + 4) >> 3;
308    b = (b + 4) >> 3;
309 
310    if(r > 0x1F)
311       r = 0x1F;
312 
313    if(g > 0x1F)
314       g = 0x1F;
315 
316    if(b > 0x1F)
317       b = 0x1F;
318 
319    return((r << 0) | (g << 5) | (b << 10));
320 }
321 
EncodeImage(const unsigned ybn)322 static void EncodeImage(const unsigned ybn)
323 {
324    //printf("ENCODE, %d\n", (Command & 0x08000000) ? 256 : 384);
325 
326    PixelBufferCount32 = 0;
327 
328    switch((Command >> 27) & 0x3)
329    {
330       case 0:	// 4bpp
331          {
332             const uint8 us_xor = (Command & (1U << 26)) ? 0x00 : 0x88;
333             uint8* pix_out = PixelBuffer.pix8;
334 
335             for(int y = 0; y < 8; y++)
336             {
337                for(int x = 0; x < 8; x += 2)
338                {
339                   uint8 p0 = std::min<int>(127, block_y[y][x + 0] + 8);
340                   uint8 p1 = std::min<int>(127, block_y[y][x + 1] + 8);
341 
342                   *pix_out = ((p0 >> 4) | (p1 & 0xF0)) ^ us_xor;
343                   pix_out++;
344                }
345             }
346             PixelBufferCount32 = 8;
347          }
348          break;
349 
350 
351       case 1:	// 8bpp
352          {
353             const uint8 us_xor = (Command & (1U << 26)) ? 0x00 : 0x80;
354             uint8* pix_out = PixelBuffer.pix8;
355 
356             for(int y = 0; y < 8; y++)
357             {
358                for(int x = 0; x < 8; x++)
359                {
360                   *pix_out = (uint8)block_y[y][x] ^ us_xor;
361                   pix_out++;
362                }
363             }
364             PixelBufferCount32 = 16;
365          }
366          break;
367 
368       case 2:	// 24bpp
369          {
370             const uint8 rgb_xor = (Command & (1U << 26)) ? 0x80 : 0x00;
371             uint8* pix_out = PixelBuffer.pix8;
372 
373             for(int y = 0; y < 8; y++)
374             {
375                const int8* by = &block_y[y][0];
376                const int8* cb = &block_cb[(y >> 1) | ((ybn & 2) << 1)][(ybn & 1) << 2];
377                const int8* cr = &block_cr[(y >> 1) | ((ybn & 2) << 1)][(ybn & 1) << 2];
378 
379                for(int x = 0; x < 8; x++)
380                {
381                   int r, g, b;
382 
383                   YCbCr_to_RGB(by[x], cb[x >> 1], cr[x >> 1], r, g, b);
384 
385                   pix_out[0] = r ^ rgb_xor;
386                   pix_out[1] = g ^ rgb_xor;
387                   pix_out[2] = b ^ rgb_xor;
388                   pix_out += 3;
389                }
390             }
391             PixelBufferCount32 = 48;
392          }
393          break;
394 
395       case 3:	// 16bpp
396          {
397             uint16 pixel_xor = ((Command & 0x02000000) ? 0x8000 : 0x0000) | ((Command & (1U << 26)) ? 0x4210 : 0x0000);
398             uint16* pix_out = PixelBuffer.pix16;
399 
400             for(int y = 0; y < 8; y++)
401             {
402                const int8* by = &block_y[y][0];
403                const int8* cb = &block_cb[(y >> 1) | ((ybn & 2) << 1)][(ybn & 1) << 2];
404                const int8* cr = &block_cr[(y >> 1) | ((ybn & 2) << 1)][(ybn & 1) << 2];
405 
406                for(int x = 0; x < 8; x++)
407                {
408                   int r, g, b;
409 
410                   YCbCr_to_RGB(by[x], cb[x >> 1], cr[x >> 1], r, g, b);
411 
412                   StoreU16_LE(pix_out, pixel_xor ^ RGB_to_RGB555(r, g, b));
413                   pix_out++;
414                }
415             }
416             PixelBufferCount32 = 32;
417          }
418          break;
419 
420    }
421 }
422 
WriteImageData(uint16 V,int32 * eat_cycles)423 static INLINE void WriteImageData(uint16 V, int32* eat_cycles)
424 {
425    const uint32 qmw = (bool)(DecodeWB < 2);
426 
427    //printf("MDEC DMA SubWrite: %04x, %d\n", V, CoeffIndex);
428 
429    if(!CoeffIndex)
430    {
431       if(V == 0xFE00)
432       {
433          //printf("FE00 @ %u\n", DecodeWB);
434          return;
435       }
436 
437       QScale = V >> 10;
438 
439       {
440          int q = QMatrix[qmw][0];	// No QScale here!
441          int ci = sign_10_to_s16(V & 0x3FF);
442          int tmp;
443 
444          if(q != 0)
445             tmp = (int32)((uint32)(ci * q) << 4) + (ci ? ((ci < 0) ? 8 : -8) : 0);
446          else
447             tmp = (uint32)(ci * 2) << 4;
448 
449          // Not sure if it should be 0x3FFF or 0x3FF0 or maybe 0x3FF8?
450          Coeff[ZigZag[0]] = std::min<int>(0x3FFF, std::max<int>(-0x4000, tmp));
451          CoeffIndex++;
452       }
453    }
454    else
455    {
456       if(V == 0xFE00)
457       {
458          while(CoeffIndex < 64)
459             Coeff[ZigZag[CoeffIndex++]] = 0;
460       }
461       else
462       {
463          uint32 rlcount = V >> 10;
464 
465          for(uint32 i = 0; i < rlcount && CoeffIndex < 64; i++)
466          {
467             Coeff[ZigZag[CoeffIndex]] = 0;
468             CoeffIndex++;
469          }
470 
471          if(CoeffIndex < 64)
472          {
473             int q = QScale * QMatrix[qmw][CoeffIndex];
474             int ci = sign_10_to_s16(V & 0x3FF);
475             int tmp;
476 
477             if(q != 0)
478                tmp = (int32)((uint32)((ci * q) >> 3) << 4) + (ci ? ((ci < 0) ? 8 : -8) : 0);
479             else
480                tmp = (uint32)(ci * 2) << 4;
481 
482             // Not sure if it should be 0x3FFF or 0x3FF0 or maybe 0x3FF8?
483             Coeff[ZigZag[CoeffIndex]] = std::min<int>(0x3FFF, std::max<int>(-0x4000, tmp));
484             CoeffIndex++;
485          }
486       }
487    }
488 
489    if(CoeffIndex == 64)
490    {
491       CoeffIndex = 0;
492 
493       //printf("Block %d finished\n", DecodeWB);
494 
495       switch(DecodeWB)
496       {
497          case 0:
498             IDCT(Coeff, &block_cr[0][0]);
499             break;
500          case 1:
501             IDCT(Coeff, &block_cb[0][0]);
502             break;
503          case 2:
504          case 3:
505          case 4:
506          case 5:
507             IDCT(Coeff, &block_y[0][0]);
508             break;
509       }
510 
511       // Timing in the actual PS1 MDEC is complex due to (apparent) pipelining, but the average when decoding a large number of blocks is
512       // about 512.
513       *eat_cycles += 512;
514 
515       if(DecodeWB >= 2)
516          EncodeImage((DecodeWB + 4) % 6);
517 
518       DecodeWB++;
519       if(DecodeWB == (((Command >> 27) & 2) ? 6 : 3))
520          DecodeWB = ((Command >> 27) & 2) ? 0 : 2;
521    }
522 }
523 
MDEC_Run(int32 clocks)524 void MDEC_Run(int32 clocks)
525 {
526    static const unsigned MDRPhaseBias = 0 + 1;
527 
528    ClockCounter += clocks;
529 
530    if(ClockCounter > 128)
531    {
532       //if(MDRPhase != 0)
533       // printf("SNORT: %d\n", ClockCounter);
534       ClockCounter = 128;
535    }
536 
537    switch(MDRPhase + MDRPhaseBias)
538    {
539       for(;;)
540       {
541          InCommand = false;
542          { { case 1: if(!(InFIFO.in_count)) { MDRPhase = 2 - MDRPhaseBias - 1; return; } }; Command = InFIFO.Read(); };
543          InCommand = true;
544          { ClockCounter -= (1); { case 3: if(!(ClockCounter > 0)) { MDRPhase = 4 - MDRPhaseBias - 1; return; } }; };
545 
546          //printf("****************** Command: %08x, %02x\n", Command, Command >> 29);
547 
548          //
549          //
550          //
551          if(((Command >> 29) & 0x7) == 1)
552          {
553             InCounter = Command & 0xFFFF;
554             OutFIFO.Flush();
555             //OutBuffer.Flush();
556 
557             PixelBufferCount32 = 0;
558             CoeffIndex = 0;
559 
560             if((Command >> 27) & 2)
561                DecodeWB = 0;
562             else
563                DecodeWB = 2;
564 
565             switch((Command >> 27) & 0x3)
566             {
567                case 0:
568                case 1: RAMOffsetWWS = 0; break;
569                case 2: RAMOffsetWWS = 6; break;
570                case 3: RAMOffsetWWS = 4; break;
571             }
572             RAMOffsetY = 0;
573             RAMOffsetCounter = RAMOffsetWWS;
574 
575             InCounter--;
576             do
577             {
578                uint32 tfr;
579                int32 need_eat; // = 0;
580 
581                { { case 5: if(!(InFIFO.in_count)) { MDRPhase = 6 - MDRPhaseBias - 1; return; } }; tfr = InFIFO.Read(); };
582                InCounter--;
583 
584                //     printf("KA: %04x %08x\n", InCounter, tfr);
585 
586                need_eat = 0;
587                PixelBufferCount32 = 0;
588                WriteImageData(tfr, &need_eat);
589                WriteImageData(tfr >> 16, &need_eat);
590 
591                { ClockCounter -= (need_eat); { case 7: if(!(ClockCounter > 0)) { MDRPhase = 8 - MDRPhaseBias - 1; return; } }; };
592 
593                PixelBufferReadOffset = 0;
594 
595                while(PixelBufferReadOffset < PixelBufferCount32)
596                {
597                   { { case 9: if(!(OutFIFO.CanWrite())) { MDRPhase = 10 - MDRPhaseBias - 1; return; } }; OutFIFO.Write(LoadU32_LE(&PixelBuffer.pix32[PixelBufferReadOffset++])); };
598                }
599             } while(InCounter != 0xFFFF);
600          }
601          //
602          //
603          //
604          else if(((Command >> 29) & 0x7) == 2)
605          {
606             QMIndex = 0;
607             InCounter = 0x10 + ((Command & 0x1) ? 0x10 : 0x00);
608 
609             InCounter--;
610             do
611             {
612                uint32 tfr;
613 
614                { { case 11: if(!(InFIFO.in_count)) { MDRPhase = 12 - MDRPhaseBias - 1; return; } }; tfr = InFIFO.Read(); };
615                InCounter--;
616 
617                //printf("KA: %04x %08x\n", InCounter, tfr);
618 
619                for(int i = 0; i < 4; i++)
620                {
621                   QMatrix[QMIndex >> 6][QMIndex & 0x3F] = (uint8)tfr;
622                   QMIndex = (QMIndex + 1) & 0x7F;
623                   tfr >>= 8;
624                }
625             } while(InCounter != 0xFFFF);
626          }
627          //
628          //
629          //
630          else if(((Command >> 29) & 0x7) == 3)
631          {
632             IDCTMIndex = 0;
633             InCounter = 0x20;
634 
635             InCounter--;
636             do
637             {
638                uint32 tfr;
639 
640                { { case 13: if(!(InFIFO.in_count)) { MDRPhase = 14 - MDRPhaseBias - 1; return; } }; tfr = InFIFO.Read(); };
641                InCounter--;
642 
643                for(unsigned i = 0; i < 2; i++)
644                {
645                   IDCTMatrix[((IDCTMIndex & 0x7) << 3) | ((IDCTMIndex >> 3) & 0x7)] = (int16)(tfr & 0xFFFF) >> 3;
646                   IDCTMIndex = (IDCTMIndex + 1) & 0x3F;
647 
648                   tfr >>= 16;
649                }
650             } while(InCounter != 0xFFFF);
651          }
652          else
653          {
654             InCounter = Command & 0xFFFF;
655          }
656       } // end for(;;)
657    }
658 }
659 
MDEC_DMAWrite(uint32 V)660 void MDEC_DMAWrite(uint32 V)
661 {
662    if(!InFIFO.CanWrite())
663       return;
664 
665    InFIFO.Write(V);
666    MDEC_Run(0);
667 }
668 
MDEC_DMARead(uint32 * offs)669 uint32 MDEC_DMARead(uint32* offs)
670 {
671    uint32 V = 0;
672 
673    *offs = 0;
674 
675    if(MDFN_LIKELY(OutFIFO.in_count))
676    {
677       V = OutFIFO.Read();
678 
679       *offs = (RAMOffsetY & 0x7) * RAMOffsetWWS;
680 
681       if(RAMOffsetY & 0x08)
682       {
683          *offs = (*offs - RAMOffsetWWS*7);
684       }
685 
686       RAMOffsetCounter--;
687       if(!RAMOffsetCounter)
688       {
689          RAMOffsetCounter = RAMOffsetWWS;
690          RAMOffsetY++;
691       }
692 
693       MDEC_Run(0);
694    }
695 
696    return(V);
697 }
698 
MDEC_DMACanWrite(void)699 bool MDEC_DMACanWrite(void)
700 {
701  return((InFIFO.CanWrite() >= 0x20) && (Control & (1U << 30)) && InCommand && InCounter != 0xFFFF);
702 }
703 
MDEC_DMACanRead(void)704 bool MDEC_DMACanRead(void)
705 {
706  return((OutFIFO.in_count >= 0x20) && (Control & (1U << 29)));
707 }
708 
MDEC_Write(const int32_t timestamp,uint32 A,uint32 V)709 void MDEC_Write(const int32_t timestamp, uint32 A, uint32 V)
710 {
711    //PSX_WARNING("[MDEC] Write: 0x%08x 0x%08x, %d  --- %u %u", A, V, timestamp, InFIFO.in_count, OutFIFO.in_count);
712    if(A & 4)
713    {
714       if(V & 0x80000000) // Reset?
715       {
716          MDRPhase = 0;
717          InCounter = 0;
718          Command = 0;
719          InCommand = false;
720 
721          PixelBufferCount32 = 0;
722          ClockCounter = 0;
723          QMIndex = 0;
724          IDCTMIndex = 0;
725 
726          QScale = 0;
727 
728          memset(Coeff, 0, sizeof(Coeff));
729          CoeffIndex = 0;
730          DecodeWB = 0;
731 
732          InFIFO.Flush();
733          OutFIFO.Flush();
734       }
735       Control = V & 0x7FFFFFFF;
736    }
737    else
738    {
739       if(InFIFO.CanWrite())
740       {
741          InFIFO.Write(V);
742 
743          if(!InCommand)
744          {
745             if(ClockCounter < 1)
746                ClockCounter = 1;
747          }
748          MDEC_Run(0);
749       }
750    }
751 }
752 
MDEC_Read(const int32_t timestamp,uint32 A)753 uint32 MDEC_Read(const int32_t timestamp, uint32 A)
754 {
755  uint32 ret = 0;
756 
757  if(A & 4)
758  {
759   ret = 0;
760 
761   ret |= (OutFIFO.in_count == 0) << 31;
762   ret |= (InFIFO.CanWrite() == 0) << 30;
763   ret |= InCommand << 29;
764 
765   ret |= MDEC_DMACanWrite() << 28;
766   ret |= MDEC_DMACanRead() << 27;
767 
768   ret |= ((Command >> 25) & 0xF) << 23;
769 
770   // Needs refactoring elsewhere to work right: ret |= ((DecodeWB + 4) % 6) << 16;
771 
772   ret |= InCounter & 0xFFFF;
773  }
774  else
775  {
776   if(OutFIFO.in_count)
777    ret = OutFIFO.Read();
778  }
779 
780  //PSX_WARNING("[MDEC] Read: 0x%08x 0x%08x -- %d %d", A, ret, InputBuffer.in_count, InCounter);
781 
782  return(ret);
783 }
784