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