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