dfxvideo: make fskip update on vsync too
[pcsx_rearmed.git] / plugins / dfxvideo / gpu.c
1 /***************************************************************************
2                           gpu.c  -  description
3                              -------------------
4     begin                : Sun Oct 28 2001
5     copyright            : (C) 2001 by Pete Bernert
6     email                : BlackDove@addcom.de
7  ***************************************************************************/
8 /***************************************************************************
9  *                                                                         *
10  *   This program is free software; you can redistribute it and/or modify  *
11  *   it under the terms of the GNU General Public License as published by  *
12  *   the Free Software Foundation; either version 2 of the License, or     *
13  *   (at your option) any later version. See also the license.txt file for *
14  *   additional informations.                                              *
15  *                                                                         *
16  ***************************************************************************/
17
18 #include "gpu.h"
19 #include "stdint.h"
20 #include "psemu_plugin_defs.h"
21
22 ////////////////////////////////////////////////////////////////////////
23 // memory image of the PSX vram 
24 ////////////////////////////////////////////////////////////////////////
25
26 unsigned char  *psxVSecure;
27 unsigned char  *psxVub;
28 signed   char  *psxVsb;
29 unsigned short *psxVuw;
30 unsigned short *psxVuw_eom;
31 signed   short *psxVsw;
32 uint32_t *psxVul;
33 int32_t  *psxVsl;
34
35 ////////////////////////////////////////////////////////////////////////
36 // GPU globals
37 ////////////////////////////////////////////////////////////////////////
38
39 static long       lGPUdataRet;
40 long              lGPUstatusRet;
41 uint32_t          ulStatusControl[256];
42
43 static uint32_t gpuDataM[256];
44 static unsigned   char gpuCommand = 0;
45 static long       gpuDataC = 0;
46 static long       gpuDataP = 0;
47
48 VRAMLoad_t        VRAMWrite;
49 VRAMLoad_t        VRAMRead;
50 DATAREGISTERMODES DataWriteMode;
51 DATAREGISTERMODES DataReadMode;
52
53 BOOL              bSkipNextFrame = FALSE;
54 BOOL              fskip_frameReady;
55 DWORD             lace_count_since_flip;
56 DWORD             dwLaceCnt=0;
57 short             sDispWidths[8] = {256,320,512,640,368,384,512,640};
58 PSXDisplay_t      PSXDisplay;
59 PSXDisplay_t      PreviousPSXDisplay;
60 long              lSelectedSlot=0;
61 BOOL              bDoLazyUpdate=FALSE;
62 uint32_t          lGPUInfoVals[16];
63 static int        iFakePrimBusy=0;
64 static uint32_t   vBlank=0;
65
66 ////////////////////////////////////////////////////////////////////////
67 // some misc external display funcs
68 ////////////////////////////////////////////////////////////////////////
69
70 #include <time.h>
71
72 // FPS library
73 #include "fps.c"
74
75
76 ////////////////////////////////////////////////////////////////////////
77 // sets all kind of act fixes
78 ////////////////////////////////////////////////////////////////////////
79
80 static void SetFixes(void)
81  {
82   if(dwActFixes&0x02) sDispWidths[4]=384;
83   else                sDispWidths[4]=368;
84  }
85
86 ////////////////////////////////////////////////////////////////////////
87 // INIT, will be called after lib load... well, just do some var init...
88 ////////////////////////////////////////////////////////////////////////
89
90 long CALLBACK GPUinit(void)                                // GPU INIT
91 {
92  memset(ulStatusControl,0,256*sizeof(uint32_t));  // init save state scontrol field
93
94  psxVSecure = (unsigned char *)malloc((512*2)*1024 + (1024*1024)); // always alloc one extra MB for soft drawing funcs security
95  if (!psxVSecure)
96   return -1;
97
98  //!!! ATTENTION !!!
99  psxVub=psxVSecure + 512 * 1024;                           // security offset into double sized psx vram!
100
101  psxVsb=(signed char *)psxVub;                         // different ways of accessing PSX VRAM
102  psxVsw=(signed short *)psxVub;
103  psxVsl=(int32_t *)psxVub;
104  psxVuw=(unsigned short *)psxVub;
105  psxVul=(uint32_t *)psxVub;
106
107  psxVuw_eom=psxVuw+1024*512;                    // pre-calc of end of vram
108
109  memset(psxVSecure,0x00,(512*2)*1024 + (1024*1024));
110  memset(lGPUInfoVals,0x00,16*sizeof(uint32_t));
111
112  PSXDisplay.RGB24        = FALSE;                      // init some stuff
113  PSXDisplay.Interlaced   = FALSE;
114  PSXDisplay.DrawOffset.x = 0;
115  PSXDisplay.DrawOffset.y = 0;
116  PSXDisplay.DisplayMode.x= 320;
117  PSXDisplay.DisplayMode.y= 240;
118  PreviousPSXDisplay.DisplayMode.x= 320;
119  PreviousPSXDisplay.DisplayMode.y= 240;
120  PSXDisplay.Disabled     = FALSE;
121  PreviousPSXDisplay.Range.x0 =0;
122  PreviousPSXDisplay.Range.y0 =0;
123  PSXDisplay.Range.x0=0;
124  PSXDisplay.Range.x1=0;
125  PreviousPSXDisplay.DisplayModeNew.y=0;
126  PSXDisplay.Double = 1;
127  lGPUdataRet = 0x400;
128
129  DataWriteMode = DR_NORMAL;
130
131  // Reset transfer values, to prevent mis-transfer of data
132  memset(&VRAMWrite, 0, sizeof(VRAMLoad_t));
133  memset(&VRAMRead, 0, sizeof(VRAMLoad_t));
134  
135  // device initialised already !
136  lGPUstatusRet = 0x14802000;
137  GPUIsIdle;
138  GPUIsReadyForCommands;
139  bDoVSyncUpdate = TRUE;
140
141  return 0;
142 }
143
144 ////////////////////////////////////////////////////////////////////////
145 // Here starts all...
146 ////////////////////////////////////////////////////////////////////////
147
148
149 long GPUopen(unsigned long * disp,char * CapText,char * CfgFile)
150 {
151  unsigned long d;
152  
153  SetFixes();
154
155  InitFPS();
156
157  bDoVSyncUpdate = TRUE;
158
159  d=ulInitDisplay();                                    // setup x
160
161  if(disp)
162         *disp=d;                                     // wanna x pointer? ok
163
164  if(d) return 0;
165  return -1;
166 }
167
168
169 ////////////////////////////////////////////////////////////////////////
170 // time to leave...
171 ////////////////////////////////////////////////////////////////////////
172
173 long CALLBACK GPUclose()                               // GPU CLOSE
174 {
175  CloseDisplay();                                       // shutdown direct draw
176
177  return 0;
178 }
179
180 ////////////////////////////////////////////////////////////////////////
181 // I shot the sheriff
182 ////////////////////////////////////////////////////////////////////////
183
184 long CALLBACK GPUshutdown(void)                            // GPU SHUTDOWN
185 {
186  CloseDisplay();                                       // shutdown direct draw
187  free(psxVSecure);
188  return 0;                                             // nothinh to do
189 }
190
191 ////////////////////////////////////////////////////////////////////////
192 // Update display (swap buffers)
193 ////////////////////////////////////////////////////////////////////////
194
195 static void updateDisplay(void)                               // UPDATE DISPLAY
196 {
197  if(PSXDisplay.Disabled)                               // disable?
198   {
199    return;                                             // -> and bye
200   }
201
202  if(dwActFixes&32)                                     // pc fps calculation fix
203   {
204    if(UseFrameLimit) PCFrameCap();                     // -> brake
205    if(UseFrameSkip) PCcalcfps();         
206   }
207
208  if(UseFrameSkip)                                      // skip ?
209   {
210    if(fskip_frameReady)
211     {
212      DoBufferSwap();                                   // -> to skip or not to skip
213      fskip_frameReady=FALSE;
214      bDoVSyncUpdate=FALSE;                             // vsync done
215     }
216   }
217  else                                                  // no skip ?
218   {
219    bSkipNextFrame = FALSE;
220    DoBufferSwap();                                     // -> swap
221    bDoVSyncUpdate=FALSE;                               // vsync done
222   }
223 }
224
225 static void decideSkip(void)
226 {
227  if(!bDoVSyncUpdate)
228    return;
229
230  lace_count_since_flip=0;
231  fskip_frameReady=!bSkipNextFrame;
232
233  if(dwActFixes&0xa0)                                   // -> pc fps calculation fix/old skipping fix
234   {
235    if((fps_skip < fFrameRateHz) && !bSkipNextFrame)    // -> skip max one in a row
236        {bSkipNextFrame = TRUE; fps_skip=fFrameRateHz;}
237    else bSkipNextFrame = FALSE;
238   }
239  else FrameSkip();
240 }
241
242 ////////////////////////////////////////////////////////////////////////
243 // roughly emulated screen centering bits... not complete !!!
244 ////////////////////////////////////////////////////////////////////////
245
246 void ChangeDispOffsetsX(void)                          // X CENTER
247 {
248  long lx,l;
249
250  if(!PSXDisplay.Range.x1) return;
251
252  l=PreviousPSXDisplay.DisplayMode.x;
253
254  l*=(long)PSXDisplay.Range.x1;
255  l/=2560;lx=l;l&=0xfffffff8;
256
257  if(l==PreviousPSXDisplay.Range.y1) return;            // abusing range.y1 for
258  PreviousPSXDisplay.Range.y1=(short)l;                 // storing last x range and test
259
260  if(lx>=PreviousPSXDisplay.DisplayMode.x)
261   {
262    PreviousPSXDisplay.Range.x1=
263     (short)PreviousPSXDisplay.DisplayMode.x;
264    PreviousPSXDisplay.Range.x0=0;
265   }
266  else
267   {
268    PreviousPSXDisplay.Range.x1=(short)l;
269
270    PreviousPSXDisplay.Range.x0=
271     (PSXDisplay.Range.x0-500)/8;
272
273    if(PreviousPSXDisplay.Range.x0<0)
274     PreviousPSXDisplay.Range.x0=0;
275
276    if((PreviousPSXDisplay.Range.x0+lx)>
277       PreviousPSXDisplay.DisplayMode.x)
278     {
279      PreviousPSXDisplay.Range.x0=
280       (short)(PreviousPSXDisplay.DisplayMode.x-lx);
281      PreviousPSXDisplay.Range.x0+=2; //???
282
283      PreviousPSXDisplay.Range.x1+=(short)(lx-l);
284
285      PreviousPSXDisplay.Range.x1-=2; // makes linux stretching easier
286
287     }
288
289    // some linux alignment security
290    PreviousPSXDisplay.Range.x0=PreviousPSXDisplay.Range.x0>>1;
291    PreviousPSXDisplay.Range.x0=PreviousPSXDisplay.Range.x0<<1;
292    PreviousPSXDisplay.Range.x1=PreviousPSXDisplay.Range.x1>>1;
293    PreviousPSXDisplay.Range.x1=PreviousPSXDisplay.Range.x1<<1;
294
295    DoClearScreenBuffer();
296   }
297
298  bDoVSyncUpdate=TRUE;
299 }
300
301 ////////////////////////////////////////////////////////////////////////
302
303 void ChangeDispOffsetsY(void)                          // Y CENTER
304 {
305  int iT,iO=PreviousPSXDisplay.Range.y0;
306  int iOldYOffset=PreviousPSXDisplay.DisplayModeNew.y;
307
308 // new
309
310  if((PreviousPSXDisplay.DisplayModeNew.x+PSXDisplay.DisplayModeNew.y)>512)
311   {
312    int dy1=512-PreviousPSXDisplay.DisplayModeNew.x;
313    int dy2=(PreviousPSXDisplay.DisplayModeNew.x+PSXDisplay.DisplayModeNew.y)-512;
314
315    if(dy1>=dy2)
316     {
317      PreviousPSXDisplay.DisplayModeNew.y=-dy2;
318     }
319    else
320     {
321      PSXDisplay.DisplayPosition.y=0;
322      PreviousPSXDisplay.DisplayModeNew.y=-dy1;
323     }
324   }
325  else PreviousPSXDisplay.DisplayModeNew.y=0;
326
327 // eon
328
329  if(PreviousPSXDisplay.DisplayModeNew.y!=iOldYOffset) // if old offset!=new offset: recalc height
330   {
331    PSXDisplay.Height = PSXDisplay.Range.y1 - 
332                        PSXDisplay.Range.y0 +
333                        PreviousPSXDisplay.DisplayModeNew.y;
334    PSXDisplay.DisplayModeNew.y=PSXDisplay.Height*PSXDisplay.Double;
335   }
336
337 //
338
339  if(PSXDisplay.PAL) iT=48; else iT=28;
340
341  if(PSXDisplay.Range.y0>=iT)
342   {
343    PreviousPSXDisplay.Range.y0=
344     (short)((PSXDisplay.Range.y0-iT-4)*PSXDisplay.Double);
345    if(PreviousPSXDisplay.Range.y0<0)
346     PreviousPSXDisplay.Range.y0=0;
347    PSXDisplay.DisplayModeNew.y+=
348     PreviousPSXDisplay.Range.y0;
349   }
350  else 
351   PreviousPSXDisplay.Range.y0=0;
352
353  if(iO!=PreviousPSXDisplay.Range.y0)
354   {
355    DoClearScreenBuffer();
356  }
357 }
358
359 ////////////////////////////////////////////////////////////////////////
360 // check if update needed
361 ////////////////////////////////////////////////////////////////////////
362
363 static void updateDisplayIfChanged(void)                      // UPDATE DISPLAY IF CHANGED
364 {
365  if ((PSXDisplay.DisplayMode.y == PSXDisplay.DisplayModeNew.y) && 
366      (PSXDisplay.DisplayMode.x == PSXDisplay.DisplayModeNew.x))
367   {
368    if((PSXDisplay.RGB24      == PSXDisplay.RGB24New) && 
369       (PSXDisplay.Interlaced == PSXDisplay.InterlacedNew)) return;
370   }
371
372  PSXDisplay.RGB24         = PSXDisplay.RGB24New;       // get new infos
373
374  PSXDisplay.DisplayMode.y = PSXDisplay.DisplayModeNew.y;
375  PSXDisplay.DisplayMode.x = PSXDisplay.DisplayModeNew.x;
376  PreviousPSXDisplay.DisplayMode.x=                     // previous will hold
377   min(640,PSXDisplay.DisplayMode.x);                   // max 640x512... that's
378  PreviousPSXDisplay.DisplayMode.y=                     // the size of my 
379   min(512,PSXDisplay.DisplayMode.y);                   // back buffer surface
380  PSXDisplay.Interlaced    = PSXDisplay.InterlacedNew;
381     
382  PSXDisplay.DisplayEnd.x=                              // calc end of display
383   PSXDisplay.DisplayPosition.x+ PSXDisplay.DisplayMode.x;
384  PSXDisplay.DisplayEnd.y=
385   PSXDisplay.DisplayPosition.y+ PSXDisplay.DisplayMode.y+PreviousPSXDisplay.DisplayModeNew.y;
386  PreviousPSXDisplay.DisplayEnd.x=
387   PreviousPSXDisplay.DisplayPosition.x+ PSXDisplay.DisplayMode.x;
388  PreviousPSXDisplay.DisplayEnd.y=
389   PreviousPSXDisplay.DisplayPosition.y+ PSXDisplay.DisplayMode.y+PreviousPSXDisplay.DisplayModeNew.y;
390
391  ChangeDispOffsetsX();
392
393  if(iFrameLimit==2) SetAutoFrameCap();                 // -> set it
394
395  if(UseFrameSkip) decideSkip();                        // stupid stuff when frame skipping enabled
396 }
397
398 ////////////////////////////////////////////////////////////////////////
399 // update lace is called evry VSync
400 ////////////////////////////////////////////////////////////////////////
401
402 void CALLBACK GPUupdateLace(void)                      // VSYNC
403 {
404  //if(!(dwActFixes&1))
405  // lGPUstatusRet^=0x80000000;                           // odd/even bit
406
407  //pcsx-rearmed: removed, this is handled by core
408  //if(!(dwActFixes&32))                                  // std fps limitation?
409  // CheckFrameRate();
410
411  if(PSXDisplay.Interlaced)                             // interlaced mode?
412   {
413    lGPUstatusRet^=0x80000000;                          // odd/even bit?
414
415    if(bDoVSyncUpdate && PSXDisplay.DisplayMode.x>0 && PSXDisplay.DisplayMode.y>0)
416     {
417      updateDisplay();
418     }
419   }
420  else                                                  // non-interlaced?
421   {
422    if(dwActFixes&64)                                   // lazy screen update fix
423     {
424      if(bDoLazyUpdate)
425       updateDisplay(); 
426      bDoLazyUpdate=FALSE;
427     }
428    else
429     {
430      if(bDoVSyncUpdate)                                // some primitives drawn?
431        updateDisplay();                                // -> update display
432     }
433   }
434
435  if(UseFrameSkip) {                                    // frame over-skip guard
436   lace_count_since_flip++;
437   if(lace_count_since_flip > 8) {
438    bSkipNextFrame=FALSE;
439    fskip_frameReady=TRUE;
440   }
441  }
442 }
443
444 ////////////////////////////////////////////////////////////////////////
445 // process read request from GPU status register
446 ////////////////////////////////////////////////////////////////////////
447
448
449 uint32_t CALLBACK GPUreadStatus(void)             // READ STATUS
450 {
451  if(dwActFixes&1)
452   {
453    static int iNumRead=0;                         // odd/even hack
454    if((iNumRead++)==2)
455     {
456      iNumRead=0;
457      lGPUstatusRet^=0x80000000;                   // interlaced bit toggle... we do it on every 3 read status... needed by some games (like ChronoCross) with old epsxe versions (1.5.2 and older)
458     }
459   }
460
461  if(iFakePrimBusy)                                // 27.10.2007 - PETE : emulating some 'busy' while drawing... pfff
462   {
463    iFakePrimBusy--;
464
465    if(iFakePrimBusy&1)                            // we do a busy-idle-busy-idle sequence after/while drawing prims
466     {
467      GPUIsBusy;
468      GPUIsNotReadyForCommands;
469     }
470    else
471     {
472      GPUIsIdle;
473      GPUIsReadyForCommands;
474     }
475   }
476  return lGPUstatusRet | vBlank;
477 }
478
479 ////////////////////////////////////////////////////////////////////////
480 // processes data send to GPU status register
481 // these are always single packet commands.
482 ////////////////////////////////////////////////////////////////////////
483
484 void CALLBACK GPUwriteStatus(uint32_t gdata)      // WRITE STATUS
485 {
486  uint32_t lCommand=(gdata>>24)&0xff;
487
488  ulStatusControl[lCommand]=gdata;                      // store command for freezing
489
490  switch(lCommand)
491   {
492    //--------------------------------------------------//
493    // reset gpu
494    case 0x00:
495     memset(lGPUInfoVals,0x00,16*sizeof(uint32_t));
496     lGPUstatusRet=0x14802000;
497     PSXDisplay.Disabled=1;
498     DataWriteMode=DataReadMode=DR_NORMAL;
499     PSXDisplay.DrawOffset.x=PSXDisplay.DrawOffset.y=0;
500     drawX=drawY=0;drawW=drawH=0;
501     sSetMask=0;lSetMask=0;bCheckMask=FALSE;
502     usMirror=0;
503     GlobalTextAddrX=0;GlobalTextAddrY=0;
504     GlobalTextTP=0;GlobalTextABR=0;
505     PSXDisplay.RGB24=FALSE;
506     PSXDisplay.Interlaced=FALSE;
507     bUsingTWin = FALSE;
508     return;
509    //--------------------------------------------------//
510    // dis/enable display 
511    case 0x03:  
512
513     PreviousPSXDisplay.Disabled = PSXDisplay.Disabled;
514     PSXDisplay.Disabled = (gdata & 1);
515
516     if(PSXDisplay.Disabled) 
517          lGPUstatusRet|=GPUSTATUS_DISPLAYDISABLED;
518     else lGPUstatusRet&=~GPUSTATUS_DISPLAYDISABLED;
519     return;
520
521    //--------------------------------------------------//
522    // setting transfer mode
523    case 0x04:
524     gdata &= 0x03;                                     // Only want the lower two bits
525
526     DataWriteMode=DataReadMode=DR_NORMAL;
527     if(gdata==0x02) DataWriteMode=DR_VRAMTRANSFER;
528     if(gdata==0x03) DataReadMode =DR_VRAMTRANSFER;
529     lGPUstatusRet&=~GPUSTATUS_DMABITS;                 // Clear the current settings of the DMA bits
530     lGPUstatusRet|=(gdata << 29);                      // Set the DMA bits according to the received data
531
532     return;
533    //--------------------------------------------------//
534    // setting display position
535    case 0x05: 
536     {
537      PreviousPSXDisplay.DisplayPosition.x = PSXDisplay.DisplayPosition.x;
538      PreviousPSXDisplay.DisplayPosition.y = PSXDisplay.DisplayPosition.y;
539
540 // new
541      PSXDisplay.DisplayPosition.y = (short)((gdata>>10)&0x1ff);
542
543      // store the same val in some helper var, we need it on later compares
544      PreviousPSXDisplay.DisplayModeNew.x=PSXDisplay.DisplayPosition.y;
545
546      if((PSXDisplay.DisplayPosition.y+PSXDisplay.DisplayMode.y)>512)
547       {
548        int dy1=512-PSXDisplay.DisplayPosition.y;
549        int dy2=(PSXDisplay.DisplayPosition.y+PSXDisplay.DisplayMode.y)-512;
550
551        if(dy1>=dy2)
552         {
553          PreviousPSXDisplay.DisplayModeNew.y=-dy2;
554         }
555        else
556         {
557          PSXDisplay.DisplayPosition.y=0;
558          PreviousPSXDisplay.DisplayModeNew.y=-dy1;
559         }
560       }
561      else PreviousPSXDisplay.DisplayModeNew.y=0;
562 // eon
563
564      PSXDisplay.DisplayPosition.x = (short)(gdata & 0x3ff);
565      PSXDisplay.DisplayEnd.x=
566       PSXDisplay.DisplayPosition.x+ PSXDisplay.DisplayMode.x;
567      PSXDisplay.DisplayEnd.y=
568       PSXDisplay.DisplayPosition.y+ PSXDisplay.DisplayMode.y + PreviousPSXDisplay.DisplayModeNew.y;
569      PreviousPSXDisplay.DisplayEnd.x=
570       PreviousPSXDisplay.DisplayPosition.x+ PSXDisplay.DisplayMode.x;
571      PreviousPSXDisplay.DisplayEnd.y=
572       PreviousPSXDisplay.DisplayPosition.y+ PSXDisplay.DisplayMode.y + PreviousPSXDisplay.DisplayModeNew.y;
573  
574      bDoVSyncUpdate=TRUE;
575
576      if (!(PSXDisplay.Interlaced))                      // stupid frame skipping option
577       {
578        if(UseFrameSkip)  decideSkip();
579        if(dwActFixes&64) bDoLazyUpdate=TRUE;
580       }
581     }return;
582    //--------------------------------------------------//
583    // setting width
584    case 0x06:
585
586     PSXDisplay.Range.x0=(short)(gdata & 0x7ff);
587     PSXDisplay.Range.x1=(short)((gdata>>12) & 0xfff);
588
589     PSXDisplay.Range.x1-=PSXDisplay.Range.x0;
590
591     ChangeDispOffsetsX();
592
593     return;
594    //--------------------------------------------------//
595    // setting height
596    case 0x07:
597     {
598
599      PSXDisplay.Range.y0=(short)(gdata & 0x3ff);
600      PSXDisplay.Range.y1=(short)((gdata>>10) & 0x3ff);
601                                       
602      PreviousPSXDisplay.Height = PSXDisplay.Height;
603
604      PSXDisplay.Height = PSXDisplay.Range.y1 - 
605                          PSXDisplay.Range.y0 +
606                          PreviousPSXDisplay.DisplayModeNew.y;
607
608      if(PreviousPSXDisplay.Height!=PSXDisplay.Height)
609       {
610        PSXDisplay.DisplayModeNew.y=PSXDisplay.Height*PSXDisplay.Double;
611
612        ChangeDispOffsetsY();
613
614        updateDisplayIfChanged();
615       }
616      return;
617     }
618    //--------------------------------------------------//
619    // setting display infos
620    case 0x08:
621
622     PSXDisplay.DisplayModeNew.x =
623      sDispWidths[(gdata & 0x03) | ((gdata & 0x40) >> 4)];
624
625     if (gdata&0x04) PSXDisplay.Double=2;
626     else            PSXDisplay.Double=1;
627
628     PSXDisplay.DisplayModeNew.y = PSXDisplay.Height*PSXDisplay.Double;
629
630     ChangeDispOffsetsY();
631
632     PSXDisplay.PAL           = (gdata & 0x08)?TRUE:FALSE; // if 1 - PAL mode, else NTSC
633     PSXDisplay.RGB24New      = (gdata & 0x10)?TRUE:FALSE; // if 1 - TrueColor
634     PSXDisplay.InterlacedNew = (gdata & 0x20)?TRUE:FALSE; // if 1 - Interlace
635
636     lGPUstatusRet&=~GPUSTATUS_WIDTHBITS;                   // Clear the width bits
637     lGPUstatusRet|=
638                (((gdata & 0x03) << 17) | 
639                ((gdata & 0x40) << 10));                // Set the width bits
640
641     if(PSXDisplay.InterlacedNew)
642      {
643       if(!PSXDisplay.Interlaced)
644        {
645         PreviousPSXDisplay.DisplayPosition.x = PSXDisplay.DisplayPosition.x;
646         PreviousPSXDisplay.DisplayPosition.y = PSXDisplay.DisplayPosition.y;
647        }
648       lGPUstatusRet|=GPUSTATUS_INTERLACED;
649      }
650     else lGPUstatusRet&=~GPUSTATUS_INTERLACED;
651
652     if (PSXDisplay.PAL)
653          lGPUstatusRet|=GPUSTATUS_PAL;
654     else lGPUstatusRet&=~GPUSTATUS_PAL;
655
656     if (PSXDisplay.Double==2)
657          lGPUstatusRet|=GPUSTATUS_DOUBLEHEIGHT;
658     else lGPUstatusRet&=~GPUSTATUS_DOUBLEHEIGHT;
659
660     if (PSXDisplay.RGB24New)
661          lGPUstatusRet|=GPUSTATUS_RGB24;
662     else lGPUstatusRet&=~GPUSTATUS_RGB24;
663
664     updateDisplayIfChanged();
665
666     return;
667    //--------------------------------------------------//
668    // ask about GPU version and other stuff
669    case 0x10: 
670
671     gdata&=0xff;
672
673     switch(gdata) 
674      {
675       case 0x02:
676        lGPUdataRet=lGPUInfoVals[INFO_TW];              // tw infos
677        return;
678       case 0x03:
679        lGPUdataRet=lGPUInfoVals[INFO_DRAWSTART];       // draw start
680        return;
681       case 0x04:
682        lGPUdataRet=lGPUInfoVals[INFO_DRAWEND];         // draw end
683        return;
684       case 0x05:
685       case 0x06:
686        lGPUdataRet=lGPUInfoVals[INFO_DRAWOFF];         // draw offset
687        return;
688       case 0x07:
689        lGPUdataRet=0x02;                               // gpu type
690        return;
691       case 0x08:
692       case 0x0F:                                       // some bios addr?
693        lGPUdataRet=0xBFC03720;
694        return;
695      }
696     return;
697    //--------------------------------------------------//
698   }   
699 }
700
701 ////////////////////////////////////////////////////////////////////////
702 // vram read/write helpers, needed by LEWPY's optimized vram read/write :)
703 ////////////////////////////////////////////////////////////////////////
704
705 static inline void FinishedVRAMWrite(void)
706 {
707  // Set register to NORMAL operation
708  DataWriteMode = DR_NORMAL;
709  // Reset transfer values, to prevent mis-transfer of data
710  VRAMWrite.x = 0;
711  VRAMWrite.y = 0;
712  VRAMWrite.Width = 0;
713  VRAMWrite.Height = 0;
714  VRAMWrite.ColsRemaining = 0;
715  VRAMWrite.RowsRemaining = 0;
716 }
717
718 static inline void FinishedVRAMRead(void)
719 {
720  // Set register to NORMAL operation
721  DataReadMode = DR_NORMAL;
722  // Reset transfer values, to prevent mis-transfer of data
723  VRAMRead.x = 0;
724  VRAMRead.y = 0;
725  VRAMRead.Width = 0;
726  VRAMRead.Height = 0;
727  VRAMRead.ColsRemaining = 0;
728  VRAMRead.RowsRemaining = 0;
729
730  // Indicate GPU is no longer ready for VRAM data in the STATUS REGISTER
731  lGPUstatusRet&=~GPUSTATUS_READYFORVRAM;
732 }
733
734 ////////////////////////////////////////////////////////////////////////
735 // core read from vram
736 ////////////////////////////////////////////////////////////////////////
737
738 void CALLBACK GPUreadDataMem(uint32_t * pMem, int iSize)
739 {
740  int i;
741
742  if(DataReadMode!=DR_VRAMTRANSFER) return;
743
744  GPUIsBusy;
745
746  // adjust read ptr, if necessary
747  while(VRAMRead.ImagePtr>=psxVuw_eom)
748   VRAMRead.ImagePtr-=512*1024;
749  while(VRAMRead.ImagePtr<psxVuw)
750   VRAMRead.ImagePtr+=512*1024;
751
752  for(i=0;i<iSize;i++)
753   {
754    // do 2 seperate 16bit reads for compatibility (wrap issues)
755    if ((VRAMRead.ColsRemaining > 0) && (VRAMRead.RowsRemaining > 0))
756     {
757      // lower 16 bit
758      lGPUdataRet=(uint32_t)GETLE16(VRAMRead.ImagePtr);
759
760      VRAMRead.ImagePtr++;
761      if(VRAMRead.ImagePtr>=psxVuw_eom) VRAMRead.ImagePtr-=512*1024;
762      VRAMRead.RowsRemaining --;
763
764      if(VRAMRead.RowsRemaining<=0)
765       {
766        VRAMRead.RowsRemaining = VRAMRead.Width;
767        VRAMRead.ColsRemaining--;
768        VRAMRead.ImagePtr += 1024 - VRAMRead.Width;
769        if(VRAMRead.ImagePtr>=psxVuw_eom) VRAMRead.ImagePtr-=512*1024;
770       }
771
772      // higher 16 bit (always, even if it's an odd width)
773      lGPUdataRet|=(uint32_t)GETLE16(VRAMRead.ImagePtr)<<16;
774      PUTLE32(pMem, lGPUdataRet); pMem++;
775
776      if(VRAMRead.ColsRemaining <= 0)
777       {FinishedVRAMRead();goto ENDREAD;}
778
779      VRAMRead.ImagePtr++;
780      if(VRAMRead.ImagePtr>=psxVuw_eom) VRAMRead.ImagePtr-=512*1024;
781      VRAMRead.RowsRemaining--;
782      if(VRAMRead.RowsRemaining<=0)
783       {
784        VRAMRead.RowsRemaining = VRAMRead.Width;
785        VRAMRead.ColsRemaining--;
786        VRAMRead.ImagePtr += 1024 - VRAMRead.Width;
787        if(VRAMRead.ImagePtr>=psxVuw_eom) VRAMRead.ImagePtr-=512*1024;
788       }
789      if(VRAMRead.ColsRemaining <= 0)
790       {FinishedVRAMRead();goto ENDREAD;}
791     }
792    else {FinishedVRAMRead();goto ENDREAD;}
793   }
794
795 ENDREAD:
796  GPUIsIdle;
797 }
798
799
800 ////////////////////////////////////////////////////////////////////////
801
802 uint32_t CALLBACK GPUreadData(void)
803 {
804  uint32_t l;
805  GPUreadDataMem(&l,1);
806  return lGPUdataRet;
807 }
808
809 // Software drawing function
810 #include "soft.c"
811
812 // PSX drawing primitives
813 #include "prim.c"
814
815 ////////////////////////////////////////////////////////////////////////
816 // processes data send to GPU data register
817 // extra table entries for fixing polyline troubles
818 ////////////////////////////////////////////////////////////////////////
819
820 static const unsigned char primTableCX[256] =
821 {
822     // 00
823     0,0,3,0,0,0,0,0,
824     // 08
825     0,0,0,0,0,0,0,0,
826     // 10
827     0,0,0,0,0,0,0,0,
828     // 18
829     0,0,0,0,0,0,0,0,
830     // 20
831     4,4,4,4,7,7,7,7,
832     // 28
833     5,5,5,5,9,9,9,9,
834     // 30
835     6,6,6,6,9,9,9,9,
836     // 38
837     8,8,8,8,12,12,12,12,
838     // 40
839     3,3,3,3,0,0,0,0,
840     // 48
841 //  5,5,5,5,6,6,6,6,    // FLINE
842     254,254,254,254,254,254,254,254,
843     // 50
844     4,4,4,4,0,0,0,0,
845     // 58
846 //  7,7,7,7,9,9,9,9,    // GLINE
847     255,255,255,255,255,255,255,255,
848     // 60
849     3,3,3,3,4,4,4,4,    
850     // 68
851     2,2,2,2,3,3,3,3,    // 3=SPRITE1???
852     // 70
853     2,2,2,2,3,3,3,3,
854     // 78
855     2,2,2,2,3,3,3,3,
856     // 80
857     4,0,0,0,0,0,0,0,
858     // 88
859     0,0,0,0,0,0,0,0,
860     // 90
861     0,0,0,0,0,0,0,0,
862     // 98
863     0,0,0,0,0,0,0,0,
864     // a0
865     3,0,0,0,0,0,0,0,
866     // a8
867     0,0,0,0,0,0,0,0,
868     // b0
869     0,0,0,0,0,0,0,0,
870     // b8
871     0,0,0,0,0,0,0,0,
872     // c0
873     3,0,0,0,0,0,0,0,
874     // c8
875     0,0,0,0,0,0,0,0,
876     // d0
877     0,0,0,0,0,0,0,0,
878     // d8
879     0,0,0,0,0,0,0,0,
880     // e0
881     0,1,1,1,1,1,1,0,
882     // e8
883     0,0,0,0,0,0,0,0,
884     // f0
885     0,0,0,0,0,0,0,0,
886     // f8
887     0,0,0,0,0,0,0,0
888 };
889
890 void CALLBACK GPUwriteDataMem(uint32_t * pMem, int iSize)
891 {
892  unsigned char command;
893  uint32_t gdata=0;
894  int i=0;
895  GPUIsBusy;
896  GPUIsNotReadyForCommands;
897
898 STARTVRAM:
899
900  if(DataWriteMode==DR_VRAMTRANSFER)
901   {
902    BOOL bFinished=FALSE;
903
904    // make sure we are in vram
905    while(VRAMWrite.ImagePtr>=psxVuw_eom)
906     VRAMWrite.ImagePtr-=512*1024;
907    while(VRAMWrite.ImagePtr<psxVuw)
908     VRAMWrite.ImagePtr+=512*1024;
909
910    // now do the loop
911    while(VRAMWrite.ColsRemaining>0)
912     {
913      while(VRAMWrite.RowsRemaining>0)
914       {
915        if(i>=iSize) {goto ENDVRAM;}
916        i++;
917
918        gdata=GETLE32(pMem); pMem++;
919
920        PUTLE16(VRAMWrite.ImagePtr, (unsigned short)gdata); VRAMWrite.ImagePtr++;
921        if(VRAMWrite.ImagePtr>=psxVuw_eom) VRAMWrite.ImagePtr-=512*1024;
922        VRAMWrite.RowsRemaining --;
923
924        if(VRAMWrite.RowsRemaining <= 0)
925         {
926          VRAMWrite.ColsRemaining--;
927          if (VRAMWrite.ColsRemaining <= 0)             // last pixel is odd width
928           {
929            gdata=(gdata&0xFFFF)|(((uint32_t)GETLE16(VRAMWrite.ImagePtr))<<16);
930            FinishedVRAMWrite();
931            bDoVSyncUpdate=TRUE;
932            goto ENDVRAM;
933           }
934          VRAMWrite.RowsRemaining = VRAMWrite.Width;
935          VRAMWrite.ImagePtr += 1024 - VRAMWrite.Width;
936         }
937
938        PUTLE16(VRAMWrite.ImagePtr, (unsigned short)(gdata>>16)); VRAMWrite.ImagePtr++;
939        if(VRAMWrite.ImagePtr>=psxVuw_eom) VRAMWrite.ImagePtr-=512*1024;
940        VRAMWrite.RowsRemaining --;
941       }
942
943      VRAMWrite.RowsRemaining = VRAMWrite.Width;
944      VRAMWrite.ColsRemaining--;
945      VRAMWrite.ImagePtr += 1024 - VRAMWrite.Width;
946      bFinished=TRUE;
947     }
948
949    FinishedVRAMWrite();
950    if(bFinished) bDoVSyncUpdate=TRUE;
951   }
952
953 ENDVRAM:
954
955  if(DataWriteMode==DR_NORMAL)
956   {
957    void (* *primFunc)(unsigned char *);
958    if(bSkipNextFrame) primFunc=primTableSkip;
959    else               primFunc=primTableJ;
960
961    for(;i<iSize;)
962     {
963      if(DataWriteMode==DR_VRAMTRANSFER) goto STARTVRAM;
964
965      gdata=GETLE32(pMem); pMem++; i++;
966  
967      if(gpuDataC == 0)
968       {
969        command = (unsigned char)((gdata>>24) & 0xff);
970  
971 //if(command>=0xb0 && command<0xc0) auxprintf("b0 %x!!!!!!!!!\n",command);
972
973        if(primTableCX[command])
974         {
975          gpuDataC = primTableCX[command];
976          gpuCommand = command;
977          PUTLE32_(&gpuDataM[0], gdata);
978          gpuDataP = 1;
979         }
980        else continue;
981       }
982      else
983       {
984        PUTLE32_(&gpuDataM[gpuDataP], gdata);
985        if(gpuDataC>128)
986         {
987          if((gpuDataC==254 && gpuDataP>=3) ||
988             (gpuDataC==255 && gpuDataP>=4 && !(gpuDataP&1)))
989           {
990            if((gpuDataM[gpuDataP] & 0xF000F000) == 0x50005000)
991             gpuDataP=gpuDataC-1;
992           }
993         }
994        gpuDataP++;
995       }
996  
997      if(gpuDataP == gpuDataC)
998       {
999        gpuDataC=gpuDataP=0;
1000        primFunc[gpuCommand]((unsigned char *)gpuDataM);
1001        if(dwActFixes&0x0400)      // hack for emulating "gpu busy" in some games
1002         iFakePrimBusy=4;
1003       }
1004     } 
1005   }
1006
1007  lGPUdataRet=gdata;
1008
1009  GPUIsReadyForCommands;
1010  GPUIsIdle;                
1011 }
1012
1013 ////////////////////////////////////////////////////////////////////////
1014
1015 void CALLBACK GPUwriteData(uint32_t gdata)
1016 {
1017  PUTLE32_(&gdata, gdata);
1018  GPUwriteDataMem(&gdata,1);
1019 }
1020
1021 ////////////////////////////////////////////////////////////////////////
1022 // process gpu commands
1023 ////////////////////////////////////////////////////////////////////////
1024
1025 unsigned long lUsedAddr[3];
1026
1027 static inline BOOL CheckForEndlessLoop(unsigned long laddr)
1028 {
1029  if(laddr==lUsedAddr[1]) return TRUE;
1030  if(laddr==lUsedAddr[2]) return TRUE;
1031
1032  if(laddr<lUsedAddr[0]) lUsedAddr[1]=laddr;
1033  else                   lUsedAddr[2]=laddr;
1034  lUsedAddr[0]=laddr;
1035  return FALSE;
1036 }
1037
1038 long CALLBACK GPUdmaChain(uint32_t * baseAddrL, uint32_t addr)
1039 {
1040  uint32_t dmaMem;
1041  unsigned char * baseAddrB;
1042  short count;unsigned int DMACommandCounter = 0;
1043
1044  GPUIsBusy;
1045
1046  lUsedAddr[0]=lUsedAddr[1]=lUsedAddr[2]=0xffffff;
1047
1048  baseAddrB = (unsigned char*) baseAddrL;
1049
1050  do
1051   {
1052    addr&=0x1FFFFC;
1053    if(DMACommandCounter++ > 2000000) break;
1054    if(CheckForEndlessLoop(addr)) break;
1055
1056    count = baseAddrB[addr+3];
1057
1058    dmaMem=addr+4;
1059
1060    if(count>0) GPUwriteDataMem(&baseAddrL[dmaMem>>2],count);
1061
1062    addr = GETLE32(&baseAddrL[addr>>2])&0xffffff;
1063   }
1064  while (addr != 0xffffff);
1065
1066  GPUIsIdle;
1067
1068  return 0;
1069 }
1070
1071 ////////////////////////////////////////////////////////////////////////
1072 // Freeze
1073 ////////////////////////////////////////////////////////////////////////
1074
1075 typedef struct GPUFREEZETAG
1076 {
1077  uint32_t ulFreezeVersion;      // should be always 1 for now (set by main emu)
1078  uint32_t ulStatus;             // current gpu status
1079  uint32_t ulControl[256];       // latest control register values
1080  unsigned char psxVRam[1024*1024*2]; // current VRam image (full 2 MB for ZN)
1081 } GPUFreeze_t;
1082
1083 ////////////////////////////////////////////////////////////////////////
1084
1085 long CALLBACK GPUfreeze(uint32_t ulGetFreezeData,GPUFreeze_t * pF)
1086 {
1087  //----------------------------------------------------//
1088  if(ulGetFreezeData==2)                                // 2: info, which save slot is selected? (just for display)
1089   {
1090    long lSlotNum=*((long *)pF);
1091    if(lSlotNum<0) return 0;
1092    if(lSlotNum>8) return 0;
1093    lSelectedSlot=lSlotNum+1;
1094    return 1;
1095   }
1096  //----------------------------------------------------//
1097  if(!pF)                    return 0;                  // some checks
1098  if(pF->ulFreezeVersion!=1) return 0;
1099
1100  if(ulGetFreezeData==1)                                // 1: get data
1101   {
1102    pF->ulStatus=lGPUstatusRet;
1103    memcpy(pF->ulControl,ulStatusControl,256*sizeof(uint32_t));
1104    memcpy(pF->psxVRam,  psxVub,         1024*512*2);
1105
1106    return 1;
1107   }
1108
1109  if(ulGetFreezeData!=0) return 0;                      // 0: set data
1110
1111  lGPUstatusRet=pF->ulStatus;
1112  memcpy(ulStatusControl,pF->ulControl,256*sizeof(uint32_t));
1113  memcpy(psxVub,         pF->psxVRam,  1024*512*2);
1114
1115 // RESET TEXTURE STORE HERE, IF YOU USE SOMETHING LIKE THAT
1116
1117  GPUwriteStatus(ulStatusControl[0]);
1118  GPUwriteStatus(ulStatusControl[1]);
1119  GPUwriteStatus(ulStatusControl[2]);
1120  GPUwriteStatus(ulStatusControl[3]);
1121  GPUwriteStatus(ulStatusControl[8]);                   // try to repair things
1122  GPUwriteStatus(ulStatusControl[6]);
1123  GPUwriteStatus(ulStatusControl[7]);
1124  GPUwriteStatus(ulStatusControl[5]);
1125  GPUwriteStatus(ulStatusControl[4]);
1126
1127  return 1;
1128 }
1129
1130 void CALLBACK GPUvBlank(int val)
1131 {
1132  vBlank=val?0x80000000:0;
1133 }
1134